[analysis_server] Add highlights/semantic tokens for augmented()
+ tests for fetching semantic tokens for macro-generated sources.
Change-Id: I1811e5fd3eb3f5564a1d6f21348f9650e30eb536
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/368421
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights.dart b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
index 253d965..b444474 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
@@ -647,6 +647,20 @@
}
@override
+ void visitAugmentedExpression(AugmentedExpression node) {
+ computer._addRegion_token(
+ node.augmentedKeyword, HighlightRegionType.KEYWORD);
+ super.visitAugmentedExpression(node);
+ }
+
+ @override
+ void visitAugmentedInvocation(AugmentedInvocation node) {
+ computer._addRegion_token(
+ node.augmentedKeyword, HighlightRegionType.KEYWORD);
+ super.visitAugmentedInvocation(node);
+ }
+
+ @override
void visitAwaitExpression(AwaitExpression node) {
computer._addRegion_token(node.awaitKeyword, HighlightRegionType.BUILT_IN,
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
diff --git a/pkg/analysis_server/test/analysis/notification_highlights2_test.dart b/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
index cd9922f..11fe4ed 100644
--- a/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
@@ -1557,6 +1557,70 @@
assertHasRegion(HighlightRegionType.KEYWORD, 'with A;');
}
+ Future<void> test_KEYWORD_augmented_onInstanceGetter() async {
+ addTestFile('''
+class C {
+ augment int get g => augmented;
+}
+''');
+ await prepareHighlights();
+ assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
+ }
+
+ Future<void> test_KEYWORD_augmented_onInstanceMethod() async {
+ addTestFile('''
+class C {
+ augment int m() => augmented();
+}
+''');
+ await prepareHighlights();
+ assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
+ }
+
+ Future<void> test_KEYWORD_augmented_onInstanceSetter() async {
+ addTestFile('''
+class C {
+ augment set s(int x) { augmented = x; }
+}
+''');
+ await prepareHighlights();
+ assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
+ }
+
+ Future<void> test_KEYWORD_augmented_onStaticMethod() async {
+ addTestFile('''
+class C {
+ augment static int m() => augmented();
+}
+''');
+ await prepareHighlights();
+ assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
+ }
+
+ Future<void> test_KEYWORD_augmented_onTopLevelFunction() async {
+ addTestFile('''
+augment int f(int x) => augmented(x);
+''');
+ await prepareHighlights();
+ assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
+ }
+
+ Future<void> test_KEYWORD_augmented_onTopLevelGetter() async {
+ addTestFile('''
+augment int get g => augmented;
+''');
+ await prepareHighlights();
+ assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
+ }
+
+ Future<void> test_KEYWORD_augmented_onTopLevelSetter() async {
+ addTestFile('''
+augment set s(int x) { augmented = x; }
+''');
+ await prepareHighlights();
+ assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
+ }
+
Future<void> test_KEYWORD_const_constructor() async {
addTestFile('''
class A {
diff --git a/pkg/analysis_server/test/lsp/augmentation_test.dart b/pkg/analysis_server/test/lsp/augmentation_test.dart
index b98e146..d412648 100644
--- a/pkg/analysis_server/test/lsp/augmentation_test.dart
+++ b/pkg/analysis_server/test/lsp/augmentation_test.dart
@@ -18,8 +18,6 @@
@reflectiveTest
class AugmentationTest extends AbstractLspAnalysisServerTest {
- String get mainFileAugmentationPath => fromUri(mainFileAugmentationUri);
-
Future<void> test_class_body_augmentationToAugmentation() async {
await verifyGoToAugmentation('''
class A {}
diff --git a/pkg/analysis_server/test/lsp/augmented_test.dart b/pkg/analysis_server/test/lsp/augmented_test.dart
index b40fce9..9eaa4a2 100644
--- a/pkg/analysis_server/test/lsp/augmented_test.dart
+++ b/pkg/analysis_server/test/lsp/augmented_test.dart
@@ -18,8 +18,6 @@
@reflectiveTest
class AugmentedTest extends AbstractLspAnalysisServerTest {
- String get mainFileAugmentationPath => fromUri(mainFileAugmentationUri);
-
Future<void> test_class_body_augmentationToAugmentation() async {
await verifyGoToAugmented('''
class A {}
diff --git a/pkg/analysis_server/test/lsp/code_lens/augmentations_test.dart b/pkg/analysis_server/test/lsp/code_lens/augmentations_test.dart
index e7a3657..1853251 100644
--- a/pkg/analysis_server/test/lsp/code_lens/augmentations_test.dart
+++ b/pkg/analysis_server/test/lsp/code_lens/augmentations_test.dart
@@ -39,8 +39,6 @@
/// The title of the [Command] in the [CodeLens]es being tested.
String get codeLensTitle;
- String get mainFileAugmentationPath => fromUri(mainFileAugmentationUri);
-
/// The range in [sourceUri] that the CodeLens should appear for.
Range get sourceRange;
diff --git a/pkg/analysis_server/test/lsp/semantic_tokens_test.dart b/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
index 5d10f34..438604f 100644
--- a/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
+++ b/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
@@ -140,6 +140,87 @@
);
}
+ Future<void> test_augmentations() async {
+ var mainContent = '''
+import augment 'main_augmentation.dart';
+
+class A {
+ void f() {}
+ String get g => '';
+}
+''';
+
+ var augmentationContent = '''
+augment library 'main.dart';
+
+augment class A {
+ augment void f() {
+ augmented();
+ }
+ augment get g => augmented;
+}
+''';
+
+ newFile(mainFilePath, mainContent);
+ newFile(mainFileAugmentationPath, augmentationContent);
+ await initialize();
+
+ // Main library.
+ await _verifyTokens(mainFileUri, mainContent, [
+ _Token('import', SemanticTokenTypes.keyword),
+ _Token('augment', SemanticTokenTypes.keyword),
+ _Token("'main_augmentation.dart'", SemanticTokenTypes.string),
+ _Token('class', SemanticTokenTypes.keyword),
+ _Token(
+ 'A',
+ SemanticTokenTypes.class_,
+ [SemanticTokenModifiers.declaration],
+ ),
+ _Token(
+ 'void',
+ SemanticTokenTypes.keyword,
+ [CustomSemanticTokenModifiers.void_],
+ ),
+ _Token('f', SemanticTokenTypes.method, [
+ SemanticTokenModifiers.declaration,
+ CustomSemanticTokenModifiers.instance
+ ]),
+ _Token('String', SemanticTokenTypes.class_),
+ _Token('get', SemanticTokenTypes.keyword),
+ _Token('g', SemanticTokenTypes.property, [
+ SemanticTokenModifiers.declaration,
+ CustomSemanticTokenModifiers.instance
+ ]),
+ _Token("''", SemanticTokenTypes.string),
+ ]);
+
+ // Augmentation.
+ await _verifyTokens(mainFileAugmentationUri, augmentationContent, [
+ _Token('augment', SemanticTokenTypes.keyword),
+ _Token('library', SemanticTokenTypes.keyword),
+ _Token("'main.dart'", SemanticTokenTypes.string),
+ _Token('augment', SemanticTokenTypes.keyword),
+ _Token('class', SemanticTokenTypes.keyword),
+ _Token(
+ 'A', SemanticTokenTypes.class_, [SemanticTokenModifiers.declaration]),
+ _Token('augment', SemanticTokenTypes.keyword),
+ _Token('void', SemanticTokenTypes.keyword,
+ [CustomSemanticTokenModifiers.void_]),
+ _Token('f', SemanticTokenTypes.method, [
+ SemanticTokenModifiers.declaration,
+ CustomSemanticTokenModifiers.instance
+ ]),
+ _Token('augmented', SemanticTokenTypes.keyword),
+ _Token('augment', SemanticTokenTypes.keyword),
+ _Token('get', SemanticTokenTypes.keyword),
+ _Token('g', SemanticTokenTypes.property, [
+ SemanticTokenModifiers.declaration,
+ CustomSemanticTokenModifiers.instance
+ ]),
+ _Token('augmented', SemanticTokenTypes.keyword),
+ ]);
+ }
+
Future<void> test_class() async {
var content = '''
/// class docs
@@ -161,7 +242,7 @@
_Token('// Trailing comment', SemanticTokenTypes.comment),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_class_constructors() async {
@@ -240,7 +321,7 @@
[CustomSemanticTokenModifiers.constructor]),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_class_fields() async {
@@ -297,7 +378,7 @@
_Token("'a'", SemanticTokenTypes.string),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_class_getterSetter() async {
@@ -379,7 +460,7 @@
_Token("'a'", SemanticTokenTypes.string),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_class_method() async {
@@ -455,7 +536,7 @@
[SemanticTokenModifiers.static]),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_class_super() async {
@@ -506,7 +587,7 @@
])
];
- await _verifyTokensInRange(content, expected);
+ await _initializeAndVerifyTokensInRange(content, expected);
}
Future<void> test_class_this() async {
@@ -554,7 +635,7 @@
])
];
- await _verifyTokensInRange(content, expected);
+ await _initializeAndVerifyTokensInRange(content, expected);
}
Future<void> test_dartdoc() async {
@@ -598,7 +679,7 @@
_Token('2', SemanticTokenTypes.number)
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_directives() async {
@@ -637,7 +718,7 @@
_Token("'file_html.dart'", SemanticTokenTypes.string),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_extension() async {
@@ -652,7 +733,7 @@
_Token('String', SemanticTokenTypes.class_)
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_extensionType() async {
@@ -670,7 +751,7 @@
[SemanticTokenModifiers.declaration])
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_fromPlugin() async {
@@ -814,7 +895,7 @@
_Token('isEven', SemanticTokenTypes.variable),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_lastLine_code() async {
@@ -826,7 +907,7 @@
[SemanticTokenModifiers.declaration]),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_lastLine_comment() async {
@@ -836,7 +917,7 @@
_Token('// Trailing comment', SemanticTokenTypes.comment),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_lastLine_multilineComment() async {
@@ -854,7 +935,7 @@
[SemanticTokenModifiers.documentation]),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_local() async {
@@ -883,7 +964,61 @@
_Token('func', SemanticTokenTypes.function),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
+ }
+
+ /// Verify we can send requests for semantic tokens inside files generated
+ /// by macros (which are not file:/// scheme).
+ Future<void> test_macroGenerated() async {
+ setDartTextDocumentContentProviderSupport();
+ addMacros([declareInTypeMacro()]);
+
+ const mainContent = '''
+import 'macros.dart';
+
+@DeclareInType('void f() {}')
+class A {}
+''';
+
+ // Create the file and start up the server so that the macro-generated
+ // files is available.
+ newFile(mainFilePath, mainContent);
+ await Future.wait([
+ waitForAnalysisComplete(),
+ initialize(),
+ ]);
+
+ // Fetch the macro-generated content to ensure it was generated successfully
+ // but also because verifyTokens uses the content to map locations back to
+ // source code to simplify comparing the tokens.
+ var generatedFile = await getDartTextDocumentContent(mainFileMacroUri);
+ var generatedContent = generatedFile!.content!;
+
+ await _verifyTokens(mainFileMacroUri, generatedContent, [
+ _Token('augment', SemanticTokenTypes.keyword),
+ _Token('library', SemanticTokenTypes.keyword),
+ _Token("'package:test/main.dart'", SemanticTokenTypes.string),
+ _Token('augment', SemanticTokenTypes.keyword),
+ _Token('class', SemanticTokenTypes.keyword),
+ _Token(
+ 'A',
+ SemanticTokenTypes.class_,
+ [SemanticTokenModifiers.declaration],
+ ),
+ _Token(
+ 'void',
+ SemanticTokenTypes.keyword,
+ [CustomSemanticTokenModifiers.void_],
+ ),
+ _Token(
+ 'f',
+ SemanticTokenTypes.method,
+ [
+ SemanticTokenModifiers.declaration,
+ CustomSemanticTokenModifiers.instance
+ ],
+ )
+ ]);
}
Future<void> test_manyBools_bug() async {
@@ -936,7 +1071,7 @@
],
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_manyImports_sortBug() async {
@@ -969,7 +1104,7 @@
],
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_mixin() async {
@@ -988,7 +1123,7 @@
'C', SemanticTokenTypes.class_, [SemanticTokenModifiers.declaration])
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_multilineRegions() async {
@@ -1020,7 +1155,7 @@
[SemanticTokenModifiers.declaration]),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_namedArguments() async {
@@ -1042,7 +1177,7 @@
_Token('a', SemanticTokenTypes.parameter),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_never() async {
@@ -1066,7 +1201,7 @@
_Token("''", SemanticTokenTypes.string),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_patterns_assignment() async {
@@ -1102,7 +1237,7 @@
_Token('2', SemanticTokenTypes.number)
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_patterns_switch_list() async {
@@ -1134,7 +1269,7 @@
_Token('null', SemanticTokenTypes.keyword)
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_patterns_switch_object() async {
@@ -1167,7 +1302,7 @@
_Token('isEven', SemanticTokenTypes.variable),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_patterns_switch_object_inferredName() async {
@@ -1198,7 +1333,7 @@
_Token('isEven', SemanticTokenTypes.variable),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_range() async {
@@ -1218,7 +1353,7 @@
_Token('// class comment', SemanticTokenTypes.comment),
];
- await _verifyTokensInRange(content, expected);
+ await _initializeAndVerifyTokensInRange(content, expected);
}
Future<void> test_range_entireFile() async {
@@ -1242,7 +1377,7 @@
_Token('// Trailing comment', SemanticTokenTypes.comment),
];
- await _verifyTokensInRange(content, expected);
+ await _initializeAndVerifyTokensInRange(content, expected);
}
Future<void> test_range_multilineRegions() async {
@@ -1266,7 +1401,7 @@
_Token('class', SemanticTokenTypes.keyword),
];
- await _verifyTokensInRange(content, expected);
+ await _initializeAndVerifyTokensInRange(content, expected);
}
Future<void> test_record_fields() async {
@@ -1302,7 +1437,7 @@
_Token('unresolved', CustomSemanticTokenTypes.source),
];
- await _verifyTokensInRange(content, expected);
+ await _initializeAndVerifyTokensInRange(content, expected);
}
Future<void> test_sort_sameOffsets() async {
@@ -1327,7 +1462,7 @@
_Token("'", SemanticTokenTypes.string)
];
- await _verifyTokensInRange(content, expected);
+ await _initializeAndVerifyTokensInRange(content, expected);
}
Future<void> test_strings() async {
@@ -1394,7 +1529,7 @@
_Token("'''", SemanticTokenTypes.string),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_strings_escape() async {
@@ -1447,7 +1582,7 @@
_Token(r"\u{12345699}'", SemanticTokenTypes.string),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_topLevel() async {
@@ -1512,7 +1647,7 @@
_Token('funcTearOff', SemanticTokenTypes.property),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
Future<void> test_unresolvedOrInvalid() async {
@@ -1553,7 +1688,7 @@
_Token('baz', CustomSemanticTokenTypes.source),
];
- await _verifyTokens(content, expected);
+ await _initializeAndVerifyTokens(content, expected);
}
/// Decode tokens according to the LSP spec and pair with relevant file contents.
@@ -1588,26 +1723,52 @@
return results;
}
- Future<void> _verifyTokens(String content, List<_Token> expected) async {
+ /// Initializes the server with [content] in [uri] and then calls
+ /// [_verifyTokens] to check the semantic tokens match [expected].
+ Future<void> _initializeAndVerifyTokens(
+ String content,
+ List<_Token> expected, {
+ Uri? uri,
+ }) async {
+ uri ??= mainFileUri;
var code = TestCode.parse(content);
+ newFile(fromUri(uri), code.code);
await initialize();
- await openFile(mainFileUri, code.code);
- var tokens = await getSemanticTokens(mainFileUri);
- var decoded = _decodeSemanticTokens(content, tokens);
- expect(decoded, equals(expected));
+ await _verifyTokens(uri, content, expected);
}
- Future<void> _verifyTokensInRange(
- String content, List<_Token> expected) async {
+ /// Initializes the server with [content] in [uri] and then checks the
+ /// semantic tokens for the marked range match [expected].
+ Future<void> _initializeAndVerifyTokensInRange(
+ String content,
+ List<_Token> expected, {
+ Uri? uri,
+ }) async {
+ uri ??= mainFileUri;
var code = TestCode.parse(content);
+ newFile(fromUri(uri), code.code);
await initialize();
- await openFile(mainFileUri, code.code);
var tokens = await getSemanticTokensRange(mainFileUri, code.range.range);
var decoded = _decodeSemanticTokens(code.code, tokens);
expect(decoded, equals(expected));
}
+
+ /// Check the semantic tokens for [content] in [uri] match [expected].
+ ///
+ /// [content] is used to map the offsets in the response to the tokens and
+ /// is not sent to the server, so it must already match what the server
+ /// believes [uri] to contain.
+ Future<void> _verifyTokens(
+ Uri uri,
+ String content,
+ List<_Token> expected,
+ ) async {
+ var tokens = await getSemanticTokens(uri);
+ var decoded = _decodeSemanticTokens(content, tokens);
+ expect(decoded, equals(expected));
+ }
}
class _Token {
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index 71063ab..3175d62 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -67,6 +67,8 @@
DartFixPromptManager? get dartFixPromptManager => null;
+ String get mainFileAugmentationPath => fromUri(mainFileAugmentationUri);
+
/// The path that is not in [projectFolderPath], contains external packages.
@override
String get packagesRootPath => resourceProvider.convertPath('/packages');