Skip to content

Commit 8c471fe

Browse files
jsotuyodremkop
authored andcommitted
Fixes #1754
- Both positional params and option candidates are now stored in the autocomplete script as an array. - By setting `IFS=$'\n'` for those scenarios, each array entry is treated as a single candidate, allowing to honor any spaces in the original suggestions coded by the user.
1 parent 0207de4 commit 8c471fe

File tree

6 files changed

+144
-101
lines changed

6 files changed

+144
-101
lines changed

src/main/java/picocli/AutoComplete.java

+10-6
Original file line numberDiff line numberDiff line change
@@ -716,16 +716,16 @@ private static String generateFunctionForCommand(String functionName, String com
716716

717717
private static void generatePositionParamCompletionCandidates(StringBuilder buff, PositionalParamSpec f) {
718718
String paramName = bashify(f.paramLabel());
719-
buff.append(format(" local %s_pos_param_args=\"%s\" # %d-%d values\n",
719+
buff.append(format(" local %s_pos_param_args=(\"%s\") # %d-%d values\n",
720720
paramName,
721-
concat(" ", extract(f.completionCandidates())).trim(),
721+
concat("\" \"", extract(f.completionCandidates())).trim(),
722722
f.index().min(), f.index().max()));
723723
}
724724

725725
private static void generateCompletionCandidates(StringBuilder buff, OptionSpec f) {
726-
buff.append(format(" local %s_option_args=\"%s\" # %s values\n",
726+
buff.append(format(" local %s_option_args=(\"%s\") # %s values\n",
727727
bashify(f.paramLabel()),
728-
concat(" ", extract(f.completionCandidates())).trim(),
728+
concat("\" \"", extract(f.completionCandidates())).trim(),
729729
f.longestName()));
730730
}
731731
private static List<String> extract(Iterable<String> generator) {
@@ -750,7 +750,9 @@ private static String generatePositionalParamsCases(List<PositionalParamSpec> po
750750
int max = param.index().max();
751751
if (param.completionCandidates() != null) {
752752
buff.append(format("%s %s (( currIndex >= %d && currIndex <= %d )); then\n", indent, ifOrElif, min, max));
753-
buff.append(format("%s positionals=$( compgen -W \"$%s_pos_param_args\" -- \"%s\" )\n", indent, paramName, currWord));
753+
buff.append(format("%s local IFS=$'\\n'\n", indent));
754+
buff.append(format("%s positionals=$( compgen -W \"${%s_pos_param_args[*]}\" -- \"%s\" )\n",
755+
indent, paramName, currWord));
754756
} else if (type.equals(File.class) || "java.nio.file.Path".equals(type.getName())) {
755757
buff.append(format("%s %s (( currIndex >= %d && currIndex <= %d )); then\n", indent, ifOrElif, min, max));
756758
buff.append(format("%s local IFS=$'\\n'\n", indent));
@@ -793,7 +795,9 @@ private static String generateOptionsCases(List<OptionSpec> argOptionFields, Str
793795
}
794796
if (option.completionCandidates() != null) {
795797
buff.append(format("%s %s)\n", indent, concat("|", option.names()))); // " -u|--timeUnit)\n"
796-
buff.append(format("%s COMPREPLY=( $( compgen -W \"${%s_option_args}\" -- \"%s\" ) )\n", indent, bashify(option.paramLabel()), currWord));
798+
buff.append(format("%s local IFS=$'\\n'\n", indent));
799+
buff.append(format("%s COMPREPLY=( $( compgen -W \"${%s_option_args[*]}\" -- \"%s\" ) )\n", indent,
800+
bashify(option.paramLabel()), currWord));
797801
buff.append(format("%s return $?\n", indent));
798802
buff.append(format("%s ;;\n", indent));
799803
} else if (type.equals(File.class) || "java.nio.file.Path".equals(type.getName())) {

src/test/java/picocli/AutoCompleteTest.java

+32-27
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,15 @@
1515
*/
1616
package picocli;
1717

18-
import org.hamcrest.MatcherAssert;
19-
import org.junit.Rule;
20-
import org.junit.Test;
21-
import org.junit.contrib.java.lang.system.Assertion;
22-
import org.junit.contrib.java.lang.system.ExpectedSystemExit;
23-
import org.junit.contrib.java.lang.system.ProvideSystemProperty;
24-
import org.junit.contrib.java.lang.system.RestoreSystemProperties;
25-
import org.junit.contrib.java.lang.system.SystemErrRule;
26-
import org.junit.contrib.java.lang.system.SystemOutRule;
27-
import org.junit.rules.TestRule;
28-
import picocli.CommandLine.Command;
29-
import picocli.CommandLine.Model.CommandSpec;
30-
import picocli.CommandLine.Model.OptionSpec;
31-
import picocli.CommandLine.Model.PositionalParamSpec;
32-
import picocli.CommandLine.Option;
33-
import picocli.CommandLine.Parameters;
18+
import static java.lang.String.format;
19+
import static org.hamcrest.CoreMatchers.containsString;
20+
import static org.hamcrest.CoreMatchers.not;
21+
import static org.hamcrest.MatcherAssert.assertThat;
22+
import static org.junit.Assert.assertEquals;
23+
import static org.junit.Assert.assertFalse;
24+
import static org.junit.Assert.assertNotEquals;
25+
import static org.junit.Assert.assertTrue;
26+
import static org.junit.Assert.fail;
3427

3528
import java.io.BufferedReader;
3629
import java.io.File;
@@ -53,11 +46,23 @@
5346
import java.util.Scanner;
5447
import java.util.concurrent.TimeUnit;
5548

56-
import static java.lang.String.format;
57-
import static org.hamcrest.CoreMatchers.containsString;
58-
import static org.hamcrest.CoreMatchers.not;
59-
import static org.hamcrest.MatcherAssert.assertThat;
60-
import static org.junit.Assert.*;
49+
import org.hamcrest.MatcherAssert;
50+
import org.junit.Rule;
51+
import org.junit.Test;
52+
import org.junit.contrib.java.lang.system.Assertion;
53+
import org.junit.contrib.java.lang.system.ExpectedSystemExit;
54+
import org.junit.contrib.java.lang.system.ProvideSystemProperty;
55+
import org.junit.contrib.java.lang.system.RestoreSystemProperties;
56+
import org.junit.contrib.java.lang.system.SystemErrRule;
57+
import org.junit.contrib.java.lang.system.SystemOutRule;
58+
import org.junit.rules.TestRule;
59+
60+
import picocli.CommandLine.Command;
61+
import picocli.CommandLine.Model.CommandSpec;
62+
import picocli.CommandLine.Model.OptionSpec;
63+
import picocli.CommandLine.Model.PositionalParamSpec;
64+
import picocli.CommandLine.Option;
65+
import picocli.CommandLine.Parameters;
6166

6267
/**
6368
* Tests the scripts generated by AutoComplete.
@@ -93,7 +98,7 @@ public void run() {
9398
public void basic() throws Exception {
9499
String script = AutoComplete.bash("basicExample", new CommandLine(new BasicExample()));
95100
String expected = format(loadTextFromClasspath("/basic.bash"),
96-
CommandLine.VERSION, spaced(TimeUnit.values()));
101+
CommandLine.VERSION, concat("\" \"", TimeUnit.values()));
97102
assertEquals(expected, script);
98103
}
99104

@@ -188,7 +193,7 @@ public void nestedSubcommands() throws Exception {
188193
);
189194
String script = AutoComplete.bash("picocompletion-demo", hierarchy);
190195
String expected = format(loadTextFromClasspath("/picocompletion-demo_completion.bash"),
191-
CommandLine.VERSION, spaced(TimeUnit.values()));
196+
CommandLine.VERSION, concat("\" \"", TimeUnit.values()));
192197
assertEquals(expected, script);
193198
}
194199

@@ -204,16 +209,16 @@ public void helpCommand() {
204209
.addSubcommand(new CommandLine.HelpCommand());
205210
String script = AutoComplete.bash("picocompletion-demo-help", hierarchy);
206211
String expected = format(loadTextFromClasspath("/picocompletion-demo-help_completion.bash"),
207-
CommandLine.VERSION, spaced(TimeUnit.values()));
212+
CommandLine.VERSION, concat("\" \"", TimeUnit.values()));
208213
assertEquals(expected, script);
209214
}
210215

211-
private static String spaced(Object[] values) {
216+
private static String concat(String infix, Object[] values) {
212217
StringBuilder result = new StringBuilder();
213218
for (Object value : values) {
214-
result.append(value).append(' ');
219+
result.append(value).append(infix);
215220
}
216-
return result.toString().substring(0, result.length() - 1);
221+
return result.toString().substring(0, result.length() - infix.length());
217222
}
218223

219224
static String loadTextFromClasspath(String path) {

src/test/resources/bashify_completion.bash

+3-2
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,14 @@ function _picocli_bashify() {
136136
local commands=""
137137
local flag_opts=""
138138
local arg_opts="-x"
139-
local _AB_C_option_args="1" # -x values
139+
local _AB_C_option_args=("1") # -x values
140140

141141
type compopt &>/dev/null && compopt +o default
142142

143143
case ${prev_word} in
144144
-x)
145-
COMPREPLY=( $( compgen -W "${_AB_C_option_args}" -- "${curr_word}" ) )
145+
local IFS=$'\n'
146+
COMPREPLY=( $( compgen -W "${_AB_C_option_args[*]}" -- "${curr_word}" ) )
146147
return $?
147148
;;
148149
esac

src/test/resources/basic.bash

+3-2
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,14 @@ function _picocli_basicExample() {
136136
local commands=""
137137
local flag_opts=""
138138
local arg_opts="-u --timeUnit -t --timeout"
139-
local timeUnit_option_args="%2$s" # --timeUnit values
139+
local timeUnit_option_args=("%2$s") # --timeUnit values
140140

141141
type compopt &>/dev/null && compopt +o default
142142

143143
case ${prev_word} in
144144
-u|--timeUnit)
145-
COMPREPLY=( $( compgen -W "${timeUnit_option_args}" -- "${curr_word}" ) )
145+
local IFS=$'\n'
146+
COMPREPLY=( $( compgen -W "${timeUnit_option_args[*]}" -- "${curr_word}" ) )
146147
return $?
147148
;;
148149
-t|--timeout)

0 commit comments

Comments
 (0)