diff --git a/languageservice/src/complete.expressions.test.ts b/languageservice/src/complete.expressions.test.ts index 64fdd12..f76ce45 100644 --- a/languageservice/src/complete.expressions.test.ts +++ b/languageservice/src/complete.expressions.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import {data, DescriptionDictionary} from "@github/actions-expressions"; import {CompletionItem, CompletionItemKind} from "vscode-languageserver-types"; import {complete, getExpressionInput} from "./complete"; @@ -9,17 +10,19 @@ import {testFileProvider} from "./test-utils/test-file-provider"; import {clearCache} from "./utils/workflow-cache"; const contextProviderConfig: ContextProviderConfig = { - getContext: async (context: string) => { + getContext: (context: string) => { switch (context) { case "github": - return new DescriptionDictionary({ - key: "event", - value: new data.StringData("push"), - description: "The event that triggered the workflow" - }); + return Promise.resolve( + new DescriptionDictionary({ + key: "event", + value: new data.StringData("push"), + description: "The event that triggered the workflow" + }) + ); } - return undefined; + return Promise.resolve(undefined); } }; diff --git a/languageservice/src/complete.ts b/languageservice/src/complete.ts index 870138d..8b6a043 100644 --- a/languageservice/src/complete.ts +++ b/languageservice/src/complete.ts @@ -225,6 +225,10 @@ function getExpressionCompletionItems( context: DescriptionDictionary, pos: Position ): CompletionItem[] { + if (!token.range) { + return []; + } + let currentInput = ""; if (isBasicExpression(token)) { @@ -234,15 +238,15 @@ function getExpressionCompletionItems( currentInput = stringToken.source || stringToken.value; } - const relCharOffset = getRelCharOffset(token.range!, currentInput, pos); + const relCharOffset = getRelCharOffset(token.range, currentInput, pos); const expressionInput = (getExpressionInput(currentInput, relCharOffset) || "").trim(); try { return completeExpression(expressionInput, context, [], validatorFunctions).map(item => mapExpressionCompletionItem(item, currentInput[relCharOffset]) ); - } catch (e: any) { - error(`Error while completing expression: '${e?.message || ""}'`); + } catch (e) { + error(`Error while completing expression: '${(e as Error)?.message || ""}'`); return []; } } diff --git a/languageservice/src/context-providers/events/eventPayloads.ts b/languageservice/src/context-providers/events/eventPayloads.ts index 039f0b1..e0228ea 100644 --- a/languageservice/src/context-providers/events/eventPayloads.ts +++ b/languageservice/src/context-providers/events/eventPayloads.ts @@ -73,10 +73,10 @@ type DeduplicatedWebhooks = { }; }; -// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment +/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any */ const dedupedWebhookPayloads: DeduplicatedWebhooks = webhooks as any; -// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const objects: Param[] = webhookObjects as any; +/* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any */ // Hydrated webhook payloads const webhookPayloads: Webhooks = {}; diff --git a/languageservice/src/hover.expressions.test.ts b/languageservice/src/hover.expressions.test.ts index 990ebe0..f5f4141 100644 --- a/languageservice/src/hover.expressions.test.ts +++ b/languageservice/src/hover.expressions.test.ts @@ -8,28 +8,30 @@ import {TestLogger} from "./test-utils/logger"; import {clearCache} from "./utils/workflow-cache"; const contextProviderConfig: ContextProviderConfig = { - getContext: async (context: string) => { + getContext: (context: string) => { switch (context) { case "github": - return new DescriptionDictionary( - { - key: "event", - value: new data.StringData("push"), - description: "The event that triggered the workflow" - }, - { - key: "test", - value: new DescriptionDictionary({ - key: "name", + return Promise.resolve( + new DescriptionDictionary( + { + key: "event", value: new data.StringData("push"), - description: "Name for the test" - }), - description: "Test dictionary" - } + description: "The event that triggered the workflow" + }, + { + key: "test", + value: new DescriptionDictionary({ + key: "name", + value: new data.StringData("push"), + description: "Name for the test" + }), + description: "Test dictionary" + } + ) ); } - return undefined; + return Promise.resolve(undefined); } }; diff --git a/languageservice/src/hover.test.ts b/languageservice/src/hover.test.ts index d43d868..9d48303 100644 --- a/languageservice/src/hover.test.ts +++ b/languageservice/src/hover.test.ts @@ -1,5 +1,4 @@ import {isString} from "@github/actions-workflow-parser"; -import {StringToken} from "@github/actions-workflow-parser/templates/tokens/string-token"; import {DescriptionProvider, hover, HoverConfig} from "./hover"; import {getPositionFromCursor} from "./test-utils/cursor-position"; import {testFileProvider} from "./test-utils/test-file-provider"; @@ -8,15 +7,16 @@ import {clearCache} from "./utils/workflow-cache"; export function testHoverConfig(tokenValue: string, tokenKey: string, description?: string) { return { descriptionProvider: { - getDescription: async (_, token, __) => { + getDescription: (_, token) => { if (!isString(token)) { throw new Error("Test provider only supports string tokens"); } expect(token.value).toEqual(tokenValue); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion expect(token.definition!.key).toEqual(tokenKey); - return description; + return Promise.resolve(description); } } satisfies DescriptionProvider, fileProvider: testFileProvider diff --git a/languageservice/src/hover.ts b/languageservice/src/hover.ts index f2cfed0..a1081c0 100644 --- a/languageservice/src/hover.ts +++ b/languageservice/src/hover.ts @@ -155,7 +155,8 @@ async function getDescription( function isCronMappingValue(tokenResult: TokenResult): boolean { return ( tokenResult.parent?.definition?.key === "cron-mapping" && - isString(tokenResult.token!) && + !!tokenResult.token && + isString(tokenResult.token) && tokenResult.token.value !== "cron" ); } @@ -199,7 +200,7 @@ function expressionHover( }; } catch (e) { // Hovering over an invalid expression should not cause an error here - info(`Encountered error trying to calculate expression hover: ${e}`); + info(`Encountered error trying to calculate expression hover: ${(e as Error).message}`); return null; } } diff --git a/languageservice/src/nulltrace.ts b/languageservice/src/nulltrace.ts index 51d9f66..8955a1a 100644 --- a/languageservice/src/nulltrace.ts +++ b/languageservice/src/nulltrace.ts @@ -1,7 +1,3 @@ -import {TraceWriter} from "@github/actions-workflow-parser/templates/trace-writer"; +import {NoOperationTraceWriter} from "@github/actions-workflow-parser/templates/trace-writer"; -export const nullTrace: TraceWriter = { - info: x => {}, - verbose: x => {}, - error: x => {} -}; +export const nullTrace = new NoOperationTraceWriter(); diff --git a/languageservice/src/validate.actions.test.ts b/languageservice/src/validate.actions.test.ts index 027d9e5..81f61bd 100644 --- a/languageservice/src/validate.actions.test.ts +++ b/languageservice/src/validate.actions.test.ts @@ -14,7 +14,7 @@ beforeEach(() => { }); const validationConfig: ValidationConfig = { - fetchActionMetadata: async (ref: ActionReference) => { + fetchActionMetadata: (ref: ActionReference) => { let metadata: ActionMetadata | undefined = undefined; switch (ref.owner + "/" + ref.name + "@" + ref.ref) { case "actions/checkout@v3": @@ -82,7 +82,7 @@ const validationConfig: ValidationConfig = { description: "An action with no inputs" }; } - return metadata; + return Promise.resolve(metadata); } }; @@ -326,8 +326,8 @@ jobs: config.valueProviderConfig = { "step-with": { kind: ValueProviderKind.AllowedValues, - get: async () => { - return [{label: "repository", description: "Repository name with owner."}]; + get: () => { + return Promise.resolve([{label: "repository", description: "Repository name with owner."}]); } } }; diff --git a/languageservice/src/validate.expressions.test.ts b/languageservice/src/validate.expressions.test.ts index 747bb58..95a1c6d 100644 --- a/languageservice/src/validate.expressions.test.ts +++ b/languageservice/src/validate.expressions.test.ts @@ -1,4 +1,4 @@ -import {data, DescriptionDictionary} from "@github/actions-expressions/."; +import {DescriptionDictionary} from "@github/actions-expressions/."; import {DiagnosticSeverity} from "vscode-languageserver-types"; import {ContextProviderConfig} from "./context-providers/config"; import {registerLogger} from "./log"; @@ -48,15 +48,16 @@ jobs: it("partial skip access invalid context on incomplete", async () => { const contextProviderConfig: ContextProviderConfig = { - getContext: async (context: string) => { + getContext: (context: string) => { switch (context) { - case "secrets": + case "secrets": { const dict = new DescriptionDictionary(); dict.complete = false; - return dict; + return Promise.resolve(dict); + } } - return undefined; + return Promise.resolve(undefined); } }; diff --git a/languageservice/src/validate.test.ts b/languageservice/src/validate.test.ts index 533de34..cb87c76 100644 --- a/languageservice/src/validate.test.ts +++ b/languageservice/src/validate.test.ts @@ -3,7 +3,7 @@ import {createDocument} from "./test-utils/document"; import {validate} from "./validate"; import {defaultValueProviders} from "./value-providers/default"; import {clearCache} from "./utils/workflow-cache"; -import {ValueProvider, ValueProviderConfig, ValueProviderKind} from "./value-providers/config"; +import {ValueProviderConfig, ValueProviderKind} from "./value-providers/config"; beforeEach(() => { clearCache(); @@ -249,7 +249,7 @@ jobs: const valueProviderConfig: ValueProviderConfig = { "job-environment": { kind: ValueProviderKind.AllowedValues, - get: async () => [{label: "test"}], + get: () => Promise.resolve([{label: "test"}]), caseInsensitive: false } }; @@ -286,7 +286,7 @@ jobs: const valueProviderConfig: ValueProviderConfig = { "job-environment": { kind: ValueProviderKind.AllowedValues, - get: async () => [{label: "test"}], + get: () => Promise.resolve([{label: "test"}]), caseInsensitive: true } }; diff --git a/languageservice/src/validate.ts b/languageservice/src/validate.ts index f422873..13c0de7 100644 --- a/languageservice/src/validate.ts +++ b/languageservice/src/validate.ts @@ -23,6 +23,7 @@ import {validateAction} from "./validate-action"; import {ValueProviderConfig, ValueProviderKind} from "./value-providers/config"; import {defaultValueProviders} from "./value-providers/default"; import {fetchOrParseWorkflow, fetchOrConvertWorkflowTemplate} from "./utils/workflow-cache"; +import {TokenRange} from "@github/actions-workflow-parser/templates/tokens/token-range"; export type ValidationConfig = { valueProviderConfig?: ValueProviderConfig; @@ -72,7 +73,7 @@ export async function validate(textDocument: TextDocument, config?: ValidationCo }); } } catch (e) { - error(`Unhandled error while validating: ${e}`); + error(`Unhandled error while validating: ${(e as Error).message}`); } return diagnostics; @@ -92,18 +93,18 @@ async function additionalValidations( const validationDefinition = validationToken.definition; // If this is an expression, validate it - if (isBasicExpression(token)) { + if (isBasicExpression(token) && token.range) { await validateExpression( diagnostics, token, validationToken.definitionInfo?.allowedContext || [], config?.contextProviderConfig, - getProviderContext(documentUri, template, root, token) + getProviderContext(documentUri, template, root, token.range) ); } - if (token.definition?.key === "regular-step") { - const context = getProviderContext(documentUri, template, root, token); + if (token.definition?.key === "regular-step" && token.range) { + const context = getProviderContext(documentUri, template, root, token.range); await validateAction(diagnostics, token, context.step, config); } @@ -129,7 +130,7 @@ async function additionalValidations( } if (valueProvider) { - const customValues = await valueProvider.get(getProviderContext(documentUri, template, root, token)); + const customValues = await valueProvider.get(getProviderContext(documentUri, template, root, token.range)); const caseInsensitive = valueProvider.caseInsensitive ?? false; const customValuesMap = new Set(customValues.map(x => (caseInsensitive ? x.label.toLowerCase() : x.label))); @@ -161,12 +162,12 @@ function getProviderContext( documentUri: URI, template: WorkflowTemplate, root: TemplateToken, - token: TemplateToken + tokenRange: TokenRange ): WorkflowContext { - const {parent, path} = findToken( + const {path} = findToken( { - line: token.range!.start.line - 1, - character: token.range!.start.column - 1 + line: tokenRange.start.line - 1, + character: tokenRange.start.column - 1 }, root );