Lint the language service package
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import {MarkupContent, TextEdit} from "vscode-languageserver-types";
|
||||
import {complete} from "./complete";
|
||||
import {WorkflowContext} from "./context/workflow-context";
|
||||
import {registerLogger} from "./log";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position";
|
||||
import {TestLogger} from "./test-utils/logger";
|
||||
@@ -182,8 +182,8 @@ jobs:
|
||||
const config: ValueProviderConfig = {
|
||||
"runs-on": {
|
||||
kind: ValueProviderKind.SuggestedValues,
|
||||
get: async (_: WorkflowContext) => {
|
||||
return [{label: "my-custom-label"}];
|
||||
get: () => {
|
||||
return Promise.resolve([{label: "my-custom-label"}]);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -200,8 +200,8 @@ jobs:
|
||||
const config: ValueProviderConfig = {
|
||||
"runs-on": {
|
||||
kind: ValueProviderKind.SuggestedValues,
|
||||
get: async (_: WorkflowContext) => {
|
||||
return [{label: "my-custom-label"}];
|
||||
get: () => {
|
||||
return Promise.resolve([{label: "my-custom-label"}]);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -349,7 +349,7 @@ jobs:
|
||||
`;
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
expect(result).toHaveLength(16);
|
||||
let textEdit = result[0].textEdit as TextEdit;
|
||||
const textEdit = result[0].textEdit as TextEdit;
|
||||
expect(textEdit.range).toEqual({
|
||||
start: {line: 5, character: 4},
|
||||
end: {line: 5, character: 5}
|
||||
@@ -390,7 +390,7 @@ jobs:
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result.length).toEqual(1);
|
||||
|
||||
let textEdit = result[0].textEdit as TextEdit;
|
||||
const textEdit = result[0].textEdit as TextEdit;
|
||||
expect(textEdit.newText).toEqual("pre-build");
|
||||
expect(textEdit.range).toEqual({
|
||||
start: {line: 6, character: 11},
|
||||
@@ -405,7 +405,7 @@ jobs:
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result.map(e => e.label)).toContain("runs-on");
|
||||
|
||||
let textEdit = result.filter(e => e.label === "runs-on")[0].textEdit as TextEdit;
|
||||
const textEdit = result.filter(e => e.label === "runs-on")[0].textEdit as TextEdit;
|
||||
expect(textEdit.newText).toEqual("runs-on");
|
||||
expect(textEdit.range).toEqual({
|
||||
start: {line: 3, character: 4},
|
||||
@@ -420,7 +420,7 @@ jobs:
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result.map(e => e.label)).toContain("runs-on");
|
||||
|
||||
let textEdit = result.filter(e => e.label === "runs-on")[0].textEdit as TextEdit;
|
||||
const textEdit = result.filter(e => e.label === "runs-on")[0].textEdit as TextEdit;
|
||||
expect(textEdit.newText).toEqual("runs-on");
|
||||
expect(textEdit.range).toEqual({
|
||||
start: {line: 3, character: 4},
|
||||
|
||||
@@ -10,5 +10,6 @@ export const RootContext = "root";
|
||||
*/
|
||||
export function getDescription(context: string, key: string): string | undefined {
|
||||
// The inferred type doesn't quite match the actual type, use any to work around that
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
||||
return (descriptions as any)[context]?.[key]?.description;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export function getJobsContext(workflowContext: WorkflowContext): DescriptionDic
|
||||
|
||||
const outputs = job.outputs || new data.Null();
|
||||
if (outputs instanceof MappingToken) {
|
||||
jobContext.add("outputs", createOutputsContext(outputs as MappingToken), getDescription("jobs", "outputs"));
|
||||
jobContext.add("outputs", createOutputsContext(outputs), getDescription("jobs", "outputs"));
|
||||
}
|
||||
|
||||
jobsContext.add(job.id.toString(), jobContext);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {Job} from "@github/actions-workflow-parser/model/workflow-template";
|
||||
import {BasicExpressionToken} from "@github/actions-workflow-parser/templates/tokens/basic-expression-token";
|
||||
|
||||
@@ -121,7 +121,7 @@ function matrixProperties(matrix: MappingToken, mode: Mode): Map<string, Set<str
|
||||
|
||||
case "exclude":
|
||||
break;
|
||||
default:
|
||||
default: {
|
||||
if (!isSequence(pair.value)) {
|
||||
properties.set(key, undefined);
|
||||
continue;
|
||||
@@ -137,6 +137,7 @@ function matrixProperties(matrix: MappingToken, mode: Mode): Map<string, Set<str
|
||||
|
||||
properties.set(key, values);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@ import {WorkflowContext} from "../context/workflow-context";
|
||||
import {TokenResult} from "../utils/find-token";
|
||||
|
||||
export function isReusableWorkflowJobInput(tokenResult: TokenResult): boolean {
|
||||
return tokenResult.parent?.definition?.key === "workflow-job-with" && isString(tokenResult.token!);
|
||||
return (
|
||||
tokenResult.parent?.definition?.key === "workflow-job-with" &&
|
||||
tokenResult.token !== null &&
|
||||
isString(tokenResult.token)
|
||||
);
|
||||
}
|
||||
|
||||
export function getReusableWorkflowInputDescription(
|
||||
|
||||
@@ -29,7 +29,7 @@ export function mapToExpressionPos(token: TemplateToken, position: Position): Ex
|
||||
if (token.originalExpressions?.length) {
|
||||
for (const originalExp of token.originalExpressions) {
|
||||
// Find the original expression that contains the position
|
||||
if (posWithinRange(pos, originalExp.expressionRange!)) {
|
||||
if (originalExp.expressionRange && posWithinRange(pos, originalExp.expressionRange)) {
|
||||
const exprRange = mapRange(originalExp.expressionRange);
|
||||
|
||||
return {
|
||||
@@ -47,7 +47,7 @@ export function mapToExpressionPos(token: TemplateToken, position: Position): Ex
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const exprRange = mapRange(token.expressionRange!);
|
||||
const exprRange = mapRange(token.expressionRange);
|
||||
return {
|
||||
expression: token.expression,
|
||||
// Adjust the position to point into the expression
|
||||
|
||||
@@ -11,28 +11,30 @@ import {getPositionFromCursor} from "../test-utils/cursor-position";
|
||||
import {HoverVisitor} from "./visitor";
|
||||
|
||||
const contextProviderConfig: ContextProviderConfig = {
|
||||
getContext: async (context: string) => {
|
||||
getContext: (context: string) => {
|
||||
switch (context) {
|
||||
case "github":
|
||||
return new DescriptionDictionary(
|
||||
{
|
||||
key: "event",
|
||||
value: new data.StringData("push"),
|
||||
description: "The event that triggered the workflow"
|
||||
},
|
||||
{
|
||||
key: "test",
|
||||
value: new DescriptionDictionary({
|
||||
key: "name",
|
||||
return Promise.resolve(
|
||||
new DescriptionDictionary(
|
||||
{
|
||||
key: "event",
|
||||
value: new data.StringData("push"),
|
||||
description: "Name for the test"
|
||||
}),
|
||||
description: "Test dictionary"
|
||||
}
|
||||
description: "The event that triggered the workflow"
|
||||
},
|
||||
{
|
||||
key: "test",
|
||||
value: new DescriptionDictionary({
|
||||
key: "name",
|
||||
value: new data.StringData("push"),
|
||||
description: "Name for the test"
|
||||
}),
|
||||
description: "Test dictionary"
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
Logical,
|
||||
Unary
|
||||
} from "@github/actions-expressions/ast";
|
||||
import {FunctionDefinition, FunctionInfo} from "@github/actions-expressions/funcs/info";
|
||||
import {FunctionDefinition} from "@github/actions-expressions/funcs/info";
|
||||
import {Pos, Range} from "@github/actions-expressions/lexer";
|
||||
import {posWithinRange} from "./pos-range";
|
||||
|
||||
@@ -35,7 +35,6 @@ export class HoverVisitor implements ExprVisitor<HoverResult> {
|
||||
constructor(
|
||||
private pos: Pos,
|
||||
private context: DescriptionDictionary,
|
||||
private extensionFunctions: FunctionInfo[],
|
||||
private functions: Map<string, FunctionDefinition>
|
||||
) {}
|
||||
|
||||
@@ -43,7 +42,7 @@ export class HoverVisitor implements ExprVisitor<HoverResult> {
|
||||
return n.accept(this);
|
||||
}
|
||||
|
||||
visitLiteral(literal: Literal): HoverResult {
|
||||
visitLiteral(): HoverResult {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export class ErrorDictionary extends data.Dictionary {
|
||||
constructor(...pairs: Pair[]) {
|
||||
super(...pairs);
|
||||
}
|
||||
public complete: boolean = true;
|
||||
public complete = true;
|
||||
|
||||
get(key: string): ExpressionData | undefined {
|
||||
const value = super.get(key);
|
||||
|
||||
@@ -13,7 +13,7 @@ export function testHoverConfig(tokenValue: string, tokenKey: string, descriptio
|
||||
throw new Error("Test provider only supports string tokens");
|
||||
}
|
||||
|
||||
expect((token as StringToken).value).toEqual(tokenValue);
|
||||
expect((token ).value).toEqual(tokenValue);
|
||||
expect(token.definition!.key).toEqual(tokenKey);
|
||||
|
||||
return description;
|
||||
|
||||
@@ -112,7 +112,7 @@ function appendContext(description: string, allowedContext?: string[]) {
|
||||
if (!allowedContext || allowedContext.length == 0) {
|
||||
return description;
|
||||
}
|
||||
let {namedContexts, functions} = splitAllowedContext(allowedContext);
|
||||
const {namedContexts, functions} = splitAllowedContext(allowedContext);
|
||||
let namedContextsString = "";
|
||||
let functionsString = "";
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import {FileProvider} from "@github/actions-workflow-parser/workflows/file-provi
|
||||
import {fileIdentifier} from "@github/actions-workflow-parser/workflows/file-reference";
|
||||
|
||||
export const testFileProvider: FileProvider = {
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
getFileContent: async ref => {
|
||||
switch (fileIdentifier(ref)) {
|
||||
case "monalisa/octocat/workflow.yaml@main":
|
||||
|
||||
@@ -40,7 +40,7 @@ function testFindToken(input: string): {
|
||||
parent: getTokenInfo(r.parent),
|
||||
key: getTokenInfo(r.keyToken),
|
||||
token: getTokenInfo(r.token),
|
||||
path: r.path.map(x => getTokenInfo(x)!)
|
||||
path: r.path.map(x => getTokenInfo(x)!) // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ export function findToken(pos: Position, root?: TemplateToken): TokenResult {
|
||||
];
|
||||
|
||||
while (s.length > 0) {
|
||||
const result = s.shift()!;
|
||||
const result = s.shift()!; // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||
const {parent, token, keyToken, path} = result;
|
||||
if (!token) {
|
||||
break;
|
||||
@@ -67,7 +67,7 @@ export function findToken(pos: Position, root?: TemplateToken): TokenResult {
|
||||
|
||||
// Position is in token, enqueue children if there are any
|
||||
switch (token.templateTokenType) {
|
||||
case TokenType.Mapping:
|
||||
case TokenType.Mapping: {
|
||||
const mappingToken = token as MappingToken;
|
||||
for (const {key, value} of mappingToken) {
|
||||
// If the position is within the key, immediately return it as the token.
|
||||
@@ -99,8 +99,8 @@ export function findToken(pos: Position, root?: TemplateToken): TokenResult {
|
||||
});
|
||||
}
|
||||
continue;
|
||||
|
||||
case TokenType.Sequence:
|
||||
}
|
||||
case TokenType.Sequence: {
|
||||
const sequenceToken = token as SequenceToken;
|
||||
for (const token of sequenceToken) {
|
||||
s.push({
|
||||
@@ -111,6 +111,7 @@ export function findToken(pos: Position, root?: TemplateToken): TokenResult {
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -43,8 +43,8 @@ function getLineCharCode(doc: TextDocument, lineNumber: number, index: number):
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
class SpacesDiffResult {
|
||||
public spacesDiff: number = 0;
|
||||
public looksLikeAlignment: boolean = false;
|
||||
public spacesDiff = 0;
|
||||
public looksLikeAlignment = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,7 +39,8 @@ export async function fetchOrConvertWorkflowTemplate(
|
||||
if (!template) {
|
||||
template = await convertWorkflowTemplate(
|
||||
parsedWorkflow.context,
|
||||
parsedWorkflow.value!,
|
||||
// TODO: @joshmgross We can't assume that the value is non-null here
|
||||
parsedWorkflow.value!, // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||
config?.fileProvider,
|
||||
options
|
||||
);
|
||||
|
||||
@@ -64,7 +64,7 @@ export async function validate(textDocument: TextDocument, config?: ValidationCo
|
||||
|
||||
// For now map parser errors directly to diagnostics
|
||||
for (const error of result.context.errors.getErrors()) {
|
||||
let range = mapRange(error.range);
|
||||
const range = mapRange(error.range);
|
||||
|
||||
diagnostics.push({
|
||||
message: error.rawMessage,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {ValueProviderConfig, ValueProviderKind} from "./config";
|
||||
import {needs} from "./needs";
|
||||
import {reusableJobInputs} from "./reusable-job-inputs";
|
||||
@@ -20,6 +19,7 @@ export const DEFAULT_RUNNER_LABELS = [
|
||||
"self-hosted"
|
||||
];
|
||||
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
export const defaultValueProviders: ValueProviderConfig = {
|
||||
needs: {
|
||||
kind: ValueProviderKind.AllowedValues,
|
||||
@@ -35,6 +35,7 @@ export const defaultValueProviders: ValueProviderConfig = {
|
||||
},
|
||||
"runs-on": {
|
||||
kind: ValueProviderKind.SuggestedValues,
|
||||
get: async (_: WorkflowContext) => stringsToValues(DEFAULT_RUNNER_LABELS)
|
||||
get: async () => stringsToValues(DEFAULT_RUNNER_LABELS)
|
||||
}
|
||||
};
|
||||
/* eslint-enable @typescript-eslint/require-await */
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {Value} from "./config";
|
||||
|
||||
export async function needs(context: WorkflowContext): Promise<Value[]> {
|
||||
export function needs(context: WorkflowContext): Promise<Value[]> {
|
||||
if (!context.template) {
|
||||
return [];
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
const uniquejobIDs = new Set(context.template.jobs.map(j => j.id)).values();
|
||||
return Array.from(uniquejobIDs)
|
||||
.filter(x => x.value !== context.job?.id.value)
|
||||
.map(x => ({label: x.value}));
|
||||
return Promise.resolve(
|
||||
Array.from(uniquejobIDs)
|
||||
.filter(x => x.value !== context.job?.id.value)
|
||||
.map(x => ({label: x.value}))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token";
|
||||
import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/template-token";
|
||||
import {isMapping, isString} from "@github/actions-workflow-parser/templates/tokens/type-guards";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {Value} from "./config";
|
||||
import {stringsToValues} from "./strings-to-values";
|
||||
|
||||
export function reusableJobInputs(context: WorkflowContext): Value[] {
|
||||
if (!context.reusableWorkflowJob?.["input-definitions"]) {
|
||||
|
||||
Reference in New Issue
Block a user