Merge pull request #137 from github/joshmgross/completion-file-provider

Add a `FileProvider` to auto-completion
This commit is contained in:
Josh Gross
2023-02-07 19:45:34 -05:00
committed by GitHub
5 changed files with 90 additions and 74 deletions
+1
View File
@@ -119,6 +119,7 @@ export function initConnection(connection: Connection) {
connection.onCompletion(async ({position, textDocument}: TextDocumentPositionParams): Promise<CompletionItem[]> => { connection.onCompletion(async ({position, textDocument}: TextDocumentPositionParams): Promise<CompletionItem[]> => {
return await onCompletion( return await onCompletion(
connection,
position, position,
documents.get(textDocument.uri)!, documents.get(textDocument.uri)!,
client, client,
+10 -7
View File
@@ -1,23 +1,26 @@
import {complete} from "@github/actions-languageservice/complete"; import {complete} from "@github/actions-languageservice/complete";
import {Octokit} from "@octokit/rest"; import {Octokit} from "@octokit/rest";
import {CompletionItem, Position} from "vscode-languageserver"; import {CompletionItem, Connection, Position} from "vscode-languageserver";
import {TextDocument} from "vscode-languageserver-textdocument"; import {TextDocument} from "vscode-languageserver-textdocument";
import {contextProviders} from "./context-providers"; import {contextProviders} from "./context-providers";
import {getFileProvider} from "./file-provider";
import {RepositoryContext} from "./initializationOptions"; import {RepositoryContext} from "./initializationOptions";
import {TTLCache} from "./utils/cache"; import {TTLCache} from "./utils/cache";
import {valueProviders} from "./value-providers"; import {valueProviders} from "./value-providers";
export async function onCompletion( export async function onCompletion(
connection: Connection,
position: Position, position: Position,
document: TextDocument, document: TextDocument,
client: Octokit | undefined, client: Octokit | undefined,
repoContext: RepositoryContext | undefined, repoContext: RepositoryContext | undefined,
cache: TTLCache cache: TTLCache
): Promise<CompletionItem[]> { ): Promise<CompletionItem[]> {
return await complete( return await complete(document, position, {
document, valueProviderConfig: repoContext && valueProviders(client, repoContext, cache),
position, contextProviderConfig: repoContext && contextProviders(client, repoContext, cache),
repoContext && valueProviders(client, repoContext, cache), fileProvider: getFileProvider(client, cache, repoContext?.workspaceUri, async path => {
repoContext && contextProviders(client, repoContext, cache) return await connection.sendRequest("actions/readFile", {path});
); })
});
} }
@@ -59,7 +59,7 @@ describe("expressions", () => {
describe("top-level auto-complete", () => { describe("top-level auto-complete", () => {
it("single region", async () => { it("single region", async () => {
const input = "run-name: ${{ | }}"; const input = "run-name: ${{ | }}";
const result = await complete(...getPositionFromCursor(input), undefined, undefined); const result = await complete(...getPositionFromCursor(input));
expect(result.map(x => x.label)).toEqual([ expect(result.map(x => x.label)).toEqual([
"github", "github",
@@ -76,18 +76,16 @@ describe("expressions", () => {
}); });
it("within parentheses", async () => { it("within parentheses", async () => {
const result = await complete( const result = await complete(...getPositionFromCursor("run-name: ${{ 1 == (github.|) }}"), {
...getPositionFromCursor("run-name: ${{ 1 == (github.|) }}"),
undefined,
contextProviderConfig contextProviderConfig
); });
expect(result.map(x => x.label)).toEqual(["event"]); expect(result.map(x => x.label)).toEqual(["event"]);
}); });
it("contains description", async () => { it("contains description", async () => {
const input = "run-name: ${{ github.| }}"; const input = "run-name: ${{ github.| }}";
const result = await complete(...getPositionFromCursor(input), undefined, undefined); const result = await complete(...getPositionFromCursor(input));
expect(result).toContainEqual<CompletionItem>({ expect(result).toContainEqual<CompletionItem>({
label: "api_url", label: "api_url",
@@ -101,7 +99,7 @@ describe("expressions", () => {
it("single region with existing input", async () => { it("single region with existing input", async () => {
const input = "run-name: ${{ g| }}"; const input = "run-name: ${{ g| }}";
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual([ expect(result.map(x => x.label)).toEqual([
"github", "github",
@@ -119,7 +117,7 @@ describe("expressions", () => {
it("single region with existing condition", async () => { it("single region with existing condition", async () => {
const input = "run-name: ${{ g| == 'test' }}"; const input = "run-name: ${{ g| == 'test' }}";
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual([ expect(result.map(x => x.label)).toEqual([
"github", "github",
@@ -137,7 +135,7 @@ describe("expressions", () => {
it("multiple regions with partial function", async () => { it("multiple regions with partial function", async () => {
const input = "run-name: Run a ${{ inputs.test }} one-line script ${{ from|('test') == inputs.name }}"; const input = "run-name: Run a ${{ inputs.test }} one-line script ${{ from|('test') == inputs.name }}";
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual([ expect(result.map(x => x.label)).toEqual([
"github", "github",
@@ -155,7 +153,7 @@ describe("expressions", () => {
it("multiple regions - first region", async () => { it("multiple regions - first region", async () => {
const input = "run-name: test-${{ git| == 1 }}-${{ github.event }}"; const input = "run-name: test-${{ git| == 1 }}-${{ github.event }}";
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual([ expect(result.map(x => x.label)).toEqual([
"github", "github",
@@ -173,7 +171,7 @@ describe("expressions", () => {
it("multiple regions", async () => { it("multiple regions", async () => {
const input = "run-name: test-${{ github }}-${{ | }}"; const input = "run-name: test-${{ github }}-${{ | }}";
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual([ expect(result.map(x => x.label)).toEqual([
"github", "github",
@@ -199,7 +197,7 @@ jobs:
first line first line
test \${{ github.| }} test \${{ github.| }}
test2`; test2`;
const result = await complete(...getPositionFromCursor(input, 1), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input, 1), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["event"]); expect(result.map(x => x.label)).toEqual(["event"]);
}); });
@@ -213,7 +211,7 @@ jobs:
first line first line
test \${{ github.| }} test \${{ github.| }}
test2`; test2`;
const result = await complete(...getPositionFromCursor(input, 1), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input, 1), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["event"]); expect(result.map(x => x.label)).toEqual(["event"]);
}); });
@@ -227,7 +225,7 @@ jobs:
first line first line
test \${{ github.| }} test \${{ github.| }}
test2`; test2`;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["event"]); expect(result.map(x => x.label)).toEqual(["event"]);
}); });
@@ -241,7 +239,7 @@ jobs:
first line first line
test \${{ github.| }} test \${{ github.| }}
test2`; test2`;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["event"]); expect(result.map(x => x.label)).toEqual(["event"]);
}); });
@@ -261,21 +259,21 @@ jobs:
foo: '{}' foo: '{}'
steps: steps:
- name: "\${{ fromJSON('test') == (inputs.|) }}"`; - name: "\${{ fromJSON('test') == (inputs.|) }}"`;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["test"]); expect(result.map(x => x.label)).toEqual(["test"]);
}); });
it("nested auto-complete", async () => { it("nested auto-complete", async () => {
const input = "run-name: ${{ github.| }}"; const input = "run-name: ${{ github.| }}";
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["event"]); expect(result.map(x => x.label)).toEqual(["event"]);
}); });
it("auto-complete partial", async () => { it("auto-complete partial", async () => {
const input = "run-name: ${{ github.ev| }}"; const input = "run-name: ${{ github.ev| }}";
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["event"]); expect(result.map(x => x.label)).toEqual(["event"]);
}); });
@@ -283,7 +281,7 @@ jobs:
it("using default context provider", async () => { it("using default context provider", async () => {
const input = const input =
"on: push\njobs:\n build:\n runs-on: ubuntu-latest\n environment:\n url: ${{ runner.| }}\n steps:\n - run: echo"; "on: push\njobs:\n build:\n runs-on: ubuntu-latest\n environment:\n url: ${{ runner.| }}\n steps:\n - run: echo";
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["arch", "name", "os", "temp", "tool_cache"]); expect(result.map(x => x.label)).toEqual(["arch", "name", "os", "temp", "tool_cache"]);
}); });
@@ -298,7 +296,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- run: echo`; - run: echo`;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["event"]); expect(result.map(x => x.label)).toEqual(["event"]);
}); });
@@ -311,7 +309,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- run: echo`; - run: echo`;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["event"]); expect(result.map(x => x.label)).toEqual(["event"]);
}); });
@@ -326,7 +324,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- run: echo`; - run: echo`;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["event"]); expect(result.map(x => x.label)).toEqual(["event"]);
}); });
@@ -339,7 +337,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- run: echo`; - run: echo`;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["event"]); expect(result.map(x => x.label)).toEqual(["event"]);
}); });
@@ -355,7 +353,7 @@ jobs:
steps: steps:
- run: echo - run: echo
if: \${{ github.| }}`; if: \${{ github.| }}`;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["event"]); expect(result.map(x => x.label)).toEqual(["event"]);
}); });
@@ -368,7 +366,7 @@ jobs:
steps: steps:
- run: echo - run: echo
if: github.|`; if: github.|`;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["event"]); expect(result.map(x => x.label)).toEqual(["event"]);
}); });
@@ -388,7 +386,7 @@ jobs:
with: with:
deploy-key: \${{ secrets.| deploy-key: \${{ secrets.|
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["GITHUB_TOKEN"]); expect(result.map(x => x.label)).toEqual(["GITHUB_TOKEN"]);
}); });
@@ -412,7 +410,7 @@ jobs:
steps: steps:
- run: echo "hello \${{ needs.| - run: echo "hello \${{ needs.|
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["b"]); expect(result.map(x => x.label)).toEqual(["b"]);
}); });
@@ -431,7 +429,7 @@ jobs:
steps: steps:
- run: echo "hello \${{ needs.a.| - run: echo "hello \${{ needs.a.|
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["outputs", "result"]); expect(result.map(x => x.label)).toEqual(["outputs", "result"]);
}); });
@@ -452,7 +450,7 @@ jobs:
steps: steps:
- run: echo "hello \${{ needs.a.outputs.| - run: echo "hello \${{ needs.a.outputs.|
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["build_id"]); expect(result.map(x => x.label)).toEqual(["build_id"]);
}); });
@@ -481,7 +479,7 @@ jobs:
env: env:
envstepb: step_b_env envstepb: step_b_env
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["envjobb", "envstepb", "envwf"]); expect(result.map(x => x.label)).toEqual(["envjobb", "envstepb", "envwf"]);
}); });
@@ -509,7 +507,7 @@ jobs:
steps: steps:
- run: echo "hello \${{ inputs.| - run: echo "hello \${{ inputs.|
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["another-name", "name", "third-name"]); expect(result.map(x => x.label)).toEqual(["another-name", "name", "third-name"]);
}); });
@@ -524,7 +522,7 @@ jobs:
steps: steps:
- run: echo "hello \${{ inputs.| - run: echo "hello \${{ inputs.|
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result).toEqual([]); expect(result).toEqual([]);
}); });
@@ -542,7 +540,7 @@ jobs:
- run: echo \${{ github.| }} - run: echo \${{ github.| }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, undefined); const result = await complete(...getPositionFromCursor(input));
expect(result.map(x => x.label)).toContain("actor"); expect(result.map(x => x.label)).toContain("actor");
}); });
@@ -570,7 +568,7 @@ jobs:
steps: steps:
- run: echo "hello \${{ github.event.inputs.| - run: echo "hello \${{ github.event.inputs.|
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, undefined); const result = await complete(...getPositionFromCursor(input));
expect(result.map(x => x.label)).toEqual(["another-name", "name", "third-name"]); expect(result.map(x => x.label)).toEqual(["another-name", "name", "third-name"]);
}); });
@@ -587,7 +585,7 @@ jobs:
- run: echo \${{ github.event.| }} - run: echo \${{ github.event.| }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, undefined); const result = await complete(...getPositionFromCursor(input));
expect(result.map(x => x.label)).not.toContain("inputs"); expect(result.map(x => x.label)).not.toContain("inputs");
expect(result.map(x => x.label)).not.toContain("schedule"); expect(result.map(x => x.label)).not.toContain("schedule");
@@ -607,7 +605,7 @@ jobs:
- run: echo \${{ github.event.| }} - run: echo \${{ github.event.| }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, undefined); const result = await complete(...getPositionFromCursor(input));
expect(result.map(x => x.label)).toEqual(["repository", "schedule", "workflow"]); expect(result.map(x => x.label)).toEqual(["repository", "schedule", "workflow"]);
}); });
@@ -623,7 +621,7 @@ jobs:
- run: echo \${{ github.event.| }} - run: echo \${{ github.event.| }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, undefined); const result = await complete(...getPositionFromCursor(input));
// forced is part of the push event payload // forced is part of the push event payload
expect(result.map(x => x.label)).toContain("forced"); expect(result.map(x => x.label)).toContain("forced");
@@ -642,7 +640,7 @@ jobs:
- run: echo \${{ github.event.| }} - run: echo \${{ github.event.| }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, undefined); const result = await complete(...getPositionFromCursor(input));
// We don't validate github.event for workflow_call, // We don't validate github.event for workflow_call,
// but there should still be auto-completion suggestions // but there should still be auto-completion suggestions
@@ -665,7 +663,7 @@ jobs:
- id: c - id: c
run: echo "hello \${{ steps.| run: echo "hello \${{ steps.|
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["a", "b"]); expect(result.map(x => x.label)).toEqual(["a", "b"]);
}); });
@@ -681,7 +679,7 @@ jobs:
run: echo hello a run: echo hello a
- run: echo "hello \${{ steps.a.| - run: echo "hello \${{ steps.a.|
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["conclusion", "outcome", "outputs"]); expect(result.map(x => x.label)).toEqual(["conclusion", "outcome", "outputs"]);
}); });
@@ -700,7 +698,7 @@ jobs:
- id: c - id: c
run: echo hello c run: echo hello c
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["a"]); expect(result.map(x => x.label)).toEqual(["a"]);
}); });
@@ -717,7 +715,7 @@ jobs:
run: echo hello b run: echo hello b
- run: echo "hello \${{ steps.| - run: echo "hello \${{ steps.|
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["b"]); expect(result.map(x => x.label)).toEqual(["b"]);
}); });
@@ -736,7 +734,7 @@ jobs:
- run: npm test > test-job-\${{ | }}.txt - run: npm test > test-job-\${{ | }}.txt
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).not.toContain("strategy"); expect(result.map(x => x.label)).not.toContain("strategy");
}); });
@@ -757,7 +755,7 @@ jobs:
- run: npm test > test-job-\${{ | }}.txt - run: npm test > test-job-\${{ | }}.txt
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toContain("strategy"); expect(result.map(x => x.label)).toContain("strategy");
}); });
@@ -778,7 +776,7 @@ jobs:
- run: npm test > test-job-\${{ strategy.| }}.txt - run: npm test > test-job-\${{ strategy.| }}.txt
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["fail-fast", "job-index", "job-total", "max-parallel"]); expect(result.map(x => x.label)).toEqual(["fail-fast", "job-index", "job-total", "max-parallel"]);
}); });
@@ -797,7 +795,7 @@ jobs:
- run: npm test > test-job-\${{ | }}.txt - run: npm test > test-job-\${{ | }}.txt
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).not.toContain("strategy"); expect(result.map(x => x.label)).not.toContain("strategy");
}); });
@@ -818,7 +816,7 @@ jobs:
- run: npm test > test-job-\${{ | }}.txt - run: npm test > test-job-\${{ | }}.txt
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toContain("strategy"); expect(result.map(x => x.label)).toContain("strategy");
}); });
@@ -841,7 +839,7 @@ jobs:
node-version: \${{ matrix.| }} node-version: \${{ matrix.| }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["node", "os"]); expect(result.map(x => x.label)).toEqual(["node", "os"]);
}); });
@@ -873,7 +871,7 @@ jobs:
node-version: \${{ matrix.| }} node-version: \${{ matrix.| }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["animal", "color", "fruit", "shape"]); expect(result.map(x => x.label)).toEqual(["animal", "color", "fruit", "shape"]);
}); });
@@ -894,7 +892,7 @@ jobs:
node-version: \${{ matrix.| }} node-version: \${{ matrix.| }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual([]); expect(result.map(x => x.label)).toEqual([]);
}); });
@@ -918,7 +916,7 @@ jobs:
node-version: \${{ matrix.| }} node-version: \${{ matrix.| }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["animal", "fruit"]); expect(result.map(x => x.label)).toEqual(["animal", "fruit"]);
}); });
@@ -940,7 +938,7 @@ jobs:
node-version: \${{ matrix.| }} node-version: \${{ matrix.| }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["color"]); expect(result.map(x => x.label)).toEqual(["color"]);
}); });
@@ -963,7 +961,7 @@ jobs:
- run: echo \${{ job.| }} - run: echo \${{ job.| }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["container", "services", "status"]); expect(result.map(x => x.label)).toEqual(["container", "services", "status"]);
}); });
@@ -980,7 +978,7 @@ jobs:
run: echo hi run: echo hi
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual([ expect(result.map(x => x.label)).toEqual([
"env", "env",
"github", "github",
@@ -1014,7 +1012,7 @@ jobs:
run: echo hi run: echo hi
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["foo"]); expect(result.map(x => x.label)).toEqual(["foo"]);
}); });
@@ -1031,7 +1029,7 @@ jobs:
- run: echo \${{ job.container.| }} - run: echo \${{ job.container.| }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["id", "network"]); expect(result.map(x => x.label)).toEqual(["id", "network"]);
}); });
@@ -1051,7 +1049,7 @@ jobs:
- run: echo \${{ job.services.| }} - run: echo \${{ job.services.| }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["nginx", "redis"]); expect(result.map(x => x.label)).toEqual(["nginx", "redis"]);
}); });
@@ -1072,7 +1070,7 @@ jobs:
- run: echo \${{ job.services.nginx.| }} - run: echo \${{ job.services.nginx.| }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["id", "network", "ports"]); expect(result.map(x => x.label)).toEqual(["id", "network", "ports"]);
}); });
}); });
@@ -1089,7 +1087,7 @@ jobs:
- run: echo \${{ | }}.txt - run: echo \${{ | }}.txt
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
// Built-in function // Built-in function
const toJSON = result.find(x => x.label === "toJson"); const toJSON = result.find(x => x.label === "toJson");
@@ -1121,7 +1119,7 @@ jobs:
- run: echo \${{ toJS|(github.event) }} - run: echo \${{ toJS|(github.event) }}
`; `;
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig); const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.find(x => x.label === "toJson")!.insertText).toBe("toJson"); expect(result.find(x => x.label === "toJson")!.insertText).toBe("toJson");
}); });
+2 -2
View File
@@ -192,7 +192,7 @@ jobs:
} }
} }
}; };
const result = await complete(...getPositionFromCursor(input), config); const result = await complete(...getPositionFromCursor(input), {valueProviderConfig: config});
expect(result).not.toBeUndefined(); expect(result).not.toBeUndefined();
expect(result.length).toEqual(1); expect(result.length).toEqual(1);
@@ -210,7 +210,7 @@ jobs:
} }
} }
}; };
const result = await complete(...getPositionFromCursor(input), config); const result = await complete(...getPositionFromCursor(input), {valueProviderConfig: config});
expect(result).not.toBeUndefined(); expect(result).not.toBeUndefined();
expect(result.length).toEqual(1); expect(result.length).toEqual(1);
+19 -5
View File
@@ -13,6 +13,7 @@ import {TemplateToken} from "@github/actions-workflow-parser/templates/tokens/in
import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token"; import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token";
import {TokenType} from "@github/actions-workflow-parser/templates/tokens/types"; import {TokenType} from "@github/actions-workflow-parser/templates/tokens/types";
import {File} from "@github/actions-workflow-parser/workflows/file"; import {File} from "@github/actions-workflow-parser/workflows/file";
import {FileProvider} from "@github/actions-workflow-parser/workflows/file-provider";
import {Position, TextDocument} from "vscode-languageserver-textdocument"; import {Position, TextDocument} from "vscode-languageserver-textdocument";
import {CompletionItem, CompletionItemKind, CompletionItemTag, Range, TextEdit} from "vscode-languageserver-types"; import {CompletionItem, CompletionItemKind, CompletionItemTag, Range, TextEdit} from "vscode-languageserver-types";
import {ContextProviderConfig} from "./context-providers/config"; import {ContextProviderConfig} from "./context-providers/config";
@@ -43,11 +44,16 @@ export function getExpressionInput(input: string, pos: number): string {
return input.substring(startPos, pos); return input.substring(startPos, pos);
} }
export type CompletionConfig = {
valueProviderConfig?: ValueProviderConfig;
contextProviderConfig?: ContextProviderConfig;
fileProvider?: FileProvider;
};
export async function complete( export async function complete(
textDocument: TextDocument, textDocument: TextDocument,
position: Position, position: Position,
valueProviderConfig?: ValueProviderConfig, config?: CompletionConfig
contextProviderConfig?: ContextProviderConfig
): Promise<CompletionItem[]> { ): Promise<CompletionItem[]> {
// Edge case: when completing a key like `foo:|`, do not calculate auto-completions // Edge case: when completing a key like `foo:|`, do not calculate auto-completions
const charBeforePos = textDocument.getText({ const charBeforePos = textDocument.getText({
@@ -71,7 +77,15 @@ export async function complete(
} }
const {token, keyToken, parent, path} = findToken(newPos, result.value); const {token, keyToken, parent, path} = findToken(newPos, result.value);
const template = await convertWorkflowTemplate(result.context, result.value, ErrorPolicy.TryConversion); const template = await convertWorkflowTemplate(
result.context,
result.value,
ErrorPolicy.TryConversion,
config?.fileProvider,
{
fetchReusableWorkflowDepth: config?.fileProvider ? 1 : 0
}
);
const workflowContext = getWorkflowContext(textDocument.uri, template, path); const workflowContext = getWorkflowContext(textDocument.uri, template, path);
// If we are inside an expression, take a different code-path. The workflow parser does not correctly create // If we are inside an expression, take a different code-path. The workflow parser does not correctly create
@@ -79,7 +93,7 @@ export async function complete(
if (token) { if (token) {
if (isBasicExpression(token) || isPotentiallyExpression(token)) { if (isBasicExpression(token) || isPotentiallyExpression(token)) {
const allowedContext = token.definitionInfo?.allowedContext || []; const allowedContext = token.definitionInfo?.allowedContext || [];
const context = await getContext(allowedContext, contextProviderConfig, workflowContext, Mode.Completion); const context = await getContext(allowedContext, config?.contextProviderConfig, workflowContext, Mode.Completion);
return getExpressionCompletionItems(token, context, newPos); return getExpressionCompletionItems(token, context, newPos);
} }
@@ -88,7 +102,7 @@ export async function complete(
const indentation = guessIndentation(newDoc, 2, true); // Use 2 spaces as default and most common for YAML const indentation = guessIndentation(newDoc, 2, true); // Use 2 spaces as default and most common for YAML
const indentString = " ".repeat(indentation.tabSize); const indentString = " ".repeat(indentation.tabSize);
const values = await getValues(token, keyToken, parent, valueProviderConfig, workflowContext, indentString); const values = await getValues(token, keyToken, parent, config?.valueProviderConfig, workflowContext, indentString);
let replaceRange: Range | undefined; let replaceRange: Range | undefined;
if (token?.range) { if (token?.range) {