Skip to content

Commit 7f98054

Browse files
committed
Scope visitor tests
1 parent 159ba9e commit 7f98054

File tree

4 files changed

+98
-24
lines changed

4 files changed

+98
-24
lines changed

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:sass_language_services/src/features/go_to_definition/scoped_symb
44
import 'package:sass_language_services/src/features/node_at_offset_visitor.dart';
55

66
import '../language_feature.dart';
7+
import 'scope_visitor.dart';
78

89
class GoToDefinitionFeature extends LanguageFeature {
910
GoToDefinitionFeature({required super.ls});
@@ -38,7 +39,8 @@ class GoToDefinitionFeature extends LanguageFeature {
3839

3940
// Look for the symbol in the current document.
4041
// It may be a scoped symbol.
41-
var symbols = ScopedSymbols(stylesheet);
42+
var symbols = ScopedSymbols(stylesheet,
43+
document.languageId == 'sass' ? Dialect.indented : Dialect.scss);
4244
var symbol = symbols.findSymbolFromNode(node);
4345
if (symbol != null) {
4446
// Found the definition in the same document.
@@ -64,7 +66,8 @@ class GoToDefinitionFeature extends LanguageFeature {
6466
? '$prefix$name'
6567
: name;
6668

67-
var symbols = ScopedSymbols(stylesheet);
69+
var symbols = ScopedSymbols(stylesheet,
70+
document.languageId == 'sass' ? Dialect.indented : Dialect.scss);
6871
var symbol = symbols.globalScope.getSymbol(
6972
name: prefixedName,
7073
referenceKind: kind,

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

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,19 @@ import '../document_symbols/stylesheet_document_symbol.dart';
66
import 'scope.dart';
77

88
final deprecated = RegExp(r'///\s*@deprecated');
9-
final scopeStart = RegExp(r'[\{\n]');
9+
final openBracketOrNewline = RegExp(r'[\{\n]');
10+
11+
enum Dialect { scss, indented }
1012

1113
/// Builds scopes and a list of symbols in those scopes starting at [scope] (typically the global scope).
1214
class ScopeVisitor with sass.RecursiveStatementVisitor {
1315
final Scope scope;
16+
final Dialect dialect;
1417

15-
ScopeVisitor(this.scope);
18+
ScopeVisitor(
19+
this.scope,
20+
this.dialect,
21+
);
1622

1723
void _addSymbol({
1824
required String name,
@@ -58,7 +64,8 @@ class ScopeVisitor with sass.RecursiveStatementVisitor {
5864
void visitAtRule(node) {
5965
if (node.children != null) {
6066
var nameEndIndex = node.name.span.end.offset - node.span.start.offset;
61-
var scopeIndex = node.span.text.indexOf(scopeStart, nameEndIndex);
67+
var scopeIndex =
68+
node.span.text.indexOf(openBracketOrNewline, nameEndIndex);
6269

6370
_addScope(
6471
offset: node.span.start.offset + scopeIndex,
@@ -152,7 +159,7 @@ class ScopeVisitor with sass.RecursiveStatementVisitor {
152159
@override
153160
void visitForRule(sass.ForRule node) {
154161
var toEndIndex = node.to.span.end.offset - node.span.start.offset;
155-
var scopeIndex = node.span.text.indexOf(scopeStart, toEndIndex);
162+
var scopeIndex = node.span.text.indexOf(openBracketOrNewline, toEndIndex);
156163
var scope = _addScope(
157164
offset: node.span.start.offset + scopeIndex,
158165
length: node.span.length - scopeIndex,
@@ -197,7 +204,7 @@ class ScopeVisitor with sass.RecursiveStatementVisitor {
197204
);
198205

199206
var argsEndIndex = node.arguments.span.end.offset - node.span.start.offset;
200-
var scopeIndex = node.span.text.indexOf(scopeStart, argsEndIndex);
207+
var scopeIndex = node.span.text.indexOf(openBracketOrNewline, argsEndIndex);
201208
var scope = _addScope(
202209
offset: node.span.start.offset + scopeIndex,
203210
length: node.span.length - scopeIndex,
@@ -223,10 +230,43 @@ class ScopeVisitor with sass.RecursiveStatementVisitor {
223230

224231
@override
225232
void visitIfRule(sass.IfRule node) {
226-
_addScope(
227-
offset: node.span.start.offset,
228-
length: node.span.length,
229-
);
233+
// TODO: would be nice to have the spans for clauses from sass_api.
234+
Scope? previousClause;
235+
for (var clause in node.clauses) {
236+
var argsEndIndex =
237+
clause.expression.span.end.offset - node.span.start.offset;
238+
var scopeStartIndex =
239+
node.span.text.indexOf(openBracketOrNewline, argsEndIndex);
240+
241+
var clauseChildrenLength = clause.children
242+
.map<int>((e) => e.span.context.length)
243+
.reduce((value, element) => value + element);
244+
245+
var toMatch = dialect == Dialect.indented ? '\n' : '}';
246+
247+
var scopeEndIndex = node.span.text
248+
.indexOf(toMatch, scopeStartIndex + clauseChildrenLength);
249+
250+
previousClause = _addScope(
251+
offset: node.span.start.offset + scopeStartIndex,
252+
length: scopeEndIndex - scopeStartIndex + 1,
253+
);
254+
}
255+
256+
if (previousClause != null && node.lastClause != null) {
257+
var scopeIndex = node.span.text.indexOf(
258+
openBracketOrNewline,
259+
previousClause.offset -
260+
node.span.start.offset +
261+
previousClause.length +
262+
"@else".length);
263+
264+
_addScope(
265+
offset: node.span.start.offset + scopeIndex,
266+
length: node.span.length - scopeIndex,
267+
);
268+
}
269+
230270
super.visitIfRule(node);
231271
}
232272

@@ -242,7 +282,7 @@ class ScopeVisitor with sass.RecursiveStatementVisitor {
242282
);
243283

244284
var argsEndIndex = node.arguments.span.end.offset - node.span.start.offset;
245-
var scopeIndex = node.span.text.indexOf(scopeStart, argsEndIndex);
285+
var scopeIndex = node.span.text.indexOf(openBracketOrNewline, argsEndIndex);
246286
var scope = _addScope(
247287
offset: node.span.start.offset + scopeIndex,
248288
length: node.span.length - scopeIndex,
@@ -349,11 +389,14 @@ class ScopeVisitor with sass.RecursiveStatementVisitor {
349389

350390
@override
351391
void visitWhileRule(sass.WhileRule node) {
352-
var lengthSubtract =
392+
var conditionEndIndex =
353393
node.condition.span.end.offset - node.span.start.offset;
394+
var scopeIndex =
395+
node.span.text.indexOf(openBracketOrNewline, conditionEndIndex);
396+
354397
_addScope(
355-
offset: node.condition.span.end.offset,
356-
length: node.span.length - lengthSubtract,
398+
offset: node.span.start.offset + scopeIndex,
399+
length: node.span.length - scopeIndex,
357400
);
358401

359402
super.visitWhileRule(node);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ String? getNodeName(sass.AstNode node) {
7575
class ScopedSymbols {
7676
final globalScope = Scope(length: double.maxFinite.floor(), offset: 0);
7777

78-
ScopedSymbols(sass.Stylesheet stylesheet) {
79-
stylesheet.accept(ScopeVisitor(globalScope));
78+
ScopedSymbols(sass.Stylesheet stylesheet, Dialect dialect) {
79+
stylesheet.accept(ScopeVisitor(globalScope, dialect));
8080
}
8181

8282
StylesheetDocumentSymbol? findSymbolFromNode(sass.AstNode node) {

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

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:sass_language_services/sass_language_services.dart';
2+
import 'package:sass_language_services/src/features/go_to_definition/scope_visitor.dart';
23
import 'package:sass_language_services/src/features/go_to_definition/scoped_symbols.dart';
34
import 'package:test/test.dart';
45

@@ -10,7 +11,8 @@ final ls = LanguageServices(fs: fs, clientCapabilities: getCapabilities());
1011

1112
ScopedSymbols getSymbols(TextDocument document) {
1213
var stylesheet = ls.parseStylesheet(document);
13-
var symbols = ScopedSymbols(stylesheet);
14+
var symbols = ScopedSymbols(stylesheet,
15+
document.languageId == 'sass' ? Dialect.indented : Dialect.scss);
1416
return symbols;
1517
}
1618

@@ -151,17 +153,33 @@ void main() {
151153
@return false;
152154
}
153155
}
156+
157+
@function is-odd($int) {
158+
@if $int % 2 != 0 {
159+
@return true;
160+
} @else {
161+
@return false;
162+
}
163+
}
154164
''');
155165
var symbols = getSymbols(document);
156166

167+
expect(symbols.globalScope.children, hasLength(2));
157168
expect(symbols.globalScope.children.first.children, hasLength(2));
158169

159170
var [first, second] = symbols.globalScope.children.first.children;
160-
expect(first.offset, equals(18));
161-
expect(first.length, equals(19));
171+
expect(first.offset, equals(46));
172+
expect(first.length, equals(23));
162173

163-
expect(second.offset, equals(44));
164-
expect(second.length, equals(20));
174+
expect(second.offset, equals(76));
175+
expect(second.length, equals(24));
176+
177+
var [third, fourth] = symbols.globalScope.children.last.children;
178+
expect(third.offset, equals(149));
179+
expect(third.length, equals(23));
180+
181+
expect(fourth.offset, equals(179));
182+
expect(fourth.length, equals(24));
165183
});
166184

167185
test('mixin rules', () {
@@ -194,14 +212,24 @@ void main() {
194212
}
195213
$i: $i - 2;
196214
}
215+
216+
@while $y < 0 {
217+
.item-#{$y} {
218+
width: 1em * $y;
219+
}
220+
$y: $y - 1;
221+
}
197222
''');
198223
var symbols = getSymbols(document);
199224

200-
expect(symbols.globalScope.children, hasLength(1));
225+
expect(symbols.globalScope.children, hasLength(2));
201226

202-
var [first] = symbols.globalScope.children;
227+
var [first, second] = symbols.globalScope.children;
203228
expect(first.offset, equals(14));
204229
expect(first.length, equals(58));
230+
231+
expect(second.offset, equals(88));
232+
expect(second.length, equals(58));
205233
});
206234
});
207235
}

0 commit comments

Comments
 (0)