diff --git a/cli/src/main/java/com/devonfw/tools/ide/cli/CliArgument.java b/cli/src/main/java/com/devonfw/tools/ide/cli/CliArgument.java index d91efa30cb..58bce2a9f6 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/cli/CliArgument.java +++ b/cli/src/main/java/com/devonfw/tools/ide/cli/CliArgument.java @@ -273,6 +273,19 @@ public String toString() { return this.arg; } + public static CliArgument of(int completionIdx, String... args) { + CliArgument current = END; + int last = args.length - 1; + + for (int argsIdx = last; argsIdx >= 0; argsIdx--) { + String arg = args[argsIdx]; + boolean completionArg = argsIdx == completionIdx; + + current = new CliArgument(arg, current, completionArg); + } + + return current.createStart(); + } /** * @param args the command-line arguments (e.g. from {@code main} method). * @return the first {@link CliArgument} of the parsed arguments or {@code null} if for empty arguments. diff --git a/cli/src/main/java/com/devonfw/tools/ide/cli/CliArguments.java b/cli/src/main/java/com/devonfw/tools/ide/cli/CliArguments.java index 248a6c495c..a272644072 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/cli/CliArguments.java +++ b/cli/src/main/java/com/devonfw/tools/ide/cli/CliArguments.java @@ -15,6 +15,10 @@ public class CliArguments implements Iterator { private boolean splitShortOpts; + public CliArguments(int completionIdx, String... args) { + this(CliArgument.of(completionIdx, args)); + } + /** * The constructor. * @@ -165,6 +169,10 @@ public String toString() { return this.currentArg.getArgs(); } + public static CliArguments of(int completionIdx, String... args) { + return new CliArguments(completionIdx, args); + } + /** * @param args the {@link CliArgument#of(String...) command line arguments}. * @return the {@link CliArguments}. diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManager.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManager.java index 92105a33ea..813efdd59f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManager.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManager.java @@ -95,6 +95,9 @@ default LocalToolCommandlet getRequiredLocalToolCommandlet(String name) { throw new IllegalArgumentException("The commandlet " + name + " is not a LocalToolCommandlet!"); } + public abstract void collectCompletionCandidates(CliArguments arguments, + CompletionCandidateCollector collector); + /** * @param arguments the {@link CliArguments}. * @param collector the optional {@link CompletionCandidateCollector}. Will be {@code null} if no argument {@link CliArguments#isCompletion() completion} diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java index 9d4217fb8b..e4dfc63acb 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java @@ -226,6 +226,28 @@ public Commandlet getCommandletByFirstKeyword(String keyword) { return this.firstKeywordMap.get(keyword); } + @Override + public void collectCompletionCandidates(CliArguments arguments, + CompletionCandidateCollector collector) { + CliArgument current = arguments.current(); + if (current.isStart()) { + current = current.getNext(); + } + if (current.isEnd()) { + return; + } + + for (Commandlet cmd : this.getCommandlets()) { + if (!cmd.isIdeHomeRequired() || this.context.getIdeHome() != null) { + for (Property property : cmd.getProperties()) { + if (property instanceof KeywordProperty keyword) { + keyword.apply(arguments, this.context, cmd, collector); + } + } + } + } + } + @Override public Iterator findCommandlet(CliArguments arguments, CompletionCandidateCollector collector) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidate.java b/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidate.java index c62bd07d84..c58fe20908 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidate.java +++ b/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidate.java @@ -1,16 +1,24 @@ package com.devonfw.tools.ide.completion; +import java.util.List; + /** * Candidate for auto-completion. * * @param text the text to suggest (CLI argument value). * @param description the description of the candidate. */ -public record CompletionCandidate(String text, String description) implements Comparable { +public record CompletionCandidate(List entries, + String description, + boolean complete) implements Comparable { @Override public int compareTo(CompletionCandidate o) { - return this.text.compareTo(o.text); + return this.text().compareTo(o.text()); + } + + public String text() { + return String.join(" ", this.entries); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidateCollector.java b/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidateCollector.java index 2a9e3c7161..258b87b6d2 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidateCollector.java +++ b/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidateCollector.java @@ -27,12 +27,14 @@ public interface CompletionCandidateCollector { * @param commandlet the {@link Commandlet} owning the {@link Property}. * @return the {@link CompletionCandidate} for the given parameters. */ - default CompletionCandidate createCandidate(String text, String description, Property property, Commandlet commandlet) { + default CompletionCandidate createCandidate(String text, String description, + Property property, Commandlet commandlet, boolean complete) { if (description == null) { // compute description from property + commandlet like in HelpCommandlet? } - CompletionCandidate candidate = new CompletionCandidate(text, description); + CompletionCandidate candidate = new CompletionCandidate(Arrays.asList(text.split(" ")), + description, complete); return candidate; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidateCollectorDefault.java b/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidateCollectorDefault.java index 33f6fc25fb..91da2eb82d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidateCollectorDefault.java +++ b/cli/src/main/java/com/devonfw/tools/ide/completion/CompletionCandidateCollectorDefault.java @@ -48,7 +48,8 @@ public void add(String text, String description, Property property, Commandle } } - CompletionCandidate candidate = createCandidate(text, description, property, commandlet); + CompletionCandidate candidate = this.createCandidate(text, description, property, commandlet, + !text.endsWith("=")); this.candidates.add(candidate); LOG.trace("Added {} for auto-completion of property {}.{}", candidate, commandlet, property); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/completion/IdeCompleter.java b/cli/src/main/java/com/devonfw/tools/ide/completion/IdeCompleter.java index 3051cf309c..7e211283ac 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/completion/IdeCompleter.java +++ b/cli/src/main/java/com/devonfw/tools/ide/completion/IdeCompleter.java @@ -35,7 +35,8 @@ public void complete(LineReader reader, ParsedLine commandLine, List List completion = this.context.complete(args, true); int i = 0; for (CompletionCandidate candidate : completion) { - candidates.add(new Candidate(candidate.text(), candidate.text(), null, null, null, null, true, i++)); + candidates.add(new Candidate(candidate.text(), candidate.text(), null, null, null, null, + candidate.complete(), i++)); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java index f810c7d4d1..c793dbf8cd 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java @@ -1442,7 +1442,10 @@ public List complete(CliArguments arguments, boolean includ property.apply(arguments, this, cc, collector); } } - Iterator commandletIterator = this.commandletManager.findCommandlet(arguments, collector); + + this.commandletManager.collectCompletionCandidates(arguments, collector); + + Iterator commandletIterator = this.commandletManager.findCommandlet(arguments, null); CliArgument current = arguments.current(); if (current.isCompletion() && current.isCombinedShortOption()) { collector.add(current.get(), null, null, null); @@ -1461,7 +1464,7 @@ private void completeCommandlet(CliArguments arguments, Commandlet cmd, Completi LOG.trace("Trying to match arguments for auto-completion for commandlet {}", cmd.getName()); Iterator> valueIterator = cmd.getValues().iterator(); - valueIterator.next(); // skip first property since this is the keyword property that already matched to find the commandlet + Property lastValueProperty = null; List> properties = cmd.getProperties(); // we are creating our own list of options and remove them when matched to avoid duplicate suggestions List> optionProperties = new ArrayList<>(properties.size()); @@ -1499,18 +1502,30 @@ private void completeCommandlet(CliArguments arguments, Commandlet cmd, Completi } } } else { + if (currentArgument.isCompletion() && currentArgument.get().isEmpty() + && !arguments.isEndOptions()) { + for (Property option : optionProperties) { + option.apply(arguments, this, cmd, collector); + } + } + + Property valueProperty = null; if (valueIterator.hasNext()) { - Property valueProperty = valueIterator.next(); + valueProperty = valueIterator.next(); boolean success = valueProperty.apply(arguments, this, cmd, collector); if (!success) { LOG.trace("Completion cannot match any further."); return; } + } else if (lastValueProperty != null && lastValueProperty.isMultiValued()) { + valueProperty = lastValueProperty; } else { LOG.trace("No value left for completion."); return; } } + + arguments.next(); currentArgument = arguments.current(); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/BooleanProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/BooleanProperty.java index 680b62b1f3..51069a604d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/BooleanProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/BooleanProperty.java @@ -54,6 +54,10 @@ public Boolean parse(String valueAsString, IdeContext context) { return result; } + @Override + protected void completeValue(String arg, IdeContext contextual, Commandlet commandlet, + CompletionCandidateCollector collector) {} + private Boolean parse(String valueAsString) { if (valueAsString == null) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/EditionProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/EditionProperty.java index 7cc2d782b2..564d034232 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/EditionProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/EditionProperty.java @@ -1,5 +1,7 @@ package com.devonfw.tools.ide.property; +import com.devonfw.tools.ide.commandlet.Commandlet; +import com.devonfw.tools.ide.completion.CompletionCandidateCollector; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.validation.PropertyValidator; @@ -41,4 +43,8 @@ public String parse(String valueAsString, IdeContext context) { return valueAsString; } + + @Override + protected void completeValue(String arg, IdeContext contextual, Commandlet commandlet, + CompletionCandidateCollector collector) {} } diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/MvnArgProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/MvnArgProperty.java new file mode 100644 index 0000000000..5d748539ab --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/property/MvnArgProperty.java @@ -0,0 +1,50 @@ +package com.devonfw.tools.ide.property; + +import com.devonfw.tools.ide.commandlet.Commandlet; +import com.devonfw.tools.ide.completion.CompletionCandidateCollector; +import com.devonfw.tools.ide.context.IdeContext; +import java.util.List; + +public class MvnArgProperty extends StringProperty { + private static final List MAVEN_GOALS = List.of( + "clean", + "compile", + "dependency:list", + "dependency:tree", + "deploy", + "exec:java", + "generate-resources", + "generate-sources", + "help:effective-settings", + "install", + "integration-test", + "package", + "post-clean", + "post-integration-test", + "prepare-package", + "pre-clean", + "pre-integration-test", + "process-resources", + "process-sources", + "site", + "site-deploy", + "test", + "test-compile", + "validate", + "verify" + ); + + public MvnArgProperty(String name, String alias) { + super(name, true, true, alias); + } + + @Override + protected void completeValue(String arg, IdeContext context, Commandlet commandlet, + CompletionCandidateCollector collector) { + for (String goal : MAVEN_GOALS) { + if (goal.startsWith(arg)) { + collector.add(goal, null, this, commandlet); + } + } + } +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/NumberProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/NumberProperty.java index f13c59bb32..e6a433ef2b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/NumberProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/NumberProperty.java @@ -1,5 +1,7 @@ package com.devonfw.tools.ide.property; +import com.devonfw.tools.ide.commandlet.Commandlet; +import com.devonfw.tools.ide.completion.CompletionCandidateCollector; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.validation.PropertyValidator; @@ -49,4 +51,7 @@ public Long parse(String valueAsString, IdeContext context) { } } + @Override + protected void completeValue(String arg, IdeContext contextual, Commandlet commandlet, + CompletionCandidateCollector collector) {} } diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/Property.java b/cli/src/main/java/com/devonfw/tools/ide/property/Property.java index dbda7b1271..d27eaf2b4d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/Property.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/Property.java @@ -472,9 +472,7 @@ protected void complete(String normalizedName, CliArgument argument, CliArgument * @param commandlet the {@link Commandlet} owning this {@link Property}. * @param collector the {@link CompletionCandidateCollector}. */ - protected void completeValue(String arg, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) { - - } + protected abstract void completeValue(String arg, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector); /** * @param nameOrAlias the potential {@link #getName() name} or {@link #getAlias() alias} to match. diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/StringProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/StringProperty.java index b9ff9c4796..b5f0ecdb79 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/StringProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/StringProperty.java @@ -1,5 +1,7 @@ package com.devonfw.tools.ide.property; +import com.devonfw.tools.ide.commandlet.Commandlet; +import com.devonfw.tools.ide.completion.CompletionCandidateCollector; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.validation.PropertyValidator; @@ -59,6 +61,10 @@ public String parse(String valueAsString, IdeContext context) { return valueAsString; } + @Override + protected void completeValue(String arg, IdeContext contextual, Commandlet commandlet, + CompletionCandidateCollector collector) {} + /** * @return the {@link #getValue() value} as null-safe {@link String} array. */ diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/VersionProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/VersionProperty.java index 759512adc9..b70c85418a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/VersionProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/VersionProperty.java @@ -88,7 +88,7 @@ private void completeVersion(VersionIdentifier version2complete, ToolCommandlet List candidates = collector.getCandidates(); Collections.reverse(candidates); CompletionCandidate latest = collector.createCandidate(text + VersionSegment.PATTERN_MATCH_ANY_STABLE_VERSION, - "Latest stable matching version", this, commandlet); + "Latest stable matching version", this, commandlet, true); if (candidates.isEmpty()) { candidates.add(latest); } else { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java index 2097c3453a..a5cd9c5fc6 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java @@ -73,7 +73,6 @@ public ToolCommandlet(IdeContext context, String tool, Set tags) { this.tags = tags; addKeyword(tool); this.arguments = new StringProperty("", false, true, "args"); - initProperties(); } /** diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java index cf26227fa4..a2c3b967b5 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java @@ -20,6 +20,8 @@ import com.devonfw.tools.ide.process.ProcessContext; import com.devonfw.tools.ide.process.ProcessMode; import com.devonfw.tools.ide.process.ProcessResult; +import com.devonfw.tools.ide.property.FlagProperty; +import com.devonfw.tools.ide.property.MvnArgProperty; import com.devonfw.tools.ide.step.Step; import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; @@ -62,6 +64,20 @@ public class Mvn extends LocalToolCommandlet { private static final VariableSyntax VARIABLE_SYNTAX = VariableSyntax.SQUARE; + private final FlagProperty alsoMake; + private final FlagProperty alsoMakeDependents; + private final FlagProperty failAtEnd; + private final FlagProperty failFast; + private final FlagProperty threads; + + private final FlagProperty skipTests; + private final FlagProperty deployAtEnd; + + private final FlagProperty execMainClass; + private final FlagProperty execArgs; + + private final MvnArgProperty goals; + /** * The constructor. * @@ -70,6 +86,24 @@ public class Mvn extends LocalToolCommandlet { public Mvn(IdeContext context) { super(context, "mvn", Set.of(Tag.JAVA, Tag.BUILD)); + + this.alsoMake = this.add(new FlagProperty("--also-make", false, "-am")); + this.alsoMakeDependents = this.add(new FlagProperty("--also-make-dependents", false, "-amd")); + this.failAtEnd = this.add(new FlagProperty("--fail-at-end", false, "-fae")); + this.failFast = this.add(new FlagProperty("--fail-fast", false, "-ff")); + this.threads = this.add(new FlagProperty("--threads", false, "-t")); + + this.skipTests = this.add(new FlagProperty("--define skipTests", false, "-DskipTests")); + this.deployAtEnd = this.add(new FlagProperty("--define deployAtEnd", false, "-DdeployAtEnd")); + + this.execMainClass = this.add( + new FlagProperty("--define exec.mainClass=", false, "-Dexec.mainClass=") + ); + this.execArgs = this.add( + new FlagProperty("--define exec.args=", false, "-Dexec.args=") + ); + + this.goals = this.add(new MvnArgProperty("goals", "")); } @Override diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/CommandletManagerTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/CommandletManagerTest.java new file mode 100644 index 0000000000..6fa7a0eaa9 --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/CommandletManagerTest.java @@ -0,0 +1,64 @@ +package com.devonfw.tools.ide.commandlet; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import com.devonfw.tools.ide.cli.CliArguments; +import com.devonfw.tools.ide.completion.CompletionCandidate; +import com.devonfw.tools.ide.completion.CompletionCandidateCollector; +import com.devonfw.tools.ide.completion.CompletionCandidateCollectorDefault; +import com.devonfw.tools.ide.context.AbstractIdeContext; +import com.devonfw.tools.ide.context.IdeTestContext; +import org.junit.jupiter.api.Test; + +public class CommandletManagerTest { + @Test + public void testMvnCommandletIsPartOfCommandletIterator() { + IdeTestContext context = new IdeTestContext(); + CommandletManager manager = context.getCommandletManager(); + CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault( + context + ); + + assertThat(manager.findCommandlet(new CliArguments("mvn"), collector).hasNext()).isTrue(); + } + + @Test + public void testCollectCompletionCandidatesEmptyInputReturnsAllCommandlets() { + IdeTestContext context = new IdeTestContext(); + CommandletManager manager = context.getCommandletManager(); + CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault(context); + + manager.collectCompletionCandidates(CliArguments.ofCompletion(""), collector); + + List lines = collector.getSortedCandidates().stream() + .map(CompletionCandidate::text).toList(); + assertThat(lines).contains("complete", "create", "env", "install", "shell", "status", "update", + "mvn"); + } + + @Test + public void testCollectCompletionCandidatesPartialInputFiltersToMatches() { + IdeTestContext context = new IdeTestContext(); + CommandletManager manager = context.getCommandletManager(); + CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault(context); + + manager.collectCompletionCandidates(CliArguments.ofCompletion("ins"), collector); + + List lines = collector.getSortedCandidates().stream() + .map(CompletionCandidate::text).toList(); + assertThat(lines).containsExactly("install"); + } + + @Test + public void testCollectCompletionCandidatesNoMatchReturnsEmpty() { + IdeTestContext context = new IdeTestContext(); + CommandletManager manager = context.getCommandletManager(); + CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault(context); + + manager.collectCompletionCandidates(CliArguments.ofCompletion("xyz"), collector); + + assertThat(collector.getSortedCandidates()).isEmpty(); + } +} diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/ShellCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/ShellCommandletTest.java new file mode 100644 index 0000000000..08dbeb8f1f --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/ShellCommandletTest.java @@ -0,0 +1,33 @@ +package com.devonfw.tools.ide.commandlet; + +import com.devonfw.tools.ide.cli.CliArguments; +import com.devonfw.tools.ide.completion.CompletionCandidate; +import com.devonfw.tools.ide.context.AbstractIdeContextTest; +import com.devonfw.tools.ide.context.IdeTestContext; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * Test of {@link ShellCommandlet}. + */ +class ShellCommandletTest extends AbstractIdeContextTest { + @Test + public void testExitIsOfferedAsCompletion() { + IdeTestContext context = new IdeTestContext(); + CliArguments args = CliArguments.of(0, ""); + + List lines = context.complete(args, false).stream() + .map(CompletionCandidate::text).toList(); + assertThat(lines).contains("exit"); + } + + @Test + public void testExitIsOfferedOnPartialInput() { + IdeTestContext context = new IdeTestContext(); + CliArguments args = CliArguments.of(0, "ex"); + + List lines = context.complete(args, false).stream() + .map(CompletionCandidate::text).toList(); + assertThat(lines).containsExactly("exit"); + } +} diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java index ad5d37022a..5bc3a6329f 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java @@ -12,7 +12,12 @@ import java.util.Set; import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import com.devonfw.tools.ide.cli.CliArguments; +import com.devonfw.tools.ide.commandlet.Commandlet; +import com.devonfw.tools.ide.completion.CompletionCandidateCollector; +import com.devonfw.tools.ide.completion.CompletionCandidateCollectorDefault; import com.devonfw.tools.ide.io.FileAccess; import com.devonfw.tools.ide.io.FileCopyMode; import com.devonfw.tools.ide.io.IdeProgressBarTestImpl; @@ -319,4 +324,19 @@ protected void verifyStartScript(IdeContext context, String ide, String workspac } } + @Test + public void testMvIsCompletedToMvn() { + AbstractIdeContext context = new IdeTestContext(); + CliArguments args = CliArguments.ofCompletion("mv"); + + assertThat(context.complete(args, true).getFirst().text()).isEqualTo("mvn"); + } + + @Test + public void testMvIsCompletedToMvnWithoutContextCompletion() { + AbstractIdeContext context = new IdeTestContext(); + CliArguments args = CliArguments.ofCompletion("mv"); + + assertThat(context.complete(args, false).getFirst().text()).isEqualTo("mvn"); + } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/property/PropertyTest.java b/cli/src/test/java/com/devonfw/tools/ide/property/PropertyTest.java new file mode 100644 index 0000000000..49f99a06d6 --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/property/PropertyTest.java @@ -0,0 +1,121 @@ +package com.devonfw.tools.ide.property; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.devonfw.tools.ide.cli.CliArguments; +import com.devonfw.tools.ide.commandlet.Commandlet; +import com.devonfw.tools.ide.commandlet.ContextCommandlet; +import com.devonfw.tools.ide.completion.CompletionCandidateCollector; +import com.devonfw.tools.ide.completion.CompletionCandidateCollectorDefault; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.context.IdeTestContext; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for class `Property`. + */ +public class PropertyTest { + @Test + public void testApplyAddsPropertyAsCompletionCandidate() { + IdeContext context = new IdeTestContext(); + CliArguments args = CliArguments.ofCompletion("--for"); + + Commandlet ctxCmd = new ContextCommandlet(); + Property flagProperty = new FlagProperty("--force"); + CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault( + context + ); + + args.next(); + + flagProperty.apply("--force", args, context, ctxCmd, collector); + + assertThat(collector.getSortedCandidates().getFirst().text()).isEqualTo("--force"); + } + + @Test + public void testApplyWithCliArgumentAddsPropertyAsCompletionCandidate() { + IdeContext context = new IdeTestContext(); + CliArguments args = CliArguments.ofCompletion("mv"); + + Commandlet mvnCmd = context.getCommandletManager().getCommandletByFirstKeyword("mvn"); + Property keywordProperty = new KeywordProperty("mvn", false, ""); + CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault( + context + ); + + args.next(); + + keywordProperty.apply(args, context, mvnCmd, collector); + + assertThat(collector.getSortedCandidates().getFirst().text()).isEqualTo("mvn"); + } + + @Test + public void testMismatchingApplyReturnsFalse() { + IdeContext context = new IdeTestContext(); + CliArguments args = CliArguments.ofCompletion("mv"); + + Commandlet mvnCmd = context.getCommandletManager().getCommandletByFirstKeyword("mvn"); + Property flagProperty = new FlagProperty("--force"); + CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault( + context + ); + + args.next(); + + assertThat(flagProperty.apply(args, context, mvnCmd, collector)).isFalse(); + } + + @Test + public void testMismatchingApplyOnKeywordReturnsFalse() { + IdeContext context = new IdeTestContext(); + CliArguments args = CliArguments.ofCompletion("st"); + + Commandlet mvnCmd = context.getCommandletManager().getCommandletByFirstKeyword("mvn"); + Property keywordProperty = new KeywordProperty("mvn", false, ""); + CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault( + context + ); + + args.next(); + + assertThat(keywordProperty.apply(args, context, mvnCmd, collector)).isFalse(); + } + + @Test + public void testCompleteAddsToCandidatesOnMatch() { + IdeContext context = new IdeTestContext(); + CliArguments args = CliArguments.ofCompletion("--for"); + + Commandlet ctxCmd = context.getCommandletManager().getCommandletByFirstKeyword("context"); + Property flagProperty = new FlagProperty("--force"); + CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault( + context + ); + + args.next(); + + flagProperty.complete("--force", args.current(), args, context, ctxCmd, collector); + + assertThat(collector.getSortedCandidates().getFirst().text()).isEqualTo("--force"); + } + + @Test + public void testCompleteAddsCandidateForNonOption() { + IdeContext context = new IdeTestContext(); + CliArguments args = CliArguments.ofCompletion("mv"); + + Commandlet ctxCmd = context.getCommandletManager().getCommandletByFirstKeyword("mvn"); + Property keywordProperty = new KeywordProperty("mvn", false, ""); + CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault( + context + ); + + args.next(); + + keywordProperty.complete("mvn", args.current(), args, context, ctxCmd, collector); + + assertThat(collector.getSortedCandidates().getFirst().text()).isEqualTo("mvn"); + } +} diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/mvn/MvnTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/mvn/MvnTest.java index 7cf8e2d121..735c69d060 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/mvn/MvnTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/mvn/MvnTest.java @@ -4,12 +4,15 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.junit.jupiter.api.Test; +import com.devonfw.tools.ide.cli.CliArguments; +import com.devonfw.tools.ide.completion.CompletionCandidate; import com.devonfw.tools.ide.context.AbstractIdeContextTest; import com.devonfw.tools.ide.context.IdeTestContext; import com.devonfw.tools.ide.io.FileAccess; @@ -138,4 +141,126 @@ private void assertFileContent(Path filePath, List expectedValues) throw assertThat(values).containsExactlyInAnyOrderElementsOf(expectedValues); } + + @Test + public void testEmptyGoalInputSuggestsAllMavenProperties() { + String path = "project/workspaces"; + + IdeTestContext context = newContext(PROJECT_MVN, path, false); + CliArguments args = CliArguments.of(1, "mvn", ""); + + List lines = context.complete(args, false).stream() + .map(CompletionCandidate::text).toList(); + assertThat(lines).contains("clean", "compile", "test", "package", "install", "deploy"); + } + + @Test + public void testEmptyInputSuggestsFlagProperties() { + String path = "project/workspaces"; + + IdeTestContext context = newContext(PROJECT_MVN, path, false); + CliArguments args = CliArguments.of(1, "mvn", ""); + + List lines = context.complete(args, false).stream() + .map(CompletionCandidate::text).toList(); + assertThat(lines).contains("--also-make", "--also-make-dependents", "--fail-at-end", + "--fail-fast", "--threads", "-DskipTests", "-DdeployAtEnd"); + } + + @Test + public void testPartialGoalInputFiltersToMatchingGoals() { + String path = "project/workspaces"; + + IdeTestContext context = newContext(PROJECT_MVN, path, false); + CliArguments args = CliArguments.of(1, "mvn", "cl"); + + List lines = context.complete(args, false).stream() + .map(CompletionCandidate::text).toList(); + assertThat(lines).containsExactly("clean"); + } + + @Test + public void testUnmatchedGoalInputReturnsNoSuggestions() { + String path = "project/workspaces"; + + IdeTestContext context = newContext(PROJECT_MVN, path, false); + CliArguments args = CliArguments.of(1, "mvn", "xyz"); + + assertThat(context.complete(args, false)).isEmpty(); + } + + @Test + public void testSecondGoalIsCompletedAfterFirstGoal() { + String path = "project/workspaces"; + + IdeTestContext context = newContext(PROJECT_MVN, path, false); + CliArguments args = CliArguments.of(2, "mvn", "clean", ""); + + List lines = context.complete(args, false).stream() + .map(CompletionCandidate::text).toList(); + assertThat(lines).contains("package", "install", "deploy", "test"); + } + + @Test + public void testSecondGoalPartialInputFiltersToMatches() { + String path = "project/workspaces"; + + IdeTestContext context = newContext(PROJECT_MVN, path, false); + CliArguments args = CliArguments.of(2, "mvn", "clean", "pa"); + + List lines = context.complete(args, false).stream() + .map(CompletionCandidate::text).toList(); + assertThat(lines).containsExactly("package"); + } + + @Test + public void testAlsoMakeFlagIsOfferedOnPartialInput() { + String path = "project/workspaces"; + + IdeTestContext context = newContext(PROJECT_MVN, path, false); + CliArguments args = CliArguments.of(1, "mvn", "--also"); + + List lines = context.complete(args, false).stream() + .map(CompletionCandidate::text).toList(); + assertThat(lines).containsExactly("--also-make", "--also-make-dependents"); + } + + @Test + public void testShortAlsoMakeFlagIsOfferedOnPartialInput() { + String path = "project/workspaces"; + + IdeTestContext context = newContext(PROJECT_MVN, path, false); + CliArguments args = CliArguments.of(1, "mvn", "-am"); + + List lines = context.complete(args, false).stream() + .map(CompletionCandidate::text).toList(); + assertThat(lines).containsExactly("-am", "-amd"); + } + + @Test + public void testExecMainClassFlagCandidateIsMarkedIncomplete() { + String path = "project/workspaces"; + + IdeTestContext context = newContext(PROJECT_MVN, path, false); + CliArguments args = CliArguments.of(1, "mvn", "-Dexec.mainClass="); + + Optional candidate = context.complete(args, false).stream() + .filter(c -> c.text().equals("-Dexec.mainClass=")) + .findFirst(); + + assertThat(candidate).isPresent(); + assertThat(candidate.get().complete()).isFalse(); + } + + @Test + public void testAlsoMakeFlagIsNotOfferedWhenAlreadyUsed() { + String path = "project/workspaces"; + + IdeTestContext context = newContext(PROJECT_MVN, path, false); + CliArguments args = CliArguments.of(2, "mvn", "--also-make", "--al"); + + List lines = context.complete(args, false).stream() + .map(CompletionCandidate::text).toList(); + assertThat(lines).doesNotContain("--also-make"); + } }