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

Unified 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: code review 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/observatory/lib/cli.dart ('k') | runtime/observatory/test/command_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/observatory/lib/src/cli/command.dart
diff --git a/runtime/observatory/lib/src/cli/command.dart b/runtime/observatory/lib/src/cli/command.dart
new file mode 100644
index 0000000000000000000000000000000000000000..8c54a6453e030793d9667d8ce19c0cfc33a680ee
--- /dev/null
+++ b/runtime/observatory/lib/src/cli/command.dart
@@ -0,0 +1,174 @@
+// Copyright (c) 2015, 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.
+
+part of cli;
+
+// Splits a line into a list of string args.
+List<String> _splitLine(String line) {
+ var args = line.split(' ').where((arg) {
+ return arg != ' ' && arg != '';
+ }).toList();
+ return args;
+}
+
+// Concatenates the first 'count' args.
+String _concatArgs(List<String> args, int count) {
+ if (count == 0) {
+ return '';
+ }
+ return '${args.sublist(0, count).join(" ")} ';
+}
+
+// Shared functionality for RootCommand and Command.
+abstract class _CommandBase {
+ _CommandBase(List<Command> children) {
+ assert(children != null);
+ _children.addAll(children);
+ for (var child in _children) {
+ child._parent = this;
+ }
+ }
+
+ // A command may optionally have sub-commands.
+ List<Command> _children = [];
+
+ _CommandBase _parent;
+ int get _depth => (_parent == null ? 0 : _parent._depth + 1);
+
+ // Override in subclasses to provide command-specific argument completion.
+ //
+ // Given a list of arguments to this command, provide a list of
+ // possible completions for those arguments.
+ List<String> complete(List<String> args) => [];
+
+ // Override in subclasses to provide command-specific execution.
+ Future run(List<String> args);
+
+ // Returns a list of local subcommands which match the args.
+ List<Command> _matchLocal(String arg, bool preferExact) {
+ var matches = new List<Command>();
+ for (var child in _children) {
+ if (child.name.startsWith(arg)) {
+ if (preferExact && child.name == arg) {
+ return [child];
+ }
+ matches.add(child);
+ }
+ }
+ return matches;
+ }
+
+ // Returns the set of commands could be triggered by a list of
+ // arguments.
+ List<Command> _match(List<String> args, bool preferExact) {
+ if (args.isEmpty) {
+ return [];
+ }
+ bool lastArg = (args.length == 1);
+ var matches = _matchLocal(args[0], !lastArg || preferExact);
+ if (matches.isEmpty) {
+ return [];
+ } else if (matches.length == 1) {
+ var childMatches = matches[0]._match(args.sublist(1), preferExact);
+ if (childMatches.isEmpty) {
+ return matches;
+ } else {
+ return childMatches;
+ }
+ } else {
+ return matches;
+ }
+ }
+
+ // Builds a list of completions for this command.
+ List<String> _buildCompletions(List<String> args,
+ bool addEmptyString) {
+ var completions = complete(args.sublist(_depth, args.length));
+ if (addEmptyString && completions.isEmpty && args[args.length - 1] == '') {
+ // Special case allowance for an empty particle at the end of
+ // the command.
+ completions = [''];
+ }
+ var prefix = _concatArgs(args, _depth);
+ return completions.map((str) => '${prefix}${str}').toList();
+ }
+
+}
+
+// The root of a tree of commands.
+class RootCommand extends _CommandBase {
+ RootCommand(List<Command> children) : super(children);
+
+ // Provides a list of possible completions for a line of text.
+ List<String> completeCommand(String line) {
+ var args = _splitLine(line);
+ bool showAll = line.endsWith(' ') || args.isEmpty;
+ if (showAll) {
+ // Adding an empty string to the end causes us to match all
+ // subcommands of the last command.
+ args.add('');
+ }
+ var commands = _match(args, false);
+ if (commands.isEmpty) {
+ // No matching commands.
+ return [];
+ }
+ int matchLen = commands[0]._depth;
+ if (matchLen < args.length) {
+ // We were able to find a command which matches a prefix of the
+ // args, but not the full list.
+ if (commands.length == 1) {
+ // The matching command is unique. Attempt to provide local
+ // argument completion from the command.
+ return commands[0]._buildCompletions(args, true);
+ } else {
+ // An ambiguous prefix match leaves us nowhere. The user is
+ // typing a bunch of stuff that we don't know how to complete.
+ return [];
+ }
+ }
+
+ // We have found a set of commands which match all of the args.
+ // Return the completions strings.
+ var prefix = _concatArgs(args, args.length - 1);
+ var completions =
+ commands.map((command) => '${prefix}${command.name} ').toList();
+ if (showAll && matchLen == args.length) {
+ // If we are showing all possiblities, also include local
+ // completions for the parent command.
+ completions.addAll(commands[0]._parent._buildCompletions(args, false));
+ }
+ return completions;
+ }
+
+ // Runs a command.
+ Future runCommand(String line) {
+ var args = _splitLine(line);
+ var commands = _match(args, true);
+ if (commands.isEmpty) {
+ // TODO(turnidge): Add a proper exception class for this.
+ return new Future.error('notfound');
+ } else if (commands.length == 1) {
+ return commands[0].run(args.sublist(commands[0]._depth));
+ } else {
+ // TODO(turnidge): Add a proper exception class for this.
+ return new Future.error('ambiguous');
+ }
+ }
+
+ Future run(List<String> args) {
+ throw 'should-not-execute-the-root-command';
+ }
+
+ toString() => 'RootCommand';
+}
+
+// A node in the command tree.
+abstract class Command extends _CommandBase {
+ Command(this.name, List<Command> children) : super(children);
+
+ final name;
+
+ toString() => 'Command(${name})';
+}
« no previous file with comments | « runtime/observatory/lib/cli.dart ('k') | runtime/observatory/test/command_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698