// 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 = new 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}) { if (showAllChildren == null) 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); 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]); } }