diff --git a/languageservice/src/complete.test.ts b/languageservice/src/complete.test.ts index 9ffa945..0798cde 100644 --- a/languageservice/src/complete.test.ts +++ b/languageservice/src/complete.test.ts @@ -465,7 +465,7 @@ jobs: ]); // One-of (scalar variant) - const concurrencyScalar = result.find(x => x.label === "concurrency" && x.detail === undefined); + const concurrencyScalar = result.find(x => x.label === "concurrency" && x.labelDetails === undefined); expect(concurrencyScalar?.textEdit?.newText).toEqual("concurrency: "); }); @@ -489,7 +489,7 @@ jobs: ]); // One-of (scalar variant) - const concurrencyScalar = result.find(x => x.label === "concurrency" && x.detail === undefined); + const concurrencyScalar = result.find(x => x.label === "concurrency" && x.labelDetails === undefined); expect(concurrencyScalar?.textEdit?.newText).toEqual("concurrency: "); }); }); @@ -530,7 +530,7 @@ jobs: const result = await complete(...getPositionFromCursor(input)); // Scalar variant inserts "types: " - const scalarVariant = result.find(x => x.label === "types" && x.detail === undefined); + const scalarVariant = result.find(x => x.label === "types" && x.labelDetails === undefined); expect(scalarVariant?.textEdit?.newText).toEqual("types: "); }); @@ -586,8 +586,8 @@ jobs: // Should have both check_run (scalar) and check_run with detail "full syntax" const checkRunVariants = result.filter(x => x.label === "check_run"); - expect(checkRunVariants.some(x => x.detail === undefined)).toBe(true); - expect(checkRunVariants.some(x => x.detail === "full syntax")).toBe(true); + expect(checkRunVariants.some(x => x.labelDetails === undefined)).toBe(true); + expect(checkRunVariants.some(x => x.labelDetails?.description === "full syntax")).toBe(true); }); it("shows all three variants for scalar+sequence+mapping one-of", async () => { @@ -602,9 +602,9 @@ jobs: // Should have runs-on (scalar), runs-on with detail "list", and runs-on with detail "full syntax" const runsOnVariants = result.filter(x => x.label === "runs-on"); expect(runsOnVariants.length).toBe(3); - expect(runsOnVariants.some(x => x.detail === undefined)).toBe(true); - expect(runsOnVariants.some(x => x.detail === "list")).toBe(true); - expect(runsOnVariants.some(x => x.detail === "full syntax")).toBe(true); + expect(runsOnVariants.some(x => x.labelDetails === undefined)).toBe(true); + expect(runsOnVariants.some(x => x.labelDetails?.description === "list")).toBe(true); + expect(runsOnVariants.some(x => x.labelDetails?.description === "full syntax")).toBe(true); }); it("generates correct insertText for one-of variants in parent mode", async () => { @@ -619,13 +619,17 @@ jobs: const runsOnVariants = result.filter(x => x.label === "runs-on"); // Scalar: just key with colon and space - expect(runsOnVariants.find(x => x.detail === undefined)?.textEdit?.newText).toEqual("runs-on: "); + expect(runsOnVariants.find(x => x.labelDetails === undefined)?.textEdit?.newText).toEqual("runs-on: "); // Sequence: key with colon, newline, and list item - expect(runsOnVariants.find(x => x.detail === "list")?.textEdit?.newText).toEqual("runs-on:\n - "); + expect(runsOnVariants.find(x => x.labelDetails?.description === "list")?.textEdit?.newText).toEqual( + "runs-on:\n - " + ); // Mapping: key with colon, newline, and indentation for nested keys - expect(runsOnVariants.find(x => x.detail === "full syntax")?.textEdit?.newText).toEqual("runs-on:\n "); + expect(runsOnVariants.find(x => x.labelDetails?.description === "full syntax")?.textEdit?.newText).toEqual( + "runs-on:\n " + ); }); it("generates correct insertText for one-of variants in parent mode", async () => { @@ -654,11 +658,11 @@ jobs: const runsOnVariants = result.filter(x => x.label === "runs-on"); // Scalar: no sortText needed (sorts naturally first) - expect(runsOnVariants.find(x => x.detail === undefined)?.sortText).toBeUndefined(); + expect(runsOnVariants.find(x => x.labelDetails === undefined)?.sortText).toBeUndefined(); // Sequence and mapping: sortText controls ordering - expect(runsOnVariants.find(x => x.detail === "list")?.sortText).toEqual("runs-on 1"); - expect(runsOnVariants.find(x => x.detail === "full syntax")?.sortText).toEqual("runs-on 2"); + expect(runsOnVariants.find(x => x.labelDetails?.description === "list")?.sortText).toEqual("runs-on 1"); + expect(runsOnVariants.find(x => x.labelDetails?.description === "full syntax")?.sortText).toEqual("runs-on 2"); }); it("scalar event completion inserts inline without newline", async () => { @@ -672,13 +676,13 @@ jobs: const push = result.find(x => x.label === "push"); expect(push?.textEdit?.newText).toEqual("push"); - const checkRun = result.find(x => x.label === "check_run" && x.detail === undefined); + const checkRun = result.find(x => x.label === "check_run" && x.labelDetails === undefined); expect(checkRun?.textEdit?.newText).toEqual("check_run"); // Full syntax form should NOT be shown in Key mode - it requires a newline // which is confusing when typing inline. Users who want the mapping form // can use `on (full syntax)` at the parent level. - expect(result.find(x => x.label === "check_run" && x.detail === "full syntax")).toBeUndefined(); + expect(result.find(x => x.label === "check_run" && x.labelDetails?.description === "full syntax")).toBeUndefined(); }); it("filters to sequence options when user has started a sequence", async () => { diff --git a/languageservice/src/complete.ts b/languageservice/src/complete.ts index 98ae1be..65e37d0 100644 --- a/languageservice/src/complete.ts +++ b/languageservice/src/complete.ts @@ -194,7 +194,7 @@ export async function complete( const item: CompletionItem = { label: value.label, - detail: value.detail, + labelDetails: value.labelDetail ? {description: value.labelDetail} : undefined, filterText: value.filterText, sortText: value.sortText, documentation: value.description && { diff --git a/languageservice/src/e2e.test.ts b/languageservice/src/e2e.test.ts index b03b572..9b36fcb 100644 --- a/languageservice/src/e2e.test.ts +++ b/languageservice/src/e2e.test.ts @@ -22,7 +22,9 @@ describe("end-to-end", () => { expect(result).not.toBeUndefined(); expect(result.length).toEqual(13); - const labelsWithDetails = result.map(x => (x.detail ? `${x.label} (${x.detail})` : x.label)); + const labelsWithDetails = result.map(x => + x.labelDetails?.description ? `${x.label} (${x.labelDetails.description})` : x.label + ); expect(labelsWithDetails).toEqual([ "concurrency", "concurrency (full syntax)", diff --git a/languageservice/src/value-providers/config.ts b/languageservice/src/value-providers/config.ts index 63cd694..11d4872 100644 --- a/languageservice/src/value-providers/config.ts +++ b/languageservice/src/value-providers/config.ts @@ -7,8 +7,8 @@ export interface Value { /** Optional description to show when auto-completing */ description?: string; - /** Optional detail shown after the label, e.g. type or kind information */ - detail?: string; + /** Optional qualifier shown inline after the label, e.g. "full syntax" or "list" */ + labelDetail?: string; /** Whether this value is deprecated */ deprecated?: boolean; diff --git a/languageservice/src/value-providers/definition.ts b/languageservice/src/value-providers/definition.ts index f0ff41e..3939dd1 100644 --- a/languageservice/src/value-providers/definition.ts +++ b/languageservice/src/value-providers/definition.ts @@ -216,14 +216,14 @@ function oneOfValues( } /** - * Deduplicates values by label and detail. - * Values with the same label but different details are preserved as distinct items. + * Deduplicates values by label and labelDetail. + * Values with the same label but different labelDetails are preserved as distinct items. */ function distinctValues(values: Value[]): Value[] { const map = new Map(); for (const value of values) { - // Include detail in the key to preserve variants with different details - const key = value.detail ? `${value.label}\0${value.detail}` : value.label; + // Include labelDetail in the key to preserve variants with different details + const key = value.labelDetail ? `${value.label}\0${value.labelDetail}` : value.label; map.set(key, value); } return Array.from(map.values()); @@ -326,7 +326,7 @@ function expandOneOfToCompletions( results.push({ label: key, description, - detail: needsQualifier ? "list" : undefined, + labelDetail: needsQualifier ? "list" : undefined, insertText, sortText: needsQualifier ? `${key} 1` : undefined }); @@ -340,7 +340,7 @@ function expandOneOfToCompletions( results.push({ label: key, description, - detail: needsQualifier ? "full syntax" : undefined, + labelDetail: needsQualifier ? "full syntax" : undefined, insertText, sortText: needsQualifier ? `${key} 2` : undefined });