diff --git a/.scripts/release.mjs b/.scripts/release.mjs index 786646e5..2a82e71b 100644 --- a/.scripts/release.mjs +++ b/.scripts/release.mjs @@ -22,6 +22,7 @@ async function call(command) { async function run() { await call(`git checkout main`); await call(`git pull`); + await call(`npm run clean`); await call(`npm clean-install`); await call(`npm run build`); await call(`npm run release`); diff --git a/docs/src/README.md b/docs/src/README.md index 18644fb5..f5599f86 100644 --- a/docs/src/README.md +++ b/docs/src/README.md @@ -8,7 +8,7 @@ Some features include: - Workspace-wide code navigation and refactoring, such as Rename Symbol. - Rich documentation through [SassDoc][sassdoc]. - Language features for [`%placeholders`][placeholder], both when using them and writing them. -- Support for both SCSS and intended [syntaxes]. +- Support for both SCSS and intended [syntaxes], as well as CSS. ![](./images/highlight-reel.gif) @@ -20,11 +20,11 @@ You can find the extension here: - On the [Open VSX Registry][openvsx]. - In the [Releases section on GitHub][ghreleases]. -See the User guide section to learn more about what the extension can do. +See the User guide section to learn more about what the extension can do, or jump into [Settings](./user-guide/settings.md). ## Some Sass Language Server -Some Sass is also a language server using the [Language Server Protocol (LSP)][lsp]. +Some Sass is also a language server using the [Language Server Protocol (LSP)][lsp]. It can be used for both SCSS, Sass (indented) and CSS. The language server is [published on npm][npm], and can be used with any editor that has an LSP client. See [Getting started](./language-server/getting-started.md) to learn more. diff --git a/docs/src/images/usage/settings-built-in.png b/docs/src/images/usage/settings-built-in.png new file mode 100644 index 00000000..43e522c3 Binary files /dev/null and b/docs/src/images/usage/settings-built-in.png differ diff --git a/docs/src/language-server/configure-a-client.md b/docs/src/language-server/configure-a-client.md index 32e4aa8a..a4814785 100644 --- a/docs/src/language-server/configure-a-client.md +++ b/docs/src/language-server/configure-a-client.md @@ -2,41 +2,43 @@ An editor needs a language client for the [Language Server Protocol (LSP)][lsp] to use a language server. -To configure a client for an editor that doesn't have one yet, check the documentation for your editor to see if it supports LSP natively. If not, there may be an extension, add-on or plugin that adds support for LSP. +Your editor [may have a client already](./existing-clients.md). If not, check the documentation for your editor to see if it supports LSP natively. There may be an extension, add-on or plugin that adds support for LSP if it's not built in. + +## Language clients + +This list of [language client implementations][languageclients] may be a helpful starting point. You can also look at how [existing clients](./existing-clients.md) are set up. + +### Log messages sent by VS Code to the server + +If you're having trouble it might be helpful to compare your client with VS Code's. To log the messages sent between VS Code and the language server, add this to [your `settings.json`](https://code.visualstudio.com/docs/getstarted/settings#_settingsjson) (thank you to [Stefan Schlichthärle](https://www.sscit.de/2021/04/15/trace-lsp-in-vscode.html)). + +```json +"some-sass.trace.server": "verbose" +``` + +Now you can open a Sass file, then open the Output panel (View menu -> Output) to see the messages. ## Settings -The language server requests settings via the [`workspace/configuration` message](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_configuration), on the `somesass` key. All fields are optional. +The language server requests [settings](https://wkillerud.github.io/some-sass/user-guide/settings.html) via the [`workspace/configuration` message](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_configuration), on the `somesass` key. All fields are optional. You can also configure the language server by sending the [`workspace/didChangeConfiguration` message](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_didChangeConfiguration). -While settings keys are documented with dot-notation, the shape of the settings is a nested object. +While settings keys are documented with dot-notation, the shape of the settings is a nested object. Your editor may be able to translate from dot-notation to a properly formated object, but not every editor allows this. -For example, while we may document `"somesass.loadPaths": []` (and write it this way in `settings.json` in Code), the actual shape of the settings object sent to the server looks like this. +For example, while we may document `"somesass.workspace.loadPaths": []` (and write it this way in `settings.json` in VS Code), the actual shape of the settings object sent to the server looks like this. ```json { "settings": { "somesass": { - "loadPaths": [] + "workspace": { + "loadPaths": [] + } } } } ``` -## Existing clients - -This list of [language client implementations][languageclients] may be a helpful starting point. You may also want to look at [existing clients](./existing-clients.md). - -### Log messages sent by VS Code to the server - -If you're having trouble it might be helpful to compare your client with VS Code's. To log the messages sent between VS Code and the language server, add this to [your `settings.json`](https://code.visualstudio.com/docs/getstarted/settings#_settingsjson) (thank you to [Stefan Schlichthärle](https://www.sscit.de/2021/04/15/trace-lsp-in-vscode.html)). - -```json -"some-sass.trace.server": "verbose" -``` - -Now you can open a Sass file, then open the Output panel (View menu -> Output) to see the messages. - [lsp]: https://microsoft.github.io/language-server-protocol/ [languageclients]: https://microsoft.github.io/language-server-protocol/implementors/tools/ diff --git a/docs/src/language-server/existing-clients.md b/docs/src/language-server/existing-clients.md index dd2e5b8d..153e1fc5 100644 --- a/docs/src/language-server/existing-clients.md +++ b/docs/src/language-server/existing-clients.md @@ -4,6 +4,6 @@ In addition to the extension for Visual Studio Code and VSCodium, these are editors with ready-configured clients, maintained by the community. + - [Helix](./helix.md) - [Neovim](./neovim.md) - diff --git a/docs/src/language-server/getting-started.md b/docs/src/language-server/getting-started.md index 463d887f..4f345ebf 100644 --- a/docs/src/language-server/getting-started.md +++ b/docs/src/language-server/getting-started.md @@ -2,7 +2,9 @@ Some Sass is a language server using the [Language Server Protocol (LSP)][lsp]. -The language server is [published independently to npm][npm], and can be used with any editor that has an LSP client. The server is designed to run alongside the [VS Code CSS language server](https://github.com/hrsh7th/vscode-langservers-extracted). +The language server is [published independently to npm][npm], and can be used with any editor that has an LSP client. + +It's recommended you turn off any existing language server that handles SCSS and Sass. You may also use this language server to handle CSS. Its feature set matches that of `vscode-css-language-server`. ## Getting started @@ -18,9 +20,19 @@ Then start the language server like so: some-sass-language-server --stdio ``` -**Options** +Tweak the log level by using the `--loglevel` argument, or by using the `somesass.workspace.logLevel` setting. Available loglevels are: + +- silent +- fatal +- error +- warn +- info (default) +- debug +- trace -`--debug` – runs the development build of the language server, helpful to get more context if the server crashes +```sh +some-sass-language-server --stdio --loglevel debug +``` ## Configure your editor's client diff --git a/docs/src/language-server/helix.md b/docs/src/language-server/helix.md index 55eb2fa7..ed17a316 100644 --- a/docs/src/language-server/helix.md +++ b/docs/src/language-server/helix.md @@ -8,16 +8,26 @@ You can configure new language servers in [`.config/helix/languages.toml`](https [language-server.some-sass-language-server] command = "some-sass-language-server" args = ["--stdio"] -config = { somesass = { loadPaths = [] } } +# see https://wkillerud.github.io/some-sass/user-guide/settings.html for all available settings +config = { somesass = { workspace = { loadPaths = [] } } } [[language]] name = "scss" language-servers = [ - { name = "some-sass-language-server" }, - { name = "vscode-css-language-server" }, + { name = "some-sass-language-server" } ] ``` The language server will start once you open an SCSS file. +You can also use it for CSS. + +```toml +[[language]] +name = "css" +language-servers = [ + { name = "some-sass-language-server" } +] +``` + At time of writing there doesn't seem to be a grammar for Sass indented available in Helix. diff --git a/docs/src/user-guide/settings.md b/docs/src/user-guide/settings.md index 5a33823f..fe6ae526 100644 --- a/docs/src/user-guide/settings.md +++ b/docs/src/user-guide/settings.md @@ -4,140 +4,181 @@ This document describes the settings available in Some Sass. ## Recommended settings -These are the recommended settings: +These are the recommended settings if you're just getting started. ```jsonc { // Recommended if you don't rely on @import - "somesass.suggestFromUseOnly": true, + "somesass.scss.completion.suggestFromUseOnly": true, + "somesass.sass.completion.suggestFromUseOnly": true, - // Optional, if you get suggestions from the current document after namespace.$ (you don't need the $ for narrowing down suggestions) + // Optional, if you get suggestions from the current document after namespace.$ (you don't need to type the $ for narrowing down suggestions) "editor.wordBasedSuggestions": false, } ``` -### About word-based suggestions +### Going all in on Some Sass -When you get completion suggestions and type `namespace.$`, Visual Studio Code treats `$` as a fresh start for suggestions. It will start matching any variable in the current document. There are two ways to get around this: +If you don't need language features for [Less](https://lesscss.org/) and don't rely on the built-in formatter, we recommend that you: -1. Turn off word-based suggestions by setting `"editor.wordBasedSuggestions": false`. -2. Don't type the `$` when you write the variable name, let completions fill it out for you. +1. turn off the built-in CSS/SCSS/Less language extension in Visual Studio Code +2. configure Some Sass to turn on all features for CSS, SCSS and Sass indented -With the second approach you can keep word-based suggestions turned on. +This way you get the best experience without Some Sass and VS Code getting in each others way. -## Settings reference - -These are the settings you can use to tune Some Sass. - -### Code suggestion - -#### Only include suggestions from used modules - -If your project is on the modern module syntax (`@use` and `@forward` instead of `@import`), you may want to turn -on this setting. - -With this setting turned on, Some Sass will only suggest variables, mixins and functions from the namespaces that are -in use in the open document. This setting will be turned on by default at some point after `@import` becomes CSS-only. - -- JSON key: `somesass.suggestFromUseOnly`. -- Default value: `false`. - -#### Suggest variables, mixins, and functions from the open document - -Visual Studio Code has built-in suggestions for variables, mixins and functions created in the open document. - -By default Some Sass will _not_ send suggestions for the same symbols. -If you prefer the suggestions from Some Sass (for instance if you use SassDoc), you can opt in by turning on this setting. -There will unfortunately be duplicates. - -- JSON key: `somesass.suggestAllFromOpenDocument` -- Default value: `false`. - -#### Suggestion style - -Mixins with `@content` SassDoc annotations and `%placeholders` get two suggestions by default: - -- One without `{ }`. -- One _with_ `{ }`. This one creates a new block, and moves the cursor inside the block. - -If you find this noisy, you can control which suggestions you would like to see: - -- All suggestions (default). -- No brackets. -- Only brackets. This still includes other suggestions, where there are no brackets to begin with. - -#### Decide when function suggestions should kick in - -Suggest functions after the specified symbols when in a string context. -For example, if you add the `/` symbol to this setting, then `background: url(images/he|)` -could suggest a `hello()` function (`|` in this case indicates cursor position). - -- JSON key: `somesass.suggestFunctionsInStringContextAfterSymbols`. -- Default value: `" (+-*%"`. +#### How to turn off the built-in language feature -#### Suggest values for CSS properties +1. Go to the Extensions tab and search for `@builtin css language features`. +2. Click the settings icon and pick Disable from the list. +3. Click Restart extension to turn it off. -By default, Some Sass triggers property value completion after selecting a CSS property. -Use this setting to disable this behavior. +![Screenshot showing the Extension tab filtered to show the css language features](../images/usage/settings-built-in.png) -An example would be accepting a suggestion for `display:` and immediately see suggestions like -`inline-block` for the value. +#### How to turn on Some Sass language features -Note that for SCSS this only applies if `somesass.suggestAllFromOpenDocument` is true, -which is not the case by default in VS Code. -Use `scss.completion.triggerPropertyValueCompletion` to configure the feature built in -to VS Code. +Now that you disabled the built-in language features you need to turn on those language features in Some Sass. -- JSON key: `somesass.triggerPropertyValueCompletion`. -- Default value: `true`. - -### Workspace - -#### Load paths - -A list of paths relative to the workspace root where the language server should look for stylesheets loaded by `@use` and `@import`. `node_modules` is always included. - -This feature can also be seen referred to as `includePaths`. - -Note that you will have to [configure your Sass compiler separately](https://sass-lang.com/documentation/cli/dart-sass/#load-path). - -As an example, say you have a stylesheet `shared/my-lib/variables.scss` and would like to import it as `my-lib/variables` in your other stylesheets. - -Add this to your settings and restart Visual Studio Code. +1. Open your [user settings JSON](https://code.visualstudio.com/docs/getstarted/settings#_settings-json-file) and paste the configuration shown below. +2. Restart VS Code to make sure the changes apply. ```json { - "somesass.loadPaths": ["shared/"] + "somesass.css.codeAction.enabled": true, + "somesass.css.colors.enabled": true, + "somesass.css.completion.enabled": true, + "somesass.css.definition.enabled": true, + "somesass.css.diagnostics.enabled": true, + "somesass.css.foldingRanges.enabled": true, + "somesass.css.highlights.enabled": true, + "somesass.css.hover.enabled": true, + "somesass.css.links.enabled": true, + "somesass.css.references.enabled": true, + "somesass.css.rename.enabled": true, + "somesass.css.selectionRanges.enabled": true, + "somesass.css.signatureHelp.enabled": true, + "somesass.css.workspaceSymbol.enabled": true, + + "somesass.scss.codeAction.enabled": true, + "somesass.scss.colors.enabled": true, + "somesass.scss.colors.includeFromCurrentDocument": true, + "somesass.scss.completion.enabled": true, + "somesass.scss.completion.css": true, + "somesass.scss.completion.includeFromCurrentDocument": true, + "somesass.scss.definition.enabled": true, + "somesass.scss.diagnostics.enabled": true, + "somesass.scss.diagnostics.lint.enabled": true, + "somesass.scss.foldingRanges.enabled": true, + "somesass.scss.highlights.enabled": true, + "somesass.scss.hover.enabled": true, + "somesass.scss.hover.documentation": true, + "somesass.scss.links.enabled": true, + "somesass.scss.references.enabled": true, + "somesass.scss.rename.enabled": true, + "somesass.scss.selectionRanges.enabled": true, + "somesass.scss.signatureHelp.enabled": true, + "somesass.scss.workspaceSymbol.enabled": true } ``` -In a different stylesheet you can then get `shared/my-lib/variables.scss` like this: - -```scss -@use "my-lib/variables"; -``` - -#### Exclude files or folders - -List of [micromatch](https://github.com/micromatch/micromatch) patterns for directories that are excluded when scanning. - -- JSON key: `somesass.scannerExclude`. -- Default value: `["**/.git/**", "**/node_modules/**", "**/bower_components/**"]`. - -#### Adjust scanner depth - -Depending on your project size, you may want to tweak this setting to control how many files are included. - -- JSON key: `somesass.scannerDepth`. -- Default: `30`. - -#### Stop scanner from following links +## Settings reference -`@deprecated` +You can configure similar settings for both SCSS, Sass (indented) and CSS. There are also some settings that apply to the workspace regardless of syntax. -If you don't want Some Sass to follow `@import`, `@use` or `@forward` links you can turn this setting off. -This will limit functionality, and is not recommended. This setting will be removed at some point -after `@import` becomes CSS-only. +### Workspace -- JSON key: `somesass.scanImportedFiles`. -- Default: `true`. +| Id | Description | Default | +| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | +| `somesass.workspace.loadPaths` | A list of paths relative to the workspace root where the language server should look for stylesheets loaded by `@use` and `@import`. `node_modules` is always included. Note that you will have to [configure your Sass compiler separately](https://sass-lang.com/documentation/cli/dart-sass/#load-path). | `[]` | +| `somesass.workspace.exclude` | List of glob patterns for directories that are excluded in the initial scan for Sass files. Files in the exclude list will still be processed if referenced by `@use`, `@forward` and `@import` (for example a depencendy you use from `node_modules`). | `["**/.git/**", "**/node_modules/**"]` | +| `somesass.workspace.logLevel` | Control how much gets logged to the Output window. Possible values are `"silent"`, `"fatal"`, `"error"`, `"warn"`, `"info"`, `"debug"` and `"trace"`. | `"info"` | +| `some-sass.trace.server` | Log the messages sent between VS Code and the Some Sass language server. Possible values are `"off"`, `"messages"` and `"verbose"` | `"off"` | + +### SCSS + +For brevity the ID column omits the `somesass.scss` prefix. For example, to use the setting `codeAction.enabled` use the ID `somesass.scss.codeAction.enabled`. + +| Id | Description | Default | +| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `codeAction.enabled` | Enable or disable all code actions. | `true` | +| `colors.enabled` | Enable or disable all color decorators. | `true` | +| `colors.includeFromCurrentDocument` | Compatibility setting for VS Code. By default the built-in SCSS server shows color decorators for variables declared in the current document. To avoid duplicates Some Sass will not show them unless you opt in. | `false` | +| `completion.enabled` | Enable or disable all completions (IntelliSense). | `true` | +| `completion.includeFromCurrentDocument` | Compatibility setting for VS Code. By default the built-in SCSS server shows suggestions for variables, mixins and functions declared in the current document. To avoid duplicates Some Sass will not suggest them unless you opt in. | `false` | +| `completion.suggestFromUseOnly` | If your project uses the new module system with @use and @forward, you may want to only include suggestions from your used modules. | `false` | +| `completion.mixinStyle` | Controls the style of suggestions for mixins. Options are `"all"`, `"nobracket"` (only show suggestions without brackets) and `"bracket"` (where brackets are suggested, don't suggest without brackets). | `"all"` | +| `completion.triggerPropertyValueCompletion` | By default, Some Sass triggers property value completion after selecting a CSS property. Use this setting to disable this behavior. | `true` | +| `completion.completePropertyWithSemicolon` | Insert semicolon at end of line when completing CSS properties. | `true` | +| `definition.enabled` | Enable or disable Go to Definition. | `true` | +| `diagnostics.enabled` | Enable or disable all diagnostics (deprecation, errors and lint rules). | `true` | +| `diagnostics.deprecation.enabled` | Enable or disable deprecation diagnostics (strike-through). | `true` | +| `diagnostics.lint.enabled` | Enable or disable all linting. | `false` | +| `diagnostics.lint.*` | For the available lint rules and what they do, see the [VS Code docs for CSS and SCSS lint settings](https://code.visualstudio.com/docs/languages/css#_customizing-css-scss-and-less-settings) | | +| `foldingRanges.enabled` | Enable or disable folding ranges. | `false` | +| `highlights.enabled` | Enable or disable highlights. | `false` | +| `hover.enabled` | Enable or disable all hover information. | `true` | +| `hover.documentation` | Show property and value documentation in CSS hovers. | `true` | +| `hover.references` | Show references to MDN in CSS hovers, Sass documentation for Sass built-in modules and SassDoc for annotations. | `true` | +| `links.enabled` | Enable or disable the link provider that lets you click an import and open the file. | `false` | +| `references.enabled` | Enable or disable Find all references. | `true` | +| `rename.enabled` | Enable or disable Rename. | `true` | +| `selectionRanges.enabled` | Enable or disable selection ranges. | `false` | +| `signatureHelp.enabled` | Enable or disable signature help. | `true` | +| `workspaceSymbol.enabled` | Enable or disable workspace symbol. | `true` | + +### Sass indented + +For brevity the ID column omits the `somesass.sass` prefix. For example, to use the setting `codeAction.enabled` use the ID `somesass.sass.codeAction.enabled`. + +| Id | Description | Default | +| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `codeAction.enabled` | Enable or disable all code actions. | `true` | +| `colors.enabled` | Enable or disable all color decorators. | `true` | +| `completion.enabled` | Enable or disable all completions (IntelliSense). | `true` | +| `completion.suggestFromUseOnly` | If your project uses the new module system with @use and @forward, you may want to only include suggestions from your used modules. | `false` | +| `completion.mixinStyle` | Controls the style of suggestions for mixins. Options are `"all"`, `"nobracket"` (only show suggestions without brackets) and `"bracket"` (where brackets are suggested, don't suggest without brackets). | `"all"` | +| `completion.triggerPropertyValueCompletion` | By default, Some Sass triggers property value completion after selecting a CSS property. Use this setting to disable this behavior. | `true` | +| `definition.enabled` | Enable or disable Go to Definition. | `true` | +| `diagnostics.enabled` | Enable or disable all diagnostics (deprecation, errors and lint rules). | `true` | +| `diagnostics.deprecation.enabled` | Enable or disable deprecation diagnostics (strike-through). | `true` | +| `diagnostics.lint.enabled` | Enable or disable all linting. | `true` | +| `diagnostics.lint.*` | For the available lint rules and what they do, see the [VS Code docs for CSS and SCSS lint settings](https://code.visualstudio.com/docs/languages/css#_customizing-css-scss-and-less-settings) | | +| `foldingRanges.enabled` | Enable or disable folding ranges. | `true` | +| `highlights.enabled` | Enable or disable highlights. | `true` | +| `hover.enabled` | Enable or disable all hover information. | `true` | +| `hover.documentation` | Show property and value documentation in CSS hovers. | `true` | +| `hover.references` | Show references to MDN in CSS hovers, Sass documentation for Sass built-in modules and SassDoc for annotations. | `true` | +| `links.enabled` | Enable or disable the link provider that lets you click an import and open the file. | `true` | +| `references.enabled` | Enable or disable Find all references. | `true` | +| `rename.enabled` | Enable or disable Rename. | `true` | +| `selectionRanges.enabled` | Enable or disable selection ranges. | `true` | +| `signatureHelp.enabled` | Enable or disable signature help. | `true` | +| `workspaceSymbol.enabled` | Enable or disable workspace symbol. | `true` | + +### CSS + +For brevity the ID column omits the `somesass.css` prefix. For example, to use the setting `codeAction.enabled` use the ID `somesass.css.codeAction.enabled`. + +| Id | Description | Default | +| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | +| `codeAction.enabled` | Enable or disable all code actions. | `false` | +| `colors.enabled` | Enable or disable all color decorators. | `false` | +| `completion.enabled` | Enable or disable all completions (IntelliSense). | `false` | +| `completion.triggerPropertyValueCompletion` | By default, Some Sass triggers property value completion after selecting a CSS property. Use this setting to disable this behavior. | `true` | +| `completion.completePropertyWithSemicolon` | Insert semicolon at end of line when completing CSS properties. | `true` | +| `definition.enabled` | Enable or disable Go to Definition. | `false` | +| `diagnostics.enabled` | Enable or disable all diagnostics (deprecation, errors and lint rules). | `false` | +| `diagnostics.deprecation.enabled` | Enable or disable deprecation diagnostics (strike-through). | `false` | +| `diagnostics.lint.enabled` | Enable or disable all linting. | `false` | +| `diagnostics.lint.*` | For the available lint rules and what they do, see the [VS Code docs for CSS and SCSS lint settings](https://code.visualstudio.com/docs/languages/css#_customizing-css-scss-and-less-settings) | | +| `foldingRanges.enabled` | Enable or disable folding ranges. | `false` | +| `highlights.enabled` | Enable or disable highlights. | `false` | +| `hover.enabled` | Enable or disable all hover information. | `false` | +| `hover.documentation` | Show property and value documentation in CSS hovers. | `false` | +| `hover.references` | Show references to MDN in CSS hovers, Sass documentation for Sass built-in modules and SassDoc for annotations. | `false` | +| `links.enabled` | Enable or disable the link provider that lets you click an import and open the file. | `false` | +| `references.enabled` | Enable or disable Find all references. | `false` | +| `rename.enabled` | Enable or disable Rename. | `false` | +| `selectionRanges.enabled` | Enable or disable selection ranges. | `false` | +| `signatureHelp.enabled` | Enable or disable signature help. | `false` | +| `workspaceSymbol.enabled` | Enable or disable workspace symbol. | `false` | +| `customData` | A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-css-languageservice/blob/master/docs/customData.md). Some Sass loads custom data on startup to enhance its CSS support for CSS custom properties (variables), at-rules, pseudo-classes, and pseudo-elements you specify in the JSON files. The file paths are relative to workspace and only workspace folder settings are considered. | `[]` | diff --git a/package-lock.json b/package-lock.json index 4074be6c..2230d969 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "ts-loader": "9.5.1", "typescript": "5.5.4", "typescript-eslint": "8.4.0", - "vitest": "2.0.5" + "vitest": "2.1.1" } }, "node_modules/@adobe/css-tools": { @@ -4968,6 +4968,441 @@ "win32" ] }, + "node_modules/@rsdoctor/client": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rsdoctor/client/-/client-0.4.4.tgz", + "integrity": "sha512-xMtOWtLR9qidnXhQTRaaMVHhNqPYApk3uN5cGQJwWJDzmNNmmVeB663sIpHPKXD8cmC/w0z4SDjWA4jue8LM2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rsdoctor/core": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rsdoctor/core/-/core-0.4.4.tgz", + "integrity": "sha512-cV1f9Fu/S9cjZ9F/oWhgIZHaX0qZJVNh2/DS9vh0gWBZJhBVPz5NBCKjA322W7hGT343sadVdys2PqIxjCHcOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rsdoctor/graph": "0.4.4", + "@rsdoctor/sdk": "0.4.4", + "@rsdoctor/types": "0.4.4", + "@rsdoctor/utils": "0.4.4", + "axios": "^1.7.2", + "enhanced-resolve": "5.12.0", + "filesize": "^10.1.6", + "fs-extra": "^11.1.1", + "lodash": "^4.17.21", + "path-browserify": "1.0.1", + "semver": "^7.6.3", + "source-map": "^0.7.4", + "webpack-bundle-analyzer": "^4.10.2" + } + }, + "node_modules/@rsdoctor/core/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@rsdoctor/core/node_modules/enhanced-resolve": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", + "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@rsdoctor/core/node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@rsdoctor/core/node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@rsdoctor/core/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rsdoctor/core/node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@rsdoctor/core/node_modules/webpack-bundle-analyzer": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", + "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "commander": "^7.2.0", + "debounce": "^1.2.1", + "escape-string-regexp": "^4.0.0", + "gzip-size": "^6.0.0", + "html-escaper": "^2.0.2", + "opener": "^1.5.2", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/@rsdoctor/core/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@rsdoctor/graph": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rsdoctor/graph/-/graph-0.4.4.tgz", + "integrity": "sha512-ZcCRo9ydqyNI5otai+qUGxw4HZQAE0Rolb9tV4aadNudl7A2Mcm6FT6yECB4jlbNECqc8B5kHKZuz971WgFkLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rsdoctor/types": "0.4.4", + "@rsdoctor/utils": "0.4.4", + "lodash": "^4.17.21", + "socket.io": "4.7.2", + "source-map": "^0.7.4" + } + }, + "node_modules/@rsdoctor/graph/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rsdoctor/rspack-plugin": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rsdoctor/rspack-plugin/-/rspack-plugin-0.4.4.tgz", + "integrity": "sha512-HwYnP82HqP5p6z8kdCJ5owULCHw1smNBd90sggElZlphzo2ihdR1SLjg8mcaE5ttDhRGqhYjaXcRCZFyaexTzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rsdoctor/core": "0.4.4", + "@rsdoctor/graph": "0.4.4", + "@rsdoctor/sdk": "0.4.4", + "@rsdoctor/types": "0.4.4", + "@rsdoctor/utils": "0.4.4", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "@rspack/core": "*" + } + }, + "node_modules/@rsdoctor/sdk": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rsdoctor/sdk/-/sdk-0.4.4.tgz", + "integrity": "sha512-Y+ySVfrFAT0GVwI0xTU/BdEsufnC/+1eLYISWTEa19MdWU/RpcLqsIdglKNbDUbS6k0AELOvl7CUuOiI0go8gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rsdoctor/client": "0.4.4", + "@rsdoctor/graph": "0.4.4", + "@rsdoctor/types": "0.4.4", + "@rsdoctor/utils": "0.4.4", + "@types/fs-extra": "^11.0.4", + "body-parser": "1.20.3", + "cors": "2.8.5", + "dayjs": "1.11.13", + "fs-extra": "^11.1.1", + "lodash": "^4.17.21", + "open": "^8.4.2", + "serve-static": "1.16.0", + "socket.io": "4.7.2", + "source-map": "^0.7.4", + "tapable": "2.2.1" + } + }, + "node_modules/@rsdoctor/sdk/node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/@rsdoctor/sdk/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@rsdoctor/sdk/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@rsdoctor/sdk/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rsdoctor/sdk/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@rsdoctor/sdk/node_modules/serve-static": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.0.tgz", + "integrity": "sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@rsdoctor/sdk/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rsdoctor/types": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rsdoctor/types/-/types-0.4.4.tgz", + "integrity": "sha512-Ltf03hd/gAazRTmrwz7SqNDqB+BHC0BBVwQi7wUVsF6MphvTaqSdAocNefTgWDeXj0WEP0Vy5wrj+aUPTbPWtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "3.4.38", + "@types/estree": "1.0.5", + "@types/tapable": "2.2.7", + "source-map": "^0.7.4" + }, + "peerDependencies": { + "@rspack/core": "*", + "webpack": "5.x" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + } + } + }, + "node_modules/@rsdoctor/types/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rsdoctor/utils": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rsdoctor/utils/-/utils-0.4.4.tgz", + "integrity": "sha512-J61vLwKdNnuToeU7ZfCIy5rYH4biMH7LfinQG9m4ySveyT9hD8z/CyxbpOlr7vdmnzBVzZCy+0aHm9UbhUpjxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.24.7", + "@rsdoctor/types": "0.4.4", + "@types/estree": "1.0.5", + "acorn": "^8.10.0", + "acorn-import-assertions": "1.9.0", + "acorn-walk": "8.3.4", + "chalk": "^4.1.2", + "connect": "3.7.0", + "deep-eql": "4.1.4", + "envinfo": "7.14.0", + "filesize": "^10.1.6", + "fs-extra": "^11.1.1", + "get-port": "5.1.1", + "json-stream-stringify": "3.0.1", + "lines-and-columns": "2.0.4", + "lodash": "^4.17.21", + "rslog": "^1.2.3", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/@rsdoctor/utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@rsdoctor/utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@rsdoctor/utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@rsdoctor/utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rsdoctor/utils/node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@rspack/binding": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.0.3.tgz", @@ -5566,6 +6001,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true, + "license": "MIT" + }, "node_modules/@somesass/language-services": { "resolved": "packages/language-services", "link": true @@ -5974,6 +6416,23 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -6028,6 +6487,17 @@ "@types/send": "*" } }, + "node_modules/@types/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", @@ -6079,6 +6549,31 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/lodash": { + "version": "4.17.7", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", + "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==", + "license": "MIT" + }, + "node_modules/@types/lodash.merge": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.9.tgz", + "integrity": "sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -6184,6 +6679,16 @@ "@types/node": "*" } }, + "node_modules/@types/tapable": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-2.2.7.tgz", + "integrity": "sha512-D6QzACV9vNX3r8HQQNTOnpG+Bv1rko+yEA82wKs3O9CQ5+XW7HI7TED17/UE7+5dIxyxZIWTxKbsBeF6uKFCwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tapable": "^2.2.0" + } + }, "node_modules/@types/vscode": { "version": "1.86.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.86.0.tgz", @@ -6495,14 +7000,14 @@ } }, "node_modules/@vitest/expect": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.5.tgz", - "integrity": "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.1.tgz", + "integrity": "sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.0.5", - "@vitest/utils": "2.0.5", + "@vitest/spy": "2.1.1", + "@vitest/utils": "2.1.1", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" }, @@ -6510,10 +7015,38 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/mocker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.1.tgz", + "integrity": "sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "^2.1.0-beta.1", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.11" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/spy": "2.1.1", + "msw": "^2.3.5", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, "node_modules/@vitest/pretty-format": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.5.tgz", - "integrity": "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.1.tgz", + "integrity": "sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6524,13 +7057,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.0.5.tgz", - "integrity": "sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.1.tgz", + "integrity": "sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.0.5", + "@vitest/utils": "2.1.1", "pathe": "^1.1.2" }, "funding": { @@ -6538,14 +7071,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.0.5.tgz", - "integrity": "sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.1.tgz", + "integrity": "sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.0.5", - "magic-string": "^0.30.10", + "@vitest/pretty-format": "2.1.1", + "magic-string": "^0.30.11", "pathe": "^1.1.2" }, "funding": { @@ -6553,9 +7086,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.5.tgz", - "integrity": "sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.1.tgz", + "integrity": "sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==", "dev": true, "license": "MIT", "dependencies": { @@ -6566,14 +7099,13 @@ } }, "node_modules/@vitest/utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.5.tgz", - "integrity": "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.1.tgz", + "integrity": "sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.0.5", - "estree-walker": "^3.0.3", + "@vitest/pretty-format": "2.1.1", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" }, @@ -7542,6 +8074,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/acorn-import-attributes": { "version": "1.9.5", "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", @@ -7563,9 +8105,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", - "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, "license": "MIT", "dependencies": { @@ -8135,6 +8677,16 @@ ], "license": "MIT" }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -9117,6 +9669,22 @@ "dev": true, "license": "MIT" }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/connect-history-api-fallback": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", @@ -9127,6 +9695,65 @@ "node": ">=0.8" } }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/connect/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -9292,6 +9919,20 @@ "dev": true, "license": "MIT" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/corser": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", @@ -9690,6 +10331,20 @@ "node": ">=4.0" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", @@ -10377,6 +11032,48 @@ "once": "^1.4.0" } }, + "node_modules/engine.io": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", + "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/enhanced-resolve": { "version": "5.17.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", @@ -10427,6 +11124,19 @@ "node": ">=6" } }, + "node_modules/envinfo": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", + "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/environment": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", @@ -11324,6 +12034,16 @@ "node": ">=10" } }, + "node_modules/filesize": { + "version": "10.1.6", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.6.tgz", + "integrity": "sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 10.4.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -11946,6 +12666,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", @@ -13763,6 +14496,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stream-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.0.1.tgz", + "integrity": "sha512-vuxs3G1ocFDiAQ/SX0okcZbtqXwgj1g71qE9+vrjJ2EkjKQlEFDAcUNRxRU8O+GekV4v5cM2qXP0Wyt/EMDBiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -14906,7 +15646,6 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, "license": "MIT" }, "node_modules/lodash.mergewith": { @@ -16491,6 +17230,16 @@ "node": ">=8" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", @@ -19071,6 +19820,15 @@ "fsevents": "~2.3.2" } }, + "node_modules/rslog": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/rslog/-/rslog-1.2.3.tgz", + "integrity": "sha512-antALPJaKBRPBU1X2q9t085K4htWDOOv/K1qhTUk7h0l1ePU/KbDqKJn19eKP0dk7PqMioeA0+fu3gyPXCsXxQ==", + "dev": true, + "engines": { + "node": ">=14.17.6" + } + }, "node_modules/run-applescript": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", @@ -19734,6 +20492,50 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/socket.io": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", + "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -20873,6 +21675,13 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyexec": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", + "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", + "dev": true, + "license": "MIT" + }, "node_modules/tinypool": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", @@ -20894,9 +21703,9 @@ } }, "node_modules/tinyspy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.0.tgz", - "integrity": "sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true, "license": "MIT", "engines": { @@ -21250,6 +22059,16 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -21669,16 +22488,15 @@ } }, "node_modules/vite-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.0.5.tgz", - "integrity": "sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.1.tgz", + "integrity": "sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.5", + "debug": "^4.3.6", "pathe": "^1.1.2", - "tinyrainbow": "^1.2.0", "vite": "^5.0.0" }, "bin": { @@ -21692,30 +22510,30 @@ } }, "node_modules/vitest": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.0.5.tgz", - "integrity": "sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.1.tgz", + "integrity": "sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@vitest/expect": "2.0.5", - "@vitest/pretty-format": "^2.0.5", - "@vitest/runner": "2.0.5", - "@vitest/snapshot": "2.0.5", - "@vitest/spy": "2.0.5", - "@vitest/utils": "2.0.5", + "@vitest/expect": "2.1.1", + "@vitest/mocker": "2.1.1", + "@vitest/pretty-format": "^2.1.1", + "@vitest/runner": "2.1.1", + "@vitest/snapshot": "2.1.1", + "@vitest/spy": "2.1.1", + "@vitest/utils": "2.1.1", "chai": "^5.1.1", - "debug": "^4.3.5", - "execa": "^8.0.1", - "magic-string": "^0.30.10", + "debug": "^4.3.6", + "magic-string": "^0.30.11", "pathe": "^1.1.2", "std-env": "^3.7.0", - "tinybench": "^2.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.0", "tinypool": "^1.0.0", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.0.5", + "vite-node": "2.1.1", "why-is-node-running": "^2.3.0" }, "bin": { @@ -21730,8 +22548,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.0.5", - "@vitest/ui": "2.0.5", + "@vitest/browser": "2.1.1", + "@vitest/ui": "2.1.1", "happy-dom": "*", "jsdom": "*" }, @@ -22876,16 +23694,20 @@ "some-sass-language-server": "bin/some-sass-language-server" }, "devDependencies": { + "@rsdoctor/rspack-plugin": "0.4.4", "@somesass/language-services": "1.7.1", + "@types/lodash.merge": "4.6.9", "@types/node": "20.16.5", "@vitest/coverage-v8": "2.0.5", "fast-glob": "3.3.2", + "lodash.merge": "4.6.2", "merge-options": "3.0.4", "path-browserify": "1.0.1", "process": "0.11.10", "shx": "0.3.4", "url": "0.11.4", "util": "0.12.5", + "vitest": "2.1.1", "vscode-languageserver": "9.0.1", "vscode-languageserver-textdocument": "1.0.12", "vscode-languageserver-types": "3.17.5", @@ -22931,7 +23753,9 @@ "license": "MIT", "dependencies": { "@somesass/vscode-css-languageservice": "1.7.0", + "@types/lodash.merge": "4.6.9", "colorjs.io": "0.5.2", + "lodash.merge": "4.6.2", "sassdoc-parser": "3.4.0" }, "devDependencies": { @@ -22981,7 +23805,7 @@ "some-sass-language-server": "1.8.3", "vscode-css-languageservice": "6.3.1", "vscode-languageclient": "9.0.1", - "vscode-uri": "3.0.7" + "vscode-uri": "3.0.8" }, "devDependencies": { "@types/mocha": "10.0.7", @@ -23019,10 +23843,6 @@ "engines": { "node": ">= 6" } - }, - "vscode-extension/node_modules/vscode-uri": { - "version": "3.0.7", - "license": "MIT" } } } diff --git a/package.json b/package.json index 854448a9..16fa0aa6 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,6 @@ "ts-loader": "9.5.1", "typescript": "5.5.4", "typescript-eslint": "8.4.0", - "vitest": "2.0.5" + "vitest": "2.1.1" } } diff --git a/packages/language-server/README.md b/packages/language-server/README.md index 81ca4928..447d310e 100644 --- a/packages/language-server/README.md +++ b/packages/language-server/README.md @@ -9,33 +9,17 @@ The language server provides: - Rich documentation through [SassDoc](http://sassdoc.com). - Language features for `%placeholder-selectors`, both when using them and writing them. - Suggestions and hover info for built-in Sass modules, when used with `@use`. -- Support for [both Sass syntaxes](https://sass-lang.com/documentation/syntax/). - -This language server is designed to run alongside the [VS Code CSS language server](https://github.com/hrsh7th/vscode-langservers-extracted). See [the settings documentation](https://wkillerud.github.io/some-sass/user-guide/settings.html#suggest-variables-mixins-and-functions-from-the-open-document) for information on tweaking the user experience. +- Support for [both Sass syntaxes](https://sass-lang.com/documentation/syntax/) as well as CSS. ## Usage -See [Editors with clients](https://github.com/wkillerud/some-sass/blob/main/README.md#editors-with-clients). If your editor is not listed, refer to your editor's documentation for integrating with a language server using the Language Server Protocol (LSP). - You can install the language server with `npm`: ```sh npm install --global some-sass-language-server ``` -Then start the language server like so: - -```sh -some-sass-language-server --stdio -``` - -**Options** - -`--debug` – runs the development build of the language server, helpful to get more context if the server crashes - -### Workspace configuration - -See [how to configure a client](https://wkillerud.github.io/some-sass/language-server/configure-a-client.html) and the [documentation for the available settings](https://wkillerud.github.io/some-sass/user-guide/settings.html). +Then see [how to configure a client](https://wkillerud.github.io/some-sass/language-server/configure-a-client.html). ## Capabilities diff --git a/packages/language-server/bin/some-sass-language-server b/packages/language-server/bin/some-sass-language-server index f44eb7ef..90e12734 100755 --- a/packages/language-server/bin/some-sass-language-server +++ b/packages/language-server/bin/some-sass-language-server @@ -1,9 +1,31 @@ #!/usr/bin/env node const path = require("path"); +const fs = require("fs"); -const args = process.argv.slice(2); -if (args.includes("--debug")) { - require(path.join(__dirname, "..", "dist", "development", "node-server.js")); -} else { - require(path.join(__dirname, "..", "dist", "node-server.js")); +const args = process.argv; + +if (args.includes("--version") || args.includes("-v") || args.includes("-V")) { + try { + const pkg = fs.readFileSync( + path.join(__dirname, "..", "package.json"), + "utf-8", + ); + const json = JSON.parse(pkg); + if (!json.version) { + throw new Error(); + } + console.info(json.version); + } catch (e) { + console.info("Something went wrong reading the current version number."); + } + return; } + +if (args.includes("--help") || args.includes("-h")) { + console.info(`some-sass-language-server <--stdio|--node-ipc|--socket={number}> [--loglevel ] + +For documentation, visit https://wkillerud.github.io/some-sass/language-server/getting-started.html`); + return; +} + +require(path.join(__dirname, "..", "dist", "node-main.js")); diff --git a/packages/language-server/package.json b/packages/language-server/package.json index 41bbe69c..f1380d5b 100644 --- a/packages/language-server/package.json +++ b/packages/language-server/package.json @@ -9,6 +9,7 @@ "lsp", "language-server-protocol" ], + "type": "commonjs", "engines": { "node": ">=20" }, @@ -31,33 +32,42 @@ "exports": { ".": { "browser": "./dist/browser-server.js", - "development": "./dist/development/node-server.js", "default": "./dist/node-server.js" - }, - "./development": { - "browser": "./dist/development/browser-server.js", - "default": "./dist/development/node-server.js" } }, "author": "William Killerud (https://www.williamkillerud.com/)", "license": "MIT", "scripts": { - "build": "rspack", + "prepublishOnly": "run-s build:production:*", + "prebuild": "tsc --noEmit", + "build": "run-s build:development:*", + "build:node": "rspack --config ./rspack.node.config.js", + "build:browser": "rspack --config ./rspack.browser.config.js", + "build:development:node": "npm run build:node -- --mode=development", + "build:development:browser": "npm run build:browser -- --mode=development", + "build:production:node": "npm run build:node -- --mode=production", + "build:production:browser": "npm run build:browser -- --mode=production", "clean": "shx rm -rf dist node_modules", "test": "vitest", - "coverage": "vitest run --coverage" + "coverage": "vitest run --coverage", + "doctor:node": "RSDOCTOR=true run-s build:production:node", + "doctor:browser": "RSDOCTOR=true run-s build:production:browser" }, "devDependencies": { - "@vitest/coverage-v8": "2.0.5", + "@rsdoctor/rspack-plugin": "0.4.4", "@somesass/language-services": "1.7.1", + "@types/lodash.merge": "4.6.9", "@types/node": "20.16.5", + "@vitest/coverage-v8": "2.0.5", "fast-glob": "3.3.2", + "lodash.merge": "4.6.2", "merge-options": "3.0.4", "path-browserify": "1.0.1", "process": "0.11.10", "shx": "0.3.4", - "util": "0.12.5", "url": "0.11.4", + "util": "0.12.5", + "vitest": "2.1.1", "vscode-languageserver": "9.0.1", "vscode-languageserver-textdocument": "1.0.12", "vscode-languageserver-types": "3.17.5", diff --git a/packages/language-server/rspack.browser.config.js b/packages/language-server/rspack.browser.config.js new file mode 100644 index 00000000..652bc135 --- /dev/null +++ b/packages/language-server/rspack.browser.config.js @@ -0,0 +1,72 @@ +/* eslint-disable */ +const path = require("path"); +const rspack = require("@rspack/core"); +const { RsdoctorRspackPlugin } = require("@rsdoctor/rspack-plugin"); + +/** @type {import('@rspack/core').Configuration} */ +const config = { + context: __dirname, + target: "webworker", + entry: { + "browser-main": "./src/browser-main.ts", + }, + output: { + libraryTarget: "var", + library: "serverExportVar", + filename: "[name].js", + path: path.join(__dirname, "dist"), + }, + module: { + rules: [ + { + test: /\.m?js/, + resolve: { + fullySpecified: false, + }, + }, + { + test: /\.ts$/, + exclude: [/[\\/]node_modules[\\/]/], + loader: "builtin:swc-loader", + options: { + jsc: { + parser: { + syntax: "typescript", + }, + externalHelpers: true, + }, + }, + }, + ], + }, + resolve: { + extensions: [".ts", ".js"], + mainFields: ["browser", "module", "main"], + conditionNames: ["import", "require", "default"], + fallback: { + events: require.resolve("events/"), + path: require.resolve("path-browserify"), + util: require.resolve("util/"), + url: require.resolve("url/"), + "fs/promises": false, + }, + }, + plugins: [ + new rspack.ProvidePlugin({ + process: "process/browser", + }), + new rspack.optimize.LimitChunkCountPlugin({ + maxChunks: 1, + }), + // Only register the plugin when RSDOCTOR is true, as the plugin will increase the build time. + process.env.RSDOCTOR && new RsdoctorRspackPlugin(), + ].filter(Boolean), + devtool: "cheap-source-map", +}; + +module.exports = (env, argv) => { + if (argv.mode === "development") { + config.devtool = "source-map"; + } + return config; +}; diff --git a/packages/language-server/rspack.config.js b/packages/language-server/rspack.config.js deleted file mode 100644 index f16e9e01..00000000 --- a/packages/language-server/rspack.config.js +++ /dev/null @@ -1,121 +0,0 @@ -// @ts-check -/* eslint-disable @typescript-eslint/no-var-requires */ - -const path = require("path"); -const merge = require("merge-options"); -const rspack = require("@rspack/core"); - -/** @typedef {import('@rspack/core').Configuration} RspackConfig **/ -/** @type RspackConfig */ -const nodeConfig = { - target: "node", - entry: { - "node-server": "./src/node-server.ts", - }, - output: { - libraryTarget: "commonjs2", - }, -}; - -/** @type RspackConfig */ -const browserConfig = { - context: __dirname, - target: "webworker", - entry: { - "browser-server": "./src/browser-server.ts", - }, - output: { - libraryTarget: "var", - library: "serverExportVar", - }, - resolve: { - mainFields: ["browser", "module", "main"], - fallback: { - events: require.resolve("events/"), - path: require.resolve("path-browserify"), - util: require.resolve("util/"), - url: require.resolve("url/"), - "fs/promises": false, - }, - }, - module: { - rules: [ - { - test: /\.m?js/, - resolve: { - fullySpecified: false, - }, - }, - { - test: /\.ts$/, - exclude: [/[\\/]node_modules[\\/]/], - loader: "builtin:swc-loader", - options: { - jsc: { - parser: { - syntax: "typescript", - }, - externalHelpers: true, - }, - }, - }, - ], - }, - plugins: [ - new rspack.ProvidePlugin({ - process: "process/browser", - }), - ], -}; - -function defineConfig(config, mode) { - /** @type RspackConfig */ - const baseConfig = { - mode, - output: { - filename: "[name].js", - path: path.join(__dirname, "dist"), - }, - resolve: { - extensions: [".ts", ".js"], - }, - module: { - rules: [ - { - test: /\.ts$/, - exclude: [/[\\/]node_modules[\\/]/], - loader: "builtin:swc-loader", - options: { - jsc: { - parser: { - syntax: "typescript", - }, - externalHelpers: true, - }, - }, - }, - ], - }, - devtool: false, - }; - - /** @type RspackConfig */ - const developmentConfig = { - devtool: "source-map", - output: { - path: path.join(__dirname, "dist", "development"), - }, - }; - - if (mode === "development") { - return merge(baseConfig, config, developmentConfig); - } - return merge(baseConfig, config); -} - -module.exports = [ - defineConfig(nodeConfig, "development"), - defineConfig(nodeConfig, "production"), - defineConfig(browserConfig, "development"), - defineConfig(browserConfig, "production"), -]; diff --git a/packages/language-server/rspack.node.config.js b/packages/language-server/rspack.node.config.js new file mode 100644 index 00000000..63c35b6a --- /dev/null +++ b/packages/language-server/rspack.node.config.js @@ -0,0 +1,52 @@ +/* eslint-disable */ +const path = require("path"); +const rspack = require("@rspack/core"); +const { RsdoctorRspackPlugin } = require("@rsdoctor/rspack-plugin"); + +/** @type {import('@rspack/core').Configuration} */ +const config = { + target: "node", + entry: { + "node-main": "./src/node-main.ts", + }, + output: { + filename: "[name].js", + path: path.join(__dirname, "dist"), + libraryTarget: "commonjs2", + }, + resolve: { + extensions: [".ts", ".js"], + conditionNames: ["import", "require", "default"], + mainFields: ["module", "main"], + }, + module: { + rules: [ + { + test: /\.ts$/, + exclude: [/[\\/]node_modules[\\/]/], + loader: "builtin:swc-loader", + options: { + jsc: { + parser: { + syntax: "typescript", + }, + externalHelpers: true, + target: "es2022", + }, + }, + }, + ], + }, + devtool: "cheap-source-map", + plugins: [ + // Only register the plugin when RSDOCTOR is true, as the plugin will increase the build time. + process.env.RSDOCTOR && new RsdoctorRspackPlugin(), + ].filter(Boolean), +}; + +module.exports = (env, argv) => { + if (argv.mode === "development") { + config.devtool = "source-map"; + } + return config; +}; diff --git a/packages/language-server/src/utils/__tests__/embedded.test.ts b/packages/language-server/src/__tests__/embedded.test.ts similarity index 100% rename from packages/language-server/src/utils/__tests__/embedded.test.ts rename to packages/language-server/src/__tests__/embedded.test.ts diff --git a/packages/language-server/src/__tests__/logger.test.ts b/packages/language-server/src/__tests__/logger.test.ts new file mode 100644 index 00000000..1fa765eb --- /dev/null +++ b/packages/language-server/src/__tests__/logger.test.ts @@ -0,0 +1,116 @@ +import { assert, test, vi } from "vitest"; +import { createLogger } from "../logger"; + +test("default log level is info", () => { + const remote = { + debug: vi.fn(), + info: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + }; + const log = createLogger(remote); + + log.trace("hello"); + log.debug("hello"); + log.info("hello"); + log.warn("hello"); + log.error("hello"); + log.fatal("hello"); + + assert.equal(remote.debug.mock.calls.length, 0); + assert.equal(remote.info.mock.calls.length, 1); + assert.equal(remote.warn.mock.calls.length, 1); + assert.equal(remote.error.mock.calls.length, 2); +}); + +test("trace logs all the things", () => { + const remote = { + debug: vi.fn(), + info: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + }; + const log = createLogger(remote); + log.setLogLevel("trace"); + + log.trace("hello"); + log.debug("hello"); + log.info("hello"); + log.warn("hello"); + log.error("hello"); + log.fatal("hello"); + + assert.equal(remote.debug.mock.calls.length, 2); + assert.equal(remote.info.mock.calls.length, 1); + assert.equal(remote.warn.mock.calls.length, 1); + assert.equal(remote.error.mock.calls.length, 2); +}); + +test("warn logs warn, error and fatal", () => { + const remote = { + debug: vi.fn(), + info: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + }; + const log = createLogger(remote); + log.setLogLevel("warn"); + + log.trace("hello"); + log.debug("hello"); + log.info("hello"); + log.warn("hello"); + log.error("hello"); + log.fatal("hello"); + + assert.equal(remote.debug.mock.calls.length, 0); + assert.equal(remote.info.mock.calls.length, 0); + assert.equal(remote.warn.mock.calls.length, 1); + assert.equal(remote.error.mock.calls.length, 2); +}); + +test("error logs error and fatal", () => { + const remote = { + debug: vi.fn(), + info: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + }; + const log = createLogger(remote); + log.setLogLevel("error"); + + log.trace("hello"); + log.debug("hello"); + log.info("hello"); + log.warn("hello"); + log.error("hello"); + log.fatal("hello"); + + assert.equal(remote.debug.mock.calls.length, 0); + assert.equal(remote.info.mock.calls.length, 0); + assert.equal(remote.warn.mock.calls.length, 0); + assert.equal(remote.error.mock.calls.length, 2); +}); + +test("silent logs nothing", () => { + const remote = { + debug: vi.fn(), + info: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + }; + const log = createLogger(remote); + log.setLogLevel("silent"); + + log.trace("hello"); + log.debug("hello"); + log.info("hello"); + log.warn("hello"); + log.error("hello"); + log.fatal("hello"); + + assert.equal(remote.debug.mock.calls.length, 0); + assert.equal(remote.info.mock.calls.length, 0); + assert.equal(remote.warn.mock.calls.length, 0); + assert.equal(remote.error.mock.calls.length, 0); +}); diff --git a/packages/language-server/src/browser-main.ts b/packages/language-server/src/browser-main.ts new file mode 100644 index 00000000..8f8f229b --- /dev/null +++ b/packages/language-server/src/browser-main.ts @@ -0,0 +1,4 @@ +async function setupBrowser() { + await import("./browser-server"); +} +setupBrowser(); diff --git a/packages/language-server/src/configuration.ts b/packages/language-server/src/configuration.ts new file mode 100644 index 00000000..4dc2b281 --- /dev/null +++ b/packages/language-server/src/configuration.ts @@ -0,0 +1,102 @@ +import { + defaultConfiguration, + type LanguageServerConfiguration, +} from "@somesass/language-services"; +import { Logger } from "./logger"; + +export function toNewConfiguration( + v1: Partial, + log: Logger, +): LanguageServerConfiguration { + const newSettings = Object.assign({}, defaultConfiguration, v1); + if (v1.loadPaths) { + log.info("somesass.loadPaths is now somesass.workspace.loadPaths"); + newSettings.workspace.loadPaths = v1.loadPaths; + } + if (v1.scannerExclude) { + log.info("somesass.scannerExclude is now somesass.workspace.exclude"); + newSettings.workspace.exclude = v1.scannerExclude; + } + + if (typeof v1.suggestAllFromOpenDocument !== "undefined") { + newSettings.css.completion.includeFromCurrentDocument = + v1.suggestAllFromOpenDocument; + } + if (typeof v1.triggerPropertyValueCompletion !== "undefined") { + newSettings.css.completion.triggerPropertyValueCompletion = + v1.triggerPropertyValueCompletion; + } + + if (typeof v1.suggestAllFromOpenDocument !== "undefined") { + newSettings.sass.completion.includeFromCurrentDocument = + v1.suggestAllFromOpenDocument; + } + if (typeof v1.suggestionStyle !== "undefined") { + newSettings.sass.completion.mixinStyle = v1.suggestionStyle; + } + if (typeof v1.suggestFromUseOnly !== "undefined") { + newSettings.sass.completion.suggestFromUseOnly = v1.suggestFromUseOnly; + } + if (typeof v1.triggerPropertyValueCompletion !== "undefined") { + newSettings.sass.completion.triggerPropertyValueCompletion = + v1.triggerPropertyValueCompletion; + } + + if (typeof v1.suggestAllFromOpenDocument !== "undefined") { + log.info( + "somesass.suggestAllFromOpenDocument is now somesass.scss.completion.includeFromCurrentDocument and somesass.sass.completion.includeFromCurrentDocument", + ); + newSettings.scss.completion.includeFromCurrentDocument = + v1.suggestAllFromOpenDocument; + } + if (typeof v1.suggestionStyle !== "undefined") { + log.info( + "somesass.suggestionStyle is now somesass.scss.completion.mixinStyle and somesass.sass.completion.mixinStyle", + ); + newSettings.scss.completion.mixinStyle = v1.suggestionStyle; + } + if (typeof v1.suggestFromUseOnly !== "undefined") { + log.info( + "somesass.suggestFromUseOnly is now somesass.scss.completion.suggestFromUseOnly and somesass.sass.completion.suggestFromUseOnly", + ); + newSettings.scss.completion.suggestFromUseOnly = v1.suggestFromUseOnly; + } + if (typeof v1.triggerPropertyValueCompletion !== "undefined") { + log.info( + "somesass.triggerPropertyValueCompletion is now somesass.scss.completion.triggerPropertyValueCompletion and somesass.sass.completion.triggerPropertyValueCompletion", + ); + newSettings.scss.completion.triggerPropertyValueCompletion = + v1.triggerPropertyValueCompletion; + } + + return newSettings; +} + +export function isOldConfiguration( + maybeV1: Partial, +) { + const asV1 = maybeV1 as Partial; + if (typeof asV1.loadPaths !== "undefined") return true; + if (typeof asV1.scannerExclude !== "undefined") return true; + if (typeof asV1.scannerDepth !== "undefined") return true; + if (typeof asV1.scanImportedFiles !== "undefined") return true; + if (typeof asV1.suggestionStyle !== "undefined") return true; + if (typeof asV1.suggestAllFromOpenDocument !== "undefined") return true; + if (typeof asV1.suggestFromUseOnly !== "undefined") return true; + if (typeof asV1.suggestFunctionsInStringContextAfterSymbols !== "undefined") + return true; + if (typeof asV1.triggerPropertyValueCompletion !== "undefined") return true; + return false; +} + +export interface ConfigurationV1 { + readonly loadPaths: string[]; + readonly scannerExclude: string[]; + readonly scannerDepth: number; + readonly scanImportedFiles: boolean; + readonly suggestionStyle: "all" | "nobracket" | "bracket"; + readonly suggestAllFromOpenDocument: boolean; + readonly suggestFromUseOnly: boolean; + readonly suggestFunctionsInStringContextAfterSymbols: " (+-*%"; + readonly triggerPropertyValueCompletion: boolean; +} diff --git a/packages/language-server/src/utils/embedded.ts b/packages/language-server/src/embedded.ts similarity index 96% rename from packages/language-server/src/utils/embedded.ts rename to packages/language-server/src/embedded.ts index bd0bfc88..a11d35ec 100644 --- a/packages/language-server/src/utils/embedded.ts +++ b/packages/language-server/src/embedded.ts @@ -7,7 +7,11 @@ type Region = { }; export function isFileWhereScssCanBeEmbedded(path: string) { - if (path.endsWith(".scss") || path.endsWith(".sass")) { + if ( + path.endsWith(".scss") || + path.endsWith(".sass") || + path.endsWith(".css") + ) { return false; } return true; diff --git a/packages/language-server/src/logger.ts b/packages/language-server/src/logger.ts new file mode 100644 index 00000000..c332e993 --- /dev/null +++ b/packages/language-server/src/logger.ts @@ -0,0 +1,106 @@ +import type { RemoteConsole } from "vscode-languageserver"; + +export type RemoteLogger = Pick< + RemoteConsole, + "debug" | "info" | "warn" | "error" +>; + +export interface Logger { + fatal(message: string): void; + error(message: string): void; + warn(message: string): void; + info(message: string): void; + debug(message: string): void; + trace(message: string): void; + /** + * Accepts the same levels as [pino](https://getpino.io/#/docs/api?id=level-string) + */ + setLogLevel(level: string): void; +} + +const fatal = 1; +const error = 2; +const warn = 3; +const info = 4; +const debug = 5; +const trace = 6; +const silent = 0; + +function levelToRank(level: string): number { + switch (level) { + case "fatal": + return fatal; + case "error": + return error; + case "warn": + return warn; + case "debug": + return debug; + case "trace": + return trace; + case "silent": + return silent; + case "info": + default: + return info; + } +} + +class LoggerImpl implements Logger { + #remoteConsole: RemoteLogger; + #level: number = levelToRank("info"); + + constructor(remoteConsole: RemoteLogger) { + this.#remoteConsole = remoteConsole; + try { + const levelArg = process.argv.indexOf("--loglevel"); + if (levelArg !== -1) { + this.#level = levelToRank(process.argv[levelArg + 1]); + } + } catch {} + } + + setLogLevel(level: string): void { + this.#level = levelToRank(level); + } + + fatal(message: string): void { + if (this.#level >= fatal) { + this.#remoteConsole.error(message); + } + } + + error(message: string): void { + if (this.#level >= error) { + this.#remoteConsole.error(message); + } + } + + warn(message: string): void { + if (this.#level >= warn) { + this.#remoteConsole.warn(message); + } + } + + info(message: string): void { + if (this.#level >= info) { + this.#remoteConsole.info(message); + } + } + + debug(message: string): void { + if (this.#level >= debug) { + this.#remoteConsole.debug(message); + } + } + + trace(message: string): void { + if (this.#level >= trace) { + this.#remoteConsole.debug(message); + } + } +} + +export function createLogger(remoteConsole: RemoteLogger): Logger { + return new LoggerImpl(remoteConsole); +} diff --git a/packages/language-server/src/node-main.ts b/packages/language-server/src/node-main.ts new file mode 100644 index 00000000..d7b93a66 --- /dev/null +++ b/packages/language-server/src/node-main.ts @@ -0,0 +1,4 @@ +async function setupNode() { + await import("./node-server"); +} +setupNode(); diff --git a/packages/language-server/src/server.ts b/packages/language-server/src/server.ts index 79b755b9..b904ba73 100644 --- a/packages/language-server/src/server.ts +++ b/packages/language-server/src/server.ts @@ -1,6 +1,10 @@ import { + defaultConfiguration, getLanguageService, LanguageService, + LanguageServerConfiguration, + LanguageConfiguration, + EditorConfiguration, } from "@somesass/language-services"; import { ClientCapabilities, @@ -21,17 +25,27 @@ import { URI } from "vscode-uri"; import type { FileSystemProvider } from "./file-system"; import { getFileSystemProvider } from "./file-system-provider"; import { RuntimeEnvironment } from "./runtime"; -import { defaultSettings, IEditorSettings, ISettings } from "./settings"; -import { getSassRegionsDocument } from "./utils/embedded"; +import { + ConfigurationV1, + isOldConfiguration, + toNewConfiguration, +} from "./configuration"; +import { getSassRegionsDocument } from "./embedded"; import WorkspaceScanner from "./workspace-scanner"; +import { createLogger, type Logger } from "./logger"; +import merge from "lodash.merge"; export class SomeSassServer { private readonly connection: Connection; private readonly runtime: RuntimeEnvironment; + private readonly log: Logger; + private configuration: LanguageServerConfiguration = defaultConfiguration; constructor(connection: Connection, runtime: RuntimeEnvironment) { this.connection = connection; this.runtime = runtime; + this.log = createLogger(connection.console); + this.log.trace(`Process ID ${process.pid}`); } public listen(): void { @@ -42,22 +56,10 @@ export class SomeSassServer { let clientCapabilities: ClientCapabilities | undefined = undefined; let initialScan: Promise | null = null; - // Create a simple text document manager. The text document manager - // _supports full document sync only const documents = new TextDocuments(TextDocument); - - // Make the text document manager listen on the connection - // _for open, change and close text document events documents.listen(this.connection); - this.connection.console.log(`[Server(${process.pid})] Listening`); - // After the server has started the client sends an initilize request. The server receives - // _in the passed params the rootPath of the workspace plus the client capabilites this.connection.onInitialize((params) => { - this.connection.console.debug( - `[Server${process.pid ? `(${process.pid})` : ""} ${workspaceRoot}] received`, - ); - clientCapabilities = params.capabilities; fileSystemProvider = getFileSystemProvider(this.connection, this.runtime); @@ -65,16 +67,11 @@ export class SomeSassServer { ls = getLanguageService({ clientCapabilities, fileSystemProvider, + logger: this.log, }); - // TODO: migrate to workspace folders. Workspace was an unnecessary older workaround of mine. - workspaceRoot = URI.parse( - params.initializationOptions?.workspace || params.rootUri!, - ); - - this.connection.console.debug( - `[Server${process.pid ? `(${process.pid})` : ""} ${workspaceRoot}] returning server capabilities`, - ); + workspaceRoot = URI.parse(params.rootUri!); + this.log.info(`Workspace root ${workspaceRoot}`); return { capabilities: { @@ -130,121 +127,133 @@ export class SomeSassServer { }; }); - function applySettings( - editorSettings: IEditorSettings, - settings: ISettings, - ) { - if (!ls) return; + const applyConfiguration = ( + somesass: Partial, + editor: Partial, + ): LanguageServerConfiguration => { + if (isOldConfiguration(somesass)) { + this.log.warn( + `Your somesass configuration uses old setting names. They will continue to work for some time, but it's recommended you change your settings to the new names. For all the available settings see https://wkillerud.github.io/some-sass/user-guide/settings.html`, + ); + + somesass = toNewConfiguration( + somesass as Partial, + this.log, + ); + + this.log.info( + "Replace old setting IDs with new ones to remove these messages", + ); + } - ls.configure({ - editorSettings, - workspaceRoot, - loadPaths: settings.loadPaths, - completionSettings: { - suggestAllFromOpenDocument: settings.suggestAllFromOpenDocument, - suggestFromUseOnly: settings.suggestFromUseOnly, - suggestionStyle: settings.suggestionStyle, - suggestFunctionsInStringContextAfterSymbols: - settings.suggestFunctionsInStringContextAfterSymbols, + const settings: LanguageServerConfiguration = merge( + defaultConfiguration, + somesass, + { + editor: { + ...editor, + }, }, - }); - } + ); + + settings.workspace.workspaceRoot = workspaceRoot; + + this.configuration = settings; + if (ls) { + ls.configure(settings); + } + + this.log.setLogLevel(settings.workspace.logLevel); + this.log.debug("Applied user configuration"); + this.log.trace(JSON.stringify(this.configuration, null, 2)); + + return settings; + }; this.connection.onInitialized(async () => { - this.connection.console.debug( - `[Server${process.pid ? `(${process.pid})` : ""} ${workspaceRoot}] received`, - ); try { + // Let other methods await the result of the initial scan before proceeding initialScan = new Promise((resolve, reject) => { - Promise.all([ + const configurationRequests = [ this.connection.workspace.getConfiguration("somesass"), this.connection.workspace.getConfiguration("editor"), - ]).then( - ([somesassConfiguration, editorConfiguration]: [ - Partial, - Partial, - ]) => { - const settings = { - ...defaultSettings, - ...somesassConfiguration, - }; - - const editorSettings: IEditorSettings = { - insertSpaces: false, - indentSize: undefined, - tabSize: 2, - ...editorConfiguration, - }; - - if ( - !ls || - !clientCapabilities || - !workspaceRoot || - !fileSystemProvider - ) { - return reject( - new Error( - "Got onInitialized without onInitialize readying up all required globals", - ), - ); - } + ]; + + Promise.all(configurationRequests).then((configs) => { + if ( + !ls || + !clientCapabilities || + !workspaceRoot || + !fileSystemProvider + ) { + return reject( + new Error( + "Got onInitialized without onInitialize readying up all required globals", + ), + ); + } - applySettings(editorSettings, settings); + let [somesass, editor] = configs as [ + Partial, + Partial, + ]; - this.connection.console.debug( - `[Server${process.pid ? `(${process.pid})` : ""} ${workspaceRoot}] scanning workspace for files`, - ); + const configuration = applyConfiguration(somesass, editor); - return fileSystemProvider - .findFiles( - "**/*.{scss,sass,svelte,astro,vue}", - settings.scannerExclude, - ) - .then((files) => { - this.connection.console.debug( - `[Server${process.pid ? `(${process.pid})` : ""} ${workspaceRoot}] found ${files.length} files, starting parse`, - ); - - workspaceScanner = new WorkspaceScanner( - ls!, - fileSystemProvider!, - { - scanImportedFiles: settings.scanImportedFiles, - scannerDepth: settings.scannerDepth, - }, - ); - - return workspaceScanner.scan(files); - }) - .then((promises) => { - this.connection.console.debug( - `[Server${process.pid ? `(${process.pid})` : ""} ${workspaceRoot}] parsed ${promises.length} files`, - ); - resolve(); - }); - }, - ); + this.log.debug("Scanning workspace for files"); + + return fileSystemProvider + .findFiles( + "**/*.{css,scss,sass,svelte,astro,vue}", + configuration.workspace.exclude, + ) + .then((files) => { + this.log.debug(`Found ${files.length} files, starting parse`); + + workspaceScanner = new WorkspaceScanner( + ls!, + fileSystemProvider!, + ); + + return workspaceScanner.scan(files); + }) + .then((promises) => { + this.log.debug( + `Initial scan finished, parsed ${promises.length} files`, + ); + resolve(); + }); + }); }); await initialScan; } catch (error) { - this.connection.console.log(String(error)); + this.log.fatal(String(error)); } }); - const onDocumentChanged = async ( + this.connection.onDidChangeConfiguration((params) => { + applyConfiguration(params.settings.somesass, params.settings.editor); + }); + + const doDiagnostics = async ( params: TextDocumentChangeEvent, - ) => { - if (!workspaceScanner || !ls) return; + ): Promise => { + if (!ls) return; - try { - ls.onDocumentChanged(params.document); - // Check that no new version has been made while we waited, - // in which case the diagnostics may no longer be valid. + const document = getSassRegionsDocument( + documents.get(params.document.uri), + ); + if (!document) return; + + const config = this.languageConfiguration(document); + if (config.diagnostics.enabled) { let latest = documents.get(params.document.uri); if (!latest || latest.version !== params.document.version) return; const diagnostics = await ls.doDiagnostics(params.document); + // Check that no new version has been made while we waited, + // in which case the diagnostics may no longer be valid. latest = documents.get(params.document.uri); if (!latest || latest.version !== params.document.version) return; @@ -252,36 +261,32 @@ export class SomeSassServer { uri: latest.uri, diagnostics, }); - } catch { - // Do nothing, the document might have changed } }; - documents.onDidOpen(onDocumentChanged); - documents.onDidChangeContent(onDocumentChanged); - - this.connection.onDidChangeConfiguration((params) => { - if (!ls) return; - - const somesassConfiguration: Partial = - params.settings.somesass; - - const editorConfiguration: Partial = - params.settings.editor; - - const settings: ISettings = { - ...defaultSettings, - ...somesassConfiguration, - }; - - const editorSettings: IEditorSettings = { - insertSpaces: false, - indentSize: undefined, - tabSize: 2, - ...editorConfiguration, - }; + documents.onDidOpen(async (params) => { + try { + if (initialScan) { + await initialScan; + } + await doDiagnostics(params); + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + } + }); - applySettings(editorSettings, settings); + documents.onDidChangeContent(async (params) => { + if (!workspaceScanner || !ls) return; + try { + ls.onDocumentChanged(params.document); + await doDiagnostics(params); + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + } }); this.connection.onDidChangeWatchedFiles(async (event) => { @@ -307,258 +312,416 @@ export class SomeSassServer { } await workspaceScanner.scan(newFiles); - } catch { - // do nothing + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); } }); this.connection.onCompletion(async (params) => { if (!ls) return null; - - const document = getSassRegionsDocument( - documents.get(params.textDocument.uri), - params.position, - ); - if (!document) return null; - - const result = await ls.doComplete(document, params.position); - if (result.items.length === 0) { - result.isIncomplete = true; + try { + const document = getSassRegionsDocument( + documents.get(params.textDocument.uri), + params.position, + ); + if (!document) return null; + + const config = this.languageConfiguration(document); + if (config.completion.enabled) { + const result = await ls.doComplete(document, params.position); + if (result.items.length === 0) { + result.isIncomplete = true; + } + return result; + } else { + return null; + } + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; } - return result; }); - this.connection.onHover((params) => { + this.connection.onHover(async (params) => { if (!ls) return null; - - const document = getSassRegionsDocument( - documents.get(params.textDocument.uri), - params.position, - ); - if (!document) return null; - - const result = ls.doHover(document, params.position); - return result; + try { + const document = getSassRegionsDocument( + documents.get(params.textDocument.uri), + params.position, + ); + if (!document) return null; + + const config = this.languageConfiguration(document); + if (config.hover.enabled) { + const result = await ls.doHover(document, params.position); + return result; + } else { + return null; + } + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; + } }); this.connection.onSignatureHelp(async (params) => { if (!ls) return null; - - const document = getSassRegionsDocument( - documents.get(params.textDocument.uri), - params.position, - ); - if (!document) return null; - - const result = await ls.doSignatureHelp(document, params.position); - return result; + try { + const document = getSassRegionsDocument( + documents.get(params.textDocument.uri), + params.position, + ); + if (!document) return null; + + const config = this.languageConfiguration(document); + if (config.signatureHelp.enabled) { + const result = await ls.doSignatureHelp(document, params.position); + return result; + } else { + return null; + } + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; + } }); - this.connection.onDefinition((params) => { + this.connection.onDefinition(async (params) => { if (!ls) return null; - - const document = getSassRegionsDocument( - documents.get(params.textDocument.uri), - params.position, - ); - if (!document) return null; - - const result = ls.findDefinition(document, params.position); - return result; + try { + const document = getSassRegionsDocument( + documents.get(params.textDocument.uri), + params.position, + ); + if (!document) return null; + + const config = this.languageConfiguration(document); + if (config.definition.enabled) { + const result = await ls.findDefinition(document, params.position); + return result; + } else { + return null; + } + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; + } }); this.connection.onDocumentHighlight(async (params) => { if (!ls) return null; - - const document = getSassRegionsDocument( - documents.get(params.textDocument.uri), - params.position, - ); - if (!document) return null; - try { - if (initialScan) { - await initialScan; + const document = getSassRegionsDocument( + documents.get(params.textDocument.uri), + params.position, + ); + if (!document) return null; + + const config = this.languageConfiguration(document); + if (config.highlights.enabled) { + if (initialScan) { + await initialScan; + } + const result = ls.findDocumentHighlights(document, params.position); + return result; + } else { + return null; } - const result = ls.findDocumentHighlights(document, params.position); - return result; - } catch { - // Do nothing + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; } }); this.connection.onDocumentLinks(async (params) => { if (!ls) return null; - - const document = getSassRegionsDocument( - documents.get(params.textDocument.uri), - ); - if (!document) return null; - try { - if (initialScan) { - await initialScan; + const document = getSassRegionsDocument( + documents.get(params.textDocument.uri), + ); + if (!document) return null; + + const config = this.languageConfiguration(document); + if (config.links.enabled) { + if (initialScan) { + await initialScan; + } + const result = await ls.findDocumentLinks(document); + return result; + } else { + return null; } - const result = await ls.findDocumentLinks(document); - return result; - } catch { - // Do nothing + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; } }); this.connection.onReferences(async (params) => { if (!ls) return null; - - const document = getSassRegionsDocument( - documents.get(params.textDocument.uri), - params.position, - ); - if (!document) return null; - - const references = await ls.findReferences( - document, - params.position, - params.context, - ); - return references; + try { + const document = getSassRegionsDocument( + documents.get(params.textDocument.uri), + params.position, + ); + if (!document) return null; + + const config = this.languageConfiguration(document); + if (config.references.enabled) { + const references = await ls.findReferences( + document, + params.position, + params.context, + ); + return references; + } else { + return null; + } + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; + } }); this.connection.onWorkspaceSymbol((params) => { if (!ls) return null; - - const result = ls.findWorkspaceSymbols(params.query); - return result; + try { + const result = ls.findWorkspaceSymbols(params.query); + return result; + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; + } }); this.connection.onCodeAction(async (params) => { if (!ls) return null; + try { + const document = getSassRegionsDocument( + documents.get(params.textDocument.uri), + ); + if (!document) return null; + + const config = this.languageConfiguration(document); + if (!config.codeAction.enabled) { + return null; + } - const document = getSassRegionsDocument( - documents.get(params.textDocument.uri), - ); - if (!document) return null; + const result: (Command | CodeAction)[] = []; - const result: (Command | CodeAction)[] = []; + const actions = await ls.getCodeActions( + document, + params.range, + params.context, + ); - const actions = await ls.getCodeActions( - document, - params.range, - params.context, - ); + for (const action of actions) { + if (action.kind?.startsWith("refactor.extract")) { + // TODO: can we detect support for the custom command here before we do this? - for (const action of actions) { - if (action.kind?.startsWith("refactor.extract")) { - // Replace with a custom command that immediately starts a rename after applying the edit. - // If this causes problems for other clients, look into passing some kind of client identifier (optional) - // with initOptions that indicate this command exists in the client. - - const edit: TextDocumentEdit | undefined = action.edit - ?.documentChanges?.[0] as TextDocumentEdit; - - const command = Command.create( - action.title, - "_somesass.applyExtractCodeAction", - document.uri, - document.version, - edit && edit.edits[0], - ); + // Replace with a custom command that immediately starts a rename after applying the edit. + // If this causes problems for other clients, look into passing some kind of client identifier (optional) + // with initOptions that indicate this command exists in the client. - result.push(CodeAction.create(action.title, command, action.kind)); - } else { - result.push(action); + const edit: TextDocumentEdit | undefined = action.edit + ?.documentChanges?.[0] as TextDocumentEdit; + + const command = Command.create( + action.title, + "_somesass.applyExtractCodeAction", + document.uri, + document.version, + edit && edit.edits[0], + ); + + result.push(CodeAction.create(action.title, command, action.kind)); + } else { + result.push(action); + } } - } - return result; + return result; + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; + } }); this.connection.onPrepareRename(async (params) => { if (!ls) return null; - - const document = getSassRegionsDocument( - documents.get(params.textDocument.uri), - params.position, - ); - if (!document) return null; - - const preparations = await ls.prepareRename(document, params.position); - return preparations; + try { + const document = getSassRegionsDocument( + documents.get(params.textDocument.uri), + params.position, + ); + if (!document) return null; + + const config = this.languageConfiguration(document); + if (config.rename.enabled) { + const preparations = await ls.prepareRename( + document, + params.position, + ); + return preparations; + } else { + return null; + } + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; + } }); this.connection.onRenameRequest(async (params) => { if (!ls) return null; - - const document = getSassRegionsDocument( - documents.get(params.textDocument.uri), - params.position, - ); - if (!document) return null; - - const edits = await ls.doRename( - document, - params.position, - params.newName, - ); - return edits; + try { + const document = getSassRegionsDocument( + documents.get(params.textDocument.uri), + params.position, + ); + if (!document) return null; + + const config = this.languageConfiguration(document); + if (config.rename.enabled) { + const edits = await ls.doRename( + document, + params.position, + params.newName, + ); + return edits; + } else { + return null; + } + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; + } }); this.connection.onDocumentColor(async (params) => { if (!ls) return null; - - const document = getSassRegionsDocument( - documents.get(params.textDocument.uri), - ); - if (!document) return null; - try { - if (initialScan) { - await initialScan; + const document = getSassRegionsDocument( + documents.get(params.textDocument.uri), + ); + if (!document) return null; + + const config = this.languageConfiguration(document); + if (config.colors.enabled) { + if (initialScan) { + await initialScan; + } + const information = await ls.findColors(document); + return information; + } else { + return null; } - const information = await ls.findColors(document); - return information; - } catch { - // Do nothing + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; } }); this.connection.onColorPresentation((params) => { if (!ls) return null; - - const document = getSassRegionsDocument( - documents.get(params.textDocument.uri), - ); - if (!document) return null; - - const result = ls.getColorPresentations( - document, - params.color, - params.range, - ); - return result; + try { + const document = getSassRegionsDocument( + documents.get(params.textDocument.uri), + ); + if (!document) return null; + + const config = this.languageConfiguration(document); + if (config.colors.enabled) { + const result = ls.getColorPresentations( + document, + params.color, + params.range, + ); + return result; + } else { + return null; + } + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; + } }); this.connection.onFoldingRanges(async (params) => { if (!ls) return null; - - const document = getSassRegionsDocument( - documents.get(params.textDocument.uri), - ); - if (!document) return null; - - const result = await ls.getFoldingRanges(document); - return result; + try { + const document = getSassRegionsDocument( + documents.get(params.textDocument.uri), + ); + if (!document) return null; + + const config = this.languageConfiguration(document); + if (config.foldingRanges.enabled) { + const result = await ls.getFoldingRanges(document); + return result; + } else { + return null; + } + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; + } }); this.connection.onSelectionRanges(async (params) => { if (!ls) return null; - - const document = getSassRegionsDocument( - documents.get(params.textDocument.uri), - ); - if (!document) return null; - - const result = await ls.getSelectionRanges(document, params.positions); - return result; + try { + const document = getSassRegionsDocument( + documents.get(params.textDocument.uri), + ); + if (!document) return null; + + const config = this.languageConfiguration(document); + if (config.selectionRanges.enabled) { + const result = await ls.getSelectionRanges( + document, + params.positions, + ); + return result; + } else { + return null; + } + } catch (e) { + const error = e as Error; + this.log.debug(String(error)); + if (error.stack) this.log.debug(error.stack); + return null; + } }); this.connection.onShutdown(() => { @@ -568,5 +731,21 @@ export class SomeSassServer { }); this.connection.listen(); + this.log.debug(`Some Sass language server is running`); + } + + languageConfiguration(document: TextDocument): LanguageConfiguration { + switch (document.languageId) { + case "css": { + return this.configuration.css; + } + case "sass": { + return this.configuration.sass; + } + case "scss": { + return this.configuration.scss; + } + } + throw new Error(`Unsupported language ${document.languageId}`); } } diff --git a/packages/language-server/src/settings.ts b/packages/language-server/src/settings.ts deleted file mode 100644 index acf0d3f3..00000000 --- a/packages/language-server/src/settings.ts +++ /dev/null @@ -1,35 +0,0 @@ -export interface ISettings { - readonly loadPaths: string[]; - readonly scannerExclude: string[]; - readonly scannerDepth: number; - readonly scanImportedFiles: boolean; - readonly suggestionStyle: "all" | "nobracket" | "bracket"; - readonly suggestAllFromOpenDocument: boolean; - readonly suggestFromUseOnly: boolean; - readonly suggestFunctionsInStringContextAfterSymbols: " (+-*%"; - readonly triggerPropertyValueCompletion: boolean; -} - -export interface IEditorSettings { - insertSpaces: boolean; - /** Introduced in 1.74 */ - indentSize: number | undefined; - tabSize: number; -} - -export const defaultSettings: ISettings = Object.freeze({ - loadPaths: [], - scannerExclude: [ - "**/.git/**", - "**/node_modules/**", - "**/bower_components/**", - ], - scannerDepth: 30, - scanImportedFiles: true, - // This setting is essentially "VS Code Compat Mode" if set to false. - suggestAllFromOpenDocument: true, - suggestionStyle: "all", - suggestFromUseOnly: false, - suggestFunctionsInStringContextAfterSymbols: " (+-*%", - triggerPropertyValueCompletion: true, -}); diff --git a/packages/language-server/src/workspace-scanner.ts b/packages/language-server/src/workspace-scanner.ts index 7599c70e..9125c274 100644 --- a/packages/language-server/src/workspace-scanner.ts +++ b/packages/language-server/src/workspace-scanner.ts @@ -4,32 +4,22 @@ import { } from "@somesass/language-services"; import { TextDocument } from "vscode-languageserver-textdocument"; import { URI } from "vscode-uri"; -import { getSassRegionsDocument } from "./utils/embedded"; +import { getSassRegionsDocument } from "./embedded"; export default class WorkspaceScanner { #ls: LanguageService; #fs: FileSystemProvider; - #settings: { scannerDepth: number; scanImportedFiles: boolean }; - constructor( - ls: LanguageService, - fs: FileSystemProvider, - settings = { scannerDepth: 30, scanImportedFiles: true }, - ) { + constructor(ls: LanguageService, fs: FileSystemProvider) { this.#ls = ls; this.#fs = fs; - - this.#settings = settings; } public async scan(files: URI[]): Promise { // Populate the cache for the new language services return Promise.all( files.map((uri) => { - if ( - this.#settings.scanImportedFiles && - (uri.path.includes("/_") || uri.path.includes("\\_")) - ) { + if (uri.path.includes("/_") || uri.path.includes("\\_")) { // If we scan imported files (which we do by default), don't include partials in the initial scan. // This way we can be reasonably sure that we scan whatever index files there are _before_ we scan // partials which may or may not have been forwarded with a prefix. @@ -41,8 +31,8 @@ export default class WorkspaceScanner { } private async parse(file: URI, depth = 0) { - const maxDepth = this.#settings.scannerDepth ?? 30; - if (depth > maxDepth || !this.#settings.scanImportedFiles) { + const maxDepth = 256; + if (depth > maxDepth) { return; } @@ -65,7 +55,11 @@ export default class WorkspaceScanner { document = getSassRegionsDocument( TextDocument.create( uriString, - uriString.endsWith(".sass") ? "sass" : "scss", + uriString.endsWith(".sass") + ? "sass" + : uriString.endsWith(".css") + ? "css" + : "scss", 1, content, ), @@ -80,7 +74,7 @@ export default class WorkspaceScanner { for (const link of links) { if ( !link.target || - link.target.endsWith(".css") || + link.target.endsWith(".css") || // we'll get to it via our glob link.target.includes("#{") || link.target.startsWith("sass:") ) { diff --git a/packages/language-server/tsconfig.json b/packages/language-server/tsconfig.json index 9b509303..9aa26efc 100644 --- a/packages/language-server/tsconfig.json +++ b/packages/language-server/tsconfig.json @@ -5,9 +5,10 @@ "sourceMap": true, "module": "commonjs", "moduleResolution": "node", - "rootDir": ".", "outDir": "out", "strict": true, + "allowSyntheticDefaultImports": true, "resolveJsonModule": true - } + }, + "exclude": ["**/*.test.ts", "vitest.config.mts"] } diff --git a/packages/language-services/package.json b/packages/language-services/package.json index cb9aa5d3..f553987b 100644 --- a/packages/language-services/package.json +++ b/packages/language-services/package.json @@ -49,7 +49,9 @@ }, "dependencies": { "@somesass/vscode-css-languageservice": "1.7.0", + "@types/lodash.merge": "4.6.9", "colorjs.io": "0.5.2", + "lodash.merge": "4.6.2", "sassdoc-parser": "3.4.0" }, "devDependencies": { diff --git a/packages/language-services/src/configuration.ts b/packages/language-services/src/configuration.ts new file mode 100644 index 00000000..39f4c302 --- /dev/null +++ b/packages/language-services/src/configuration.ts @@ -0,0 +1,258 @@ +import { LanguageServerConfiguration } from "./language-services-types"; + +export const defaultConfiguration: LanguageServerConfiguration = { + workspace: { + exclude: ["**/.git/**", "**/node_modules/**"], + importAliases: {}, + loadPaths: [], + logLevel: "info", + }, + editor: { + colorDecoratorsLimit: 500, + insertSpaces: false, + indentSize: 2, + tabSize: 2, + }, + css: { + codeAction: { + enabled: true, + }, + completion: { + enabled: true, + css: true, + includeFromCurrentDocument: true, + completePropertyWithSemicolon: true, + triggerPropertyValueCompletion: true, + }, + colors: { + enabled: true, + includeFromCurrentDocument: true, + }, + definition: { + enabled: true, + }, + diagnostics: { + enabled: true, + deprecation: { + enabled: true, + }, + lint: { + enabled: true, + compatibleVendorPrefixes: "ignore", + vendorPrefix: "warning", + duplicateProperties: "ignore", + emptyRules: "warning", + importStatement: "ignore", + boxModel: "ignore", + universalSelector: "ignore", + zeroUnits: "ignore", + fontFaceProperties: "warning", + hexColorLength: "error", + argumentsInColorFunction: "error", + unknownProperties: "warning", + validProperties: [], + ieHack: "ignore", + unknownVendorSpecificProperties: "ignore", + propertyIgnoredDueToDisplay: "warning", + important: "ignore", + float: "ignore", + idSelector: "ignore", + unknownAtRules: "warning", + }, + }, + foldingRanges: { + enabled: true, + }, + highlights: { + enabled: true, + }, + hover: { + enabled: true, + documentation: true, + references: true, + }, + links: { + enabled: true, + }, + references: { + enabled: true, + }, + rename: { + enabled: true, + }, + selectionRanges: { + enabled: true, + }, + semanticTokens: { + enabled: true, + }, + signatureHelp: { + enabled: true, + }, + workspaceSymbol: { + enabled: true, + }, + }, + scss: { + codeAction: { + enabled: true, + }, + completion: { + enabled: true, + css: true, + mixinStyle: "all", + includeFromCurrentDocument: true, + suggestFromUseOnly: false, + triggerPropertyValueCompletion: true, + }, + colors: { + enabled: true, + includeFromCurrentDocument: true, + }, + definition: { + enabled: true, + }, + diagnostics: { + enabled: true, + deprecation: { + enabled: true, + }, + lint: { + enabled: true, + compatibleVendorPrefixes: "ignore", + vendorPrefix: "warning", + duplicateProperties: "ignore", + emptyRules: "warning", + importStatement: "ignore", + boxModel: "ignore", + universalSelector: "ignore", + zeroUnits: "ignore", + fontFaceProperties: "warning", + hexColorLength: "error", + argumentsInColorFunction: "error", + unknownProperties: "warning", + validProperties: [], + ieHack: "ignore", + unknownVendorSpecificProperties: "ignore", + propertyIgnoredDueToDisplay: "warning", + important: "ignore", + float: "ignore", + idSelector: "ignore", + unknownAtRules: "warning", + }, + }, + foldingRanges: { + enabled: true, + }, + highlights: { + enabled: true, + }, + hover: { + enabled: true, + documentation: true, + references: true, + }, + links: { + enabled: true, + }, + references: { + enabled: true, + }, + rename: { + enabled: true, + }, + selectionRanges: { + enabled: true, + }, + semanticTokens: { + enabled: true, + }, + signatureHelp: { + enabled: true, + }, + workspaceSymbol: { + enabled: true, + }, + }, + sass: { + codeAction: { + enabled: true, + }, + completion: { + enabled: true, + css: true, + mixinStyle: "all", + includeFromCurrentDocument: true, + suggestFromUseOnly: false, + triggerPropertyValueCompletion: true, + }, + colors: { + enabled: true, + includeFromCurrentDocument: true, + }, + definition: { + enabled: true, + }, + diagnostics: { + enabled: true, + deprecation: { + enabled: true, + }, + lint: { + enabled: true, + compatibleVendorPrefixes: "ignore", + vendorPrefix: "warning", + duplicateProperties: "ignore", + emptyRules: "warning", + importStatement: "ignore", + boxModel: "ignore", + universalSelector: "ignore", + zeroUnits: "ignore", + fontFaceProperties: "warning", + hexColorLength: "error", + argumentsInColorFunction: "error", + unknownProperties: "warning", + validProperties: [], + ieHack: "ignore", + unknownVendorSpecificProperties: "ignore", + propertyIgnoredDueToDisplay: "warning", + important: "ignore", + float: "ignore", + idSelector: "ignore", + unknownAtRules: "warning", + }, + }, + foldingRanges: { + enabled: true, + }, + highlights: { + enabled: true, + }, + hover: { + enabled: true, + documentation: true, + references: true, + }, + links: { + enabled: true, + }, + references: { + enabled: true, + }, + rename: { + enabled: true, + }, + selectionRanges: { + enabled: true, + }, + semanticTokens: { + enabled: true, + }, + signatureHelp: { + enabled: true, + }, + workspaceSymbol: { + enabled: true, + }, + }, +}; diff --git a/packages/language-services/src/features/__tests__/code-actions-extract.test.ts b/packages/language-services/src/features/__tests__/code-actions-extract.test.ts index acf1028f..21376699 100644 --- a/packages/language-services/src/features/__tests__/code-actions-extract.test.ts +++ b/packages/language-services/src/features/__tests__/code-actions-extract.test.ts @@ -1,6 +1,9 @@ import { EOL } from "node:os"; import { test, assert, beforeEach } from "vitest"; -import { getLanguageService } from "../../language-services"; +import { + defaultConfiguration, + getLanguageService, +} from "../../language-services"; import { CodeAction, Position, @@ -15,7 +18,7 @@ const ls = getLanguageService({ fileSystemProvider, ...rest }); beforeEach(() => { ls.clearCache(); - ls.configure({}); // Reset any configuration to default + ls.configure(defaultConfiguration); }); const getEdit = (result: CodeAction): TextEdit[] => { @@ -117,7 +120,8 @@ test("extraction for multiline variable", async () => { test("extraction for mixin with tab indents", async () => { ls.configure({ - editorSettings: { + ...defaultConfiguration, + editor: { insertSpaces: false, }, }); @@ -164,7 +168,8 @@ a.cta { test("extraction for mixin with space indents", async () => { ls.configure({ - editorSettings: { + ...defaultConfiguration, + editor: { insertSpaces: true, indentSize: 4, }, @@ -212,7 +217,8 @@ a.cta { test("indented: extraction for mixin", async () => { ls.configure({ - editorSettings: { + ...defaultConfiguration, + editor: { insertSpaces: true, indentSize: 2, }, @@ -260,7 +266,8 @@ a.cta test("extraction for function with tab indents", async () => { ls.configure({ - editorSettings: { + ...defaultConfiguration, + editor: { insertSpaces: false, }, }); @@ -298,7 +305,8 @@ box-shadow: _function();`, test("extraction for function with space indents", async () => { ls.configure({ - editorSettings: { + ...defaultConfiguration, + editor: { insertSpaces: true, indentSize: 2, }, @@ -337,7 +345,8 @@ box-shadow: _function();`, test("indented: extraction for function", async () => { ls.configure({ - editorSettings: { + ...defaultConfiguration, + editor: { insertSpaces: true, indentSize: 2, }, diff --git a/packages/language-services/src/features/__tests__/do-complete-at-rules.test.ts b/packages/language-services/src/features/__tests__/do-complete-at-rules.test.ts index e5248925..1d0c0891 100644 --- a/packages/language-services/src/features/__tests__/do-complete-at-rules.test.ts +++ b/packages/language-services/src/features/__tests__/do-complete-at-rules.test.ts @@ -6,18 +6,19 @@ import { getOptions } from "../../utils/test-helpers"; const { fileSystemProvider, ...rest } = getOptions(); const ls = getLanguageService({ fileSystemProvider, ...rest }); +ls.configure({ + scss: { + completion: { + suggestFromUseOnly: true, + }, + }, +}); + beforeEach(() => { ls.clearCache(); - ls.configure({}); // Reset any configuration to default }); test("should suggest symbol from a different document via @use when in @return", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument("$primary: limegreen;", { uri: "one.scss", }); @@ -37,12 +38,6 @@ test("should suggest symbol from a different document via @use when in @return", }); test("should suggest symbol from a different document via @use when in @debug", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument("$primary: limegreen;", { uri: "one.scss", }); @@ -62,12 +57,6 @@ test("should suggest symbol from a different document via @use when in @debug", }); test("should suggest symbol from a different document via @use when in @warn", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument("$primary: limegreen;", { uri: "one.scss", }); @@ -87,12 +76,6 @@ test("should suggest symbol from a different document via @use when in @warn", a }); test("should suggest symbol from a different document via @use when in @error", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument("$primary: limegreen;", { uri: "one.scss", }); diff --git a/packages/language-services/src/features/__tests__/do-complete-embedded.test.ts b/packages/language-services/src/features/__tests__/do-complete-embedded.test.ts index 2a2c70ca..a206b251 100644 --- a/packages/language-services/src/features/__tests__/do-complete-embedded.test.ts +++ b/packages/language-services/src/features/__tests__/do-complete-embedded.test.ts @@ -1,4 +1,4 @@ -import { test, assert, beforeEach } from "vitest"; +import { test, assert } from "vitest"; import { getLanguageService } from "../../language-services"; import { CompletionItemKind, @@ -76,11 +76,6 @@ function getSCSSRegionsDocument(document: TextDocument, position?: Position) { return document; } -beforeEach(() => { - ls.clearCache(); - ls.configure({}); // Reset any configuration to default -}); - 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-interpolation.test.ts b/packages/language-services/src/features/__tests__/do-complete-interpolation.test.ts index 6db38e63..9bbf97df 100644 --- a/packages/language-services/src/features/__tests__/do-complete-interpolation.test.ts +++ b/packages/language-services/src/features/__tests__/do-complete-interpolation.test.ts @@ -1,5 +1,8 @@ import { test, assert, beforeEach } from "vitest"; -import { getLanguageService } from "../../language-services"; +import { + defaultConfiguration, + getLanguageService, +} from "../../language-services"; import { Position } from "../../language-services-types"; import { getOptions } from "../../utils/test-helpers"; @@ -8,14 +11,16 @@ const ls = getLanguageService({ fileSystemProvider, ...rest }); beforeEach(() => { ls.clearCache(); - ls.configure({}); // Reset any configuration to default + ls.configure(defaultConfiguration); }); test("should not suggest mixin or placeholder in string interpolation", async () => { ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, + scss: { + completion: { + suggestAllFromOpenDocument: true, + suggestFromUseOnly: false, + }, }, }); @@ -39,8 +44,10 @@ test("should not suggest mixin or placeholder in string interpolation", async () test("should not suggest module mixin in string interpolation", async () => { ls.configure({ - completionSettings: { - suggestFromUseOnly: true, + scss: { + completion: { + suggestFromUseOnly: true, + }, }, }); @@ -69,8 +76,10 @@ test("should not suggest module mixin in string interpolation", async () => { test("should suggest symbol from a different document via @use when in string interpolation", async () => { ls.configure({ - completionSettings: { - suggestFromUseOnly: true, + scss: { + completion: { + suggestFromUseOnly: true, + }, }, }); @@ -94,8 +103,10 @@ test("should suggest symbol from a different document via @use when in string in test("should suggest symbols when interpolation is part of CSS selector", async () => { ls.configure({ - completionSettings: { - suggestFromUseOnly: true, + scss: { + completion: { + suggestFromUseOnly: true, + }, }, }); @@ -119,8 +130,10 @@ test("should suggest symbols when interpolation is part of CSS selector", async test("should suggest variables and functions as function parameters in string interpolation ", async () => { ls.configure({ - completionSettings: { - suggestFromUseOnly: true, + scss: { + completion: { + suggestFromUseOnly: true, + }, }, }); 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 8d2a84b4..effd42d6 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 @@ -13,10 +13,18 @@ const ls = getLanguageService({ fileSystemProvider, ...rest }); beforeEach(() => { ls.clearCache(); ls.configure({ - completionSettings: { - suggestFromUseOnly: true, + scss: { + completion: { + suggestFromUseOnly: true, + css: false, + }, + }, + sass: { + completion: { + suggestFromUseOnly: true, + }, }, - }); // Reset any configuration to default + }); }); test("suggests built-in sass modules", async () => { @@ -306,12 +314,6 @@ test("should suggest symbols from the document we use when it also forwards anot }); test("should not suggest symbols from a module used by the one we use", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument( ['@use "./three";', "$primary: limegreen;"], { @@ -342,12 +344,6 @@ test("should not suggest symbols from a module used by the one we use", async () }); test("should only suggest symbols from the current namespace, not others being used", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument( ['@use "./two";', '@use "./three";', "@function test() { @return two."], { @@ -375,12 +371,6 @@ test("should only suggest symbols from the current namespace, not others being u }); test("should not suggest private symbols from the current namespace", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument( ['@use "./two";', "@function test() { @return two."], { @@ -405,12 +395,6 @@ test("should not suggest private symbols from the current namespace", async () = }); test("should suggest symbol from a different document via @use when in @if", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument("$primary: limegreen;", { uri: "one.scss", }); @@ -427,12 +411,6 @@ test("should suggest symbol from a different document via @use when in @if", asy }); test("should suggest symbol from a different document via @use when in @else if", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument("$primary: limegreen;", { uri: "one.scss", }); @@ -452,12 +430,6 @@ test("should suggest symbol from a different document via @use when in @else if" }); test("should suggest symbol from a different document via @use when in @each", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument("$primary: limegreen;", { uri: "one.scss", }); @@ -477,12 +449,6 @@ test("should suggest symbol from a different document via @use when in @each", a }); test("should suggest symbol from a different document via @use when in @for", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument("$primary: limegreen;", { uri: "one.scss", }); @@ -502,12 +468,6 @@ test("should suggest symbol from a different document via @use when in @for", as }); test("should suggest symbol from a different document via @use when in @wile", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument("$primary: limegreen;", { uri: "one.scss", }); @@ -579,12 +539,6 @@ test("should suggest prefixed symbol from a different document via @use and @for }); test("should not include hidden symbol if configured", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument( ["$primary: limegreen;", "$secret: red;"], { @@ -646,12 +600,6 @@ test("should not include private symbol", async () => { }); test("should only include shown symbol if configured", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument( ["$primary: limegreen;", "$secondary: yellow;", "$public: red;"], { @@ -692,12 +640,6 @@ test("should only include shown symbol if configured", async () => { }); test("should suggest mixin with no parameter", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument( ["@mixin primary() { color: $primary; }"], { uri: "one.scss" }, @@ -746,9 +688,11 @@ test("should suggest mixin with no parameter", async () => { test("should suggest mixin with optional parameter", async () => { ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - suggestionStyle: "nobracket", + scss: { + completion: { + mixinStyle: "nobracket", + suggestFromUseOnly: true, + }, }, }); @@ -802,9 +746,11 @@ test("should suggest mixin with optional parameter", async () => { test("should suggest mixin with required parameter", async () => { ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - suggestionStyle: "bracket", + scss: { + completion: { + mixinStyle: "bracket", + suggestFromUseOnly: true, + }, }, }); @@ -858,9 +804,11 @@ test("should suggest mixin with required parameter", async () => { test("given both required and optional parameters should suggest two variants of mixin - one with all parameters and one with only required", async () => { ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - suggestionStyle: "nobracket", + scss: { + completion: { + mixinStyle: "nobracket", + suggestFromUseOnly: true, + }, }, }); @@ -942,12 +890,6 @@ test("given both required and optional parameters should suggest two variants of }); test("should suggest function with no parameter", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument( ["@function primary() { @return $primary; }"], { uri: "one.scss" }, @@ -995,12 +937,6 @@ test("should suggest function with no parameter", async () => { }); test("should suggest function with optional parameter", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument( ["@function primary($color: red) { @return $color; }"], { uri: "one.scss" }, @@ -1048,12 +984,6 @@ test("should suggest function with optional parameter", async () => { }); test("should suggest function with required parameter", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument( ["@function primary($color) { @return $color; }"], { uri: "one.scss" }, @@ -1101,12 +1031,6 @@ test("should suggest function with required parameter", async () => { }); test("given both required and optional parameters should suggest two variants of function - one with all parameters and one with only required", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument( ["@function primary($a, $b: 1) { @return $a * $b; }"], { uri: "one.scss" }, @@ -1184,8 +1108,10 @@ test("given both required and optional parameters should suggest two variants of test("should suggest all symbols as legacy @import may be in use", async () => { ls.configure({ - completionSettings: { - suggestFromUseOnly: false, + scss: { + completion: { + suggestFromUseOnly: false, + }, }, }); const one = fileSystemProvider.createDocument("$primary: limegreen;", { @@ -1233,9 +1159,10 @@ test("should suggest all symbols as legacy @import may be in use", async () => { test("should not suggest legacy @import symbols if configured", async () => { ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: true, + scss: { + completion: { + suggestFromUseOnly: true, + }, }, }); @@ -1258,12 +1185,6 @@ test("should not suggest legacy @import symbols if configured", async () => { }); test("should suggest symbol from a different document via @use with wildcard alias", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const one = fileSystemProvider.createDocument( ["$primary: limegreen;", "@function one() { @return 1; }"], { @@ -1342,26 +1263,7 @@ test("should suggest symbol from a different document via @use with wildcard ali }); test("does not suggest sass globals if suggestFromUseOnly is true", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: true, - }, - }); - const document = fileSystemProvider.createDocument("@debug co"); const { items } = await ls.doComplete(document, Position.create(0, 9)); assert.isUndefined(items.find((item) => item.label === "comparable")); }); - -// We don't call the upstream for suggestions here since we got complaints about duplicates -test.skip("does suggest sass globals if suggestFromUseOnly is false", async () => { - ls.configure({ - completionSettings: { - suggestFromUseOnly: false, - }, - }); - - const document = fileSystemProvider.createDocument("@debug co"); - const { items } = await ls.doComplete(document, Position.create(0, 9)); - assert.isOk(items.find((item) => item.label === "comparable")); -}); diff --git a/packages/language-services/src/features/__tests__/do-complete-node-modules.test.ts b/packages/language-services/src/features/__tests__/do-complete-node-modules.test.ts index 07529adb..b60bb4c4 100644 --- a/packages/language-services/src/features/__tests__/do-complete-node-modules.test.ts +++ b/packages/language-services/src/features/__tests__/do-complete-node-modules.test.ts @@ -1,4 +1,4 @@ -import { test, assert, beforeEach } from "vitest"; +import { test, assert } from "vitest"; import { getLanguageService } from "../../language-services"; import { Position } from "../../language-services-types"; import { getOptions } from "../../utils/test-helpers"; @@ -6,13 +6,17 @@ import { getOptions } from "../../utils/test-helpers"; const { fileSystemProvider, ...rest } = getOptions(); const ls = getLanguageService({ fileSystemProvider, ...rest }); -beforeEach(() => { - ls.clearCache(); - ls.configure({ - completionSettings: { +ls.configure({ + scss: { + completion: { suggestFromUseOnly: true, }, - }); // Reset any configuration to default + }, + sass: { + completion: { + suggestFromUseOnly: true, + }, + }, }); test("symbols forwarded from node_modules don't get suggested unless used", async () => { diff --git a/packages/language-services/src/features/__tests__/do-complete-placeholders.test.ts b/packages/language-services/src/features/__tests__/do-complete-placeholders.test.ts index 0e79025e..22b2559f 100644 --- a/packages/language-services/src/features/__tests__/do-complete-placeholders.test.ts +++ b/packages/language-services/src/features/__tests__/do-complete-placeholders.test.ts @@ -1,4 +1,4 @@ -import { test, assert, beforeEach } from "vitest"; +import { test, assert } from "vitest"; import { getLanguageService } from "../../language-services"; import { CompletionItemKind, @@ -10,10 +10,6 @@ import { getOptions } from "../../utils/test-helpers"; const { fileSystemProvider, ...rest } = getOptions(); const ls = getLanguageService({ fileSystemProvider, ...rest }); -beforeEach(() => { - ls.clearCache(); -}); - test("when declaring a placeholder selector, suggest placeholders that have an @extend usage", async () => { // https://github.com/wkillerud/some-sass/issues/49 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 e8014f30..8b8c9332 100644 --- a/packages/language-services/src/features/__tests__/do-complete.test.ts +++ b/packages/language-services/src/features/__tests__/do-complete.test.ts @@ -6,19 +6,21 @@ import { getOptions } from "../../utils/test-helpers"; const { fileSystemProvider, ...rest } = getOptions(); const ls = getLanguageService({ fileSystemProvider, ...rest }); +ls.configure({ + scss: { + completion: { + includeFromCurrentDocument: true, + suggestFromUseOnly: false, + css: false, + }, + }, +}); + beforeEach(() => { ls.clearCache(); - ls.configure({}); // Reset any configuration to default }); test("should not suggest mixin or placeholder as a property value", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -38,13 +40,6 @@ test("should not suggest mixin or placeholder as a property value", async () => }); test("should not suggest mixin or placeholder as a variable value", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -64,13 +59,6 @@ test("should not suggest mixin or placeholder as a variable value", async () => }); test("should not suggest function, variable or placeholder after an @include", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -90,13 +78,6 @@ test("should not suggest function, variable or placeholder after an @include", a }); test("should not suggest function, variable or mixin after an @extend", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -116,13 +97,6 @@ test("should not suggest function, variable or mixin after an @extend", async () }); test("should suggest variable in @return", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -141,13 +115,6 @@ test("should suggest variable in @return", async () => { }); test("should suggest function in @return", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -166,13 +133,6 @@ test("should suggest function in @return", async () => { }); test("should suggest variable in @if", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -192,13 +152,6 @@ test("should suggest variable in @if", async () => { }); test("should suggest function in @if", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -218,13 +171,6 @@ test("should suggest function in @if", async () => { }); test("should suggest variable in @else if", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -245,13 +191,6 @@ test("should suggest variable in @else if", async () => { }); test("should suggest function in @else if", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -272,13 +211,6 @@ test("should suggest function in @else if", async () => { }); test("should not suggest anything for @each before in", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -295,13 +227,6 @@ test("should not suggest anything for @each before in", async () => { }); test("should suggest variable in for @each $foo in", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -321,13 +246,6 @@ test("should suggest variable in for @each $foo in", async () => { }); test("should suggest function in for @each $foo in", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -347,13 +265,6 @@ test("should suggest function in for @each $foo in", async () => { }); test("should not suggest anything in @for before from", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -370,13 +281,6 @@ test("should not suggest anything in @for before from", async () => { }); test("should suggest variable in @for $i from ", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -396,13 +300,6 @@ test("should suggest variable in @for $i from ", async () => { }); test("should suggest function in @for $i from ", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -422,13 +319,6 @@ test("should suggest function in @for $i from ", async () => { }); test("should suggest variable @for $i from 1 to ", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -448,13 +338,6 @@ test("should suggest variable @for $i from 1 to ", async () => { }); test("should suggest function @for $i from 1 to ", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -474,13 +357,6 @@ test("should suggest function @for $i from 1 to ", async () => { }); test("should suggest variable in @for $i from 1 through ", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -500,13 +376,6 @@ test("should suggest variable in @for $i from 1 through ", async () => { }); test("should suggest function in @for $i from 1 through ", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", @@ -526,13 +395,6 @@ test("should suggest function in @for $i from 1 through ", async () => { }); test("should suggest variable and function as parameter to mixin, not mixin or placeholder", async () => { - ls.configure({ - completionSettings: { - suggestAllFromOpenDocument: true, - suggestFromUseOnly: false, - }, - }); - const one = fileSystemProvider.createDocument([ '$name: "value";', "@mixin mixin($a: 1, $b) {}", diff --git a/packages/language-services/src/features/__tests__/do-signature-help.test.ts b/packages/language-services/src/features/__tests__/do-signature-help.test.ts index b17eb18c..a078fc65 100644 --- a/packages/language-services/src/features/__tests__/do-signature-help.test.ts +++ b/packages/language-services/src/features/__tests__/do-signature-help.test.ts @@ -311,11 +311,7 @@ test("signature help when given more parameters than are supported", async () => }); const result = await ls.doSignatureHelp(document, Position.create(0, 18)); - assert.deepStrictEqual(result, { - signatures: [], - activeParameter: 0, - activeSignature: 0, - }); + assert.deepStrictEqual(result, null); }); test("is not confused by using a function as a parameter", async () => { diff --git a/packages/language-services/src/features/code-actions.ts b/packages/language-services/src/features/code-actions.ts index 18feee26..8f64f8d5 100644 --- a/packages/language-services/src/features/code-actions.ts +++ b/packages/language-services/src/features/code-actions.ts @@ -3,7 +3,7 @@ import { CodeAction, CodeActionContext, CodeActionKind, - LanguageServiceConfiguration, + LanguageServerConfiguration, Position, Range, TextDocument, @@ -24,18 +24,13 @@ export class CodeActions extends LanguageFeature { } let upstream: CodeAction[] = []; - if ( - document.languageId === "sass" || - this.configuration.completionSettings?.suggestAllFromOpenDocument - ) { - const stylesheet = this.ls.parseStylesheet(document); - upstream = this.getUpstreamLanguageServer().doCodeActions2( - document, - range, - context, - stylesheet, - ); - } + const stylesheet = this.ls.parseStylesheet(document); + upstream = this.getUpstreamLanguageServer(document).doCodeActions2( + document, + range, + context, + stylesheet, + ); return [ this.getExtractVariableAction(document, range), @@ -271,14 +266,14 @@ const tab = " "; function indentText( text: string, - settings: LanguageServiceConfiguration, + settings: LanguageServerConfiguration, ): string { - if (settings.editorSettings?.insertSpaces) { + if (settings.editor?.insertSpaces) { const numberOfSpaces: number = - typeof settings.editorSettings?.indentSize === "number" - ? settings.editorSettings?.indentSize - : typeof settings.editorSettings?.tabSize === "number" - ? settings.editorSettings?.tabSize + typeof settings.editor?.indentSize === "number" + ? settings.editor?.indentSize + : typeof settings.editor?.tabSize === "number" + ? settings.editor?.tabSize : 2; return `${space.repeat(numberOfSpaces)}${text}`; } diff --git a/packages/language-services/src/features/do-complete.ts b/packages/language-services/src/features/do-complete.ts index 108a8864..20614532 100644 --- a/packages/language-services/src/features/do-complete.ts +++ b/packages/language-services/src/features/do-complete.ts @@ -92,7 +92,7 @@ export class DoComplete extends LanguageFeature { ): Promise { const result = CompletionList.create([]); - const upstreamLs = this.getUpstreamLanguageServer(); + const upstreamLs = this.getUpstreamLanguageServer(document); const context = this.createCompletionContext(document, position); const stylesheet = this.ls.parseStylesheet(document); @@ -185,7 +185,6 @@ export class DoComplete extends LanguageFeature { position, stylesheet, this.getDocumentContext(), - this.configuration.completionSettings, ); if (upstreamResult.items.length > 0) { result.items.push(...upstreamResult.items); @@ -286,12 +285,13 @@ export class DoComplete extends LanguageFeature { } } + const config = this.languageConfiguration(document); // Legacy @import style suggestions - if (!this.configuration.completionSettings?.suggestFromUseOnly) { + if (!config.completion.suggestFromUseOnly) { const documents = this.cache.documents(); for (const currentDocument of documents) { if ( - !this.configuration.completionSettings?.suggestAllFromOpenDocument && + !config.completion.includeFromCurrentDocument && currentDocument.uri === document.uri ) { continue; @@ -323,6 +323,7 @@ export class DoComplete extends LanguageFeature { if (!context.isMixinContext) break; const items = await this.doMixinCompletion( + document, currentDocument, context, symbol, @@ -352,16 +353,15 @@ export class DoComplete extends LanguageFeature { } } - if (document.languageId === "sass") { - const upstreamResult = await upstreamLs.doComplete2( + if (config.completion.css) { + const cssResults = await upstreamLs.doComplete2( document, position, stylesheet, this.getDocumentContext(), - this.configuration.completionSettings, ); - if (upstreamResult.items.length > 0) { - result.items.push(...upstreamResult.items); + if (cssResults.items.length > 0) { + result.items.push(...cssResults.items); } } @@ -495,11 +495,9 @@ export class DoComplete extends LanguageFeature { const lastChar = lineBeforePosition.charAt( lineBeforePosition.length - 1, ); - const triggers = - this.configuration.completionSettings - ?.suggestFunctionsInStringContextAfterSymbols; - if (triggers) { - context.isFunctionContext = triggers.includes(lastChar); + const functionTriggers = " (+-*%"; + if (functionTriggers) { + context.isFunctionContext = functionTriggers.includes(lastChar); } } } else if (isQuotes) { @@ -522,7 +520,7 @@ export class DoComplete extends LanguageFeature { const items: CompletionItem[] = []; const result = await this.findInWorkspace((document) => { // keep track of visited to avoid duplicates - // if completionSettings?.suggestFromUseOnly is false + // if suggestFromUseOnly is false visited.add(document.uri); const symbols = this.ls.findDocumentSymbols(document); @@ -544,7 +542,8 @@ export class DoComplete extends LanguageFeature { items.push(...result); } - if (!this.configuration.completionSettings?.suggestFromUseOnly) { + const config = this.languageConfiguration(initialDocument); + if (!config.completion.suggestFromUseOnly) { const documents = this.cache.documents(); for (const current of documents) { if (visited.has(current.uri)) { @@ -754,6 +753,7 @@ export class DoComplete extends LanguageFeature { if (!context.isMixinContext) break; const mixs = await this.doMixinCompletion( + document, currentDocument, context, symbol, @@ -906,6 +906,7 @@ export class DoComplete extends LanguageFeature { } private async doMixinCompletion( + initialDocument: TextDocument, document: TextDocument, context: CompletionContext, symbol: SassDocumentSymbol, @@ -956,13 +957,12 @@ export class DoComplete extends LanguageFeature { base.filterText = `${context.namespace}.${label}`; } + const config = this.languageConfiguration(initialDocument); const makeCompletionVariants = (insert: string, detail?: string) => { // Not all mixins have @content, but when they do, be smart about adding brackets // and move the cursor to be ready to add said contents. // Include as separate suggestion since content may not always be needed or wanted. - if ( - this.configuration.completionSettings?.suggestionStyle !== "bracket" - ) { + if (config.completion.mixinStyle !== "bracket") { items.push({ ...base, labelDetails: detail ? { detail: `(${detail})` } : undefined, @@ -972,11 +972,9 @@ export class DoComplete extends LanguageFeature { if ( snippetSupport && - this.configuration.completionSettings?.suggestionStyle !== - "nobracket" && + config.completion.mixinStyle !== "nobracket" && document.languageId === "scss" ) { - // TODO: test if this works correctly with multiline, I think so from the spec text const insertSnippet = `${insert} {\n\t$0\n}`; items.push({ ...base, @@ -1196,8 +1194,11 @@ export class DoComplete extends LanguageFeature { const url = node.getText().replace(/["']/g, ""); const moduleName = getModuleNameFromPath(url); - const rootFolderUri = this.configuration.workspaceRoot - ? Utils.joinPath(this.configuration.workspaceRoot, "/").toString(true) + const rootFolderUri = this.configuration.workspace.workspaceRoot + ? Utils.joinPath( + this.configuration.workspace.workspaceRoot, + "/", + ).toString(true) : ""; const documentFolderUri = Utils.dirname(URI.parse(document.uri)).toString( true, diff --git a/packages/language-services/src/features/do-diagnostics.ts b/packages/language-services/src/features/do-diagnostics.ts index 1c90a6a4..44c4dbe7 100644 --- a/packages/language-services/src/features/do-diagnostics.ts +++ b/packages/language-services/src/features/do-diagnostics.ts @@ -74,11 +74,16 @@ export class DoDiagnostics extends LanguageFeature { return []; } + const config = this.languageConfiguration(document); + const stylesheet = this.ls.parseStylesheet(document); - const diagnostics = this.getUpstreamLanguageServer().doValidation( + const diagnostics = this.getUpstreamLanguageServer(document).doValidation( document, stylesheet, - { validate: true }, + { + validate: config.diagnostics.enabled, + lint: config.diagnostics.lint.enabled ? config.diagnostics.lint : false, + }, ); return diagnostics; } @@ -86,6 +91,11 @@ export class DoDiagnostics extends LanguageFeature { private async doDeprecationDiagnostics( document: TextDocument, ): Promise { + const config = this.languageConfiguration(document); + if (config.diagnostics.deprecation.enabled === false) { + return []; + } + const references = this.getReferences(document); const diagnostics: Diagnostic[] = []; diff --git a/packages/language-services/src/features/do-hover.ts b/packages/language-services/src/features/do-hover.ts index 4d9cb370..b8e5376e 100644 --- a/packages/language-services/src/features/do-hover.ts +++ b/packages/language-services/src/features/do-hover.ts @@ -25,6 +25,7 @@ export class DoHover extends LanguageFeature { document: TextDocument, position: Position, ): Promise { + const config = this.languageConfiguration(document); const stylesheet = this.ls.parseStylesheet(document); const offset = document.offsetAt(position); @@ -67,10 +68,10 @@ export class DoHover extends LanguageFeature { type = SymbolKind.Method; } if (type === null) { - if (document.languageId === "sass") { - // We are probably hovering over a CSS identifier - // and want to defer this to vscode-css-languageservice's hover handler - return this.getUpstreamLanguageServer().doHover( + if (config.hover.documentation) { + // We are probably hovering over a CSS identifier. + // In VS Code, by default we defer this to vscode-css-languageservice's hover handler. + return this.getUpstreamLanguageServer(document).doHover( document, position, stylesheet, @@ -136,11 +137,14 @@ export class DoHover extends LanguageFeature { kind: MarkupKind.Markdown, value: [ candidate.annotation, - "____", - `[SassDoc reference](http://sassdoc.com/annotations/#${candidate.annotation.slice( - 1, - )})`, - ].join("\n"), + config.hover.references + ? `\n[SassDoc reference](http://sassdoc.com/annotations/#${candidate.annotation.slice( + 1, + )})` + : "", + ] + .join("\n") + .trim(), }, }; } @@ -259,9 +263,12 @@ export class DoHover extends LanguageFeature { kind: MarkupKind.Markdown, value: [ description, - "", - `[Sass reference](${reference}#${builtinName})`, - ].join("\n"), + config.hover.references + ? `\n[Sass reference](${reference}#${builtinName})` + : "", + ] + .join("\n") + .trim(), }, }; } @@ -271,7 +278,7 @@ export class DoHover extends LanguageFeature { } // Lastly, fall back to CSS hover information - return this.getUpstreamLanguageServer().doHover( + return this.getUpstreamLanguageServer(document).doHover( document, position, stylesheet, diff --git a/packages/language-services/src/features/do-signature-help.ts b/packages/language-services/src/features/do-signature-help.ts index afe95192..2b38f24b 100644 --- a/packages/language-services/src/features/do-signature-help.ts +++ b/packages/language-services/src/features/do-signature-help.ts @@ -18,21 +18,15 @@ export class DoSignatureHelp extends LanguageFeature { async doSignatureHelp( document: TextDocument, position: Position, - ): Promise { + ): Promise { const stylesheet = this.ls.parseStylesheet(document); let node = getNodeAtOffset(stylesheet, document.offsetAt(position)) as | Function | MixinReference | null; - const result: SignatureHelp = { - activeSignature: 0, - activeParameter: 0, - signatures: [], - }; - if (!node) { - return result; + return null; } if ( @@ -44,12 +38,17 @@ export class DoSignatureHelp extends LanguageFeature { (node.parent.type !== NodeType.Function && node.parent.type !== NodeType.MixinReference) ) { - return result; + return null; } node = node.parent as Function | MixinReference; } + const result: SignatureHelp = { + activeSignature: 0, + activeParameter: 0, + signatures: [], + }; const identifier = node.getIdentifier()!.getText(); const parameters = node.getArguments().getChildren(); result.activeParameter = parameters.length; diff --git a/packages/language-services/src/features/find-colors.ts b/packages/language-services/src/features/find-colors.ts index 0931d7df..b2c8a97c 100644 --- a/packages/language-services/src/features/find-colors.ts +++ b/packages/language-services/src/features/find-colors.ts @@ -13,6 +13,7 @@ import { export class FindColors extends LanguageFeature { async findColors(document: TextDocument): Promise { + const config = this.languageConfiguration(document); const variables: Variable[] = []; const stylesheet = this.ls.parseStylesheet(document); stylesheet.accept((node) => { @@ -31,11 +32,8 @@ export class FindColors extends LanguageFeature { return true; }); - if ( - variables.length > - (this.configuration.editorSettings?.colorDecoratorsLimit || 500) - ) { - // skip color decorators for large documents + if (variables.length > this.configuration.editor.colorDecoratorsLimit) { + // skip color decorators for large documents, it freezes up other features return []; } @@ -73,11 +71,10 @@ export class FindColors extends LanguageFeature { }), ); - if (document.languageId === "sass") { - const upstream = this.getUpstreamLanguageServer().findDocumentColors( + if (config.colors.includeFromCurrentDocument) { + const upstream = this.getUpstreamLanguageServer( document, - stylesheet, - ); + ).findDocumentColors(document, stylesheet); result.push(...upstream); } @@ -97,25 +94,22 @@ export class FindColors extends LanguageFeature { if (node && node.type === NodeType.VariableName) { const parent = node.getParent(); if (parent && parent.type === NodeType.VariableDeclaration) { - return this.getUpstreamLanguageServer().getColorPresentations( + return this.getUpstreamLanguageServer(document).getColorPresentations( document, stylesheet, color, range, ); + } else { + return []; } - } else if ( - document.languageId === "sass" || - this.configuration.completionSettings?.suggestAllFromOpenDocument - ) { - return this.getUpstreamLanguageServer().getColorPresentations( - document, - stylesheet, - color, - range, - ); } - return []; + return this.getUpstreamLanguageServer(document).getColorPresentations( + document, + stylesheet, + color, + range, + ); } } diff --git a/packages/language-services/src/features/find-definition.ts b/packages/language-services/src/features/find-definition.ts index 1821cbda..ec9ddf96 100644 --- a/packages/language-services/src/features/find-definition.ts +++ b/packages/language-services/src/features/find-definition.ts @@ -24,7 +24,11 @@ export class FindDefinition extends LanguageFeature { const offset = document.offsetAt(position); const node = getNodeAtOffset(stylesheet, offset); if (!node) { - return this.goUpstream(document, position, stylesheet); + return this.getUpstreamLanguageServer(document).findDefinition( + document, + position, + stylesheet, + ); } // Sometimes we can't tell at position whether an identifier is a Method or a Function @@ -99,7 +103,11 @@ export class FindDefinition extends LanguageFeature { } if (!name || !kinds) { - return this.goUpstream(document, position, stylesheet); + return this.getUpstreamLanguageServer(document).findDefinition( + document, + position, + stylesheet, + ); } // Traverse the workspace looking for a symbol of kinds.includes(symbol.kind) && name === symbol.name @@ -130,22 +138,20 @@ export class FindDefinition extends LanguageFeature { } // If not found, go through the old fashioned way and assume everything is in scope via @import - const symbols = this.ls.findWorkspaceSymbols(name); - for (const symbol of symbols) { - if (kinds.includes(symbol.kind)) { - return symbol.location; + const documents = this.cache.documents(); + for (const document of documents) { + const symbols = this.ls.findDocumentSymbols(document); + for (const symbol of symbols) { + if (symbol.name !== name) { + continue; + } + if (kinds.includes(symbol.kind)) { + return Location.create(document.uri, symbol.selectionRange); + } } } - return this.goUpstream(document, position, stylesheet); - } - - private goUpstream( - document: TextDocument, - position: Position, - stylesheet: Node, - ): Location | null { - return this.getUpstreamLanguageServer().findDefinition( + return this.getUpstreamLanguageServer(document).findDefinition( document, position, stylesheet, diff --git a/packages/language-services/src/features/find-document-highlights.ts b/packages/language-services/src/features/find-document-highlights.ts index b5989e90..65422a46 100644 --- a/packages/language-services/src/features/find-document-highlights.ts +++ b/packages/language-services/src/features/find-document-highlights.ts @@ -11,7 +11,7 @@ export class FindDocumentHighlights extends LanguageFeature { position: Position, ): DocumentHighlight[] { const stylesheet = this.ls.parseStylesheet(document); - return this.getUpstreamLanguageServer().findDocumentHighlights( + return this.getUpstreamLanguageServer(document).findDocumentHighlights( document, position, stylesheet, diff --git a/packages/language-services/src/features/find-document-links.ts b/packages/language-services/src/features/find-document-links.ts index 89be1b51..bd8649a7 100644 --- a/packages/language-services/src/features/find-document-links.ts +++ b/packages/language-services/src/features/find-document-links.ts @@ -13,11 +13,10 @@ export class FindDocumentLinks extends LanguageFeature { if (cached) return cached; const stylesheet = this.ls.parseStylesheet(document); - const links = await this.getUpstreamLanguageServer().findDocumentLinks2( + const links = await this.getUpstreamLanguageServer( document, - stylesheet, - this.getDocumentContext(), - ); + ).findDocumentLinks2(document, stylesheet, this.getDocumentContext()); + for (const link of links) { if (link.target && !link.target.includes("sass:")) { // For monorepos, resolve the real path behind a symlink, since multiple links in `node_modules/` can point to the same file. diff --git a/packages/language-services/src/features/find-references.ts b/packages/language-services/src/features/find-references.ts index badd21f2..3e736097 100644 --- a/packages/language-services/src/features/find-references.ts +++ b/packages/language-services/src/features/find-references.ts @@ -520,7 +520,7 @@ export class FindReferences extends LanguageFeature { return result; } - async isSameRealPath( + private async isSameRealPath( candidate: string, definition: string, ): Promise { diff --git a/packages/language-services/src/features/find-symbols.ts b/packages/language-services/src/features/find-symbols.ts index d5d715ca..7e6bdebc 100644 --- a/packages/language-services/src/features/find-symbols.ts +++ b/packages/language-services/src/features/find-symbols.ts @@ -16,10 +16,9 @@ export class FindSymbols extends LanguageFeature { if (cachedSymbols) return cachedSymbols; const stylesheet = this.ls.parseStylesheet(document); - const symbols = this.getUpstreamLanguageServer().findDocumentSymbols2( + const symbols = this.getUpstreamLanguageServer( document, - stylesheet, - ) as SassDocumentSymbol[]; + ).findDocumentSymbols2(document, stylesheet) as SassDocumentSymbol[]; const sassdoc: ParseResult[] = this.cache.getSassdoc(document); for (const doc of sassdoc) { @@ -70,16 +69,22 @@ export class FindSymbols extends LanguageFeature { const documents = this.cache.documents(); const result: SymbolInformation[] = []; for (const document of documents) { - const symbols = this.findDocumentSymbols(document); - for (const symbol of symbols) { - if (query && !symbol.name.includes(query)) { - continue; + // This is the exception to the rule that this enabled check + // should happen at the server edge. It's only at this point + // we know if the document should be included or not. + const config = this.languageConfiguration(document); + if (config.workspaceSymbol.enabled) { + const symbols = this.findDocumentSymbols(document); + for (const symbol of symbols) { + if (query && !symbol.name.includes(query)) { + continue; + } + result.push({ + name: symbol.name, + kind: symbol.kind, + location: Location.create(document.uri, symbol.selectionRange), + }); } - result.push({ - name: symbol.name, - kind: symbol.kind, - location: Location.create(document.uri, symbol.selectionRange), - }); } } return result; diff --git a/packages/language-services/src/features/folding-ranges.ts b/packages/language-services/src/features/folding-ranges.ts index c89b8456..be6780bc 100644 --- a/packages/language-services/src/features/folding-ranges.ts +++ b/packages/language-services/src/features/folding-ranges.ts @@ -10,7 +10,7 @@ export class FoldingRanges extends LanguageFeature { document: TextDocument, context?: FoldingRangeContext, ): Promise { - const result = this.getUpstreamLanguageServer().getFoldingRanges( + const result = this.getUpstreamLanguageServer(document).getFoldingRanges( document, context, ); diff --git a/packages/language-services/src/features/selection-ranges.ts b/packages/language-services/src/features/selection-ranges.ts index 42597807..eaba3b3b 100644 --- a/packages/language-services/src/features/selection-ranges.ts +++ b/packages/language-services/src/features/selection-ranges.ts @@ -11,7 +11,7 @@ export class SelectionRanges extends LanguageFeature { positions: Position[], ): Promise { const stylesheet = this.ls.parseStylesheet(document); - const result = this.getUpstreamLanguageServer().getSelectionRanges( + const result = this.getUpstreamLanguageServer(document).getSelectionRanges( document, positions, stylesheet, diff --git a/packages/language-services/src/language-feature.ts b/packages/language-services/src/language-feature.ts index a9187e4e..e912b06b 100644 --- a/packages/language-services/src/language-feature.ts +++ b/packages/language-services/src/language-feature.ts @@ -5,12 +5,14 @@ import { Scanner, SassScanner, } from "@somesass/vscode-css-languageservice"; +import merge from "lodash.merge"; +import { defaultConfiguration } from "./configuration"; import { LanguageModelCache } from "./language-model-cache"; import { LanguageServiceOptions, TextDocument, LanguageService, - LanguageServiceConfiguration, + LanguageServerConfiguration, NodeType, Range, SassDocumentSymbol, @@ -21,12 +23,16 @@ import { URI, Utils, ClientCapabilities, + RecursivePartial, + LanguageConfiguration, } from "./language-services-types"; import { asDollarlessVariable } from "./utils/sass"; export type LanguageFeatureInternal = { cache: LanguageModelCache; + cssLs: VSCodeLanguageService; sassLs: VSCodeLanguageService; + scssLs: VSCodeLanguageService; }; type FindOptions = { @@ -39,16 +45,6 @@ type FindOptions = { depth?: number; }; -const defaultConfiguration: LanguageServiceConfiguration = { - completionSettings: { - suggestAllFromOpenDocument: false, - suggestFromUseOnly: false, - suggestFunctionsInStringContextAfterSymbols: " (+-*%", - suggestionStyle: "all", - triggerPropertyValueCompletion: true, - }, -}; - /** * Base class for features. Provides helpers to do the navigation * between modules. @@ -57,7 +53,7 @@ export abstract class LanguageFeature { protected ls; protected options; protected clientCapabilities: ClientCapabilities; - protected configuration: LanguageServiceConfiguration = {}; + protected configuration: LanguageServerConfiguration = defaultConfiguration; private _internal: LanguageFeatureInternal; @@ -76,19 +72,64 @@ export abstract class LanguageFeature { this._internal = _internal; } - configure(configuration: LanguageServiceConfiguration): void { - this.configuration = { - ...defaultConfiguration, - ...configuration, - completionSettings: { - ...defaultConfiguration.completionSettings, - ...(configuration.completionSettings || {}), - }, - }; - this._internal.sassLs.configure(configuration); + languageConfiguration(document: TextDocument): LanguageConfiguration { + switch (document.languageId) { + case "css": { + return this.configuration.css; + } + case "sass": { + return this.configuration.sass; + } + case "scss": { + return this.configuration.scss; + } + } + + throw new Error(`Unsupported language ${document.languageId}`); + } + + configure( + configuration: RecursivePartial, + ): void { + this.configuration = merge(defaultConfiguration, configuration); + + this._internal.sassLs.configure({ + validate: this.configuration.sass.diagnostics.enabled, + lint: this.configuration.sass.diagnostics.lint, + completion: this.configuration.sass.completion, + hover: this.configuration.sass.hover, + importAliases: this.configuration.workspace.importAliases, + loadPaths: this.configuration.workspace.loadPaths, + }); + + this._internal.scssLs.configure({ + validate: this.configuration.scss.diagnostics.enabled, + lint: this.configuration.scss.diagnostics.lint, + completion: this.configuration.scss.completion, + hover: this.configuration.scss.hover, + importAliases: this.configuration.workspace.importAliases, + loadPaths: this.configuration.workspace.loadPaths, + }); + + this._internal.cssLs.configure({ + validate: this.configuration.css.diagnostics.enabled, + lint: this.configuration.css.diagnostics.lint, + completion: this.configuration.css.completion, + hover: this.configuration.css.hover, + importAliases: this.configuration.workspace.importAliases, + loadPaths: this.configuration.workspace.loadPaths, + }); } - protected getUpstreamLanguageServer(): VSCodeLanguageService { + protected getUpstreamLanguageServer( + document: TextDocument, + ): VSCodeLanguageService { + if (document.languageId === "scss") { + return this._internal.scssLs; + } + if (document.languageId === "css") { + return this._internal.cssLs; + } return this._internal.sassLs; } @@ -99,10 +140,11 @@ export abstract class LanguageFeature { * @returns The resolved path */ resolveReference: (ref: string, base: string) => { - if (ref.startsWith("/") && this.configuration.workspaceRoot) { - return Utils.joinPath(this.configuration.workspaceRoot, ref).toString( - true, - ); + if (ref.startsWith("/") && this.configuration.workspace.workspaceRoot) { + return Utils.joinPath( + this.configuration.workspace.workspaceRoot, + ref, + ).toString(true); } try { return resolve(base, ref); diff --git a/packages/language-services/src/language-services-types.ts b/packages/language-services/src/language-services-types.ts index c1cc3a2b..88d72abd 100644 --- a/packages/language-services/src/language-services-types.ts +++ b/packages/language-services/src/language-services-types.ts @@ -74,6 +74,14 @@ export interface SassDocumentSymbol extends DocumentSymbol { children?: SassDocumentSymbol[]; } +export type RecursivePartial = { + [P in keyof T]?: T[P] extends (infer U)[] + ? RecursivePartial[] + : T[P] extends object | undefined + ? RecursivePartial + : T[P]; +}; + export interface LanguageService { /** * Clears all cached documents, forcing everything to be reparsed the next time a feature is used. @@ -81,16 +89,18 @@ export interface LanguageService { clearCache(): void; /** * You may want to use this to set the workspace root. - * @param settings {@link LanguageServiceConfiguration} + * @param settings {@link LanguageServerConfiguration} * * @example * ```js * languageService.configure({ - * workspaceRoot: URI.parse(this.workspace), + * workspace: { + * workspaceRoot: URI.parse(this.workspace), + * }, * }); * ``` */ - configure(settings: LanguageServiceConfiguration): void; + configure(settings: RecursivePartial): void; doComplete( document: TextDocument, position: Position, @@ -108,7 +118,7 @@ export interface LanguageService { doSignatureHelp( document: TextDocument, position: Position, - ): Promise; + ): Promise; findColors(document: TextDocument): Promise; findDefinition( document: TextDocument, @@ -175,21 +185,34 @@ export type Rename = | { range: Range; placeholder: string } | { defaultBehavior: boolean }; -export interface LanguageServiceConfiguration { +export type LintLevel = "ignore" | "warning" | "error"; + +export interface LanguageConfiguration { /** - * Pass in [load paths](https://sass-lang.com/documentation/cli/dart-sass/#load-path) that will be used in addition to `node_modules`. + * A list of relative file paths pointing to JSON files following the custom data format. + * Some Sass loads custom data on startup to enhance its CSS support for CSS custom properties (variables), at-rules, pseudo-classes, and pseudo-elements you specify in the JSON files. + * The file paths are relative to workspace and only workspace folder settings are considered. */ - loadPaths?: string[]; - completionSettings?: { + customData?: string[]; + codeAction: { + enabled: boolean; + }; + colors: { + enabled: boolean; /** - * Essentially "Visual Studio Code compat mode". + * Compatibility setting for VS Code. * - * If false, Some Sass will not give code suggestions based on contents from the current document. - * - * If you use this server as well as vscode-css-languageserver you can set this setting to avoid duplicates. - * Has no effect for Sass Indented. + * By default the built-in SCSS server shows color decorators for colors declared in the current document. + * To avoid duplicates, by default Some Sass (only in VS Code) will only show color decorators where a variable is being used. */ - suggestAllFromOpenDocument?: boolean; + includeFromCurrentDocument: boolean; + }; + completion: { + enabled: boolean; + /** + * Include CSS completions. + */ + css?: boolean; /** * Mixins with `@content` SassDoc annotations and `%placeholders` get two suggestions by default: * - One without `{ }`. @@ -199,55 +222,133 @@ export interface LanguageServiceConfiguration { * - All suggestions (default). * - No brackets. * - Only brackets. This still includes other suggestions, where there are no brackets to begin with. - * - * @default "all" */ - suggestionStyle?: "all" | "nobracket" | "bracket"; + mixinStyle?: "all" | "nobracket" | "bracket"; /** - * Recommended if you don't rely on `@import`. With this setting turned on, + * Recommended if you don't rely on `@import` in Sass. With this setting turned on, * Some Sass will only suggest variables, mixins and functions from the * namespaces that are in use in the open document. */ suggestFromUseOnly?: boolean; - /** - * Suggest functions after the specified symbols when in a string context. - * For example, if you add the `/` symbol to this setting, then `background: url(images/he|)` - * could suggest a `hello()` function (`|` in this case indicates cursor position). - * - * @default " (+-*%" - */ - suggestFunctionsInStringContextAfterSymbols?: string; /** * By default, Some Sass triggers property value completion after selecting a CSS property. * Use this setting to disable this behavior. + */ + triggerPropertyValueCompletion: boolean; + completePropertyWithSemicolon?: boolean; + /** + * Compatibility setting for VS Code. * - * @default true + * By default the built-in SCSS server shows suggestions for variables, mixins and functions declared in the current document. + * To avoid duplicates, by default Some Sass (only in VS Code) will not suggest them. */ - triggerPropertyValueCompletion?: boolean; - includePrefixDot?: boolean; + includeFromCurrentDocument: boolean; + }; + definition: { + enabled: boolean; + }; + diagnostics: { + enabled: boolean; + deprecation: { + enabled: boolean; + }; + lint: { + enabled: boolean; + compatibleVendorPrefixes: LintLevel; + vendorPrefix: LintLevel; + duplicateProperties: LintLevel; + emptyRules: LintLevel; + importStatement: LintLevel; + boxModel: LintLevel; + universalSelector: LintLevel; + zeroUnits: LintLevel; + fontFaceProperties: LintLevel; + hexColorLength: LintLevel; + argumentsInColorFunction: LintLevel; + unknownProperties: LintLevel; + validProperties: string[]; + ieHack: LintLevel; + unknownVendorSpecificProperties: LintLevel; + propertyIgnoredDueToDisplay: LintLevel; + important: LintLevel; + float: LintLevel; + idSelector: LintLevel; + unknownAtRules: LintLevel; + [key: string]: any; + }; + }; + foldingRanges: { + enabled: boolean; }; - editorSettings?: EditorSettings; + highlights: { + enabled: boolean; + }; + hover: { + enabled: boolean; + documentation: boolean; + references: boolean; + }; + links: { + enabled: boolean; + }; + references: { + enabled: boolean; + }; + rename: { + enabled: boolean; + }; + selectionRanges: { + enabled: boolean; + }; + semanticTokens: { + enabled: boolean; + }; + signatureHelp: { + enabled: boolean; + }; + workspaceSymbol: { + enabled: boolean; + }; +} + +export interface WorkspaceConfiguration { + exclude: string[]; + importAliases: { + [key: string]: any; + }; + /** + * Pass in [load paths](https://sass-lang.com/documentation/cli/dart-sass/#load-path) that will be used in addition to `node_modules`. + */ + loadPaths: string[]; workspaceRoot?: URI; + logLevel: "trace" | "debug" | "info" | "warn" | "error" | "fatal" | "silent"; } -export interface EditorSettings { +export interface LanguageServerConfiguration { + css: LanguageConfiguration; + sass: LanguageConfiguration; + scss: LanguageConfiguration; + editor: EditorConfiguration; + workspace: WorkspaceConfiguration; +} + +export interface EditorConfiguration { /** * Insert spaces rather than tabs. */ - insertSpaces?: boolean; + insertSpaces: boolean; /** * If {@link insertSpaces} is true this option determines the number of space characters is inserted per indent level. */ - indentSize?: number; + indentSize: number; /** * An older editor setting in VS Code. If both this and {@link indentSize} is set, only `indentSize` will be used. */ - tabSize?: number; + tabSize: number; /** * Controls the max number of color decorators that can be rendered in an editor at once. - * @default 500 */ - colorDecoratorsLimit?: number; + colorDecoratorsLimit: number; } export interface AliasSettings { @@ -269,6 +370,15 @@ export interface ClientCapabilities { }; } +export type Logger = { + fatal(message: string): void; + error(message: string): void; + warn(message: string): void; + info(message: string): void; + debug(message: string): void; + trace(message: string): void; +}; + export interface LanguageServiceOptions { clientCapabilities: ClientCapabilities; /** @@ -280,6 +390,7 @@ export interface LanguageServiceOptions { */ fileSystemProvider: FileSystemProvider; languageModelCache?: LanguageModelCacheOptions; + logger?: Logger; } export type LanguageModelCacheOptions = { diff --git a/packages/language-services/src/language-services.ts b/packages/language-services/src/language-services.ts index f53dcb92..6166d802 100644 --- a/packages/language-services/src/language-services.ts +++ b/packages/language-services/src/language-services.ts @@ -1,4 +1,8 @@ -import { getSassLanguageService } from "@somesass/vscode-css-languageservice"; +import { + getCSSLanguageService, + getSassLanguageService, +} from "@somesass/vscode-css-languageservice"; +import { defaultConfiguration } from "./configuration"; import { CodeActions } from "./features/code-actions"; import { DoComplete } from "./features/do-complete"; import { DoDiagnostics } from "./features/do-diagnostics"; @@ -17,7 +21,9 @@ import { LanguageModelCache as LanguageServerCache } from "./language-model-cach import { CodeActionContext, LanguageService, - LanguageServiceConfiguration, + LanguageConfiguration, + EditorConfiguration, + LanguageServerConfiguration, LanguageServiceOptions, Position, TextDocument, @@ -32,8 +38,11 @@ import { import { mapFsProviders } from "./utils/fs-provider"; export { + defaultConfiguration, LanguageService, - LanguageServiceConfiguration, + LanguageServerConfiguration, + LanguageConfiguration, + EditorConfiguration, FileStat, FileSystemProvider, FileType, @@ -63,18 +72,25 @@ class LanguageServiceImpl implements LanguageService { #selectionRanges: SelectionRanges; constructor(options: LanguageServiceOptions) { - const sassLs = getSassLanguageService({ + const vscodeLsOptions = { clientCapabilities: options.clientCapabilities, fileSystemProvider: mapFsProviders(options.fileSystemProvider), - }); + }; + const sassLs = getSassLanguageService(vscodeLsOptions); + const cache = new LanguageServerCache({ sassLs, ...options.languageModelCache, }); + const internal = { - sassLs, cache, + sassLs, + cssLs: getCSSLanguageService(vscodeLsOptions), + // The server code is the same as sassLs, but separate on syntax in case the user has different settings + scssLs: getSassLanguageService(vscodeLsOptions), }; + this.#cache = cache; this.#codeActions = new CodeActions(this, options, internal); this.#doComplete = new DoComplete(this, options, internal); @@ -96,7 +112,7 @@ class LanguageServiceImpl implements LanguageService { this.#selectionRanges = new SelectionRanges(this, options, internal); } - configure(configuration: LanguageServiceConfiguration): void { + configure(configuration: LanguageServerConfiguration): void { this.#codeActions.configure(configuration); this.#doComplete.configure(configuration); this.#doDiagnostics.configure(configuration); diff --git a/packages/language-services/tsconfig.json b/packages/language-services/tsconfig.json index 2b0655a9..de63c7b7 100644 --- a/packages/language-services/tsconfig.json +++ b/packages/language-services/tsconfig.json @@ -7,6 +7,7 @@ "moduleResolution": "node", "declaration": true, "rootDir": "src", + "esModuleInterop": true, "outDir": "dist", "strict": true }, diff --git a/packages/vscode-css-languageservice/src/cssLanguageTypes.ts b/packages/vscode-css-languageservice/src/cssLanguageTypes.ts index f4ddc21d..70886d90 100644 --- a/packages/vscode-css-languageservice/src/cssLanguageTypes.ts +++ b/packages/vscode-css-languageservice/src/cssLanguageTypes.ts @@ -96,7 +96,7 @@ export interface CompletionSettings { export interface LanguageSettings { validate?: boolean; - lint?: LintSettings; + lint?: false | LintSettings; completion?: CompletionSettings; hover?: HoverSettings; importAliases?: AliasSettings; diff --git a/packages/vscode-css-languageservice/src/services/cssValidation.ts b/packages/vscode-css-languageservice/src/services/cssValidation.ts index e785c496..afe13ab7 100644 --- a/packages/vscode-css-languageservice/src/services/cssValidation.ts +++ b/packages/vscode-css-languageservice/src/services/cssValidation.ts @@ -35,7 +35,7 @@ export class CSSValidation { LintVisitor.entries( stylesheet, document, - new LintConfigurationSettings(settings && settings.lint), + new LintConfigurationSettings(settings && settings.lint !== false ? settings.lint : undefined), this.cssDataManager, ), ); diff --git a/packages/vscode-css-languageservice/src/services/lintRules.ts b/packages/vscode-css-languageservice/src/services/lintRules.ts index c86311d0..a57036de 100644 --- a/packages/vscode-css-languageservice/src/services/lintRules.ts +++ b/packages/vscode-css-languageservice/src/services/lintRules.ts @@ -46,7 +46,11 @@ export const Rules = { ), DuplicateDeclarations: new Rule("duplicateProperties", l10n.t("Do not use duplicate style definitions"), Ignore), EmptyRuleSet: new Rule("emptyRules", l10n.t("Do not use empty rulesets"), Warning), - ImportStatemement: new Rule("importStatement", l10n.t("Import statements do not load in parallel"), Ignore), + ImportStatemement: new Rule( + "importStatement", + l10n.t("Import statements can lead to sequential loading of CSS"), + Ignore, + ), BewareOfBoxModelSize: new Rule("boxModel", l10n.t("Do not use width or height when using padding or border"), Ignore), UniversalSelector: new Rule("universalSelector", l10n.t("The universal selector (*) is known to be slow"), Ignore), ZeroWithUnit: new Rule("zeroUnits", l10n.t("No unit for zero needed"), Ignore), diff --git a/packages/vscode-css-languageservice/src/tsconfig.esm.json b/packages/vscode-css-languageservice/src/tsconfig.esm.json index bc6eaa7a..99a4c613 100644 --- a/packages/vscode-css-languageservice/src/tsconfig.esm.json +++ b/packages/vscode-css-languageservice/src/tsconfig.esm.json @@ -4,6 +4,7 @@ "module": "es6", "moduleResolution": "node", "sourceMap": true, + "inlineSourceMap": false, "declaration": true, "strict": true, "stripInternal": true, diff --git a/packages/vscode-css-languageservice/src/tsconfig.json b/packages/vscode-css-languageservice/src/tsconfig.json index fce46f01..f0b3655d 100644 --- a/packages/vscode-css-languageservice/src/tsconfig.json +++ b/packages/vscode-css-languageservice/src/tsconfig.json @@ -4,6 +4,7 @@ "module": "umd", "moduleResolution": "node", "sourceMap": true, + "inlineSourceMap": false, "declaration": true, "strict": true, "stripInternal": true, diff --git a/vscode-extension/README.md b/vscode-extension/README.md index 8aa19a35..7a8ed7c1 100644 --- a/vscode-extension/README.md +++ b/vscode-extension/README.md @@ -26,32 +26,29 @@ See the [user guide](https://wkillerud.github.io/some-sass/) to get the most out ## Recommended settings -These are the recommended settings: +These are the recommended settings if you're just getting started. ```jsonc { // Recommended if you don't rely on @import - "somesass.suggestFromUseOnly": true, + "somesass.scss.completion.suggestFromUseOnly": true, + "somesass.sass.completion.suggestFromUseOnly": true, - // Optional, if you get suggestions from the current document after namespace.$ (you don't need the $ for narrowing down suggestions) + // Optional, if you get suggestions from the current document after namespace.$ (you don't need to type the $ for narrowing down suggestions) "editor.wordBasedSuggestions": false, } ``` -Note that if you have SCSS IntelliSense (`mrmlnc.vscode-scss`) installed you should disable or uninstall it. Otherwise the two extensions will both provide hover information and code suggestions. +### Going all in on Some Sass -### About word-based suggestions +If you don't need language features for [Less](https://lesscss.org/) and don't rely on the built-in formatter, we recommend that you: -When you get completion suggestions and type `namespace.$`, Visual Studio Code treats `$` as a fresh start for suggestions. It will start matching any variable in the current document. There are two ways to get around this: +1. turn off the built-in CSS/SCSS/Less language extension in Visual Studio Code +2. configure Some Sass to turn on all features for CSS, SCSS and Sass indented -1. Turn off word-based suggestions by setting `"editor.wordBasedSuggestions": false`. -2. Don't type the `$` when you write the variable name, let completions fill it out for you. +See the [Settings reference](https://wkillerud.github.io/some-sass/user-guide/settings.html#going-all-in-on-some-sass) for instructions on how to do this. -With the second approach you can keep word-based suggestions turned on. - -### Settings reference - -See the [settings reference](https://wkillerud.github.io/some-sass/user-guide/settings.html#settings-reference) for more information about the different settings of Some Sass. +If you have SCSS IntelliSense (`mrmlnc.vscode-scss`) installed you should disable or uninstall it. ## Changelog diff --git a/vscode-extension/package.json b/vscode-extension/package.json index f57f0b74..c2d24bac 100644 --- a/vscode-extension/package.json +++ b/vscode-extension/package.json @@ -1,179 +1,1410 @@ { - "name": "some-sass", - "displayName": "Some Sass: extended support for Sass and SassDoc", - "description": "Full support for @use and @forward, including aliases, prefixes and hiding. Rich documentation through SassDoc. Workspace-wide code navigation and refactoring.", - "version": "3.9.3", - "private": true, - "publisher": "SomewhatStationery", - "license": "MIT", - "engines": { - "vscode": "^1.86.0" - }, - "icon": "icon.png", - "homepage": "https://wkillerud.github.io/some-sass/", - "repository": { - "type": "git", - "url": "https://github.com/wkillerud/some-sass" - }, - "keywords": [ - "scss", - "sass", - "sassdoc", - "autocompletion", - "intellisense", - "refactor" - ], - "categories": [ - "Programming Languages" - ], - "activationEvents": [ - "onLanguage:scss", - "onLanguage:sass", - "onLanguage:vue", - "onLanguage:svelte", - "onLanguage:astro", - "onCommand:_somesass.applyExtractCodeAction" - ], - "capabilities": { - "virtualWorkspaces": true - }, - "browser": "./dist/browser-client.js", - "main": "./dist/node-client.js", - "contributes": { - "languages": [ - { - "id": "sass", - "aliases": [ - "Sass", - "sass-indented" - ], - "extensions": [ - ".sass" - ], - "configuration": "./languages/sass.configuration.json" - } - ], - "grammars": [ - { - "language": "sass", - "scopeName": "source.sass", - "path": "./languages/sass.tmLanguage.json" - } - ], - "configuration": { - "title": "Some Sass", - "properties": { - "somesass.loadPaths": { - "type": "array", - "items": { - "type": "string" - }, - "default": [], - "markdownDescription": "A list of paths relative to the workspace root where the language server should look for stylesheets loaded by `@use` and `@import`. `node_modules` is always included.\n\nNote that you will have to [configure your Sass compiler separately](https://sass-lang.com/documentation/cli/dart-sass/#load-path)." - }, - "somesass.scannerDepth": { - "type": "number", - "default": 30, - "description": "The maximum number of nested directories to scan." - }, - "somesass.scannerExclude": { - "type": "array", - "items": { - "type": "string" - }, - "default": [ - "**/.git/**", - "**/node_modules/**", - "**/bower_components/**" - ], - "description": "List of glob patterns for directories that are excluded when scanning." - }, - "somesass.scanImportedFiles": { - "type": "boolean", - "default": true, - "deprecationMessage": "Will be removed at some point after `@import` becomes CSS-only.", - "description": "Allows scan imported files. Turning this off will severely limit functionality, and is not recommended." - }, - "somesass.suggestionStyle": { - "type": "string", - "default": "all", - "description": "Controls the style of suggestions for mixins and placeholders.", - "enum": [ - "all", - "nobracket", - "bracket" - ], - "enumItemLabels": [ - "All", - "No brackets", - "Only brackets" - ], - "enumDescriptions": [ - "Show all suggestions", - "Only show suggestions without brackets", - "Where brackets are suggested, omit duplicates without brackets" - ] - }, - "somesass.suggestAllFromOpenDocument": { - "type": "boolean", - "default": false, - "description": "VS Code has built-in code suggestions for (S)CSS symbols declared in the open document. If you prefer the suggestions from Some Sass, you can opt in by turning on this setting. There will be duplicates. No effect for Sass Indented (always on)." - }, - "somesass.suggestFromUseOnly": { - "type": "boolean", - "default": false, - "description": "If your project uses the new module system with @use and @forward, you may want to only include suggestions from your used modules." - }, - "somesass.suggestFunctionsInStringContextAfterSymbols": { - "type": "string", - "default": " (+-*%", - "description": "Allows prompt Functions in String context after specified symbols." - }, - "somesass.triggerPropertyValueCompletion": { - "type": "boolean", - "default": false, - "description": "By default, Some Sass triggers property value completion after selecting a CSS property. Use this setting to disable this behavior." - } - } - } - }, - "dependencies": { - "fast-glob": "3.3.2", - "some-sass-language-server": "1.8.3", - "vscode-css-languageservice": "6.3.1", - "vscode-languageclient": "9.0.1", - "vscode-uri": "3.0.7" - }, - "devDependencies": { - "@types/mocha": "10.0.7", - "@types/vscode": "1.86.0", - "@vscode/test-electron": "2.4.1", - "@vscode/test-web": "0.0.60", - "assert": "2.1.0", - "mocha": "10.7.3", - "shx": "0.3.4" - }, - "scripts": { - "vscode:prepublish": "run-s clean build:production:*", - "clean": "shx rm -rf dist", - "build": "run-s build:development:*", - "build:node": "rspack --config ./rspack.node.config.js", - "build:browser": "rspack --config ./rspack.browser.config.js", - "build:development:node": "npm run build:node -- --mode=development", - "build:development:browser": "npm run build:browser -- --mode=development", - "build:production:node": "npm run build:node -- --mode=production", - "build:production:browser": "npm run build:browser -- --mode=production", - "start:web": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=.", - "lint": "eslint \"**/*.ts\" --cache", - "pretest:e2e": "run-s clean build:production:*", - "test:e2e": "node ./test/e2e/runTest.js", - "pretest:web": "rspack --config ./rspack.test-web.config.js ", - "test:web": "node ./test/web/runTest.js" - }, - "__metadata": { - "id": "6d35099c-3671-464c-ac0b-34a0c3823927", - "publisherDisplayName": "Somewhat Stationery", - "publisherId": "02638283-c13a-4acf-9f26-24bdcfdfce24", - "isPreReleaseVersion": false - } -} \ No newline at end of file + "name": "some-sass", + "displayName": "Some Sass", + "description": "Improved support for SCSS, Sass indented and SassDoc. Workspace awareness and full support for Sass modules.", + "version": "3.9.3", + "private": true, + "publisher": "SomewhatStationery", + "license": "MIT", + "engines": { + "vscode": "^1.86.0" + }, + "icon": "icon.png", + "homepage": "https://wkillerud.github.io/some-sass/", + "repository": { + "type": "git", + "url": "https://github.com/wkillerud/some-sass" + }, + "keywords": [ + "scss", + "sass", + "sassdoc", + "autocompletion", + "intellisense", + "refactor" + ], + "categories": [ + "Programming Languages" + ], + "activationEvents": [ + "onLanguage:css", + "onLanguage:scss", + "onLanguage:sass", + "onLanguage:vue", + "onLanguage:svelte", + "onLanguage:astro", + "onCommand:_somesass.applyExtractCodeAction" + ], + "capabilities": { + "virtualWorkspaces": true + }, + "browser": "./dist/browser-client.js", + "main": "./dist/node-client.js", + "contributes": { + "languages": [ + { + "id": "sass", + "aliases": [ + "Sass", + "sass-indented" + ], + "extensions": [ + ".sass" + ], + "configuration": "./languages/sass.configuration.json" + } + ], + "grammars": [ + { + "language": "sass", + "scopeName": "source.sass", + "path": "./languages/sass.tmLanguage.json" + } + ], + "configuration": [ + { + "title": "Workspace", + "properties": { + "somesass.workspace.loadPaths": { + "type": "array", + "scope": "resource", + "items": { + "type": "string" + }, + "default": [], + "markdownDescription": "A list of paths relative to the workspace root where the language server should look for stylesheets loaded by `@use` and `@import`. `node_modules` is always included.\n\nNote that you will have to [configure your Sass compiler separately](https://sass-lang.com/documentation/cli/dart-sass/#load-path).", + "order": 0 + }, + "somesass.workspace.exclude": { + "type": "array", + "scope": "resource", + "items": { + "type": "string" + }, + "default": [ + "**/.git/**", + "**/node_modules/**" + ], + "description": "List of glob patterns for directories that are excluded when scanning.", + "order": 1 + }, + "somesass.workspace.logLevel": { + "type": "string", + "scope": "resource", + "default": "info", + "enum": [ + "silent", + "fatal", + "error", + "warn", + "info", + "debug", + "trace" + ], + "description": "Control how much gets logged to the Output window.", + "order": 2 + }, + "some-sass.trace.server": { + "type": "string", + "scope": "window", + "enum": [ + "off", + "messages", + "verbose" + ], + "default": "off", + "description": "Log the messages sent between VS Code and the Some Sass language server.", + "order": 999 + } + } + }, + { + "title": "SCSS", + "properties": { + "somesass.scss.codeAction.enabled": { + "order": 10, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable all code actions." + }, + "somesass.scss.colors.enabled": { + "order": 20, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable all color decorators." + }, + "somesass.scss.colors.includeFromCurrentDocument": { + "order": 21, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Compatibility setting for VS Code. By default the built-in SCSS server shows color decorators for variables declared in the current document. To avoid duplicates Some Sass will not show them unless you opt in." + }, + "somesass.scss.completion.enabled": { + "order": 30, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable all completions (IntelliSense)." + }, + "somesass.scss.completion.suggestFromUseOnly": { + "order": 31, + "type": "boolean", + "default": false, + "description": "If your project uses the new module system with @use and @forward, you may want to only include suggestions from your used modules." + }, + "somesass.scss.completion.mixinStyle": { + "order": 32, + "type": "string", + "default": "all", + "description": "Controls the style of suggestions for mixins.", + "enum": [ + "all", + "nobracket", + "bracket" + ], + "enumItemLabels": [ + "All", + "No brackets", + "Only brackets" + ], + "enumDescriptions": [ + "Show all suggestions", + "Only show suggestions without brackets", + "Where brackets are suggested, don't suggest without brackets" + ] + }, + "somesass.scss.completion.css": { + "order": 33, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Compatibility setting for VS Code. Enable or disable CSS completions (IntelliSense). The built-in SCSS language server provides this, so by default it's turned off in Some Sass." + }, + "somesass.scss.completion.triggerPropertyValueCompletion": { + "order": 33, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "By default, Some Sass triggers property value completion after selecting a CSS property. Use this setting to disable this behavior." + }, + "somesass.scss.completion.completePropertyWithSemicolon": { + "order": 34, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Insert semicolon at end of line when completing CSS properties." + }, + "somesass.scss.completion.includeFromCurrentDocument": { + "order": 35, + "type": "boolean", + "default": false, + "description": "Compatibility setting for VS Code. By default the built-in SCSS server shows suggestions for variables, mixins and functions declared in the current document. To avoid duplicates Some Sass will not suggest them unless you opt in." + }, + "somesass.scss.definition.enabled": { + "order": 40, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable Go to Definition." + }, + "somesass.scss.diagnostics.enabled": { + "order": 50, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable all diagnostics (deprecation, errors and lint rules)." + }, + "somesass.scss.diagnostics.deprecation.enabled": { + "order": 51, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable deprecation diagnostics (strike-through)." + }, + "somesass.scss.diagnostics.lint.enabled": { + "order": 52, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable all linting." + }, + "somesass.scss.diagnostics.lint.compatibleVendorPrefixes": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties." + }, + "somesass.scss.diagnostics.lint.vendorPrefix": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "When using a vendor-specific prefix, also include the standard property." + }, + "somesass.scss.diagnostics.lint.duplicateProperties": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Do not use duplicate style definitions." + }, + "somesass.scss.diagnostics.lint.emptyRules": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Do not use empty rulesets." + }, + "somesass.scss.diagnostics.lint.importStatement": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Import statements can lead to sequential loading of CSS." + }, + "somesass.scss.diagnostics.lint.boxModel": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "markdownDescription": "Do not use `width` or `height` when using `padding` or `border`." + }, + "somesass.scss.diagnostics.lint.universalSelector": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "markdownDescription": "The universal selector (`*`) is known to be slow." + }, + "somesass.scss.diagnostics.lint.zeroUnits": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "No unit needed for zero." + }, + "somesass.scss.diagnostics.lint.fontFaceProperties": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "markdownDescription": "`@font-face` rule must define `src` and `font-family` properties." + }, + "somesass.scss.diagnostics.lint.hexColorLength": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "Hex colors must consist of 3, 4, 6 or 8 hex numbers." + }, + "somesass.scss.diagnostics.lint.argumentsInColorFunction": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "Invalid number of parameters." + }, + "somesass.scss.diagnostics.lint.unknownProperties": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Unknown vendor specific property." + }, + "somesass.scss.diagnostics.lint.validProperties": { + "order": 53, + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + }, + "scope": "resource", + "default": [], + "description": "A list of properties that are not validated against the `unknownProperties` rule." + }, + "somesass.scss.diagnostics.lint.ieHack": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "IE hacks are only necessary when supporting IE7 and older." + }, + "somesass.scss.diagnostics.lint.unknownVendorSpecificProperties": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Unknown vendor specific property." + }, + "somesass.scss.diagnostics.lint.propertyIgnoredDueToDisplay": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "markdownDescription": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect." + }, + "somesass.scss.diagnostics.lint.important": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "markdownDescription": "Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored." + }, + "somesass.scss.diagnostics.lint.float": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "markdownDescription": "Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes." + }, + "somesass.scss.diagnostics.lint.idSelector": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML." + }, + "somesass.scss.diagnostics.lint.unknownAtRules": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Unknown at-rule." + }, + "somesass.scss.foldingRanges.enabled": { + "order": 60, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable folding ranges." + }, + "somesass.scss.highlights.enabled": { + "order": 70, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable highlights." + }, + "somesass.scss.hover.enabled": { + "order": 80, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable all hover information." + }, + "somesass.scss.hover.documentation": { + "order": 81, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Show property and value documentation in CSS hovers." + }, + "somesass.scss.hover.references": { + "order": 82, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Show references to Sass documentation for Sass built-in modules and SassDoc for annotations." + }, + "somesass.scss.links.enabled": { + "order": 90, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable the link provider that lets you click an import and open the file." + }, + "somesass.scss.references.enabled": { + "order": 100, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable Find all references." + }, + "somesass.scss.rename.enabled": { + "order": 110, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable Rename." + }, + "somesass.scss.selectionRanges.enabled": { + "order": 120, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable selection ranges." + }, + "somesass.scss.signatureHelp.enabled": { + "order": 130, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable signature help." + }, + "somesass.scss.workspaceSymbol.enabled": { + "order": 140, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable workspace symbols." + } + } + }, + { + "title": "Sass (Indented)", + "properties": { + "somesass.sass.codeAction.enabled": { + "order": 10, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable all code actions." + }, + "somesass.sass.colors.enabled": { + "order": 20, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable all color decorators." + }, + "somesass.sass.completion.enabled": { + "order": 30, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable all completions (IntelliSense)." + }, + "somesass.sass.completion.suggestFromUseOnly": { + "order": 31, + "type": "boolean", + "default": false, + "description": "If your project uses the new module system with @use and @forward, you may want to only include suggestions from your used modules." + }, + "somesass.sass.completion.mixinStyle": { + "order": 32, + "type": "string", + "default": "all", + "description": "Controls the style of suggestions for mixins and placeholders.", + "enum": [ + "all", + "nobracket", + "bracket" + ], + "enumItemLabels": [ + "All", + "No brackets", + "Only brackets" + ], + "enumDescriptions": [ + "Show all suggestions", + "Only show suggestions without brackets", + "Where brackets are suggested, omit duplicates without brackets" + ] + }, + "somesass.sass.completion.css": { + "order": 33, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable CSS completions (IntelliSense)." + }, + "somesass.sass.completion.triggerPropertyValueCompletion": { + "order": 33, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "By default, Some Sass triggers property value completion after selecting a CSS property. Use this setting to disable this behavior." + }, + "somesass.sass.definition.enabled": { + "order": 40, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable Go to Definition." + }, + "somesass.sass.diagnostics.enabled": { + "order": 50, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable all diagnostics (deprecation, errors and lint rules)." + }, + "somesass.sass.diagnostics.deprecation.enabled": { + "order": 51, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable deprecation diagnostics (strike-through)." + }, + "somesass.sass.diagnostics.lint.enabled": { + "order": 52, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable all linting." + }, + "somesass.sass.diagnostics.lint.compatibleVendorPrefixes": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties." + }, + "somesass.sass.diagnostics.lint.vendorPrefix": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "When using a vendor-specific prefix, also include the standard property." + }, + "somesass.sass.diagnostics.lint.duplicateProperties": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Do not use duplicate style definitions." + }, + "somesass.sass.diagnostics.lint.emptyRules": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Do not use empty rulesets." + }, + "somesass.sass.diagnostics.lint.importStatement": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Import statements can lead to sequential loading of CSS." + }, + "somesass.sass.diagnostics.lint.boxModel": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "markdownDescription": "Do not use `width` or `height` when using `padding` or `border`." + }, + "somesass.sass.diagnostics.lint.universalSelector": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "markdownDescription": "The universal selector (`*`) is known to be slow." + }, + "somesass.sass.diagnostics.lint.zeroUnits": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "No unit needed for zero." + }, + "somesass.sass.diagnostics.lint.fontFaceProperties": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "markdownDescription": "`@font-face` rule must define `src` and `font-family` properties." + }, + "somesass.sass.diagnostics.lint.hexColorLength": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "Hex colors must consist of 3, 4, 6 or 8 hex numbers." + }, + "somesass.sass.diagnostics.lint.argumentsInColorFunction": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "Invalid number of parameters." + }, + "somesass.sass.diagnostics.lint.unknownProperties": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Unknown vendor specific property." + }, + "somesass.sass.diagnostics.lint.validProperties": { + "order": 53, + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + }, + "scope": "resource", + "default": [], + "description": "A list of properties that are not validated against the `unknownProperties` rule." + }, + "somesass.sass.diagnostics.lint.ieHack": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "IE hacks are only necessary when supporting IE7 and older." + }, + "somesass.sass.diagnostics.lint.unknownVendorSpecificProperties": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Unknown vendor specific property." + }, + "somesass.sass.diagnostics.lint.propertyIgnoredDueToDisplay": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "markdownDescription": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect." + }, + "somesass.sass.diagnostics.lint.important": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "markdownDescription": "Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored." + }, + "somesass.sass.diagnostics.lint.float": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "markdownDescription": "Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes." + }, + "somesass.sass.diagnostics.lint.idSelector": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML." + }, + "somesass.sass.diagnostics.lint.unknownAtRules": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Unknown at-rule." + }, + "somesass.sass.foldingRanges.enabled": { + "order": 60, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable folding ranges." + }, + "somesass.sass.highlights.enabled": { + "order": 70, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable highlights." + }, + "somesass.sass.hover.enabled": { + "order": 80, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable all hover information." + }, + "somesass.sass.hover.documentation": { + "order": 81, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Show property and value documentation in CSS hovers." + }, + "somesass.sass.hover.references": { + "order": 82, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Show references to MDN in CSS hovers, Sass documentation for Sass built-in modules and SassDoc for annotations." + }, + "somesass.sass.links.enabled": { + "order": 90, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable the link provider that lets you click an import and open the file." + }, + "somesass.sass.references.enabled": { + "order": 100, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable Find all references." + }, + "somesass.sass.rename.enabled": { + "order": 110, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable Rename." + }, + "somesass.sass.selectionRanges.enabled": { + "order": 120, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable selection ranges." + }, + "somesass.sass.signatureHelp.enabled": { + "order": 130, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable selection ranges." + }, + "somesass.sass.workspaceSymbol.enabled": { + "order": 140, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable selection ranges." + } + } + }, + { + "title": "CSS", + "properties": { + "somesass.css.customData": { + "order": 999, + "type": "array", + "markdownDescription": "A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-css-languageservice/blob/master/docs/customData.md).\n\nSome Sass loads custom data on startup to enhance its CSS support for CSS custom properties (variables), at-rules, pseudo-classes, and pseudo-elements you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.", + "default": [], + "items": { + "type": "string" + }, + "scope": "resource" + }, + "somesass.css.codeAction.enabled": { + "order": 10, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable all code actions." + }, + "somesass.css.colors.enabled": { + "order": 20, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable all color decorators." + }, + "somesass.css.completion.enabled": { + "order": 30, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable all completions (IntelliSense)." + }, + "somesass.css.completion.triggerPropertyValueCompletion": { + "order": 31, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "By default, Some Sass triggers property value completion after selecting a CSS property. Use this setting to disable this behavior." + }, + "somesass.css.completion.completePropertyWithSemicolon": { + "order": 32, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Insert semicolon at end of line when completing CSS properties." + }, + "somesass.css.definition.enabled": { + "order": 40, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable Go to Definition." + }, + "somesass.css.diagnostics.enabled": { + "order": 50, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable all diagnostics (deprecation, errors and lint rules)." + }, + "somesass.css.diagnostics.deprecation.enabled": { + "order": 51, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable deprecation diagnostics (strike-through)." + }, + "somesass.css.diagnostics.lint.enabled": { + "order": 52, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Enable or disable all linting." + }, + "somesass.css.diagnostics.lint.compatibleVendorPrefixes": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties." + }, + "somesass.css.diagnostics.lint.vendorPrefix": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "When using a vendor-specific prefix, also include the standard property." + }, + "somesass.css.diagnostics.lint.duplicateProperties": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Do not use duplicate style definitions." + }, + "somesass.css.diagnostics.lint.emptyRules": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Do not use empty rulesets." + }, + "somesass.css.diagnostics.lint.importStatement": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Import statements can lead to sequential loading of CSS." + }, + "somesass.css.diagnostics.lint.boxModel": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "markdownDescription": "Do not use `width` or `height` when using `padding` or `border`." + }, + "somesass.css.diagnostics.lint.universalSelector": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "markdownDescription": "The universal selector (`*`) is known to be slow." + }, + "somesass.css.diagnostics.lint.zeroUnits": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "No unit needed for zero." + }, + "somesass.css.diagnostics.lint.fontFaceProperties": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "markdownDescription": "`@font-face` rule must define `src` and `font-family` properties." + }, + "somesass.css.diagnostics.lint.hexColorLength": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "Hex colors must consist of 3, 4, 6 or 8 hex numbers." + }, + "somesass.css.diagnostics.lint.argumentsInColorFunction": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "error", + "description": "Invalid number of parameters." + }, + "somesass.css.diagnostics.lint.unknownProperties": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Unknown vendor specific property." + }, + "somesass.css.diagnostics.lint.validProperties": { + "order": 53, + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + }, + "scope": "resource", + "default": [], + "description": "A list of properties that are not validated against the `unknownProperties` rule." + }, + "somesass.css.diagnostics.lint.ieHack": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "IE hacks are only necessary when supporting IE7 and older." + }, + "somesass.css.diagnostics.lint.unknownVendorSpecificProperties": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Unknown vendor specific property." + }, + "somesass.css.diagnostics.lint.propertyIgnoredDueToDisplay": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "markdownDescription": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect." + }, + "somesass.css.diagnostics.lint.important": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "markdownDescription": "Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored." + }, + "somesass.css.diagnostics.lint.float": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "markdownDescription": "Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes." + }, + "somesass.css.diagnostics.lint.idSelector": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "ignore", + "description": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML." + }, + "somesass.css.diagnostics.lint.unknownAtRules": { + "order": 53, + "type": "string", + "scope": "resource", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "description": "Unknown at-rule." + }, + "somesass.css.foldingRanges.enabled": { + "order": 60, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable folding ranges." + }, + "somesass.css.highlights.enabled": { + "order": 70, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable highlights." + }, + "somesass.css.hover.enabled": { + "order": 80, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable all hover information." + }, + "somesass.css.hover.documentation": { + "order": 81, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Show property and value documentation in CSS hovers." + }, + "somesass.css.hover.references": { + "order": 82, + "type": "boolean", + "scope": "resource", + "default": true, + "description": "Show references to MDN in CSS hovers." + }, + "somesass.css.links.enabled": { + "order": 90, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable the link provider that lets you click an import and open the file." + }, + "somesass.css.references.enabled": { + "order": 100, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable Find all references." + }, + "somesass.css.rename.enabled": { + "order": 110, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable Rename." + }, + "somesass.css.selectionRanges.enabled": { + "order": 120, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable selection ranges." + }, + "somesass.css.signatureHelp.enabled": { + "order": 130, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable selection ranges." + }, + "somesass.css.workspaceSymbol.enabled": { + "order": 140, + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable or disable selection ranges." + } + } + } + ], + "jsonValidation": [ + { + "fileMatch": "*.css-data.json", + "url": "https://raw.githubusercontent.com/microsoft/vscode-css-languageservice/master/docs/customData.schema.json" + }, + { + "fileMatch": "package.json", + "url": "./schemas/package.schema.json" + } + ] + }, + "dependencies": { + "fast-glob": "3.3.2", + "some-sass-language-server": "1.8.3", + "vscode-css-languageservice": "6.3.1", + "vscode-languageclient": "9.0.1", + "vscode-uri": "3.0.8" + }, + "devDependencies": { + "@types/mocha": "10.0.7", + "@types/vscode": "1.86.0", + "@vscode/test-electron": "2.4.1", + "@vscode/test-web": "0.0.60", + "assert": "2.1.0", + "mocha": "10.7.3", + "shx": "0.3.4" + }, + "scripts": { + "vscode:prepublish": "run-s clean build:production:*", + "clean": "shx rm -rf dist", + "build": "run-s build:development:*", + "build:node": "rspack --config ./rspack.node.config.js", + "build:browser": "rspack --config ./rspack.browser.config.js", + "build:development:node": "npm run build:node -- --mode=development", + "build:development:browser": "npm run build:browser -- --mode=development", + "build:production:node": "npm run build:node -- --mode=production", + "build:production:browser": "npm run build:browser -- --mode=production", + "doctor:node": "RSDOCTOR=true run-s build:production:node", + "doctor:browser": "RSDOCTOR=true run-s build:production:browser", + "start:web": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=.", + "lint": "eslint \"**/*.ts\" --cache", + "pretest:e2e": "run-s clean build:production:*", + "test:e2e": "node ./test/e2e/runTest.js", + "pretest:web": "rspack --config ./rspack.test-web.config.js ", + "test:web": "node ./test/web/runTest.js" + }, + "__metadata": { + "id": "6d35099c-3671-464c-ac0b-34a0c3823927", + "publisherDisplayName": "Somewhat Stationery", + "publisherId": "02638283-c13a-4acf-9f26-24bdcfdfce24", + "isPreReleaseVersion": false + } +} diff --git a/vscode-extension/rspack.browser.config.js b/vscode-extension/rspack.browser.config.js index 1e780af6..8e5f7ed1 100644 --- a/vscode-extension/rspack.browser.config.js +++ b/vscode-extension/rspack.browser.config.js @@ -1,5 +1,4 @@ -// @ts-check -/* eslint-disable @typescript-eslint/no-var-requires */ +/* eslint-disable */ const path = require("path"); const rspack = require("@rspack/core"); @@ -13,10 +12,9 @@ const config = { "browser-client": "./src/browser-client.ts", }, output: { - libraryTarget: "commonjs", - - path: path.join(__dirname, "./dist"), filename: "[name].js", + path: path.join(__dirname, "./dist"), + libraryTarget: "commonjs", }, externals: { vscode: "commonjs vscode", @@ -25,8 +23,8 @@ const config = { devtool: false, resolve: { extensions: [".ts", ".js"], - - mainFields: ["browser", "module", "main"], // prefer `browser` entry point in imported modules + mainFields: ["browser", "module", "main"], + conditionNames: ["import", "require", "default"], fallback: { events: require.resolve("events/"), assert: require.resolve("assert"), @@ -56,28 +54,27 @@ const config = { }, ], }, -}; - -module.exports = (env, argv) => { - const baseFrom = - argv.mode === "development" - ? "../node_modules/some-sass-language-server/dist/development" - : "../node_modules/some-sass-language-server/dist/"; - - config.plugins?.push( + plugins: [ new rspack.ProvidePlugin({ process: "process/browser", }), + new rspack.optimize.LimitChunkCountPlugin({ + maxChunks: 1, + }), new rspack.CopyRspackPlugin({ patterns: [ { - from: `${baseFrom}/browser-server.*`, + from: "../node_modules/some-sass-language-server/dist/**/*.js", to: "[name][ext]", }, ], }), - ); + // Only register the plugin when RSDOCTOR is true, as the plugin will increase the build time. + process.env.RSDOCTOR && new RsdoctorRspackPlugin(), + ].filter(Boolean), +}; +module.exports = (env, argv) => { if (argv.mode === "development") { config.devtool = "source-map"; } diff --git a/vscode-extension/rspack.node.config.js b/vscode-extension/rspack.node.config.js index e310cb13..f2cf442d 100644 --- a/vscode-extension/rspack.node.config.js +++ b/vscode-extension/rspack.node.config.js @@ -1,8 +1,8 @@ -// @ts-check -/* eslint-disable @typescript-eslint/no-var-requires */ +/* eslint-disable */ const path = require("path"); const rspack = require("@rspack/core"); +const { RsdoctorRspackPlugin } = require("@rsdoctor/rspack-plugin"); /** @type {import('@rspack/core').Configuration} **/ const config = { @@ -22,8 +22,21 @@ const config = { }, resolve: { extensions: [".ts", ".js"], + conditionNames: ["import", "require", "default"], + mainFields: ["module", "main"], }, - plugins: [], + plugins: [ + new rspack.CopyRspackPlugin({ + patterns: [ + { + from: "../node_modules/some-sass-language-server/dist/**/*.js", + to: "[name][ext]", + }, + ], + }), + // Only register the plugin when RSDOCTOR is true, as the plugin will increase the build time. + process.env.RSDOCTOR && new RsdoctorRspackPlugin(), + ].filter(Boolean), devtool: false, module: { rules: [ @@ -45,22 +58,6 @@ const config = { }; module.exports = (env, argv) => { - const baseFrom = - argv.mode === "development" - ? "../node_modules/some-sass-language-server/dist/development" - : "../node_modules/some-sass-language-server/dist/"; - - config.plugins?.push( - new rspack.CopyRspackPlugin({ - patterns: [ - { - from: `${baseFrom}/node-server.*`, - to: "[name][ext]", - }, - ], - }), - ); - if (argv.mode === "development") { config.devtool = "source-map"; } diff --git a/vscode-extension/rspack.test-web.config.js b/vscode-extension/rspack.test-web.config.js index a50d87f1..7f9ffcd4 100644 --- a/vscode-extension/rspack.test-web.config.js +++ b/vscode-extension/rspack.test-web.config.js @@ -1,5 +1,4 @@ -// @ts-check -/* eslint-disable @typescript-eslint/no-var-requires */ +/* eslint-disable */ const path = require("path"); const rspack = require("@rspack/core"); @@ -40,6 +39,9 @@ const browserTestsConfig = { }, ], }, + performance: { + hints: false, + }, plugins: [ new rspack.ProvidePlugin({ process: "process/browser", diff --git a/vscode-extension/schemas/package.schema.json b/vscode-extension/schemas/package.schema.json new file mode 100644 index 00000000..6c4b91fa --- /dev/null +++ b/vscode-extension/schemas/package.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "contributes": { + "type": "object", + "properties": { + "css.customData": { + "type": "array", + "markdownDescription": "A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-css-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its CSS support for the custom CSS properties, at directives, pseudo classes and pseudo elements you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.", + "items": { + "type": "string", + "description": "Relative path to a CSS custom data file" + } + } + } + } + } +} diff --git a/vscode-extension/src/browser-client.ts b/vscode-extension/src/browser-client.ts index 141c57d0..e3a68b19 100644 --- a/vscode-extension/src/browser-client.ts +++ b/vscode-extension/src/browser-client.ts @@ -145,10 +145,7 @@ function createWorkerLanguageClient( context: ExtensionContext, clientOptions: LanguageClientOptions, ) { - const serverMain = Uri.joinPath( - context.extensionUri, - "dist/browser-server.js", - ); + const serverMain = Uri.joinPath(context.extensionUri, "dist/browser-main.js"); const worker = new Worker(serverMain.toString(/* skipEncoding */ true)); return new LanguageClient( diff --git a/vscode-extension/src/client.ts b/vscode-extension/src/client.ts index f1add4ec..2135c066 100644 --- a/vscode-extension/src/client.ts +++ b/vscode-extension/src/client.ts @@ -37,6 +37,7 @@ export function createLanguageClientOptions( currentWorkspace?: WorkspaceFolder, ): LanguageClientOptions { let documentSelector: DocumentSelector = [ + { scheme: "untitled", language: "css" }, { scheme: "untitled", language: "scss" }, { scheme: "untitled", language: "sass" }, { scheme: "untitled", language: "vue" }, @@ -53,16 +54,19 @@ export function createLanguageClientOptions( const webPattern = `${currentWorkspace.uri.path}**`; documentSelector = [ + { scheme: "file", language: "css", pattern }, { scheme: "file", language: "scss", pattern }, { scheme: "file", language: "sass", pattern }, { scheme: "file", language: "vue", pattern }, { scheme: "file", language: "svelte", pattern }, { scheme: "file", language: "astro", pattern }, + { scheme: "vscode-vfs", language: "css", pattern }, { scheme: "vscode-vfs", language: "scss", pattern }, { scheme: "vscode-vfs", language: "sass", pattern }, { scheme: "vscode-vfs", language: "vue", pattern }, { scheme: "vscode-vfs", language: "svelte", pattern }, { scheme: "vscode-vfs", language: "astro", pattern }, + { scheme: "vscode-test-web", language: "css", pattern: webPattern }, { scheme: "vscode-test-web", language: "scss", pattern: webPattern }, { scheme: "vscode-test-web", language: "sass", pattern: webPattern }, { scheme: "vscode-test-web", language: "vue", pattern: webPattern }, @@ -79,7 +83,7 @@ export function createLanguageClientOptions( ? workspace.createFileSystemWatcher({ baseUri: currentWorkspace.uri, base: currentWorkspace.uri.fsPath, - pattern: "**/*.{scss,sass,vue,svelte,astro}", + pattern: "**/*.{css,scss,sass,vue,svelte,astro}", }) : undefined, }, diff --git a/vscode-extension/src/node-client.ts b/vscode-extension/src/node-client.ts index f5e02a08..7e007905 100644 --- a/vscode-extension/src/node-client.ts +++ b/vscode-extension/src/node-client.ts @@ -67,13 +67,14 @@ function getOuterMostWorkspaceFolder(folder: WorkspaceFolder): WorkspaceFolder { } export async function activate(context: ExtensionContext): Promise { - const serverModule = context.asAbsolutePath(`./dist/node-server.js`); + const serverModule = context.asAbsolutePath(`./dist/node-main.js`); async function didOpenTextDocument(document: TextDocument): Promise { if ( document.uri.scheme !== "file" && document.uri.scheme !== "untitled" && document.uri.scheme !== "vscode-vfs" && + document.languageId !== "css" && document.languageId !== "scss" && document.languageId !== "sass" && document.languageId !== "vue" && diff --git a/vscode-extension/test/e2e/defaults-sass/workspace/.vscode/settings.json b/vscode-extension/test/e2e/defaults-sass/workspace/.vscode/settings.json deleted file mode 100644 index f680ec48..00000000 --- a/vscode-extension/test/e2e/defaults-sass/workspace/.vscode/settings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "somesass.loadPaths": [], - "somesass.scannerDepth": 30, - "somesass.scannerExclude": [ - "**/.git/**", - "**/node_modules/**", - "**/bower_components/**" - ], - "somesass.scanImportedFiles": true, - "somesass.suggestAllFromOpenDocument": false, - "somesass.suggestFromUseOnly": false, - "somesass.suggestionStyle": "all", - "somesass.suggestFunctionsInStringContextAfterSymbols": " (+-*%", - "somesass.triggerPropertyValueCompletion": true, -} diff --git a/vscode-extension/test/e2e/defaults-scss/hover.test.js b/vscode-extension/test/e2e/defaults-scss/hover.test.js index 259d980c..bc05de6f 100644 --- a/vscode-extension/test/e2e/defaults-scss/hover.test.js +++ b/vscode-extension/test/e2e/defaults-scss/hover.test.js @@ -77,7 +77,7 @@ test("shows hover for SassDoc annotations", async () => { // Prefixed symbols are shown with their original names const expectedContents = { contents: [ - "@type\n____\n[SassDoc reference](http://sassdoc.com/annotations/#type)", + "@type\n\n[SassDoc reference](http://sassdoc.com/annotations/#type)", ], }; diff --git a/vscode-extension/test/e2e/defaults-scss/workspace/.vscode/settings.json b/vscode-extension/test/e2e/defaults-scss/workspace/.vscode/settings.json deleted file mode 100644 index f680ec48..00000000 --- a/vscode-extension/test/e2e/defaults-scss/workspace/.vscode/settings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "somesass.loadPaths": [], - "somesass.scannerDepth": 30, - "somesass.scannerExclude": [ - "**/.git/**", - "**/node_modules/**", - "**/bower_components/**" - ], - "somesass.scanImportedFiles": true, - "somesass.suggestAllFromOpenDocument": false, - "somesass.suggestFromUseOnly": false, - "somesass.suggestionStyle": "all", - "somesass.suggestFunctionsInStringContextAfterSymbols": " (+-*%", - "somesass.triggerPropertyValueCompletion": true, -} diff --git a/vscode-extension/test/e2e/suggest-from-use-only/workspace/.vscode/settings.json b/vscode-extension/test/e2e/suggest-from-use-only/workspace/.vscode/settings.json index e0ca07df..9fb70d6e 100644 --- a/vscode-extension/test/e2e/suggest-from-use-only/workspace/.vscode/settings.json +++ b/vscode-extension/test/e2e/suggest-from-use-only/workspace/.vscode/settings.json @@ -1,3 +1,4 @@ { - "somesass.suggestFromUseOnly": true + "somesass.scss.completion.suggestFromUseOnly": true, + "somesass.sass.completion.suggestFromUseOnly": true } diff --git a/vscode-extension/test/web/suite/hover.test.js b/vscode-extension/test/web/suite/hover.test.js index 84c4e819..ddba088f 100644 --- a/vscode-extension/test/web/suite/hover.test.js +++ b/vscode-extension/test/web/suite/hover.test.js @@ -75,7 +75,7 @@ test("for SassDoc annotations", async () => { // Prefixed symbols are shown with their original names const expectedContents = { contents: [ - "@type\n____\n[SassDoc reference](http://sassdoc.com/annotations/#type)", + "@type\n\n[SassDoc reference](http://sassdoc.com/annotations/#type)", ], };