From 115966533bfc4fe316bdac7bc55e6baf17d7060a Mon Sep 17 00:00:00 2001 From: William Killerud Date: Sun, 11 Aug 2024 15:39:36 +0200 Subject: [PATCH] fix: adjust when suggesting functions, variables in control flow Functions should be allowed in logic, debug etc --- .../__tests__/do-complete-modules.test.ts | 44 +++++++++++++++++++ .../features/__tests__/do-complete.test.ts | 28 ++++++------ .../src/features/do-complete.ts | 42 +++++++++--------- 3 files changed, 79 insertions(+), 35 deletions(-) diff --git a/packages/language-services/src/features/__tests__/do-complete-modules.test.ts b/packages/language-services/src/features/__tests__/do-complete-modules.test.ts index b95b4ddd..7f49a690 100644 --- a/packages/language-services/src/features/__tests__/do-complete-modules.test.ts +++ b/packages/language-services/src/features/__tests__/do-complete-modules.test.ts @@ -93,6 +93,50 @@ test("suggest sass built-ins that are forwarded by the stylesheet that is @used" ); }); +test("suggest sass built-ins that are forwarded with a prefix", async () => { + const one = fileSystemProvider.createDocument( + ['@forward "sass:math" as math-*;'], + { + uri: "test.scss", + }, + ); + const two = fileSystemProvider.createDocument([ + '@use "./test";', + "$var: test.", + ]); + + // emulate scanner of language service which adds workspace documents to the cache + ls.parseStylesheet(one); + ls.parseStylesheet(two); + + const { items } = await ls.doComplete(two, Position.create(1, 11)); + assert.notEqual( + items.length, + 0, + "Expected to get completions from the sass:math module", + ); + + // Quick sampling of the results + assert.deepStrictEqual( + items.find((annotation) => annotation.label === "$math-pi"), + { + documentation: { + kind: "markdown", + value: + "The value of the mathematical constant **π**.\n\n[Sass documentation](https://sass-lang.com/documentation/modules/math#$pi)", + }, + filterText: "test.$math-pi", + insertText: ".$math-pi", + insertTextFormat: InsertTextFormat.PlainText, + kind: CompletionItemKind.Variable, + label: "$math-pi", + labelDetails: { + detail: undefined, + }, + }, + ); +}); + test("should suggest symbol from a different document via @use", async () => { const one = fileSystemProvider.createDocument("$primary: limegreen;", { uri: "one.scss", diff --git a/packages/language-services/src/features/__tests__/do-complete.test.ts b/packages/language-services/src/features/__tests__/do-complete.test.ts index d0131d46..e8014f30 100644 --- a/packages/language-services/src/features/__tests__/do-complete.test.ts +++ b/packages/language-services/src/features/__tests__/do-complete.test.ts @@ -135,7 +135,7 @@ test("should suggest variable in @return", async () => { const { items } = await ls.doComplete(one, Position.create(3, 40)); assert.ok(items.find((item) => item.label === "$name")); - assert.isUndefined(items.find((item) => item.label === "compare")); // allow for recursion + assert.ok(items.find((item) => item.label === "compare")); // allow for recursion assert.isUndefined(items.find((item) => item.label === "mixin")); assert.isUndefined(items.find((item) => item.label === "%placeholder")); }); @@ -160,7 +160,7 @@ test("should suggest function in @return", async () => { const { items } = await ls.doComplete(one, Position.create(3, 39)); assert.ok(items.find((item) => item.label === "compare")); // allow for recursion - assert.isUndefined(items.find((item) => item.label === "$name")); + assert.ok(items.find((item) => item.label === "$name")); assert.isUndefined(items.find((item) => item.label === "mixin")); assert.isUndefined(items.find((item) => item.label === "%placeholder")); }); @@ -186,7 +186,7 @@ test("should suggest variable in @if", async () => { const { items } = await ls.doComplete(one, Position.create(4, 5)); assert.ok(items.find((item) => item.label === "$name")); - assert.isUndefined(items.find((item) => item.label === "compare")); + assert.ok(items.find((item) => item.label === "compare")); assert.isUndefined(items.find((item) => item.label === "mixin")); assert.isUndefined(items.find((item) => item.label === "%placeholder")); }); @@ -212,7 +212,7 @@ test("should suggest function in @if", async () => { const { items } = await ls.doComplete(one, Position.create(4, 4)); assert.ok(items.find((item) => item.label === "compare")); - assert.isUndefined(items.find((item) => item.label === "$name")); + assert.ok(items.find((item) => item.label === "$name")); assert.isUndefined(items.find((item) => item.label === "mixin")); assert.isUndefined(items.find((item) => item.label === "%placeholder")); }); @@ -239,7 +239,7 @@ test("should suggest variable in @else if", async () => { const { items } = await ls.doComplete(one, Position.create(5, 12)); assert.ok(items.find((item) => item.label === "$name")); - assert.isUndefined(items.find((item) => item.label === "compare")); + assert.ok(items.find((item) => item.label === "compare")); assert.isUndefined(items.find((item) => item.label === "mixin")); assert.isUndefined(items.find((item) => item.label === "%placeholder")); }); @@ -266,7 +266,7 @@ test("should suggest function in @else if", async () => { const { items } = await ls.doComplete(one, Position.create(5, 12)); assert.ok(items.find((item) => item.label === "compare")); - assert.isUndefined(items.find((item) => item.label === "$name")); + assert.ok(items.find((item) => item.label === "$name")); assert.isUndefined(items.find((item) => item.label === "mixin")); assert.isUndefined(items.find((item) => item.label === "%placeholder")); }); @@ -315,7 +315,7 @@ test("should suggest variable in for @each $foo in", async () => { const { items } = await ls.doComplete(one, Position.create(4, 15)); assert.ok(items.find((item) => item.label === "$name")); - assert.isUndefined(items.find((item) => item.label === "compare")); + assert.ok(items.find((item) => item.label === "compare")); assert.isUndefined(items.find((item) => item.label === "mixin")); assert.isUndefined(items.find((item) => item.label === "%placeholder")); }); @@ -341,7 +341,7 @@ test("should suggest function in for @each $foo in", async () => { const { items } = await ls.doComplete(one, Position.create(4, 15)); assert.ok(items.find((item) => item.label === "compare")); - assert.isUndefined(items.find((item) => item.label === "$name")); + assert.ok(items.find((item) => item.label === "$name")); assert.isUndefined(items.find((item) => item.label === "mixin")); assert.isUndefined(items.find((item) => item.label === "%placeholder")); }); @@ -390,7 +390,7 @@ test("should suggest variable in @for $i from ", async () => { const { items } = await ls.doComplete(one, Position.create(4, 15)); assert.ok(items.find((item) => item.label === "$name")); - assert.isUndefined(items.find((item) => item.label === "compare")); + assert.ok(items.find((item) => item.label === "compare")); assert.isUndefined(items.find((item) => item.label === "mixin")); assert.isUndefined(items.find((item) => item.label === "%placeholder")); }); @@ -416,7 +416,7 @@ test("should suggest function in @for $i from ", async () => { const { items } = await ls.doComplete(one, Position.create(4, 15)); assert.ok(items.find((item) => item.label === "compare")); - assert.isUndefined(items.find((item) => item.label === "$name")); + assert.ok(items.find((item) => item.label === "$name")); assert.isUndefined(items.find((item) => item.label === "mixin")); assert.isUndefined(items.find((item) => item.label === "%placeholder")); }); @@ -442,7 +442,7 @@ test("should suggest variable @for $i from 1 to ", async () => { const { items } = await ls.doComplete(one, Position.create(4, 19)); assert.ok(items.find((item) => item.label === "$name")); - assert.isUndefined(items.find((item) => item.label === "compare")); + assert.ok(items.find((item) => item.label === "compare")); assert.isUndefined(items.find((item) => item.label === "mixin")); assert.isUndefined(items.find((item) => item.label === "%placeholder")); }); @@ -468,7 +468,7 @@ test("should suggest function @for $i from 1 to ", async () => { const { items } = await ls.doComplete(one, Position.create(4, 23)); assert.ok(items.find((item) => item.label === "compare")); - assert.isUndefined(items.find((item) => item.label === "$name")); + assert.ok(items.find((item) => item.label === "$name")); assert.isUndefined(items.find((item) => item.label === "mixin")); assert.isUndefined(items.find((item) => item.label === "%placeholder")); }); @@ -494,7 +494,7 @@ test("should suggest variable in @for $i from 1 through ", async () => { const { items } = await ls.doComplete(one, Position.create(4, 24)); assert.ok(items.find((item) => item.label === "$name")); - assert.isUndefined(items.find((item) => item.label === "compare")); + assert.ok(items.find((item) => item.label === "compare")); assert.isUndefined(items.find((item) => item.label === "mixin")); assert.isUndefined(items.find((item) => item.label === "%placeholder")); }); @@ -520,7 +520,7 @@ test("should suggest function in @for $i from 1 through ", async () => { const { items } = await ls.doComplete(one, Position.create(4, 24)); assert.ok(items.find((item) => item.label === "compare")); - assert.isUndefined(items.find((item) => item.label === "$name")); + assert.ok(items.find((item) => item.label === "$name")); assert.isUndefined(items.find((item) => item.label === "mixin")); assert.isUndefined(items.find((item) => item.label === "%placeholder")); }); diff --git a/packages/language-services/src/features/do-complete.ts b/packages/language-services/src/features/do-complete.ts index c99d87db..03c2c3bf 100644 --- a/packages/language-services/src/features/do-complete.ts +++ b/packages/language-services/src/features/do-complete.ts @@ -447,23 +447,29 @@ export class DoComplete extends LanguageFeature { reError.test(lineBeforePosition) || reWarn.test(lineBeforePosition); - if ((isControlFlow || isPropertyValue) && !isEmptyValue && !isQuotes) { - if (context.namespace && currentWord.endsWith(".")) { + if (isControlFlow) { + context.isVariableContext = true; + context.isFunctionContext = true; + return context; + } + + if (reMixinReference.test(lineBeforePosition)) { + context.isMixinContext = true; + if (reCompletedMixinWithParametersReference.test(lineBeforePosition)) { + context.isMixinContext = false; context.isVariableContext = true; - } else { - context.isVariableContext = currentWord.includes("$"); + context.isFunctionContext = true; } - } else if (isQuotes) { - context.isVariableContext = isInterpolation; - } else { - context.isVariableContext = - currentWord.startsWith("$") || isInterpolation || isEmptyValue; + return context; } - if ((isControlFlow || isPropertyValue) && !isEmptyValue && !isQuotes) { + if (isPropertyValue && !isEmptyValue && !isQuotes) { if (context.namespace) { context.isFunctionContext = true; + context.isVariableContext = true; } else { + context.isVariableContext = currentWord.includes("$"); + const lastChar = lineBeforePosition.charAt( lineBeforePosition.length - 1, ); @@ -475,18 +481,12 @@ export class DoComplete extends LanguageFeature { } } } else if (isQuotes) { + context.isVariableContext = isInterpolation; context.isFunctionContext = isInterpolation; - } else if (isPropertyValue && isEmptyValue) { - context.isFunctionContext = true; - } - - if (!isPropertyValue && reMixinReference.test(lineBeforePosition)) { - context.isMixinContext = true; - if (reCompletedMixinWithParametersReference.test(lineBeforePosition)) { - context.isMixinContext = false; - context.isVariableContext = true; - context.isFunctionContext = true; - } + } else { + context.isVariableContext = + currentWord.startsWith("$") || isInterpolation || isEmptyValue; + context.isFunctionContext = isPropertyValue && isEmptyValue; } return context;