Skip to content

Commit

Permalink
Merge pull request #33 from sass/hover
Browse files Browse the repository at this point in the history
Hover
  • Loading branch information
wkillerud authored Dec 16, 2024
2 parents ae6b6b5 + c9d9686 commit 6c8cb3f
Show file tree
Hide file tree
Showing 24 changed files with 1,748 additions and 493 deletions.
9 changes: 8 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
"outFiles": ["${workspaceRoot}/extension/dist/main.js"],
"autoAttachChildProcesses": true,
"preLaunchTask": "npm: build - extension"
}
},
{
"name": "Attach to language server",
"request": "attach",
"type": "dart",
"cwd": "pkgs/sass_language_server",
"vmServiceUri": "${command:dart.promptForVmService}" // Prompt for the VM Service URI
},
]
}
59 changes: 42 additions & 17 deletions docs/contributing/testing-and-debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,42 +13,65 @@ The quickest way to test the language server is to debug the language extension

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

### Testing in isolation
### Find the link to Dart DevTools or VM service

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.
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)
in the local `sass_language_server` package.

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.
Use the `[Extension Development Host]` window to find the link to open Dart DevTools or to [attach the debugger](#attach-to-language-server).

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

### Open the Dart DevTools
You should see something similar to this.

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.
```
The Dart VM service is listening on http://127.0.0.1:8181/SMIxtkPzlAY=/
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
```

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

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

### Attach to language server

You should see output similar to this.
The debugger in Dart DevTools is deprecated in favor the debugger that ships with [Dart for Visual Studio Code][vscodedart].

To start debugging in VS Code (provided you have the Dart extension):

1. [Run the language server and extension](#run-the-language-extension-and-server) in debug mode.
2. [Find the link to the Dart VM](#find-the-link-to-dart-devtools-or-vm-service).

You should see output similar to this in the `[Extension Development Host]`.

```
The Dart VM service is listening on http://127.0.0.1:8181/SMIxtkPzlAY=/
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
```

![screenshot showing the output pane and the dropdown with sass selected](https://github.com/user-attachments/assets/85839d2f-4305-4fb9-aeb0-d78f435e8b7d)
Copy the first link, then go back to the Run and debug window where you started the language server and extension.

1. Click the Run and debug drop-down and run `Attach to language server`.
2. Paste the link you copied and hit Enter.

Your debugger should be attached, allowing you to place breakpoints and step through code.

### Test in VS Code without built-in SCSS features

Click the second link to open Dart DevTools.
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.

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.
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.

You should also turn off extensions like SCSS IntelliSense or Some Sass.

## Debug unit tests

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.
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.

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

Expand All @@ -71,3 +94,5 @@ test profile in the Run and Debug view in VS Code.
]
}
```

[vscodedart]: https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code
54 changes: 1 addition & 53 deletions extension/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,61 +132,9 @@ export async function activate(context: ExtensionContext): Promise<void> {
}
}
});

// TODO: Maybe worth looking into so links to built-ins resolve to something?
// workspace.registerFileSystemProvider(
// 'sass',
// {
// readFile(uri) {
// return Uint8Array.from(
// '@function hello();'.split('').map((c) => c.charCodeAt(0))
// );
// },
// watch(uri, options) {
// return Disposable.create(() => {
// console.log('hello');
// });
// },
// readDirectory(uri) {
// return [];
// },
// stat(uri) {
// return {
// ctime: 0,
// mtime: 0,
// size: 0,
// type: 1,
// };
// },
// writeFile(uri, content, options) {
// return;
// },
// createDirectory(uri) {
// return;
// },
// delete(uri, options) {
// return;
// },
// rename(oldUri, newUri, options) {
// return;
// },
// copy(source, destination, options) {
// return;
// },
// onDidChangeFile(e) {
// return Disposable.create(() => {
// console.log('hello');
// });
// },
// },
// {
// isCaseSensitive: false,
// isReadonly: true,
// }
// );
}

export async function deactivate(): Promise<void> {
export function deactivate(): Promise<void> {
const promises: Thenable<void>[] = [];
if (defaultClient) {
promises.push(defaultClient.stop());
Expand Down
2 changes: 1 addition & 1 deletion extension/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export async function createServerOptions(
args: [
'run',
// '--pause-isolates-on-start', // Uncomment this to debug issues during startup and initial scan
'--observe',
'--enable-vm-service',
'sass_language_server',
'--loglevel=debug',
],
Expand Down
3 changes: 3 additions & 0 deletions extension/test/electron/hover/fixtures/other.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/// Docstring.
/// @type String
$from-other: 'hello';
9 changes: 9 additions & 0 deletions extension/test/electron/hover/fixtures/styles.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@use "sass:string"
@use "other"

$_id: string.unique-id()
$_prefix: other.$from-other

.card
.body:has(:not(.stuff))
padding: 4px
69 changes: 69 additions & 0 deletions extension/test/electron/hover/hover.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
const assert = require('node:assert');
const path = require('node:path');
const vscode = require('vscode');
const { showFile, sleepCI } = require('../util');

const stylesUri = vscode.Uri.file(
path.resolve(__dirname, 'fixtures', 'styles.sass')
);

before(async () => {
await showFile(stylesUri);
await sleepCI();
});

after(async () => {
await vscode.commands.executeCommand('workbench.action.closeAllEditors');
});

/**
* @param {import('vscode').Hover[]} hover
* @returns {string}
*/
function getHoverContents(hover) {
return hover
.flatMap((item) => {
return item.contents.map((content) =>
typeof content === 'string' ? content : content.value
);
})
.join('\n');
}

/**
* @param {import('vscode').Uri} documentUri
* @param {import('vscode').Position} position
* @returns {Promise<import('vscode').Hover[]>}
*/
async function hover(documentUri, position) {
const result = await vscode.commands.executeCommand(
'vscode.executeHoverProvider',
documentUri,
position
);
return result;
}

test('gets hover information from the same document', async () => {
const result = await hover(stylesUri, new vscode.Position(7, 10));

assert.match(
getHoverContents(result),
/\.card \.body:has\(:not\(\.stuff\)\)/
);
});

test('gets hover information from the workspace', async () => {
const result = await hover(stylesUri, new vscode.Position(4, 19));

assert.match(getHoverContents(result), /Docstring/);
});

test('gets hover information for Sass built-in', async () => {
const result = await hover(stylesUri, new vscode.Position(3, 14));

assert.match(
getHoverContents(result),
/Returns a randomly-generated unquoted string/
);
});
25 changes: 25 additions & 0 deletions extension/test/electron/hover/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const path = require('node:path');
const fs = require('node:fs/promises');
const vscode = require('vscode');
const { runMocha } = require('../mocha');

/**
* @returns {Promise<void>}
*/
async function run() {
const filePaths = [];

const dir = await fs.readdir(__dirname, { withFileTypes: true });
for (let entry of dir) {
if (entry.isFile() && entry.name.endsWith('test.js')) {
filePaths.push(path.join(entry.parentPath, entry.name));
}
}

await runMocha(
filePaths,
vscode.Uri.file(path.resolve(__dirname, 'fixtures', 'styles.sass'))
);
}

module.exports = { run };
36 changes: 30 additions & 6 deletions pkgs/sass_language_server/bin/sass_language_server.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import 'dart:io';

import 'package:lsp_server/lsp_server.dart';
import 'package:sass_language_server/sass_language_server.dart';

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

Connection connection;
Socket? socket;
if (transport == '--stdio') {
await server.start(
logLevel: logLevel, fileSystemProvider: fileSystemProvider);
connection = Connection(stdin, stdout);
} else {
// The client is the one listening to socket connections on the specified port.
// In other words the language server is a _client_ for the socket transport.
Expand All @@ -46,11 +50,31 @@ Logging options:
// the language server.
var split = transport.split('=');
int port = int.parse(split.last);
socket = await Socket.connect('127.0.0.1', port);
connection = Connection(socket, socket);
}

try {
exitCode = 1;

await server.start(
logLevel: logLevel,
fileSystemProvider: fileSystemProvider,
transport: Transport.socket,
port: port);
connection: connection,
logLevel: logLevel,
fileSystemProvider: fileSystemProvider,
);

// See
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#shutdown
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#exit
connection.onShutdown(() async {
socket?.close();
exitCode = 0;
});

connection.onExit(() async {
exit(exitCode);
});
} on Exception catch (_) {
exit(1);
}
}
Loading

0 comments on commit 6c8cb3f

Please sign in to comment.