Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(911)

Side by Side Diff: runtime/observatory/lib/src/cli/command.dart

Issue 848393005: Command line parsing utilities. Not yet used in observatory. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
Cutch 2015/01/15 22:00:08 2015
turnidge 2015/01/16 18:27:42 Done.
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.
4
5 part of cli;
6
7 // Splits a line into a list of string args.
8 List<String> _splitLine(String line) {
9 var args = line.split(' ').where((arg) {
10 return arg != ' ' && arg != '';
11 }).toList();
12 return args;
13 }
14
15 // Concatenates the first 'count' args.
16 String _concatArgs(List<String> args, int count) {
17 if (count == 0) {
18 return '';
19 }
20 return '${args.sublist(0, count).join(" ")} ';
21 }
22
23 // Shared functionality for RootCommand and Command.
24 abstract class _CommandBase {
25 _CommandBase(List<Command> children) {
26 assert(children != null);
27 _children.addAll(children);
28 for (var child in _children) {
29 child._parent = this;
30 }
31 }
32
33 // A command may optionally have sub-commands.
34 List<Command> _children = [];
35
36 _CommandBase _parent;
37 int get _depth => (_parent == null ? 0 : _parent._depth + 1);
38
39 // Override in subclasses to provide command-specific argument completion.
40 //
41 // Given a list of arguments to this command, provide a list of
42 // possible completions for those arguments.
43 List<String> complete(List<String> args) => [];
44
45 // Override in subclasses to provide command-specific execution.
46 Future run(List<String> args);
47
48 // Returns a list of local subcommands which match the args.
49 List<Command> _matchLocal(String arg, bool preferExact) {
50 var matches = new List<Command>();
51 for (var child in _children) {
52 if (child.name.startsWith(arg)) {
53 if (preferExact && child.name == arg) {
54 return [child];
55 }
56 matches.add(child);
57 }
58 }
59 return matches;
60 }
61
62 // Returns the set of commands could be triggered by a list of
63 // arguments.
64 List<Command> _match(List<String> args, bool preferExact) {
65 if (args.isEmpty) {
66 return [];
67 }
68 bool lastArg = (args.length == 1);
69 var matches = _matchLocal(args[0], !lastArg || preferExact);
70 if (matches.isEmpty) {
71 return [];
72 } else if (matches.length == 1) {
73 var childMatches = matches[0]._match(args.sublist(1), preferExact);
74 if (childMatches.isEmpty) {
75 return matches;
76 } else {
77 return childMatches;
78 }
79 } else {
80 return matches;
81 }
82 }
83
84 // Builds a list of completions for this command.
85 List<String> _buildCompletions(List<String> args,
86 bool addEmptyString) {
87 var completions = complete(args.sublist(_depth, args.length));
88 if (addEmptyString && completions.isEmpty && args[args.length - 1] == '') {
89 // Special case allowance for an empty particle at the end of
90 // the command.
91 completions = [''];
92 }
93 var prefix = _concatArgs(args, _depth);
94 return completions.map((str) => '${prefix}${str}').toList();
95 }
96
97 }
98
99 // The root of a tree of commands.
100 class RootCommand extends _CommandBase {
101 RootCommand(List<Command> children) : super(children);
102
103 // Provides a list of possible completions for a line of text.
104 List<String> completeCommand(String line) {
105 var args = _splitLine(line);
106 bool showAll = line.endsWith(' ') || args.isEmpty;
107 if (showAll) {
108 // Adding an empty string to the end causes us to match all
109 // subcommands of the last command.
110 args.add('');
111 }
112 var commands = _match(args, false);
113 if (commands.isEmpty) {
114 // No matching commands.
115 return [];
116 }
117 int matchLen = commands[0]._depth;
118 if (matchLen < args.length) {
119 // We were able to find a command which matches a prefix of the
120 // args, but not the full list.
121 if (commands.length == 1) {
122 // The matching command is unique. Attempt to provide local
123 // argument completion from the command.
124 return commands[0]._buildCompletions(args, true);
125 } else {
126 // An ambiguous prefix match leaves us nowhere. The user is
127 // typing a bunch of stuff that we don't know how to complete.
128 return [];
129 }
130 }
131
132 // We have found a set of commands which match all of the args.
133 // Return the completions strings.
134 var prefix = _concatArgs(args, args.length - 1);
135 var completions =
136 commands.map((command) => '${prefix}${command.name} ').toList();
137 if (showAll && matchLen == args.length) {
138 // If we are showing all possiblities, also include local
139 // completions for the parent command.
140 completions.addAll(commands[0]._parent._buildCompletions(args, false));
141 }
142 return completions;
143 }
144
145 // Runs a command.
146 Future runCommand(String line) {
147 var args = _splitLine(line);
148 var commands = _match(args, true);
149 if (commands.isEmpty) {
150 // TODO(turnidge): Add a proper exception class for this.
151 return new Future.error('notfound');
152 } else if (commands.length == 1) {
153 return commands[0].run(args.sublist(commands[0]._depth));
154 } else {
155 // TODO(turnidge): Add a proper exception class for this.
156 return new Future.error('ambiguous');
157 }
158 }
159
160 Future run(List<String> args) {
161 throw 'should-not-execute-the-root-command';
162 }
163
164 toString() => 'RootCommand';
165 }
166
167 // A node in the command tree.
168 abstract class Command extends _CommandBase {
169 Command(this.name, List<Command> children) : super(children);
170
171 final name;
172
173 toString() => 'Command(${name})';
174 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698