Skip to content

Commit c9b5354

Browse files
DanTupcommit-bot@chromium.org
authored andcommitted
[analysis_server] Add "annotation" modifier to identifiers used as annotations
Fixes Dart-Code/Dart-Code#3621. Change-Id: Idf0cfb7f8a0b2a8b4f1ed269f125f6743fa19471 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/217940 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent 4258e46 commit c9b5354

File tree

4 files changed

+144
-5
lines changed

4 files changed

+144
-5
lines changed

pkg/analysis_server/lib/src/computer/computer_highlights.dart

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,12 @@ class DartUnitHighlightsComputer {
179179
} else {
180180
type = HighlightRegionType.CLASS;
181181
}
182+
183+
if (_isAnnotationIdentifier(node)) {
184+
semanticModifiers ??= {};
185+
semanticModifiers.add(CustomSemanticTokenModifiers.annotation);
186+
}
187+
182188
// add region
183189
return _addRegion_node(
184190
node,
@@ -199,7 +205,11 @@ class DartUnitHighlightsComputer {
199205
// For semantic tokens, constructor names are coloured like methods but
200206
// have a modifier applied.
201207
semanticTokenType: SemanticTokenTypes.method,
202-
semanticTokenModifiers: {CustomSemanticTokenModifiers.constructor},
208+
semanticTokenModifiers: {
209+
CustomSemanticTokenModifiers.constructor,
210+
if (_isAnnotationIdentifier(node))
211+
CustomSemanticTokenModifiers.annotation,
212+
},
203213
);
204214
}
205215

@@ -293,7 +303,13 @@ class DartUnitHighlightsComputer {
293303
}
294304
// add region
295305
if (type != null) {
296-
return _addRegion_node(node, type);
306+
return _addRegion_node(
307+
node,
308+
type,
309+
semanticTokenModifiers: _isAnnotationIdentifier(node)
310+
? {CustomSemanticTokenModifiers.annotation}
311+
: null,
312+
);
297313
}
298314
return false;
299315
}
@@ -575,6 +591,18 @@ class DartUnitHighlightsComputer {
575591
_addRegion(offset, end - offset, type);
576592
}
577593

