| 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. Each arg retains any | 7 // Splits a line into a list of string args. Each arg retains any |
| 8 // trailing whitespace so that we can reconstruct the original command | 8 // trailing whitespace so that we can reconstruct the original command |
| 9 // line from the pieces. | 9 // line from the pieces. |
| 10 List<String> _splitLine(String line) { | 10 List<String> _splitLine(String line) { |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 77 // arguments. | 77 // arguments. |
| 78 List<Command> _match(List<String> args, bool preferExact) { | 78 List<Command> _match(List<String> args, bool preferExact) { |
| 79 if (args.isEmpty) { | 79 if (args.isEmpty) { |
| 80 return []; | 80 return []; |
| 81 } | 81 } |
| 82 bool lastArg = (args.length == 1); | 82 bool lastArg = (args.length == 1); |
| 83 var matches = _matchLocal(args[0], !lastArg || preferExact); | 83 var matches = _matchLocal(args[0], !lastArg || preferExact); |
| 84 if (matches.isEmpty) { | 84 if (matches.isEmpty) { |
| 85 return []; | 85 return []; |
| 86 } else if (matches.length == 1) { | 86 } else if (matches.length == 1) { |
| 87 var childMatches = matches[0]._match(args.sublist(1), preferExact); | 87 var childMatches = matches[0]._match(args.sublist(1), preferExact); |
| 88 if (childMatches.isEmpty) { | 88 if (childMatches.isEmpty) { |
| 89 return matches; | 89 return matches; |
| 90 } else { | 90 } else { |
| 91 return childMatches; | 91 return childMatches; |
| 92 } | 92 } |
| 93 } else { | 93 } else { |
| 94 return matches; | 94 return matches; |
| 95 } | 95 } |
| 96 } | 96 } |
| 97 | 97 |
| 98 // Builds a list of completions for this command. | 98 // Builds a list of completions for this command. |
| 99 Future<List<String>> _buildCompletions(List<String> args, | 99 Future<List<String>> _buildCompletions( |
| 100 bool addEmptyString) { | 100 List<String> args, bool addEmptyString) { |
| 101 return complete(args.sublist(_depth, args.length)) | 101 return complete(args.sublist(_depth, args.length)).then((completions) { |
| 102 .then((completions) { | 102 if (addEmptyString && |
| 103 if (addEmptyString && completions.isEmpty && | 103 completions.isEmpty && |
| 104 args[args.length - 1] == '') { | 104 args[args.length - 1] == '') { |
| 105 // Special case allowance for an empty particle at the end of | 105 // Special case allowance for an empty particle at the end of |
| 106 // the command. | 106 // the command. |
| 107 completions = ['']; | 107 completions = ['']; |
| 108 } | 108 } |
| 109 var prefix = _concatArgs(args, _depth); | 109 var prefix = _concatArgs(args, _depth); |
| 110 return completions.map((str) => '${prefix}${str}').toList(); | 110 return completions.map((str) => '${prefix}${str}').toList(); |
| 111 }); | 111 }); |
| 112 } | 112 } |
| 113 | |
| 114 } | 113 } |
| 115 | 114 |
| 116 // The root of a tree of commands. | 115 // The root of a tree of commands. |
| 117 class RootCommand extends _CommandBase { | 116 class RootCommand extends _CommandBase { |
| 118 RootCommand(List<Command> children, [List<String> history]) | 117 RootCommand(List<Command> children, [List<String> history]) |
| 119 : this._(children, history ?? ['']); | 118 : this._(children, history ?? ['']); |
| 120 | 119 |
| 121 RootCommand._(List<Command> children, List<String> history) | 120 RootCommand._(List<Command> children, List<String> history) |
| 122 : history = history, | 121 : history = history, |
| 123 historyPos = history.length - 1, | 122 historyPos = history.length - 1, |
| 124 super(children); | 123 super(children); |
| 125 | 124 |
| 126 // Provides a list of possible completions for a line of text. | 125 // Provides a list of possible completions for a line of text. |
| 127 Future<List<String>> completeCommand(String line) { | 126 Future<List<String>> completeCommand(String line) { |
| 128 var args = _splitLine(line); | 127 var args = _splitLine(line); |
| 129 bool showAll = line.endsWith(' ') || args.isEmpty; | 128 bool showAll = line.endsWith(' ') || args.isEmpty; |
| 130 if (showAll) { | 129 if (showAll) { |
| 131 // Adding an empty string to the end causes us to match all | 130 // Adding an empty string to the end causes us to match all |
| 132 // subcommands of the last command. | 131 // subcommands of the last command. |
| 133 args.add(''); | 132 args.add(''); |
| 134 } | 133 } |
| 135 var commands = _match(args, false); | 134 var commands = _match(args, false); |
| 136 if (commands.isEmpty) { | 135 if (commands.isEmpty) { |
| 137 // No matching commands. | 136 // No matching commands. |
| 138 return new Future.value([]); | 137 return new Future.value([]); |
| 139 } | 138 } |
| 140 int matchLen = commands[0]._depth; | 139 int matchLen = commands[0]._depth; |
| 141 if (matchLen < args.length) { | 140 if (matchLen < args.length) { |
| 142 // We were able to find a command which matches a prefix of the | 141 // We were able to find a command which matches a prefix of the |
| 143 // args, but not the full list. | 142 // args, but not the full list. |
| 144 if (commands.length == 1) { | 143 if (commands.length == 1) { |
| 145 // The matching command is unique. Attempt to provide local | 144 // The matching command is unique. Attempt to provide local |
| 146 // argument completion from the command. | 145 // argument completion from the command. |
| 147 return commands[0]._buildCompletions(args, true); | 146 return commands[0]._buildCompletions(args, true); |
| 148 } else { | 147 } else { |
| 149 // An ambiguous prefix match leaves us nowhere. The user is | 148 // An ambiguous prefix match leaves us nowhere. The user is |
| 150 // typing a bunch of stuff that we don't know how to complete. | 149 // typing a bunch of stuff that we don't know how to complete. |
| 151 return new Future.value([]); | 150 return new Future.value([]); |
| 152 } | 151 } |
| 153 } | 152 } |
| 154 | 153 |
| 155 // We have found a set of commands which match all of the args. | 154 // We have found a set of commands which match all of the args. |
| 156 // Return the completion strings. | 155 // Return the completion strings. |
| 157 var prefix = _concatArgs(args, args.length - 1); | 156 var prefix = _concatArgs(args, args.length - 1); |
| 158 var completions = | 157 var completions = |
| 159 commands.map((command) => '${prefix}${command.name} ').toList(); | 158 commands.map((command) => '${prefix}${command.name} ').toList(); |
| 160 if (matchLen == args.length) { | 159 if (matchLen == args.length) { |
| 161 // If we are showing all possiblities, also include local | 160 // If we are showing all possiblities, also include local |
| 162 // completions for the parent command. | 161 // completions for the parent command. |
| 163 return commands[0]._parent._buildCompletions(args, false) | 162 return commands[0] |
| 164 .then((localCompletions) { | 163 ._parent |
| 165 completions.addAll(localCompletions); | 164 ._buildCompletions(args, false) |
| 166 return completions; | 165 .then((localCompletions) { |
| 167 }); | 166 completions.addAll(localCompletions); |
| 167 return completions; |
| 168 }); |
| 168 } | 169 } |
| 169 return new Future.value(completions); | 170 return new Future.value(completions); |
| 170 } | 171 } |
| 171 | 172 |
| 172 // Runs a command. | 173 // Runs a command. |
| 173 Future runCommand(String line) { | 174 Future runCommand(String line) { |
| 174 _historyAdvance(line); | 175 _historyAdvance(line); |
| 175 var args = _splitLine(line); | 176 var args = _splitLine(line); |
| 176 var commands = _match(args, true); | 177 var commands = _match(args, true); |
| 177 if (commands.isEmpty) { | 178 if (commands.isEmpty) { |
| 178 // TODO(turnidge): Add a proper exception class for this. | 179 // TODO(turnidge): Add a proper exception class for this. |
| 179 return new Future.error('No such command'); | 180 return new Future.error('No such command'); |
| 180 } else if (commands.length == 1) { | 181 } else if (commands.length == 1) { |
| 181 return commands[0].run(args.sublist(commands[0]._depth)); | 182 return commands[0].run(args.sublist(commands[0]._depth)); |
| 182 } else { | 183 } else { |
| 183 // TODO(turnidge): Add a proper exception class for this. | 184 // TODO(turnidge): Add a proper exception class for this. |
| 184 return new Future.error('Ambiguous command'); | 185 return new Future.error('Ambiguous command'); |
| 185 } | 186 } |
| 186 } | 187 } |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 246 if (_parent is RootCommand) { | 247 if (_parent is RootCommand) { |
| 247 return name; | 248 return name; |
| 248 } else { | 249 } else { |
| 249 Command parent = _parent; | 250 Command parent = _parent; |
| 250 return '${parent.fullName} $name'; | 251 return '${parent.fullName} $name'; |
| 251 } | 252 } |
| 252 } | 253 } |
| 253 | 254 |
| 254 toString() => 'Command(${name})'; | 255 toString() => 'Command(${name})'; |
| 255 } | 256 } |
| OLD | NEW |