Skip to content

Commit

Permalink
Suggest command based on aliases (#235)
Browse files Browse the repository at this point in the history
  • Loading branch information
sigurdm authored Feb 9, 2023
1 parent bd3ac85 commit b08471e
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 6 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 2.4.0

* Command suggestions will now also suggest based on aliases of a command.
* Introduce getter `Command.suggestionAliases` for names that cannot be used as
aliases, but will trigger suggestions.

## 2.3.2

* Require Dart 2.18
Expand Down
26 changes: 22 additions & 4 deletions lib/command_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,17 @@ class CommandRunner<T> {
SplayTreeSet<Command<T>>((a, b) => distances[a]! - distances[b]!);
for (var command in commands) {
if (command.hidden) continue;
var distance = _editDistance(name, command.name);
if (distance <= suggestionDistanceLimit) {
distances[command] = distance;
candidates.add(command);
for (var alias in [
command.name,
...command.aliases,
...command.suggestionAliases
]) {
var distance = _editDistance(name, alias);
if (distance <= suggestionDistanceLimit) {
distances[command] =
math.min(distances[command] ?? distance, distance);
candidates.add(command);
}
}
}
if (candidates.isEmpty) return '';
Expand Down Expand Up @@ -412,6 +419,17 @@ abstract class Command<T> {
/// This is intended to be overridden.
List<String> get aliases => const [];

/// Alternate non-functional names for this command.
///
/// These names won't be used in the documentation, and also they won't work
/// when invoked on the command line. But if an unknown command is used it
/// will be matched against this when creating suggestions.
///
/// A name does not have to be repeated both here and in [aliases].
///
/// This is intended to be overridden.
List<String> get suggestionAliases => const [];

Command() {
if (!argParser.allowsAnything) {
argParser.addFlag('help',
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: args
version: 2.3.2
version: 2.4.0
description: >-
Library for defining parsers for parsing raw command-line arguments into a set
of options and values using GNU and POSIX style options.
Expand Down
22 changes: 22 additions & 0 deletions test/command_runner_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,28 @@ Did you mean one of these?
throwsUsageException(
'Could not find a command named "hidde".', anything));
});

test('Suggests based on aliases', () {
var command = AliasedCommand();
runner.addCommand(command);
expect(() => runner.run(['rename']), throwsUsageException('''
Could not find a command named "rename".
Did you mean one of these?
aliased
''', anything));
});

test('Suggests based on suggestedAliases', () {
var command = SuggestionAliasedCommand();
runner.addCommand(command);
expect(() => runner.run(['renamed']), throwsUsageException('''
Could not find a command named "renamed".
Did you mean one of these?
aliased
''', anything));
});
});

group('with --help', () {
Expand Down
23 changes: 22 additions & 1 deletion test/test_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,28 @@ class AliasedCommand extends Command {
final takesArguments = false;

@override
final aliases = const ['alias', 'als'];
final aliases = const ['alias', 'als', 'renamed'];

@override
void run() {
hasRun = true;
}
}

class SuggestionAliasedCommand extends Command {
bool hasRun = false;

@override
final name = 'aliased';

@override
final description = 'Set a value.';

@override
final takesArguments = false;

@override
final suggestionAliases = const ['renamed'];

@override
void run() {
Expand Down

0 comments on commit b08471e

Please sign in to comment.