| // 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 = false, | 
 | }) { | 
 |   // 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 = false}) { | 
 |   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 = false, | 
 |   bool isLast = 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(false, 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]); | 
 |   } | 
 | } |