diff --git a/spring-shell-jline/src/main/java/org/springframework/shell/jline/CommandCompleter.java b/spring-shell-jline/src/main/java/org/springframework/shell/jline/CommandCompleter.java index e2858f531..705e26813 100644 --- a/spring-shell-jline/src/main/java/org/springframework/shell/jline/CommandCompleter.java +++ b/spring-shell-jline/src/main/java/org/springframework/shell/jline/CommandCompleter.java @@ -12,12 +12,15 @@ import org.springframework.shell.core.command.completion.CompletionProposal; import org.springframework.shell.core.command.completion.CompletionProvider; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** * A JLine {@link Completer} that completes command names from a {@link CommandRegistry}. * * @author Mahmoud Ben Hassine + * @author David Pilar * @since 4.0.0 */ public class CommandCompleter implements Completer { @@ -36,20 +39,24 @@ public CommandCompleter(CommandRegistry commandRegistry) { public void complete(LineReader reader, ParsedLine line, List candidates) { Command commandByName = findCommandByWords(line.words()); if (commandByName != null) { - // add option completions for the command List options = commandByName.getOptions(); - for (CommandOption option : options) { - if (option.longName() != null && !line.line().contains("--" + option.longName())) { - candidates.add(new Candidate("--" + option.longName())); - } - if (option.shortName() != ' ' && !line.line().contains("-" + option.shortName())) { - candidates.add(new Candidate("-" + option.shortName())); + CommandOption commandOption = findOptionByWords(line.words(), options); + if (commandOption == null) { + // add option completions for the command + for (CommandOption option : options) { + boolean present = isOptionPresent(line, option); + if (option.longName() != null && !present) { + candidates.add(new Candidate("--" + option.longName())); + } + if (option.shortName() != ' ' && !present) { + candidates.add(new Candidate("-" + option.shortName())); + } } } // add custom completions from the command's completion provider CompletionProvider completionProvider = commandByName.getCompletionProvider(); - CompletionContext context = new CompletionContext(line.words(), line.wordIndex(), line.wordCursor(), null, - null); + CompletionContext context = new CompletionContext(line.words(), line.wordIndex(), line.wordCursor(), + commandByName, commandOption); List proposals = completionProvider.apply(context); for (CompletionProposal proposal : proposals) { candidates.add(new Candidate(proposal.value())); @@ -63,6 +70,11 @@ public void complete(LineReader reader, ParsedLine line, List candida } } + private boolean isOptionPresent(ParsedLine line, CommandOption option) { + return option.longName() != null && line.line().contains(" --" + option.longName() + " ") + || option.shortName() != ' ' && line.line().contains(" -" + option.shortName() + " "); + } + @Nullable private Command findCommandByWords(List words) { StringBuilder commandName = new StringBuilder(); for (String word : words) { @@ -74,4 +86,18 @@ public void complete(LineReader reader, ParsedLine line, List candida return this.commandRegistry.getCommandByName(commandName.toString().trim()); } + @Nullable private CommandOption findOptionByWords(List words, List options) { + List reversed = new ArrayList<>(words); + Collections.reverse(reversed); + String optionName = reversed.stream().filter(word -> !word.trim().isEmpty()).findFirst().orElse(""); + + for (CommandOption option : options) { + if (option.longName() != null && optionName.equals("--" + option.longName()) + || option.shortName() != ' ' && optionName.equals("-" + option.shortName())) { + return option; + } + } + return null; + } + }