| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of cli; | 5 part of cli; |
| 6 | 6 |
| 7 // Splits a line into a list of string args. | 7 // Splits a line into a list of string args. |
| 8 List<String> _splitLine(String line) { | 8 List<String> _splitLine(String line) { |
| 9 var args = line.split(' ').where((arg) { | 9 var args = line.split(' ').where((arg) { |
| 10 return arg != ' ' && arg != ''; | 10 return arg != ' ' && arg != ''; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 33 // A command may optionally have sub-commands. | 33 // A command may optionally have sub-commands. |
| 34 List<Command> _children = []; | 34 List<Command> _children = []; |
| 35 | 35 |
| 36 _CommandBase _parent; | 36 _CommandBase _parent; |
| 37 int get _depth => (_parent == null ? 0 : _parent._depth + 1); | 37 int get _depth => (_parent == null ? 0 : _parent._depth + 1); |
| 38 | 38 |
| 39 // Override in subclasses to provide command-specific argument completion. | 39 // Override in subclasses to provide command-specific argument completion. |
| 40 // | 40 // |
| 41 // Given a list of arguments to this command, provide a list of | 41 // Given a list of arguments to this command, provide a list of |
| 42 // possible completions for those arguments. | 42 // possible completions for those arguments. |
| 43 List<String> complete(List<String> args) => []; | 43 Future<List<String>> complete(List<String> args) => new Future.value([]); |
| 44 | 44 |
| 45 // Override in subclasses to provide command-specific execution. | 45 // Override in subclasses to provide command-specific execution. |
| 46 Future run(List<String> args); | 46 Future run(List<String> args); |
| 47 | 47 |
| 48 // Returns a list of local subcommands which match the args. | 48 // Returns a list of local subcommands which match the args. |
| 49 List<Command> _matchLocal(String arg, bool preferExact) { | 49 List<Command> _matchLocal(String arg, bool preferExact) { |
| 50 var matches = new List<Command>(); | 50 var matches = new List<Command>(); |
| 51 for (var child in _children) { | 51 for (var child in _children) { |
| 52 if (child.name.startsWith(arg)) { | 52 if (child.name.startsWith(arg)) { |
| 53 if (preferExact && child.name == arg) { | 53 if (preferExact && child.name == arg) { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 75 return matches; | 75 return matches; |
| 76 } else { | 76 } else { |
| 77 return childMatches; | 77 return childMatches; |
| 78 } | 78 } |
| 79 } else { | 79 } else { |
| 80 return matches; | 80 return matches; |
| 81 } | 81 } |
| 82 } | 82 } |
| 83 | 83 |
| 84 // Builds a list of completions for this command. | 84 // Builds a list of completions for this command. |
| 85 List<String> _buildCompletions(List<String> args, | 85 Future<List<String>> _buildCompletions(List<String> args, |
| 86 bool addEmptyString) { | 86 bool addEmptyString) { |
| 87 var completions = complete(args.sublist(_depth, args.length)); | 87 return complete(args.sublist(_depth, args.length)) |
| 88 if (addEmptyString && completions.isEmpty && args[args.length - 1] == '') { | 88 .then((completions) { |
| 89 // Special case allowance for an empty particle at the end of | 89 if (addEmptyString && completions.isEmpty && |
| 90 // the command. | 90 args[args.length - 1] == '') { |
| 91 completions = ['']; | 91 // Special case allowance for an empty particle at the end of |
| 92 } | 92 // the command. |
| 93 var prefix = _concatArgs(args, _depth); | 93 completions = ['']; |
| 94 return completions.map((str) => '${prefix}${str}').toList(); | 94 } |
| 95 var prefix = _concatArgs(args, _depth); |
| 96 return completions.map((str) => '${prefix}${str}').toList(); |
| 97 }); |
| 95 } | 98 } |
| 96 | 99 |
| 97 } | 100 } |
| 98 | 101 |
| 99 // The root of a tree of commands. | 102 // The root of a tree of commands. |
| 100 class RootCommand extends _CommandBase { | 103 class RootCommand extends _CommandBase { |
| 101 RootCommand(List<Command> children) : super(children); | 104 RootCommand(List<Command> children) : super(children); |
| 102 | 105 |
| 103 // Provides a list of possible completions for a line of text. | 106 // Provides a list of possible completions for a line of text. |
| 104 List<String> completeCommand(String line) { | 107 Future<List<String>> completeCommand(String line) { |
| 105 var args = _splitLine(line); | 108 var args = _splitLine(line); |
| 106 bool showAll = line.endsWith(' ') || args.isEmpty; | 109 bool showAll = line.endsWith(' ') || args.isEmpty; |
| 107 if (showAll) { | 110 if (showAll) { |
| 108 // Adding an empty string to the end causes us to match all | 111 // Adding an empty string to the end causes us to match all |
| 109 // subcommands of the last command. | 112 // subcommands of the last command. |
| 110 args.add(''); | 113 args.add(''); |
| 111 } | 114 } |
| 112 var commands = _match(args, false); | 115 var commands = _match(args, false); |
| 113 if (commands.isEmpty) { | 116 if (commands.isEmpty) { |
| 114 // No matching commands. | 117 // No matching commands. |
| 115 return []; | 118 return new Future.value([]); |
| 116 } | 119 } |
| 117 int matchLen = commands[0]._depth; | 120 int matchLen = commands[0]._depth; |
| 118 if (matchLen < args.length) { | 121 if (matchLen < args.length) { |
| 119 // We were able to find a command which matches a prefix of the | 122 // We were able to find a command which matches a prefix of the |
| 120 // args, but not the full list. | 123 // args, but not the full list. |
| 121 if (commands.length == 1) { | 124 if (commands.length == 1) { |
| 122 // The matching command is unique. Attempt to provide local | 125 // The matching command is unique. Attempt to provide local |
| 123 // argument completion from the command. | 126 // argument completion from the command. |
| 124 return commands[0]._buildCompletions(args, true); | 127 return commands[0]._buildCompletions(args, true); |
| 125 } else { | 128 } else { |
| 126 // An ambiguous prefix match leaves us nowhere. The user is | 129 // An ambiguous prefix match leaves us nowhere. The user is |
| 127 // typing a bunch of stuff that we don't know how to complete. | 130 // typing a bunch of stuff that we don't know how to complete. |
| 128 return []; | 131 return new Future.value([]); |
| 129 } | 132 } |
| 130 } | 133 } |
| 131 | 134 |
| 132 // We have found a set of commands which match all of the args. | 135 // We have found a set of commands which match all of the args. |
| 133 // Return the completions strings. | 136 // Return the completions strings. |
| 134 var prefix = _concatArgs(args, args.length - 1); | 137 var prefix = _concatArgs(args, args.length - 1); |
| 135 var completions = | 138 var completions = |
| 136 commands.map((command) => '${prefix}${command.name} ').toList(); | 139 commands.map((command) => '${prefix}${command.name} ').toList(); |
| 137 if (showAll && matchLen == args.length) { | 140 if (showAll && matchLen == args.length) { |
| 138 // If we are showing all possiblities, also include local | 141 // If we are showing all possiblities, also include local |
| 139 // completions for the parent command. | 142 // completions for the parent command. |
| 140 completions.addAll(commands[0]._parent._buildCompletions(args, false)); | 143 return commands[0]._parent._buildCompletions(args, false) |
| 144 .then((localCompletions) { |
| 145 completions.addAll(localCompletions); |
| 146 return completions; |
| 147 }); |
| 141 } | 148 } |
| 142 return completions; | 149 return new Future.value(completions); |
| 143 } | 150 } |
| 144 | 151 |
| 145 // Runs a command. | 152 // Runs a command. |
| 146 Future runCommand(String line) { | 153 Future runCommand(String line) { |
| 147 var args = _splitLine(line); | 154 var args = _splitLine(line); |
| 148 var commands = _match(args, true); | 155 var commands = _match(args, true); |
| 149 if (commands.isEmpty) { | 156 if (commands.isEmpty) { |
| 150 // TODO(turnidge): Add a proper exception class for this. | 157 // TODO(turnidge): Add a proper exception class for this. |
| 151 return new Future.error('notfound'); | 158 return new Future.error('notfound'); |
| 152 } else if (commands.length == 1) { | 159 } else if (commands.length == 1) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 165 } | 172 } |
| 166 | 173 |
| 167 // A node in the command tree. | 174 // A node in the command tree. |
| 168 abstract class Command extends _CommandBase { | 175 abstract class Command extends _CommandBase { |
| 169 Command(this.name, List<Command> children) : super(children); | 176 Command(this.name, List<Command> children) : super(children); |
| 170 | 177 |
| 171 final name; | 178 final name; |
| 172 | 179 |
| 173 toString() => 'Command(${name})'; | 180 toString() => 'Command(${name})'; |
| 174 } | 181 } |
| OLD | NEW |