edccf64f54
Change-Id: Idadb02aa1b86525fde307a5eddc8876f152d1b31 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/291045 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
1601 lines
52 KiB
Dart
1601 lines
52 KiB
Dart
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
import 'dart:math' as math;
|
|
|
|
import 'package:_fe_analyzer_shared/src/parser/quote.dart'
|
|
show analyzeQuote, Quote, firstQuoteLength, lastQuoteLength;
|
|
import 'package:_fe_analyzer_shared/src/scanner/characters.dart' as char;
|
|
import 'package:analysis_server/lsp_protocol/protocol.dart'
|
|
show SemanticTokenTypes, SemanticTokenModifiers;
|
|
import 'package:analysis_server/src/lsp/constants.dart'
|
|
show CustomSemanticTokenModifiers, CustomSemanticTokenTypes;
|
|
import 'package:analysis_server/src/lsp/semantic_tokens/encoder.dart'
|
|
show SemanticTokenInfo;
|
|
import 'package:analysis_server/src/lsp/semantic_tokens/mapping.dart'
|
|
show highlightRegionTokenModifiers, highlightRegionTokenTypes;
|
|
import 'package:analyzer/dart/ast/ast.dart';
|
|
import 'package:analyzer/dart/ast/token.dart';
|
|
import 'package:analyzer/dart/ast/visitor.dart';
|
|
import 'package:analyzer/dart/element/element.dart';
|
|
import 'package:analyzer/dart/element/type.dart';
|
|
import 'package:analyzer/source/source_range.dart';
|
|
import 'package:analyzer/src/dart/ast/extensions.dart';
|
|
import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element;
|
|
|
|
/// A computer for [HighlightRegion]s and LSP [SemanticTokenInfo] in a Dart [CompilationUnit].
|
|
class DartUnitHighlightsComputer {
|
|
final CompilationUnit _unit;
|
|
final SourceRange? range;
|
|
|
|
final _regions = <HighlightRegion>[];
|
|
final _semanticTokens = <SemanticTokenInfo>[];
|
|
bool _computeRegions = false;
|
|
bool _computeSemanticTokens = false;
|
|
|
|
/// Creates a computer for [HighlightRegion]s and LSP [SemanticTokenInfo] in a
|
|
/// Dart [CompilationUnit].
|
|
///
|
|
/// If [range] is supplied, tokens outside of this range will not be included
|
|
/// in results.
|
|
DartUnitHighlightsComputer(this._unit, {this.range});
|
|
|
|
/// Returns the computed highlight regions, not `null`.
|
|
List<HighlightRegion> compute() {
|
|
_reset();
|
|
_computeRegions = true;
|
|
_unit.accept(_DartUnitHighlightsComputerVisitor(this));
|
|
_addCommentRanges();
|
|
return _regions;
|
|
}
|
|
|
|
/// Returns the computed semantic tokens, not `null`.
|
|
List<SemanticTokenInfo> computeSemanticTokens() {
|
|
_reset();
|
|
_computeSemanticTokens = true;
|
|
_unit.accept(_DartUnitHighlightsComputerVisitor(this));
|
|
_addCommentRanges();
|
|
return _semanticTokens;
|
|
}
|
|
|
|
void _addCommentRanges() {
|
|
Token? token = _unit.beginToken;
|
|
while (token != null) {
|
|
Token? commentToken = token.precedingComments;
|
|
while (commentToken != null) {
|
|
HighlightRegionType? highlightType;
|
|
if (commentToken.type == TokenType.MULTI_LINE_COMMENT) {
|
|
if (commentToken.lexeme.startsWith('/**')) {
|
|
highlightType = HighlightRegionType.COMMENT_DOCUMENTATION;
|
|
} else {
|
|
highlightType = HighlightRegionType.COMMENT_BLOCK;
|
|
}
|
|
}
|
|
if (commentToken.type == TokenType.SINGLE_LINE_COMMENT) {
|
|
if (commentToken.lexeme.startsWith('///')) {
|
|
highlightType = HighlightRegionType.COMMENT_DOCUMENTATION;
|
|
} else {
|
|
highlightType = HighlightRegionType.COMMENT_END_OF_LINE;
|
|
}
|
|
}
|
|
if (highlightType != null) {
|
|
_addRegion_token(commentToken, highlightType);
|
|
}
|
|
commentToken = commentToken.next;
|
|
}
|
|
if (token.isEof) {
|
|
// Only exit the loop *after* processing the EOF token as it may
|
|
// have preceding comments.
|
|
break;
|
|
}
|
|
token = token.next;
|
|
}
|
|
}
|
|
|
|
void _addIdentifierRegion(SimpleIdentifier node) {
|
|
if (_addIdentifierRegion_keyword(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_class(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_extension(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_constructor(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_getterSetterDeclaration(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_field(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_dynamicLocal(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_function(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_importPrefix(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_label(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_localVariable(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_method(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_parameter(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_typeAlias(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_typeParameter(node)) {
|
|
return;
|
|
}
|
|
if (_addIdentifierRegion_unresolvedInstanceMemberReference(node)) {
|
|
return;
|
|
}
|
|
_addRegion_node(node, HighlightRegionType.IDENTIFIER_DEFAULT);
|
|
}
|
|
|
|
void _addIdentifierRegion_annotation(Annotation node) {
|
|
var arguments = node.arguments;
|
|
if (arguments == null) {
|
|
_addRegion_node(node, HighlightRegionType.ANNOTATION);
|
|
} else {
|
|
_addRegion_nodeStart_tokenEnd(
|
|
node, arguments.beginToken, HighlightRegionType.ANNOTATION);
|
|
_addRegion_token(arguments.endToken, HighlightRegionType.ANNOTATION);
|
|
}
|
|
}
|
|
|
|
bool _addIdentifierRegion_class(SimpleIdentifier node) {
|
|
var element = node.writeOrReadElement;
|
|
if (element is! InterfaceElement) {
|
|
return false;
|
|
}
|
|
// prepare type
|
|
HighlightRegionType type;
|
|
SemanticTokenTypes? semanticType;
|
|
Set<SemanticTokenModifiers>? semanticModifiers;
|
|
var parent = node.parent;
|
|
var grandParent = parent?.parent;
|
|
if (parent is NamedType &&
|
|
grandParent is ConstructorName &&
|
|
grandParent.parent is InstanceCreationExpression) {
|
|
// new Class()
|
|
type = HighlightRegionType.CONSTRUCTOR;
|
|
semanticType = SemanticTokenTypes.class_;
|
|
semanticModifiers = {CustomSemanticTokenModifiers.constructor};
|
|
} else if (element is EnumElement) {
|
|
type = HighlightRegionType.ENUM;
|
|
} else {
|
|
type = HighlightRegionType.CLASS;
|
|
}
|
|
|
|
if (_isAnnotationIdentifier(node)) {
|
|
semanticModifiers ??= {};
|
|
semanticModifiers.add(CustomSemanticTokenModifiers.annotation);
|
|
}
|
|
|
|
// add region
|
|
return _addRegion_node(
|
|
node,
|
|
type,
|
|
semanticTokenType: semanticType,
|
|
semanticTokenModifiers: semanticModifiers,
|
|
);
|
|
}
|
|
|
|
bool _addIdentifierRegion_constructor(SimpleIdentifier node) {
|
|
var element = node.writeOrReadElement;
|
|
if (element is! ConstructorElement) {
|
|
return false;
|
|
}
|
|
return _addRegion_node(
|
|
node,
|
|
HighlightRegionType.CONSTRUCTOR,
|
|
// For semantic tokens, constructor names are coloured like methods but
|
|
// have a modifier applied.
|
|
semanticTokenType: SemanticTokenTypes.method,
|
|
semanticTokenModifiers: {
|
|
CustomSemanticTokenModifiers.constructor,
|
|
if (_isAnnotationIdentifier(node))
|
|
CustomSemanticTokenModifiers.annotation,
|
|
},
|
|
);
|
|
}
|
|
|
|
bool _addIdentifierRegion_dynamicLocal(SimpleIdentifier node) {
|
|
var element = node.writeOrReadElement;
|
|
if (element is LocalVariableElement) {
|
|
var elementType = element.type;
|
|
if (elementType.isDynamic) {
|
|
var type = node.inDeclarationContext()
|
|
? HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_DECLARATION
|
|
: HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_REFERENCE;
|
|
return _addRegion_node(node, type);
|
|
}
|
|
}
|
|
if (element is ParameterElement) {
|
|
var elementType = element.type;
|
|
if (elementType.isDynamic) {
|
|
var type = node.inDeclarationContext()
|
|
? HighlightRegionType.DYNAMIC_PARAMETER_DECLARATION
|
|
: HighlightRegionType.DYNAMIC_PARAMETER_REFERENCE;
|
|
return _addRegion_node(node, type);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool _addIdentifierRegion_extension(SimpleIdentifier node) {
|
|
var element = node.writeOrReadElement;
|
|
if (element is! ExtensionElement) {
|
|
return false;
|
|
}
|
|
|
|
return _addRegion_node(
|
|
node,
|
|
HighlightRegionType.EXTENSION,
|
|
);
|
|
}
|
|
|
|
bool _addIdentifierRegion_field(SimpleIdentifier node) {
|
|
var element = node.writeOrReadElement;
|
|
// prepare type
|
|
HighlightRegionType? type;
|
|
if (element is FieldElement) {
|
|
if (element.isEnumConstant) {
|
|
type = HighlightRegionType.ENUM_CONSTANT;
|
|
} else if (element.isStatic) {
|
|
type = HighlightRegionType.STATIC_FIELD_DECLARATION;
|
|
} else {
|
|
type = node.inDeclarationContext()
|
|
? HighlightRegionType.INSTANCE_FIELD_DECLARATION
|
|
: HighlightRegionType.INSTANCE_FIELD_REFERENCE;
|
|
}
|
|
} else if (element is TopLevelVariableElement) {
|
|
type = HighlightRegionType.TOP_LEVEL_VARIABLE_DECLARATION;
|
|
}
|
|
if (element is PropertyAccessorElement) {
|
|
var accessor = element;
|
|
var variable = accessor.variable;
|
|
if (variable is TopLevelVariableElement) {
|
|
type = accessor.isGetter
|
|
? HighlightRegionType.TOP_LEVEL_GETTER_REFERENCE
|
|
: HighlightRegionType.TOP_LEVEL_SETTER_REFERENCE;
|
|
} else if (variable is FieldElement && variable.isEnumConstant) {
|
|
type = HighlightRegionType.ENUM_CONSTANT;
|
|
} else if (accessor.isStatic) {
|
|
type = accessor.isGetter
|
|
? HighlightRegionType.STATIC_GETTER_REFERENCE
|
|
: HighlightRegionType.STATIC_SETTER_REFERENCE;
|
|
} else {
|
|
type = accessor.isGetter
|
|
? HighlightRegionType.INSTANCE_GETTER_REFERENCE
|
|
: HighlightRegionType.INSTANCE_SETTER_REFERENCE;
|
|
}
|
|
}
|
|
// add region
|
|
if (type != null) {
|
|
return _addRegion_node(
|
|
node,
|
|
type,
|
|
semanticTokenModifiers: _isAnnotationIdentifier(node)
|
|
? {CustomSemanticTokenModifiers.annotation}
|
|
: null,
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool _addIdentifierRegion_function(SimpleIdentifier node) {
|
|
var element = node.writeOrReadElement;
|
|
if (element is! FunctionElement) {
|
|
return false;
|
|
}
|
|
var parent = node.parent;
|
|
var isInvocation = parent is MethodInvocation && parent.methodName == node;
|
|
HighlightRegionType type;
|
|
var isTopLevel = element.enclosingElement is CompilationUnitElement;
|
|
if (node.inDeclarationContext()) {
|
|
type = isTopLevel
|
|
? HighlightRegionType.TOP_LEVEL_FUNCTION_DECLARATION
|
|
: HighlightRegionType.LOCAL_FUNCTION_DECLARATION;
|
|
} else {
|
|
type = isTopLevel
|
|
? isInvocation
|
|
? HighlightRegionType.TOP_LEVEL_FUNCTION_REFERENCE
|
|
: HighlightRegionType.TOP_LEVEL_FUNCTION_TEAR_OFF
|
|
: isInvocation
|
|
? HighlightRegionType.LOCAL_FUNCTION_REFERENCE
|
|
: HighlightRegionType.LOCAL_FUNCTION_TEAR_OFF;
|
|
}
|
|
return _addRegion_node(node, type);
|
|
}
|
|
|
|
bool _addIdentifierRegion_getterSetterDeclaration(SimpleIdentifier node) {
|
|
// should be declaration
|
|
var parent = node.parent;
|
|
if (!(parent is MethodDeclaration || parent is FunctionDeclaration)) {
|
|
return false;
|
|
}
|
|
// should be property accessor
|
|
var element = node.writeOrReadElement;
|
|
if (element is! PropertyAccessorElement) {
|
|
return false;
|
|
}
|
|
// getter or setter
|
|
var isTopLevel = element.enclosingElement is CompilationUnitElement;
|
|
HighlightRegionType type;
|
|
if (element.isGetter) {
|
|
if (isTopLevel) {
|
|
type = HighlightRegionType.TOP_LEVEL_GETTER_DECLARATION;
|
|
} else if (element.isStatic) {
|
|
type = HighlightRegionType.STATIC_GETTER_DECLARATION;
|
|
} else {
|
|
type = HighlightRegionType.INSTANCE_GETTER_DECLARATION;
|
|
}
|
|
} else {
|
|
if (isTopLevel) {
|
|
type = HighlightRegionType.TOP_LEVEL_SETTER_DECLARATION;
|
|
} else if (element.isStatic) {
|
|
type = HighlightRegionType.STATIC_SETTER_DECLARATION;
|
|
} else {
|
|
type = HighlightRegionType.INSTANCE_SETTER_DECLARATION;
|
|
}
|
|
}
|
|
return _addRegion_node(node, type);
|
|
}
|
|
|
|
bool _addIdentifierRegion_importPrefix(SimpleIdentifier node) {
|
|
var element = node.writeOrReadElement;
|
|
if (element is! PrefixElement) {
|
|
return false;
|
|
}
|
|
return _addRegion_node(node, HighlightRegionType.IMPORT_PREFIX);
|
|
}
|
|
|
|
bool _addIdentifierRegion_keyword(SimpleIdentifier node) {
|
|
var name = node.name;
|
|
if (name == 'void') {
|
|
return _addRegion_node(
|
|
node,
|
|
HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.void_},
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool _addIdentifierRegion_label(SimpleIdentifier node) {
|
|
var element = node.writeOrReadElement;
|
|
if (element is! LabelElement) {
|
|
return false;
|
|
}
|
|
return _addRegion_node(node, HighlightRegionType.LABEL);
|
|
}
|
|
|
|
bool _addIdentifierRegion_localVariable(SimpleIdentifier node) {
|
|
var element = node.writeOrReadElement;
|
|
if (element is! LocalVariableElement) {
|
|
return false;
|
|
}
|
|
// OK
|
|
var type = node.inDeclarationContext()
|
|
? HighlightRegionType.LOCAL_VARIABLE_DECLARATION
|
|
: HighlightRegionType.LOCAL_VARIABLE_REFERENCE;
|
|
return _addRegion_node(node, type);
|
|
}
|
|
|
|
bool _addIdentifierRegion_method(SimpleIdentifier node) {
|
|
var element = node.writeOrReadElement;
|
|
if (element is! MethodElement) {
|
|
return false;
|
|
}
|
|
var isStatic = element.isStatic;
|
|
var parent = node.parent;
|
|
var isInvocation = parent is MethodInvocation && parent.methodName == node;
|
|
// OK
|
|
HighlightRegionType type;
|
|
if (node.inDeclarationContext()) {
|
|
if (isStatic) {
|
|
type = HighlightRegionType.STATIC_METHOD_DECLARATION;
|
|
} else {
|
|
type = HighlightRegionType.INSTANCE_METHOD_DECLARATION;
|
|
}
|
|
} else {
|
|
if (isStatic) {
|
|
type = isInvocation
|
|
? HighlightRegionType.STATIC_METHOD_REFERENCE
|
|
: HighlightRegionType.STATIC_METHOD_TEAR_OFF;
|
|
} else {
|
|
type = isInvocation
|
|
? HighlightRegionType.INSTANCE_METHOD_REFERENCE
|
|
: HighlightRegionType.INSTANCE_METHOD_TEAR_OFF;
|
|
}
|
|
}
|
|
return _addRegion_node(node, type);
|
|
}
|
|
|
|
bool _addIdentifierRegion_parameter(SimpleIdentifier node) {
|
|
var element = node.writeOrReadElement;
|
|
if (element is! ParameterElement) {
|
|
return false;
|
|
}
|
|
var type = node.inDeclarationContext()
|
|
? HighlightRegionType.PARAMETER_DECLARATION
|
|
: HighlightRegionType.PARAMETER_REFERENCE;
|
|
var modifiers =
|
|
node.parent is Label ? {CustomSemanticTokenModifiers.label} : null;
|
|
return _addRegion_node(node, type, semanticTokenModifiers: modifiers);
|
|
}
|
|
|
|
bool _addIdentifierRegion_typeAlias(SimpleIdentifier node) {
|
|
var element = node.writeOrReadElement;
|
|
if (element is TypeAliasElement) {
|
|
var type = element.aliasedType is FunctionType
|
|
? HighlightRegionType.FUNCTION_TYPE_ALIAS
|
|
: HighlightRegionType.TYPE_ALIAS;
|
|
return _addRegion_node(node, type);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool _addIdentifierRegion_typeParameter(SimpleIdentifier node) {
|
|
var element = node.writeOrReadElement;
|
|
if (element is! TypeParameterElement) {
|
|
return false;
|
|
}
|
|
return _addRegion_node(node, HighlightRegionType.TYPE_PARAMETER);
|
|
}
|
|
|
|
bool _addIdentifierRegion_unresolvedInstanceMemberReference(
|
|
SimpleIdentifier node) {
|
|
// unresolved
|
|
var element = node.writeOrReadElement;
|
|
if (element != null) {
|
|
return false;
|
|
}
|
|
// invoke / get / set
|
|
var decorate = false;
|
|
var parent = node.parent;
|
|
if (parent is MethodInvocation) {
|
|
var target = parent.realTarget;
|
|
if (parent.methodName == node &&
|
|
target != null &&
|
|
_isDynamicExpression(target)) {
|
|
decorate = true;
|
|
}
|
|
} else if (node.inGetterContext() || node.inSetterContext()) {
|
|
if (parent is PrefixedIdentifier) {
|
|
decorate = parent.identifier == node;
|
|
} else if (parent is PropertyAccess) {
|
|
decorate = parent.propertyName == node;
|
|
}
|
|
}
|
|
if (decorate) {
|
|
_addRegion_node(
|
|
node, HighlightRegionType.UNRESOLVED_INSTANCE_MEMBER_REFERENCE);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Adds a highlight region/semantic token for the given [offset]/[length].
|
|
///
|
|
/// If [semanticTokenType] or [semanticTokenModifiers] are not provided, the
|
|
/// values from the default LSP mapping for [type] (also used for plugins)
|
|
/// will be used instead.
|
|
///
|
|
/// If the computer has a [range] set, tokens that fall outside of that range
|
|
/// will not be recorded.
|
|
void _addRegion(
|
|
int offset,
|
|
int length,
|
|
HighlightRegionType type, {
|
|
SemanticTokenTypes? semanticTokenType,
|
|
Set<SemanticTokenModifiers>? semanticTokenModifiers,
|
|
}) {
|
|
final range = this.range;
|
|
if (range != null) {
|
|
final end = offset + length;
|
|
// Skip token if it ends before the range of starts after the range.
|
|
if (end < range.offset || offset > range.end) {
|
|
return;
|
|
}
|
|
}
|
|
if (_computeRegions) {
|
|
_regions.add(HighlightRegion(type, offset, length));
|
|
}
|
|
if (_computeSemanticTokens) {
|
|
// Use default mappings if an overridden type/modifiers were not supplied.
|
|
semanticTokenType ??= highlightRegionTokenTypes[type];
|
|
semanticTokenModifiers ??= highlightRegionTokenModifiers[type];
|
|
if (semanticTokenType != null) {
|
|
_semanticTokens.add(SemanticTokenInfo(
|
|
offset, length, semanticTokenType, semanticTokenModifiers));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool _addRegion_node(
|
|
AstNode node,
|
|
HighlightRegionType type, {
|
|
SemanticTokenTypes? semanticTokenType,
|
|
Set<SemanticTokenModifiers>? semanticTokenModifiers,
|
|
}) {
|
|
var offset = node.offset;
|
|
var length = node.length;
|
|
_addRegion(
|
|
offset,
|
|
length,
|
|
type,
|
|
semanticTokenType: semanticTokenType,
|
|
semanticTokenModifiers: semanticTokenModifiers,
|
|
);
|
|
return true;
|
|
}
|
|
|
|
void _addRegion_nodeStart_tokenEnd(
|
|
AstNode a, Token b, HighlightRegionType type) {
|
|
var offset = a.offset;
|
|
var end = b.end;
|
|
_addRegion(offset, end - offset, type);
|
|
}
|
|
|
|
void _addRegion_token(
|
|
Token? token,
|
|
HighlightRegionType type, {
|
|
SemanticTokenTypes? semanticTokenType,
|
|
Set<SemanticTokenModifiers>? semanticTokenModifiers,
|
|
}) {
|
|
if (token != null) {
|
|
var offset = token.offset;
|
|
var length = token.length;
|
|
_addRegion(offset, length, type,
|
|
semanticTokenType: semanticTokenType,
|
|
semanticTokenModifiers: semanticTokenModifiers);
|
|
}
|
|
}
|
|
|
|
void _addRegion_tokenStart_tokenEnd(
|
|
Token a, Token b, HighlightRegionType type) {
|
|
var offset = a.offset;
|
|
var end = b.end;
|
|
_addRegion(offset, end - offset, type);
|
|
}
|
|
|
|
/// Checks whether [node] is the identifier part of an annotation.
|
|
bool _isAnnotationIdentifier(AstNode node) {
|
|
if (node.parent is Annotation) {
|
|
return true;
|
|
} else if (node.parent is PrefixedIdentifier &&
|
|
node.parent?.parent is Annotation) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void _reset() {
|
|
_computeRegions = false;
|
|
_computeSemanticTokens = false;
|
|
_regions.clear();
|
|
_semanticTokens.clear();
|
|
}
|
|
|
|
static bool _isDynamicExpression(Expression e) {
|
|
var type = e.staticType;
|
|
return type != null && type.isDynamic;
|
|
}
|
|
}
|
|
|
|
/// An AST visitor for [DartUnitHighlightsComputer].
|
|
class _DartUnitHighlightsComputerVisitor extends RecursiveAstVisitor<void> {
|
|
final DartUnitHighlightsComputer computer;
|
|
|
|
_DartUnitHighlightsComputerVisitor(this.computer);
|
|
|
|
@override
|
|
void visitAnnotation(Annotation node) {
|
|
computer._addIdentifierRegion_annotation(node);
|
|
super.visitAnnotation(node);
|
|
}
|
|
|
|
@override
|
|
void visitAsExpression(AsExpression node) {
|
|
computer._addRegion_token(node.asOperator, HighlightRegionType.BUILT_IN);
|
|
super.visitAsExpression(node);
|
|
}
|
|
|
|
@override
|
|
void visitAssertStatement(AssertStatement node) {
|
|
computer._addRegion_token(node.assertKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitAssertStatement(node);
|
|
}
|
|
|
|
@override
|
|
void visitAssignedVariablePattern(AssignedVariablePattern node) {
|
|
computer._addRegion_token(
|
|
node.name, HighlightRegionType.LOCAL_VARIABLE_REFERENCE);
|
|
super.visitAssignedVariablePattern(node);
|
|
}
|
|
|
|
@override
|
|
void visitAwaitExpression(AwaitExpression node) {
|
|
computer._addRegion_token(node.awaitKeyword, HighlightRegionType.BUILT_IN,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitAwaitExpression(node);
|
|
}
|
|
|
|
@override
|
|
void visitBlockFunctionBody(BlockFunctionBody node) {
|
|
_addRegions_functionBody(node);
|
|
super.visitBlockFunctionBody(node);
|
|
}
|
|
|
|
@override
|
|
void visitBooleanLiteral(BooleanLiteral node) {
|
|
computer._addRegion_node(node, HighlightRegionType.KEYWORD);
|
|
computer._addRegion_node(node, HighlightRegionType.LITERAL_BOOLEAN);
|
|
super.visitBooleanLiteral(node);
|
|
}
|
|
|
|
@override
|
|
void visitBreakStatement(BreakStatement node) {
|
|
computer._addRegion_token(node.breakKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitBreakStatement(node);
|
|
}
|
|
|
|
@override
|
|
void visitCaseClause(CaseClause node) {
|
|
computer._addRegion_token(node.caseKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitCaseClause(node);
|
|
}
|
|
|
|
@override
|
|
void visitCastPattern(CastPattern node) {
|
|
computer._addRegion_token(node.asToken, HighlightRegionType.BUILT_IN);
|
|
super.visitCastPattern(node);
|
|
}
|
|
|
|
@override
|
|
void visitCatchClause(CatchClause node) {
|
|
computer._addRegion_token(node.catchKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
computer._addRegion_token(node.onKeyword, HighlightRegionType.BUILT_IN,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
|
|
var exceptionParameter = node.exceptionParameter;
|
|
if (exceptionParameter != null) {
|
|
computer._addRegion_token(exceptionParameter.name,
|
|
HighlightRegionType.LOCAL_VARIABLE_DECLARATION);
|
|
}
|
|
var stackTraceParameter = node.stackTraceParameter;
|
|
if (stackTraceParameter != null) {
|
|
computer._addRegion_token(stackTraceParameter.name,
|
|
HighlightRegionType.LOCAL_VARIABLE_DECLARATION);
|
|
}
|
|
|
|
super.visitCatchClause(node);
|
|
}
|
|
|
|
@override
|
|
void visitClassDeclaration(ClassDeclaration node) {
|
|
computer._addRegion_token(node.inlineKeyword, HighlightRegionType.KEYWORD);
|
|
computer._addRegion_token(
|
|
node.abstractKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(node.sealedKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(node.baseKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(
|
|
node.interfaceKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(node.finalKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(node.mixinKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(node.classKeyword, HighlightRegionType.KEYWORD);
|
|
computer._addRegion_token(node.name, HighlightRegionType.CLASS);
|
|
super.visitClassDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
void visitClassTypeAlias(ClassTypeAlias node) {
|
|
computer._addRegion_token(
|
|
node.abstractKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(node.sealedKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(node.baseKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(
|
|
node.interfaceKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(node.finalKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(node.mixinKeyword, HighlightRegionType.BUILT_IN);
|
|
super.visitClassTypeAlias(node);
|
|
}
|
|
|
|
@override
|
|
void visitConstantPattern(ConstantPattern node) {
|
|
computer._addRegion_token(node.constKeyword, HighlightRegionType.KEYWORD);
|
|
super.visitConstantPattern(node);
|
|
}
|
|
|
|
@override
|
|
void visitConstructorDeclaration(ConstructorDeclaration node) {
|
|
computer._addRegion_token(
|
|
node.externalKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(
|
|
node.factoryKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(node.constKeyword, HighlightRegionType.KEYWORD);
|
|
computer._addRegion_token(
|
|
node.name,
|
|
HighlightRegionType.CONSTRUCTOR,
|
|
semanticTokenType: SemanticTokenTypes.method,
|
|
semanticTokenModifiers: {
|
|
CustomSemanticTokenModifiers.constructor,
|
|
},
|
|
);
|
|
super.visitConstructorDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
void visitConstructorReference(ConstructorReference node) {
|
|
var constructorName = node.constructorName;
|
|
constructorName.type.accept(this);
|
|
|
|
// We have a `ConstructorReference` only when it is resolved.
|
|
// TODO(scheglov) The `ConstructorName` in a tear-off always has a name,
|
|
// but this is not expressed via types.
|
|
computer._addRegion_node(
|
|
constructorName.name!, HighlightRegionType.CONSTRUCTOR_TEAR_OFF);
|
|
}
|
|
|
|
@override
|
|
void visitConstructorSelector(ConstructorSelector node) {
|
|
computer._addRegion_node(
|
|
node.name,
|
|
HighlightRegionType.CONSTRUCTOR,
|
|
);
|
|
node.visitChildren(this);
|
|
}
|
|
|
|
@override
|
|
void visitContinueStatement(ContinueStatement node) {
|
|
computer._addRegion_token(node.continueKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitContinueStatement(node);
|
|
}
|
|
|
|
@override
|
|
void visitDeclaredIdentifier(DeclaredIdentifier node) {
|
|
computer._addRegion_token(node.keyword, HighlightRegionType.KEYWORD);
|
|
|
|
computer._addRegion_token(
|
|
node.name,
|
|
HighlightRegionType.LOCAL_VARIABLE_DECLARATION,
|
|
);
|
|
|
|
super.visitDeclaredIdentifier(node);
|
|
}
|
|
|
|
@override
|
|
void visitDeclaredVariablePattern(DeclaredVariablePattern node) {
|
|
computer._addRegion_token(node.keyword, HighlightRegionType.KEYWORD);
|
|
computer._addRegion_token(
|
|
node.name, HighlightRegionType.LOCAL_VARIABLE_DECLARATION);
|
|
super.visitDeclaredVariablePattern(node);
|
|
}
|
|
|
|
@override
|
|
void visitDefaultFormalParameter(DefaultFormalParameter node) {
|
|
computer._addRegion_token(
|
|
node.requiredKeyword, HighlightRegionType.KEYWORD);
|
|
super.visitDefaultFormalParameter(node);
|
|
}
|
|
|
|
@override
|
|
void visitDoStatement(DoStatement node) {
|
|
computer._addRegion_token(node.doKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
computer._addRegion_token(node.whileKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitDoStatement(node);
|
|
}
|
|
|
|
@override
|
|
void visitDoubleLiteral(DoubleLiteral node) {
|
|
computer._addRegion_node(node, HighlightRegionType.LITERAL_DOUBLE);
|
|
super.visitDoubleLiteral(node);
|
|
}
|
|
|
|
@override
|
|
void visitEnumConstantDeclaration(EnumConstantDeclaration node) {
|
|
computer._addRegion_token(
|
|
node.name,
|
|
HighlightRegionType.ENUM_CONSTANT,
|
|
);
|
|
node.visitChildren(this);
|
|
}
|
|
|
|
@override
|
|
void visitEnumDeclaration(EnumDeclaration node) {
|
|
computer._addRegion_token(node.enumKeyword, HighlightRegionType.KEYWORD);
|
|
computer._addRegion_token(node.name, HighlightRegionType.ENUM);
|
|
super.visitEnumDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
void visitExportDirective(ExportDirective node) {
|
|
computer._addRegion_node(node, HighlightRegionType.DIRECTIVE);
|
|
computer._addRegion_token(node.exportKeyword, HighlightRegionType.BUILT_IN);
|
|
_addRegions_configurations(node.configurations);
|
|
super.visitExportDirective(node);
|
|
}
|
|
|
|
@override
|
|
void visitExpressionFunctionBody(ExpressionFunctionBody node) {
|
|
_addRegions_functionBody(node);
|
|
super.visitExpressionFunctionBody(node);
|
|
}
|
|
|
|
@override
|
|
void visitExtendsClause(ExtendsClause node) {
|
|
computer._addRegion_token(node.extendsKeyword, HighlightRegionType.KEYWORD);
|
|
super.visitExtendsClause(node);
|
|
}
|
|
|
|
@override
|
|
void visitExtensionDeclaration(ExtensionDeclaration node) {
|
|
computer._addRegion_token(
|
|
node.extensionKeyword, HighlightRegionType.KEYWORD);
|
|
computer._addRegion_token(node.name, HighlightRegionType.EXTENSION);
|
|
computer._addRegion_token(node.onKeyword, HighlightRegionType.BUILT_IN);
|
|
super.visitExtensionDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
void visitFieldDeclaration(FieldDeclaration node) {
|
|
computer._addRegion_token(
|
|
node.abstractKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(
|
|
node.externalKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(node.staticKeyword, HighlightRegionType.BUILT_IN);
|
|
|
|
super.visitFieldDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
void visitFieldFormalParameter(FieldFormalParameter node) {
|
|
computer._addRegion_token(
|
|
node.requiredKeyword,
|
|
HighlightRegionType.KEYWORD,
|
|
);
|
|
|
|
computer._addRegion_token(
|
|
node.thisKeyword,
|
|
HighlightRegionType.KEYWORD,
|
|
);
|
|
|
|
var element = node.declaredElement;
|
|
if (element is FieldFormalParameterElement) {
|
|
var field = element.field;
|
|
if (field != null) {
|
|
computer._addRegion_token(
|
|
node.name,
|
|
HighlightRegionType.INSTANCE_FIELD_REFERENCE,
|
|
);
|
|
}
|
|
}
|
|
|
|
super.visitFieldFormalParameter(node);
|
|
}
|
|
|
|
@override
|
|
void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) {
|
|
computer._addRegion_token(node.inKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitForEachPartsWithDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
void visitForEachPartsWithIdentifier(ForEachPartsWithIdentifier node) {
|
|
computer._addRegion_token(node.inKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitForEachPartsWithIdentifier(node);
|
|
}
|
|
|
|
@override
|
|
void visitForEachPartsWithPattern(ForEachPartsWithPattern node) {
|
|
computer._addRegion_token(node.keyword, HighlightRegionType.KEYWORD);
|
|
super.visitForEachPartsWithPattern(node);
|
|
}
|
|
|
|
@override
|
|
void visitForElement(ForElement node) {
|
|
computer._addRegion_token(node.awaitKeyword, HighlightRegionType.BUILT_IN,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
computer._addRegion_token(node.forKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitForElement(node);
|
|
}
|
|
|
|
@override
|
|
void visitForStatement(ForStatement node) {
|
|
computer._addRegion_token(node.awaitKeyword, HighlightRegionType.BUILT_IN,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
computer._addRegion_token(node.forKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitForStatement(node);
|
|
}
|
|
|
|
@override
|
|
void visitFunctionDeclaration(FunctionDeclaration node) {
|
|
computer._addRegion_token(
|
|
node.externalKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(
|
|
node.propertyKeyword, HighlightRegionType.BUILT_IN);
|
|
|
|
final HighlightRegionType nameType;
|
|
if (node.isGetter) {
|
|
nameType = HighlightRegionType.TOP_LEVEL_GETTER_DECLARATION;
|
|
} else if (node.isSetter) {
|
|
nameType = HighlightRegionType.TOP_LEVEL_SETTER_DECLARATION;
|
|
} else if (node.parent is CompilationUnit) {
|
|
nameType = HighlightRegionType.TOP_LEVEL_FUNCTION_DECLARATION;
|
|
} else {
|
|
nameType = HighlightRegionType.LOCAL_FUNCTION_DECLARATION;
|
|
}
|
|
computer._addRegion_token(node.name, nameType);
|
|
|
|
super.visitFunctionDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
void visitFunctionTypeAlias(FunctionTypeAlias node) {
|
|
computer._addRegion_token(
|
|
node.typedefKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(
|
|
node.name, HighlightRegionType.FUNCTION_TYPE_ALIAS);
|
|
super.visitFunctionTypeAlias(node);
|
|
}
|
|
|
|
@override
|
|
void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
|
|
computer._addRegion_token(
|
|
node.requiredKeyword, HighlightRegionType.KEYWORD);
|
|
computer._addRegion_token(
|
|
node.name, HighlightRegionType.PARAMETER_DECLARATION);
|
|
super.visitFunctionTypedFormalParameter(node);
|
|
}
|
|
|
|
@override
|
|
void visitGenericFunctionType(GenericFunctionType node) {
|
|
computer._addRegion_token(
|
|
node.functionKeyword, HighlightRegionType.BUILT_IN);
|
|
super.visitGenericFunctionType(node);
|
|
}
|
|
|
|
@override
|
|
void visitGenericTypeAlias(GenericTypeAlias node) {
|
|
computer._addRegion_token(
|
|
node.typedefKeyword, HighlightRegionType.BUILT_IN);
|
|
|
|
final HighlightRegionType nameType;
|
|
if (node.functionType != null) {
|
|
nameType = HighlightRegionType.FUNCTION_TYPE_ALIAS;
|
|
} else {
|
|
nameType = HighlightRegionType.TYPE_ALIAS;
|
|
}
|
|
computer._addRegion_token(node.name, nameType);
|
|
|
|
super.visitGenericTypeAlias(node);
|
|
}
|
|
|
|
@override
|
|
void visitHideCombinator(HideCombinator node) {
|
|
computer._addRegion_token(node.keyword, HighlightRegionType.BUILT_IN);
|
|
super.visitHideCombinator(node);
|
|
}
|
|
|
|
@override
|
|
void visitIfElement(IfElement node) {
|
|
computer._addRegion_token(node.ifKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
computer._addRegion_token(node.elseKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitIfElement(node);
|
|
}
|
|
|
|
@override
|
|
void visitIfStatement(IfStatement node) {
|
|
computer._addRegion_token(node.ifKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
computer._addRegion_token(node.elseKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitIfStatement(node);
|
|
}
|
|
|
|
@override
|
|
void visitImplementsClause(ImplementsClause node) {
|
|
computer._addRegion_token(
|
|
node.implementsKeyword, HighlightRegionType.BUILT_IN);
|
|
super.visitImplementsClause(node);
|
|
}
|
|
|
|
@override
|
|
void visitImportDirective(ImportDirective node) {
|
|
computer._addRegion_node(node, HighlightRegionType.DIRECTIVE);
|
|
computer._addRegion_token(node.importKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(
|
|
node.deferredKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(node.asKeyword, HighlightRegionType.BUILT_IN);
|
|
_addRegions_configurations(node.configurations);
|
|
super.visitImportDirective(node);
|
|
}
|
|
|
|
@override
|
|
void visitInstanceCreationExpression(InstanceCreationExpression node) {
|
|
if (node.keyword != null) {
|
|
computer._addRegion_token(node.keyword, HighlightRegionType.KEYWORD);
|
|
}
|
|
super.visitInstanceCreationExpression(node);
|
|
}
|
|
|
|
@override
|
|
void visitIntegerLiteral(IntegerLiteral node) {
|
|
computer._addRegion_node(node, HighlightRegionType.LITERAL_INTEGER);
|
|
super.visitIntegerLiteral(node);
|
|
}
|
|
|
|
@override
|
|
void visitInterpolationExpression(InterpolationExpression node) {
|
|
if (computer._computeSemanticTokens) {
|
|
// Interpolation expressions may include uncolored code, but clients may
|
|
// be providing their own basic coloring for strings that would leak
|
|
// into those uncolored parts so we mark them up to allow the client to
|
|
// reset the coloring if required.
|
|
//
|
|
// Using the String token type with a modifier would work for VS Code but
|
|
// would cause other editors that don't know about the modifier (and also
|
|
// do not have their own local coloring) to color the tokens as a string,
|
|
// which is exactly what we'd like to avoid).
|
|
|
|
computer._addRegion_node(
|
|
node,
|
|
// The HighlightRegionType here is not used because of the
|
|
// computer._computeSemanticTokens check above.
|
|
HighlightRegionType.LITERAL_STRING,
|
|
semanticTokenType: CustomSemanticTokenTypes.source,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.interpolation},
|
|
);
|
|
}
|
|
super.visitInterpolationExpression(node);
|
|
}
|
|
|
|
@override
|
|
void visitInterpolationString(InterpolationString node) {
|
|
computer._addRegion_node(node, HighlightRegionType.LITERAL_STRING);
|
|
super.visitInterpolationString(node);
|
|
}
|
|
|
|
@override
|
|
void visitIsExpression(IsExpression node) {
|
|
computer._addRegion_token(node.isOperator, HighlightRegionType.KEYWORD);
|
|
super.visitIsExpression(node);
|
|
}
|
|
|
|
@override
|
|
void visitLibraryDirective(LibraryDirective node) {
|
|
computer._addRegion_node(node, HighlightRegionType.DIRECTIVE);
|
|
computer._addRegion_token(
|
|
node.libraryKeyword, HighlightRegionType.BUILT_IN);
|
|
super.visitLibraryDirective(node);
|
|
}
|
|
|
|
@override
|
|
void visitLibraryIdentifier(LibraryIdentifier node) {
|
|
computer._addRegion_node(node, HighlightRegionType.LIBRARY_NAME);
|
|
null;
|
|
}
|
|
|
|
@override
|
|
void visitListLiteral(ListLiteral node) {
|
|
computer._addRegion_node(node, HighlightRegionType.LITERAL_LIST);
|
|
computer._addRegion_token(node.constKeyword, HighlightRegionType.KEYWORD);
|
|
super.visitListLiteral(node);
|
|
}
|
|
|
|
@override
|
|
void visitMethodDeclaration(MethodDeclaration node) {
|
|
computer._addRegion_token(
|
|
node.externalKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(
|
|
node.modifierKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(
|
|
node.operatorKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(
|
|
node.propertyKeyword, HighlightRegionType.BUILT_IN);
|
|
|
|
final HighlightRegionType nameType;
|
|
if (node.isGetter) {
|
|
nameType = node.isStatic
|
|
? HighlightRegionType.STATIC_GETTER_DECLARATION
|
|
: HighlightRegionType.INSTANCE_GETTER_DECLARATION;
|
|
} else if (node.isSetter) {
|
|
nameType = node.isStatic
|
|
? HighlightRegionType.STATIC_SETTER_DECLARATION
|
|
: HighlightRegionType.INSTANCE_SETTER_DECLARATION;
|
|
} else {
|
|
nameType = node.isStatic
|
|
? HighlightRegionType.STATIC_METHOD_DECLARATION
|
|
: HighlightRegionType.INSTANCE_METHOD_DECLARATION;
|
|
}
|
|
computer._addRegion_token(node.name, nameType);
|
|
|
|
super.visitMethodDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
void visitMixinDeclaration(MixinDeclaration node) {
|
|
computer._addRegion_token(node.baseKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(node.mixinKeyword, HighlightRegionType.BUILT_IN);
|
|
computer._addRegion_token(node.name, HighlightRegionType.MIXIN);
|
|
super.visitMixinDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
void visitNamedType(NamedType node) {
|
|
var type = node.type;
|
|
if (type != null) {
|
|
var isDynamic = type.isDynamic && node.name.name == 'dynamic';
|
|
var isNever = type is NeverType;
|
|
if (isDynamic || isNever) {
|
|
computer._addRegion_node(
|
|
node.name,
|
|
isDynamic
|
|
? HighlightRegionType.TYPE_NAME_DYNAMIC
|
|
: HighlightRegionType.CLASS,
|
|
semanticTokenType: SemanticTokenTypes.type,
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
super.visitNamedType(node);
|
|
}
|
|
|
|
@override
|
|
void visitNativeClause(NativeClause node) {
|
|
computer._addRegion_token(node.nativeKeyword, HighlightRegionType.BUILT_IN);
|
|
super.visitNativeClause(node);
|
|
}
|
|
|
|
@override
|
|
void visitNativeFunctionBody(NativeFunctionBody node) {
|
|
computer._addRegion_token(node.nativeKeyword, HighlightRegionType.BUILT_IN);
|
|
super.visitNativeFunctionBody(node);
|
|
}
|
|
|
|
@override
|
|
void visitNullLiteral(NullLiteral node) {
|
|
computer._addRegion_token(node.literal, HighlightRegionType.KEYWORD);
|
|
super.visitNullLiteral(node);
|
|
}
|
|
|
|
@override
|
|
void visitOnClause(OnClause node) {
|
|
computer._addRegion_token(node.onKeyword, HighlightRegionType.BUILT_IN);
|
|
super.visitOnClause(node);
|
|
}
|
|
|
|
@override
|
|
void visitPartDirective(PartDirective node) {
|
|
computer._addRegion_node(node, HighlightRegionType.DIRECTIVE);
|
|
computer._addRegion_token(node.partKeyword, HighlightRegionType.BUILT_IN);
|
|
super.visitPartDirective(node);
|
|
}
|
|
|
|
@override
|
|
void visitPartOfDirective(PartOfDirective node) {
|
|
computer._addRegion_node(node, HighlightRegionType.DIRECTIVE);
|
|
computer._addRegion_tokenStart_tokenEnd(
|
|
node.partKeyword, node.ofKeyword, HighlightRegionType.BUILT_IN);
|
|
super.visitPartOfDirective(node);
|
|
}
|
|
|
|
@override
|
|
void visitPatternFieldName(PatternFieldName node) {
|
|
final name = node.name;
|
|
if (name != null) {
|
|
computer._addRegion_token(
|
|
node.name, HighlightRegionType.INSTANCE_GETTER_REFERENCE);
|
|
}
|
|
super.visitPatternFieldName(node);
|
|
}
|
|
|
|
@override
|
|
void visitPatternVariableDeclaration(PatternVariableDeclaration node) {
|
|
computer._addRegion_token(node.keyword, HighlightRegionType.KEYWORD);
|
|
super.visitPatternVariableDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
void visitRecordLiteral(RecordLiteral node) {
|
|
computer._addRegion_node(node, HighlightRegionType.LITERAL_RECORD);
|
|
computer._addRegion_token(node.constKeyword, HighlightRegionType.KEYWORD);
|
|
|
|
for (final field in node.fields) {
|
|
if (field is NamedExpression) {
|
|
computer._addRegion_token(
|
|
field.name.label.token,
|
|
HighlightRegionType.PARAMETER_REFERENCE,
|
|
);
|
|
field.expression.accept(this);
|
|
} else {
|
|
field.accept(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
void visitRecordTypeAnnotation(RecordTypeAnnotation node) {
|
|
for (final field in node.fields) {
|
|
computer._addRegion_token(field.name, HighlightRegionType.FIELD);
|
|
}
|
|
|
|
super.visitRecordTypeAnnotation(node);
|
|
}
|
|
|
|
@override
|
|
void visitRethrowExpression(RethrowExpression node) {
|
|
computer._addRegion_token(node.rethrowKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitRethrowExpression(node);
|
|
}
|
|
|
|
@override
|
|
void visitReturnStatement(ReturnStatement node) {
|
|
computer._addRegion_token(node.returnKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitReturnStatement(node);
|
|
}
|
|
|
|
@override
|
|
void visitSetOrMapLiteral(SetOrMapLiteral node) {
|
|
if (node.isMap) {
|
|
computer._addRegion_node(node, HighlightRegionType.LITERAL_MAP);
|
|
// TODO(brianwilkerson) Add a highlight region for set literals. This
|
|
// would be a breaking change, but would be consistent with list and map
|
|
// literals.
|
|
// } else if (node.isSet) {
|
|
// computer._addRegion_node(node, HighlightRegionType.LITERAL_SET);
|
|
}
|
|
computer._addRegion_token(node.constKeyword, HighlightRegionType.KEYWORD);
|
|
super.visitSetOrMapLiteral(node);
|
|
}
|
|
|
|
@override
|
|
void visitShowCombinator(ShowCombinator node) {
|
|
computer._addRegion_token(node.keyword, HighlightRegionType.BUILT_IN);
|
|
super.visitShowCombinator(node);
|
|
}
|
|
|
|
@override
|
|
void visitSimpleFormalParameter(SimpleFormalParameter node) {
|
|
computer._addRegion_token(
|
|
node.requiredKeyword, HighlightRegionType.KEYWORD);
|
|
|
|
computer._addRegion_token(
|
|
node.name,
|
|
node.declaredElement!.type is DynamicType
|
|
? HighlightRegionType.DYNAMIC_PARAMETER_DECLARATION
|
|
: HighlightRegionType.PARAMETER_DECLARATION,
|
|
);
|
|
|
|
super.visitSimpleFormalParameter(node);
|
|
}
|
|
|
|
@override
|
|
void visitSimpleIdentifier(SimpleIdentifier node) {
|
|
computer._addIdentifierRegion(node);
|
|
super.visitSimpleIdentifier(node);
|
|
}
|
|
|
|
@override
|
|
void visitSimpleStringLiteral(SimpleStringLiteral node) {
|
|
computer._addRegion_node(node, HighlightRegionType.LITERAL_STRING);
|
|
if (computer._computeSemanticTokens) {
|
|
_addRegions_stringEscapes(node);
|
|
}
|
|
super.visitSimpleStringLiteral(node);
|
|
}
|
|
|
|
@override
|
|
void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
|
|
computer._addRegion_token(node.superKeyword, HighlightRegionType.KEYWORD);
|
|
super.visitSuperConstructorInvocation(node);
|
|
}
|
|
|
|
@override
|
|
void visitSuperFormalParameter(SuperFormalParameter node) {
|
|
computer._addRegion_token(
|
|
node.requiredKeyword,
|
|
HighlightRegionType.KEYWORD,
|
|
);
|
|
|
|
computer._addRegion_token(
|
|
node.superKeyword,
|
|
HighlightRegionType.KEYWORD,
|
|
);
|
|
|
|
computer._addRegion_token(
|
|
node.name,
|
|
HighlightRegionType.PARAMETER_DECLARATION,
|
|
);
|
|
|
|
node.type?.accept(this);
|
|
node.typeParameters?.accept(this);
|
|
node.parameters?.accept(this);
|
|
}
|
|
|
|
@override
|
|
void visitSwitchCase(SwitchCase node) {
|
|
computer._addRegion_token(node.keyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitSwitchCase(node);
|
|
}
|
|
|
|
@override
|
|
void visitSwitchDefault(SwitchDefault node) {
|
|
computer._addRegion_token(node.keyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitSwitchDefault(node);
|
|
}
|
|
|
|
@override
|
|
void visitSwitchExpression(SwitchExpression node) {
|
|
computer._addRegion_token(node.switchKeyword, HighlightRegionType.KEYWORD);
|
|
super.visitSwitchExpression(node);
|
|
}
|
|
|
|
@override
|
|
void visitSwitchPatternCase(SwitchPatternCase node) {
|
|
computer._addRegion_token(node.keyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitSwitchPatternCase(node);
|
|
}
|
|
|
|
@override
|
|
void visitSwitchStatement(SwitchStatement node) {
|
|
computer._addRegion_token(node.switchKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitSwitchStatement(node);
|
|
}
|
|
|
|
@override
|
|
void visitThisExpression(ThisExpression node) {
|
|
computer._addRegion_token(node.thisKeyword, HighlightRegionType.KEYWORD);
|
|
super.visitThisExpression(node);
|
|
}
|
|
|
|
@override
|
|
void visitThrowExpression(ThrowExpression node) {
|
|
computer._addRegion_token(node.throwKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitThrowExpression(node);
|
|
}
|
|
|
|
@override
|
|
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
|
|
computer._addRegion_token(
|
|
node.externalKeyword, HighlightRegionType.BUILT_IN);
|
|
|
|
super.visitTopLevelVariableDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
void visitTryStatement(TryStatement node) {
|
|
computer._addRegion_token(node.tryKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
computer._addRegion_token(node.finallyKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitTryStatement(node);
|
|
}
|
|
|
|
@override
|
|
void visitTypeParameter(TypeParameter node) {
|
|
computer._addRegion_token(node.name, HighlightRegionType.TYPE_PARAMETER);
|
|
super.visitTypeParameter(node);
|
|
}
|
|
|
|
@override
|
|
void visitVariableDeclaration(VariableDeclaration node) {
|
|
var element = node.declaredElement;
|
|
if (element is FieldElement) {
|
|
computer._addRegion_token(
|
|
node.name,
|
|
element.isStatic
|
|
? HighlightRegionType.STATIC_FIELD_DECLARATION
|
|
: HighlightRegionType.INSTANCE_FIELD_DECLARATION,
|
|
);
|
|
} else if (element is LocalVariableElement) {
|
|
computer._addRegion_token(
|
|
node.name,
|
|
element.type is DynamicType
|
|
? HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_DECLARATION
|
|
: HighlightRegionType.LOCAL_VARIABLE_DECLARATION,
|
|
);
|
|
} else if (element is TopLevelVariableElement) {
|
|
computer._addRegion_token(
|
|
node.name,
|
|
HighlightRegionType.TOP_LEVEL_VARIABLE_DECLARATION,
|
|
);
|
|
}
|
|
|
|
super.visitVariableDeclaration(node);
|
|
}
|
|
|
|
@override
|
|
void visitVariableDeclarationList(VariableDeclarationList node) {
|
|
computer._addRegion_token(node.lateKeyword, HighlightRegionType.KEYWORD);
|
|
computer._addRegion_token(node.keyword, HighlightRegionType.KEYWORD);
|
|
super.visitVariableDeclarationList(node);
|
|
}
|
|
|
|
@override
|
|
void visitWhenClause(WhenClause node) {
|
|
computer._addRegion_token(node.whenKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitWhenClause(node);
|
|
}
|
|
|
|
@override
|
|
void visitWhileStatement(WhileStatement node) {
|
|
computer._addRegion_token(node.whileKeyword, HighlightRegionType.KEYWORD,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitWhileStatement(node);
|
|
}
|
|
|
|
@override
|
|
void visitWildcardPattern(WildcardPattern node) {
|
|
computer._addRegion_token(node.keyword, HighlightRegionType.KEYWORD);
|
|
super.visitWildcardPattern(node);
|
|
}
|
|
|
|
@override
|
|
void visitWithClause(WithClause node) {
|
|
computer._addRegion_token(node.withKeyword, HighlightRegionType.KEYWORD);
|
|
super.visitWithClause(node);
|
|
}
|
|
|
|
@override
|
|
void visitYieldStatement(YieldStatement node) {
|
|
var keyword = node.yieldKeyword;
|
|
var star = node.star;
|
|
var offset = keyword.offset;
|
|
var end = star != null ? star.end : keyword.end;
|
|
computer._addRegion(offset, end - offset, HighlightRegionType.BUILT_IN,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
super.visitYieldStatement(node);
|
|
}
|
|
|
|
void _addRegions_configurations(List<Configuration> configurations) {
|
|
for (final configuration in configurations) {
|
|
computer._addRegion_token(
|
|
configuration.ifKeyword, HighlightRegionType.BUILT_IN,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
}
|
|
}
|
|
|
|
void _addRegions_functionBody(FunctionBody node) {
|
|
var keyword = node.keyword;
|
|
if (keyword != null) {
|
|
var star = node.star;
|
|
var offset = keyword.offset;
|
|
var end = star != null ? star.end : keyword.end;
|
|
computer._addRegion(offset, end - offset, HighlightRegionType.BUILT_IN,
|
|
semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
|
|
}
|
|
}
|
|
|
|
void _addRegions_stringEscapes(SimpleStringLiteral node) {
|
|
final string = node.literal.lexeme;
|
|
final quote = analyzeQuote(string);
|
|
final startIndex = firstQuoteLength(string, quote);
|
|
final endIndex = string.length - lastQuoteLength(quote);
|
|
switch (quote) {
|
|
case Quote.Single:
|
|
case Quote.Double:
|
|
case Quote.MultiLineSingle:
|
|
case Quote.MultiLineDouble:
|
|
_findEscapes(node, startIndex: startIndex, endIndex: endIndex,
|
|
listener: (offset, end) {
|
|
final length = end - offset;
|
|
computer._addRegion(node.offset + offset, length,
|
|
HighlightRegionType.VALID_STRING_ESCAPE);
|
|
});
|
|
break;
|
|
case Quote.RawSingle:
|
|
case Quote.RawDouble:
|
|
case Quote.RawMultiLineSingle:
|
|
case Quote.RawMultiLineDouble:
|
|
// Raw strings don't have escape characters.
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// Finds escaped regions within a string between [startIndex] and [endIndex],
|
|
/// calling [listener] for each found region.
|
|
void _findEscapes(
|
|
SimpleStringLiteral node, {
|
|
required int startIndex,
|
|
required int endIndex,
|
|
required void Function(int offset, int end) listener,
|
|
}) {
|
|
final string = node.literal.lexeme;
|
|
final codeUnits = string.codeUnits;
|
|
final length = string.length;
|
|
|
|
bool isBackslash(int i) => i <= length && codeUnits[i] == char.$BACKSLASH;
|
|
bool isHexEscape(int i) => i <= length && codeUnits[i] == char.$x;
|
|
bool isUnicodeHexEscape(int i) => i <= length && codeUnits[i] == char.$u;
|
|
bool isOpenBrace(int i) =>
|
|
i <= length && codeUnits[i] == char.$OPEN_CURLY_BRACKET;
|
|
bool isCloseBrace(int i) =>
|
|
i <= length && codeUnits[i] == char.$CLOSE_CURLY_BRACKET;
|
|
int? numHexDigits(int i, {required int min, required int max}) {
|
|
var numHexDigits = 0;
|
|
for (var j = i; j < math.min(i + max, length); j++) {
|
|
if (!char.isHexDigit(codeUnits[j])) {
|
|
break;
|
|
}
|
|
numHexDigits++;
|
|
}
|
|
return numHexDigits >= min ? numHexDigits : null;
|
|
}
|
|
|
|
for (var i = startIndex; i < endIndex;) {
|
|
if (isBackslash(i)) {
|
|
final backslashOffset = i++;
|
|
// All escaped characters are a single character except for:
|
|
// `\uXXXX` or `\u{XX?X?X?X?X?}` for Unicode hex escape.
|
|
// `\xXX` for hex escape.
|
|
if (isHexEscape(i)) {
|
|
// Expect exactly 2 hex digits.
|
|
final numDigits = numHexDigits(i + 1, min: 2, max: 2);
|
|
if (numDigits != null) {
|
|
i += 1 + numDigits;
|
|
listener(backslashOffset, i);
|
|
}
|
|
} else if (isUnicodeHexEscape(i) && isOpenBrace(i + 1)) {
|
|
// Expect 1-6 hex digits followed by '}'.
|
|
final numDigits = numHexDigits(i + 2, min: 1, max: 6);
|
|
if (numDigits != null && isCloseBrace(i + 2 + numDigits)) {
|
|
i += 2 + numDigits + 1;
|
|
listener(backslashOffset, i);
|
|
}
|
|
} else if (isUnicodeHexEscape(i)) {
|
|
// Expect exactly 4 hex digits.
|
|
final numDigits = numHexDigits(i + 1, min: 4, max: 4);
|
|
if (numDigits != null) {
|
|
i += 1 + numDigits;
|
|
listener(backslashOffset, i);
|
|
}
|
|
} else {
|
|
i++;
|
|
// Single-character escape.
|
|
listener(backslashOffset, i);
|
|
}
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
}
|