594+
/// Checks whether [node] is the identifier part of an annotation.
595+
bool _isAnnotationIdentifier(AstNode node) {
596+
if (node.parent is Annotation) {
597+
return true;
598+
} else if (node.parent is PrefixedIdentifier &&
599+
node.parent?.parent is Annotation) {
600+
return true;
601+
} else {
602+
return false;
603+
}
604+
}
605+
578606
void _reset() {
579607
_computeRegions = false;
580608
_computeSemanticTokens = false;

pkg/analysis_server/lib/src/lsp/constants.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ abstract class CustomMethods {
9292
}
9393

9494
abstract class CustomSemanticTokenModifiers {
95+
/// A modifier applied to the identifier following the `@` annotation token to
96+
/// allow users to color it differently (for example in the same way as `@`).
97+
static const annotation = SemanticTokenModifiers('annotation');
98+
9599
/// A modifier applied to control keywords like if/for/etc. so they can be
96100
/// colored differently to other keywords (void, import, etc), matching the
97101
/// original Dart textmate grammar.
@@ -127,8 +131,8 @@ abstract class CustomSemanticTokenModifiers {
127131
/// of the expression would show through the simple-colorings "string" colors.
128132
static const interpolation = SemanticTokenModifiers('interpolation');
129133

130-
/// A modifier applied to the void keyword to users to color it differently
131-
/// (for example as a type).
134+
/// A modifier applied to the void keyword to allow users to color it
135+
/// differently (for example as a type).
132136
static const void_ = SemanticTokenModifiers('void');
133137

134138
/// All custom semantic token modifiers, used to populate the LSP Legend.
@@ -137,6 +141,7 @@ abstract class CustomSemanticTokenModifiers {
137141
/// HighlightRegion mappings will be automatically included, but should still
138142
/// be listed here in case they are removed from mappings in the future.
139143
static const values = [
144+
annotation,
140145
control,
141146
label,
142147
constructor,

pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ final highlightRegionTokenTypes = {
103103
HighlightRegionType.ENUM_CONSTANT: SemanticTokenTypes.enumMember,
104104
HighlightRegionType.FUNCTION_TYPE_ALIAS: SemanticTokenTypes.type,
105105
HighlightRegionType.IDENTIFIER_DEFAULT: CustomSemanticTokenTypes.source,
106+
HighlightRegionType.IMPORT_PREFIX: SemanticTokenTypes.variable,
106107
HighlightRegionType.INSTANCE_FIELD_DECLARATION: SemanticTokenTypes.property,
107108
HighlightRegionType.INSTANCE_FIELD_REFERENCE: SemanticTokenTypes.property,
108109
HighlightRegionType.INSTANCE_GETTER_DECLARATION: SemanticTokenTypes.property,

pkg/analysis_server/test/lsp/semantic_tokens_test.dart

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,110 @@ class SemanticTokensTest extends AbstractLspAnalysisServerTest {
5454
return results;
5555
}
5656

57+
Future<void> test_annotation() async {
58+
final content = '''
59+
import 'other_file.dart' as other;
60+
61+
@a
62+
@A()
63+
@A.n()
64+
@B(A())
65+
@other.C()
66+
@other.C.n()
67+
void foo() {}
68+
69+
class A {
70+
const A();
71+
const A.n();
72+
}
73+
74+
const a = A();
75+
76+
class B {
77+
final A a;
78+
const B(this.a);
79+
}
80+
''';
81+
82+
final otherContent = '''
83+
class C {
84+
const C();
85+
const C.n();
86+
}
87+
''';
88+
89+
final expectedStart = [
90+
_Token('import', SemanticTokenTypes.keyword),
91+
_Token("'other_file.dart'", SemanticTokenTypes.string),
92+
_Token('as', SemanticTokenTypes.keyword),
93+
_Token('other', SemanticTokenTypes.variable),
94+
_Token('@', CustomSemanticTokenTypes.annotation),
95+
_Token('a', SemanticTokenTypes.property,
96+
[CustomSemanticTokenModifiers.annotation]),
97+
_Token('@', CustomSemanticTokenTypes.annotation),
98+
_Token('A', SemanticTokenTypes.class_,
99+
[CustomSemanticTokenModifiers.annotation]),
100+
_Token('(', CustomSemanticTokenTypes.annotation),
101+
_Token(')', CustomSemanticTokenTypes.annotation),
102+
_Token('@', CustomSemanticTokenTypes.annotation),
103+
_Token('A', SemanticTokenTypes.class_,
104+
[CustomSemanticTokenModifiers.annotation]),
105+
_Token('.', CustomSemanticTokenTypes.annotation),
106+
_Token('n', SemanticTokenTypes.method, [
107+
CustomSemanticTokenModifiers.constructor,
108+
CustomSemanticTokenModifiers.annotation
109+
]),
110+
_Token('(', CustomSemanticTokenTypes.annotation),
111+
_Token(')', CustomSemanticTokenTypes.annotation),
112+
_Token('@', CustomSemanticTokenTypes.annotation),
113+
_Token('B', SemanticTokenTypes.class_,
114+
[CustomSemanticTokenModifiers.annotation]),
115+
_Token('(', CustomSemanticTokenTypes.annotation),
116+
_Token('A', SemanticTokenTypes.class_,
117+
[CustomSemanticTokenModifiers.constructor]),
118+
_Token(')', CustomSemanticTokenTypes.annotation),
119+
_Token('@', CustomSemanticTokenTypes.annotation),
120+
_Token('other', SemanticTokenTypes.variable),
121+
_Token('.', CustomSemanticTokenTypes.annotation),
122+
_Token('C', SemanticTokenTypes.class_,
123+
[CustomSemanticTokenModifiers.annotation]),
124+
_Token('(', CustomSemanticTokenTypes.annotation),
125+
_Token(')', CustomSemanticTokenTypes.annotation),
126+
_Token('@', CustomSemanticTokenTypes.annotation),
127+
_Token('other', SemanticTokenTypes.variable),
128+
_Token('.', CustomSemanticTokenTypes.annotation),
129+
_Token('C', SemanticTokenTypes.class_,
130+
[CustomSemanticTokenModifiers.annotation]),
131+
_Token('.', CustomSemanticTokenTypes.annotation),
132+
_Token('n', SemanticTokenTypes.method, [
133+
CustomSemanticTokenModifiers.constructor,
134+
CustomSemanticTokenModifiers.annotation
135+
]),
136+
_Token('(', CustomSemanticTokenTypes.annotation),
137+
_Token(')', CustomSemanticTokenTypes.annotation),
138+
_Token('void', SemanticTokenTypes.keyword,
139+
[CustomSemanticTokenModifiers.void_]),
140+
_Token('foo', SemanticTokenTypes.function,
141+
[SemanticTokenModifiers.declaration, SemanticTokenModifiers.static])
142+
];
143+
144+
final otherFilePath = join(projectFolderPath, 'lib', 'other_file.dart');
145+
final otherFileUri = Uri.file(otherFilePath);
146+
147+
await initialize();
148+
await openFile(mainFileUri, withoutMarkers(content));
149+
await openFile(otherFileUri, withoutMarkers(otherContent));
150+
151+
final tokens = await getSemanticTokens(mainFileUri);
152+
final decoded = decodeSemanticTokens(content, tokens);
153+
expect(
154+
// Only check the first expectedStart.length items since the test code
155+
// is mostly unrelated to the annotations.
156+
decoded.sublist(0, expectedStart.length),
157+
equals(expectedStart),
158+
);
159+
}
160+
57161
Future<void> test_class() async {
58162
final content = '''
59163
/// class docs
@@ -305,7 +409,8 @@ class SemanticTokensTest extends AbstractLspAnalysisServerTest {
305409
_Token('/// method docs', SemanticTokenTypes.comment,
306410
[SemanticTokenModifiers.documentation]),
307411
_Token('@', CustomSemanticTokenTypes.annotation),
308-
_Token('override', SemanticTokenTypes.property),
412+
_Token('override', SemanticTokenTypes.property,
413+
[CustomSemanticTokenModifiers.annotation]),
309414
_Token('void', SemanticTokenTypes.keyword,
310415
[CustomSemanticTokenModifiers.void_]),
311416
_Token('myMethod', SemanticTokenTypes.method,

0 commit comments

Comments
 (0)