Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/devonfw/IDEasy into 100-imp…
Browse files Browse the repository at this point in the history
…lement-repository-commandlet
  • Loading branch information
salimbouch committed Feb 2, 2024
2 parents e21f222 + 905710a commit d06a970
Show file tree
Hide file tree
Showing 37 changed files with 1,379 additions and 103 deletions.
12 changes: 12 additions & 0 deletions cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
<releaseName>${project.artifactId}-${os.detected.classifier}-${os.detected.arch}</releaseName>
<java.version>17</java.version>
<native.maven.plugin.version>0.9.28</native.maven.plugin.version>
<jline.version>3.24.1</jline.version>
<jansi.version>2.4.0</jansi.version>
</properties>

<dependencies>
Expand Down Expand Up @@ -75,6 +77,16 @@
<artifactId>progressbar</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline</artifactId>
<version>${jline.version}</version>
</dependency>
<dependency>
<groupId>org.fusesource.jansi</groupId>
<artifactId>jansi</artifactId>
<version>${jansi.version}</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.property.KeywordProperty;
import com.devonfw.tools.ide.property.Property;
import com.devonfw.tools.ide.tool.ToolCommandlet;
import com.devonfw.tools.ide.version.VersionIdentifier;

/**
Expand Down Expand Up @@ -208,12 +209,11 @@ public String toString() {
}

/**
* @param version the {@link VersionIdentifier} to complete.
* @param collector the {@link CompletionCandidateCollector}.
* @return {@code true} on success, {@code false} otherwise.
* @return the {@link ToolCommandlet} set in a {@link Property} of this commandlet used for auto-completion of a
* {@link VersionIdentifier} or {@code null} if not exists or not configured.
*/
public boolean completeVersion(VersionIdentifier version, CompletionCandidateCollector collector) {
public ToolCommandlet getToolForVersionCompletion() {

return false;
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.property.KeywordProperty;
import com.devonfw.tools.ide.property.Property;
import com.devonfw.tools.ide.tool.aws.Aws;
import com.devonfw.tools.ide.tool.az.Azure;
import com.devonfw.tools.ide.tool.eclipse.Eclipse;
import com.devonfw.tools.ide.tool.gcviewer.GcViewer;
import com.devonfw.tools.ide.tool.gh.Gh;
import com.devonfw.tools.ide.tool.gradle.Gradle;
import com.devonfw.tools.ide.tool.helm.Helm;
Expand All @@ -23,6 +25,7 @@
import com.devonfw.tools.ide.tool.quarkus.Quarkus;
import com.devonfw.tools.ide.tool.terraform.Terraform;
import com.devonfw.tools.ide.tool.vscode.Vscode;
import com.devonfw.tools.ide.tool.cobigen.Cobigen;

/**
* Implementation of {@link CommandletManager}.
Expand Down Expand Up @@ -52,6 +55,7 @@ public CommandletManagerImpl(IdeContext context) {
add(new HelpCommandlet(context));
add(new EnvironmentCommandlet(context));
add(new CompleteCommandlet(context));
add(new ShellCommandlet(context));
add(new InstallCommandlet(context));
add(new VersionSetCommandlet(context));
add(new VersionGetCommandlet(context));
Expand All @@ -63,6 +67,7 @@ public CommandletManagerImpl(IdeContext context) {
add(new Java(context));
add(new Node(context));
add(new Mvn(context));
add(new GcViewer(context));
add(new Gradle(context));
add(new Eclipse(context));
add(new Terraform(context));
Expand All @@ -72,6 +77,8 @@ public CommandletManagerImpl(IdeContext context) {
add(new KotlincNative(context));
add(new Vscode(context));
add(new Azure(context));
add(new Aws(context));
add(new Cobigen(context));
}

private void add(Commandlet commandlet) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,9 @@ public void run() {
commandlet.install(false);
}

@Override
public ToolCommandlet getToolForVersionCompletion() {

return this.tool.getValue();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package com.devonfw.tools.ide.commandlet;

import java.io.IOException;
import java.util.Iterator;

import org.fusesource.jansi.AnsiConsole;
import org.jline.reader.EndOfFileException;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.MaskingCallback;
import org.jline.reader.Parser;
import org.jline.reader.UserInterruptException;
import org.jline.reader.impl.DefaultParser;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import org.jline.widget.AutosuggestionWidgets;

import com.devonfw.tools.ide.cli.CliArgument;
import com.devonfw.tools.ide.cli.CliArguments;
import com.devonfw.tools.ide.cli.CliException;
import com.devonfw.tools.ide.completion.IdeCompleter;
import com.devonfw.tools.ide.context.AbstractIdeContext;
import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.property.BooleanProperty;
import com.devonfw.tools.ide.property.FlagProperty;
import com.devonfw.tools.ide.property.KeywordProperty;
import com.devonfw.tools.ide.property.Property;

/**
* {@link Commandlet} for internal interactive shell with build-in auto-completion and help.
*/
public final class ShellCommandlet extends Commandlet {

private static final int AUTOCOMPLETER_MAX_RESULTS = 50;

private static final int RC_EXIT = 987654321;

/**
* The constructor.
*
* @param context the {@link IdeContext}.
*/
public ShellCommandlet(IdeContext context) {

super(context);
addKeyword(getName());
}

@Override
public String getName() {

return "shell";
}

@Override
public boolean isIdeHomeRequired() {

return false;
}

@Override
public void run() {

try {
// TODO: add BuiltIns here, see: https://github.com/devonfw/IDEasy/issues/168

Parser parser = new DefaultParser();
try (Terminal terminal = TerminalBuilder.builder().build()) {

// initialize our own completer here
IdeCompleter completer = new IdeCompleter((AbstractIdeContext) this.context);

LineReader reader = LineReaderBuilder.builder().terminal(terminal).completer(completer).parser(parser)
.variable(LineReader.LIST_MAX, AUTOCOMPLETER_MAX_RESULTS).build();

// Create autosuggestion widgets
AutosuggestionWidgets autosuggestionWidgets = new AutosuggestionWidgets(reader);
// Enable autosuggestions
autosuggestionWidgets.enable();

// TODO: implement TailTipWidgets, see: https://github.com/devonfw/IDEasy/issues/169

String prompt = "ide> ";
String rightPrompt = null;
String line;

AnsiConsole.systemInstall();
while (true) {
try {
line = reader.readLine(prompt, rightPrompt, (MaskingCallback) null, null);
reader.getHistory().add(line);
int rc = runCommand(line);
if (rc == RC_EXIT) {
return;
}
} catch (UserInterruptException e) {
// Ignore CTRL+C
return;
} catch (EndOfFileException e) {
// CTRL+D
return;
} finally {
AnsiConsole.systemUninstall();
}
}

} catch (IOException e) {
throw new RuntimeException(e);
}
} catch (Exception e) {
throw new RuntimeException("Unexpected error during interactive auto-completion", e);
}
}

/**
* Converts String of arguments to array and runs the command
*
* @param args String of arguments
* @return status code
*/
private int runCommand(String args) {

if ("exit".equals(args) || "quit".equals(args)) {
return RC_EXIT;
}
String[] arguments = args.split(" ", 0);
CliArguments cliArgs = new CliArguments(arguments);
cliArgs.next();
return ((AbstractIdeContext) this.context).run(cliArgs);
}

/**
* @param argument the current {@link CliArgument} (position) to match.
* @param commandlet the potential {@link Commandlet} to match.
* @return {@code true} if the given {@link Commandlet} matches to the given {@link CliArgument}(s) and those have
* been applied (set in the {@link Commandlet} and {@link Commandlet#validate() validated}), {@code false}
* otherwise (the {@link Commandlet} did not match and we have to try a different candidate).
*/
private boolean apply(CliArgument argument, Commandlet commandlet) {

this.context.trace("Trying to match arguments to commandlet {}", commandlet.getName());
CliArgument currentArgument = argument;
Iterator<Property<?>> valueIterator = commandlet.getValues().iterator();
Property<?> currentProperty = null;
boolean endOpts = false;
while (!currentArgument.isEnd()) {
if (currentArgument.isEndOptions()) {
endOpts = true;
} else {
String arg = currentArgument.get();
this.context.trace("Trying to match argument '{}'", currentArgument);
if ((currentProperty != null) && (currentProperty.isExpectValue())) {
currentProperty.setValueAsString(arg, this.context);
if (!currentProperty.isMultiValued()) {
currentProperty = null;
}
} else {
Property<?> property = null;
if (!endOpts) {
property = commandlet.getOption(currentArgument.getKey());
}
if (property == null) {
if (!valueIterator.hasNext()) {
this.context.trace("No option or next value found");
return false;
}
currentProperty = valueIterator.next();
this.context.trace("Next value candidate is {}", currentProperty);
if (currentProperty instanceof KeywordProperty) {
KeywordProperty keyword = (KeywordProperty) currentProperty;
if (keyword.matches(arg)) {
keyword.setValue(Boolean.TRUE);
this.context.trace("Keyword matched");
} else {
this.context.trace("Missing keyword");
return false;
}
} else {
boolean success = currentProperty.assignValueAsString(arg, this.context, commandlet);
if (!success && currentProperty.isRequired()) {
return false;
}
}
if ((currentProperty != null) && !currentProperty.isMultiValued()) {
currentProperty = null;
}
} else {
this.context.trace("Found option by name");
String value = currentArgument.getValue();
if (value != null) {
property.setValueAsString(value, this.context);
} else if (property instanceof BooleanProperty) {
((BooleanProperty) property).setValue(Boolean.TRUE);
} else {
currentProperty = property;
if (property.isEndOptions()) {
endOpts = true;
}
throw new UnsupportedOperationException("not implemented");
}
}
}
}
currentArgument = currentArgument.getNext(!endOpts);
}
return commandlet.validate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,31 +52,9 @@ public void run() {
}

@Override
public boolean completeVersion(VersionIdentifier version2complete, CompletionCandidateCollector collector) {
public ToolCommandlet getToolForVersionCompletion() {

ToolCommandlet toolCmd = this.tool.getValue();
if (toolCmd != null) {
String text;
if (version2complete == null) {
text = "";
} else {
text = version2complete.toString();
if (version2complete.isPattern()) {
collector.add(text, this.version, this);
return true;
}
}
collector.add(text + VersionSegment.PATTERN_MATCH_ANY_STABLE_VERSION, this.tool, this);
collector.add(text + VersionSegment.PATTERN_MATCH_ANY_VERSION, this.tool, this);
List<VersionIdentifier> versions = this.context.getUrls().getSortedVersions(toolCmd.getName(),
toolCmd.getEdition());
int size = versions.size();
String[] sorderCandidates = IntStream.rangeClosed(1, size).mapToObj(i -> versions.get(size - i).toString())
.toArray(s -> new String[s]);
collector.addAllMatches(text, sorderCandidates, this.version, this);
return true;
}
return super.completeVersion(version2complete, collector);
return this.tool.getValue();
}

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.devonfw.tools.ide.completion;

import com.devonfw.tools.ide.version.VersionSegment;

/**
* 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<CompletionCandidate> {
public record CompletionCandidate(String text, String description) implements Comparable<CompletionCandidate> {

@Override
public int compareTo(CompletionCandidate o) {
Expand Down
Loading

0 comments on commit d06a970

Please sign in to comment.