diff --git a/actions-languageservice/src/complete.ts b/actions-languageservice/src/complete.ts index b6011e0..15f1439 100644 --- a/actions-languageservice/src/complete.ts +++ b/actions-languageservice/src/complete.ts @@ -15,6 +15,7 @@ import {findToken} from "./utils/find-token"; import {transform} from "./utils/transform"; import {Value, ValueProviderConfig} from "./value-providers/config"; import {defaultValueProviders} from "./value-providers/default"; +import {definitionValues} from "./value-providers/definition"; export function getExpressionInput(input: string, pos: number): string { // Find start marker around the cursor position @@ -98,16 +99,23 @@ async function getValues( const valueProviders = defaultValueProviders(); - // Use the key from the parent if we don't have a value provider for the current key - // Ideally each token would have a valid key + // Use the value provider from the parent if we don't have a value provider for the current key const valueProvider = (token?.definition?.key && valueProviders[token.definition.key]) || - (parent?.definition?.key && valueProviders[parent.definition.key]); - if (!valueProvider) { + (parent.definition?.key && valueProviders[parent.definition.key]); + + if (valueProvider) { + const values = valueProvider(); + return filterAndSortCompletionOptions(values, existingValues); + } + + // Use the definition if there are no value providers + const def = token?.definition || parent.definition; + if (!def) { return []; } - const values = valueProvider(); + const values = definitionValues(def); return filterAndSortCompletionOptions(values, existingValues); } diff --git a/actions-languageservice/src/value-providers/default.ts b/actions-languageservice/src/value-providers/default.ts index f0398b2..7fecea5 100644 --- a/actions-languageservice/src/value-providers/default.ts +++ b/actions-languageservice/src/value-providers/default.ts @@ -1,24 +1,7 @@ -import {BooleanDefinition} from "@github/actions-workflow-parser/templates/schema/boolean-definition"; -import {Definition} from "@github/actions-workflow-parser/templates/schema/definition"; -import {MappingDefinition} from "@github/actions-workflow-parser/templates/schema/mapping-definition"; -import {OneOfDefinition} from "@github/actions-workflow-parser/templates/schema/one-of-definition"; -import {StringDefinition} from "@github/actions-workflow-parser/templates/schema/string-definition"; -import {getWorkflowSchema} from "@github/actions-workflow-parser/workflows/workflow-schema"; import {Value, ValueProvider} from "./config"; export function defaultValueProviders(): {[key: string]: ValueProvider} { - const schema = getWorkflowSchema(); - - const map: {[key: string]: ValueProvider} = {}; - for (const key of Object.keys(schema.definitions)) { - const provider = definitionValueProvider(key, schema.definitions); - if (provider) { - map[key] = provider; - } - } - return { - ...map, "runs-on": () => stringsToValues([ "ubuntu-latest", @@ -36,58 +19,6 @@ export function defaultValueProviders(): {[key: string]: ValueProvider} { }; } -function definitionValueProvider(key: string, definitions: {[key: string]: Definition}): ValueProvider | undefined { - const def = definitions[key]; - - if (def instanceof MappingDefinition) { - return mappingValueProvider(def); - } - - if (def instanceof OneOfDefinition) { - return oneOfValueProvider(def, definitions); - } - - if (def instanceof BooleanDefinition) { - return () => stringsToValues(["true", "false"]); - } - - if (def instanceof StringDefinition && def.constant) { - return () => stringsToValues([def.constant]); - } -} - -function mappingValueProvider(mappingDefinition: MappingDefinition): ValueProvider { - const properties: Value[] = []; - for (const [key, value] of Object.entries(mappingDefinition.properties)) { - properties.push({label: key, description: value.description}); - } - return () => properties; -} - -function oneOfValueProvider(oneOfDefinition: OneOfDefinition, definitions: {[key: string]: Definition}): ValueProvider { - return () => { - const values: Value[] = []; - for (const key of oneOfDefinition.oneOf) { - const provider = definitionValueProvider(key, definitions); - if (!provider) { - continue; - } - for (const prop of provider()) { - values.push(prop); - } - } - return distinctValues(values); - }; -} - -function stringsToValues(labels: string[]): Value[] { +export function stringsToValues(labels: string[]): Value[] { return labels.map(x => ({label: x})); } - -function distinctValues(values: Value[]): Value[] { - const map = new Map(); - for (const value of values) { - map.set(value.label, value); - } - return Array.from(map.values()); -} diff --git a/actions-languageservice/src/value-providers/definition.ts b/actions-languageservice/src/value-providers/definition.ts new file mode 100644 index 0000000..71aeefb --- /dev/null +++ b/actions-languageservice/src/value-providers/definition.ts @@ -0,0 +1,54 @@ +import {BooleanDefinition} from "@github/actions-workflow-parser/templates/schema/boolean-definition"; +import {Definition} from "@github/actions-workflow-parser/templates/schema/definition"; +import {MappingDefinition} from "@github/actions-workflow-parser/templates/schema/mapping-definition"; +import {OneOfDefinition} from "@github/actions-workflow-parser/templates/schema/one-of-definition"; +import {StringDefinition} from "@github/actions-workflow-parser/templates/schema/string-definition"; +import {getWorkflowSchema} from "@github/actions-workflow-parser/workflows/workflow-schema"; +import {Value} from "./config"; +import {stringsToValues} from "./default"; + +export function definitionValues(def: Definition): Value[] { + const schema = getWorkflowSchema(); + + if (def instanceof MappingDefinition) { + return mappingValues(def); + } + + if (def instanceof OneOfDefinition) { + return oneOfValues(def, schema.definitions); + } + + if (def instanceof BooleanDefinition) { + return stringsToValues(["true", "false"]); + } + + if (def instanceof StringDefinition && def.constant) { + return stringsToValues([def.constant]); + } + + return []; +} + +function mappingValues(mappingDefinition: MappingDefinition): Value[] { + const properties: Value[] = []; + for (const [key, value] of Object.entries(mappingDefinition.properties)) { + properties.push({label: key, description: value.description}); + } + return properties; +} + +function oneOfValues(oneOfDefinition: OneOfDefinition, definitions: {[key: string]: Definition}): Value[] { + const values: Value[] = []; + for (const key of oneOfDefinition.oneOf) { + values.push(...definitionValues(definitions[key])); + } + return distinctValues(values); +} + +function distinctValues(values: Value[]): Value[] { + const map = new Map(); + for (const value of values) { + map.set(value.label, value); + } + return Array.from(map.values()); +}