Skip to content

Commit

Permalink
Merge pull request #1 from codejive/consoleui
Browse files Browse the repository at this point in the history
feat: added interactive search mode
  • Loading branch information
quintesse authored Jul 12, 2024
2 parents b335a6d + 3813dfd commit 8be2a43
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 22 deletions.
2 changes: 2 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"eu.maveniverse.maven.mima.runtime:standalone-static": "2.4.15",
"eu.maveniverse.maven.mima:context": "2.4.15",
"info.picocli:picocli": "4.7.6",
"org.jline:jline-console-ui": "3.26.2",
"org.jline:jline-terminal-jni": "3.26.2",
"org.slf4j:slf4j-api": "2.0.13",
"org.slf4j:slf4j-log4j12": "2.0.13",
"org.slf4j:slf4j-simple": "2.0.13"
Expand Down
11 changes: 11 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<version.mima>2.4.15</version.mima>
<version.picocli>4.7.6</version.picocli>
<version.gson>2.11.0</version.gson>
<version.jline>3.26.2</version.jline>
<version.slf4j>2.0.13</version.slf4j>
<version.spotless>2.43.0</version.spotless>
<version.google-java-format>1.22.0</version.google-java-format>
Expand All @@ -61,6 +62,16 @@
<artifactId>gson</artifactId>
<version>${version.gson}</version>
</dependency>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline-console-ui</artifactId>
<version>${version.jline}</version>
</dependency>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline-terminal-jni</artifactId>
<version>${version.jline}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
Expand Down
145 changes: 136 additions & 9 deletions src/main/java/org/codejive/jpm/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//DEPS eu.maveniverse.maven.mima:context:2.4.15 eu.maveniverse.maven.mima.runtime:standalone-static:2.4.15
//DEPS info.picocli:picocli:4.7.6
//DEPS com.google.code.gson:gson:2.11.0
//DEPS org.jline:jline-console-ui:3.26.2 org.jline:jline-terminal-jni:3.26.2
//DEPS org.slf4j:slf4j-api:2.0.13 org.slf4j:slf4j-simple:2.0.13
//SOURCES Jpm.java json/AppInfo.java util/FileUtils.java util/ResolverUtils.java util/SearchUtils.java
//SOURCES util/SearchResult.java util/SyncStats.java util/Version.java
Expand All @@ -16,6 +17,13 @@
import java.util.stream.Collectors;
import org.codejive.jpm.util.SyncStats;
import org.codejive.jpm.util.Version;
import org.jline.consoleui.prompt.ConsolePrompt;
import org.jline.consoleui.prompt.ListResult;
import org.jline.consoleui.prompt.PromptResultItemIF;
import org.jline.consoleui.prompt.builder.ListPromptBuilder;
import org.jline.consoleui.prompt.builder.PromptBuilder;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Mixin;
Expand Down Expand Up @@ -74,6 +82,12 @@ static class Sync implements Callable<Integer> {
@Mixin QuietMixin quietMixin;
@Mixin CopyMixin copyMixin;

@Option(
names = {"-i", "--interactive"},
description = "Interactively search and select artifacts to install",
defaultValue = "false")
private boolean interactive;

@Option(
names = {"-m", "--max"},
description = "Maximum number of results to return",
Expand All @@ -82,22 +96,135 @@ static class Sync implements Callable<Integer> {

@Parameters(
paramLabel = "artifactPattern",
description = "Partial or full artifact name to search for.")
description = "Partial or full artifact name to search for.",
defaultValue = "")
private String artifactPattern;

@Override
public Integer call() throws Exception {
String[] artifactNames =
Jpm.builder()
.directory(copyMixin.directory)
.noLinks(copyMixin.noLinks)
.build()
.search(artifactPattern, Math.min(max, 200));
if (artifactNames.length > 0) {
Arrays.stream(artifactNames).forEach(System.out::println);
if (interactive || artifactPattern == null || artifactPattern.isEmpty()) {
try (Terminal terminal = TerminalBuilder.builder().build()) {
while (true) {
ConsolePrompt prompt = new ConsolePrompt(terminal);
if (artifactPattern == null || artifactPattern.isEmpty()) {
artifactPattern = askString(prompt, "Search for:");
}
String[] artifactNames = search(artifactPattern);
PromptBuilder promptBuilder = prompt.getPromptBuilder();
addSelectItem(promptBuilder, "Select artifact:", artifactNames);
addSelectArtifactAction(promptBuilder);
Map<String, PromptResultItemIF> result =
prompt.prompt(promptBuilder.build());
String selectedArtifact = getSelectedId(result, "item");
String artifactAction = getSelectedId(result, "action");
if ("install".equals(artifactAction)) {
SyncStats stats =
Jpm.builder()
.directory(copyMixin.directory)
.noLinks(copyMixin.noLinks)
.build()
.install(new String[] {selectedArtifact});
if (!quietMixin.quiet) {
printStats(stats);
}
} else if ("copy".equals(artifactAction)) {
SyncStats stats =
Jpm.builder()
.directory(copyMixin.directory)
.noLinks(copyMixin.noLinks)
.build()
.copy(new String[] {selectedArtifact}, false);
if (!quietMixin.quiet) {
printStats(stats);
}
} else if ("version".equals(artifactAction)) {
artifactPattern = selectedArtifact;
continue;
} else { // quit
break;
}
String finalAction = selectFinalAction(prompt);
if ("quit".equals(finalAction)) {
break;
}
artifactPattern = null;
}
}
} else {
String[] artifactNames = search(artifactPattern);
if (artifactNames.length > 0) {
Arrays.stream(artifactNames).forEach(System.out::println);
}
}
return 0;
}

String[] search(String artifactPattern) throws IOException {
return Jpm.builder()
.directory(copyMixin.directory)
.noLinks(copyMixin.noLinks)
.build()
.search(artifactPattern, Math.min(max, 200));
}

String askString(ConsolePrompt prompt, String message) throws IOException {
PromptBuilder promptBuilder = prompt.getPromptBuilder();
promptBuilder.createInputPrompt().name("input").message(message).addPrompt();
Map<String, PromptResultItemIF> result = prompt.prompt(promptBuilder.build());
return result.get("input").getResult();
}

void addSelectItem(PromptBuilder promptBuilder, String message, String[] items)
throws IOException {
ListPromptBuilder artifactsList =
promptBuilder.createListPrompt().name("item").message(message).pageSize(10);
for (String artifactName : items) {
artifactsList.newItem(artifactName).text(artifactName).add();
}
artifactsList.addPrompt();
}

void addSelectArtifactAction(PromptBuilder promptBuilder) throws IOException {
promptBuilder
.createListPrompt()
.name("action")
.message("What to do:")
.newItem("install")
.text("Install artifact")
.add()
.newItem("copy")
.text("Copy artifact")
.add()
.newItem("version")
.text("Select different version")
.add()
.newItem("quit")
.text("Quit")
.add()
.addPrompt();
}

String selectFinalAction(ConsolePrompt prompt) throws IOException {
PromptBuilder promptBuilder = prompt.getPromptBuilder();
promptBuilder
.createListPrompt()
.name("action")
.message("What to do:")
.newItem("again")
.text("Search again")
.add()
.newItem("quit")
.text("Quit")
.add()
.addPrompt();
Map<String, PromptResultItemIF> result = prompt.prompt(promptBuilder.build());
return getSelectedId(result, "action");
}

private static String getSelectedId(
Map<String, PromptResultItemIF> result, String itemName) {
return ((ListResult) result.get(itemName)).getSelectedId();
}
}

@Command(
Expand Down
28 changes: 15 additions & 13 deletions src/main/java/org/codejive/jpm/util/SearchUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ private static SearchResult select(String query, int start, int count) throws IO
}
String searchUrl =
String.format(
"https://search.maven.org/solrsearch/select?start=%d&rows=%d&q=%s",
"https://search.maven.org/solrsearch/select?start=%d&rows=%d&q=p:jar+AND+%s",
start, count, URLEncoder.encode(finalQuery, "UTF-8"));
if (parts.length >= 3) {
searchUrl += "&core=gav";
Expand All @@ -90,23 +90,23 @@ private static SearchResult select(String query, int start, int count) throws IO
}
List<DefaultArtifact> artifacts =
result.response.docs.stream()
.filter(
d ->
parts.length != 2
|| d.g.contains(parts[0])
&& d.a.contains(parts[1]))
.map(
d ->
new DefaultArtifact(
d.g,
d.a,
"",
d.v != null ? d.v : d.latestVersion))
.filter(d -> acceptDoc(d, parts))
.map(SearchUtils::toArtifact)
.collect(Collectors.toList());
return new SearchResult(artifacts, query, start, count, result.response.numFound);
}
}
}

private static boolean acceptDoc(MsrDoc d, String[] parts) {
return d.ec != null
&& d.ec.contains(".jar")
&& (parts.length != 2 || d.g.contains(parts[0]) && d.a.contains(parts[1]));
}

private static DefaultArtifact toArtifact(MsrDoc d) {
return new DefaultArtifact(d.g, d.a, "", d.v != null ? d.v : d.latestVersion);
}
}

class MvnSearchResult {
Expand All @@ -129,4 +129,6 @@ class MsrDoc {
public String a;
public String v;
public String latestVersion;
public String p;
public List<String> ec;
}

0 comments on commit 8be2a43

Please sign in to comment.