Skip to content

Commit 2f698dd

Browse files
committed
Document highlights feature
1 parent 02f5428 commit 2f698dd

File tree

6 files changed

+118
-40
lines changed

6 files changed

+118
-40
lines changed

pkgs/sass_language_server/lib/src/language_server.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ class LanguageServer {
174174

175175
var serverCapabilities = ServerCapabilities(
176176
definitionProvider: Either2.t1(true),
177+
documentHighlightProvider: Either2.t1(true),
177178
documentLinkProvider: DocumentLinkOptions(resolveProvider: false),
178179
documentSymbolProvider: Either2.t1(true),
179180
referencesProvider: Either2.t1(true),
@@ -265,6 +266,28 @@ class LanguageServer {
265266
}
266267
});
267268

269+
_connection.onDocumentHighlight((params) async {
270+
try {
271+
var document = _documents.get(params.textDocument.uri);
272+
if (document == null) return [];
273+
274+
var configuration = _getLanguageConfiguration(document);
275+
if (configuration.highlights.enabled) {
276+
if (initialScan != null) {
277+
await initialScan;
278+
}
279+
280+
var result = _ls.findDocumentHighlights(document, params.position);
281+
return result;
282+
} else {
283+
return [];
284+
}
285+
} on Exception catch (e) {
286+
_log.debug(e.toString());
287+
return [];
288+
}
289+
});
290+
268291
_connection.onDocumentLinks((params) async {
269292
try {
270293
var document = _documents.get(params.textDocument.uri);

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

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,26 @@ class FeatureConfiguration {
44
FeatureConfiguration({required this.enabled});
55
}
66

7-
class DefinitionConfiguration extends FeatureConfiguration {
8-
DefinitionConfiguration({required super.enabled});
9-
}
10-
11-
class DocumentSymbolsConfiguration extends FeatureConfiguration {
12-
DocumentSymbolsConfiguration({required super.enabled});
13-
}
14-
15-
class DocumentLinksConfiguration extends FeatureConfiguration {
16-
DocumentLinksConfiguration({required super.enabled});
17-
}
18-
19-
class ReferencesConfiguration extends FeatureConfiguration {
20-
ReferencesConfiguration({required super.enabled});
21-
}
22-
23-
class WorkspaceSymbolsConfiguration extends FeatureConfiguration {
24-
WorkspaceSymbolsConfiguration({required super.enabled});
25-
}
26-
277
class LanguageConfiguration {
28-
late final DefinitionConfiguration definition;
29-
late final DocumentSymbolsConfiguration documentSymbols;
30-
late final DocumentLinksConfiguration documentLinks;
31-
late final ReferencesConfiguration references;
32-
late final WorkspaceSymbolsConfiguration workspaceSymbols;
8+
late final FeatureConfiguration definition;
9+
late final FeatureConfiguration highlights;
10+
late final FeatureConfiguration documentSymbols;
11+
late final FeatureConfiguration documentLinks;
12+
late final FeatureConfiguration references;
13+
late final FeatureConfiguration workspaceSymbols;
3314

3415
LanguageConfiguration.from(dynamic config) {
35-
definition = DefinitionConfiguration(
16+
definition = FeatureConfiguration(
3617
enabled: config?['definition']?['enabled'] as bool? ?? true);
37-
documentSymbols = DocumentSymbolsConfiguration(
18+
documentSymbols = FeatureConfiguration(
3819
enabled: config?['documentSymbols']?['enabled'] as bool? ?? true);
39-
documentLinks = DocumentLinksConfiguration(
20+
documentLinks = FeatureConfiguration(
4021
enabled: config?['documentLinks']?['enabled'] as bool? ?? true);
41-
references = ReferencesConfiguration(
22+
highlights = FeatureConfiguration(
23+
enabled: config?['highlights']?['enabled'] as bool? ?? true);
24+
references = FeatureConfiguration(
4225
enabled: config?['references']?['enabled'] as bool? ?? true);
43-
workspaceSymbols = WorkspaceSymbolsConfiguration(
26+
workspaceSymbols = FeatureConfiguration(
4427
enabled: config?['workspaceSymbols']?['enabled'] as bool? ?? true);
4528
}
4629
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import 'package:lsp_server/lsp_server.dart' as lsp;
2+
import 'package:sass_api/sass_api.dart' as sass;
3+
import 'package:sass_language_services/sass_language_services.dart';
4+
import 'package:sass_language_services/src/features/find_references/find_references_visitor.dart';
5+
import 'package:sass_language_services/src/utils/sass_lsp_utils.dart';
6+
7+
import '../go_to_definition/scope_visitor.dart';
8+
import '../go_to_definition/scoped_symbols.dart';
9+
import '../language_feature.dart';
10+
import '../node_at_offset_visitor.dart';
11+
12+
class DocumentHighlightsFeature extends LanguageFeature {
13+
DocumentHighlightsFeature({required super.ls});
14+
15+
List<lsp.DocumentHighlight> findDocumentHighlights(
16+
TextDocument document, lsp.Position position) {
17+
var stylesheet = ls.parseStylesheet(document);
18+
// Find the node whose definition we're looking for.
19+
var offset = document.offsetAt(position);
20+
var visitor = NodeAtOffsetVisitor(offset);
21+
var node = stylesheet.accept(visitor);
22+
if (node == null || node is sass.Stylesheet) {
23+
return [];
24+
}
25+
26+
var name = getNodeName(node);
27+
if (name == null) {
28+
return [];
29+
}
30+
var kind = getNodeReferenceKind(node);
31+
32+
var symbols = ScopedSymbols(
33+
stylesheet,
34+
document.languageId == 'sass' ? Dialect.indented : Dialect.scss,
35+
);
36+
var symbol = symbols.findSymbolFromNode(node);
37+
38+
var result = <lsp.DocumentHighlight>[];
39+
var references = FindReferencesVisitor(document, name);
40+
for (var reference in references.candidates) {
41+
if (symbol != null) {
42+
if (symbol.referenceKind == reference.kind &&
43+
symbol.name == reference.name &&
44+
isSameRange(symbol.range, reference.location.range)) {
45+
result.add(
46+
lsp.DocumentHighlight(range: reference.location.range),
47+
);
48+
}
49+
} else if (kind != null &&
50+
reference.kind == kind &&
51+
reference.name == name) {
52+
result.add(
53+
lsp.DocumentHighlight(range: reference.location.range),
54+
);
55+
}
56+
}
57+
58+
return result;
59+
}
60+
}

pkgs/sass_language_services/lib/src/features/find_references/find_references_feature.dart

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:sass_language_services/src/features/find_references/find_referen
44
import 'package:sass_language_services/src/features/go_to_definition/go_to_definition_feature.dart';
55

66
import '../../sass/sass_data.dart';
7+
import '../../utils/sass_lsp_utils.dart';
78
import '../go_to_definition/definition.dart';
89
import 'reference.dart';
910

@@ -89,7 +90,7 @@ class FindReferencesFeature extends GoToDefinitionFeature {
8990
continue;
9091
}
9192

92-
var candidateIsDefinition = _isSameLocation(
93+
var candidateIsDefinition = isSameLocation(
9394
candidate.location,
9495
definition.location!,
9596
);
@@ -109,7 +110,7 @@ class FindReferencesFeature extends GoToDefinitionFeature {
109110

110111
if (candidateDefinition != null &&
111112
candidateDefinition.location != null) {
112-
if (_isSameLocation(
113+
if (isSameLocation(
113114
candidateDefinition.location!,
114115
definition.location!,
115116
)) {
@@ -125,12 +126,4 @@ class FindReferencesFeature extends GoToDefinitionFeature {
125126

126127
return (definition: definition, references: references);
127128
}
128-
129-
bool _isSameLocation(lsp.Location a, lsp.Location b) {
130-
return a.uri.toString() == b.uri.toString() &&
131-
a.range.start.line == b.range.start.line &&
132-
a.range.start.character == b.range.start.character &&
133-
a.range.end.line == b.range.end.line &&
134-
a.range.end.character == b.range.end.character;
135-
}
136129
}

pkgs/sass_language_services/lib/src/language_services.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:lsp_server/lsp_server.dart' as lsp;
22
import 'package:sass_api/sass_api.dart' as sass;
33
import 'package:sass_language_services/sass_language_services.dart';
4+
import 'package:sass_language_services/src/features/document_highlights/document_highlights_feature.dart';
45
import 'package:sass_language_services/src/features/find_references/find_references_feature.dart';
56
import 'package:sass_language_services/src/features/go_to_definition/go_to_definition_feature.dart';
67

@@ -17,6 +18,7 @@ class LanguageServices {
1718
LanguageServerConfiguration configuration =
1819
LanguageServerConfiguration.create(null);
1920

21+
late final DocumentHighlightsFeature _documentHighlights;
2022
late final DocumentLinksFeature _documentLinks;
2123
late final DocumentSymbolsFeature _documentSymbols;
2224
late final GoToDefinitionFeature _goToDefinition;
@@ -27,6 +29,7 @@ class LanguageServices {
2729
required this.clientCapabilities,
2830
required this.fs,
2931
}) : cache = LanguageServicesCache() {
32+
_documentHighlights = DocumentHighlightsFeature(ls: this);
3033
_documentLinks = DocumentLinksFeature(ls: this);
3134
_documentSymbols = DocumentSymbolsFeature(ls: this);
3235
_goToDefinition = GoToDefinitionFeature(ls: this);
@@ -38,6 +41,11 @@ class LanguageServices {
3841
this.configuration = configuration;
3942
}
4043

44+
List<lsp.DocumentHighlight> findDocumentHighlights(
45+
TextDocument document, lsp.Position position) {
46+
return _documentHighlights.findDocumentHighlights(document, position);
47+
}
48+
4149
Future<List<StylesheetDocumentLink>> findDocumentLinks(
4250
TextDocument document) {
4351
return _documentLinks.findDocumentLinks(document);

pkgs/sass_language_services/lib/src/utils/sass_lsp_utils.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,14 @@ bool isInRange({required lsp.Position position, required lsp.Range range}) {
5151
range.end.line >= position.line &&
5252
range.end.character >= position.character;
5353
}
54+
55+
bool isSameLocation(lsp.Location a, lsp.Location b) {
56+
return a.uri.toString() == b.uri.toString() && isSameRange(a.range, b.range);
57+
}
58+
59+
bool isSameRange(lsp.Range a, lsp.Range b) {
60+
return a.start.line == b.start.line &&
61+
a.start.character == b.start.character &&
62+
a.end.line == b.end.line &&
63+
a.end.character == b.end.character;
64+
}

0 commit comments

Comments
 (0)