From bca45e9eaba15cec17de4a95ee9845f5d2957272 Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Tue, 22 Nov 2022 16:46:42 -0800 Subject: [PATCH 01/15] WIP --- .../src/utils/find-token.test.ts | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 actions-languageservice/src/utils/find-token.test.ts diff --git a/actions-languageservice/src/utils/find-token.test.ts b/actions-languageservice/src/utils/find-token.test.ts new file mode 100644 index 0000000..46fecb0 --- /dev/null +++ b/actions-languageservice/src/utils/find-token.test.ts @@ -0,0 +1,98 @@ +import {isScalar, parseWorkflow} from "@github/actions-workflow-parser/."; +import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/template-token"; +import {TokenType} from "@github/actions-workflow-parser/templates/tokens/types"; +import {nullTrace} from "../nulltrace"; +import {getPositionFromCursor} from "../test-utils/cursor-position"; +import {findToken} from "./find-token"; + +type testTokenInfo = [string | null, TokenType, string?]; + +function getTokenInfo(token: TemplateToken | null): testTokenInfo | null { + if (!token) { + return null; + } + + return [ + token.definition?.key ?? null, + token.templateTokenType, + isScalar(token) ? token.toDisplayString() : undefined + ].filter(x => x !== undefined) as testTokenInfo; +} + +function testFindToken(input: string): { + parent: testTokenInfo | null; + key: testTokenInfo | null; + token: testTokenInfo | null; +} { + const [textDocument, pos] = getPositionFromCursor(input); + const result = parseWorkflow( + "wf.yaml", + [ + { + content: textDocument.getText(), + name: "wf.yaml" + } + ], + nullTrace + ); + + const r = findToken(pos, result.value); + + return { + parent: getTokenInfo(r.parent), + key: getTokenInfo(r.keyToken), + token: getTokenInfo(r.token) + }; +} + +describe("find-token", () => { + it("on string key", () => { + expect(testFindToken(`o|n: push`)).toEqual({ + parent: ["workflow-root-strict", TokenType.Mapping], + key: null, + token: [null, TokenType.String, "on"] + }); + }); + + it("on string value", () => { + expect(testFindToken(`on: pu|sh`)).toEqual({ + parent: ["workflow-root-strict", TokenType.Mapping], + key: [null, TokenType.String, "on"], + token: ["on-strict", TokenType.String, "push"] + }); + }); + + it("on mapping", () => { + expect( + testFindToken(`on: + pu|sh:`) + ).toEqual({ + parent: ["on-strict", TokenType.Mapping], + key: ["push-event-mapping", TokenType.String, "push"], + token: ["on-strict", TokenType.Mapping] + }); + }); + + it("on sequence", () => { + expect( + testFindToken(`on: + - pu|sh`) + ).toEqual({ + parent: ["on-strict", TokenType.Sequence], + key: null, + token: ["non-empty-string", TokenType.String, "push"] + }); + }); + + it("jobs key", () => { + expect( + testFindToken(`on: push +jo|bs: + build:`) + ).toEqual({ + parent: ["workflow-root-strict", TokenType.Mapping], + key: null, + token: ["jobs", TokenType.Mapping] + }); + }); +}); From 3eabb5ad81b5de454b4b91ea29f2a2ccf74db7c9 Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Tue, 22 Nov 2022 16:53:01 -0800 Subject: [PATCH 02/15] Add some docs for findToken --- actions-languageservice/src/utils/find-token.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/actions-languageservice/src/utils/find-token.ts b/actions-languageservice/src/utils/find-token.ts index 8d2cbfd..4e840f2 100644 --- a/actions-languageservice/src/utils/find-token.ts +++ b/actions-languageservice/src/utils/find-token.ts @@ -16,6 +16,18 @@ export type TokenResult = { parent: TemplateToken | null; }; +/** + * Find a token at the given position in the document. + * + * If the position is within + * - the key of a mapping, parent will be the mapping, keyToken will be null, and token will be the key. + * - the value of a mapping, parent will be the mapping, keyToken will be the key for the value, and token will be the value + * - a sequence item, parent will be the sequence, keyToken will be null, and token will be the item + * + * @param pos Position within the document for which to find a token + * @param root Root node + * @returns Token result + */ export function findToken(pos: Position, root?: TemplateToken): TokenResult { if (!root) { return { From 9f5ced7bd3456fc49c60476a0b7baaabb876ddb1 Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Tue, 22 Nov 2022 17:01:59 -0800 Subject: [PATCH 03/15] Passing findToken tests --- .../src/utils/find-token.test.ts | 8 ++-- .../src/utils/find-token.ts | 39 +++++++------------ 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/actions-languageservice/src/utils/find-token.test.ts b/actions-languageservice/src/utils/find-token.test.ts index 46fecb0..20f0dec 100644 --- a/actions-languageservice/src/utils/find-token.test.ts +++ b/actions-languageservice/src/utils/find-token.test.ts @@ -67,9 +67,9 @@ describe("find-token", () => { testFindToken(`on: pu|sh:`) ).toEqual({ - parent: ["on-strict", TokenType.Mapping], - key: ["push-event-mapping", TokenType.String, "push"], - token: ["on-strict", TokenType.Mapping] + parent: ["on-mapping-strict", TokenType.Mapping], + key: null, + token: [null, TokenType.String, "push"] }); }); @@ -92,7 +92,7 @@ jo|bs: ).toEqual({ parent: ["workflow-root-strict", TokenType.Mapping], key: null, - token: ["jobs", TokenType.Mapping] + token: [null, TokenType.String, "jobs"] }); }); }); diff --git a/actions-languageservice/src/utils/find-token.ts b/actions-languageservice/src/utils/find-token.ts index 4e840f2..5e95694 100644 --- a/actions-languageservice/src/utils/find-token.ts +++ b/actions-languageservice/src/utils/find-token.ts @@ -67,32 +67,21 @@ export function findToken(pos: Position, root?: TemplateToken): TokenResult { for (let i = 0; i < mappingToken.count; i++) { const {key, value} = mappingToken.get(i); - if (onSameLine(pos, key, value)) { - if (posInToken(pos, key)) { - if (key.range!.end[1] + 1 === value.range!.start[1]) { - // There's no space between the key and value, this is not valid - return { - token: null, - keyToken: null, - parent: null - }; - } + if (posInToken(pos, key)) { + return { + token: key, + keyToken: null, + parent: mappingToken + }; + } - return { - token: key, - keyToken: null, - parent: mappingToken - }; - } - - // Empty nodes positions won't always match the cursor, so check if we're on the same line - if (emptyNode(value)) { - return { - token: value, - keyToken: null, - parent: key - }; - } + // Empty nodes positions won't always match the cursor, so check if we're on the same line + if (emptyNode(value)) { + return { + token: value, + keyToken: null, + parent: key + }; } s.push({ From 1962968440858e6810bd06e8bdede2565cabe5a6 Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Wed, 23 Nov 2022 06:26:44 -0800 Subject: [PATCH 04/15] Add more tests --- actions-languageservice/src/complete.test.ts | 23 +++++- .../src/utils/find-token.test.ts | 79 +++++++++++++++++++ .../src/utils/find-token.ts | 18 ++--- .../src/utils/transform.test.ts | 12 +++ .../src/utils/transform.ts | 20 ++++- 5 files changed, 138 insertions(+), 14 deletions(-) diff --git a/actions-languageservice/src/complete.test.ts b/actions-languageservice/src/complete.test.ts index bf10a08..910da01 100644 --- a/actions-languageservice/src/complete.test.ts +++ b/actions-languageservice/src/complete.test.ts @@ -157,7 +157,7 @@ jobs: const result = await complete(...getPositionFromCursor(input)); expect(result).not.toBeUndefined(); - expect(result.length).toEqual(0); + expect(result.length).toEqual(17); }); it("custom value providers override defaults", async () => { @@ -202,4 +202,25 @@ jobs: expect(result).not.toBeUndefined(); expect(result.map(x => x.label).sort()).toEqual(["cancel-in-progress", "group"]); }); + + it("job key", async () => { + const input = `on: push +jobs: + build: + runs-|`; + const result = await complete(...getPositionFromCursor(input)); + expect(result).not.toBeUndefined(); + expect(result).toHaveLength(20); + }); + + it("job key with comment afterwards", async () => { + const input = `on: push +jobs: + build: + runs-| + #`; + const result = await complete(...getPositionFromCursor(input)); + expect(result).not.toBeUndefined(); + expect(result).toHaveLength(20); + }); }); diff --git a/actions-languageservice/src/utils/find-token.test.ts b/actions-languageservice/src/utils/find-token.test.ts index 20f0dec..1325fb4 100644 --- a/actions-languageservice/src/utils/find-token.test.ts +++ b/actions-languageservice/src/utils/find-token.test.ts @@ -95,4 +95,83 @@ jo|bs: token: [null, TokenType.String, "jobs"] }); }); + + it("value in job", () => { + expect( + testFindToken(`on: push +jobs: + build: + runs-on: ubu|`) + ).toEqual({ + parent: ["job-factory", TokenType.Mapping], + key: [null, TokenType.String, "runs-on"], + token: ["runs-on", TokenType.String, "ubu"] + }); + }); + + it("key in job", () => { + expect( + testFindToken(`on: push +jobs: + build: + run|s-on: ubu`) + ).toEqual({ + parent: ["job-factory", TokenType.Mapping], + key: null, + token: [null, TokenType.String, "runs-on"] + }); + }); + + it("pos after colon in empty mapping", () => { + expect( + testFindToken(`on: push +jobs: + build: + continue-on-error:|`) + ).toEqual({ + parent: ["job-factory", TokenType.Mapping], + key: [null, TokenType.String, "continue-on-error"], + token: ["boolean-strategy-context", TokenType.Null, ""] + }); + }); + + it("pos after colon in mapping", () => { + expect( + testFindToken(`on: push +jobs: + build: + continue-on-error:|foo`) + ).toEqual({ + parent: ["jobs", TokenType.Mapping], + key: ["job-id", TokenType.String, "build"], + token: ["job", TokenType.String, "continue-on-error:foo"] + }); + }); + + it("pos in mapping key without comment", () => { + expect( + testFindToken(`on: push +jobs: + build: + runs-|`) + ).toEqual({ + parent: ["jobs", TokenType.Mapping], + key: ["job-id", TokenType.String, "build"], + token: ["job", TokenType.String, "runs-"] + }); + }); + + it("pos in mapping key before comment", () => { + expect( + testFindToken(`on: push +jobs: + build: + runs-| + #`) + ).toEqual({ + parent: ["jobs", TokenType.Mapping], + key: ["job-id", TokenType.String, "build"], + token: ["job", TokenType.String, "runs-"] + }); + }); }); diff --git a/actions-languageservice/src/utils/find-token.ts b/actions-languageservice/src/utils/find-token.ts index 5e95694..2bdf54b 100644 --- a/actions-languageservice/src/utils/find-token.ts +++ b/actions-languageservice/src/utils/find-token.ts @@ -69,25 +69,25 @@ export function findToken(pos: Position, root?: TemplateToken): TokenResult { if (posInToken(pos, key)) { return { - token: key, + parent: mappingToken, keyToken: null, - parent: mappingToken + token: key }; } // Empty nodes positions won't always match the cursor, so check if we're on the same line if (emptyNode(value)) { return { - token: value, - keyToken: null, - parent: key + parent: mappingToken, + keyToken: key, + token: value }; } s.push({ - token: value, + parent: mappingToken, keyToken: key, - parent: mappingToken + token: value }); } continue; @@ -96,9 +96,9 @@ export function findToken(pos: Position, root?: TemplateToken): TokenResult { const sequenceToken = token as SequenceToken; for (let i = 0; i < sequenceToken.count; i++) { s.push({ - token: sequenceToken.get(i), + parent: sequenceToken, keyToken: null, - parent: sequenceToken + token: sequenceToken.get(i) }); } continue; diff --git a/actions-languageservice/src/utils/transform.test.ts b/actions-languageservice/src/utils/transform.test.ts index e9b2339..b4200bd 100644 --- a/actions-languageservice/src/utils/transform.test.ts +++ b/actions-languageservice/src/utils/transform.test.ts @@ -13,6 +13,18 @@ jobs: expect(newPos.character).toEqual(11); }); + it("adds : at end of line with trailing comment", () => { + const [doc, pos] = getPositionFromCursor("on: push\njobs:\n build:\n runs-on| \n#"); + const [newDoc, newPos] = transform(doc, pos); + + expect(newDoc.getText()).toEqual(`on: push +jobs: + build: + runs-on: +#`); + expect(newPos.character).toEqual(11); + }); + it("adds placeholder node in empty sequence", () => { const [doc, pos] = getPositionFromCursor(`on: push jobs: diff --git a/actions-languageservice/src/utils/transform.ts b/actions-languageservice/src/utils/transform.ts index 4f8591e..a6c831d 100644 --- a/actions-languageservice/src/utils/transform.ts +++ b/actions-languageservice/src/utils/transform.ts @@ -1,4 +1,5 @@ import {Position, TextDocument} from "vscode-languageserver-textdocument"; +import {Range} from "vscode-languageserver-types"; const DUMMY_KEY = "dummy"; @@ -6,10 +7,21 @@ const DUMMY_KEY = "dummy"; // Based on `_transform` in https://github.com/cschleiden/github-actions-parser/blob/main/src/lib/parser/complete.ts#L311 export function transform(doc: TextDocument, pos: Position): [TextDocument, Position] { let offset = doc.offsetAt(pos); - let line = doc.getText({ + + const lineRange: Range = { start: {line: pos.line, character: 0}, end: {line: pos.line, character: Number.MAX_SAFE_INTEGER} - }); + }; + + let line = doc.getText(lineRange); + + // If the line includes a new-line char, strip that out + const newLinePos = line.indexOf("\n"); + if (newLinePos >= 0) { + line = line.substring(0, newLinePos); + } + lineRange.end.character = line.length; + const linePos = pos.character; // Special case for Actions, if this line contains an expression marker, do _not_ transform. This is @@ -34,7 +46,7 @@ export function transform(doc: TextDocument, pos: Position): [TextDocument, Posi offset++; } else if (!trimmedLine.startsWith("-")) { // Add `:` to end of line - line = line + ":"; + line = line.trimEnd() + ":"; } } else { offset = offset - 1; @@ -47,7 +59,7 @@ export function transform(doc: TextDocument, pos: Position): [TextDocument, Posi newDoc, [ { - range: {start: {line: pos.line, character: 0}, end: {line: pos.line, character: Number.MAX_SAFE_INTEGER}}, + range: lineRange, text: line } ], From 1e339cb732a34a0405646efa071e6587bf89663d Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Wed, 23 Nov 2022 07:08:50 -0800 Subject: [PATCH 05/15] Fix `:|` edge case --- actions-languageservice/src/complete.test.ts | 2 +- actions-languageservice/src/complete.ts | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/actions-languageservice/src/complete.test.ts b/actions-languageservice/src/complete.test.ts index 910da01..4d032d9 100644 --- a/actions-languageservice/src/complete.test.ts +++ b/actions-languageservice/src/complete.test.ts @@ -157,7 +157,7 @@ jobs: const result = await complete(...getPositionFromCursor(input)); expect(result).not.toBeUndefined(); - expect(result.length).toEqual(17); + expect(result.length).toEqual(0); }); it("custom value providers override defaults", async () => { diff --git a/actions-languageservice/src/complete.ts b/actions-languageservice/src/complete.ts index 15f1439..733ade0 100644 --- a/actions-languageservice/src/complete.ts +++ b/actions-languageservice/src/complete.ts @@ -40,6 +40,15 @@ export async function complete( valueProviderConfig?: ValueProviderConfig, contextProviderConfig?: ContextProviderConfig ): Promise { + // Edge case: when completing a key like `foo:|`, do not calculate auto-completions + const charBeforePos = textDocument.getText({ + start: {line: position.line, character: position.character - 1}, + end: {line: position.line, character: position.character} + }); + if (charBeforePos === ":") { + return []; + } + // Fix the input to work around YAML parsing issues const [newDoc, newPos] = transform(textDocument, position); From 99a9f08a3cb9eeabef5f25a311118d2cda01dd81 Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Wed, 23 Nov 2022 07:35:43 -0800 Subject: [PATCH 06/15] Add more tests --- actions-languageservice/src/complete.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/actions-languageservice/src/complete.test.ts b/actions-languageservice/src/complete.test.ts index 4d032d9..30c2a4d 100644 --- a/actions-languageservice/src/complete.test.ts +++ b/actions-languageservice/src/complete.test.ts @@ -223,4 +223,16 @@ jobs: expect(result).not.toBeUndefined(); expect(result).toHaveLength(20); }); + + it("job key with other values afterwards", async () => { + const input = `on: push +jobs: + build: + runs-| + + concurrency: 'group-name'`; + const result = await complete(...getPositionFromCursor(input)); + expect(result).not.toBeUndefined(); + expect(result).toHaveLength(19); + }); }); From 190192438764dd56f9236bb1f703fcce9dd6fca9 Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Wed, 23 Nov 2022 07:43:29 -0800 Subject: [PATCH 07/15] Add failing test for empty node scenario --- .../src/utils/find-token.test.ts | 14 ++++++++++++++ actions-languageservice/src/utils/find-token.ts | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/actions-languageservice/src/utils/find-token.test.ts b/actions-languageservice/src/utils/find-token.test.ts index 1325fb4..29e8812 100644 --- a/actions-languageservice/src/utils/find-token.test.ts +++ b/actions-languageservice/src/utils/find-token.test.ts @@ -174,4 +174,18 @@ jobs: token: ["job", TokenType.String, "runs-"] }); }); + + it("empty node", () => { + expect( + testFindToken(`on: push +jobs: + build: + concurrency: + runs-on: ubu|`) + ).toEqual({ + parent: ["job-factory", TokenType.Mapping], + key: [null, TokenType.String, "runs-on"], + token: ["runs-on", TokenType.String, "ubu"] + }); + }); }); diff --git a/actions-languageservice/src/utils/find-token.ts b/actions-languageservice/src/utils/find-token.ts index 2bdf54b..d522172 100644 --- a/actions-languageservice/src/utils/find-token.ts +++ b/actions-languageservice/src/utils/find-token.ts @@ -67,6 +67,7 @@ export function findToken(pos: Position, root?: TemplateToken): TokenResult { for (let i = 0; i < mappingToken.count; i++) { const {key, value} = mappingToken.get(i); + // If the position is within the key, immediately return it as the token. if (posInToken(pos, key)) { return { parent: mappingToken, @@ -75,7 +76,7 @@ export function findToken(pos: Position, root?: TemplateToken): TokenResult { }; } - // Empty nodes positions won't always match the cursor, so check if we're on the same line + // If the value is an empty node (null, empty string) if (emptyNode(value)) { return { parent: mappingToken, From 2843bdc2d849020046f5ae04db2d816807a69da0 Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Wed, 23 Nov 2022 07:58:05 -0800 Subject: [PATCH 08/15] Fix empty node test --- actions-languageservice/src/utils/find-token.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/actions-languageservice/src/utils/find-token.ts b/actions-languageservice/src/utils/find-token.ts index d522172..4e9c9a1 100644 --- a/actions-languageservice/src/utils/find-token.ts +++ b/actions-languageservice/src/utils/find-token.ts @@ -76,8 +76,9 @@ export function findToken(pos: Position, root?: TemplateToken): TokenResult { }; } - // If the value is an empty node (null, empty string) - if (emptyNode(value)) { + // If pos, key, and value are on the same line, and value is an empty node (null, empty string) return early + // we cannot reliably check the position in that empty node + if (onSameLine(pos, key, value) && emptyNode(value)) { return { parent: mappingToken, keyToken: key, From 28c2cd42a146e0bbcb8651074667e17b94e7c86f Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Wed, 23 Nov 2022 08:11:59 -0800 Subject: [PATCH 09/15] Test for step key --- actions-languageservice/src/complete.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/actions-languageservice/src/complete.test.ts b/actions-languageservice/src/complete.test.ts index 30c2a4d..8c6959e 100644 --- a/actions-languageservice/src/complete.test.ts +++ b/actions-languageservice/src/complete.test.ts @@ -235,4 +235,16 @@ jobs: expect(result).not.toBeUndefined(); expect(result).toHaveLength(19); }); + + it("step key without space after colon", async () => { + const input = `on: push +jobs: + build: + runs-on: ubuntu-latest + steps: + - env:| + run: echo`; + const result = await complete(...getPositionFromCursor(input)); + expect(result).toHaveLength(0); + }); }); From 1b6b7423518cfee32cf1f68590c9a7d6ef1d1187 Mon Sep 17 00:00:00 2001 From: Josh Gross Date: Wed, 23 Nov 2022 11:35:13 -0500 Subject: [PATCH 10/15] Add more sequence tests --- .../src/utils/find-token.test.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/actions-languageservice/src/utils/find-token.test.ts b/actions-languageservice/src/utils/find-token.test.ts index 29e8812..4d657ab 100644 --- a/actions-languageservice/src/utils/find-token.test.ts +++ b/actions-languageservice/src/utils/find-token.test.ts @@ -84,6 +84,42 @@ describe("find-token", () => { }); }); + it("on sequence with cursor outside of sequence values", () => { + expect( + testFindToken(`on: + -| push`) + ).toEqual({ + parent: ["on-strict", TokenType.Sequence], + key: null, + token: null + }); + }); + + it("on sequence with multiple values", () => { + expect( + testFindToken(`on: + - push + - pull_request|`) + ).toEqual({ + parent: ["on-strict", TokenType.Sequence], + key: null, + token: ["non-empty-string", TokenType.String, "pull_request"] + }); + }); + + it("single-line sequence with multiple values", () => { + expect( + testFindToken(`on: push +jobs: + build: + runs-on: [ubuntu-latest, self|`) + ).toEqual({ + parent: ["runs-on", TokenType.Sequence], + key: null, + token: ["non-empty-string", TokenType.String, "self"] + }); + }); + it("jobs key", () => { expect( testFindToken(`on: push From 353f4db6741c86aff1876e51024b3efad3b8961d Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Wed, 23 Nov 2022 09:08:03 -0800 Subject: [PATCH 11/15] Name the tuple values --- actions-languageservice/src/utils/find-token.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions-languageservice/src/utils/find-token.test.ts b/actions-languageservice/src/utils/find-token.test.ts index 4d657ab..74cb9e2 100644 --- a/actions-languageservice/src/utils/find-token.test.ts +++ b/actions-languageservice/src/utils/find-token.test.ts @@ -5,7 +5,7 @@ import {nullTrace} from "../nulltrace"; import {getPositionFromCursor} from "../test-utils/cursor-position"; import {findToken} from "./find-token"; -type testTokenInfo = [string | null, TokenType, string?]; +type testTokenInfo = [definitionKey: string | null, tokenType: TokenType, literalValue?: string]; function getTokenInfo(token: TemplateToken | null): testTokenInfo | null { if (!token) { From 109d9bc1be913c09661682413351ceb0f28770ea Mon Sep 17 00:00:00 2001 From: Josh Gross Date: Wed, 23 Nov 2022 14:30:36 -0500 Subject: [PATCH 12/15] Add empty string mapping test case --- .../src/utils/find-token.test.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/actions-languageservice/src/utils/find-token.test.ts b/actions-languageservice/src/utils/find-token.test.ts index 74cb9e2..37cd3da 100644 --- a/actions-languageservice/src/utils/find-token.test.ts +++ b/actions-languageservice/src/utils/find-token.test.ts @@ -158,7 +158,7 @@ jobs: }); }); - it("pos after colon in empty mapping", () => { + it("pos after colon in empty null mapping ", () => { expect( testFindToken(`on: push jobs: @@ -171,6 +171,19 @@ jobs: }); }); + it("pos after colon in empty string mapping", () => { + expect( + testFindToken(`on: push +jobs: + build: + container:|`) + ).toEqual({ + parent: ["job-factory", TokenType.Mapping], + key: [null, TokenType.String, "container"], + token: ["container", TokenType.String, ""] + }); + }); + it("pos after colon in mapping", () => { expect( testFindToken(`on: push From 20e99a225cc2e3a0737e59d0e37be4e4e90cffd7 Mon Sep 17 00:00:00 2001 From: Josh Gross Date: Wed, 23 Nov 2022 15:23:17 -0500 Subject: [PATCH 13/15] Add more mapping key test cases --- .../src/utils/find-token.test.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/actions-languageservice/src/utils/find-token.test.ts b/actions-languageservice/src/utils/find-token.test.ts index 37cd3da..45fdae3 100644 --- a/actions-languageservice/src/utils/find-token.test.ts +++ b/actions-languageservice/src/utils/find-token.test.ts @@ -197,6 +197,32 @@ jobs: }); }); + it("pos after mapping key", () => { + expect( + testFindToken(`on: push +jobs: + build: + continue-on-error:| foo`) + ).toEqual({ + parent: ["job-factory", TokenType.Mapping], + key: null, + token: null + }); + }); + + it("pos at end of completed mapping key", () => { + expect( + testFindToken(`on: push +jobs: + build: + continue-on-error|: foo`) + ).toEqual({ + parent: ["job-factory", TokenType.Mapping], + key: null, + token: [null, TokenType.String, "continue-on-error"] + }); + }); + it("pos in mapping key without comment", () => { expect( testFindToken(`on: push From df92695981d32608f85c64fdc02fd73ca23e737b Mon Sep 17 00:00:00 2001 From: Josh Gross Date: Wed, 23 Nov 2022 15:34:27 -0500 Subject: [PATCH 14/15] Add `parentKey` to tests --- .../src/utils/find-token.test.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/actions-languageservice/src/utils/find-token.test.ts b/actions-languageservice/src/utils/find-token.test.ts index 45fdae3..6ae9757 100644 --- a/actions-languageservice/src/utils/find-token.test.ts +++ b/actions-languageservice/src/utils/find-token.test.ts @@ -23,6 +23,7 @@ function testFindToken(input: string): { parent: testTokenInfo | null; key: testTokenInfo | null; token: testTokenInfo | null; + parentKey: testTokenInfo | null; } { const [textDocument, pos] = getPositionFromCursor(input); const result = parseWorkflow( @@ -40,6 +41,7 @@ function testFindToken(input: string): { return { parent: getTokenInfo(r.parent), + parentKey: getTokenInfo(r.parentKey), key: getTokenInfo(r.keyToken), token: getTokenInfo(r.token) }; @@ -49,6 +51,7 @@ describe("find-token", () => { it("on string key", () => { expect(testFindToken(`o|n: push`)).toEqual({ parent: ["workflow-root-strict", TokenType.Mapping], + parentKey: null, key: null, token: [null, TokenType.String, "on"] }); @@ -57,6 +60,7 @@ describe("find-token", () => { it("on string value", () => { expect(testFindToken(`on: pu|sh`)).toEqual({ parent: ["workflow-root-strict", TokenType.Mapping], + parentKey: null, key: [null, TokenType.String, "on"], token: ["on-strict", TokenType.String, "push"] }); @@ -68,6 +72,7 @@ describe("find-token", () => { pu|sh:`) ).toEqual({ parent: ["on-mapping-strict", TokenType.Mapping], + parentKey: [null, TokenType.String, "on"], key: null, token: [null, TokenType.String, "push"] }); @@ -79,6 +84,7 @@ describe("find-token", () => { - pu|sh`) ).toEqual({ parent: ["on-strict", TokenType.Sequence], + parentKey: null, key: null, token: ["non-empty-string", TokenType.String, "push"] }); @@ -90,6 +96,7 @@ describe("find-token", () => { -| push`) ).toEqual({ parent: ["on-strict", TokenType.Sequence], + parentKey: null, key: null, token: null }); @@ -102,6 +109,7 @@ describe("find-token", () => { - pull_request|`) ).toEqual({ parent: ["on-strict", TokenType.Sequence], + parentKey: null, key: null, token: ["non-empty-string", TokenType.String, "pull_request"] }); @@ -115,6 +123,7 @@ jobs: runs-on: [ubuntu-latest, self|`) ).toEqual({ parent: ["runs-on", TokenType.Sequence], + parentKey: null, key: null, token: ["non-empty-string", TokenType.String, "self"] }); @@ -127,6 +136,7 @@ jo|bs: build:`) ).toEqual({ parent: ["workflow-root-strict", TokenType.Mapping], + parentKey: null, key: null, token: [null, TokenType.String, "jobs"] }); @@ -140,6 +150,7 @@ jobs: runs-on: ubu|`) ).toEqual({ parent: ["job-factory", TokenType.Mapping], + parentKey: ["job-id", TokenType.String, "build"], key: [null, TokenType.String, "runs-on"], token: ["runs-on", TokenType.String, "ubu"] }); @@ -153,6 +164,7 @@ jobs: run|s-on: ubu`) ).toEqual({ parent: ["job-factory", TokenType.Mapping], + parentKey: ["job-id", TokenType.String, "build"], key: null, token: [null, TokenType.String, "runs-on"] }); @@ -166,6 +178,7 @@ jobs: continue-on-error:|`) ).toEqual({ parent: ["job-factory", TokenType.Mapping], + parentKey: ["job-id", TokenType.String, "build"], key: [null, TokenType.String, "continue-on-error"], token: ["boolean-strategy-context", TokenType.Null, ""] }); @@ -179,6 +192,7 @@ jobs: container:|`) ).toEqual({ parent: ["job-factory", TokenType.Mapping], + parentKey: ["job-id", TokenType.String, "build"], key: [null, TokenType.String, "container"], token: ["container", TokenType.String, ""] }); @@ -192,6 +206,7 @@ jobs: continue-on-error:|foo`) ).toEqual({ parent: ["jobs", TokenType.Mapping], + parentKey: [null, TokenType.String, "jobs"], key: ["job-id", TokenType.String, "build"], token: ["job", TokenType.String, "continue-on-error:foo"] }); @@ -205,6 +220,7 @@ jobs: continue-on-error:| foo`) ).toEqual({ parent: ["job-factory", TokenType.Mapping], + parentKey: null, key: null, token: null }); @@ -218,6 +234,7 @@ jobs: continue-on-error|: foo`) ).toEqual({ parent: ["job-factory", TokenType.Mapping], + parentKey: ["job-id", TokenType.String, "build"], key: null, token: [null, TokenType.String, "continue-on-error"] }); @@ -231,6 +248,7 @@ jobs: runs-|`) ).toEqual({ parent: ["jobs", TokenType.Mapping], + parentKey: [null, TokenType.String, "jobs"], key: ["job-id", TokenType.String, "build"], token: ["job", TokenType.String, "runs-"] }); @@ -245,6 +263,7 @@ jobs: #`) ).toEqual({ parent: ["jobs", TokenType.Mapping], + parentKey: [null, TokenType.String, "jobs"], key: ["job-id", TokenType.String, "build"], token: ["job", TokenType.String, "runs-"] }); @@ -259,6 +278,7 @@ jobs: runs-on: ubu|`) ).toEqual({ parent: ["job-factory", TokenType.Mapping], + parentKey: ["job-id", TokenType.String, "build"], key: [null, TokenType.String, "runs-on"], token: ["runs-on", TokenType.String, "ubu"] }); From a6cc41d6ffcd77ba75c0e96dbd01b27a4f302bf5 Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Wed, 23 Nov 2022 14:09:05 -0800 Subject: [PATCH 15/15] Remove trimEnd() call when transformign --- actions-languageservice/src/utils/transform.test.ts | 2 +- actions-languageservice/src/utils/transform.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actions-languageservice/src/utils/transform.test.ts b/actions-languageservice/src/utils/transform.test.ts index b4200bd..ba4d85d 100644 --- a/actions-languageservice/src/utils/transform.test.ts +++ b/actions-languageservice/src/utils/transform.test.ts @@ -14,7 +14,7 @@ jobs: }); it("adds : at end of line with trailing comment", () => { - const [doc, pos] = getPositionFromCursor("on: push\njobs:\n build:\n runs-on| \n#"); + const [doc, pos] = getPositionFromCursor("on: push\njobs:\n build:\n runs-on|\n#"); const [newDoc, newPos] = transform(doc, pos); expect(newDoc.getText()).toEqual(`on: push diff --git a/actions-languageservice/src/utils/transform.ts b/actions-languageservice/src/utils/transform.ts index a6c831d..015f709 100644 --- a/actions-languageservice/src/utils/transform.ts +++ b/actions-languageservice/src/utils/transform.ts @@ -46,7 +46,7 @@ export function transform(doc: TextDocument, pos: Position): [TextDocument, Posi offset++; } else if (!trimmedLine.startsWith("-")) { // Add `:` to end of line - line = line.trimEnd() + ":"; + line = line + ":"; } } else { offset = offset - 1;