Skip to content

Commit 6c8cb3f

Browse files
authored
Merge pull request #33 from sass/hover
Hover
2 parents ae6b6b5 + c9d9686 commit 6c8cb3f

24 files changed

+1748
-493
lines changed

.vscode/launch.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
"outFiles": ["${workspaceRoot}/extension/dist/main.js"],
1212
"autoAttachChildProcesses": true,
1313
"preLaunchTask": "npm: build - extension"
14-
}
14+
},
15+
{
16+
"name": "Attach to language server",
17+
"request": "attach",
18+
"type": "dart",
19+
"cwd": "pkgs/sass_language_server",
20+
"vmServiceUri": "${command:dart.promptForVmService}" // Prompt for the VM Service URI
21+
},
1522
]
1623
}

docs/contributing/testing-and-debugging.md

+42-17
Original file line numberDiff line numberDiff line change
@@ -13,42 +13,65 @@ The quickest way to test the language server is to debug the language extension
1313

1414
This will open another window of Visual Studio Code, this one running as an `[Extension Development Host]`.
1515

16-
### Testing in isolation
16+
### Find the link to Dart DevTools or VM service
1717

18-
VS Code ships with some built-in support for SCSS and CSS. To test this language server in isolation you can disable the built-in extension.
18+
When debugging, the client runs [`dart run --enable-vm-service`](https://github.com/sass/dart-sass-language-server/blob/main/extension/src/server.ts#L49)
19+
in the local `sass_language_server` package.
1920

20-
1. Go to the Extensions tab and search for `@builtin css language features`.
21-
2. Click the settings icon and pick Disable from the list.
22-
3. Click Restart extension to turn it off.
21+
Use the `[Extension Development Host]` window to find the link to open Dart DevTools or to [attach the debugger](#attach-to-language-server).
2322

24-
You should also turn off extensions like SCSS IntelliSense or Some Sass.
23+
1. Open a CSS, SCSS or Sass file to activate the language server.
24+
2. Open the Output pane (View -> Output in the menu).
25+
3. Choose Sass in the dropdown to the top right of the Output pane.
26+
4. Scroll to the top of the output.
2527

26-
### Open the Dart DevTools
28+
You should see something similar to this.
2729

28-
In this configuration, the client has run `dart run --observe` in the local `sass_language_server` package. You can now use [Dart DevTools](https://dart.dev/tools/dart-devtools) to debug the language server.
30+
```
31+
The Dart VM service is listening on http://127.0.0.1:8181/SMIxtkPzlAY=/
32+
The Dart DevTools debugger and profiler is available at: http://127.0.0.1:8181/SMIxtkPzlAY=/devtools/?uri=ws://127.0.0.1:8181/SMIxtkPzlAY=/ws
33+
```
2934

30-
To find the link to open Dart DevTools, use the `[Extension Development Host]`.
35+
Click the second link to open Dart DevTools, or copy the first link to [attach a debugger](#attach-to-language-server).
3136

32-
1. Open a CSS, SCSS or Sass file to activate the language server.
33-
2. Open the Output pane (View -> Output in the menu).
34-
3. In the dropdown in the top right, choose Sass from the list.
37+
![screenshot showing the output pane and the dropdown with sass selected](https://github.com/user-attachments/assets/85839d2f-4305-4fb9-aeb0-d78f435e8b7d)
38+
39+
### Attach to language server
3540

36-
You should see output similar to this.
41+
The debugger in Dart DevTools is deprecated in favor the debugger that ships with [Dart for Visual Studio Code][vscodedart].
42+
43+
To start debugging in VS Code (provided you have the Dart extension):
44+
45+
1. [Run the language server and extension](#run-the-language-extension-and-server) in debug mode.
46+
2. [Find the link to the Dart VM](#find-the-link-to-dart-devtools-or-vm-service).
47+
48+
You should see output similar to this in the `[Extension Development Host]`.
3749

3850
```
3951
The Dart VM service is listening on http://127.0.0.1:8181/SMIxtkPzlAY=/
4052
The Dart DevTools debugger and profiler is available at: http://127.0.0.1:8181/SMIxtkPzlAY=/devtools/?uri=ws://127.0.0.1:8181/SMIxtkPzlAY=/ws
4153
```
4254

43-
![screenshot showing the output pane and the dropdown with sass selected](https://github.com/user-attachments/assets/85839d2f-4305-4fb9-aeb0-d78f435e8b7d)
55+
Copy the first link, then go back to the Run and debug window where you started the language server and extension.
56+
57+
1. Click the Run and debug drop-down and run `Attach to language server`.
58+
2. Paste the link you copied and hit Enter.
59+
60+
Your debugger should be attached, allowing you to place breakpoints and step through code.
61+
62+
### Test in VS Code without built-in SCSS features
4463

45-
Click the second link to open Dart DevTools.
64+
VS Code ships with some built-in support for SCSS and CSS. To test this language server in isolation you can disable the built-in extension.
4665

47-
The Debugger tab has a File explorer in which you can find `package:sass_language_server`. Go to `src/language_server.dart` to find the request handlers for messages coming in from the client.
66+
1. Go to the Extensions tab and search for `@builtin css language features`.
67+
2. Click the settings icon and pick Disable from the list.
68+
3. Click Restart extension to turn it off.
69+
70+
You should also turn off extensions like SCSS IntelliSense or Some Sass.
4871

4972
## Debug unit tests
5073

51-
Assuming you installed [Dart for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code) you can debug individual unit tests by right-clicking the Run button in the editor gutter.
74+
Assuming you installed [Dart for Visual Studio Code][vscodedart] you can debug individual unit tests by right-clicking the Run button in the editor gutter.
5275

5376
Writing a test is often faster when debugging an issue with a specific language feature, and helps improve test coverage.
5477

@@ -71,3 +94,5 @@ test profile in the Run and Debug view in VS Code.
7194
]
7295
}
7396
```
97+
98+
[vscodedart]: https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code

extension/src/main.ts

+1-53
Original file line numberDiff line numberDiff line change
@@ -132,61 +132,9 @@ export async function activate(context: ExtensionContext): Promise<void> {
132132
}
133133
}
134134
});
135-
136-
// TODO: Maybe worth looking into so links to built-ins resolve to something?
137-
// workspace.registerFileSystemProvider(
138-
// 'sass',
139-
// {
140-
// readFile(uri) {
141-
// return Uint8Array.from(
142-
// '@function hello();'.split('').map((c) => c.charCodeAt(0))
143-
// );
144-
// },
145-
// watch(uri, options) {
146-
// return Disposable.create(() => {
147-
// console.log('hello');
148-
// });
149-
// },
150-
// readDirectory(uri) {
151-
// return [];
152-
// },
153-
// stat(uri) {
154-
// return {
155-
// ctime: 0,
156-
// mtime: 0,
157-
// size: 0,
158-
// type: 1,
159-
// };
160-
// },
161-
// writeFile(uri, content, options) {
162-
// return;
163-
// },
164-
// createDirectory(uri) {
165-
// return;
166-
// },
167-
// delete(uri, options) {
168-
// return;
169-
// },
170-
// rename(oldUri, newUri, options) {
171-
// return;
172-
// },
173-
// copy(source, destination, options) {
174-
// return;
175-
// },
176-
// onDidChangeFile(e) {
177-
// return Disposable.create(() => {
178-
// console.log('hello');
179-
// });
180-
// },
181-
// },
182-
// {
183-
// isCaseSensitive: false,
184-
// isReadonly: true,
185-
// }
186-
// );
187135
}
188136

189-
export async function deactivate(): Promise<void> {
137+
export function deactivate(): Promise<void> {
190138
const promises: Thenable<void>[] = [];
191139
if (defaultClient) {
192140
promises.push(defaultClient.stop());

extension/src/server.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export async function createServerOptions(
4646
args: [
4747
'run',
4848
// '--pause-isolates-on-start', // Uncomment this to debug issues during startup and initial scan
49-
'--observe',
49+
'--enable-vm-service',
5050
'sass_language_server',
5151
'--loglevel=debug',
5252
],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/// Docstring.
2+
/// @type String
3+
$from-other: 'hello';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@use "sass:string"
2+
@use "other"
3+
4+
$_id: string.unique-id()
5+
$_prefix: other.$from-other
6+
7+
.card
8+
.body:has(:not(.stuff))
9+
padding: 4px
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
const assert = require('node:assert');
2+
const path = require('node:path');
3+
const vscode = require('vscode');
4+
const { showFile, sleepCI } = require('../util');
5+
6+
const stylesUri = vscode.Uri.file(
7+
path.resolve(__dirname, 'fixtures', 'styles.sass')
8+
);
9+
10+
before(async () => {
11+
await showFile(stylesUri);
12+
await sleepCI();
13+
});
14+
15+
after(async () => {
16+
await vscode.commands.executeCommand('workbench.action.closeAllEditors');
17+
});
18+
19+
/**
20+
* @param {import('vscode').Hover[]} hover
21+
* @returns {string}
22+
*/
23+
function getHoverContents(hover) {
24+
return hover
25+
.flatMap((item) => {
26+
return item.contents.map((content) =>
27+
typeof content === 'string' ? content : content.value
28+
);
29+
})
30+
.join('\n');
31+
}
32+
33+
/**
34+
* @param {import('vscode').Uri} documentUri
35+
* @param {import('vscode').Position} position
36+
* @returns {Promise<import('vscode').Hover[]>}
37+
*/
38+
async function hover(documentUri, position) {
39+
const result = await vscode.commands.executeCommand(
40+
'vscode.executeHoverProvider',
41+
documentUri,
42+
position
43+
);
44+
return result;
45+
}
46+
47+
test('gets hover information from the same document', async () => {
48+
const result = await hover(stylesUri, new vscode.Position(7, 10));
49+
50+
assert.match(
51+
getHoverContents(result),
52+
/\.card \.body:has\(:not\(\.stuff\)\)/
53+
);
54+
});
55+
56+
test('gets hover information from the workspace', async () => {
57+
const result = await hover(stylesUri, new vscode.Position(4, 19));
58+
59+
assert.match(getHoverContents(result), /Docstring/);
60+
});
61+
62+
test('gets hover information for Sass built-in', async () => {
63+
const result = await hover(stylesUri, new vscode.Position(3, 14));
64+
65+
assert.match(
66+
getHoverContents(result),
67+
/Returns a randomly-generated unquoted string/
68+
);
69+
});
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const path = require('node:path');
2+
const fs = require('node:fs/promises');
3+
const vscode = require('vscode');
4+
const { runMocha } = require('../mocha');
5+
6+
/**
7+
* @returns {Promise<void>}
8+
*/
9+
async function run() {
10+
const filePaths = [];
11+
12+
const dir = await fs.readdir(__dirname, { withFileTypes: true });
13+
for (let entry of dir) {
14+
if (entry.isFile() && entry.name.endsWith('test.js')) {
15+
filePaths.push(path.join(entry.parentPath, entry.name));
16+
}
17+
}
18+
19+
await runMocha(
20+
filePaths,
21+
vscode.Uri.file(path.resolve(__dirname, 'fixtures', 'styles.sass'))
22+
);
23+
}
24+
25+
module.exports = { run };

pkgs/sass_language_server/bin/sass_language_server.dart

+30-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import 'dart:io';
2+
3+
import 'package:lsp_server/lsp_server.dart';
14
import 'package:sass_language_server/sass_language_server.dart';
25

36
void main(List<String> arguments) async {
@@ -35,9 +38,10 @@ Logging options:
3538
var fileSystemProvider = LocalFileSystem();
3639
var server = LanguageServer();
3740

41+
Connection connection;
42+
Socket? socket;
3843
if (transport == '--stdio') {
39-
await server.start(
40-
logLevel: logLevel, fileSystemProvider: fileSystemProvider);
44+
connection = Connection(stdin, stdout);
4145
} else {
4246
// The client is the one listening to socket connections on the specified port.
4347
// In other words the language server is a _client_ for the socket transport.
@@ -46,11 +50,31 @@ Logging options:
4650
// the language server.
4751
var split = transport.split('=');
4852
int port = int.parse(split.last);
53+
socket = await Socket.connect('127.0.0.1', port);
54+
connection = Connection(socket, socket);
55+
}
56+
57+
try {
58+
exitCode = 1;
4959

5060
await server.start(
51-
logLevel: logLevel,
52-
fileSystemProvider: fileSystemProvider,
53-
transport: Transport.socket,
54-
port: port);
61+
connection: connection,
62+
logLevel: logLevel,
63+
fileSystemProvider: fileSystemProvider,
64+
);
65+
66+
// See
67+
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#shutdown
68+
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#exit
69+
connection.onShutdown(() async {
70+
socket?.close();
71+
exitCode = 0;
72+
});
73+
74+
connection.onExit(() async {
75+
exit(exitCode);
76+
});
77+
} on Exception catch (_) {
78+
exit(1);
5579
}
5680
}

0 commit comments

Comments
 (0)