blob: e3467d2692ed271f5d02bcfd1c9cd24950e12e2a [file] [log] [blame]
// 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.
// Many functions here are mostly camelcase, with an occasional underscore to
// separate phrases.
// ignore_for_file: non_constant_identifier_names
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/ast.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/extensions.dart';
import 'package:analyzer/src/utilities/extensions/ast.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({
required AstNode parent,
required Token nameToken,
required Element? element,
}) {
if (_addIdentifierRegion_keyword(nameToken)) {
return;
}
if (_addIdentifierRegion_class(parent, nameToken, element)) {
return;
}
if (_addIdentifierRegion_extension(nameToken, element)) {
return;
}
if (_addIdentifierRegion_constructor(parent, nameToken, element)) {
return;
}
if (_addIdentifierRegion_getterSetterDeclaration(
parent,
nameToken,
element,
)) {
return;
}
if (_addIdentifierRegion_field(parent, nameToken, element)) {
return;
}
if (_addIdentifierRegion_function(parent, nameToken, element)) {
return;
}
if (_addIdentifierRegion_importPrefix(nameToken, element)) {
return;
}
if (_addIdentifierRegion_label(parent, nameToken, element)) {
return;
}
if (_addIdentifierRegion_localVariable(nameToken, element)) {
return;
}
if (_addIdentifierRegion_method(parent, nameToken, element)) {
return;
}
if (_addIdentifierRegion_parameter(parent, nameToken, element)) {
return;
}
if (_addIdentifierRegion_typeAlias(nameToken, element)) {
return;
}
if (_addIdentifierRegion_typeParameter(nameToken, element)) {
return;
}
if (_addIdentifierRegion_unresolvedInstanceMemberReference(
parent,
nameToken,
element,
)) {
return;
}
_addRegion_token(nameToken, 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(
AstNode parent,
Token nameToken,
Element? element,
) {
if (element is! InterfaceElement) {
return false;
}
// prepare type
HighlightRegionType type;
SemanticTokenTypes? semanticType;
Set<SemanticTokenModifiers>? semanticModifiers;
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 if (element is ExtensionTypeElement) {
type = HighlightRegionType.EXTENSION_TYPE;
} else {
type = HighlightRegionType.CLASS;
if (parent is ConstructorDeclaration) {
semanticModifiers = {
CustomSemanticTokenModifiers.constructor,
SemanticTokenModifiers.declaration,
};
}
}
// add region
return _addRegion_token(
nameToken,
type,
semanticTokenType: semanticType,
semanticTokenModifiers: semanticModifiers,
additionalSemanticTokenModifiers:
_isAnnotationIdentifier(parent)
? {CustomSemanticTokenModifiers.annotation}
: null,
);
}
bool _addIdentifierRegion_constructor(
AstNode parent,
Token nameToken,
Element? element,
) {
if (element is! ConstructorElement) {
return false;
}
return _addRegion_token(
nameToken,
HighlightRegionType.CONSTRUCTOR,
// For semantic tokens, constructor names are coloured like methods but
// have a modifier applied.
semanticTokenType: SemanticTokenTypes.method,
semanticTokenModifiers: {
CustomSemanticTokenModifiers.constructor,
if (_isAnnotationIdentifier(parent))
CustomSemanticTokenModifiers.annotation,
},
);
}
bool _addIdentifierRegion_extension(Token nameToken, Element? element) {
if (element is! ExtensionElement) {
return false;
}
return _addRegion_token(nameToken, HighlightRegionType.EXTENSION);
}
bool _addIdentifierRegion_field(
AstNode parent,
Token nameToken,
Element? element,
) {
// Compute the type of the identifier.
HighlightRegionType? type;
if (element is FieldElement) {
if (element.isEnumConstant) {
type = HighlightRegionType.ENUM_CONSTANT;
} else if (element.isStatic) {
type = HighlightRegionType.STATIC_FIELD_DECLARATION;
} else {
type = HighlightRegionType.INSTANCE_FIELD_REFERENCE;
}
} else if (element is TopLevelVariableElement) {
type = HighlightRegionType.TOP_LEVEL_VARIABLE_DECLARATION;
} else if (element is GetterElement) {
var accessor = element;
var variable = accessor.variable3;
if (variable is TopLevelVariableElement) {
type = HighlightRegionType.TOP_LEVEL_GETTER_REFERENCE;
} else if (variable is FieldElement && variable.isEnumConstant) {
type = HighlightRegionType.ENUM_CONSTANT;
} else if (accessor.isStatic) {
type = HighlightRegionType.STATIC_GETTER_REFERENCE;
} else {
type = HighlightRegionType.INSTANCE_GETTER_REFERENCE;
}
} else if (element is SetterElement) {
var accessor = element;
var variable = accessor.variable3;
if (variable is TopLevelVariableElement) {
type = HighlightRegionType.TOP_LEVEL_SETTER_REFERENCE;
} else if (variable is FieldElement && variable.isEnumConstant) {
type = HighlightRegionType.ENUM_CONSTANT;
} else if (accessor.isStatic) {
type = HighlightRegionType.STATIC_SETTER_REFERENCE;
} else {
type = HighlightRegionType.INSTANCE_SETTER_REFERENCE;
}
} else if (element == null) {
DartType? staticType;
if (parent is PropertyAccess && nameToken == parent.propertyName.token) {
staticType = parent.realTarget.staticType;
} else if (parent.enclosingInstanceElement case ExtensionElement(
:var extendedType,
) when parent is! PrefixedIdentifier) {
staticType = extendedType;
}
// Handle tokens that are references to record fields.
if (staticType is RecordType) {
type =
staticType.fieldByName(nameToken.lexeme) != null
? HighlightRegionType.INSTANCE_GETTER_REFERENCE
: HighlightRegionType.UNRESOLVED_INSTANCE_MEMBER_REFERENCE;
}
}
// Add the highlight region.
if (type != null) {
return _addRegion_token(
nameToken,
type,
additionalSemanticTokenModifiers:
_isAnnotationIdentifier(parent)
? {CustomSemanticTokenModifiers.annotation}
: null,
);
}
return false;
}
bool _addIdentifierRegion_function(
AstNode parent,
Token nameToken,
Element? element,
) {
if (element is! TopLevelFunctionElement &&
element is! LocalFunctionElement) {
return false;
}
var isInvocation =
parent is MethodInvocation && parent.methodName.token == nameToken;
HighlightRegionType type;
var isTopLevel = element is TopLevelFunctionElement;
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_token(nameToken, type);
}
bool _addIdentifierRegion_getterSetterDeclaration(
AstNode parent,
Token nameToken,
Element? element,
) {
// should be declaration
if (!(parent is MethodDeclaration || parent is FunctionDeclaration)) {
return false;
}
var isTopLevel = parent.parent is CompilationUnit;
HighlightRegionType type;
if (element is GetterElement) {
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 (element is SetterElement) {
if (isTopLevel) {
type = HighlightRegionType.TOP_LEVEL_SETTER_DECLARATION;
} else if (element.isStatic) {
type = HighlightRegionType.STATIC_SETTER_DECLARATION;
} else {
type = HighlightRegionType.INSTANCE_SETTER_DECLARATION;
}
} else {
// should be property accessor
return false;
}
return _addRegion_token(nameToken, type);
}
bool _addIdentifierRegion_importPrefix(Token nameToken, Element? element) {
if (element is! PrefixElement) {
return false;
}
return _addRegion_token(nameToken, HighlightRegionType.IMPORT_PREFIX);
}
bool _addIdentifierRegion_keyword(Token nameToken) {
var name = nameToken.lexeme;
if (name == 'void') {
return _addRegion_token(
nameToken,
HighlightRegionType.KEYWORD,
semanticTokenModifiers: {CustomSemanticTokenModifiers.void_},
);
}
return false;
}
bool _addIdentifierRegion_label(
AstNode parent,
Token nameToken,
Element? element,
) {
if (element is! LabelElement) {
return false;
}
return _addRegion_token(
nameToken,
HighlightRegionType.LABEL,
semanticTokenModifiers:
parent is! BreakStatement
? {SemanticTokenModifiers.declaration}
: null,
);
}
bool _addIdentifierRegion_localVariable(Token nameToken, Element? element) {
if (element is! LocalVariableElement) {
return false;
}
// OK
var type =
element.type is DynamicType
? HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_REFERENCE
: HighlightRegionType.LOCAL_VARIABLE_REFERENCE;
return _addRegion_token(nameToken, type);
}
bool _addIdentifierRegion_method(
AstNode parent,
Token nameToken,
Element? element,
) {
if (element is! MethodElement) {
return false;
}
var isStatic = element.isStatic;
var isInvocation =
(parent is MethodInvocation && parent.methodName.token == nameToken) ||
(parent is DotShorthandInvocation &&
parent.memberName.token == nameToken);
// OK
HighlightRegionType type;
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_token(nameToken, type);
}
bool _addIdentifierRegion_parameter(
AstNode parent,
Token nameToken,
Element? element,
) {
if (element is! FormalParameterElement) {
return false;
}
var type =
element.type is DynamicType
? HighlightRegionType.DYNAMIC_PARAMETER_REFERENCE
: HighlightRegionType.PARAMETER_REFERENCE;
var modifiers =
parent is Label ? {CustomSemanticTokenModifiers.label} : null;
return _addRegion_token(nameToken, type, semanticTokenModifiers: modifiers);
}
bool _addIdentifierRegion_typeAlias(Token nameToken, Element? element) {
if (element is TypeAliasElement) {
var type =
element.aliasedType is FunctionType
? HighlightRegionType.FUNCTION_TYPE_ALIAS
: HighlightRegionType.TYPE_ALIAS;
return _addRegion_token(nameToken, type);
}
return false;
}
bool _addIdentifierRegion_typeParameter(Token nameToken, Element? element) {
if (element is! TypeParameterElement) {
return false;
}
return _addRegion_token(
nameToken,
HighlightRegionType.TYPE_PARAMETER,
additionalSemanticTokenModifiers: _additionalModifiersForElement(element),
);
}
bool _addIdentifierRegion_unresolvedInstanceMemberReference(
AstNode parent,
Token nameToken,
Element? element,
) {
// unresolved
if (element != null) {
return false;
}
// invoke / get / set
var decorate = false;
if (parent is MethodInvocation) {
var target = parent.realTarget;
if (parent.methodName.token == nameToken &&
target != null &&
_isDynamicExpression(target)) {
decorate = true;
}
} else {
if (parent is PrefixedIdentifier) {
decorate = parent.identifier.token == nameToken;
} else if (parent is PropertyAccess) {
decorate = parent.propertyName.token == nameToken;
}
}
if (decorate) {
_addRegion_token(
nameToken,
HighlightRegionType.UNRESOLVED_INSTANCE_MEMBER_REFERENCE,
);
return true;
}
return false;
}
/// Returns a set of additional semantic token modifiers that apply to
/// [element].
Set<SemanticTokenModifiers>? _additionalModifiersForElement(
Element? element,
) {
return (element?.isWildcardVariable ?? false)
? {CustomSemanticTokenModifiers.wildcard}
: null;
}
/// 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 [additionalSemanticTokenModifiers] are provided,
/// they will always be added in addition to [semanticTokenModifiers]/the
/// defaults.
///
/// 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,
Set<SemanticTokenModifiers>? additionalSemanticTokenModifiers,
}) {
var range = this.range;
if (range != null) {
var 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 (additionalSemanticTokenModifiers != null &&
additionalSemanticTokenModifiers.isNotEmpty) {
semanticTokenModifiers = {
...?semanticTokenModifiers,
...additionalSemanticTokenModifiers,
};
}
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);
}
bool _addRegion_token(
Token? token,
HighlightRegionType type, {
SemanticTokenTypes? semanticTokenType,
Set<SemanticTokenModifiers>? semanticTokenModifiers,
Set<SemanticTokenModifiers>? additionalSemanticTokenModifiers,
}) {
if (token != null) {
var offset = token.offset;
var length = token.length;
_addRegion(
offset,
length,
type,
semanticTokenType: semanticTokenType,
semanticTokenModifiers: semanticTokenModifiers,
additionalSemanticTokenModifiers: additionalSemanticTokenModifiers,
);
}
return true;
}
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 [parent] is the identifier part of an annotation.
bool _isAnnotationIdentifier(AstNode? parent) {
if (parent is Annotation) {
return true;
} else if (parent is PrefixedIdentifier && 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 is DynamicType || type is InvalidType;
}
}
/// 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 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},
);
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,
additionalSemanticTokenModifiers: _additionalModifiersForElement(
exceptionParameter.declaredElement2,
),
);
}
var stackTraceParameter = node.stackTraceParameter;
if (stackTraceParameter != null) {
computer._addRegion_token(
stackTraceParameter.name,
HighlightRegionType.LOCAL_VARIABLE_DECLARATION,
additionalSemanticTokenModifiers: _additionalModifiersForElement(
stackTraceParameter.declaredElement2,
),
);
}
super.visitCatchClause(node);
}
@override
void visitClassDeclaration(ClassDeclaration node) {
computer._addRegion_token(
node.augmentKeyword,
HighlightRegionType.BUILT_IN,
);
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,
semanticTokenModifiers: {SemanticTokenModifiers.declaration},
);
super.visitClassDeclaration(node);
}
@override
void visitClassTypeAlias(ClassTypeAlias node) {
// TODO(brianwilkerson): Update the interface to expose the token.
// computer._addRegion_token(
// node.augmentKeyword, HighlightRegionType.BUILT_IN);
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.augmentKeyword,
HighlightRegionType.BUILT_IN,
);
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,
SemanticTokenModifiers.declaration,
},
);
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);
}
@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,
additionalSemanticTokenModifiers: _additionalModifiersForElement(
node.declaredElement2,
),
);
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 visitDotShorthandPropertyAccess(DotShorthandPropertyAccess node) {
var element = node.propertyName.element;
if (element is ConstructorElement) {
computer._addRegion_node(
node.propertyName,
HighlightRegionType.CONSTRUCTOR_TEAR_OFF,
);
} else {
computer._addIdentifierRegion(
parent: node,
nameToken: node.propertyName.token,
element: element,
);
}
}
@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) {
// TODO(brianwilkerson): Uncomment the following lines when the token is
// supported.
// computer._addRegion_token(
// node.augmentKeyword, HighlightRegionType.BUILT_IN);
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.augmentKeyword,
HighlightRegionType.BUILT_IN,
);
computer._addRegion_token(
node.extensionKeyword,
HighlightRegionType.KEYWORD,
);
computer._addRegion_token(node.name, HighlightRegionType.EXTENSION);
super.visitExtensionDeclaration(node);
}
@override
void visitExtensionOnClause(ExtensionOnClause node) {
computer._addRegion_token(node.onKeyword, HighlightRegionType.BUILT_IN);
super.visitExtensionOnClause(node);
}
@override
void visitExtensionOverride(ExtensionOverride node) {
computer._addRegion_token(node.name, HighlightRegionType.EXTENSION);
super.visitExtensionOverride(node);
}
@override
void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
computer._addRegion_token(
node.extensionKeyword,
HighlightRegionType.BUILT_IN,
);
computer._addRegion_token(node.typeKeyword, HighlightRegionType.BUILT_IN);
computer._addRegion_token(node.constKeyword, HighlightRegionType.BUILT_IN);
computer._addRegion_token(
node.name,
HighlightRegionType.EXTENSION_TYPE,
semanticTokenModifiers: {SemanticTokenModifiers.declaration},
);
super.visitExtensionTypeDeclaration(node);
}
@override
void visitFieldDeclaration(FieldDeclaration node) {
// TODO(brianwilkerson): Update the interface to expose the token.
// computer._addRegion_token(
// node.augmentKeyword, HighlightRegionType.BUILT_IN);
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);
computer._addRegion_token(
node.name,
HighlightRegionType.INSTANCE_FIELD_REFERENCE,
additionalSemanticTokenModifiers: _additionalModifiersForElement(
node.declaredFragment?.element,
),
);
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.augmentKeyword,
HighlightRegionType.BUILT_IN,
);
computer._addRegion_token(
node.externalKeyword,
HighlightRegionType.BUILT_IN,
);
computer._addRegion_token(
node.propertyKeyword,
HighlightRegionType.BUILT_IN,
);
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,
);
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 visitImportPrefixReference(ImportPrefixReference node) {
computer._addRegion_token(node.name, HighlightRegionType.IMPORT_PREFIX);
}
@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);
if (computer._computeSemanticTokens) {
var string = node.contents.lexeme;
var parent = node.parent as StringInterpolation;
_addRegions_stringEscapes(
string,
parent.quote,
node.offset,
0,
string.length,
);
}
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.augmentKeyword,
HighlightRegionType.BUILT_IN,
);
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,
);
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.augmentKeyword,
HighlightRegionType.BUILT_IN,
);
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 visitMixinOnClause(MixinOnClause node) {
computer._addRegion_token(node.onKeyword, HighlightRegionType.BUILT_IN);
super.visitMixinOnClause(node);
}
@override
void visitNamedType(NamedType node) {
if (node.importPrefix case var importPrefix?) {
computer._addRegion_token(
importPrefix.name,
HighlightRegionType.IMPORT_PREFIX,
);
}
var type = node.type;
if (type != null) {
var isDynamic = type is DynamicType && node.name.lexeme == 'dynamic';
var isNever = type is NeverType;
if (isDynamic || isNever) {
computer._addRegion_token(
node.name,
isDynamic
? HighlightRegionType.TYPE_NAME_DYNAMIC
: HighlightRegionType.CLASS,
semanticTokenType: SemanticTokenTypes.type,
);
return;
}
}
computer._addIdentifierRegion(
parent: node,
nameToken: node.name,
element: node.element2,
);
node.typeArguments?.accept(this);
}
@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 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 visitPatternField(PatternField node) {
var name = node.name?.name;
if (name != null) {
// Patterns can be method tear-offs as well as getters:
// https://github.com/dart-lang/sdk/issues/59976#issuecomment-2613558317
var type = switch (node.element2) {
MethodElement() => HighlightRegionType.INSTANCE_METHOD_TEAR_OFF,
_ => HighlightRegionType.INSTANCE_GETTER_REFERENCE,
};
computer._addRegion_token(name, type);
}
super.visitPatternField(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 (var 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 (var field in node.fields) {
computer._addRegion_token(field.name, HighlightRegionType.FIELD);
}
super.visitRecordTypeAnnotation(node);
}
@override
void visitRepresentationConstructorName(RepresentationConstructorName node) {
computer._addRegion_token(
node.name,
HighlightRegionType.CONSTRUCTOR,
semanticTokenType: SemanticTokenTypes.method,
semanticTokenModifiers: {
CustomSemanticTokenModifiers.constructor,
SemanticTokenModifiers.declaration,
},
);
super.visitRepresentationConstructorName(node);
}
@override
void visitRepresentationDeclaration(RepresentationDeclaration node) {
computer._addRegion_token(
node.fieldName,
HighlightRegionType.INSTANCE_FIELD_DECLARATION,
);
super.visitRepresentationDeclaration(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,
);
var declaredElement = node.declaredFragment!.element;
computer._addRegion_token(
node.name,
declaredElement.type is DynamicType
? HighlightRegionType.DYNAMIC_PARAMETER_DECLARATION
: HighlightRegionType.PARAMETER_DECLARATION,
additionalSemanticTokenModifiers: _additionalModifiersForElement(
declaredElement,
),
);
super.visitSimpleFormalParameter(node);
}
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
var parent = node.parent;
if (parent != null) {
computer._addIdentifierRegion(
parent: parent,
nameToken: node.token,
element: node.writeOrReadElement2,
);
}
super.visitSimpleIdentifier(node);
}
@override
void visitSimpleStringLiteral(SimpleStringLiteral node) {
computer._addRegion_node(node, HighlightRegionType.LITERAL_STRING);
if (computer._computeSemanticTokens) {
var string = node.literal.lexeme;
var quote = analyzeQuote(string);
var startIndex = firstQuoteLength(string, quote);
var endIndex = string.length - lastQuoteLength(quote);
_addRegions_stringEscapes(
string,
quote,
node.offset,
startIndex,
endIndex,
);
}
super.visitSimpleStringLiteral(node);
}
@override
void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
computer._addRegion_token(node.superKeyword, HighlightRegionType.KEYWORD);
super.visitSuperConstructorInvocation(node);
}
@override
void visitSuperExpression(SuperExpression node) {
computer._addRegion_token(node.superKeyword, HighlightRegionType.KEYWORD);
super.visitSuperExpression(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,
node.declaredFragment?.element.type is DynamicType
? HighlightRegionType.DYNAMIC_PARAMETER_DECLARATION
: HighlightRegionType.PARAMETER_DECLARATION,
);
super.visitSuperFormalParameter(node);
}
@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.augmentKeyword,
HighlightRegionType.BUILT_IN,
);
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,
additionalSemanticTokenModifiers: _additionalModifiersForElement(
node.declaredFragment?.element,
),
);
super.visitTypeParameter(node);
}
@override
void visitVariableDeclaration(VariableDeclaration node) {
var element = node.declaredFragment?.element ?? node.declaredElement2;
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,
additionalSemanticTokenModifiers: _additionalModifiersForElement(
element,
),
);
} 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);
// For consistency, wildcard patterns are treated like wildcard variable
// declarations.
// https://github.com/dart-lang/sdk/issues/56567
computer._addRegion_token(
node.name,
HighlightRegionType.LOCAL_VARIABLE_DECLARATION,
additionalSemanticTokenModifiers: {CustomSemanticTokenModifiers.wildcard},
);
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);
}
/// Returns a set of additional semantic token modifiers that apply to
/// [element].
Set<SemanticTokenModifiers>? _additionalModifiersForElement(
Element? element,
) {
return computer._additionalModifiersForElement(element);
}
void _addRegions_configurations(List<Configuration> configurations) {
for (var 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(
String string,
Quote quote,
int nodeOffset,
int startIndex,
int endIndex,
) {
switch (quote) {
case Quote.RawSingle ||
Quote.RawDouble ||
Quote.RawMultiLineSingle ||
Quote.RawMultiLineDouble:
// Raw strings don't have escape characters.
break;
case Quote.Single ||
Quote.Double ||
Quote.MultiLineSingle ||
Quote.MultiLineDouble:
_findEscapes(
string,
startIndex: startIndex,
endIndex: endIndex,
listener: (offset, end) {
var length = end - offset;
computer._addRegion(
nodeOffset + offset,
length,
HighlightRegionType.VALID_STRING_ESCAPE,
);
},
);
}
}
/// Finds escaped regions within a string between [startIndex] and [endIndex],
/// calling [listener] for each found region.
void _findEscapes(
String string, {
required int startIndex,
required int endIndex,
required void Function(int offset, int end) listener,
}) {
var codeUnits = string.codeUnits;
var 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)) {
var 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.
var 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 '}'.
var 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.
var 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++;
}
}
}
}
extension on StringInterpolation {
Quote get quote => switch ((isRaw, isMultiline, isSingleQuoted)) {
(true, true, true) => Quote.RawMultiLineSingle,
(true, true, false) => Quote.RawMultiLineDouble,
(true, false, true) => Quote.RawSingle,
(true, false, false) => Quote.RawDouble,
(false, true, true) => Quote.MultiLineSingle,
(false, true, false) => Quote.MultiLineDouble,
(false, false, true) => Quote.Single,
(false, false, false) => Quote.Double,
};
}