[analysis_server] Improve coloring of invalid/unresolved methods in LSP semantic tokens

See https://github.com/Dart-Code/Dart-Code/issues/3412.

Change-Id: Icc44ff0537122d6d2211d64037666ecb58a28bd6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/203940
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Danny Tuppeny 2021-06-17 20:15:21 +00:00 committed by commit-bot@chromium.org
parent dc6875b64a
commit 75bcd5ca33
3 changed files with 96 additions and 1 deletions

View file

@ -100,6 +100,9 @@ class DartUnitHighlightsComputer {
if (_addIdentifierRegion_class(node)) {
return;
}
if (_addIdentifierRegion_extension(node)) {
return;
}
if (_addIdentifierRegion_constructor(node)) {
return;
}
@ -223,6 +226,29 @@ class DartUnitHighlightsComputer {
return false;
}
bool _addIdentifierRegion_extension(SimpleIdentifier node) {
var element = node.writeOrReadElement;
if (element is! ExtensionElement) {
return false;
}
// TODO(dantup): Right now there is no highlight type for extension, so
// bail out and do the default thing (which will be to return
// IDENTIFIER_DEFAULT). Adding EXTENSION requires coordination with
// IntelliJ + bumping protocol version.
if (!_computeSemanticTokens) {
return false;
}
return _addRegion_node(
node,
// TODO(dantup): Change this to EXTENSION and add to LSP mapping when
// we have it, but for now use CLASS (which is probably what we'll map it
// to for LSP semantic tokens anyway).
HighlightRegionType.CLASS,
);
}
bool _addIdentifierRegion_field(SimpleIdentifier node) {
var element = node.writeOrReadElement;
if (element is FieldFormalParameterElement) {

View file

@ -97,6 +97,7 @@ final highlightRegionTokenTypes = {
HighlightRegionType.ENUM: SemanticTokenTypes.enum_,
HighlightRegionType.ENUM_CONSTANT: SemanticTokenTypes.enumMember,
HighlightRegionType.FUNCTION_TYPE_ALIAS: SemanticTokenTypes.type,
HighlightRegionType.IDENTIFIER_DEFAULT: CustomSemanticTokenTypes.source,
HighlightRegionType.INSTANCE_FIELD_DECLARATION: SemanticTokenTypes.variable,
HighlightRegionType.INSTANCE_FIELD_REFERENCE: SemanticTokenTypes.variable,
HighlightRegionType.INSTANCE_GETTER_DECLARATION: SemanticTokenTypes.property,
@ -137,7 +138,7 @@ final highlightRegionTokenTypes = {
HighlightRegionType.TYPE_NAME_DYNAMIC: SemanticTokenTypes.type,
HighlightRegionType.TYPE_PARAMETER: SemanticTokenTypes.typeParameter,
HighlightRegionType.UNRESOLVED_INSTANCE_MEMBER_REFERENCE:
SemanticTokenTypes.variable,
CustomSemanticTokenTypes.source,
HighlightRegionType.VALID_STRING_ESCAPE: SemanticTokenTypes.string,
};

View file

@ -396,9 +396,15 @@ class SemanticTokensTest extends AbstractLspAnalysisServerTest {
_Token("'../file.dart'", SemanticTokenTypes.string),
_Token('if', SemanticTokenTypes.keyword,
[CustomSemanticTokenModifiers.control]),
_Token('dart', CustomSemanticTokenTypes.source),
_Token('library', CustomSemanticTokenTypes.source),
_Token('io', CustomSemanticTokenTypes.source),
_Token("'file_io.dart'", SemanticTokenTypes.string),
_Token('if', SemanticTokenTypes.keyword,
[CustomSemanticTokenModifiers.control]),
_Token('dart', CustomSemanticTokenTypes.source),
_Token('library', CustomSemanticTokenTypes.source),
_Token('html', CustomSemanticTokenTypes.source),
_Token("'file_html.dart'", SemanticTokenTypes.string),
_Token('library', SemanticTokenTypes.keyword),
_Token('foo', SemanticTokenTypes.namespace),
@ -412,6 +418,26 @@ class SemanticTokensTest extends AbstractLspAnalysisServerTest {
expect(decoded, equals(expected));
}
Future<void> test_extension() async {
final content = '''
extension A on String {}
''';
final expected = [
_Token('extension', SemanticTokenTypes.keyword),
_Token('A', SemanticTokenTypes.class_),
_Token('on', SemanticTokenTypes.keyword),
_Token('String', SemanticTokenTypes.class_)
];
await initialize();
await openFile(mainFileUri, withoutMarkers(content));
final tokens = await getSemanticTokens(mainFileUri);
final decoded = decodeSemanticTokens(content, tokens);
expect(decoded, equals(expected));
}
Future<void> test_fromPlugin() async {
final pluginAnalyzedFilePath = join(projectFolderPath, 'lib', 'foo.foo');
final pluginAnalyzedFileUri = Uri.file(pluginAnalyzedFilePath);
@ -1002,6 +1028,48 @@ const string3 = 'unicode \u1234\u123499\u{123456}\u{12345699}';
final decoded = decodeSemanticTokens(content, tokens);
expect(decoded, equals(expected));
}
Future<void> test_unresolvedOrInvalid() async {
// Unresolved/invalid names should be marked as "source", which is used to
// mark up code the server thinks should be uncolored (without this, a
// clients other grammars would show through, losing the benefit from having
// resolved the code).
final content = '''
main() {
int a;
a.foo().bar.baz();
dynamic b;
b.foo().bar.baz();
}
''';
final expected = [
_Token('main', SemanticTokenTypes.function,
[SemanticTokenModifiers.declaration, SemanticTokenModifiers.static]),
_Token('int', SemanticTokenTypes.class_),
_Token('a', SemanticTokenTypes.variable,
[SemanticTokenModifiers.declaration]),
_Token('a', SemanticTokenTypes.variable),
_Token('foo', CustomSemanticTokenTypes.source),
_Token('bar', CustomSemanticTokenTypes.source),
_Token('baz', CustomSemanticTokenTypes.source),
_Token('dynamic', SemanticTokenTypes.type),
_Token('b', SemanticTokenTypes.variable,
[SemanticTokenModifiers.declaration]),
_Token('b', SemanticTokenTypes.variable),
_Token('foo', CustomSemanticTokenTypes.source),
_Token('bar', CustomSemanticTokenTypes.source),
_Token('baz', CustomSemanticTokenTypes.source),
];
await initialize();
await openFile(mainFileUri, withoutMarkers(content));
final tokens = await getSemanticTokens(mainFileUri);
final decoded = decodeSemanticTokens(content, tokens);
expect(decoded, equals(expected));
}
}
class _Token {