From 4f7d03ed0c71ba71bcd1fe226b507e68c9554d81 Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Fri, 4 Aug 2023 11:19:42 -0700 Subject: [PATCH 1/3] Correctly indent completion options --- languageservice/src/complete.test.ts | 12 +++++- languageservice/src/complete.ts | 4 +- .../src/value-providers/definition.ts | 38 +++++++++++++++---- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/languageservice/src/complete.test.ts b/languageservice/src/complete.test.ts index 060d9da..11a617d 100644 --- a/languageservice/src/complete.test.ts +++ b/languageservice/src/complete.test.ts @@ -4,8 +4,8 @@ import {complete} from "./complete"; import {registerLogger} from "./log"; import {getPositionFromCursor} from "./test-utils/cursor-position"; import {TestLogger} from "./test-utils/logger"; -import {ValueProviderConfig, ValueProviderKind} from "./value-providers/config"; import {clearCache} from "./utils/workflow-cache"; +import {ValueProviderConfig, ValueProviderKind} from "./value-providers/config"; registerLogger(new TestLogger()); @@ -475,7 +475,7 @@ jobs: }); }); - it("adds a new line and indentation for mapping keys", async () => { + it("adds a new line and indentation for mapping keys when the key is given", async () => { const input = "concurrency: |"; const result = await complete(...getPositionFromCursor(input)); @@ -485,4 +485,12 @@ jobs: ]); expect(result.filter(x => x.label === "group").map(x => x.textEdit?.newText)).toEqual(["\n group: "]); }); + + it("does not add new line if no key in line", async () => { + const input = "run-n|"; + + const result = await complete(...getPositionFromCursor(input)); + + expect(result.filter(x => x.label === "run-name").map(x => x.textEdit?.newText)).toEqual(["run-name: "]); + }); }); diff --git a/languageservice/src/complete.ts b/languageservice/src/complete.ts index 0bcda95..943905b 100644 --- a/languageservice/src/complete.ts +++ b/languageservice/src/complete.ts @@ -24,7 +24,7 @@ import {isPlaceholder, transform} from "./utils/transform"; import {fetchOrConvertWorkflowTemplate, fetchOrParseWorkflow} from "./utils/workflow-cache"; import {Value, ValueProviderConfig} from "./value-providers/config"; import {defaultValueProviders} from "./value-providers/default"; -import {definitionValues} from "./value-providers/definition"; +import {DefinitionValueMode, definitionValues} from "./value-providers/definition"; export function getExpressionInput(input: string, pos: number): string { // Find start marker around the cursor position @@ -180,7 +180,7 @@ async function getValues( return []; } - const values = definitionValues(def, indentation); + const values = definitionValues(def, indentation, keyToken ? DefinitionValueMode.Key : DefinitionValueMode.Parent); return filterAndSortCompletionOptions(values, existingValues); } diff --git a/languageservice/src/value-providers/definition.ts b/languageservice/src/value-providers/definition.ts index 059a204..1745264 100644 --- a/languageservice/src/value-providers/definition.ts +++ b/languageservice/src/value-providers/definition.ts @@ -9,15 +9,30 @@ import {getWorkflowSchema} from "@actions/workflow-parser/workflows/workflow-sch import {Value} from "./config"; import {stringsToValues} from "./strings-to-values"; -export function definitionValues(def: Definition, indentation: string): Value[] { +export enum DefinitionValueMode { + /** + * We're getting completion options for a parent token + * foo: + * ba| + */ + Parent, + + /** + * We're getting completion options for a key token. For example: + * foo: | + */ + Key +} + +export function definitionValues(def: Definition, indentation: string, mode: DefinitionValueMode): Value[] { const schema = getWorkflowSchema(); if (def instanceof MappingDefinition) { - return mappingValues(def, schema.definitions, indentation); + return mappingValues(def, schema.definitions, indentation, mode); } if (def instanceof OneOfDefinition) { - return oneOfValues(def, schema.definitions, indentation); + return oneOfValues(def, schema.definitions, indentation, mode); } if (def instanceof BooleanDefinition) { @@ -36,7 +51,7 @@ export function definitionValues(def: Definition, indentation: string): Value[] if (def instanceof SequenceDefinition) { const itemDef = schema.getDefinition(def.itemType); if (itemDef) { - return definitionValues(itemDef, indentation); + return definitionValues(itemDef, indentation, mode); } } @@ -46,7 +61,8 @@ export function definitionValues(def: Definition, indentation: string): Value[] function mappingValues( mappingDefinition: MappingDefinition, definitions: {[key: string]: Definition}, - indentation: string + indentation: string, + mode: DefinitionValueMode ): Value[] { const properties: Value[] = []; for (const [key, value] of Object.entries(mappingDefinition.properties)) { @@ -73,8 +89,13 @@ function mappingValues( case DefinitionType.String: case DefinitionType.Boolean: - insertText = `\n${indentation}${key}: `; + if (mode == DefinitionValueMode.Key) { + insertText = `\n${indentation}${key}: `; + } else { + insertText = `${key}: `; + } break; + default: insertText = `${key}: `; } @@ -93,11 +114,12 @@ function mappingValues( function oneOfValues( oneOfDefinition: OneOfDefinition, definitions: {[key: string]: Definition}, - indentation: string + indentation: string, + mode: DefinitionValueMode ): Value[] { const values: Value[] = []; for (const key of oneOfDefinition.oneOf) { - values.push(...definitionValues(definitions[key], indentation)); + values.push(...definitionValues(definitions[key], indentation, mode)); } return distinctValues(values); } From a324b8b9dc210999f589c6cccd343d920f6ff69b Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Fri, 4 Aug 2023 11:32:10 -0700 Subject: [PATCH 2/3] small tweaks --- languageservice/src/complete.test.ts | 24 +++++++++++++++++++ .../src/value-providers/definition.ts | 18 +++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/languageservice/src/complete.test.ts b/languageservice/src/complete.test.ts index 11a617d..9b6a2c7 100644 --- a/languageservice/src/complete.test.ts +++ b/languageservice/src/complete.test.ts @@ -493,4 +493,28 @@ jobs: expect(result.filter(x => x.label === "run-name").map(x => x.textEdit?.newText)).toEqual(["run-name: "]); }); + + it("adds new line for nested mapping", async () => { + const input = "on:\n workflow_dispatch: in|"; + + const result = await complete(...getPositionFromCursor(input)); + + expect(result.filter(x => x.label === "inputs").map(x => x.textEdit?.newText)).toEqual(["\n inputs:\n "]); + }); + + it("adds : for one-of", async () => { + const input = "on:\n check_run:\n ty|"; + + const result = await complete(...getPositionFromCursor(input)); + + expect(result.filter(x => x.label === "types").map(x => x.textEdit?.newText)).toEqual(["types: "]); + }); + + it("does not add : for one-of in key mode", async () => { + const input = "on:\n check_run: ty|"; + + const result = await complete(...getPositionFromCursor(input)); + + expect(result.filter(x => x.label === "types").map(x => x.textEdit?.newText)).toEqual(["types"]); + }); }); diff --git a/languageservice/src/value-providers/definition.ts b/languageservice/src/value-providers/definition.ts index 1745264..40c6ab0 100644 --- a/languageservice/src/value-providers/definition.ts +++ b/languageservice/src/value-providers/definition.ts @@ -76,15 +76,27 @@ function mappingValues( if (typeDef) { switch (typeDef.definitionType) { case DefinitionType.Sequence: - insertText = `${key}:\n${indentation}- `; + if (mode == DefinitionValueMode.Key) { + insertText = `\n${indentation}${key}:\n${indentation}${indentation}- `; + } else { + insertText = `${key}:\n${indentation}- `; + } break; case DefinitionType.Mapping: - insertText = `${key}:\n${indentation}`; + if (mode == DefinitionValueMode.Key) { + insertText = `\n${indentation}${key}:\n${indentation}${indentation}`; + } else { + insertText = `${key}:\n${indentation}`; + } break; case DefinitionType.OneOf: - // No special insertText in this case + if (mode == DefinitionValueMode.Parent) { + insertText = `${key}: `; + } else { + // No special insertText in this case + } break; case DefinitionType.String: From afbe42bffe7d55884187ef502af8a9774b3ef618 Mon Sep 17 00:00:00 2001 From: Jonathan Tamsut Date: Fri, 4 Aug 2023 12:13:21 -0700 Subject: [PATCH 3/3] fix test failures --- languageservice/src/complete.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/languageservice/src/complete.test.ts b/languageservice/src/complete.test.ts index 9b6a2c7..3971f96 100644 --- a/languageservice/src/complete.test.ts +++ b/languageservice/src/complete.test.ts @@ -406,7 +406,7 @@ jobs: expect(result.map(e => e.label)).toContain("runs-on"); const textEdit = result.filter(e => e.label === "runs-on")[0].textEdit as TextEdit; - expect(textEdit.newText).toEqual("runs-on"); + expect(textEdit.newText).toEqual("runs-on: "); expect(textEdit.range).toEqual({ start: {line: 3, character: 4}, end: {line: 3, character: 10} @@ -421,7 +421,7 @@ jobs: expect(result.map(e => e.label)).toContain("runs-on"); const textEdit = result.filter(e => e.label === "runs-on")[0].textEdit as TextEdit; - expect(textEdit.newText).toEqual("runs-on"); + expect(textEdit.newText).toEqual("runs-on: "); expect(textEdit.range).toEqual({ start: {line: 3, character: 4}, end: {line: 3, character: 4} @@ -448,7 +448,7 @@ jobs: ]); // One-of - expect(result.filter(x => x.label === "concurrency").map(x => x.textEdit?.newText)).toEqual(["concurrency"]); + expect(result.filter(x => x.label === "concurrency").map(x => x.textEdit?.newText)).toEqual(["concurrency: "]); }); it("custom indentation", async () => { @@ -471,7 +471,7 @@ jobs: ]); // One-of - expect(result.filter(x => x.label === "concurrency").map(x => x.textEdit?.newText)).toEqual(["concurrency"]); + expect(result.filter(x => x.label === "concurrency").map(x => x.textEdit?.newText)).toEqual(["concurrency: "]); }); });