React to extracted functions, add test

This commit is contained in:
Christopher Schleiden
2023-02-02 08:57:16 -08:00
parent 60757d5de8
commit f5463c18b5
2 changed files with 29 additions and 30 deletions
@@ -153,6 +153,24 @@ describe("expressions", () => {
]); ]);
}); });
it("multiple regions - first region", async () => {
const input = "run-name: test-${{ git| == 1 }}-${{ github.event }}";
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig);
expect(result.map(x => x.label)).toEqual([
"github",
"inputs",
"vars",
"contains",
"endsWith",
"format",
"fromJson",
"join",
"startsWith",
"toJson"
]);
});
it("multiple regions", async () => { it("multiple regions", async () => {
const input = "run-name: test-${{ github }}-${{ | }}"; const input = "run-name: test-${{ github }}-${{ | }}";
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig);
+11 -30
View File
@@ -1,4 +1,4 @@
import {DescriptionDictionary, complete as completeExpression} from "@github/actions-expressions"; import {complete as completeExpression, DescriptionDictionary} from "@github/actions-expressions";
import {CompletionItem as ExpressionCompletionItem} from "@github/actions-expressions/completion"; import {CompletionItem as ExpressionCompletionItem} from "@github/actions-expressions/completion";
import { import {
convertWorkflowTemplate, convertWorkflowTemplate,
@@ -8,8 +8,6 @@ import {
parseWorkflow parseWorkflow
} from "@github/actions-workflow-parser"; } from "@github/actions-workflow-parser";
import {ErrorPolicy} from "@github/actions-workflow-parser/model/convert"; import {ErrorPolicy} from "@github/actions-workflow-parser/model/convert";
import {DefinitionType} from "@github/actions-workflow-parser/templates/schema/definition-type";
import {StringDefinition} from "@github/actions-workflow-parser/templates/schema/string-definition";
import {OPEN_EXPRESSION} from "@github/actions-workflow-parser/templates/template-constants"; import {OPEN_EXPRESSION} from "@github/actions-workflow-parser/templates/template-constants";
import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/index"; import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/index";
import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token"; import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token";
@@ -26,11 +24,12 @@ import {nullTrace} from "./nulltrace";
import {findToken} from "./utils/find-token"; import {findToken} from "./utils/find-token";
import {guessIndentation} from "./utils/indentation-guesser"; import {guessIndentation} from "./utils/indentation-guesser";
import {mapRange} from "./utils/range"; import {mapRange} from "./utils/range";
import {getRelCharOffset} from "./utils/rel-char-pos";
import {transform} from "./utils/transform"; import {transform} from "./utils/transform";
import {isStringExpression} from "./utils/type-guards";
import {Value, ValueProviderConfig} from "./value-providers/config"; import {Value, ValueProviderConfig} from "./value-providers/config";
import {defaultValueProviders} from "./value-providers/default"; import {defaultValueProviders} from "./value-providers/default";
import {definitionValues} from "./value-providers/definition"; import {definitionValues} from "./value-providers/definition";
import {TokenRange} from "@github/actions-workflow-parser/templates/tokens/token-range";
export function getExpressionInput(input: string, pos: number): string { export function getExpressionInput(input: string, pos: number): string {
// Find start marker around the cursor position // Find start marker around the cursor position
@@ -79,7 +78,7 @@ export async function complete(
// expression nodes for invalid expressions and during editing expressions are invalid most of the time. // expression nodes for invalid expressions and during editing expressions are invalid most of the time.
if (token) { if (token) {
const isStringExpressionToken = isStringExpression(token); const isStringExpressionToken = isStringExpression(token);
const isBasicExpressionToken = isBasicExpression(token) && token.isExpression; const isBasicExpressionToken = isBasicExpression(token);
if (isStringExpressionToken || isBasicExpressionToken) { if (isStringExpressionToken || isBasicExpressionToken) {
const allowedContext = token.definitionInfo?.allowedContext || []; const allowedContext = token.definitionInfo?.allowedContext || [];
@@ -93,12 +92,14 @@ export async function complete(
const indentString = " ".repeat(indentation.tabSize); const indentString = " ".repeat(indentation.tabSize);
const values = await getValues(token, keyToken, parent, valueProviderConfig, workflowContext, indentString); const values = await getValues(token, keyToken, parent, valueProviderConfig, workflowContext, indentString);
let replaceRange: Range | undefined; let replaceRange: Range | undefined;
if (token?.range) { if (token?.range) {
replaceRange = mapRange(token.range); replaceRange = mapRange(token.range);
} else if (!token) { } else if (!token) {
// Not a valid token, create a range from the current position // Not a valid token, create a range from the current position
const line = newDoc.getText({start: {line: position.line, character: 0}, end: position}); const line = newDoc.getText({start: {line: position.line, character: 0}, end: position});
// Get the length of the current word // Get the length of the current word
const val = line.match(/[\w_-]*$/)?.[0].length || 0; const val = line.match(/[\w_-]*$/)?.[0].length || 0;
replaceRange = Range.create({line: position.line, character: position.character - val}, position); replaceRange = Range.create({line: position.line, character: position.character - val}, position);
@@ -207,21 +208,21 @@ function getExpressionCompletionItems(
): CompletionItem[] { ): CompletionItem[] {
let expressionInput = ""; let expressionInput = "";
let currentInput = ""; let currentInput = "";
let relCharPos: number = 0; let relCharOffset: number = 0;
if (isBasicExpression(token)) { if (isBasicExpression(token)) {
expressionInput = currentInput = token.expression; expressionInput = currentInput = token.expression;
relCharPos = getRelCharPos(token.range!, expressionInput, pos); relCharOffset = getRelCharOffset(token.range!, expressionInput, pos);
} else { } else {
const stringToken = token.assertString("Expected string token for expression completion"); const stringToken = token.assertString("Expected string token for expression completion");
currentInput = stringToken.source || stringToken.value; currentInput = stringToken.source || stringToken.value;
relCharPos = getRelCharPos(stringToken.range!, currentInput, pos); relCharOffset = getRelCharOffset(stringToken.range!, currentInput, pos);
expressionInput = (getExpressionInput(currentInput, relCharPos) || "").trim(); expressionInput = (getExpressionInput(currentInput, relCharOffset) || "").trim();
} }
try { try {
return completeExpression(expressionInput, context, [], validatorFunctions).map(item => return completeExpression(expressionInput, context, [], validatorFunctions).map(item =>
mapExpressionCompletionItem(item, currentInput[relCharPos]) mapExpressionCompletionItem(item, currentInput[relCharOffset])
); );
} catch (e: any) { } catch (e: any) {
error(`Error while completing expression: '${e?.message || "<no details>"}'`); error(`Error while completing expression: '${e?.message || "<no details>"}'`);
@@ -252,23 +253,3 @@ function mapExpressionCompletionItem(item: ExpressionCompletionItem, charAfterPo
kind: item.function ? CompletionItemKind.Function : CompletionItemKind.Variable kind: item.function ? CompletionItemKind.Function : CompletionItemKind.Variable
}; };
} }
function getRelCharPos(tokenRange: TokenRange, currentInput: string, pos: Position): number {
// Transform the overall position into a node relative position
const range = mapRange(tokenRange);
if (range.start.line !== range.end.line) {
const lines = currentInput.split("\n");
const lineDiff = pos.line - range.start.line - 1;
const linesBeforeCusor = lines.slice(0, lineDiff);
return linesBeforeCusor.join("\n").length + pos.character + 1;
} else {
return pos.character - range.start.character;
}
}
function isStringExpression(token: TemplateToken): boolean {
const isExpression =
token.definition?.definitionType === DefinitionType.String && (token.definition as StringDefinition).isExpression;
const containsExpression = isString(token) && token.value.indexOf(OPEN_EXPRESSION) >= 0;
return isExpression || containsExpression;
}