| // Copyright 2020 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // @dart = 2.8 | |
| import 'package:io/ansi.dart' as ansi; | |
| import '../common_util.dart'; | |
| import '../queries/index.dart'; | |
| import 'ast.dart'; | |
| /// A renderer into rich terminal output, supporting any level of nested nodes. | |
| class TerminalRenderer extends Renderer { | |
| TerminalRenderer({this.supportsControlCharacters = true}); | |
| @override | |
| void render(StringSink output, Iterable<Query> queries) { | |
| ansi.overrideAnsiOutput(supportsControlCharacters, () { | |
| for (final query in queries) { | |
| final separator = '─' * (80 - query.name.length - 5); | |
| output.writeln('── ${ansi.styleBold.wrap(query.name)} $separator'); | |
| final result = query.distill().export(); | |
| for (final node in result) { | |
| _renderNode(output, node, indent: 0); | |
| } | |
| output.writeln(); | |
| } | |
| }); | |
| } | |
| void _renderNode(StringSink output, AnyNode node, {int indent = 0}) { | |
| if (node.title != null) { | |
| if (indent == 0) { | |
| output.write('└─ '); | |
| } else if (indent > 0) { | |
| output..write('│')..write(' ' * indent); | |
| } | |
| _renderTitle(output, node.title); | |
| output.writeln(); | |
| } | |
| for (final child in node.children ?? []) { | |
| _renderNode(output, child, indent: indent + 1); | |
| } | |
| } | |
| void _renderTitle(StringSink output, Title title) { | |
| if (title is StringPiece) { | |
| // Dart analysis failed to narrow the type here.. | |
| // ignore: avoid_as | |
| output.write(_renderStringPiece(title as StringPiece)); | |
| } else if (title is UniqueSymbolSizeRecord) { | |
| output | |
| ..write('${_renderStringPiece(title.name)}: ' | |
| '${formatSize(title.tally.size)} (${title.tally.size}), ' | |
| '${title.tally.count} total symbols') | |
| ..writeln() | |
| ..write('│ In categories: ' | |
| '${title.categories.map(_renderStringPiece).join(' ')}') | |
| ..write(title.rustCrates.isEmpty | |
| ? '' | |
| : '\n│ In rust crates: ' | |
| '${title.rustCrates.map(_renderStringPiece).join(' ')}'); | |
| } else if (title is SizeRecord) { | |
| output.write('${_renderStringPiece(title.name)}: ' | |
| '${formatSize(title.tally.size)} (${title.tally.size}), ' | |
| '${title.tally.count} total symbols'); | |
| } else { | |
| throw Exception('Unsupported $title'); | |
| } | |
| } | |
| String _renderStringPiece(StringPiece piece) { | |
| if (piece is AddColor) { | |
| return ansi.wrapWith(piece.details.map(_renderStringPiece).join(''), [ | |
| (() { | |
| switch (piece.color) { | |
| case Color.white: | |
| return ansi.white; | |
| case Color.green: | |
| return ansi.green; | |
| case Color.gray: | |
| return ansi.darkGray; | |
| } | |
| })(), | |
| ansi.styleBold, | |
| ]); | |
| } else if (piece is StyledString) { | |
| return piece.details.map(_renderStringPiece).join(''); | |
| } else if (piece is Plain) { | |
| return piece.text; | |
| } else { | |
| throw Exception('Unsupported $piece'); | |
| } | |
| } | |
| final bool supportsControlCharacters; | |
| } |