Skip to content

Commit

Permalink
[#411] Bugfix: Completion candidates were only generated for the firs…
Browse files Browse the repository at this point in the history
…t option, not for subsequent options.
  • Loading branch information
remkop committed Jul 13, 2018
1 parent 526bf99 commit 81b7b27
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 32 deletions.
33 changes: 33 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
# picocli Release Notes

# <a name="3.3.0"></a> Picocli 3.3.0
The picocli community is pleased to announce picocli 3.3.0.

This release contains a bugfix for the JLine TAB completion support and error message improvements.

This is the thirty-fourth public release.
Picocli follows [semantic versioning](http://semver.org/).

## <a name="3.3.0"></a> Table of Contents
* [New and noteworthy](#3.3.0-new)
* [Promoted features](#3.3.0-promoted)
* [Fixed issues](#3.3.0-fixes)
* [Deprecations](#3.3.0-deprecated)
* [Potential breaking changes](#3.3.0-breaking-changes)

## <a name="3.3.0-new"></a> New and Noteworthy
###

## <a name="3.3.0-promoted"></a> Promoted Features
Promoted features are features that were incubating in previous versions of picocli but are now supported and subject to backwards compatibility.

No features have been promoted in this picocli release.

## <a name="3.3.0-fixes"></a> Fixed issues
- [#411] Bugfix: Completion candidates were only generated for the first option, not for subsequent options.

## <a name="3.3.0-deprecated"></a> Deprecations
No features were deprecated in this release.

## <a name="3.3.0-breaking-changes"></a> Potential breaking changes
This release has no breaking changes.


# <a name="3.2.0"></a> Picocli 3.2.0
The picocli community is pleased to announce picocli 3.2.0.

Expand Down
29 changes: 23 additions & 6 deletions src/main/java/picocli/AutoComplete.java
Original file line number Diff line number Diff line change
Expand Up @@ -579,12 +579,8 @@ public static int complete(CommandSpec spec, String[] args, int argIndex, int po
CommandLine parser = new CommandLine(spec);
ParseResult parseResult = parser.parseArgs(args);
if (argIndex >= parseResult.tentativeMatch.size()) {
int count = parseResult.tentativeMatch.size();
if (count == 0) {
addCandidatesForArgsFollowing(spec, candidates);
} else {
addCandidatesForArgsFollowing(parseResult.tentativeMatch.get(count - 1), candidates);
}
Object startPoint = findCompletionStartPoint(parseResult);
addCandidatesForArgsFollowing(startPoint, candidates);
} else {
Object obj = parseResult.tentativeMatch.get(argIndex);
if (obj instanceof CommandSpec) { // subcommand
Expand Down Expand Up @@ -615,7 +611,28 @@ public static int complete(CommandSpec spec, String[] args, int argIndex, int po
spec.parser().collectErrors(reset);
}
}
private static Object findCompletionStartPoint(ParseResult parseResult) {
List<Object> tentativeMatches = parseResult.tentativeMatch;
for (int i = 1; i <= tentativeMatches.size(); i++) {
Object found = tentativeMatches.get(tentativeMatches.size() - i);
if (found instanceof CommandSpec) {
return found;
}
if (found instanceof ArgSpec) {
CommandLine.Range arity = ((ArgSpec) found).arity();
if (i < arity.min) {
return found; // not all parameters have been supplied yet
} else {
return findCommandFor((ArgSpec) found, parseResult.commandSpec());
}
}
}
return parseResult.commandSpec();
}

private static CommandSpec findCommandFor(ArgSpec arg, CommandSpec cmd) {
return (arg instanceof OptionSpec) ? findCommandFor((OptionSpec) arg, cmd) : findCommandFor((PositionalParamSpec) arg, cmd);
}
private static CommandSpec findCommandFor(OptionSpec option, CommandSpec commandSpec) {
OptionSpec found = commandSpec.findOption(option.longestName());
if (found != null) { return commandSpec; }
Expand Down
81 changes: 55 additions & 26 deletions src/test/java/picocli/AutoCompleteTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -508,32 +508,55 @@ public void testComplete() {

CommandSpec spec = hierarchy.getCommandSpec();
spec.parser().collectErrors(true);
List<CharSequence> result = new ArrayList<CharSequence>();
String[] args = new String[] {};

test(spec, a(), 0, 0, 500, l("--help", "--version", "-V", "-h", "sub1", "sub2"));
test(spec, a("-"), 0, 0, 500, l("--help", "--version", "-V", "-h", "sub1", "sub2"));
test(spec, a("-"), 0, 1, 500, l("-help", "-version", "V", "h"));
test(spec, a("-h"), 0, 1, 500, l("-help", "-version", "V", "h"));
test(spec, a("-h"), 0, 2, 500, l(""));
test(spec, a("s"), 0, 1, 500, l("ub1", "ub2"));
test(spec, a("sub1"), 1, 0, 500, l("--candidates", "--num", "--str"));
test(spec, a("sub1", "-"), 1, 0, 500, l("--candidates", "--num", "--str"));
test(spec, a("sub1", "-"), 1, 1, 500, l("-candidates", "-num", "-str"));
test(spec, a("sub1", "--"), 1, 1, 500, l("-candidates", "-num", "-str"));
test(spec, a("sub1", "--"), 1, 2, 500, l("candidates", "num", "str"));
test(spec, a("sub1", "--c"), 1, 2, 500, l("candidates", "num", "str"));
test(spec, a("sub1", "--c"), 1, 3, 500, l("andidates"));
test(spec, a("sub1", "--candidates"), 2, 0, 500, l("a", "b", "c"));
test(spec, a("sub1", "--num"), 2, 0, 500, l());
test(spec, a("sub1", "--str"), 2, 0, 500, l());
test(spec, a("sub2"), 1, 0, 500, l("--directory", "--num2", "-d", "subsub1", "subsub2"));
test(spec, a("sub2", "-"), 1, 1, 500, l("-directory", "-num2", "d"));
test(spec, a("sub2", "-d"), 2, 0, 500, l());
test(spec, a("sub2", "subsub1"), 2, 0, 500, l("--host", "-h"));
test(spec, a("sub2", "subsub2"), 2, 0, 500, l("--timeUnit", "--timeout", "-t", "-u", "a", "b", "c"));
test(spec, a("sub2", "subsub2", "-"), 2, 1, 500, l("-timeUnit", "-timeout", "t", "u"));
test(spec, a("sub2", "subsub2", "a"), 2, 1, 500, l(""));
int cur = 500;

test(spec, a(), 0, 0, cur, l("--help", "--version", "-V", "-h", "sub1", "sub2"));
test(spec, a("-"), 0, 0, cur, l("--help", "--version", "-V", "-h", "sub1", "sub2"));
test(spec, a("-"), 0, 1, cur, l("-help", "-version", "V", "h"));
test(spec, a("-h"), 0, 1, cur, l("-help", "-version", "V", "h"));
test(spec, a("-h"), 0, 2, cur, l(""));
test(spec, a("s"), 0, 1, cur, l("ub1", "ub2"));
test(spec, a("sub1"), 1, 0, cur, l("--candidates", "--num", "--str"));
test(spec, a("sub1", "-"), 1, 0, cur, l("--candidates", "--num", "--str"));
test(spec, a("sub1", "-"), 1, 1, cur, l("-candidates", "-num", "-str"));
test(spec, a("sub1", "--"), 1, 1, cur, l("-candidates", "-num", "-str"));
test(spec, a("sub1", "--"), 1, 2, cur, l("candidates", "num", "str"));
test(spec, a("sub1", "--c"), 1, 2, cur, l("candidates", "num", "str"));
test(spec, a("sub1", "--c"), 1, 3, cur, l("andidates"));
test(spec, a("sub1", "--candidates"), 2, 0, cur, l("a", "b", "c"));
test(spec, a("sub1", "--candidates", "a"), 2, 1, cur, l(""));
test(spec, a("sub1", "--candidates", "a"), 3, 0, cur, l("--candidates", "--num", "--str"));
test(spec, a("sub1", "--candidates", "a", "-"), 3, 1, cur, l("-candidates", "-num", "-str"));
test(spec, a("sub1", "--candidates", "a", "--"), 3, 2, cur, l("candidates", "num", "str"));
test(spec, a("sub1", "--num"), 2, 0, cur, l());
test(spec, a("sub1", "--str"), 2, 0, cur, l());
test(spec, a("sub2"), 1, 0, cur, l("--directory", "--num2", "-d", "subsub1", "subsub2"));
test(spec, a("sub2", "-"), 1, 1, cur, l("-directory", "-num2", "d"));
test(spec, a("sub2", "-d"), 2, 0, cur, l());
test(spec, a("sub2", "-d", "/"), 3, 0, cur, l("--directory", "--num2", "-d", "subsub1", "subsub2"));
test(spec, a("sub2", "-d", "/", "-"), 3, 1, cur, l("-directory", "-num2", "d"));
test(spec, a("sub2", "-d", "/", "--"), 3, 2, cur, l("directory", "num2"));
test(spec, a("sub2", "-d", "/", "--n"), 3, 3, cur, l("um2"));
test(spec, a("sub2", "-d", "/", "--num2"), 3, 6, cur, l(""));
test(spec, a("sub2", "-d", "/", "--num2"), 4, 0, cur, l());
test(spec, a("sub2", "-d", "/", "--num2", "0"), 4, 1, cur, l());
test(spec, a("sub2", "-d", "/", "--num2", "0"), 5, 0, cur, l("--directory", "--num2", "-d", "subsub1", "subsub2"));
test(spec, a("sub2", "-d", "/", "--num2", "0", "s"), 5, 1, cur, l("ubsub1", "ubsub2"));
test(spec, a("sub2", "subsub1"), 2, 0, cur, l("--host", "-h"));
test(spec, a("sub2", "subsub2"), 2, 0, cur, l("--timeUnit", "--timeout", "-t", "-u", "a", "b", "c"));
test(spec, a("sub2", "subsub2", "-"), 2, 1, cur, l("-timeUnit", "-timeout", "t", "u"));
test(spec, a("sub2", "subsub2", "-t"), 2, 2, cur, l(""));
test(spec, a("sub2", "subsub2", "-t"), 3, 0, cur, l());
test(spec, a("sub2", "subsub2", "-t", "0"), 3, 1, cur, l());
test(spec, a("sub2", "subsub2", "-t", "0"), 4, 0, cur, l("--timeUnit", "--timeout", "-t", "-u", "a", "b", "c"));
test(spec, a("sub2", "subsub2", "-t", "0", "-"), 4, 1, cur, l("-timeUnit", "-timeout", "t", "u"));
test(spec, a("sub2", "subsub2", "-t", "0", "--"), 4, 2, cur, l("timeUnit", "timeout"));
test(spec, a("sub2", "subsub2", "-t", "0", "--t"), 4, 3, cur, l("imeUnit", "imeout"));
test(spec, a("sub2", "subsub2", "-t", "0", "-u"), 4, 2, cur, l(""));
test(spec, a("sub2", "subsub2", "-t", "0", "-u"), 5, 0, cur, timeUnitValues());
test(spec, a("sub2", "subsub2", "-t", "0", "-u", "M"),5, 1, cur, l("ICROSECONDS", "ILLISECONDS", "INUTES"));
test(spec, a("sub2", "subsub2", "a"), 2, 1, cur, l(""));
test(spec, a("sub2", "subsub2", "a"), 3, 0, cur, l("--timeUnit", "--timeout", "-t", "-u", "a", "b", "c"));
}

private static void test(CommandSpec spec, String[] args, int argIndex, int positionInArg, int cursor, List<CharSequence> expected) {
Expand All @@ -552,6 +575,12 @@ private static List<CharSequence> l(CharSequence... args) {
return Arrays.asList(args);
}

private static List<CharSequence> timeUnitValues() {
List<CharSequence> result = new ArrayList<CharSequence>();
for (TimeUnit tu : TimeUnit.values()) { result.add(tu.toString()); }
return result;
}

static class CharSequenceSort implements Comparator<CharSequence> {
public int compare(CharSequence left, CharSequence right) {
return left.toString().compareTo(right.toString());
Expand Down

0 comments on commit 81b7b27

Please sign in to comment.