@@ -422,6 +422,41 @@ private static class CommandDescriptor {
422422 " done\n " +
423423 " echo \" $result\" \n " +
424424 "}\n " +
425+ "\n " +
426+ "# compReplyArray generates a list of completion suggestions based on an array, ensuring all values are properly escaped.\n " +
427+ "#\n " +
428+ "# compReplyArray takes a single parameter: the array of options to be displayed\n " +
429+ "#\n " +
430+ "# The output is echoed to std_out, one option per line.\n "
431+ + "#\n "
432+ + "# Example usage:\n "
433+ + "# local options=(\" foo\" , \" bar\" , \" baz\" )\n "
434+ + "# local IFS=$'\\ n'\n "
435+ + "# COMPREPLY=$(compReplyArray \" ${options[@]}\" )\n " +
436+ "function compReplyArray() {\n " +
437+ " declare -a options\n " +
438+ " options=(\" $@\" )\n " +
439+ " local curr_word=${COMP_WORDS[COMP_CWORD]}\n " +
440+ " local i\n " +
441+ " local quoted\n " +
442+ " local optionList=()\n " +
443+ "\n " +
444+ " for (( i=0; i<${#options[@]}; i++ )); do\n " +
445+ " # Double escape, since we want escaped values, but compgen -W expands the argument\n " +
446+ " printf -v quoted %%q \" ${options[i]}\" \n " +
447+ " quoted=\\ '${quoted//\\ '/\\ '\\ \\ \\ '\\ '}\\ '\n " +
448+ "\n " +
449+ " optionList[i]=$quoted\n " +
450+ " done\n " +
451+ "\n " +
452+ " # We also have to add another round of escaping to $curr_word.\n " +
453+ " curr_word=${curr_word//\\ \\ /\\ \\ \\ \\ }\n " +
454+ " curr_word=${curr_word//\\ '/\\ \\ \\ '}\n " +
455+ "\n " +
456+ " # Actually generate completions.\n " +
457+ " local IFS=$'\\ n'\n " +
458+ " echo -e \" $(compgen -W \" ${optionList[*]}\" -- \" $curr_word\" )\" \n " +
459+ "}\n " +
425460 "\n " ;
426461
427462 private static final String SCRIPT_FOOTER = "" +
@@ -750,9 +785,7 @@ private static String generatePositionalParamsCases(List<PositionalParamSpec> po
750785 int max = param .index ().max ();
751786 if (param .completionCandidates () != null ) {
752787 buff .append (format ("%s %s (( currIndex >= %d && currIndex <= %d )); then\n " , indent , ifOrElif , min , max ));
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 ));
788+ buff .append (format ("%s positionals=$( compReplyArray \" ${%s_pos_param_args[@]}\" )\n " , indent , paramName , currWord ));
756789 } else if (type .equals (File .class ) || "java.nio.file.Path" .equals (type .getName ())) {
757790 buff .append (format ("%s %s (( currIndex >= %d && currIndex <= %d )); then\n " , indent , ifOrElif , min , max ));
758791 buff .append (format ("%s local IFS=$'\\ n'\n " , indent ));
@@ -796,8 +829,7 @@ private static String generateOptionsCases(List<OptionSpec> argOptionFields, Str
796829 if (option .completionCandidates () != null ) {
797830 buff .append (format ("%s %s)\n " , indent , concat ("|" , option .names ()))); // " -u|--timeUnit)\n"
798831 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 ));
832+ buff .append (format ("%s COMPREPLY=( $( compReplyArray \" ${%s_option_args[@]}\" ) )\n " , indent , bashify (option .paramLabel ()), currWord ));
801833 buff .append (format ("%s return $?\n " , indent ));
802834 buff .append (format ("%s ;;\n " , indent ));
803835 } else if (type .equals (File .class ) || "java.nio.file.Path" .equals (type .getName ())) {
0 commit comments