Skip to content

Commit

Permalink
Merge pull request #12 from wkillerud/feat/astro
Browse files Browse the repository at this point in the history
feat: add support for SCSS in Astro + some bugfixes for completions
  • Loading branch information
wkillerud authored Jul 30, 2022
2 parents 9c685b3 + 2b6cf24 commit 8c776d7
Show file tree
Hide file tree
Showing 33 changed files with 798 additions and 331 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ This method of debugging is **not recommended** if you want to debug the functio

If you still want to debug the integration tests there are a few things to keep in mind, since tests run in this way use your main stable install of VS Code (not Insiders, like from the terminal):

- You will need to install Vetur (`octref.vetur`) and Svelte for VS Code (`svelte.svelte-vscode`)
- You will need to install Vetur (`octref.vetur`), Astro (`astro-build.astro-vscode`) and Svelte for VS Code (`svelte.svelte-vscode`).
- You **must** use default settings for Some Sass. Tip: use the included Workspace Settings.
- To compile changes in test code, run `npm run test:compile`

Expand Down
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Some Sass provides code suggestions, documentation and code navigation for SCSS.
- Rich documentation through [SassDoc](http://sassdoc.com).
- Suggestions and hover info for built-in Sass modules, when used with `@use`.

Supports standalone SCSS, as well as style blocks inside Vue and Svelte components.
Supports standalone SCSS, as well as style blocks inside Vue, Svelte and Astro components.

Based on SCSS Intellisense by [Denis Malinochkin and contributors](https://github.com/mrmlnc/vscode-scss). Uses the built-in VS Code language server for SCSS.

Expand Down Expand Up @@ -81,7 +81,7 @@ To use this feature, right-click a variable, mixin or function and choose

**Improved code suggestions for variables under namespaces**

When providing code suggestions under namespaces (`@use "~namespace"`, then typing `namespace.$`)
When providing code suggestions under namespaces (`@use "namespace"`, then typing `namespace.$`)
you may see the default word-based suggestions appear again. VS Code seems to think of `$` as a
new fresh start for suggestions, so it will start matching any variable in the current document.

Expand All @@ -91,8 +91,19 @@ and use the provided suggestion. This way you can keep word based suggestions if

```jsonc
{
"editor.wordBasedSuggestions": false,
"somesass.suggestOnlyFromUse": true
// Recommended if you don't rely on @import
"somesass.suggestOnlyFromUse": true,

// Optional, if you get suggestions from the current document after namespace.$ (you don't need the $ for narrowing down suggestions)
"editor.wordBasedSuggestions": false,

// Add `scss` to the list of excluded languages for Emmet to avoid suggestions in Vue, Svelte or Astro files.
// VS Code understands that <style lang="scss">`blocks are SCSS, and so won't show Emmet suggestions in that block.
"emmet.excludeLanguages": [
// Markdown is excluded by default in VS Code
"markdown",
"scss"
]
}
```

Expand Down
5 changes: 5 additions & 0 deletions fixtures/e2e/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# editorconfig.org

[*]
indent_style = space
indent_size = 2
33 changes: 33 additions & 0 deletions fixtures/e2e/completion/AppButton.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
---

<p class="foo"></p>

<style lang="scss">
$color: blue;
$fonts: -apple-system;

.foo {
color: $;
}

@import "~foo/bar.scss";

.foo {
color: $;
}

@import "./partial";

.foo {
color: $;
}

@use '../namespace' as ns;

.foo {
color: ns.
@include ns.
--runtime-var: var(--other-var, #{ns.})
}
</style>
1 change: 1 addition & 0 deletions fixtures/e2e/completion/AppButton.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ $fonts: -apple-system;
.foo {
color: ns.
@include ns.
--runtime-var: var(--other-var, #{ns.})
}
</style>
1 change: 1 addition & 0 deletions fixtures/e2e/completion/AppButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ $fonts: -apple-system;
.foo {
color: ns.
@include ns.
--runtime-var: var(--other-var, #{ns.})
}
</style>
1 change: 1 addition & 0 deletions fixtures/e2e/completion/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ $fonts: -apple-system;
.foo {
color: ns.
@include ns.
--runtime-var: var(--other-var, #{ns.})
}
23 changes: 23 additions & 0 deletions fixtures/e2e/definition/AppButton.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
---

<p class="foo" />

<style lang="scss">
@use '../functions' as *;
@use '../mixins' as *;
@use '../variables' as *;

.test {
content: $variable + function();

@include mixin();
}

@use '../namespace' as ns;

.foo {
color: ns.$var-var-variable;
@include ns.mix-mix-mixin;
}
</style>
19 changes: 19 additions & 0 deletions fixtures/e2e/diagnostics/AppButton.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
---

<p class="foo" />

<style lang="scss">
@use './helpers' as *;

/// @deprecated Use something else
$_old-private-variable: 1px;

.test {
content: $old-variable + old-function();

@include old-mixin();

content: $_old-private-variable;
}
</style>
23 changes: 23 additions & 0 deletions fixtures/e2e/hover/AppButton.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
---

<p class="foo" />

<style lang="scss">
@use '../functions' as *;
@use '../mixins' as *;
@use '../variables' as *;

.test {
content: $variable + function();

@include mixin();
}

@use '../namespace' as ns;

.foo {
color: ns.$var-var-variable;
@include ns.mix-mix-mixin;
}
</style>
25 changes: 25 additions & 0 deletions fixtures/e2e/signature/AppButton.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
---

<p class="foo" />

<style lang="scss">
@use '../functions' as *;
@use '../mixins' as *;

.test {
@include square();
@include square(1,);

content: pow() + pow(1,);
}

@use '../namespace' as ns;

.foo {
@include ns.mix-mix-square();
@include ns.mix-mix-square(1,);

content: ns.fun-fun-pow() + ns.fun-fun-pow(1,);
}
</style>
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "some-sass",
"displayName": "Some Sass",
"description": "Provides code suggestions, documentation and code navigation for modern SCSS",
"version": "2.4.0",
"version": "2.5.0",
"publisher": "SomewhatStationery",
"license": "MIT",
"engines": {
Expand All @@ -27,7 +27,8 @@
"activationEvents": [
"onLanguage:scss",
"onLanguage:vue",
"onLanguage:svelte"
"onLanguage:svelte",
"onLanguage:astro"
],
"main": "./dist/client.js",
"contributes": {
Expand Down
1 change: 1 addition & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ function buildClientOptions(workspace: URI): LanguageClientOptions {
{ scheme: 'file', language: 'scss', pattern },
{ scheme: 'file', language: 'vue', pattern },
{ scheme: 'file', language: 'svelte', pattern },
{ scheme: 'file', language: 'astro', pattern },
],
synchronize: {
configurationSection: ['somesass'],
Expand Down
21 changes: 15 additions & 6 deletions src/unsafe/providers/completion/completion-context.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { getCurrentWord, getTextBeforePosition } from '../../utils/string';

import type { TextDocument } from 'vscode-languageserver-textdocument';
import type { ISettings } from '../../types/settings';

type SupportedExtensions = 'scss' | 'vue' | 'svelte' | 'astro';

export type CompletionContext = {
word: string;
textBeforeWord: string;
Expand All @@ -12,6 +15,7 @@ export type CompletionContext = {
variable: boolean;
function: boolean;
mixin: boolean;
originalExtension: SupportedExtensions;
};

const rePropertyValue = /.*:\s*/;
Expand Down Expand Up @@ -90,14 +94,15 @@ function isInterpolationContext(text: string): boolean {
return text.includes('#{');
}

function checkNamespaceContext(currentWord: string): string | null {
function checkNamespaceContext(currentWord: string, isInterpolation: boolean): string | null {
if (currentWord.length === 0 || !currentWord.includes(".")) {
return null;
}
return currentWord.substring(0, currentWord.indexOf("."));
// Skip #{ if this is interpolation
return currentWord.substring(isInterpolation ? 2 : 0, currentWord.indexOf("."));
}

export function createCompletionContext(text: string, offset: number, settings: ISettings): CompletionContext {
export function createCompletionContext(document: TextDocument, text: string, offset: number, settings: ISettings): CompletionContext {
const currentWord = getCurrentWord(text, offset);
const textBeforeWord = getTextBeforePosition(text, offset);

Expand All @@ -112,7 +117,10 @@ export function createCompletionContext(text: string, offset: number, settings:
const isQuotes = reQuotes.test(textBeforeWord.replace(reQuotedValueInString, ''));

// Is namespace, e.g. `namespace.$var` or `@include namespace.mixin` or `namespace.func()`
const namespace = checkNamespaceContext(currentWord)
const namespace = checkNamespaceContext(currentWord, isInterpolation)

const lastDot = document.uri.lastIndexOf('.');
const originalExtension = document.uri.substring(lastDot + 1) as SupportedExtensions;

return {
word: currentWord,
Expand All @@ -131,6 +139,7 @@ export function createCompletionContext(text: string, offset: number, settings:
Boolean(namespace),
settings
),
mixin: checkMixinContext(textBeforeWord, isPropertyValue)
mixin: checkMixinContext(textBeforeWord, isPropertyValue),
originalExtension,
};
}
}
60 changes: 60 additions & 0 deletions src/unsafe/providers/completion/completion-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { ScssMixin, ScssParameter } from '../../types/symbols';
import { asDollarlessVariable } from '../../utils/string';

export const rePrivate = /^\$?[_-].*$/;

/**
* Return Mixin as string.
*/
export function makeMixinDocumentation(symbol: ScssMixin): string {
const args = symbol.parameters.map(item => `${item.name}: ${item.value}`).join(', ');
return `${symbol.name}(${args})`;
}

/**
* Use the SnippetString syntax to provide smart completions of parameter names.
*/
export function mapParameterSnippet(p: ScssParameter, index: number): string {
if (p.sassdoc?.type?.length) {
const choices = parseStringLiteralChoices(p.sassdoc.type)
if (choices.length) {
return "${" + (index + 1) + "|" + choices.join(",") + "|}"
}
}

return "${" + (index + 1) + ":" + asDollarlessVariable(p.name) + "}";
}

export function mapParameterSignature(p: ScssParameter): string {
return p.value ? `${p.name}: ${p.value}` : p.name;
}

const reStringLiteral = /^["'].+["']$/; // yes, this will match 'foo", but let the parser deal with yelling about that.

/**
* @param docstring A TypeScript-like string of accepted string literal values, for example `"standard" | "entrance" | "exit"`.
*/
export function parseStringLiteralChoices(docstring: string | string[]): string[] {
const docstrings = typeof docstring === "string" ? [docstring] : docstring;
const result: string[] = [];

for (const doc of docstrings) {
const parts = doc.split("|");
if (parts.length === 1) {
// This may be a docstring to indicate only a single valid string literal option.
const trimmed = doc.trim();
if (reStringLiteral.test(trimmed)) {
result.push(trimmed);
}
} else {
for (const part of parts) {
const trimmed = part.trim();
if (reStringLiteral.test(trimmed)) {
result.push(trimmed);
}
}
}
}

return result;
}
Loading

0 comments on commit 8c776d7

Please sign in to comment.