blob: 6afff4c1f380ede3ea8b9060d302963085bed723 [file]
// Copyright (c) 2015, 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 'package:analysis_server/src/status/utilities/tree_writer.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
/// A visitor that will produce an HTML representation of an AST structure.
class AstWriter extends UnifyingAstVisitor<void> with TreeWriter {
@override
final StringBuffer buffer;
/// Initialize a newly created element writer to write the HTML representation
/// of visited nodes on the given [buffer].
new(this.buffer);
@override
void visitNode(AstNode node) {
_writeNode(node);
writeProperties(_computeProperties(node));
indentLevel++;
try {
node.visitChildren(this);
} finally {
indentLevel--;
}
}
/// Write a representation of the properties of the given [node] to the
/// buffer.
Map<String, Object> _computeProperties(AstNode node) {
return {
'name': ?_getName(node),
if (node is ArgumentListImpl)
'corresponding static parameters': ?node.correspondingStaticParameters
else if (node is Annotation) ...{
'element': ?node.element,
'element annotation': ?node.elementAnnotation,
} else if (node is BinaryExpression) ...{
'element': ?node.element,
'static type': ?node.staticType,
} else if (node is ClassDeclaration) ...{
'declared fragment': ?node.declaredFragment,
'abstract keyword': ?node.abstractKeyword,
} else if (node is ClassTypeAlias) ...{
'declared fragment': ?node.declaredFragment,
'abstract keyword': ?node.abstractKeyword,
} else if (node is CompilationUnit)
'declared fragment': ?node.declaredFragment
else if (node is Configuration)
'uriSource': ?node.resolvedUri
else if (node is ConstructorName)
'element': ?node.element
else if (node is DeclaredIdentifier) ...{
'element': ?node.declaredFragment?.element,
'keyword': ?node.keyword,
} else if (node is ExportDirective)
'library export': ?node.libraryExport
else if (node is FieldDeclaration)
'static keyword': ?node.staticKeyword
else if (node is FormalParameter) ...{
'declared fragment': ?node.declaredFragment,
'constFinalOrVarKeyword': ?node.constFinalOrVarKeyword,
if (node.isRequiredPositional)
'kind': 'required-positional'
else if (node.isRequiredNamed)
'kind': 'required-named'
else if (node.isOptionalPositional)
'kind': 'optional-positional'
else if (node.isOptionalNamed)
'kind': 'optional-named'
else
'kind': 'unknown kind',
} else if (node is FunctionDeclaration) ...{
'declared fragment': ?node.declaredFragment,
'external keyword': ?node.externalKeyword,
'property keyword': ?node.propertyKeyword,
} else if (node is FunctionExpressionInvocation) ...{
'element': ?node.element,
'static invoke type': ?node.staticInvokeType,
'static type': ?node.staticType,
} else if (node is GenericFunctionType)
'type': ?node.type
else if (node is ImportDirective)
'library import': ?node.libraryImport
else if (node is IndexExpression) ...{
'element': ?node.element,
'static type': ?node.staticType,
} else if (node is InstanceCreationExpression)
'static type': ?node.staticType
else if (node is LibraryDirective)
'element': ?node.element
else if (node is MethodDeclaration) ...{
'declared fragment': ?node.declaredFragment,
'external keyword': ?node.externalKeyword,
'modifier keyword': ?node.modifierKeyword,
'operator keyword': ?node.operatorKeyword,
'property keyword': ?node.propertyKeyword,
} else if (node is MethodInvocation) ...{
'static invoke type': ?node.staticInvokeType,
'static type': ?node.staticType,
} else if (node is PartDirective)
'fragment include': ?node.partInclude
else if (node is PostfixExpression) ...{
'element': ?node.element,
'static type': ?node.staticType,
} else if (node is PrefixExpression) ...{
'element': ?node.element,
'static type': ?node.staticType,
} else if (node is RedirectingConstructorInvocation)
'element': ?node.element
else if (node is SimpleIdentifier) ...{
'element': ?node.element,
'static type': ?node.staticType,
} else if (node is SimpleStringLiteral)
'value': node.value
else if (node is SuperConstructorInvocation)
'element': ?node.element
else if (node is TypeAnnotation)
'type': ?node.type
else if (node is VariableDeclarationList)
'keyword': ?node.keyword
else if (node is Declaration)
'declared fragment': ?node.declaredFragment
else if (node is Expression)
'static type': ?node.staticType
else if (node is FunctionBody) ...{
'is asynchronous': node.isAsynchronous,
'is generator': node.isGenerator,
} else if (node is Identifier) ...{
'element': ?node.element,
'static type': ?node.staticType,
},
};
}
/// Return the name of the given [node], or `null` if the given node is not a
/// declaration.
String? _getName(AstNode node) {
if (node is ClassTypeAlias) {
return node.name.lexeme;
} else if (node is ClassDeclaration) {
return node.namePart.typeName.lexeme;
} else if (node is ConstructorDeclaration) {
var name = node.name;
var typeNameOrNew = node.newKeyword?.lexeme ?? node.typeName!.name;
if (name == null) {
return typeNameOrNew;
} else {
return '$typeNameOrNew.${name.lexeme}';
}
} else if (node is ConstructorName) {
return node.toSource();
} else if (node is FieldDeclaration) {
return _getNames(node.fields);
} else if (node is FunctionDeclaration) {
return node.name.lexeme;
} else if (node is FunctionTypeAlias) {
return node.name.lexeme;
} else if (node is Identifier) {
return node.name;
} else if (node is MethodDeclaration) {
return node.name.lexeme;
} else if (node is TopLevelVariableDeclaration) {
return _getNames(node.variables);
} else if (node is TypeAnnotation) {
return node.toSource();
} else if (node is TypeParameter) {
return node.name.lexeme;
} else if (node is VariableDeclaration) {
return node.name.lexeme;
}
return null;
}
/// Return a string containing a comma-separated list of the names of all of
/// the variables in the given list of [variables].
String _getNames(VariableDeclarationList variables) {
var buffer = StringBuffer();
var first = true;
for (var variable in variables.variables) {
if (first) {
first = false;
} else {
buffer.write(', ');
}
buffer.write(variable.name.lexeme);
}
return buffer.toString();
}
/// Write a representation of the given [node] to the buffer.
void _writeNode(AstNode node) {
indent();
buffer.write(node.runtimeType);
buffer.write(' <span style="color:gray">[');
buffer.write(node.offset);
buffer.write('..');
buffer.write(node.offset + node.length - 1);
buffer.write(']');
if (node.isSynthetic) {
buffer.write(' (synthetic)');
}
buffer.write('</span>');
buffer.write('<br>');
}
}