From cb0bee35fecb674e408cd904a0d7b69bb1cb9763 Mon Sep 17 00:00:00 2001 From: Josh Gross Date: Fri, 9 Dec 2022 10:40:19 -0500 Subject: [PATCH] Replace `allowPartialContext` with a mode enum --- actions-languageservice/src/complete.ts | 4 +-- .../src/context-providers/default.ts | 18 +++++----- .../src/context-providers/matrix.test.ts | 35 ++++++++++--------- .../src/context-providers/matrix.ts | 14 ++++---- actions-languageservice/src/validate.ts | 5 ++- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/actions-languageservice/src/complete.ts b/actions-languageservice/src/complete.ts index 0def058..067680c 100644 --- a/actions-languageservice/src/complete.ts +++ b/actions-languageservice/src/complete.ts @@ -11,7 +11,7 @@ import {File} from "@github/actions-workflow-parser/workflows/file"; import {Position, TextDocument} from "vscode-languageserver-textdocument"; import {CompletionItem, TextEdit, Range} from "vscode-languageserver-types"; import {ContextProviderConfig} from "./context-providers/config"; -import {getContext} from "./context-providers/default"; +import {getContext, Mode} from "./context-providers/default"; import {getWorkflowContext, WorkflowContext} from "./context/workflow-context"; import {nullTrace} from "./nulltrace"; import {getAllowedContext} from "./utils/allowed-context"; @@ -85,7 +85,7 @@ export async function complete( const expressionInput = (getExpressionInput(currentInput, relCharPos) || "").trim(); const allowedContext = getAllowedContext(token, parent); - const context = await getContext(allowedContext, contextProviderConfig, workflowContext, true); + const context = await getContext(allowedContext, contextProviderConfig, workflowContext, Mode.Completion); return completeExpression(expressionInput, context, []); } diff --git a/actions-languageservice/src/context-providers/default.ts b/actions-languageservice/src/context-providers/default.ts index 2c0f35f..ba83a74 100644 --- a/actions-languageservice/src/context-providers/default.ts +++ b/actions-languageservice/src/context-providers/default.ts @@ -12,17 +12,23 @@ import {getStrategyContext} from "./strategy"; // Null indicates that the context provider doesn't have any value to provide export type ContextValue = data.Dictionary | data.Null; +export enum Mode { + Completion, + Validation, + Hover +} + export async function getContext( names: string[], config: ContextProviderConfig | undefined, workflowContext: WorkflowContext, - allowPartialContext: boolean = false + mode: Mode ): Promise { const context = new data.Dictionary(); const filteredNames = filterContextNames(names, workflowContext); for (const contextName of filteredNames) { - let value = getDefaultContext(contextName, workflowContext, allowPartialContext) || new data.Dictionary(); + let value = getDefaultContext(contextName, workflowContext, mode) || new data.Dictionary(); if (value.kind === Kind.Null) { context.add(contextName, value); continue; @@ -36,11 +42,7 @@ export async function getContext( return context; } -function getDefaultContext( - name: string, - workflowContext: WorkflowContext, - allowPartialContext: boolean -): ContextValue | undefined { +function getDefaultContext(name: string, workflowContext: WorkflowContext, mode: Mode): ContextValue | undefined { switch (name) { case "runner": return objectToDictionary({ @@ -67,7 +69,7 @@ function getDefaultContext( return getStrategyContext(workflowContext); case "matrix": - return getMatrixContext(workflowContext, allowPartialContext); + return getMatrixContext(workflowContext, mode); } return undefined; diff --git a/actions-languageservice/src/context-providers/matrix.test.ts b/actions-languageservice/src/context-providers/matrix.test.ts index 5f00477..0aa68cb 100644 --- a/actions-languageservice/src/context-providers/matrix.test.ts +++ b/actions-languageservice/src/context-providers/matrix.test.ts @@ -7,6 +7,7 @@ import {SequenceToken} from "@github/actions-workflow-parser/templates/tokens/se import {StringToken} from "@github/actions-workflow-parser/templates/tokens/string-token"; import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/template-token"; import {WorkflowContext} from "../context/workflow-context"; +import {Mode} from "./default"; import {getMatrixContext} from "./matrix"; type MatrixMap = { @@ -62,7 +63,7 @@ describe("matrix context", () => { const workflowContext = {} as WorkflowContext; expect(workflowContext.job).toBeUndefined(); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual(new data.Dictionary()); }); @@ -71,7 +72,7 @@ describe("matrix context", () => { const workflowContext = {job} as WorkflowContext; expect(workflowContext.job!.strategy).toBeUndefined(); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual(new data.Dictionary()); }); @@ -79,7 +80,7 @@ describe("matrix context", () => { const workflowContext = contextFromStrategy(stringToToken("hello")); expect(workflowContext.job!.strategy).toBeDefined(); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual(new data.Dictionary()); }); @@ -87,7 +88,7 @@ describe("matrix context", () => { const strategy = new MappingToken(undefined, undefined, undefined); const workflowContext = contextFromStrategy(strategy); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual(new data.Null()); }); @@ -96,7 +97,7 @@ describe("matrix context", () => { strategy.add(stringToToken("matrix"), stringToToken("hello")); const workflowContext = contextFromStrategy(strategy); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual(new data.Null()); }); @@ -105,7 +106,7 @@ describe("matrix context", () => { strategy.add(stringToToken("matrix"), new MappingToken(undefined, undefined, undefined)); const workflowContext = contextFromStrategy(strategy); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual(new data.Dictionary()); }); }); @@ -116,7 +117,7 @@ describe("matrix context", () => { strategy.add(stringToToken("matrix"), expressionToToken("${{ fromJSON(needs.job1.outputs.matrix) }}")); const workflowContext = contextFromStrategy(strategy); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual(new data.Null()); }); @@ -136,12 +137,12 @@ describe("matrix context", () => { strategy.add(stringToToken("matrix"), matrix); const workflowContext = contextFromStrategy(strategy); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual(new data.Null()); }); - it("matrix with include expression and partial context allowed", () => { + it("matrix with include expression during completion", () => { const include = expressionToToken("${{ fromJSON(needs.job1.outputs.matrix) }}"); const nodeSequence = new SequenceToken(undefined, undefined, undefined); @@ -157,7 +158,7 @@ describe("matrix context", () => { const workflowContext = contextFromStrategy(strategy); - const context = getMatrixContext(workflowContext, true); + const context = getMatrixContext(workflowContext, Mode.Completion); expect(context).toEqual( new data.Dictionary({ @@ -177,7 +178,7 @@ describe("matrix context", () => { strategy.add(stringToToken("matrix"), matrix); const workflowContext = contextFromStrategy(strategy); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual( new data.Dictionary({ @@ -192,7 +193,7 @@ describe("matrix context", () => { it("basic matrix", () => { const workflowContext = createMatrix({os: ["ubuntu-latest", "windows-latest"]}); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual( new data.Dictionary({ key: "os", @@ -207,7 +208,7 @@ describe("matrix context", () => { node: ["12", "14"] }); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual( new data.Dictionary( { @@ -234,7 +235,7 @@ describe("matrix context", () => { ] }); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual( new data.Dictionary( @@ -268,7 +269,7 @@ describe("matrix context", () => { ] }); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual( new data.Dictionary( @@ -302,7 +303,7 @@ describe("matrix context", () => { ] }); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual( new data.Dictionary( @@ -337,7 +338,7 @@ describe("matrix context", () => { ] }); - const context = getMatrixContext(workflowContext, false); + const context = getMatrixContext(workflowContext, Mode.Validation); expect(context).toEqual(new data.Dictionary()); }); diff --git a/actions-languageservice/src/context-providers/matrix.ts b/actions-languageservice/src/context-providers/matrix.ts index 5b65b10..a306929 100644 --- a/actions-languageservice/src/context-providers/matrix.ts +++ b/actions-languageservice/src/context-providers/matrix.ts @@ -4,9 +4,9 @@ import {KeyValuePair} from "@github/actions-workflow-parser/templates/tokens/key import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token"; import {SequenceToken} from "@github/actions-workflow-parser/templates/tokens/sequence-token"; import {WorkflowContext} from "../context/workflow-context"; -import {ContextValue} from "./default"; +import {ContextValue, Mode} from "./default"; -export function getMatrixContext(workflowContext: WorkflowContext, allowPartialContext: boolean): ContextValue { +export function getMatrixContext(workflowContext: WorkflowContext, mode: Mode): ContextValue { // https://docs.github.com/en/actions/learn-github-actions/contexts#matrix-context const strategy = workflowContext.job?.strategy; if (!strategy || !isMapping(strategy)) { @@ -19,7 +19,7 @@ export function getMatrixContext(workflowContext: WorkflowContext, allowPartialC return new data.Null(); } - const properties = matrixProperties(matrix, allowPartialContext); + const properties = matrixProperties(matrix, mode); if (!properties) { // Matrix included an expression, so there's no context we can provide return new data.Null(); @@ -94,10 +94,7 @@ export function getMatrixContext(workflowContext: WorkflowContext, allowPartialC * * Keys: os, version, environment */ -function matrixProperties( - matrix: MappingToken, - allowPartialContext: boolean -): Map | undefined> | undefined { +function matrixProperties(matrix: MappingToken, mode: Mode): Map | undefined> | undefined { const properties = new Map | undefined>(); let include: SequenceToken | undefined; @@ -113,7 +110,8 @@ function matrixProperties( case "include": // If "include" is an expression, we can't know the full properties of the matrix if (isBasicExpression(pair.value) || !isSequence(pair.value)) { - if (!allowPartialContext) { + // Without the full properties of the matrix, we shouldn't validate anything + if (mode === Mode.Validation) { return; } else { continue; diff --git a/actions-languageservice/src/validate.ts b/actions-languageservice/src/validate.ts index 99962c9..8d2938e 100644 --- a/actions-languageservice/src/validate.ts +++ b/actions-languageservice/src/validate.ts @@ -10,7 +10,6 @@ import { } from "@github/actions-workflow-parser"; import {ErrorPolicy} from "@github/actions-workflow-parser/model/convert"; import {splitAllowedContext} from "@github/actions-workflow-parser/templates/allowed-context"; -import {Definition} from "@github/actions-workflow-parser/templates/schema/definition"; import {BasicExpressionToken} from "@github/actions-workflow-parser/templates/tokens/basic-expression-token"; import {StringToken} from "@github/actions-workflow-parser/templates/tokens/string-token"; import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/template-token"; @@ -19,7 +18,7 @@ import {TextDocument} from "vscode-languageserver-textdocument"; import {Diagnostic, DiagnosticSeverity, URI} from "vscode-languageserver-types"; import {ContextProviderConfig} from "./context-providers/config"; -import {getContext} from "./context-providers/default"; +import {getContext, Mode} from "./context-providers/default"; import {getWorkflowContext, WorkflowContext} from "./context/workflow-context"; import {AccessError, wrapDictionary} from "./expression-validation/error-dictionary"; import {error} from "./log"; @@ -196,7 +195,7 @@ async function validateExpression( } try { - const context = await getContext(namedContexts, contextProviderConfig, workflowContext); + const context = await getContext(namedContexts, contextProviderConfig, workflowContext, Mode.Validation); const e = new Evaluator(expr, wrapDictionary(context)); e.evaluate();