Skip to content

Commit

Permalink
feat: add two settings to tweak completions in other editors (#224)
Browse files Browse the repository at this point in the history
Testing in Helix, which uses a different grammar for SCSS than Code,
revealed a few annoying nits with compleitons.

This version adds two new settings that can be useful for maintainers
of language clients and editor configurations:

- `somesass.completion.afterModule`
- `somesass.completion.beforeVariable`

`afterModule` lets you control whether or not to insert a `.` when
accepting a suggestion from modules. If you see for instance
`module..$variable` then try setting `afterModule` to an empty string.

`beforeVariable` is for non-module variables. If you see double dollars
(`$$variable`) then set `beforeVariable` to the empty string.
  • Loading branch information
wkillerud authored Sep 7, 2024
1 parent 396d342 commit db4cd19
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 25 deletions.
3 changes: 3 additions & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
- [Getting started](language-server/getting-started.md)
- [Configure a client](language-server/configure-a-client.md)
- [Existing clients](language-server/existing-clients.md)
- [Helix](language-server/helix.md)
- [Kate](language-server/kate.md)
- [Neovim](language-server/neovim.md)
<!-- Sort alphabetically. Add new pages to existing-clients.md as well. -->

# Contributing to Some Sass

Expand All @@ -27,6 +29,7 @@
- [Extensions for VS Code](contributing/extensions-for-vs-code.md)
- [Language Server Protocol](contributing/language-server-protocol.md)
- [Development environment](contributing/development-environment.md)
- [Testing in other editors](contributing/testing-other-editors.md)
- [Architecture](contributing/architecture.md)
- [Building](contributing/building.md)
- [Automated tests](contributing/automated-tests.md)
Expand Down
4 changes: 2 additions & 2 deletions docs/src/contributing/development-environment.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Development environment

The language server is written in TypeScript and runs both in Node and the browser. While the server can be used outside of Visual Studio Code, it's recommended to use VS Code for development.
The language server is written in TypeScript and runs both in Node and the browser. While the server can be used outside of Visual Studio Code, it's recommended to use VS Code or VSCodium for development.

You need:

- A current long-term support version of [Node.js](https://nodejs.org/en)
- [Visual Studio Code](https://code.visualstudio.com/)
- [Visual Studio Code](https://code.visualstudio.com/) or [VSCodium](https://vscodium.com)

Recommended extensions:

Expand Down
20 changes: 20 additions & 0 deletions docs/src/contributing/testing-other-editors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Testing in other editors

While it's recommended to use Visual Studio Code or VSCodium, you can use other editors to test and debug the language server.

In this document we'll look at how we can test our local development build in the Helix editor.

## Install the local version globally

To make the local version of `some-sass-language-server` available on `PATH`, go to its directory and run `npm install --global .`.

```sh
cd packages/language-server/
npm install --global .
```

Editors using Some Sass from `PATH` now use your local build.

## Check the logs

In Helix, run the `hx` command with the `-v` verbose flag. Do your test, and then run the `:log-open` command to see the traffic between server and client.
27 changes: 26 additions & 1 deletion docs/src/language-server/configure-a-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,32 @@ To configure a client for an editor that doesn't have one yet, check the documen

## Settings

The language server requests [settings](../user-guide/settings.md) 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 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.

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.

```json
{
"settings": {
"somesass": {
"loadPaths": []
}
}
}
```

### Server-only settings

In addition to [the user settings](../user-guide/settings.md), language clients may want to configure these server-only settings to tweak how certain features interact with your specific editor and its grammar for Sass.

| Key | Description |
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| `somesass.completion.afterModule` | Set this to the empty string if you end up with `module..$variable` after accepting a code suggestion item. |
| `somesass.completion.beforeVariable` | Set this to the empty string if you end up with `$$variable` after accepting a code suggestion item. |

## Existing clients

Expand Down
3 changes: 2 additions & 1 deletion docs/src/language-server/existing-clients.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

Editors with ready-configured clients, maintained by the community.

<!-- Sort alphabetically. Add new pages to SUMMARY.md as well. -->
- [Helix](./helix.md)
- [Kate](./kate.md)
- [Neovim](./neovim.md)

<!-- Sort alphabetically. Add new pages to SUMMARY.md as well. -->
8 changes: 2 additions & 6 deletions docs/src/language-server/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,9 @@ some-sass-language-server --stdio

`--debug` – runs the development build of the language server, helpful to get more context if the server crashes

### Settings
## Configure your editor's client

The language server requests [settings](../user-guide/settings.md) 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.

## Configure a client

The next step is to [configure a language client](./configure-a-client.md).
The next step is to [configure your editor's language client](./configure-a-client.md).

[lsp]: https://microsoft.github.io/language-server-protocol/
[npm]: https://www.npmjs.com/package/some-sass-language-server
26 changes: 26 additions & 0 deletions docs/src/language-server/helix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Helix

You can configure new language servers in [`.config/helix/languages.toml`](https://docs.helix-editor.com/guides/adding_languages.html).

[Install the language server if you haven't already](./getting-started.md), then add this config you `languages.toml`.

```toml
[language-server.some-sass-language-server]
command = "some-sass-language-server"
args = ["--stdio"]
config = { somesass = { completion = { afterModule = "", beforeVariable = "" } } }

[[language]]
name = "scss"
scope = "source.scss"
injection-regex = "scss"
file-types = ["scss"]
block-comment-tokens = { start = "/*", end = "*/" }
language-servers = [ "some-sass-language-server" ]
auto-format = true
indent = { tab-width = 2, unit = " " }
```

The language server will start once you open an SCSS file.

At time of writing there doesn't seem to be a grammar for Sass indented available in Helix.
4 changes: 4 additions & 0 deletions packages/language-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ export class SomeSassServer {
suggestionStyle: settings.suggestionStyle,
suggestFunctionsInStringContextAfterSymbols:
settings.suggestFunctionsInStringContextAfterSymbols,
afterModule: settings.completion?.afterModule,
beforeVariable: settings.completion?.beforeVariable,
},
});

Expand Down Expand Up @@ -282,6 +284,8 @@ export class SomeSassServer {
suggestionStyle: settings.suggestionStyle,
suggestFunctionsInStringContextAfterSymbols:
settings.suggestFunctionsInStringContextAfterSymbols,
afterModule: settings.completion?.afterModule,
beforeVariable: settings.completion?.beforeVariable,
},
});
});
Expand Down
4 changes: 4 additions & 0 deletions packages/language-server/src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export interface ISettings {
readonly suggestFromUseOnly: boolean;
readonly suggestFunctionsInStringContextAfterSymbols: " (+-*%";
readonly triggerPropertyValueCompletion: boolean;
readonly completion?: {
afterModule?: string;
beforeVariable?: string;
};
}

export interface IEditorSettings {
Expand Down
57 changes: 42 additions & 15 deletions packages/language-services/src/features/do-complete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -848,14 +848,26 @@ export class DoComplete extends LanguageFeature {
let filterText: string | undefined;

if (namespace && namespace !== "*") {
const noDot =
isEmbedded ||
dotExt === ".sass" ||
this.configuration.completionSettings?.afterModule === "";

const noDollar = isEmbedded;

insertText = currentWord.endsWith(".")
? `${isEmbedded || dotExt === ".sass" ? "" : "."}${label}`
: isEmbedded
? `${noDot ? "" : "."}${label}`
: noDollar
? asDollarlessVariable(label)
: label;

filterText = currentWord.endsWith(".") ? `${namespace}.${label}` : label;
} else if (dotExt === ".vue" || dotExt === ".astro" || dotExt === ".sass") {
} else if (
dotExt === ".vue" ||
dotExt === ".astro" ||
dotExt === ".sass" ||
this.configuration.completionSettings?.beforeVariable === ""
) {
// In these languages the $ does not get replaced by the suggestion,
// so exclude it from the insertText.
insertText = asDollarlessVariable(label);
Expand Down Expand Up @@ -907,12 +919,17 @@ export class DoComplete extends LanguageFeature {
: symbol.name;

const isEmbedded = this.isEmbedded(initialDocument);
const includeDot =
namespace !== "*" && !isEmbedded && initialDocument.languageId !== "sass";

const noDot =
namespace === "*" ||
isEmbedded ||
initialDocument.languageId === ".sass" ||
this.configuration.completionSettings?.afterModule === "";

const insertText = namespace
? includeDot
? `.${prefix}${symbol.name}`
: `${prefix}${symbol.name}`
? noDot
? `${prefix}${symbol.name}`
: `.${prefix}${symbol.name}`
: symbol.name;

const sortText = isPrivate ? label.replace(/^$[_]/, "") : undefined;
Expand Down Expand Up @@ -1035,12 +1052,17 @@ export class DoComplete extends LanguageFeature {
}

const isEmbedded = this.isEmbedded(initialDocument);
const includeDot =
namespace !== "*" && !isEmbedded && initialDocument.languageId !== "sass";

const noDot =
namespace === "*" ||
isEmbedded ||
initialDocument.languageId === ".sass" ||
this.configuration.completionSettings?.afterModule === "";

const insertText = namespace
? includeDot
? `.${prefix}${symbol.name}`
: `${prefix}${symbol.name}`
? noDot
? `${prefix}${symbol.name}`
: `.${prefix}${symbol.name}`
: symbol.name;

const sortText = isPrivate ? label.replace(/^$[_]/, "") : undefined;
Expand Down Expand Up @@ -1130,9 +1152,14 @@ export class DoComplete extends LanguageFeature {
// be replaced (except when we're embedded in Vue, Svelte or Astro).
// Example result: .floor(${1:number})
const isEmbedded = this.isEmbedded(document);
const includeDot = !isEmbedded && document.languageId !== "sass";

const noDot =
isEmbedded ||
document.languageId === ".sass" ||
this.configuration.completionSettings?.afterModule === "";

const insertText = context.currentWord.includes(".")
? `${includeDot ? "." : ""}${label}${
? `${noDot ? "" : "."}${label}${
signature ? `(${parameterSnippet})` : ""
}`
: label;
Expand Down
2 changes: 2 additions & 0 deletions packages/language-services/src/language-feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const defaultConfiguration: LanguageServiceConfiguration = {
suggestFunctionsInStringContextAfterSymbols: " (+-*%",
suggestionStyle: "all",
triggerPropertyValueCompletion: true,
afterModule: ".",
beforeVariable: "$",
},
};

Expand Down
27 changes: 27 additions & 0 deletions packages/language-services/src/language-services-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,33 @@ export interface LanguageServiceConfiguration {
* @default true
*/
triggerPropertyValueCompletion?: boolean;
includePrefixDot?: boolean;
/**
* If you end up with an extra `.` after accepting a suggestion, set this to the empty string.
*
* @example
* ```scss
* .foo {
* // set this setting to the empty string "" to fix this bug,
* // which varies depending on your editor's grammar for Sass.
* color: module..$variable;
* }
* ```
*/
afterModule?: string;
/**
* If you end up with an extra `&` after accepting a suggestion, set this to the empty string.
*
* @example
* ```scss
* .foo {
* // set this setting to the empty string "" to fix this bug,
* // which varies depending on your editor's grammar for Sass.
* color: $$variable;
* }
* ```
*/
beforeVariable?: string;
};
editorSettings?: EditorSettings;
workspaceRoot?: URI;
Expand Down

0 comments on commit db4cd19

Please sign in to comment.