Skip to content

Commit 3813dfd

Browse files
committed
feat: added interactive search mode
1 parent ac37101 commit 3813dfd

File tree

3 files changed

+149
-9
lines changed

3 files changed

+149
-9
lines changed

app.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
"eu.maveniverse.maven.mima.runtime:standalone-static": "2.4.15",
66
"eu.maveniverse.maven.mima:context": "2.4.15",
77
"info.picocli:picocli": "4.7.6",
8+
"org.jline:jline-console-ui": "3.26.2",
9+
"org.jline:jline-terminal-jni": "3.26.2",
810
"org.slf4j:slf4j-api": "2.0.13",
911
"org.slf4j:slf4j-log4j12": "2.0.13",
1012
"org.slf4j:slf4j-simple": "2.0.13"

pom.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<version.mima>2.4.15</version.mima>
3636
<version.picocli>4.7.6</version.picocli>
3737
<version.gson>2.11.0</version.gson>
38+
<version.jline>3.26.2</version.jline>
3839
<version.slf4j>2.0.13</version.slf4j>
3940
<version.spotless>2.43.0</version.spotless>
4041
<version.google-java-format>1.22.0</version.google-java-format>
@@ -61,6 +62,16 @@
6162
<artifactId>gson</artifactId>
6263
<version>${version.gson}</version>
6364
</dependency>
65+
<dependency>
66+
<groupId>org.jline</groupId>
67+
<artifactId>jline-console-ui</artifactId>
68+
<version>${version.jline}</version>
69+
</dependency>
70+
<dependency>
71+
<groupId>org.jline</groupId>
72+
<artifactId>jline-terminal-jni</artifactId>
73+
<version>${version.jline}</version>
74+
</dependency>
6475
<dependency>
6576
<groupId>org.slf4j</groupId>
6677
<artifactId>slf4j-api</artifactId>

src/main/java/org/codejive/jpm/Main.java

