Skip to content

Commit

Permalink
picocli fish completion tests
Browse files Browse the repository at this point in the history
  • Loading branch information
serg-v committed Feb 24, 2023
1 parent 1af8274 commit a144222
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 22 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@ picocli.iml
/src/test/dejagnu.tests/testrun.log
/src/test/dejagnu.tests/testrun.sum
/src/test/dejagnu.tests/tmp/
/src/test/dejagnu.fishtests/log/completion.sum
/src/test/dejagnu.fishtests/log/completion.log
/picocli-legacy-tests/gradle/wrapper/dists/**/*.lck
/picocli-legacy-tests/gradle/wrapper/dists/**/*.ok
44 changes: 29 additions & 15 deletions src/main/java/picocli/AutoComplete.java
Original file line number Diff line number Diff line change
Expand Up @@ -539,9 +539,7 @@ public static String fish(String scriptName, CommandLine commandLine) {
if (scriptName == null) { throw new NullPointerException("scriptName"); }
if (commandLine == null) { throw new NullPointerException("commandLine"); }
List<CommandDescriptor> hierarchy = createHierarchy(scriptName, commandLine);
//print hierarchy
StringBuilder result = new StringBuilder();
//result.append("complete --command ").append(scriptName).append(" --no-files").append("\n");

String parentFunction = "";
List<CommandDescriptor> currentLevel = new ArrayList<CommandDescriptor>();
Expand All @@ -554,22 +552,18 @@ public static String fish(String scriptName, CommandLine commandLine) {
continue;
}
if (!descriptor.parentFunctionName.equals(parentFunction)) {
if (!currentLevelCommands.isEmpty()) {
processLevel(scriptName, result, currentLevel, currentLevelCommands, parentFunction, rootDescriptor);
rootDescriptor = null;
processLevel(scriptName, result, currentLevel, currentLevelCommands, parentFunction, rootDescriptor);
rootDescriptor = null;

currentLevel.clear();
currentLevelCommands.clear();
}
currentLevel.clear();
currentLevelCommands.clear();
parentFunction = descriptor.parentFunctionName;
}

currentLevel.add(descriptor);
currentLevelCommands.add(descriptor.commandName);
}
if (!currentLevelCommands.isEmpty()) {
processLevel(scriptName, result, currentLevel, currentLevelCommands, parentFunction, rootDescriptor);
}
processLevel(scriptName, result, currentLevel, currentLevelCommands, parentFunction, rootDescriptor);


