Merge pull request #3 from github/joshmgross/prettier
Run prettier on language service
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
dist
|
||||
*.md
|
||||
*.js
|
||||
*.json
|
||||
@@ -6,4 +6,4 @@
|
||||
"bracketSpacing": false,
|
||||
"trailingComma": "none",
|
||||
"arrowParens": "avoid"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,9 @@
|
||||
"prepublishOnly": "npm run build && npm run test",
|
||||
"test": "NODE_OPTIONS=\"--experimental-vm-modules\" jest",
|
||||
"test-watch": "NODE_OPTIONS=\"--experimental-vm-modules\" jest --watch",
|
||||
"watch": "tsc --build tsconfig.build.json --watch"
|
||||
"watch": "tsc --build tsconfig.build.json --watch",
|
||||
"prettier": "prettier .",
|
||||
"prettier-fix": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@github/actions-workflow-parser": "*",
|
||||
@@ -56,4 +58,4 @@
|
||||
"ts-jest": "^29.0.3",
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
import { complete } from "./complete";
|
||||
import { getPositionFromCursor } from "./test-utils/cursor-position";
|
||||
import {
|
||||
Value,
|
||||
ValueProviderConfig,
|
||||
WorkflowContext,
|
||||
} from "./value-providers/config";
|
||||
import {complete} from "./complete";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position";
|
||||
import {Value, ValueProviderConfig, WorkflowContext} from "./value-providers/config";
|
||||
|
||||
describe("completion", () => {
|
||||
it("runs-on", async () => {
|
||||
@@ -69,7 +65,7 @@ jobs:
|
||||
|`;
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result.map((x) => x.label)).not.toContain("runs-on");
|
||||
expect(result.map(x => x.label)).not.toContain("runs-on");
|
||||
});
|
||||
|
||||
it("one-of narrows down to a specific type", async () => {
|
||||
@@ -80,22 +76,18 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|`;
|
||||
const jobFactoryResult = await complete(
|
||||
...getPositionFromCursor(jobFactory)
|
||||
);
|
||||
const jobFactoryResult = await complete(...getPositionFromCursor(jobFactory));
|
||||
expect(jobFactoryResult).not.toBeUndefined();
|
||||
expect(jobFactoryResult.map((x) => x.label)).not.toContain("uses");
|
||||
expect(jobFactoryResult.map(x => x.label)).not.toContain("uses");
|
||||
|
||||
const workflowJob = `on: push
|
||||
jobs:
|
||||
build:
|
||||
uses: octo-org/this-repo/.github/workflows/workflow-1.yml@172239021f7ba04fe7327647b213799853a9eb89
|
||||
|`;
|
||||
const workflowJobResult = await complete(
|
||||
...getPositionFromCursor(workflowJob)
|
||||
);
|
||||
const workflowJobResult = await complete(...getPositionFromCursor(workflowJob));
|
||||
expect(workflowJobResult).not.toBeUndefined();
|
||||
expect(workflowJobResult.map((x) => x.label)).not.toContain("runs-on");
|
||||
expect(workflowJobResult.map(x => x.label)).not.toContain("runs-on");
|
||||
});
|
||||
|
||||
it("completes boolean values", async () => {
|
||||
@@ -108,7 +100,7 @@ jobs:
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result.length).toEqual(2);
|
||||
|
||||
expect(result.map((x) => x.label).sort()).toEqual(["false", "true"]);
|
||||
expect(result.map(x => x.label).sort()).toEqual(["false", "true"]);
|
||||
});
|
||||
|
||||
it("completes for empty map values", async () => {
|
||||
@@ -121,7 +113,7 @@ jobs:
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result.length).toEqual(2);
|
||||
|
||||
expect(result.map((x) => x.label).sort()).toEqual(["false", "true"]);
|
||||
expect(result.map(x => x.label).sort()).toEqual(["false", "true"]);
|
||||
});
|
||||
|
||||
it("does not complete empty map values when cursor is immediately after the position", async () => {
|
||||
@@ -138,17 +130,14 @@ jobs:
|
||||
it("custom value providers override defaults", async () => {
|
||||
const input = "on: push\njobs:\n build:\n runs-on: |";
|
||||
|
||||
const getCustomValues = async (
|
||||
key: string,
|
||||
_: WorkflowContext
|
||||
): Promise<Value[] | undefined> => {
|
||||
const getCustomValues = async (key: string, _: WorkflowContext): Promise<Value[] | undefined> => {
|
||||
if (key === "runs-on") {
|
||||
return [{ label: "my-custom-label" }];
|
||||
return [{label: "my-custom-label"}];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
const config: ValueProviderConfig = {
|
||||
getCustomValues: getCustomValues,
|
||||
getCustomValues: getCustomValues
|
||||
};
|
||||
const result = await complete(...getPositionFromCursor(input), config);
|
||||
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import { parseWorkflow } from "@github/actions-workflow-parser";
|
||||
import {parseWorkflow} from "@github/actions-workflow-parser";
|
||||
import {
|
||||
SEQUENCE_TYPE,
|
||||
STRING_TYPE,
|
||||
MAPPING_TYPE,
|
||||
TemplateToken,
|
||||
NULL_TYPE,
|
||||
NULL_TYPE
|
||||
} from "@github/actions-workflow-parser/templates/tokens/index";
|
||||
import { MappingToken } from "@github/actions-workflow-parser/templates/tokens/mapping-token";
|
||||
import { SequenceToken } from "@github/actions-workflow-parser/templates/tokens/sequence-token";
|
||||
import { StringToken } from "@github/actions-workflow-parser/templates/tokens/string-token";
|
||||
import { File } from "@github/actions-workflow-parser/workflows/file";
|
||||
import { Position, TextDocument } from "vscode-languageserver-textdocument";
|
||||
import { CompletionItem } from "vscode-languageserver-types";
|
||||
import { nullTrace } from "./nulltrace";
|
||||
import { findInnerTokenAndParent } from "./utils/find-token";
|
||||
import { transform } from "./utils/transform";
|
||||
import { Value, ValueProviderConfig } from "./value-providers/config";
|
||||
import { defaultValueProviders } from "./value-providers/default";
|
||||
import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token";
|
||||
import {SequenceToken} from "@github/actions-workflow-parser/templates/tokens/sequence-token";
|
||||
import {StringToken} from "@github/actions-workflow-parser/templates/tokens/string-token";
|
||||
import {File} from "@github/actions-workflow-parser/workflows/file";
|
||||
import {Position, TextDocument} from "vscode-languageserver-textdocument";
|
||||
import {CompletionItem} from "vscode-languageserver-types";
|
||||
import {nullTrace} from "./nulltrace";
|
||||
import {findInnerTokenAndParent} from "./utils/find-token";
|
||||
import {transform} from "./utils/transform";
|
||||
import {Value, ValueProviderConfig} from "./value-providers/config";
|
||||
import {defaultValueProviders} from "./value-providers/default";
|
||||
|
||||
export async function complete(
|
||||
textDocument: TextDocument,
|
||||
@@ -28,19 +28,13 @@ export async function complete(
|
||||
|
||||
const file: File = {
|
||||
name: textDocument.uri,
|
||||
content: newDoc.getText(),
|
||||
content: newDoc.getText()
|
||||
};
|
||||
const result = parseWorkflow(file.name, [file], nullTrace);
|
||||
|
||||
const [innerToken, parent] = findInnerTokenAndParent(newPos, result.value);
|
||||
const values = await getValues(
|
||||
innerToken,
|
||||
parent,
|
||||
newPos,
|
||||
textDocument.uri,
|
||||
valueProviderConfig
|
||||
);
|
||||
return values.map((value) => CompletionItem.create(value.label));
|
||||
const values = await getValues(innerToken, parent, newPos, textDocument.uri, valueProviderConfig);
|
||||
return values.map(value => CompletionItem.create(value.label));
|
||||
}
|
||||
|
||||
async function getValues(
|
||||
@@ -65,10 +59,7 @@ async function getValues(
|
||||
|
||||
let customValues: Value[] | undefined = undefined;
|
||||
if (token?.definition?.key) {
|
||||
customValues = await valueProviderConfig?.getCustomValues(
|
||||
token.definition.key,
|
||||
{ uri: workflowUri }
|
||||
);
|
||||
customValues = await valueProviderConfig?.getCustomValues(token.definition.key, {uri: workflowUri});
|
||||
}
|
||||
|
||||
if (customValues !== undefined) {
|
||||
@@ -93,10 +84,7 @@ async function getValues(
|
||||
function getExistingValues(token: TemplateToken | null, parent: TemplateToken) {
|
||||
// For incomplete YAML, we may only have a parent token
|
||||
if (token) {
|
||||
if (
|
||||
token.templateTokenType !== STRING_TYPE ||
|
||||
parent.templateTokenType !== SEQUENCE_TYPE
|
||||
) {
|
||||
if (token.templateTokenType !== STRING_TYPE || parent.templateTokenType !== SEQUENCE_TYPE) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -127,13 +115,8 @@ function getExistingValues(token: TemplateToken | null, parent: TemplateToken) {
|
||||
}
|
||||
}
|
||||
|
||||
function filterAndSortCompletionOptions(
|
||||
options: Value[],
|
||||
existingValues?: Set<string>
|
||||
) {
|
||||
options = options.filter(
|
||||
(x) => !existingValues || !existingValues.has(x.label)
|
||||
);
|
||||
function filterAndSortCompletionOptions(options: Value[], existingValues?: Set<string>) {
|
||||
options = options.filter(x => !existingValues || !existingValues.has(x.label));
|
||||
options.sort((a, b) => a.label.localeCompare(b.label));
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
import {
|
||||
parseWorkflow,
|
||||
ParseWorkflowResult,
|
||||
} 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 {parseWorkflow, ParseWorkflowResult} 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";
|
||||
|
||||
export async function hover(
|
||||
document: TextDocument,
|
||||
position: Position
|
||||
): Promise<Hover | null> {
|
||||
export async function hover(document: TextDocument, position: Position): Promise<Hover | null> {
|
||||
const file: File = {
|
||||
name: document.uri,
|
||||
content: document.getText(),
|
||||
content: document.getText()
|
||||
};
|
||||
const result = parseWorkflow(file.name, [file], nullTrace);
|
||||
|
||||
@@ -38,9 +32,9 @@ function getHover(innerToken: TemplateToken): Hover | null {
|
||||
|
||||
if (innerToken.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:** ${innerToken.definition.evaluatorContext.join(
|
||||
", "
|
||||
)}`;
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -48,13 +42,13 @@ function getHover(innerToken: TemplateToken): Hover | null {
|
||||
range: {
|
||||
start: {
|
||||
line: innerToken.range!.start[0],
|
||||
character: innerToken.range!.start[1],
|
||||
character: innerToken.range!.start[1]
|
||||
},
|
||||
end: {
|
||||
line: innerToken.range!.end[0],
|
||||
character: innerToken.range!.end[1],
|
||||
},
|
||||
},
|
||||
character: innerToken.range!.end[1]
|
||||
}
|
||||
}
|
||||
} as Hover;
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { hover } from "./hover";
|
||||
export { validate } from "./validate";
|
||||
export { complete } from "./complete";
|
||||
export {hover} from "./hover";
|
||||
export {validate} from "./validate";
|
||||
export {complete} from "./complete";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TraceWriter } from "@github/actions-workflow-parser/templates/trace-writer";
|
||||
import {TraceWriter} from "@github/actions-workflow-parser/templates/trace-writer";
|
||||
|
||||
export const nullTrace: TraceWriter = {
|
||||
info: (x) => {},
|
||||
verbose: (x) => {},
|
||||
error: (x) => {},
|
||||
info: x => {},
|
||||
verbose: x => {},
|
||||
error: x => {}
|
||||
};
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
import { getPositionFromCursor } from "./cursor-position";
|
||||
import {getPositionFromCursor} from "./cursor-position";
|
||||
|
||||
describe("getPositionFromCursor", () => {
|
||||
it("returns the position of the cursor and the document without that cursor", () => {
|
||||
const input = "on: push\njobs:|";
|
||||
const [newDoc, position] = getPositionFromCursor(input);
|
||||
|
||||
expect(position).toEqual({ line: 1, character: 5 });
|
||||
expect(position).toEqual({line: 1, character: 5});
|
||||
expect(newDoc.getText()).toEqual("on: push\njobs:");
|
||||
});
|
||||
|
||||
it("throws an error if no cursor is found", () => {
|
||||
const input = "on: push\njobs:";
|
||||
expect(() => getPositionFromCursor(input)).toThrowError(
|
||||
"No cursor found in document"
|
||||
);
|
||||
expect(() => getPositionFromCursor(input)).toThrowError("No cursor found in document");
|
||||
});
|
||||
|
||||
it("handles a cursor at the beginning of the document", () => {
|
||||
const input = "|on: push\njobs:";
|
||||
const [newDoc, position] = getPositionFromCursor(input);
|
||||
|
||||
expect(position).toEqual({ line: 0, character: 0 });
|
||||
expect(position).toEqual({line: 0, character: 0});
|
||||
expect(newDoc.getText()).toEqual("on: push\njobs:");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TextDocument, Position } from "vscode-languageserver-textdocument";
|
||||
import {TextDocument, Position} from "vscode-languageserver-textdocument";
|
||||
|
||||
// Calculates the position of the cursor and the document without that cursor
|
||||
// Cursor is represented by a `|` character
|
||||
@@ -11,12 +11,7 @@ export function getPositionFromCursor(input: string): [TextDocument, Position] {
|
||||
}
|
||||
|
||||
const position = doc.positionAt(cursorIndex);
|
||||
const newDoc = TextDocument.create(
|
||||
doc.uri,
|
||||
doc.languageId,
|
||||
doc.version,
|
||||
doc.getText().replace("|", "")
|
||||
);
|
||||
const newDoc = TextDocument.create(doc.uri, doc.languageId, doc.version, doc.getText().replace("|", ""));
|
||||
|
||||
return [newDoc, position];
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ import {
|
||||
TemplateToken,
|
||||
MAPPING_TYPE,
|
||||
SEQUENCE_TYPE,
|
||||
NULL_TYPE,
|
||||
NULL_TYPE
|
||||
} from "@github/actions-workflow-parser/templates/tokens/index";
|
||||
import { MappingToken } from "@github/actions-workflow-parser/templates/tokens/mapping-token";
|
||||
import { SequenceToken } from "@github/actions-workflow-parser/templates/tokens/sequence-token";
|
||||
import { Position } from "vscode-languageserver-textdocument";
|
||||
import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token";
|
||||
import {SequenceToken} from "@github/actions-workflow-parser/templates/tokens/sequence-token";
|
||||
import {Position} from "vscode-languageserver-textdocument";
|
||||
|
||||
export function findInnerToken(pos: Position, root?: TemplateToken) {
|
||||
const [innerToken, _] = findInnerTokenAndParent(pos, root);
|
||||
@@ -41,7 +41,7 @@ export function findInnerTokenAndParent(
|
||||
const mappingToken = token as MappingToken;
|
||||
parent = mappingToken;
|
||||
for (let i = 0; i < mappingToken.count; i++) {
|
||||
const { key, value } = mappingToken.get(i);
|
||||
const {key, value} = mappingToken.get(i);
|
||||
|
||||
// Null tokens don't have a position, we can only use the line information
|
||||
if (nullNodeOnLine(pos, key, value)) {
|
||||
@@ -84,21 +84,14 @@ function posInToken(pos: Position, token: TemplateToken): boolean {
|
||||
|
||||
// Position is within the token lines. Check character/column if pos line matches
|
||||
// start or end
|
||||
if (
|
||||
(r.start[0] === tokenLine && tokenChar < r.start[1]) ||
|
||||
(r.end[0] === tokenLine && tokenChar > r.end[1])
|
||||
) {
|
||||
if ((r.start[0] === tokenLine && tokenChar < r.start[1]) || (r.end[0] === tokenLine && tokenChar > r.end[1])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function nullNodeOnLine(
|
||||
pos: Position,
|
||||
key: TemplateToken,
|
||||
value: TemplateToken
|
||||
): boolean {
|
||||
function nullNodeOnLine(pos: Position, key: TemplateToken, value: TemplateToken): boolean {
|
||||
if (value.templateTokenType !== NULL_TYPE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { Position, TextDocument } from "vscode-languageserver-textdocument";
|
||||
import {Position, TextDocument} from "vscode-languageserver-textdocument";
|
||||
|
||||
const DUMMY_KEY = "dummy";
|
||||
|
||||
// Transform a document to work around YAML parsing issues
|
||||
// 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] {
|
||||
export function transform(doc: TextDocument, pos: Position): [TextDocument, Position] {
|
||||
const input = doc.getText();
|
||||
let offset = doc.offsetAt(pos);
|
||||
// TODO: Optimize this...
|
||||
@@ -15,9 +12,8 @@ export function transform(
|
||||
const lineNo = input
|
||||
.substring(0, offset)
|
||||
.split("")
|
||||
.filter((x) => x === "\n").length;
|
||||
const linePos =
|
||||
offset - lines.slice(0, lineNo).reduce((p, l) => p + l.length + 1, 0);
|
||||
.filter(x => x === "\n").length;
|
||||
const linePos = offset - lines.slice(0, lineNo).reduce((p, l) => p + l.length + 1, 0);
|
||||
const line = lines[lineNo];
|
||||
|
||||
let partialInput = line.trim();
|
||||
@@ -37,11 +33,7 @@ export function transform(
|
||||
}
|
||||
|
||||
lines[lineNo] =
|
||||
line.substring(0, linePos) +
|
||||
spacer +
|
||||
DUMMY_KEY +
|
||||
(trimmedLine === "-" ? "" : ":") +
|
||||
line.substring(linePos);
|
||||
line.substring(0, linePos) + spacer + DUMMY_KEY + (trimmedLine === "-" ? "" : ":") + line.substring(linePos);
|
||||
|
||||
// Adjust pos by one to prevent a sequence node being marked as active
|
||||
offset++;
|
||||
@@ -51,22 +43,13 @@ export function transform(
|
||||
}
|
||||
|
||||
if (trimmedLine.startsWith("-")) {
|
||||
partialInput = trimmedLine
|
||||
.substring(trimmedLine.indexOf("-") + 1)
|
||||
.trim();
|
||||
partialInput = trimmedLine.substring(trimmedLine.indexOf("-") + 1).trim();
|
||||
}
|
||||
} else {
|
||||
partialInput = (
|
||||
offset > colon ? line.substring(colon + 1) : line.substring(0, colon)
|
||||
).trim();
|
||||
partialInput = (offset > colon ? line.substring(colon + 1) : line.substring(0, colon)).trim();
|
||||
offset = offset - 1;
|
||||
}
|
||||
}
|
||||
const newDoc = TextDocument.create(
|
||||
doc.uri,
|
||||
doc.languageId,
|
||||
doc.version,
|
||||
lines.join("\n")
|
||||
);
|
||||
const newDoc = TextDocument.create(doc.uri, doc.languageId, doc.version, lines.join("\n"));
|
||||
return [newDoc, newDoc.positionAt(offset)];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { parseWorkflow } from "@github/actions-workflow-parser";
|
||||
import { TemplateValidationError } from "@github/actions-workflow-parser/templates/template-validation-error";
|
||||
import { nullTrace } from "./nulltrace";
|
||||
import {parseWorkflow} from "@github/actions-workflow-parser";
|
||||
import {TemplateValidationError} from "@github/actions-workflow-parser/templates/template-validation-error";
|
||||
import {nullTrace} from "./nulltrace";
|
||||
|
||||
describe("validation", () => {
|
||||
it("valid workflow", () => {
|
||||
@@ -9,8 +9,8 @@ describe("validation", () => {
|
||||
[
|
||||
{
|
||||
name: "wf.yaml",
|
||||
content: "on: push\njobs:\n build:\n runs-on: ubuntu-latest",
|
||||
},
|
||||
content: "on: push\njobs:\n build:\n runs-on: ubuntu-latest"
|
||||
}
|
||||
],
|
||||
nullTrace
|
||||
);
|
||||
@@ -24,23 +24,18 @@ describe("validation", () => {
|
||||
[
|
||||
{
|
||||
name: "wf.yaml",
|
||||
content: "on: push",
|
||||
},
|
||||
content: "on: push"
|
||||
}
|
||||
],
|
||||
nullTrace
|
||||
);
|
||||
|
||||
expect(result.context.errors.getErrors().length).toBe(1);
|
||||
expect(result.context.errors.getErrors()[0]).toEqual(
|
||||
new TemplateValidationError(
|
||||
"Required property is missing: jobs",
|
||||
"wf.yaml (Line: 1, Col: 1)",
|
||||
undefined,
|
||||
{
|
||||
start: [1, 1],
|
||||
end: [1, 9],
|
||||
}
|
||||
)
|
||||
new TemplateValidationError("Required property is missing: jobs", "wf.yaml (Line: 1, Col: 1)", undefined, {
|
||||
start: [1, 1],
|
||||
end: [1, 9]
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -56,23 +51,18 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo`,
|
||||
},
|
||||
- run: echo`
|
||||
}
|
||||
],
|
||||
nullTrace
|
||||
);
|
||||
|
||||
expect(result.context.errors.getErrors().length).toBe(1);
|
||||
expect(result.context.errors.getErrors()[0]).toEqual(
|
||||
new TemplateValidationError(
|
||||
"Unexpected value 'unknown-key'",
|
||||
"wf.yaml (Line: 2, Col: 1)",
|
||||
undefined,
|
||||
{
|
||||
start: [2, 1],
|
||||
end: [2, 12],
|
||||
}
|
||||
)
|
||||
new TemplateValidationError("Unexpected value 'unknown-key'", "wf.yaml (Line: 2, Col: 1)", undefined, {
|
||||
start: [2, 1],
|
||||
end: [2, 12]
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import {
|
||||
convertWorkflowTemplate,
|
||||
parseWorkflow,
|
||||
ParseWorkflowResult,
|
||||
} from "@github/actions-workflow-parser";
|
||||
import { TokenRange } from "@github/actions-workflow-parser/templates/tokens/token-range";
|
||||
import { File } from "@github/actions-workflow-parser/workflows/file";
|
||||
import { TextDocument } from "vscode-languageserver-textdocument";
|
||||
import { Diagnostic, Range } from "vscode-languageserver-types";
|
||||
import { nullTrace } from "./nulltrace";
|
||||
import {convertWorkflowTemplate, parseWorkflow, ParseWorkflowResult} from "@github/actions-workflow-parser";
|
||||
import {TokenRange} from "@github/actions-workflow-parser/templates/tokens/token-range";
|
||||
import {File} from "@github/actions-workflow-parser/workflows/file";
|
||||
import {TextDocument} from "vscode-languageserver-textdocument";
|
||||
import {Diagnostic, Range} from "vscode-languageserver-types";
|
||||
import {nullTrace} from "./nulltrace";
|
||||
|
||||
/**
|
||||
* Validates a workflow file
|
||||
@@ -21,15 +17,11 @@ export async function validate(
|
||||
): Promise<Diagnostic[]> {
|
||||
const file: File = {
|
||||
name: textDocument.uri,
|
||||
content: textDocument.getText(),
|
||||
content: textDocument.getText()
|
||||
};
|
||||
|
||||
try {
|
||||
const result: ParseWorkflowResult = parseWorkflow(
|
||||
file.name,
|
||||
[file],
|
||||
nullTrace
|
||||
);
|
||||
const result: ParseWorkflowResult = parseWorkflow(file.name, [file], nullTrace);
|
||||
|
||||
if (result.value) {
|
||||
// Errors will be updated in the context
|
||||
@@ -37,25 +29,25 @@ export async function validate(
|
||||
}
|
||||
|
||||
// For now map parser errors directly to diagnostics
|
||||
return result.context.errors.getErrors().map((error) => {
|
||||
return result.context.errors.getErrors().map(error => {
|
||||
let range = mapRange(error.range);
|
||||
if (!range) {
|
||||
// Use default range
|
||||
range = {
|
||||
start: {
|
||||
line: 1,
|
||||
character: 1,
|
||||
character: 1
|
||||
},
|
||||
end: {
|
||||
line: 1,
|
||||
character: 1,
|
||||
},
|
||||
character: 1
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
message: error.rawMessage,
|
||||
range,
|
||||
range
|
||||
};
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -72,11 +64,11 @@ function mapRange(range: TokenRange | undefined): Range | undefined {
|
||||
return {
|
||||
start: {
|
||||
line: range.start[0] - 1,
|
||||
character: range.start[1] - 1,
|
||||
character: range.start[1] - 1
|
||||
},
|
||||
end: {
|
||||
line: range.end[0] - 1,
|
||||
character: range.end[1] - 1,
|
||||
},
|
||||
character: range.end[1] - 1
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,16 +9,8 @@ export interface WorkflowContext {
|
||||
uri: string;
|
||||
}
|
||||
export interface ValueProviderConfig {
|
||||
getCustomValues: (
|
||||
key: string,
|
||||
context: WorkflowContext
|
||||
) => Promise<Value[] | undefined>;
|
||||
getActionInputs?: (
|
||||
owner: string,
|
||||
name: string,
|
||||
ref: string,
|
||||
path?: string
|
||||
) => Promise<ActionInput[]>;
|
||||
getCustomValues: (key: string, context: WorkflowContext) => Promise<Value[] | undefined>;
|
||||
getActionInputs?: (owner: string, name: string, ref: string, path?: string) => Promise<ActionInput[]>;
|
||||
}
|
||||
|
||||
export interface ActionInput {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Definition } from "@github/actions-workflow-parser/templates/schema/definition";
|
||||
import { BooleanDefinition } from "@github/actions-workflow-parser/templates/schema/boolean-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 { getWorkflowSchema } from "@github/actions-workflow-parser/workflows/workflow-schema";
|
||||
import { Value, ValueProvider } from "./config";
|
||||
import {Definition} from "@github/actions-workflow-parser/templates/schema/definition";
|
||||
import {BooleanDefinition} from "@github/actions-workflow-parser/templates/schema/boolean-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 {getWorkflowSchema} from "@github/actions-workflow-parser/workflows/workflow-schema";
|
||||
import {Value, ValueProvider} from "./config";
|
||||
|
||||
export function defaultValueProviders(): { [key: string]: ValueProvider } {
|
||||
export function defaultValueProviders(): {[key: string]: ValueProvider} {
|
||||
const schema = getWorkflowSchema();
|
||||
|
||||
const map: { [key: string]: ValueProvider } = {};
|
||||
const map: {[key: string]: ValueProvider} = {};
|
||||
for (const key of Object.keys(schema.definitions)) {
|
||||
const provider = definitionValueProvider(key, schema.definitions);
|
||||
if (provider) {
|
||||
@@ -30,15 +30,12 @@ export function defaultValueProviders(): { [key: string]: ValueProvider } {
|
||||
"macos-10.15",
|
||||
"macos-10.14",
|
||||
"macos-10.13",
|
||||
"self-hosted",
|
||||
]),
|
||||
"self-hosted"
|
||||
])
|
||||
};
|
||||
}
|
||||
|
||||
function definitionValueProvider(
|
||||
key: string,
|
||||
definitions: { [key: string]: Definition }
|
||||
): ValueProvider | undefined {
|
||||
function definitionValueProvider(key: string, definitions: {[key: string]: Definition}): ValueProvider | undefined {
|
||||
const def = definitions[key];
|
||||
if (def instanceof MappingDefinition) {
|
||||
return mappingValueProvider(def);
|
||||
@@ -49,20 +46,15 @@ function definitionValueProvider(
|
||||
}
|
||||
}
|
||||
|
||||
function mappingValueProvider(
|
||||
mappingDefinition: MappingDefinition
|
||||
): ValueProvider {
|
||||
function mappingValueProvider(mappingDefinition: MappingDefinition): ValueProvider {
|
||||
const properties: Value[] = [];
|
||||
for (const [key, value] of Object.entries(mappingDefinition.properties)) {
|
||||
properties.push({ label: key, description: value.description });
|
||||
properties.push({label: key, description: value.description});
|
||||
}
|
||||
return () => properties;
|
||||
}
|
||||
|
||||
function oneOfValueProvider(
|
||||
oneOfDefinition: OneOfDefinition,
|
||||
definitions: { [key: string]: Definition }
|
||||
): ValueProvider {
|
||||
function oneOfValueProvider(oneOfDefinition: OneOfDefinition, definitions: {[key: string]: Definition}): ValueProvider {
|
||||
return () => {
|
||||
const values: Value[] = [];
|
||||
for (const key of oneOfDefinition.oneOf) {
|
||||
@@ -79,7 +71,7 @@ function oneOfValueProvider(
|
||||
}
|
||||
|
||||
function stringsToValues(labels: string[]): Value[] {
|
||||
return labels.map((x) => ({ label: x }));
|
||||
return labels.map(x => ({label: x}));
|
||||
}
|
||||
|
||||
function distinctValues(values: Value[]): Value[] {
|
||||
|
||||
Reference in New Issue
Block a user