Lines changed: 136 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//DEPS eu.maveniverse.maven.mima:context:2.4.15 eu.maveniverse.maven.mima.runtime:standalone-static:2.4.15
33
//DEPS info.picocli:picocli:4.7.6
44
//DEPS com.google.code.gson:gson:2.11.0
5+
//DEPS org.jline:jline-console-ui:3.26.2 org.jline:jline-terminal-jni:3.26.2
56
//DEPS org.slf4j:slf4j-api:2.0.13 org.slf4j:slf4j-simple:2.0.13
67
//SOURCES Jpm.java json/AppInfo.java util/FileUtils.java util/ResolverUtils.java util/SearchUtils.java
78
//SOURCES util/SearchResult.java util/SyncStats.java util/Version.java
@@ -16,6 +17,13 @@
1617
import java.util.stream.Collectors;
1718
import org.codejive.jpm.util.SyncStats;
1819
import org.codejive.jpm.util.Version;
20+
import org.jline.consoleui.prompt.ConsolePrompt;
21+
import org.jline.consoleui.prompt.ListResult;
22+
import org.jline.consoleui.prompt.PromptResultItemIF;
23+
import org.jline.consoleui.prompt.builder.ListPromptBuilder;
24+
import org.jline.consoleui.prompt.builder.PromptBuilder;
25+
import org.jline.terminal.Terminal;
26+
import org.jline.terminal.TerminalBuilder;
1927
import picocli.CommandLine;
2028
import picocli.CommandLine.Command;
2129
import picocli.CommandLine.Mixin;
@@ -74,6 +82,12 @@ static class Sync implements Callable<Integer> {
7482
@Mixin QuietMixin quietMixin;
7583
@Mixin CopyMixin copyMixin;
7684

85+
@Option(
86+
names = {"-i", "--interactive"},
87+
description = "Interactively search and select artifacts to install",
88+
defaultValue = "false")
89+
private boolean interactive;
90+
7791
@Option(
7892
names = {"-m", "--max"},
7993
description = "Maximum number of results to return",
@@ -82,22 +96,135 @@ static class Sync implements Callable<Integer> {
8296

8397
@Parameters(
8498
paramLabel = "artifactPattern",
85-
description = "Partial or full artifact name to search for.")
99+
description = "Partial or full artifact name to search for.",
100+
defaultValue = "")
86101
private String artifactPattern;
87102

88103
@Override
89104
public Integer call() throws Exception {
90-
String[] artifactNames =
91-
Jpm.builder()
92-
.directory(copyMixin.directory)
93-
.noLinks(copyMixin.noLinks)
94-
.build()
95-
.search(artifactPattern, Math.min(max, 200));
96-
if (artifactNames.length > 0) {
97-
Arrays.stream(artifactNames).forEach(System.out::println);
105+
if (interactive || artifactPattern == null || artifactPattern.isEmpty()) {
106+
try (Terminal terminal = TerminalBuilder.builder().build()) {
107+
while (true) {
108+
ConsolePrompt prompt = new ConsolePrompt(terminal);
109+
if (artifactPattern == null || artifactPattern.isEmpty()) {
110+
artifactPattern = askString(prompt, "Search for:");
111+
}
112+
String[] artifactNames = search(artifactPattern);
113+
PromptBuilder promptBuilder = prompt.getPromptBuilder();
114+
addSelectItem(promptBuilder, "Select artifact:", artifactNames);
115+
addSelectArtifactAction(promptBuilder);
116+
Map<String, PromptResultItemIF> result =
117+
prompt.prompt(promptBuilder.build());
118+
String selectedArtifact = getSelectedId(result, "item");
119+
String artifactAction = getSelectedId(result, "action");
120+
if ("install".equals(artifactAction)) {
121+
SyncStats stats =
122+
Jpm.builder()
123+
.directory(copyMixin.directory)
124+
.noLinks(copyMixin.noLinks)
125+
.build()
126+
.install(new String[] {selectedArtifact});
127+
if (!quietMixin.quiet) {
128+
printStats(stats);
129+
}
130+
} else if ("copy".equals(artifactAction)) {
131+
SyncStats stats =
132+
Jpm.builder()
133+
.directory(copyMixin.directory)
134+
.noLinks(copyMixin.noLinks)
135+
.build()
136+
.copy(new String[] {selectedArtifact}, false);
137+
if (!quietMixin.quiet) {
138+
printStats(stats);
139+
}
140+
} else if ("version".equals(artifactAction)) {
141+
artifactPattern = selectedArtifact;
142+
continue;
143+
} else { // quit
144+
break;
145+
}
146+
String finalAction = selectFinalAction(prompt);
147+
if ("quit".equals(finalAction)) {
148+
break;
149+
}
150+
artifactPattern = null;
151+
}
152+
}
153+
} else {
154+
String[] artifactNames = search(artifactPattern);
155+
if (artifactNames.length > 0) {
156+
Arrays.stream(artifactNames).forEach(System.out::println);
157+
}
98158
}
99159
return 0;
100160
}
161+
162+
String[] search(String artifactPattern) throws IOException {
163+
return Jpm.builder()
164+
.directory(copyMixin.directory)
165+
.noLinks(copyMixin.noLinks)
166+
.build()
167+
.search(artifactPattern, Math.min(max, 200));
168+
}
169+
170+
String askString(ConsolePrompt prompt, String message) throws IOException {
171+
PromptBuilder promptBuilder = prompt.getPromptBuilder();
172+
promptBuilder.createInputPrompt().name("input").message(message).addPrompt();
173+
Map<String, PromptResultItemIF> result = prompt.prompt(promptBuilder.build());
174+
return result.get("input").getResult();
175+
}
176+
177+
void addSelectItem(PromptBuilder promptBuilder, String message, String[] items)
178+
throws IOException {
179+
ListPromptBuilder artifactsList =
180+
promptBuilder.createListPrompt().name("item").message(message).pageSize(10);
181+
for (String artifactName : items) {
182+
artifactsList.newItem(artifactName).text(artifactName).add();
183+
}
184+
artifactsList.addPrompt();
185+
}
186+
187+
void addSelectArtifactAction(PromptBuilder promptBuilder) throws IOException {
188+
promptBuilder
189+
.createListPrompt()
190+
.name("action")
191+
.message("What to do:")
192+
.newItem("install")
193+
.text("Install artifact")
194+
.add()
195+
.newItem("copy")
196+
.text("Copy artifact")
197+
.add()
198+
.newItem("version")
199+
.text("Select different version")
200+
.add()
201+
.newItem("quit")
202+
.text("Quit")
203+
.add()
204+
.addPrompt();
205+
}
206+
207+
String selectFinalAction(ConsolePrompt prompt) throws IOException {
208+
PromptBuilder promptBuilder = prompt.getPromptBuilder();
209+
promptBuilder
210+
.createListPrompt()
211+
.name("action")
212+
.message("What to do:")
213+
.newItem("again")
214+
.text("Search again")
215+
.add()
216+
.newItem("quit")
217+
.text("Quit")
218+
.add()
219+
.addPrompt();
220+
Map<String, PromptResultItemIF> result = prompt.prompt(promptBuilder.build());
221+
return getSelectedId(result, "action");
222+
}
223+
224+
private static String getSelectedId(
225+
Map<String, PromptResultItemIF> result, String itemName) {
226+
return ((ListResult) result.get(itemName)).getSelectedId();
227+
}
101228
}
102229

103230
@Command(

0 commit comments

Comments
 (0)