Initial code import

This commit is contained in:
Christopher Schleiden
2022-11-08 17:00:59 -08:00
parent 7352cda0cf
commit 2e1652515e
38 changed files with 1354 additions and 1 deletions
@@ -0,0 +1,126 @@
import {
TemplateToken,
MAPPING_TYPE,
SEQUENCE_TYPE,
NULL_TYPE,
} from "@github/actions-workflow-parser/templates/tokens/index";
import { MappingToken } from "@github/actions-workflow-parser/templates/tokens/mapping-token";
import { SequenceToken } from "@github/actions-workflow-parser/templates/tokens/sequence-token";
import { Position } from "vscode-languageserver-textdocument";
export function findInnerToken(pos: Position, root?: TemplateToken) {
const [innerToken, _] = findInnerTokenAndParent(pos, root);
return innerToken;
}
export function findInnerTokenAndParent(
pos: Position,
root?: TemplateToken
): [TemplateToken | null, TemplateToken | null] {
if (!root) {
return [null, null];
}
const s = [root];
let parent: TemplateToken | null = null;
for (;;) {
const token = s.shift();
if (!token) {
break;
}
if (!posInToken(pos, token)) {
continue;
}
// Position is in token, enqueue children if there are any
switch (token.templateTokenType) {
case MAPPING_TYPE:
const mappingToken = token as MappingToken;
parent = mappingToken;
for (let i = 0; i < mappingToken.count; i++) {
const { key, value } = mappingToken.get(i);
// Null tokens don't have a position, we can only use the line information
if (nullNodeOnLine(pos, key, value)) {
return [value, key];
}
s.push(value);
}
continue;
case SEQUENCE_TYPE:
const sequenceToken = token as SequenceToken;
parent = sequenceToken;
for (let i = 0; i < sequenceToken.count; i++) {
s.push(sequenceToken.get(i));
}
continue;
}
return [token, parent];
}
return [null, parent];
}
function posInToken(pos: Position, token: TemplateToken): boolean {
if (!token.range) {
return false;
}
const r = token.range;
// TokenRange is one-based, Position is zero-based
const tokenLine = pos.line + 1;
const tokenChar = pos.character + 1;
// Check lines
if (r.start[0] > tokenLine || tokenLine > r.end[0]) {
return false;
}
// Position is within the token lines. Check character/column if pos line matches
// start or end
if (
(r.start[0] === tokenLine && tokenChar < r.start[1]) ||
(r.end[0] === tokenLine && tokenChar > r.end[1])
) {
return false;
}
return true;
}
function nullNodeOnLine(
pos: Position,
key: TemplateToken,
value: TemplateToken
): boolean {
if (value.templateTokenType !== NULL_TYPE) {
return false;
}
if (!value.range) {
return false;
}
if (!key.range) {
return false;
}
if (value.range.start[0] !== value.range.end[0]) {
// Token occupies multiple lines, can't be a null node
return false;
}
// TokenRange is one-based, Position is zero-based
const posLine = pos.line + 1;
if (posLine != value.range.start[0]) {
return false;
}
return true;
}
@@ -0,0 +1,72 @@
import { Position, TextDocument } from "vscode-languageserver-textdocument";
const DUMMY_KEY = "dummy";
// Transform a document to work around YAML parsing issues
// Based on `_transform` in https://github.com/cschleiden/github-actions-parser/blob/main/src/lib/parser/complete.ts#L311
export function transform(
doc: TextDocument,
pos: Position
): [TextDocument, Position] {
const input = doc.getText();
let offset = doc.offsetAt(pos);
// TODO: Optimize this...
const lines = input.split("\n");
const lineNo = input
.substring(0, offset)
.split("")
.filter((x) => x === "\n").length;
const linePos =
offset - lines.slice(0, lineNo).reduce((p, l) => p + l.length + 1, 0);
const line = lines[lineNo];
let partialInput = line.trim();
// Special case for Actions, if this line contains an expression marker, do _not_ transform. This is
// an ugly fix for auto-completion in multi-line YAML strings. At this point in the process, we cannot
// determine if a line is in such a multi-line string.
if (partialInput.indexOf("${{") === -1) {
const colon = line.indexOf(":");
if (colon === -1) {
const trimmedLine = line.trim();
if (trimmedLine === "" || trimmedLine === "-") {
// Node in sequence or empty line
let spacer = "";
if (trimmedLine === "-" && !line.endsWith(" ")) {
spacer = " ";
offset++;
}
lines[lineNo] =
line.substring(0, linePos) +
spacer +
DUMMY_KEY +
(trimmedLine === "-" ? "" : ":") +
line.substring(linePos);
// Adjust pos by one to prevent a sequence node being marked as active
offset++;
} else if (!trimmedLine.startsWith("-")) {
// Add `:` to end of line
lines[lineNo] = line + ":";
}
if (trimmedLine.startsWith("-")) {
partialInput = trimmedLine
.substring(trimmedLine.indexOf("-") + 1)
.trim();
}
} else {
partialInput = (
offset > colon ? line.substring(colon + 1) : line.substring(0, colon)
).trim();
offset = offset - 1;
}
}
const newDoc = TextDocument.create(
doc.uri,
doc.languageId,
doc.version,
lines.join("\n")
);
return [newDoc, newDoc.positionAt(offset)];
}