Support descriptions for contexts

This commit is contained in:
Christopher Schleiden
2023-01-10 17:44:48 -08:00
parent eb25801e47
commit 43fecc8648
22 changed files with 407 additions and 150 deletions
+17 -1
View File
@@ -1,4 +1,4 @@
import {complete, CompletionItem, trimTokenVector} from "./completion";
import {complete, CompletionItem, DescriptionDictionary, trimTokenVector} from "./completion";
import {BooleanData} from "./data/boolean";
import {Dictionary} from "./data/dictionary";
import {StringData} from "./data/string";
@@ -19,6 +19,14 @@ const testContext = new Dictionary(
}
)
},
{
key: "github",
value: new DescriptionDictionary({
key: "actor",
value: new StringData(""),
description: "The name of the person or app that initiated the workflow. For example, octocat."
})
},
{
key: "secrets",
value: new Dictionary({
@@ -79,6 +87,14 @@ describe("auto-complete", () => {
expect(testComplete("env.FOO")).toEqual(expected);
});
it("includes descriptions", () => {
expect(testComplete("github.")).toContainEqual<CompletionItem>({
label: "actor",
function: false,
description: "The name of the person or app that initiated the workflow. For example, octocat."
});
});
it("provides suggestions for secrets", () => {
const expected = completionItems("AWS_TOKEN");
+35 -3
View File
@@ -1,5 +1,5 @@
import {Dictionary, isDictionary} from "./data/dictionary";
import {ExpressionData} from "./data/expressiondata";
import {ExpressionData, Kind, Pair} from "./data/expressiondata";
import {Evaluator} from "./evaluator";
import {wellKnownFunctions} from "./funcs";
import {FunctionInfo} from "./funcs/info";
@@ -12,6 +12,36 @@ export type CompletionItem = {
function: boolean;
};
export type DescriptionPair = Pair & {description?: string};
export class DescriptionDictionary extends Dictionary {
private readonly descriptions = new Map<string, string>();
constructor(...pairs: DescriptionPair[]) {
super();
for (const p of pairs) {
this.add(p.key, p.value, p.description);
}
}
override add(key: string, value: ExpressionData, description?: string): void {
super.add(key, value);
if (description) {
this.descriptions.set(key, description);
}
}
override pairs(): DescriptionPair[] {
const pairs = super.pairs();
return pairs.map(p => ({...p, description: this.descriptions.get(p.key)}));
}
}
export function isDescriptionDictionary(x: ExpressionData): x is DescriptionDictionary {
return x.kind === Kind.Dictionary && x instanceof DescriptionDictionary;
}
// Complete returns a list of completion items for the given expression.
//
// The main functionality is auto-completing functions and context access:
@@ -97,7 +127,7 @@ function contextKeys(context: ExpressionData): CompletionItem[] {
return (
context
.pairs()
.map(x => completionItemFromContext(x.key))
.map(x => completionItemFromContext(x))
// Sort contexts
.sort((a, b) => a.label.localeCompare(b.label))
);
@@ -106,12 +136,14 @@ function contextKeys(context: ExpressionData): CompletionItem[] {
return [];
}
function completionItemFromContext(context: string): CompletionItem {
function completionItemFromContext(pair: DescriptionPair): CompletionItem {
const context = pair.key.toString();
const parenIndex = context.indexOf("(");
const isFunc = parenIndex >= 0 && context.indexOf(")") >= 0;
return {
label: isFunc ? context.substring(0, parenIndex) : context,
description: pair.description,
function: isFunc
};
}
+1 -1
View File
@@ -1,5 +1,5 @@
export {Expr} from "./ast";
export {complete, CompletionItem} from "./completion";
export {complete, CompletionItem, DescriptionDictionary, DescriptionPair, isDescriptionDictionary} from "./completion";
export * as data from "./data";
export {ExpressionError} from "./errors";
export {Evaluator} from "./evaluator";