From 2e8f4ea076a0c7504094beb3235c3d960e9177ba Mon Sep 17 00:00:00 2001 From: Beth Brennan Date: Mon, 21 Nov 2022 17:21:40 -0500 Subject: [PATCH 1/4] Show description and context on key hover --- actions-languageservice/src/hover.test.ts | 37 ++++++++++---- actions-languageservice/src/hover.ts | 48 +++++++++++-------- .../src/utils/find-token.ts | 20 +++++--- 3 files changed, 72 insertions(+), 33 deletions(-) diff --git a/actions-languageservice/src/hover.test.ts b/actions-languageservice/src/hover.test.ts index d67fbc3..ab2f5b3 100644 --- a/actions-languageservice/src/hover.test.ts +++ b/actions-languageservice/src/hover.test.ts @@ -1,12 +1,33 @@ +import {TextDocument} from "vscode-languageserver-textdocument"; +import {hover} from "./hover"; + describe("validation", () => { it("valid workflow", async () => { - // const result = await hover(null as any, { - // line: 1, - // character: 2, - // }); - // expect(result).not.toBeUndefined(); - // expect(result?.contents).toEqual( - // "The name of the GitHub event that triggers the workflow. You can provide a single event string, array of events, array of event types, or an event configuration map that schedules a workflow or restricts the execution of a workflow to specific files, tags, or branch changes. For a list of available events, see https://help.github.com/en/github/automating-your-workflow-with-github-actions/events-that-trigger-workflows." - // ); + const input = `on: push +jobs: + build: + runs-on: [self-hosted, u|]`; + const doc = TextDocument.create("test://test/test.yaml", "yaml", 0, input); + const result = await hover(doc, { + line: 0, + character: 0 + }); + expect(result).not.toBeUndefined(); + expect(result?.contents).toEqual( + "The name of the GitHub event that triggers the workflow. You can provide a single event string, array of events, array of event types, or an event configuration map that schedules a workflow or restricts the execution of a workflow to specific files, tags, or branch changes. For a list of available events, see https://help.github.com/en/github/automating-your-workflow-with-github-actions/events-that-trigger-workflows." + ); + }); + + it("hover on value", async () => { + const input = `on: push +jobs: + build: + runs-on: [self-hosted, u|]`; + const doc = TextDocument.create("test://test/test.yaml", "yaml", 0, input); + const result = await hover(doc, { + line: 0, + character: 5 + }); + expect(result?.contents).toBeUndefined(); }); }); diff --git a/actions-languageservice/src/hover.ts b/actions-languageservice/src/hover.ts index 9ec3856..8af5131 100644 --- a/actions-languageservice/src/hover.ts +++ b/actions-languageservice/src/hover.ts @@ -1,13 +1,14 @@ -import {parseWorkflow, ParseWorkflowResult} from "@github/actions-workflow-parser"; +import {parseWorkflow} from "@github/actions-workflow-parser"; import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/template-token"; import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token"; -import {SequenceToken} from "@github/actions-workflow-parser/templates/tokens/sequence-token"; import {File} from "@github/actions-workflow-parser/workflows/file"; import {Position, TextDocument} from "vscode-languageserver-textdocument"; import {Hover} from "vscode-languageserver-types"; import {nullTrace} from "./nulltrace"; -import {findInnerToken} from "./utils/find-token"; +import {findInnerToken, findToken} from "./utils/find-token"; +import {TokenType} from "@github/actions-workflow-parser/templates/tokens/types"; +// Render value description and Context when hovering over a key in a MappingToken export async function hover(document: TextDocument, position: Position): Promise { const file: File = { name: document.uri, @@ -15,38 +16,47 @@ export async function hover(document: TextDocument, position: Position): Promise }; const result = parseWorkflow(file.name, [file], nullTrace); - // Find inner token returns null if position is not in a token - const innerToken = findInnerToken(position, result.value); - if (result.value && innerToken) { - return getHover(innerToken); + const {token, keyToken, parent} = findToken(position, result.value); + + if (result.value && token) { + // If the parent is a MappingToken and no keyToken was returned, our token is the key + if (parent?.templateTokenType === TokenType.Mapping && !keyToken) { + const mappingToken = parent as MappingToken; + const value = mappingToken.find(token.toString()); + if (value) { + return getHover(token, value); + } + } } return null; } -function getHover(innerToken: TemplateToken): Hover | null { - if (innerToken.definition) { +// PositionToken is the token that the cursor is on +// DescriptionToken may differ if the description is stored on an associated token, such as when hovering over a key in a mapping +function getHover(positionToken: TemplateToken, descriptionToken: TemplateToken): Hover | null { + if (descriptionToken.definition) { let description = ""; - if (innerToken.description) { - description = innerToken.description; + if (descriptionToken.description) { + description = descriptionToken.description; } - if (innerToken.definition.evaluatorContext.length > 0) { + if (descriptionToken.definition.evaluatorContext.length > 0) { // Only add padding if there is a description - description += `${description.length > 0 ? `\n\n` : ""}**Context:** ${innerToken.definition.evaluatorContext.join( - ", " - )}`; + description += `${ + description.length > 0 ? `\n\n` : "" + }**Context:** ${descriptionToken.definition.evaluatorContext.join(", ")}`; } return { contents: description, range: { start: { - line: innerToken.range!.start[0], - character: innerToken.range!.start[1] + line: positionToken.range!.start[0] - 1, + character: positionToken.range!.start[1] - 1 }, end: { - line: innerToken.range!.end[0], - character: innerToken.range!.end[1] + line: positionToken.range!.end[0] - 1, + character: positionToken.range!.end[1] - 1 } } } as Hover; diff --git a/actions-languageservice/src/utils/find-token.ts b/actions-languageservice/src/utils/find-token.ts index 88e43ee..8d2cbfd 100644 --- a/actions-languageservice/src/utils/find-token.ts +++ b/actions-languageservice/src/utils/find-token.ts @@ -56,12 +56,20 @@ export function findToken(pos: Position, root?: TemplateToken): TokenResult { const {key, value} = mappingToken.get(i); if (onSameLine(pos, key, value)) { - if (posInToken(pos, key) && key.range!.end[1] + 1 === value.range!.start[1]) { - // There's no space between the key and value, there's nothing valid to complete here + 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 + }; + } + return { - token: null, + token: key, keyToken: null, - parent: null + parent: mappingToken }; } @@ -76,9 +84,9 @@ export function findToken(pos: Position, root?: TemplateToken): TokenResult { } s.push({ - parent: mappingToken, + token: value, keyToken: key, - token: value + parent: mappingToken }); } continue; From 4b644cbfd74e03a339d67fa8bdb1a9c8ee3088bd Mon Sep 17 00:00:00 2001 From: Beth Brennan Date: Mon, 21 Nov 2022 17:36:13 -0500 Subject: [PATCH 2/4] Remove testing code --- actions-languageserver/src/value-providers/job-environment.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions-languageserver/src/value-providers/job-environment.ts b/actions-languageserver/src/value-providers/job-environment.ts index 163503c..287368e 100644 --- a/actions-languageserver/src/value-providers/job-environment.ts +++ b/actions-languageserver/src/value-providers/job-environment.ts @@ -10,7 +10,7 @@ export async function getEnvironments( try { const response = await client.repos.getAllEnvironments({ owner, - repo: "bob", + repo: name, }); if (response.data.environments) { From 07b37d88a06d0dacea831c4bb035f3251d492b2e Mon Sep 17 00:00:00 2001 From: Beth Brennan Date: Tue, 22 Nov 2022 15:27:02 -0500 Subject: [PATCH 3/4] Consume parser version --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index bcdf091..398fa07 100644 --- a/package-lock.json +++ b/package-lock.json @@ -665,9 +665,9 @@ "link": true }, "node_modules/@github/actions-workflow-parser": { - "version": "0.0.13", - "resolved": "https://npm.pkg.github.com/download/@github/actions-workflow-parser/0.0.13/ff0fdf35ecf25145c56cb7d6d6b93b5a6b0774de", - "integrity": "sha512-kQGTQZUrBqK0qK2AlSrljyhzjPh/LPcX+cg6Tc3ZmQmoLlqgIm9bN30iC5JDKzsY7j3otedQQ2YfniFmCRBWRA==", + "version": "0.0.17", + "resolved": "https://npm.pkg.github.com/download/@github/actions-workflow-parser/0.0.17/72fabe0ef0e949f9414296f7ef0475d7e93b4ea7", + "integrity": "sha512-HG/HGOBhOMZOpO6jzXd/qNqX/28aO3Cl5r3/M8gU0FRuwzmc/op5Jtcycf9Sh9EHUMniquABuSW6MqczTE/Pfg==", "license": "MIT", "dependencies": { "@github/actions-expressions": "*", @@ -10927,9 +10927,9 @@ } }, "@github/actions-workflow-parser": { - "version": "0.0.13", - "resolved": "https://npm.pkg.github.com/download/@github/actions-workflow-parser/0.0.13/ff0fdf35ecf25145c56cb7d6d6b93b5a6b0774de", - "integrity": "sha512-kQGTQZUrBqK0qK2AlSrljyhzjPh/LPcX+cg6Tc3ZmQmoLlqgIm9bN30iC5JDKzsY7j3otedQQ2YfniFmCRBWRA==", + "version": "0.0.17", + "resolved": "https://npm.pkg.github.com/download/@github/actions-workflow-parser/0.0.17/72fabe0ef0e949f9414296f7ef0475d7e93b4ea7", + "integrity": "sha512-HG/HGOBhOMZOpO6jzXd/qNqX/28aO3Cl5r3/M8gU0FRuwzmc/op5Jtcycf9Sh9EHUMniquABuSW6MqczTE/Pfg==", "requires": { "@github/actions-expressions": "*", "yaml": "^2.0.0-8" From 0d1b951c141a0c68855fc0dfc444b7c0f9b699f9 Mon Sep 17 00:00:00 2001 From: Beth Brennan Date: Tue, 22 Nov 2022 16:03:01 -0500 Subject: [PATCH 4/4] Use isMapping --- actions-languageservice/src/hover.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/actions-languageservice/src/hover.ts b/actions-languageservice/src/hover.ts index 8af5131..388236e 100644 --- a/actions-languageservice/src/hover.ts +++ b/actions-languageservice/src/hover.ts @@ -1,12 +1,10 @@ -import {parseWorkflow} from "@github/actions-workflow-parser"; +import {isMapping, parseWorkflow} from "@github/actions-workflow-parser"; import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/template-token"; -import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token"; import {File} from "@github/actions-workflow-parser/workflows/file"; import {Position, TextDocument} from "vscode-languageserver-textdocument"; import {Hover} from "vscode-languageserver-types"; import {nullTrace} from "./nulltrace"; -import {findInnerToken, findToken} from "./utils/find-token"; -import {TokenType} from "@github/actions-workflow-parser/templates/tokens/types"; +import {findToken} from "./utils/find-token"; // Render value description and Context when hovering over a key in a MappingToken export async function hover(document: TextDocument, position: Position): Promise { @@ -20,9 +18,8 @@ export async function hover(document: TextDocument, position: Position): Promise if (result.value && token) { // If the parent is a MappingToken and no keyToken was returned, our token is the key - if (parent?.templateTokenType === TokenType.Mapping && !keyToken) { - const mappingToken = parent as MappingToken; - const value = mappingToken.find(token.toString()); + if (parent && isMapping(parent) && !keyToken) { + const value = parent.find(token.toString()); if (value) { return getHover(token, value); }