diff --git a/languageserver/src/connection.ts b/languageserver/src/connection.ts index 846d696..ac330f0 100644 --- a/languageserver/src/connection.ts +++ b/languageserver/src/connection.ts @@ -1,7 +1,7 @@ -import { documentLinks, getCodeActions, hover, validate, ValidationConfig } from "@actions/languageservice"; -import { registerLogger, setLogLevel } from "@actions/languageservice/log"; -import { clearCache, clearCacheEntry } from "@actions/languageservice/utils/workflow-cache"; -import { Octokit } from "@octokit/rest"; +import {documentLinks, getCodeActions, hover, validate, ValidationConfig} from "@actions/languageservice"; +import {registerLogger, setLogLevel} from "@actions/languageservice/log"; +import {clearCache, clearCacheEntry} from "@actions/languageservice/utils/workflow-cache"; +import {Octokit} from "@octokit/rest"; import { CodeAction, CodeActionKind, @@ -20,19 +20,19 @@ import { TextDocuments, TextDocumentSyncKind } from "vscode-languageserver"; -import { TextDocument } from "vscode-languageserver-textdocument"; -import { getClient } from "./client"; -import { Commands } from "./commands"; -import { contextProviders } from "./context-providers"; -import { descriptionProvider } from "./description-provider"; -import { getFileProvider } from "./file-provider"; -import { InitializationOptions, RepositoryContext } from "./initializationOptions"; -import { onCompletion } from "./on-completion"; -import { ReadFileRequest, Requests } from "./request"; -import { getActionsMetadataProvider } from "./utils/action-metadata"; -import { TTLCache } from "./utils/cache"; -import { timeOperation } from "./utils/timer"; -import { valueProviders } from "./value-providers"; +import {TextDocument} from "vscode-languageserver-textdocument"; +import {getClient} from "./client"; +import {Commands} from "./commands"; +import {contextProviders} from "./context-providers"; +import {descriptionProvider} from "./description-provider"; +import {getFileProvider} from "./file-provider"; +import {InitializationOptions, RepositoryContext} from "./initializationOptions"; +import {onCompletion} from "./on-completion"; +import {ReadFileRequest, Requests} from "./request"; +import {getActionsMetadataProvider} from "./utils/action-metadata"; +import {TTLCache} from "./utils/cache"; +import {timeOperation} from "./utils/timer"; +import {valueProviders} from "./value-providers"; export function initConnection(connection: Connection) { const documents: TextDocuments = new TextDocuments(TextDocument); @@ -116,15 +116,15 @@ export function initConnection(connection: Connection) { contextProviderConfig: contextProviders(client, repoContext, cache), actionsMetadataProvider: getActionsMetadataProvider(client, cache), fileProvider: getFileProvider(client, cache, repoContext?.workspaceUri, async path => { - return await connection.sendRequest(Requests.ReadFile, { path } satisfies ReadFileRequest); + return await connection.sendRequest(Requests.ReadFile, {path} satisfies ReadFileRequest); }) }; const result = await validate(textDocument, config); - await connection.sendDiagnostics({ uri: textDocument.uri, diagnostics: result }); + await connection.sendDiagnostics({uri: textDocument.uri, diagnostics: result}); } - connection.onCompletion(async ({ position, textDocument }: TextDocumentPositionParams): Promise => { + connection.onCompletion(async ({position, textDocument}: TextDocumentPositionParams): Promise => { return timeOperation( "completion", async () => @@ -139,14 +139,14 @@ export function initConnection(connection: Connection) { ); }); - connection.onHover(async ({ position, textDocument }: HoverParams): Promise => { + connection.onHover(async ({position, textDocument}: HoverParams): Promise => { return timeOperation("hover", async () => { const repoContext = repos.find(repo => textDocument.uri.startsWith(repo.workspaceUri)); return await hover(getDocument(documents, textDocument), position, { descriptionProvider: descriptionProvider(client, cache), contextProviderConfig: repoContext && contextProviders(client, repoContext, cache), fileProvider: getFileProvider(client, cache, repoContext?.workspaceUri, async path => { - return await connection.sendRequest(Requests.ReadFile, { path }); + return await connection.sendRequest(Requests.ReadFile, {path}); }) }); }); @@ -159,7 +159,7 @@ export function initConnection(connection: Connection) { } }); - connection.onDocumentLinks(async ({ textDocument }: DocumentLinkParams): Promise => { + connection.onDocumentLinks(async ({textDocument}: DocumentLinkParams): Promise => { const repoContext = repos.find(repo => textDocument.uri.startsWith(repo.workspaceUri)); return documentLinks(getDocument(documents, textDocument), repoContext?.workspaceUri); }); @@ -169,7 +169,7 @@ export function initConnection(connection: Connection) { return getCodeActions({ uri: params.textDocument.uri, diagnostics: params.context.diagnostics, - only: params.context.only, + only: params.context.only }); }); }); diff --git a/languageserver/src/index.ts b/languageserver/src/index.ts index 8148a84..a109545 100644 --- a/languageserver/src/index.ts +++ b/languageserver/src/index.ts @@ -1,6 +1,6 @@ -import { Connection } from "vscode-languageserver"; +import {Connection} from "vscode-languageserver"; -import { initConnection } from "./connection.js"; +import {initConnection} from "./connection.js"; /** Helper function determining whether we are executing with node runtime */ function isNode(): boolean { @@ -9,10 +9,12 @@ function isNode(): boolean { async function getConnection(): Promise { if (isNode()) { - const { createConnection } = await import("vscode-languageserver/node.js"); + const {createConnection} = await import("vscode-languageserver/node.js"); return createConnection(); } else { - const { BrowserMessageReader, BrowserMessageWriter, createConnection } = await import("vscode-languageserver/browser.js"); + const {BrowserMessageReader, BrowserMessageWriter, createConnection} = await import( + "vscode-languageserver/browser.js" + ); const messageReader = new BrowserMessageReader(self); const messageWriter = new BrowserMessageWriter(self); return createConnection(messageReader, messageWriter); diff --git a/languageservice/src/code-actions/index.ts b/languageservice/src/code-actions/index.ts index 77fb2ec..3a85db1 100644 --- a/languageservice/src/code-actions/index.ts +++ b/languageservice/src/code-actions/index.ts @@ -1,10 +1,10 @@ -import { CodeAction, CodeActionKind, Diagnostic } from "vscode-languageserver-types"; -import { CodeActionContext, CodeActionProvider } from "./types"; -import { quickfixProviders } from "./quickfix"; +import {CodeAction, CodeActionKind, Diagnostic} from "vscode-languageserver-types"; +import {CodeActionContext, CodeActionProvider} from "./types"; +import {quickfixProviders} from "./quickfix"; // Aggregate all providers by kind const providersByKind: Map = new Map([ - [CodeActionKind.QuickFix, quickfixProviders], + [CodeActionKind.QuickFix, quickfixProviders] // [CodeActionKind. Refactor, refactorProviders], // [CodeActionKind.Source, sourceProviders], // etc @@ -23,14 +23,13 @@ export interface CodeActionParams { export function getCodeActions(params: CodeActionParams, config?: CodeActionConfig): CodeAction[] { const actions: CodeAction[] = []; const context: CodeActionContext = { - uri: params.uri, + uri: params.uri }; // Filter to requested kinds, or use all if none specified const requestedKinds = params.only; const kindsToCheck = requestedKinds - ? [...providersByKind.keys()].filter(kind => - requestedKinds.some(requested => kind.startsWith(requested))) + ? [...providersByKind.keys()].filter(kind => requestedKinds.some(requested => kind.startsWith(requested))) : [...providersByKind.keys()]; for (const diagnostic of params.diagnostics) { @@ -52,4 +51,4 @@ export function getCodeActions(params: CodeActionParams, config?: CodeActionConf return actions; } -export type { CodeActionContext, CodeActionProvider } from "./types"; +export type {CodeActionContext, CodeActionProvider} from "./types"; diff --git a/languageservice/src/code-actions/quickfix/add-missing-inputs.ts b/languageservice/src/code-actions/quickfix/add-missing-inputs.ts index 4aa4cfc..2b4a6a3 100644 --- a/languageservice/src/code-actions/quickfix/add-missing-inputs.ts +++ b/languageservice/src/code-actions/quickfix/add-missing-inputs.ts @@ -1,6 +1,6 @@ -import { CodeAction, TextEdit } from "vscode-languageserver-types"; -import { CodeActionContext, CodeActionProvider } from "../types"; -import { DiagnosticCode, MissingInputsDiagnosticData } from "../../validate-action"; +import {CodeAction, TextEdit} from "vscode-languageserver-types"; +import {CodeActionContext, CodeActionProvider} from "../types"; +import {DiagnosticCode, MissingInputsDiagnosticData} from "../../validate-action"; export const addMissingInputsProvider: CodeActionProvider = { diagnosticCodes: [DiagnosticCode.MissingRequiredInputs], @@ -22,11 +22,11 @@ export const addMissingInputsProvider: CodeActionProvider = { title: `Add missing input${data.missingInputs.length > 1 ? "s" : ""}: ${inputNames}`, edit: { changes: { - [context.uri]: edits, - }, - }, + [context.uri]: edits + } + } }; - }, + } }; function createInputEdits(data: MissingInputsDiagnosticData): TextEdit[] | undefined { @@ -42,8 +42,8 @@ function createInputEdits(data: MissingInputsDiagnosticData): TextEdit[] | undef }); edits.push({ - range: { start: data.insertPosition, end: data.insertPosition }, - newText: inputLines.map(line => line + "\n").join(""), + range: {start: data.insertPosition, end: data.insertPosition}, + newText: inputLines.map(line => line + "\n").join("") }); } else { // No `with:` key - use step indentation for `with:`, +2 for inputs @@ -58,8 +58,8 @@ function createInputEdits(data: MissingInputsDiagnosticData): TextEdit[] | undef const newText = [`${withIndent}with:\n`, ...inputLines.map(line => `${line}\n`)].join(""); edits.push({ - range: { start: data.insertPosition, end: data.insertPosition }, - newText, + range: {start: data.insertPosition, end: data.insertPosition}, + newText }); } diff --git a/languageservice/src/code-actions/quickfix/index.ts b/languageservice/src/code-actions/quickfix/index.ts index 6895e4c..a16d085 100644 --- a/languageservice/src/code-actions/quickfix/index.ts +++ b/languageservice/src/code-actions/quickfix/index.ts @@ -1,6 +1,4 @@ -import { CodeActionProvider } from "../types"; -import { addMissingInputsProvider } from "./add-missing-inputs"; +import {CodeActionProvider} from "../types"; +import {addMissingInputsProvider} from "./add-missing-inputs"; -export const quickfixProviders: CodeActionProvider[] = [ - addMissingInputsProvider, -]; +export const quickfixProviders: CodeActionProvider[] = [addMissingInputsProvider]; diff --git a/languageservice/src/code-actions/tests/runner.test.ts b/languageservice/src/code-actions/tests/runner.test.ts index ecab34b..397575d 100644 --- a/languageservice/src/code-actions/tests/runner.test.ts +++ b/languageservice/src/code-actions/tests/runner.test.ts @@ -1,9 +1,9 @@ import * as path from "path"; -import { fileURLToPath } from "url"; -import { loadTestCases, runTestCase } from "./runner"; -import { ValidationConfig } from "../../validate"; -import { ActionMetadata, ActionReference } from "../../action"; -import { clearCache } from "../../utils/workflow-cache"; +import {fileURLToPath} from "url"; +import {loadTestCases, runTestCase} from "./runner"; +import {ValidationConfig} from "../../validate"; +import {ActionMetadata, ActionReference} from "../../action"; +import {clearCache} from "../../utils/workflow-cache"; // ESM-compatible __dirname const __filename = fileURLToPath(import.meta.url); @@ -22,17 +22,17 @@ const validationConfig: ValidationConfig = { inputs: { path: { description: "A list of files to cache", - required: true, + required: true }, key: { description: "Cache key", - required: true, + required: true }, "restore-keys": { description: "Restore keys", - required: false, - }, - }, + required: false + } + } }, "actions/setup-node@v3": { name: "Setup Node", @@ -41,15 +41,15 @@ const validationConfig: ValidationConfig = { "node-version": { description: "Node version", required: true, - default: "16", - }, - }, - }, + default: "16" + } + } + } }; return Promise.resolve(metadata[key]); - }, - }, + } + } }; // Point to the source testdata directory diff --git a/languageservice/src/code-actions/tests/runner.ts b/languageservice/src/code-actions/tests/runner.ts index 410413c..5f057a2 100644 --- a/languageservice/src/code-actions/tests/runner.ts +++ b/languageservice/src/code-actions/tests/runner.ts @@ -1,9 +1,9 @@ import * as fs from "fs"; import * as path from "path"; -import { TextEdit } from "vscode-languageserver-types"; -import { TextDocument } from "vscode-languageserver-textdocument"; -import { validate, ValidationConfig } from "../../validate"; -import { getCodeActions, CodeActionParams } from "../index"; +import {TextEdit} from "vscode-languageserver-types"; +import {TextDocument} from "vscode-languageserver-textdocument"; +import {validate, ValidationConfig} from "../../validate"; +import {getCodeActions, CodeActionParams} from "../index"; // Marker pattern: # want "diagnostic message" fix="code-action-name" const MARKER_PATTERN = /#\s*want\s+"([^"]+)"(?:\s+fix="([^"]+)")?/; @@ -44,7 +44,7 @@ export function parseMarkers(content: string): Marker[] { markers.push({ line: i, message: match[1], - fix: match[2], + fix: match[2] }); } } @@ -69,7 +69,7 @@ export function loadTestCases(testdataDir: string): TestCase[] { const testCases: TestCase[] = []; function walkDir(dir: string) { - const entries = fs.readdirSync(dir, { withFileTypes: true }); + const entries = fs.readdirSync(dir, {withFileTypes: true}); for (const entry of entries) { const fullPath = path.join(dir, entry.name); @@ -89,7 +89,7 @@ export function loadTestCases(testdataDir: string): TestCase[] { goldenPath, input, golden, - markers: parseMarkers(input), + markers: parseMarkers(input) }); } } @@ -136,10 +136,7 @@ export function applyEdits(content: string, edits: TextEdit[]): string { /** * Run a single test case */ -export async function runTestCase( - testCase: TestCase, - validationConfig: ValidationConfig -): Promise { +export async function runTestCase(testCase: TestCase, validationConfig: ValidationConfig): Promise { const strippedInput = stripMarkers(testCase.input); const document = TextDocument.create("file:///test.yml", "yaml", 1, strippedInput); @@ -149,9 +146,7 @@ export async function runTestCase( // 2. Verify all expected diagnostics are present const missingDiagnostics: string[] = []; for (const marker of testCase.markers) { - const found = diagnostics.find( - d => d.range.start.line === marker.line && d.message.includes(marker.message) - ); + const found = diagnostics.find(d => d.range.start.line === marker.line && d.message.includes(marker.message)); if (!found) { missingDiagnostics.push(`line ${marker.line}: "${marker.message}"`); } @@ -161,7 +156,9 @@ export async function runTestCase( return { name: testCase.name, passed: false, - error: `Missing expected diagnostics:\n ${missingDiagnostics.join("\n ")}\n\nActual diagnostics:\n ${diagnostics.map(d => `line ${d.range.start.line}: "${d.message}"`).join("\n ")}`, + error: `Missing expected diagnostics:\n ${missingDiagnostics.join( + "\n " + )}\n\nActual diagnostics:\n ${diagnostics.map(d => `line ${d.range.start.line}: "${d.message}"`).join("\n ")}` }; } @@ -173,9 +170,7 @@ export async function runTestCase( continue; } - const diagnostic = diagnostics.find( - d => d.range.start.line === marker.line && d.message.includes(marker.message) - ); + const diagnostic = diagnostics.find(d => d.range.start.line === marker.line && d.message.includes(marker.message)); if (!diagnostic) { continue; // Already reported above @@ -183,19 +178,19 @@ export async function runTestCase( const params: CodeActionParams = { uri: document.uri, - diagnostics: [diagnostic], + diagnostics: [diagnostic] }; const actions = getCodeActions(params); - const matchingAction = actions.find(a => - a.title.toLowerCase().includes(marker.fix!.toLowerCase()) - ); + const matchingAction = actions.find(a => a.title.toLowerCase().includes(marker.fix!.toLowerCase())); if (!matchingAction) { return { name: testCase.name, passed: false, - error: `Code action "${marker.fix}" not found for diagnostic on line ${marker.line}.\nAvailable actions: ${actions.map(a => a.title).join(", ") || "(none)"}`, + error: `Code action "${marker.fix}" not found for diagnostic on line ${marker.line}.\nAvailable actions: ${ + actions.map(a => a.title).join(", ") || "(none)" + }` }; } @@ -203,7 +198,7 @@ export async function runTestCase( return { name: testCase.name, passed: false, - error: `Code action "${marker.fix}" has no edits`, + error: `Code action "${marker.fix}" has no edits` }; } @@ -221,12 +216,12 @@ export async function runTestCase( passed: false, error: "Output does not match golden file", expected: expectedOutput, - actual: actualOutput, + actual: actualOutput }; } return { name: testCase.name, - passed: true, + passed: true }; } diff --git a/languageservice/src/code-actions/types.ts b/languageservice/src/code-actions/types.ts index 1abde6e..92f7d20 100644 --- a/languageservice/src/code-actions/types.ts +++ b/languageservice/src/code-actions/types.ts @@ -1,4 +1,4 @@ -import { CodeAction, Diagnostic } from "vscode-languageserver-types"; +import {CodeAction, Diagnostic} from "vscode-languageserver-types"; export interface CodeActionContext { uri: string; diff --git a/languageservice/src/index.ts b/languageservice/src/index.ts index cce432d..0609916 100644 --- a/languageservice/src/index.ts +++ b/languageservice/src/index.ts @@ -1,8 +1,8 @@ -export { complete } from "./complete"; -export { ContextProviderConfig } from "./context-providers/config"; -export { documentLinks } from "./document-links"; -export { hover } from "./hover"; -export { Logger, LogLevel, registerLogger, setLogLevel } from "./log"; -export { validate, ValidationConfig, ActionsMetadataProvider } from "./validate"; -export { ValueProviderConfig, ValueProviderKind } from "./value-providers/config"; -export { getCodeActions } from "./code-actions"; +export {complete} from "./complete"; +export {ContextProviderConfig} from "./context-providers/config"; +export {documentLinks} from "./document-links"; +export {hover} from "./hover"; +export {Logger, LogLevel, registerLogger, setLogLevel} from "./log"; +export {validate, ValidationConfig, ActionsMetadataProvider} from "./validate"; +export {ValueProviderConfig, ValueProviderKind} from "./value-providers/config"; +export {getCodeActions} from "./code-actions"; diff --git a/languageservice/src/validate-action.ts b/languageservice/src/validate-action.ts index 147a656..ae9a4c2 100644 --- a/languageservice/src/validate-action.ts +++ b/languageservice/src/validate-action.ts @@ -1,15 +1,15 @@ -import { isMapping } from "@actions/workflow-parser"; -import { isActionStep } from "@actions/workflow-parser/model/type-guards"; -import { Step } from "@actions/workflow-parser/model/workflow-template"; -import { ScalarToken } from "@actions/workflow-parser/templates/tokens/scalar-token"; -import { TemplateToken } from "@actions/workflow-parser/templates/tokens/template-token"; -import { Diagnostic, DiagnosticSeverity } from "vscode-languageserver-types"; -import { ActionReference, parseActionReference } from "./action"; -import { mapRange } from "./utils/range"; -import { ValidationConfig } from "./validate"; +import {isMapping} from "@actions/workflow-parser"; +import {isActionStep} from "@actions/workflow-parser/model/type-guards"; +import {Step} from "@actions/workflow-parser/model/workflow-template"; +import {ScalarToken} from "@actions/workflow-parser/templates/tokens/scalar-token"; +import {TemplateToken} from "@actions/workflow-parser/templates/tokens/template-token"; +import {Diagnostic, DiagnosticSeverity} from "vscode-languageserver-types"; +import {ActionReference, parseActionReference} from "./action"; +import {mapRange} from "./utils/range"; +import {ValidationConfig} from "./validate"; export const DiagnosticCode = { - MissingRequiredInputs: "missing-required-inputs", + MissingRequiredInputs: "missing-required-inputs" } as const; export interface MissingInputsDiagnosticData { @@ -23,7 +23,7 @@ export interface MissingInputsDiagnosticData { withIndent?: number; stepIndent: number; // Position where new content should be inserted - insertPosition: { line: number; character: number }; + insertPosition: {line: number; character: number}; } export async function validateAction( @@ -53,7 +53,7 @@ export async function validateAction( let withKey: ScalarToken | undefined; let withToken: TemplateToken | undefined; - for (const { key, value } of stepToken) { + for (const {key, value} of stepToken) { if (key.toString() === "with") { withKey = key; withToken = value; @@ -63,7 +63,7 @@ export async function validateAction( const stepInputs = new Map(); if (withToken && isMapping(withToken)) { - for (const { key } of withToken) { + for (const {key} of withToken) { stepInputs.set(key.toString(), key); } } @@ -109,16 +109,16 @@ export async function validateAction( action, missingInputs: missingRequiredInputs.map(([name, input]) => ({ name, - default: input.default, + default: input.default })), hasWithKey: withKey !== undefined, withIndent, stepIndent, insertPosition: withToken?.range - ? { line: withToken.range.end.line - 1, character: 0 } + ? {line: withToken.range.end.line - 1, character: 0} : stepToken.range - ? { line: stepToken.range.end.line - 1, character: 0 } - : { line: 0, character: 0 }, + ? {line: stepToken.range.end.line - 1, character: 0} + : {line: 0, character: 0} }; diagnostics.push({ diff --git a/languageservice/src/validate.actions.test.ts b/languageservice/src/validate.actions.test.ts index 2074cd6..c7ec562 100644 --- a/languageservice/src/validate.actions.test.ts +++ b/languageservice/src/validate.actions.test.ts @@ -1,11 +1,11 @@ -import { DiagnosticSeverity } from "vscode-languageserver-types"; -import { ActionMetadata, ActionReference } from "./action"; -import { registerLogger } from "./log"; -import { createDocument } from "./test-utils/document"; -import { TestLogger } from "./test-utils/logger"; -import { validate, ValidationConfig } from "./validate"; -import { ValueProviderKind } from "./value-providers/config"; -import { clearCache } from "./utils/workflow-cache"; +import {DiagnosticSeverity} from "vscode-languageserver-types"; +import {ActionMetadata, ActionReference} from "./action"; +import {registerLogger} from "./log"; +import {createDocument} from "./test-utils/document"; +import {TestLogger} from "./test-utils/logger"; +import {validate, ValidationConfig} from "./validate"; +import {ValueProviderKind} from "./value-providers/config"; +import {clearCache} from "./utils/workflow-cache"; registerLogger(new TestLogger()); @@ -415,7 +415,7 @@ jobs: "step-with": { kind: ValueProviderKind.AllowedValues, get: () => { - return Promise.resolve([{ label: "repository", description: "Repository name with owner." }]); + return Promise.resolve([{label: "repository", description: "Repository name with owner."}]); } } };