Add descriptions for schema-defined functions
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
|
||||
{
|
||||
"$schema": "./descriptionsSchema.json",
|
||||
"root": {
|
||||
@@ -39,6 +38,20 @@
|
||||
"description": "Contains the inputs of a reusable or manually triggered workflow. For more information, see [`inputs` context](https://docs.github.com/actions/learn-github-actions/contexts#inputs-context)."
|
||||
}
|
||||
},
|
||||
"functions": {
|
||||
"always": {
|
||||
"description": "Causes the step to always execute, and returns `true`, even when canceled. The `always` expression is best used at the step level or on tasks that you expect to run even when a job is canceled. For example, you can use `always` to send logs even when a job is canceled."
|
||||
},
|
||||
"cancelled": {
|
||||
"description": "Returns `true` if the workflow was canceled."
|
||||
},
|
||||
"failure": {
|
||||
"description": "Returns `true` when any previous step of a job fails. If you have a chain of dependent jobs, `failure()` returns `true` if any ancestor job fails."
|
||||
},
|
||||
"hashFiles": {
|
||||
"description": "Returns a single hash for the set of files that matches the `path` pattern. You can provide a single `path` pattern or multiple `path` patterns separated by commas. The `path` is relative to the `GITHUB_WORKSPACE` directory and can only include files inside of the `GITHUB_WORKSPACE`."
|
||||
}
|
||||
},
|
||||
"github": {
|
||||
"action": {
|
||||
"description": "The name of the action currently running, or the [`id`](https://docs.github.com/actions/learn-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepsid) of a step. GitHub Actions removes special characters, and uses the name `__run` when the current step runs a script without an `id`. If you use the same action more than once in the same job, the name will include a suffix with the sequence number with underscore before it. For example, the first script you run will have the name `__run`, and the second script will be named `__run_2`. Similarly, the second invocation of `actions/checkout` will be `actionscheckout2`."
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import descriptions from "./descriptions.json" assert {type: "json"};
|
||||
|
||||
export const RootContext = "root";
|
||||
const FunctionContext = "functions";
|
||||
|
||||
/**
|
||||
* Get a description for a built-in context
|
||||
@@ -13,3 +14,12 @@ export function getDescription(context: string, key: string): string | undefined
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a description for a context function
|
||||
* This will not include functions defined by the expressions library (e.g. `contains`, `fromJSON`, etc.)
|
||||
* @param name Name of the function, for example `hashFiles`
|
||||
*/
|
||||
export function getFunctionDescription(name: string): string | undefined {
|
||||
return getDescription(FunctionContext, name);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {format} from "@github/actions-expressions/funcs/format";
|
||||
import {Hover} from "vscode-languageserver-types";
|
||||
import {ContextProviderConfig} from "./context-providers/config";
|
||||
import {hover} from "./hover";
|
||||
@@ -118,4 +119,64 @@ jobs:
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("built-in function", async () => {
|
||||
const input = `on: push
|
||||
run-name: \${{ form|at('Run {0}', github.run_id) }}
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [self-hosted]`;
|
||||
const result = await hover(...getPositionFromCursor(input), {
|
||||
contextProviderConfig
|
||||
});
|
||||
|
||||
expect(result).toEqual<Hover>({
|
||||
contents: format.description || "",
|
||||
range: {
|
||||
start: {line: 1, character: 14},
|
||||
end: {line: 1, character: 20}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("schema-defined function", async () => {
|
||||
const input = `on: push
|
||||
jobs:
|
||||
build:
|
||||
if: alwa|ys()
|
||||
runs-on: [self-hosted]`;
|
||||
const result = await hover(...getPositionFromCursor(input), {
|
||||
contextProviderConfig
|
||||
});
|
||||
|
||||
expect(result).toEqual<Hover>({
|
||||
contents:
|
||||
"Causes the step to always execute, and returns `true`, even when canceled. The `always` expression is best used at the step level or on tasks that you expect to run even when a job is canceled. For example, you can use `always` to send logs even when a job is canceled.",
|
||||
range: {
|
||||
start: {line: 3, character: 11},
|
||||
end: {line: 3, character: 17}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("schema-defined function with different casing", async () => {
|
||||
const input = `on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [self-hosted]
|
||||
steps:
|
||||
- run: echo \${{ hash|Files('test.txt') }}`;
|
||||
const result = await hover(...getPositionFromCursor(input), {
|
||||
contextProviderConfig
|
||||
});
|
||||
|
||||
expect(result).toEqual<Hover>({
|
||||
contents:
|
||||
"Returns a single hash for the set of files that matches the `path` pattern. You can provide a single `path` pattern or multiple `path` patterns separated by commas. The `path` is relative to the `GITHUB_WORKSPACE` directory and can only include files inside of the `GITHUB_WORKSPACE`.",
|
||||
range: {
|
||||
start: {line: 5, character: 22},
|
||||
end: {line: 5, character: 31}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {DescriptionDictionary, Parser} from "@github/actions-expressions";
|
||||
import {FunctionInfo} from "@github/actions-expressions/funcs/info";
|
||||
import {data, DescriptionDictionary, Parser} from "@github/actions-expressions";
|
||||
import {FunctionDefinition, FunctionInfo} from "@github/actions-expressions/funcs/info";
|
||||
import {Lexer} from "@github/actions-expressions/lexer";
|
||||
import {ErrorPolicy} from "@github/actions-workflow-parser/model/convert";
|
||||
import {getCronDescription} from "@github/actions-workflow-parser/model/converter/cron";
|
||||
@@ -13,6 +13,7 @@ import {Position, TextDocument} from "vscode-languageserver-textdocument";
|
||||
import {Hover} from "vscode-languageserver-types";
|
||||
import {ContextProviderConfig} from "./context-providers/config";
|
||||
import {getContext, Mode} from "./context-providers/default";
|
||||
import {getFunctionDescription} from "./context-providers/descriptions";
|
||||
import {getWorkflowContext, WorkflowContext} from "./context/workflow-context";
|
||||
import {
|
||||
isReusableWorkflowJobInput,
|
||||
@@ -72,6 +73,10 @@ export async function hover(document: TextDocument, position: Position, config?:
|
||||
const {namedContexts, functions} = splitAllowedContext(allowedContext);
|
||||
const context = await getContext(namedContexts, config?.contextProviderConfig, workflowContext, Mode.Completion);
|
||||
|
||||
for (const func of functions) {
|
||||
func.description = getFunctionDescription(func.name);
|
||||
}
|
||||
|
||||
const exprPos = mapToExpressionPos(token, position);
|
||||
if (exprPos) {
|
||||
return expressionHover(exprPos, context, namedContexts, functions);
|
||||
@@ -175,7 +180,14 @@ function expressionHover(
|
||||
const p = new Parser(lr.tokens, namedContexts, functions);
|
||||
const expr = p.parse();
|
||||
|
||||
const hv = new HoverVisitor(position, context, validatorFunctions);
|
||||
const functionMap = new Map<string, FunctionDefinition>();
|
||||
for (const func of functions) {
|
||||
functionMap.set(func.name.toLowerCase(), {
|
||||
...func,
|
||||
call: () => new data.Null()
|
||||
});
|
||||
}
|
||||
const hv = new HoverVisitor(position, context, functionMap);
|
||||
const hoverResult = hv.hover(expr);
|
||||
if (!hoverResult) {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user