return result.toString();
Expand All @@ -578,17 +572,31 @@ public static String fish(String scriptName, CommandLine commandLine) {
private static void processLevel(String scriptName, StringBuilder result, List<CommandDescriptor> currentLevel,
List<String> currentLevelCommands, String levelName,
CommandDescriptor rootDescriptor) {
result.append("\n# ").append(levelName).append(" completion \n");
result.append("set -l ").append(levelName).append(" ").append(String.join(" ", currentLevelCommands)).append(
"\n");
if (levelName.equals("")) {
levelName = "root";
}

// fish doesn't like dashes in variable names
levelName = levelName.replaceAll("-", "_");

result.append("\n# ").append(levelName).append(" completion\n");
result.append("set -l ").append(levelName);
if (!currentLevelCommands.isEmpty()) {
result.append(" ").append(String.join(" ", currentLevelCommands));
}
result.append("\n");
if (rootDescriptor != null) {
for (OptionSpec optionSpec : rootDescriptor.commandLine.getCommandSpec().options()) {
result.append("complete -c ").append(scriptName);
result.append(" -n \"not __fish_seen_subcommand_from $").append(levelName).append("\"");
result.append(" -l ").append(optionSpec.longestName().replace("--", ""));

if (optionSpec.completionCandidates() != null) {
result.append(" -f -a '").append(String.join(" ", extract(optionSpec.completionCandidates()))).append("' ");
}

String optionDescription = sanitizeDescription(optionSpec.description().length > 0 ? optionSpec.description()[0] : "");
result.append(" -d '").append(optionDescription).append("'\n");

if (!optionSpec.shortestName().equals(optionSpec.longestName())) {
result.append("complete -c ").append(scriptName);
result.append(" -n \"not __fish_seen_subcommand_from $").append(levelName).append("\"");
Expand Down Expand Up @@ -617,9 +625,15 @@ private static void processLevel(String scriptName, StringBuilder result, List<C
commandDescriptor.parentWithoutTopLevelCommand).append("'");
}
result.append(" -l ").append(optionSpec.longestName().replace("--", ""));

if (optionSpec.completionCandidates() != null) {
result.append(" -a '").append(String.join(" ", extract(optionSpec.completionCandidates()))).append("' ");
}

String optionDescription = sanitizeDescription(optionSpec.description().length > 0 ? optionSpec.description()[0] : "");
result.append(" -d '").append(optionDescription).append("'\n");


if (!optionSpec.shortestName().equals(optionSpec.longestName())) {
result.append("complete -c ").append(scriptName);
result.append(" -n \"__fish_seen_subcommand_from ").append(commandDescriptor.commandName).append("\"");
Expand Down
4 changes: 4 additions & 0 deletions src/test/dejagnu.fishtests/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Run
```
runtest --outdir log --tool completion
```
24 changes: 24 additions & 0 deletions src/test/dejagnu.fishtests/completion/basicExample.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
set timeout 1

# Setup completion and fake command
send "source ../resources/basic.fish\r"
expect -re "(.+>)"

send "function basicExample; echo 'do'; end\r"
expect -re "(.+>)"

set cmd "basicExample -"
set test "Tab should show options for '$cmd'"
set candidates "-t -u --timeout --timeUnit --timeUnit="
run_completion_test $cmd $test $candidates

set cmd "basicExample --"
set test "Tab should show options for '$cmd'"
set candidates "--timeout --timeUnit --timeUnit="
run_completion_test $cmd $test $candidates

set cmd "basicExample --timeUnit="
set test "Tab should show time unit enum values for '$cmd'"
#set candidates "1 2 3"
set candidates "\u2026timeUnit=DAYS \u2026timeUnit=MICROSECONDS \u2026timeUnit=MINUTES \u2026timeUnit=SECONDS\r\n\u2026timeUnit=HOURS \u2026timeUnit=MILLISECONDS \u2026timeUnit=NANOSECONDS "
run_completion_test $cmd $test $candidates
14 changes: 14 additions & 0 deletions src/test/dejagnu.fishtests/completion/picocompletion-demo.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
exp_internal 1
set timeout 1

# Setup completion and fake command
send "source ../resources/picocompletion-demo_completion.fish\r"
expect -re "(.+>)"

send "function picocompletion-demo; echo 'do'; end\r"
expect -re "(.+>)"

set cmd "picocompletion-demo "
set test "Tab should show sub1 and sub2 for '${cmd}'"
set candidates "sub1.*sub1-alias.*sub2.*sub2-alias.*"
run_completion_test $cmd $test $candidates
4 changes: 4 additions & 0 deletions src/test/dejagnu.fishtests/lib/completion.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
exp_spawn fish --no-config
expect -re "(.+>)"

source $::srcdir/lib/library.exp
11 changes: 11 additions & 0 deletions src/test/dejagnu.fishtests/lib/library.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
proc run_completion_test {cmd test candidates} {
send "${cmd}\t"
expect {
-re "(\n${candidates}\u001b)" { pass $test }
timeout { fail $test }
}
puts "###Output"
puts "$expect_out(1,string)"
send "\x03"
expect ">"
}
Empty file modified src/test/dejagnu.tests/runCompletion
100644 → 100755
Empty file.
36 changes: 29 additions & 7 deletions src/test/java/picocli/AutoCompleteTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ public void basic() throws Exception {
assertEquals(expected, script);
}

@Test
public void basicFish() throws Exception {
String script = AutoComplete.fish("basicExample", new CommandLine(new BasicExample()));
System.out.println(script);
String expected = loadTextFromClasspath("/basic.fish");
assertEquals(expected, script);
}

public static class TopLevel {
@Option(names = {"-V", "--version"}, help = true) boolean versionRequested;
@Option(names = {"-h", "--help"}, help = true) boolean helpRequested;
Expand Down Expand Up @@ -184,19 +192,33 @@ public static class Sub2Child3 {
// }
@Test
public void nestedSubcommands() throws Exception {
CommandLine hierarchy = new CommandLine(new TopLevel())
.addSubcommand("sub1", new Sub1())
.addSubcommand("sub2", new CommandLine(new Sub2())
.addSubcommand("subsub1", new Sub2Child1())
.addSubcommand("subsub2", new Sub2Child2())
.addSubcommand("subsub3", new Sub2Child3())
);
CommandLine hierarchy = getNestedSubcommandsCommandLine();
String script = AutoComplete.bash("picocompletion-demo", hierarchy);
String expected = format(loadTextFromClasspath("/picocompletion-demo_completion.bash"),
CommandLine.VERSION, concat("\" \"", TimeUnit.values()));
assertEquals(expected, script);
}

@Test
public void nestedSubcommandsFish() throws Exception {
CommandLine hierarchy = getNestedSubcommandsCommandLine();
String script = AutoComplete.fish("picocompletion-demo", hierarchy);
System.out.println(script);
String expected = loadTextFromClasspath("/picocompletion-demo_completion.fish");
assertEquals(expected, script);
}

private static CommandLine getNestedSubcommandsCommandLine() {
CommandLine hierarchy = new CommandLine(new TopLevel())
.addSubcommand("sub1", new Sub1())
.addSubcommand("sub2", new CommandLine(new Sub2())
.addSubcommand("subsub1", new Sub2Child1())
.addSubcommand("subsub2", new Sub2Child2())
.addSubcommand("subsub3", new Sub2Child3())
);
return hierarchy;
}

@Test
public void helpCommand() {
CommandLine hierarchy = new CommandLine(new AutoCompleteTest.TopLevel())
Expand Down
7 changes: 7 additions & 0 deletions src/test/resources/basic.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

# root completion
set -l root
complete -c basicExample -n "not __fish_seen_subcommand_from $root" -l timeUnit -f -a 'NANOSECONDS MICROSECONDS MILLISECONDS SECONDS MINUTES HOURS DAYS' -d ''
complete -c basicExample -n "not __fish_seen_subcommand_from $root" -s u -d ''
complete -c basicExample -n "not __fish_seen_subcommand_from $root" -l timeout -d ''
complete -c basicExample -n "not __fish_seen_subcommand_from $root" -s t -d ''
68 changes: 68 additions & 0 deletions src/test/resources/picocompletion-demo_completion.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@

# root completion
set -l root
complete -c picocompletion-demo -n "not __fish_seen_subcommand_from $root" -l version -d ''
complete -c picocompletion-demo -n "not __fish_seen_subcommand_from $root" -s V -d ''
complete -c picocompletion-demo -n "not __fish_seen_subcommand_from $root" -l help -d ''
complete -c picocompletion-demo -n "not __fish_seen_subcommand_from $root" -s h -d ''

# _picocli_picocompletion_demo completion
set -l _picocli_picocompletion_demo sub1 sub1-alias sub2 sub2-alias
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo" -a sub1 -d 'First level subcommand 1'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub1" -l num -d 'a number'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub1" -l str -d 'a String'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub1" -l candidates -a 'aaa bbb ccc' -d 'with candidates'
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo" -a sub1-alias -d 'First level subcommand 1'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub1-alias" -l num -d 'a number'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub1-alias" -l str -d 'a String'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub1-alias" -l candidates -a 'aaa bbb ccc' -d 'with candidates'
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo" -a sub2 -d 'First level subcommand 2'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2" -l num2 -d 'another number'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2" -l directory -d 'a directory'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2" -s d -d 'a directory'
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo" -a sub2-alias -d 'First level subcommand 2'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2-alias" -l num2 -d 'another number'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2-alias" -l directory -d 'a directory'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2-alias" -s d -d 'a directory'

# _picocli_picocompletion_demo_sub2 completion
set -l _picocli_picocompletion_demo_sub2 subsub1 sub2child1-alias subsub2 sub2child2-alias subsub3 sub2child3-alias
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo_sub2" -n '__fish_seen_subcommand_from sub2' -a subsub1 -d 'Second level sub-subcommand 1'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from subsub1" -n '__fish_seen_subcommand_from sub2' -l host -d 'a host'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from subsub1" -n '__fish_seen_subcommand_from sub2' -s h -d 'a host'
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo_sub2" -n '__fish_seen_subcommand_from sub2' -a sub2child1-alias -d 'Second level sub-subcommand 1'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2child1-alias" -n '__fish_seen_subcommand_from sub2' -l host -d 'a host'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2child1-alias" -n '__fish_seen_subcommand_from sub2' -s h -d 'a host'
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo_sub2" -n '__fish_seen_subcommand_from sub2' -a subsub2 -d 'Second level sub-subcommand 2'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from subsub2" -n '__fish_seen_subcommand_from sub2' -l timeUnit -a 'NANOSECONDS MICROSECONDS MILLISECONDS SECONDS MINUTES HOURS DAYS' -d ''
complete -c picocompletion-demo -n "__fish_seen_subcommand_from subsub2" -n '__fish_seen_subcommand_from sub2' -s u -d ''
complete -c picocompletion-demo -n "__fish_seen_subcommand_from subsub2" -n '__fish_seen_subcommand_from sub2' -l timeout -d ''
complete -c picocompletion-demo -n "__fish_seen_subcommand_from subsub2" -n '__fish_seen_subcommand_from sub2' -s t -d ''
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo_sub2" -n '__fish_seen_subcommand_from sub2' -a sub2child2-alias -d 'Second level sub-subcommand 2'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2child2-alias" -n '__fish_seen_subcommand_from sub2' -l timeUnit -a 'NANOSECONDS MICROSECONDS MILLISECONDS SECONDS MINUTES HOURS DAYS' -d ''
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2child2-alias" -n '__fish_seen_subcommand_from sub2' -s u -d ''
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2child2-alias" -n '__fish_seen_subcommand_from sub2' -l timeout -d ''
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2child2-alias" -n '__fish_seen_subcommand_from sub2' -s t -d ''
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo_sub2" -n '__fish_seen_subcommand_from sub2' -a subsub3 -d 'Second level sub-subcommand 3'
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo_sub2" -n '__fish_seen_subcommand_from sub2' -a sub2child3-alias -d 'Second level sub-subcommand 3'

# _picocli_picocompletion_demo_sub2alias completion
set -l _picocli_picocompletion_demo_sub2alias subsub1 sub2child1-alias subsub2 sub2child2-alias subsub3 sub2child3-alias
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo_sub2alias" -n '__fish_seen_subcommand_from sub2-alias' -a subsub1 -d 'Second level sub-subcommand 1'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from subsub1" -n '__fish_seen_subcommand_from sub2-alias' -l host -d 'a host'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from subsub1" -n '__fish_seen_subcommand_from sub2-alias' -s h -d 'a host'
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo_sub2alias" -n '__fish_seen_subcommand_from sub2-alias' -a sub2child1-alias -d 'Second level sub-subcommand 1'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2child1-alias" -n '__fish_seen_subcommand_from sub2-alias' -l host -d 'a host'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2child1-alias" -n '__fish_seen_subcommand_from sub2-alias' -s h -d 'a host'
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo_sub2alias" -n '__fish_seen_subcommand_from sub2-alias' -a subsub2 -d 'Second level sub-subcommand 2'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from subsub2" -n '__fish_seen_subcommand_from sub2-alias' -l timeUnit -a 'NANOSECONDS MICROSECONDS MILLISECONDS SECONDS MINUTES HOURS DAYS' -d ''
complete -c picocompletion-demo -n "__fish_seen_subcommand_from subsub2" -n '__fish_seen_subcommand_from sub2-alias' -s u -d ''
complete -c picocompletion-demo -n "__fish_seen_subcommand_from subsub2" -n '__fish_seen_subcommand_from sub2-alias' -l timeout -d ''
complete -c picocompletion-demo -n "__fish_seen_subcommand_from subsub2" -n '__fish_seen_subcommand_from sub2-alias' -s t -d ''
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo_sub2alias" -n '__fish_seen_subcommand_from sub2-alias' -a sub2child2-alias -d 'Second level sub-subcommand 2'
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2child2-alias" -n '__fish_seen_subcommand_from sub2-alias' -l timeUnit -a 'NANOSECONDS MICROSECONDS MILLISECONDS SECONDS MINUTES HOURS DAYS' -d ''
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2child2-alias" -n '__fish_seen_subcommand_from sub2-alias' -s u -d ''
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2child2-alias" -n '__fish_seen_subcommand_from sub2-alias' -l timeout -d ''
complete -c picocompletion-demo -n "__fish_seen_subcommand_from sub2child2-alias" -n '__fish_seen_subcommand_from sub2-alias' -s t -d ''
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo_sub2alias" -n '__fish_seen_subcommand_from sub2-alias' -a subsub3 -d 'Second level sub-subcommand 3'
complete -c picocompletion-demo -f -n "not __fish_seen_subcommand_from $_picocli_picocompletion_demo_sub2alias" -n '__fish_seen_subcommand_from sub2-alias' -a sub2child3-alias -d 'Second level sub-subcommand 3'

0 comments on commit a144222

Please sign in to comment.