Skip to content

Commit c76e2a0

Browse files
committed
Tests for workspace traversal
1 parent 90b671a commit c76e2a0

File tree

4 files changed

+112
-51
lines changed

4 files changed

+112
-51
lines changed

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

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:lsp_server/lsp_server.dart' as lsp;
2+
import 'package:sass_api/sass_api.dart' as sass;
23
import 'package:sass_language_services/sass_language_services.dart';
34
import 'package:sass_language_services/src/features/go_to_definition/scoped_symbols.dart';
45
import 'package:sass_language_services/src/features/node_at_offset_visitor.dart';
@@ -46,10 +47,35 @@ class GoToDefinitionFeature extends LanguageFeature {
4647
return lsp.Location(uri: document.uri, range: symbol.selectionRange);
4748
}
4849

49-
// If not, look for it elsewhere in the workspace.
50+
// Start looking from the linked document In case of a namespace
51+
// so we don't accidentally match with a symbol of the same kind
52+
// and name, but in a different module.
53+
String? namespace;
54+
if (node is sass.VariableExpression) {
55+
namespace = node.namespace;
56+
} else if (node is sass.IncludeRule) {
57+
namespace = node.namespace;
58+
} else if (node is sass.FunctionExpression) {
59+
namespace = node.namespace;
60+
}
61+
62+
var initialDocument = document;
63+
if (namespace != null) {
64+
var links = await ls.findDocumentLinks(document);
65+
try {
66+
var link = links.firstWhere((l) => l.namespace == namespace);
67+
if (link.target case var target?) {
68+
initialDocument = await getTextDocument(target);
69+
}
70+
} on StateError {
71+
return null;
72+
}
73+
}
74+
5075
var definition = await findInWorkspace<lsp.Location>(
5176
lazy: true,
52-
initialDocument: document,
77+
initialDocument: initialDocument,
78+
depth: initialDocument.uri != document.uri ? 1 : 0,
5379
callback: ({
5480
required TextDocument document,
5581
required String prefix,
@@ -58,18 +84,19 @@ class GoToDefinitionFeature extends LanguageFeature {
5884
required List<String> shownMixinsAndFunctions,
5985
required List<String> shownVariables,
6086
}) async {
61-
// Some symbols may change names in `@forward`.
62-
var prefixedName = kind == ReferenceKind.function ||
87+
// `@forward` may add a prefix to [name],
88+
// but we're comparing it to symbols without that prefix.
89+
var unprefixedName = kind == ReferenceKind.function ||
6390
kind == ReferenceKind.mixin ||
6491
kind == ReferenceKind.variable
65-
? '$prefix$name'
92+
? name.replaceFirst(prefix, '')
6693
: name;
6794

6895
var stylesheet = ls.parseStylesheet(document);
6996
var symbols = ScopedSymbols(stylesheet,
7097
document.languageId == 'sass' ? Dialect.indented : Dialect.scss);
7198
var symbol = symbols.globalScope.getSymbol(
72-
name: prefixedName,
99+
name: unprefixedName,
73100
referenceKind: kind,
74101
);
75102

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

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -102,24 +102,7 @@ abstract class LanguageFeature {
102102
}
103103

104104
var uri = link.target!;
105-
var next = ls.cache.getDocument(uri);
106-
if (next == null) {
107-
// We shouldn't really end up here outside of unit tests.
108-
// The language server's initial scan should have put all
109-
// linked documents in the cache already.
110-
var text = await ls.fs.readFile(uri);
111-
next = TextDocument(
112-
uri,
113-
uri.path.endsWith('.sass')
114-
? 'sass'
115-
: uri.path.endsWith('.css')
116-
? 'css'
117-
: 'scss',
118-
1,
119-
text,
120-
);
121-
ls.parseStylesheet(next);
122-
}
105+
var next = await getTextDocument(uri);
123106

124107
var prefix = accumulatedPrefix;
125108
if (link.type == LinkType.forward) {
@@ -164,6 +147,28 @@ abstract class LanguageFeature {
164147
return result;
165148
}
166149

150+
Future<TextDocument> getTextDocument(Uri uri) async {
151+
var textDocument = ls.cache.getDocument(uri);
152+
if (textDocument == null) {
153+
// We shouldn't really end up here outside of unit tests.
154+
// The language server's initial scan should have put all
155+
// linked documents in the cache already.
156+
var text = await ls.fs.readFile(uri);
157+
textDocument = TextDocument(
158+
uri,
159+
uri.path.endsWith('.sass')
160+
? 'sass'
161+
: uri.path.endsWith('.css')
162+
? 'css'
163+
: 'scss',
164+
1,
165+
text,
166+
);
167+
ls.parseStylesheet(textDocument);
168+
}
169+
return textDocument;
170+
}
171+
167172
DocumentContext getDocumentContext() {
168173
return DocumentContext(
169174
workspaceRoot: ls.configuration.workspace.workspaceRoot);

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

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,18 @@ $b: blue
3636

3737
test('global in a different document', () async {
3838
fs.createDocument(r'$b: #000;', uri: 'colors.scss');
39+
fs.createDocument(r'$b: 800;', uri: 'weights.scss');
3940

4041
var document = fs.createDocument(r'''
4142
@use "colors";
43+
@use "weights" as w;
4244
4345
.a {
44-
color: $b;
46+
color: colors.$b;
47+
font-weight: w.$b;
4548
}
4649
''');
47-
var result = await ls.goToDefinition(document, at(line: 3, char: 10));
50+
var result = await ls.goToDefinition(document, at(line: 4, char: 17));
4851

4952
expect(result, isNotNull);
5053
expect(result!.range, StartsAtLine(0));
@@ -53,6 +56,15 @@ $b: blue
5356
expect(result.range, EndsAtCharacter(2));
5457

5558
expect(result.uri.toString(), endsWith('colors.scss'));
59+
60+
result = await ls.goToDefinition(document, at(line: 5, char: 18));
61+
expect(result, isNotNull);
62+
expect(result!.range, StartsAtLine(0));
63+
expect(result.range, EndsAtLine(0));
64+
expect(result.range, StartsAtCharacter(0));
65+
expect(result.range, EndsAtCharacter(2));
66+
67+
expect(result.uri.toString(), endsWith('weights.scss'));
5668
});
5769

5870
test('scoped in the same document', () async {
@@ -84,10 +96,10 @@ $b: blue
8496
@use "links";
8597
8698
.a {
87-
color: $b;
99+
color: links.$b;
88100
}
89101
''');
90-
var result = await ls.goToDefinition(document, at(line: 3, char: 10));
102+
var result = await ls.goToDefinition(document, at(line: 3, char: 16));
91103

92104
expect(result, isNull);
93105
});
@@ -150,7 +162,7 @@ nav ul
150162
@use "list";
151163
152164
nav ul {
153-
@include horizontal-list;
165+
@include list.horizontal-list;
154166
}
155167
''');
156168
var result = await ls.goToDefinition(document, at(line: 3, char: 12));
@@ -163,6 +175,45 @@ nav ul {
163175

164176
expect(result.uri.toString(), endsWith('list.sass'));
165177
});
178+
179+
test('behind a prefix', () async {
180+
fs.createDocument(r'''
181+
=reset-list
182+
margin: 0
183+
padding: 0
184+
list-style: none
185+
186+
=horizontal-list
187+
+reset-list
188+
189+
li
190+
display: inline-block
191+
margin:
192+
left: -2px
193+
right: 2em
194+
''', uri: '_list.sass');
195+
196+
fs.createDocument(r'''
197+
@forward "list" as list-*
198+
''', uri: 'shared.sass');
199+
200+
var document = fs.createDocument(r'''
201+
@use "shared";
202+
203+
nav ul {
204+
@include shared.list-horizontal-list;
205+
}
206+
''');
207+
var result = await ls.goToDefinition(document, at(line: 3, char: 24));
208+
209+
expect(result, isNotNull);
210+
expect(result!.range, StartsAtLine(5));
211+
expect(result.range, EndsAtLine(5));
212+
expect(result.range, StartsAtCharacter(1));
213+
expect(result.range, EndsAtCharacter(16));
214+
215+
expect(result.uri.toString(), endsWith('_list.sass'));
216+
});
166217
});
167218

168219
group('sass functions', () {

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

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,28 +44,6 @@ void main() {
4444
expect(second.length, equals(18));
4545
});
4646

47-
test('at rules', () {
48-
var document = fs.createDocument('''
49-
@font-face {
50-
font-family: "Vulf Mono", monospace;
51-
}
52-
53-
@keyframes animation {
54-
55-
}
56-
''');
57-
var symbols = getSymbols(document);
58-
59-
expect(symbols.globalScope.children, hasLength(2));
60-
61-
var [first, second] = symbols.globalScope.children;
62-
expect(first.offset, equals(11));
63-
expect(first.length, equals(42));
64-
65-
expect(second.offset, equals(76));
66-
expect(second.length, equals(4));
67-
});
68-
6947
test('each rules', () {
7048
var document = fs.createDocument(r'''
7149
@each $i in 1, 2, 3 {

0 commit comments

Comments
 (0)