blob: a444edb3f57285ad92e8b227150a7e4643320bc6 [file] [log] [blame]
// 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 'dart:convert';
import 'package:analysis_server/src/status/utilities/tree_writer.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/element/element.dart';
/// A visitor that will produce an HTML representation of an element structure.
class ElementWriter with TreeWriter {
@override
final StringBuffer buffer;
/// Initialize a newly created element writer to write the HTML representation
/// of visited elements on the given [buffer].
ElementWriter(this.buffer);
void write(Element element) {
_writeElement(element);
writeProperties(_computeProperties(element));
_writeFragments(element);
indentLevel++;
try {
for (var child in element.children) {
write(child);
}
} finally {
indentLevel--;
}
}
/// Returns a representation Map of the properties of the given [element].
Map<String, Object> _computeProperties(Element element) {
return {
'annotations': element.metadata.annotations,
if (element is InterfaceElement) ...{
'interfaces': element.interfaces,
'isEnum': element is EnumElement,
'mixins': element.mixins,
'supertype': ?element.supertype,
if (element is ClassElement) ...{
'hasNonFinalField': element.hasNonFinalField,
'isAbstract': element.isAbstract,
'isMixinApplication': element.isMixinApplication,
'isValidMixin': element.isValidMixin,
},
},
'evaluationResult': ?switch (element) {
FieldElementImpl() => element.evaluationResult,
LocalVariableElementImpl(constantInitializer: Expression()) =>
element.evaluationResult,
TopLevelVariableElementImpl() => element.evaluationResult,
_ => null,
},
if (element is ConstructorElement) ...{
'isConst': element.isConst,
'isDefaultConstructor': element.isDefaultConstructor,
'isFactory': element.isFactory,
'redirectedConstructor': ?element.redirectedConstructor,
},
if (element is ExecutableElement) ...{
'hasImplicitReturnType': element.hasImplicitReturnType,
'isAbstract': element.isAbstract,
'isExternal': element.isExternal,
if (element is MethodElement) 'isOperator': element.isOperator,
'isStatic': element.isStatic,
'returnType': element.returnType,
'type': element.type,
},
if (element is FieldElement) 'isEnumConstant': element.isEnumConstant,
if (element is FieldFormalParameterElement) 'field': ?element.field,
if (element is TopLevelFunctionElement)
'isEntryPoint': element.isEntryPoint,
if (element is FunctionTypedElement) ...{
'returnType': element.returnType,
'type': element.type,
},
if (element is LibraryElement) ...{
'entryPoint': ?element.entryPoint,
'isDartAsync': element.isDartAsync,
'isDartCore': element.isDartCore,
'isInSdk': element.isInSdk,
},
if (element is FormalParameterElement) ...{
'defaultValueCode': ?element.defaultValueCode,
'isInitializingFormal': element.isInitializingFormal,
if (element.isRequiredPositional)
'parameterKind': 'required-positional'
else if (element.isRequiredNamed)
'parameterKind': 'required-named'
else if (element.isOptionalPositional)
'parameterKind': 'optional-positional'
else if (element.isOptionalNamed)
'parameterKind': 'optional-named'
else
'parameterKind': 'unknown kind',
},
if (element is PropertyInducingElement) 'isStatic': element.isStatic,
if (element is TypeParameterElement) 'bound': ?element.bound,
if (element is TypeParameterizedElement)
'typeParameters': element.typeParameters,
if (element is VariableElement) ...{
'hasImplicitType': element.hasImplicitType,
'isConst': element.isConst,
'isFinal': element.isFinal,
'isStatic': element.isStatic,
'type': element.type,
},
};
}
/// Write a representation of the given [element] to the buffer.
void _writeElement(Element element) {
indent();
var showItalic = switch (element) {
ConstructorElement() => !element.isOriginDeclaration,
PropertyAccessorElement() => !element.isOriginDeclaration,
PropertyInducingElement() => !element.isOriginDeclaration,
_ => false,
};
if (showItalic) {
buffer.write('<i>');
}
buffer.write(htmlEscape.convert(element.toString()));
if (showItalic) {
buffer.write('</i>');
}
buffer.write(' <span style="color:gray">(');
buffer.write(element.runtimeType);
buffer.write(')</span>');
buffer.write('<br>');
}
/// Write a representation of the given [fragment] to the buffer.
void _writeFragment(Fragment fragment, int index) {
indent();
buffer.write('fragments[$index]: ');
buffer.write(fragment.name);
buffer.write(' <span style="color:gray">(');
buffer.write(fragment.runtimeType);
buffer.write(')</span>');
buffer.write('<br>');
var properties = {
if (fragment is LibraryFragment) ...{
'source': fragment.source,
'imports': {
for (var import in fragment.libraryImports)
{
'combinators': import.combinators,
if (import.prefix != null) 'prefix': import.prefix?.name,
'isDeferred': import.prefix?.isDeferred ?? false,
'library': import.importedLibrary,
},
for (var export in fragment.libraryExports)
{
'combinators': export.combinators,
'library': export.exportedLibrary,
},
},
},
'nameOffset': ?fragment.nameOffset,
if (fragment is ExecutableFragment) ...{
'isAsynchronous': fragment.isAsynchronous,
'isGenerator': fragment.isGenerator,
'isSynchronous': fragment.isSynchronous,
},
};
writeProperties(properties);
}
void _writeFragments(Element element) {
indentLevel++;
try {
var index = 0;
Fragment? fragment = element.firstFragment;
while (fragment != null) {
_writeFragment(fragment, index++);
fragment = fragment.nextFragment;
}
} finally {
indentLevel--;
}
}
}