| // 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; |
| } |