blob: 27077850c2dbc0b6f3e12c78d41478141948a917 [file]
// Copyright (c) 2025, 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.
// ignore_for_file: implementation_imports
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/element/element.dart';
import '../api.dart';
import '../workspace.dart';
import 'display.dart';
import 'formatting.dart';
import 'utils.dart';
abstract class Renderer {
String render(GroupType type, Item item) {
switch (type) {
case GroupType.constructor:
return handleConstructor(item);
case GroupType.enumValue:
return handleEnumValue(item);
case GroupType.field:
return handleField(item);
case GroupType.accessor:
return handleAccessor(item);
case GroupType.method:
return handleMethod(item);
case GroupType.topLevelVariable:
return handleTopLevelVariable(item);
case GroupType.function:
return handleFunction(item);
case GroupType.functionTypeAlias:
return handleFunctionTypeAlias(item);
case GroupType.typeAlias:
return handleTypeAlias(item);
case GroupType.$enum:
return handleEnum(item);
case GroupType.$mixin:
return handleMixin(item);
case GroupType.$class:
return handleClass(item);
case GroupType.$extension:
return handleExtension(item);
case GroupType.$extensionType:
return handleExtensionType(item);
// unexpected values
case GroupType.skip:
case GroupType.other:
throw StateError('unexpected item: $item');
}
}
String renderItem(Item item) => render(item.type, item);
String handleConstructor(Item item) => handleDefault(item);
String handleEnumValue(Item item) => handleDefault(item);
String handleField(Item item) => handleDefault(item);
String handleAccessor(Item item) => handleDefault(item);
String handleMethod(Item item) => handleDefault(item);
String handleTopLevelVariable(Item item) => handleDefault(item);
String handleFunction(Item item) => handleDefault(item);
String handleFunctionTypeAlias(Item item) => handleDefault(item);
String handleTypeAlias(Item item) => handleDefault(item);
String handleMixin(Item item) => handleDefault(item);
String handleClass(Item item) => handleDefault(item);
String handleEnum(Item item) => handleDefault(item);
String handleExtension(Item item) => handleDefault(item);
String handleExtensionType(Item item) => handleDefault(item);
String handleDefault(Item item) => item.name;
}
class OutlineRenderer extends Renderer {
@override
String handleConstructor(Item item) {
var element = item.asConstructor;
var arity = element.parameters.isEmpty ? '' : '…';
return '${element.displayName}($arity)';
}
@override
String handleAccessor(Item item) {
var prefix = (item.element as PropertyAccessorElement).isGetter
? 'get'
: 'set';
return '$prefix\u00A0${item.name}';
}
@override
String handleMethod(Item item) {
var element = item.asMethod;
if (element.isOperator) {
return item.name;
} else {
var arity = element.parameters.isEmpty ? '' : '…';
return '${item.name}($arity)';
}
}
@override
String handleFunction(Item item) {
var element = item.asFunction;
var arity = element.parameters.isEmpty ? '' : '…';
return '${item.name}($arity)';
}
}
String writeAnnotations(Item item) {
var annotations = item.annotations;
if (annotations.isEmpty) return '';
var buf = StringBuffer('<p class="annotations-container">\n');
for (var annotation in annotations) {
var text = annotation.toSource();
buf.writeln(
'<span class="badge badge--secondary">'
'${htmlEscape(text)}</span>',
);
}
buf.writeln('</p>');
return buf.toString();
}
class LinkedCodeRenderer extends Renderer {
final Resolver resolver;
final WorkspaceFile fromFile;
LinkedCodeRenderer(this.resolver, this.fromFile);
@override
String handleConstructor(Item item) {
var element = item.asConstructor;
var text = LinkedText(resolver, fromFile);
var builder = ElementDisplayStringBuilder(text);
builder.writeConstructorElement(element);
return text.emitHtml(
(text) => DartFormat.asConstructor(
text,
className: element.enclosingElement.name,
isConst: !element.isFactory && element.isConst,
),
);
}
@override
String handleField(Item item) {
var element = item.asField;
var text = LinkedText(resolver, fromFile);
var builder = ElementDisplayStringBuilder(text);
builder.writeVariableElement(element);
return text.emitHtml(DartFormat.asField);
}
@override
String handleAccessor(Item item) {
var element = item.asAccessor;
var text = LinkedText(resolver, fromFile);
var builder = ElementDisplayStringBuilder(text);
builder.writeExecutableElement(element, element.displayName);
return text.emitHtml(DartFormat.asMethod);
}
@override
String handleMethod(Item item) {
var element = item.asMethod;
var text = LinkedText(resolver, fromFile);
var builder = ElementDisplayStringBuilder(text);
builder.writeExecutableElement(
element,
element.isOperator ? element.displayName : element.name,
);
return text.emitHtml(DartFormat.asMethod);
}
@override
String handleFunction(Item item) {
var element = item.asFunction;
var text = LinkedText(resolver, fromFile);
var builder = ElementDisplayStringBuilder(text);
builder.writeExecutableElement(element, element.name);
return text.emitHtml(DartFormat.asFunction);
}
@override
String handleFunctionTypeAlias(Item item) {
var element = item.asTypeAlias;
var text = LinkedText(resolver, fromFile);
var builder = ElementDisplayStringBuilder(text);
builder.writeTypeAliasElement(element as TypeAliasElementImpl);
return text.emitHtml(DartFormat.asTypeAlias);
}
@override
String handleTypeAlias(Item item) {
var element = item.asTypeAlias;
var text = LinkedText(resolver, fromFile);
var builder = ElementDisplayStringBuilder(text);
builder.writeTypeAliasElement(element as TypeAliasElementImpl);
return text.emitHtml(DartFormat.asTypeAlias);
}
@override
String handleClass(Item item) {
var element = item.asClass;
var text = LinkedText(resolver, fromFile);
var builder = ElementDisplayStringBuilder(text);
builder.writeClassElement(element as ClassElementImpl);
// // todo: replicate this but call out to LinkedText
// //text.write(element.getDisplayString(withNullability: true));
// // "class DocWorkspace extends DocContainer"
// text.write('class ');
// // todo: note that we don't need to link to ourself
// text.writeElement(element.name, element);
// var $super = element.supertype?.element;
// if ($super != null) {
// text.write(' extends ');
// text.writeElement($super.name, $super);
// }
return text.emitHtml(DartFormat.asClass, ' { … }');
}
@override
String handleMixin(Item item) {
var element = item.asMixin;
var text = LinkedText(resolver, fromFile);
var builder = ElementDisplayStringBuilder(text);
builder.writeMixinElement(element as MixinElementImpl);
return text.emitHtml(DartFormat.asClass, ' { … }');
}
@override
String handleEnum(Item item) {
var element = item.asEnum;
var text = LinkedText(resolver, fromFile);
var builder = ElementDisplayStringBuilder(text);
builder.writeEnumElement(element);
return text.emitHtml(DartFormat.asEnum, ' { … }');
}
@override
String handleExtension(Item item) {
var element = item.asExtension;
var text = LinkedText(resolver, fromFile);
var builder = ElementDisplayStringBuilder(text);
builder.writeExtensionElement(element);
return text.emitHtml(DartFormat.asClass, ' { … }');
}
@override
String handleExtensionType(Item item) {
var element = item.asExtensionType;
var text = LinkedText(resolver, fromFile);
var builder = ElementDisplayStringBuilder(text);
builder.writeExtensionTypeElement(element);
return text.emitHtml(DartFormat.asClass, ' { … }');
}
@override
String handleDefault(Item item) {
throw StateError('${item.type} not yet handled from LinkedCodeRenderer');
}
}