Lint the language service package

This commit is contained in:
Josh Gross
2023-03-15 16:52:56 -04:00
parent 7bb8ff9aae
commit 26f5eeede9
21 changed files with 66 additions and 54 deletions
+9 -9
View File
@@ -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);
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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":
+1 -1
View File
@@ -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
};
}
+5 -4
View File
@@ -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;
}
/**
+2 -1
View File
@@ -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
);
+1 -1
View File
@@ -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 */
+7 -5
View File
@@ -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"]) {