Skip to content

Commit 2a3d6ee

Browse files
committed
End-to-end test for Go to definition
1 parent 9716d8f commit 2a3d6ee

File tree

9 files changed

+149
-11
lines changed

9 files changed

+149
-11
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
%brand
2+
color: purple
3+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@use 'theme';
2+
3+
.a {
4+
@extend %brand;
5+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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.scss')
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').Uri} documentUri
21+
* @returns {Promise<Array<import('vscode').Location>>}
22+
*/
23+
async function goToDefinition(documentUri, position) {
24+
const result = await vscode.commands.executeCommand(
25+
'vscode.executeDefinitionProvider',
26+
documentUri,
27+
position
28+
);
29+
return result;
30+
}
31+
32+
test('gets document symbols', async () => {
33+
const [result] = await goToDefinition(stylesUri, new vscode.Position(3, 12));
34+
35+
assert.ok(result, 'Should have found the definition for %brand');
36+
assert.match(result.uri.toString(), /_theme\.sass$/);
37+
38+
assert.equal(result.range.start.line, 0);
39+
assert.equal(result.range.start.character, 0);
40+
41+
assert.equal(result.range.end.line, 0);
42+
assert.equal(result.range.end.character, 6);
43+
});
Lines changed: 25 additions & 0 deletions
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.scss'))
22+
);
23+
}
24+
25+
module.exports = { run };

pkgs/sass_language_server/lib/src/language_server.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ class LanguageServer {
171171
clientCapabilities: _clientCapabilities, fs: fileSystemProvider);
172172

173173
var serverCapabilities = ServerCapabilities(
174+
definitionProvider: Either2.t1(true),
174175
documentLinkProvider: DocumentLinkOptions(resolveProvider: false),
175176
documentSymbolProvider: Either2.t1(true),
176177
textDocumentSync: Either2.t1(TextDocumentSyncKind.Incremental),
@@ -236,6 +237,31 @@ class LanguageServer {
236237
}
237238
});
238239

240+
_connection.onDefinition((params) async {
241+
try {
242+
var document = _documents.get(params.textDocument.uri);
243+
if (document == null) return null;
244+
245+
var configuration = _getLanguageConfiguration(document);
246+
if (configuration.definition.enabled) {
247+
if (initialScan != null) {
248+
await initialScan;
249+
}
250+
var result = await _ls.goToDefinition(document, params.position);
251+
if (result is Location) {
252+
return Either3.t1(result);
253+
} else {
254+
return null;
255+
}
256+
} else {
257+
return null;
258+
}
259+
} on Exception catch (e) {
260+
_log.debug(e.toString());
261+
return null;
262+
}
263+
});
264+
239265
_connection.onDocumentLinks((params) async {
240266
try {
241267
var document = _documents.get(params.textDocument.uri);

pkgs/sass_language_services/lib/src/configuration/language_configuration.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ class FeatureConfiguration {
44
FeatureConfiguration({required this.enabled});
55
}
66

7+
class DefinitionConfiguration extends FeatureConfiguration {
8+
DefinitionConfiguration({required super.enabled});
9+
}
10+
711
class DocumentSymbolsConfiguration extends FeatureConfiguration {
812
DocumentSymbolsConfiguration({required super.enabled});
913
}
@@ -17,11 +21,14 @@ class WorkspaceSymbolsConfiguration extends FeatureConfiguration {
1721
}
1822

1923
class LanguageConfiguration {
24+
late final DefinitionConfiguration definition;
2025
late final DocumentSymbolsConfiguration documentSymbols;
2126
late final DocumentLinksConfiguration documentLinks;
2227
late final WorkspaceSymbolsConfiguration workspaceSymbols;
2328

2429
LanguageConfiguration.from(dynamic config) {
30+
definition = DefinitionConfiguration(
31+
enabled: config?['definition']?['enabled'] as bool? ?? true);
2532
documentSymbols = DocumentSymbolsConfiguration(
2633
enabled: config?['documentSymbols']?['enabled'] as bool? ?? true);
2734
documentLinks = DocumentLinksConfiguration(

pkgs/sass_language_services/lib/src/features/go_to_definition/scope_visitor.dart

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,14 +203,12 @@ class ScopeVisitor with sass.RecursiveStatementVisitor {
203203
var scopeStartIndex =
204204
node.span.text.indexOf(openBracketOrNewline, argsEndIndex);
205205

206-
var clauseChildrenLength = clause.children
207-
.map<int>((e) => e.span.context.length)
208-
.reduce((value, element) => value + element);
209-
210206
var toMatch = dialect == Dialect.indented ? '\n' : '}';
211207

212-
var scopeEndIndex = node.span.text
213-
.indexOf(toMatch, scopeStartIndex + clauseChildrenLength);
208+
var lastChildIndex =
209+
node.span.text.indexOf(clause.children.last.span.text);
210+
var scopeEndIndex = node.span.text.indexOf(
211+
toMatch, lastChildIndex + clause.children.last.span.text.length);
214212

215213
previousClause = _addScope(
216214
offset: node.span.start.offset + scopeStartIndex,

pkgs/sass_language_services/lib/src/features/language_feature.dart

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,6 @@ abstract class LanguageFeature {
7272
return result;
7373
}
7474

75-
result ??= [];
76-
7775
visited.add(currentDocument.uri.toString());
7876

7977
var allLinks = await ls.findDocumentLinks(currentDocument);
@@ -92,9 +90,10 @@ abstract class LanguageFeature {
9290
});
9391

9492
if (links.isEmpty) {
95-
return result;
93+
return null;
9694
}
9795

96+
var linksResult = <T>[];
9897
for (var link in links) {
9998
if (link.target == null ||
10099
link.target.toString() == currentDocument.uri.toString()) {
@@ -140,11 +139,11 @@ abstract class LanguageFeature {
140139
);
141140

142141
if (linkResult != null) {
143-
result.addAll(linkResult);
142+
linksResult.addAll(linkResult);
144143
}
145144
}
146145

147-
return result;
146+
return linksResult;
148147
}
149148

150149
Future<TextDocument> getTextDocument(Uri uri) async {

pkgs/sass_language_services/test/features/go_to_definition/scoped_symbols_test.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,38 @@ void main() {
160160
expect(fourth.length, equals(24));
161161
});
162162

163+
test('if rule with multiple child nodes', () {
164+
var document = fs.createDocument(r'''
165+
@mixin _single-spacing($spacing-step, $position) {
166+
@if $position and list.index($positions, $position) {
167+
// Add dash before position to ease interpolation
168+
$position: "-#{$position}";
169+
}
170+
171+
@if map.has-key($spacing, $spacing-step) {
172+
margin#{$position}: map.get($spacing, $spacing-step);
173+
} @else {
174+
@error "Could not find \"#{$spacing-step}\" in the list of spacing values";
175+
}
176+
}
177+
''');
178+
var symbols = getSymbols(document);
179+
180+
expect(symbols.globalScope.children, hasLength(1));
181+
expect(symbols.globalScope.children.first.children, hasLength(3));
182+
183+
var [first, second, third] = symbols.globalScope.children.first.children;
184+
185+
expect(first.offset, equals(107));
186+
expect(first.length, equals(101));
187+
188+
expect(second.offset, equals(255));
189+
expect(second.length, equals(69));
190+
191+
expect(third.offset, equals(331));
192+
expect(third.length, equals(91));
193+
});
194+
163195
test('mixin rules', () {
164196
var document = fs.createDocument('''
165197
@mixin large-text {

0 commit comments

Comments
 (0)