String autoCompleteScript = bash(scriptName, commandLine); Writer completionWriter = null; Writer scriptWriter = null;
@Test(expected = NullPointerException.class) public void testCompleteDisallowsNullSpec() { AutoComplete.complete(null, new String[] {"-x"}, 0, 0, 0, new ArrayList<CharSequence>()); }
ParseResult parseResult = parser.parseArgs(args); if (argIndex >= parseResult.tentativeMatch.size()) { Object startPoint = findCompletionStartPoint(parseResult); addCandidatesForArgsFollowing(startPoint, candidates); } else { Object obj = parseResult.tentativeMatch.get(argIndex); if (obj instanceof CommandSpec) { // subcommand addCandidatesForArgsFollowing(((CommandSpec) obj).parent(), candidates); addCandidatesForArgsFollowing(findCommandFor((OptionSpec) obj, spec), candidates); } else { addCandidatesForArgsFollowing((OptionSpec) obj, candidates); addCandidatesForArgsFollowing(findCommandFor((PositionalParamSpec) obj, spec), candidates); while (i > 0 && !isPicocliModelObject(parseResult.tentativeMatch.get(i))) {i--;} if (i < 0) { return -1; } addCandidatesForArgsFollowing(parseResult.tentativeMatch.get(i), candidates); filterAndTrimMatchingPrefix(committedPrefix, candidates); return candidates.isEmpty() ? -1 : cursor; } finally {
private static void generateCompletionCandidates(StringBuilder buff, OptionSpec f) { buff.append(format(" %s_OPTION_ARGS=\"%s\" # %s values\n", bashify(f.paramLabel()), concat(" ", extract(f.completionCandidates())).trim(), f.longestName())); } private static List<String> extract(Iterable<String> generator) {
private static void generateFunctionCallsToArrContains(String scriptName, List<String> predecessors, CommandLine commandLine, StringBuilder buff, List<String> functionCalls, Map<CommandDescriptor, CommandLine> function2command) { // breadth-first: generate command lists and function calls for predecessors + each subcommand for (Map.Entry<String, CommandLine> entry : commandLine.getSubcommands().entrySet()) { int count = functionCalls.size(); String functionName = "_picocli_" + scriptName + "_" + concat("_", predecessors, entry.getKey(), new Bashify()); functionCalls.add(format(" ArrContains COMP_WORDS CMDS%2$d && { %1$s; return $?; }\n", functionName, count)); buff.append( format(" CMDS%2$d=(%1$s)\n", concat(" ", predecessors, entry.getKey(), new NullFunction()), count)); // remember the function name and associated subcommand so we can easily generate a function later function2command.put(new CommandDescriptor(functionName, entry.getKey()), entry.getValue()); } // then recursively do the same for all nested subcommands for (Map.Entry<String, CommandLine> entry : commandLine.getSubcommands().entrySet()) { predecessors.add(entry.getKey()); generateFunctionCallsToArrContains(scriptName, predecessors, entry.getValue(), buff, functionCalls, function2command); predecessors.remove(predecessors.size() - 1); } } private static String concat(String infix, String... values) {
private static String generateOptionsCases(List<OptionSpec> argOptionFields, String indent, String currWord) { StringBuilder buff = new StringBuilder(1024); for (OptionSpec option : argOptionFields) { if (option.completionCandidates() != null) { buff.append(format("%s %s)\n", indent, concat("|", option.names()))); // " -u|--timeUnit)\n" buff.append(format("%s COMPREPLY=( $( compgen -W \"${%s_OPTION_ARGS}\" -- %s ) )\n", indent, bashify(option.paramLabel()), currWord)); buff.append(format("%s return $?\n", indent)); buff.append(format("%s ;;\n", indent)); } else if (option.type().equals(File.class) || "java.nio.file.Path".equals(option.type().getName())) { buff.append(format("%s %s)\n", indent, concat("|", option.names()))); // " -f|--file)\n" buff.append(format("%s compopt -o filenames\n", indent)); buff.append(format("%s COMPREPLY=( $( compgen -f -- %s ) ) # files\n", indent, currWord)); buff.append(format("%s return $?\n", indent)); buff.append(format("%s ;;\n", indent)); } else if (option.type().equals(InetAddress.class)) { buff.append(format("%s %s)\n", indent, concat("|", option.names()))); // " -h|--host)\n" buff.append(format("%s compopt -o filenames\n", indent)); buff.append(format("%s COMPREPLY=( $( compgen -A hostname -- %s ) )\n", indent, currWord)); buff.append(format("%s return $?\n", indent)); buff.append(format("%s ;;\n", indent)); } else { buff.append(format("%s %s)\n", indent, concat("|", option.names()))); // no completions available buff.append(format("%s return\n", indent)); buff.append(format("%s ;;\n", indent)); } } return buff.toString(); }
String flagOptionNames = optionNames(filter(commandSpec.options(), new BooleanArgFilter())); List<OptionSpec> argOptionFields = filter(commandSpec.options(), negate(new BooleanArgFilter())); String argOptionNames = optionNames(argOptionFields); String commands = concat(" ", new ArrayList<String>(commandLine.getSubcommands().keySet())).trim(); generateCompletionCandidates(buff, f); buff.append(generateOptionsSwitch(argOptionFields));
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) {
private static String concat(String infix, String... values) { return concat(infix, Arrays.asList(values)); } private static String concat(String infix, List<String> values) {
private static void addCandidatesForArgsFollowing(PositionalParamSpec positionalSpec, List<CharSequence> candidates) { if (positionalSpec != null) { addCompletionCandidates(positionalSpec.completionCandidates(), candidates); } } private static void addCompletionCandidates(Iterable<String> completionCandidates, List<CharSequence> candidates) {
private static void addCandidatesForArgsFollowing(Object obj, List<CharSequence> candidates) { if (obj == null) { return; } if (obj instanceof CommandSpec) { addCandidatesForArgsFollowing((CommandSpec) obj, candidates); } else if (obj instanceof OptionSpec) { addCandidatesForArgsFollowing((OptionSpec) obj, candidates); } else if (obj instanceof PositionalParamSpec) { addCandidatesForArgsFollowing((PositionalParamSpec) obj, candidates); } } private static void addCandidatesForArgsFollowing(CommandSpec commandSpec, List<CharSequence> candidates) {
String flagOptionNames = optionNames(filter(commandSpec.options(), new BooleanArgFilter())); List<OptionSpec> argOptionFields = filter(commandSpec.options(), negate(new BooleanArgFilter())); String argOptionNames = optionNames(argOptionFields); String commands = concat(" ", new ArrayList<String>(commandLine.getSubcommands().keySet())).trim(); generateCompletionCandidates(buff, f); buff.append(generateOptionsSwitch(argOptionFields));
private static void generateCompletionCandidates(StringBuilder buff, OptionSpec f) { buff.append(format(" %s_OPTION_ARGS=\"%s\" # %s values\n", bashify(f.paramLabel()), concat(" ", extract(f.completionCandidates())).trim(), f.longestName())); } private static List<String> extract(Iterable<String> generator) {
private static CommandSpec findCommandFor(OptionSpec option, CommandSpec commandSpec) { for (OptionSpec defined : commandSpec.options()) { if (defined == option) { return commandSpec; } } for (CommandLine sub : commandSpec.subcommands().values()) { CommandSpec result = findCommandFor(option, sub.getCommandSpec()); if (result != null) { return result; } } return null; } private static CommandSpec findCommandFor(PositionalParamSpec positional, CommandSpec commandSpec) {
private static void generateFunctionCallsToArrContains(String scriptName, List<String> predecessors, CommandLine commandLine, StringBuilder buff, List<String> functionCalls, Map<CommandDescriptor, CommandLine> function2command) { // breadth-first: generate command lists and function calls for predecessors + each subcommand for (Map.Entry<String, CommandLine> entry : commandLine.getSubcommands().entrySet()) { int count = functionCalls.size(); String functionName = "_picocli_" + scriptName + "_" + concat("_", predecessors, entry.getKey(), new Bashify()); functionCalls.add(format(" ArrContains COMP_WORDS CMDS%2$d && { %1$s; return $?; }\n", functionName, count)); buff.append( format(" CMDS%2$d=(%1$s)\n", concat(" ", predecessors, entry.getKey(), new NullFunction()), count)); // remember the function name and associated subcommand so we can easily generate a function later function2command.put(new CommandDescriptor(functionName, entry.getKey()), entry.getValue()); } // then recursively do the same for all nested subcommands for (Map.Entry<String, CommandLine> entry : commandLine.getSubcommands().entrySet()) { predecessors.add(entry.getKey()); generateFunctionCallsToArrContains(scriptName, predecessors, entry.getValue(), buff, functionCalls, function2command); predecessors.remove(predecessors.size() - 1); } } private static String concat(String infix, String... values) {
private static String generateOptionsCases(List<OptionSpec> argOptionFields, String indent, String currWord) { StringBuilder buff = new StringBuilder(1024); for (OptionSpec option : argOptionFields) { if (option.completionCandidates() != null) { buff.append(format("%s %s)\n", indent, concat("|", option.names()))); // " -u|--timeUnit)\n" buff.append(format("%s COMPREPLY=( $( compgen -W \"${%s_OPTION_ARGS}\" -- %s ) )\n", indent, bashify(option.paramLabel()), currWord)); buff.append(format("%s return $?\n", indent)); buff.append(format("%s ;;\n", indent)); } else if (option.type().equals(File.class) || "java.nio.file.Path".equals(option.type().getName())) { buff.append(format("%s %s)\n", indent, concat("|", option.names()))); // " -f|--file)\n" buff.append(format("%s compopt -o filenames\n", indent)); buff.append(format("%s COMPREPLY=( $( compgen -f -- %s ) ) # files\n", indent, currWord)); buff.append(format("%s return $?\n", indent)); buff.append(format("%s ;;\n", indent)); } else if (option.type().equals(InetAddress.class)) { buff.append(format("%s %s)\n", indent, concat("|", option.names()))); // " -h|--host)\n" buff.append(format("%s compopt -o filenames\n", indent)); buff.append(format("%s COMPREPLY=( $( compgen -A hostname -- %s ) )\n", indent, currWord)); buff.append(format("%s return $?\n", indent)); buff.append(format("%s ;;\n", indent)); } else { buff.append(format("%s %s)\n", indent, concat("|", option.names()))); // no completions available buff.append(format("%s return\n", indent)); buff.append(format("%s ;;\n", indent)); } } return buff.toString(); }
private static String concat(String infix, List<String> values) { return concat(infix, values, null, new NullFunction()); } private static <V, T extends V> String concat(String infix, List<T> values, T lastValue, Function<V, String> normalize) {
private static void addCandidatesForArgsFollowing(OptionSpec optionSpec, List<CharSequence> candidates) { if (optionSpec != null) { addCompletionCandidates(optionSpec.completionCandidates(), candidates); } } private static void addCandidatesForArgsFollowing(PositionalParamSpec positionalSpec, List<CharSequence> candidates) {
private static void addCandidatesForArgsFollowing(CommandSpec commandSpec, List<CharSequence> candidates) { if (commandSpec == null) { return; } for (Map.Entry<String, CommandLine> entry : commandSpec.subcommands().entrySet()) { candidates.add(entry.getKey()); candidates.addAll(Arrays.asList(entry.getValue().getCommandSpec().aliases())); } candidates.addAll(commandSpec.optionsMap().keySet()); for (PositionalParamSpec positional : commandSpec.positionalParameters()) { addCandidatesForArgsFollowing(positional, candidates); } } private static void addCandidatesForArgsFollowing(OptionSpec optionSpec, List<CharSequence> candidates) {
private void generateAutoCompleteScript() { System.out.println(AutoComplete.bash("git", mainCommand())); }