| // Copyright (c) 2012, 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. |
| |
| /// A simple library for rendering tree-like structures in ASCII. |
| import 'package:path/path.dart' as path; |
| |
| import 'log.dart' as log; |
| import 'utils.dart'; |
| |
| /// Draws a tree for the given list of files. Given files like: |
| /// |
| /// TODO |
| /// example/console_example.dart |
| /// example/main.dart |
| /// example/web copy/web_example.dart |
| /// test/absolute_test.dart |
| /// test/basename_test.dart |
| /// test/dirname_test.dart |
| /// test/extension_test.dart |
| /// test/is_absolute_test.dart |
| /// test/is_relative_test.dart |
| /// test/join_test.dart |
| /// test/normalize_test.dart |
| /// test/relative_test.dart |
| /// test/split_test.dart |
| /// .gitignore |
| /// README.md |
| /// lib/path.dart |
| /// pubspec.yaml |
| /// test/all_test.dart |
| /// test/path_posix_test.dart |
| /// test/path_windows_test.dart |
| /// |
| /// this renders: |
| /// |
| /// |-- .gitignore |
| /// |-- README.md |
| /// |-- TODO |
| /// |-- example |
| /// | |-- console_example.dart |
| /// | |-- main.dart |
| /// | '-- web copy |
| /// | '-- web_example.dart |
| /// |-- lib |
| /// | '-- path.dart |
| /// |-- pubspec.yaml |
| /// '-- test |
| /// |-- absolute_test.dart |
| /// |-- all_test.dart |
| /// |-- basename_test.dart |
| /// | (7 more...) |
| /// |-- path_windows_test.dart |
| /// |-- relative_test.dart |
| /// '-- split_test.dart |
| /// |
| /// If [baseDir] is passed, it will be used as the root of the tree. |
| /// |
| /// If [showAllChildren] is `false`, then directories with more than ten items |
| /// will have their contents truncated. Defaults to `false`. |
| String fromFiles(List<String> files, {String baseDir, bool showAllChildren}) { |
| // Parse out the files into a tree of nested maps. |
| var root = <String, Map>{}; |
| for (var file in files) { |
| if (baseDir != null) file = path.relative(file, from: baseDir); |
| var directory = root; |
| for (var part in path.split(file)) { |
| directory = directory.putIfAbsent(part, () => <String, Map>{}) |
| as Map<String, Map>; |
| } |
| } |
| |
| // Walk the map recursively and render to a string. |
| return fromMap(root, showAllChildren: showAllChildren); |
| } |
| |
| /// Draws a tree from a nested map. Given a map like: |
| /// |
| /// { |
| /// "analyzer": { |
| /// "args": { |
| /// "collection": "" |
| /// }, |
| /// "logging": {} |
| /// }, |
| /// "barback": {} |
| /// } |
| /// |
| /// this renders: |
| /// |
| /// analyzer |
| /// |-- args |
| /// | '-- collection |
| /// '---logging |
| /// barback |
| /// |
| /// Items with no children should have an empty map as the value. |
| /// |
| /// If [showAllChildren] is `false`, then directories with more than ten items |
| /// will have their contents truncated. Defaults to `false`. |
| String fromMap(Map<String, Map> map, {bool showAllChildren}) { |
| var buffer = StringBuffer(); |
| _draw(buffer, '', null, map, showAllChildren: showAllChildren); |
| return buffer.toString(); |
| } |
| |
| void _drawLine( |
| StringBuffer buffer, String prefix, bool isLastChild, String name) { |
| // Print lines. |
| buffer.write(prefix); |
| if (name != null) { |
| if (isLastChild) { |
| buffer.write(log.gray("'-- ")); |
| } else { |
| buffer.write(log.gray('|-- ')); |
| } |
| } |
| |
| // Print name. |
| buffer.writeln(name); |
| } |
| |
| String _getPrefix(bool isRoot, bool isLast) { |
| if (isRoot) return ''; |
| if (isLast) return ' '; |
| return log.gray('| '); |
| } |
| |
| void _draw( |
| StringBuffer buffer, String prefix, String name, Map<String, Map> children, |
| {bool showAllChildren, bool isLast = false}) { |
| showAllChildren ??= false; |
| |
| // Don't draw a line for the root node. |
| if (name != null) _drawLine(buffer, prefix, isLast, name); |
| |
| // Recurse to the children. |
| var childNames = ordered(children.keys); |
| |
| void drawChild(bool isLastChild, String child) { |
| var childPrefix = _getPrefix(name == null, isLast); |
| _draw(buffer, '$prefix$childPrefix', child, |
| children[child] as Map<String, Map>, |
| showAllChildren: showAllChildren, isLast: isLastChild); |
| } |
| |
| if (name == null || showAllChildren || childNames.length <= 10) { |
| // Not too many, so show all the children. |
| for (var i = 0; i < childNames.length; i++) { |
| drawChild(i == childNames.length - 1, childNames[i]); |
| } |
| } else { |
| // Show the first few. |
| drawChild(false, childNames[0]); |
| drawChild(false, childNames[1]); |
| drawChild(false, childNames[2]); |
| |
| // Elide the middle ones. |
| buffer.write(prefix); |
| buffer.write(_getPrefix(name == null, isLast)); |
| buffer.writeln(log.gray('| (${childNames.length - 6} more...)')); |
| |
| // Show the last few. |
| drawChild(false, childNames[childNames.length - 3]); |
| drawChild(false, childNames[childNames.length - 2]); |
| drawChild(true, childNames[childNames.length - 1]); |
| } |
| } |