Skip to content

Commit 50e2529

Browse files
committed
refactor: upgraded to latest version of jline3
1 parent 9818363 commit 50e2529

File tree

3 files changed

+194
-58
lines changed

3 files changed

+194
-58
lines changed

README.md

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,85 @@ It takes inspiration from Node's npm but is more focused on managing dependencie
77
is _not_ a build tool. Keep using Maven and Gradle for that. This tool is ideal for those who want to compile and
88
run Java code directly without making their lives difficult the moment they want to start using dependencies.
99

10-
## Installation
10+
## Example
11+
12+
**TL;DR**
13+
14+
```shell
15+
$ jpm install com.github.lalyos:jfiglet:0.0.9
16+
Artifacts new: 1, updated: 0, deleted: 0
17+
$ javac -cp deps/* HelloWorld.java
18+
_ _ _ _ __ __ _ _ _
19+
| | | | ___| | | ___ \ \ / /__ _ __| | __| | |
20+
| |_| |/ _ \ | |/ _ \ \ \ /\ / / _ \| '__| |/ _` | |
21+
| _ | __/ | | (_) | \ V V / (_) | | | | (_| |_|
22+
|_| |_|\___|_|_|\___( ) \_/\_/ \___/|_| |_|\__,_(_)
23+
```
24+
25+
**Slightly longer explanation:**
26+
27+
Imagine you're writing a simple Java console command, and you want to use JFigletFont for some more
28+
impactful visuals. You've written the following code:
29+
30+
```java
31+
import com.github.lalyos.jfiglet.FigletFont;
32+
33+
public class HelloWorld {
34+
public static void main(String[] args) throws Exception {
35+
System.out.println(FigletFont.convertOneLine("Hello, World!"));
36+
}
37+
}
38+
```
39+
40+
But now you get to the point that you need to add the JFigletFont library to your project. You could start
41+
using Maven or Gradle, but that seems overkill for such a simple project. Instead, you can use `jpm`.
42+
First you can search for the library if you can't remember the exact name and version:
43+
44+
```shell
45+
$ jpm search jfiglet
46+
com.github.dtmo.jfiglet:jfiglet:1.0.1
47+
com.github.lalyos:jfiglet:0.0.9
48+
```
49+
50+
So let's install the second library from that list:
51+
52+
```shell
53+
$ jpm install com.github.lalyos:jfiglet:0.0.9
54+
Artifacts new: 1, updated: 0, deleted: 0
55+
```
56+
57+
Let's see what that did:
58+
59+
```shell
60+
$ tree
61+
.
62+
├── app.json
63+
├── deps
64+
│   └── jfiglet-0.0.9.jar -> /home/user/.m2/repository/com/github/lalyos/jfiglet/0.0.9/jfiglet-0.0.9.jar
65+
└── HelloWorld.java
66+
```
67+
68+
As you can see `jpm` has created a `deps` directory and copied the JFigletFont library there
69+
(_although in fact it didn't actually copy the library itself, but instead it created a symbolic link to save space_).
70+
71+
We can now simply run the program like this (using Java 11 or newer):
72+
73+
```shell
74+
$ javac -cp deps/* HelloWorld.java
75+
_ _ _ _ __ __ _ _ _
76+
| | | | ___| | | ___ \ \ / /__ _ __| | __| | |
77+
| |_| |/ _ \ | |/ _ \ \ \ /\ / / _ \| '__| |/ _` | |
78+
| _ | __/ | | (_) | \ V V / (_) | | | | (_| |_|
79+
|_| |_|\___|_|_|\___( ) \_/\_/ \___/|_| |_|\__,_(_)
80+
```
81+
82+
But if we look again at the above output of `tree`, we also see an `app.json` file.
83+
This file is used by `jpm` to keep track of the dependencies of your project. If you want to share your project
84+
with someone else, you can simply share the `app.json` file along with the code, and they can run `jpm install`
85+
to get the required dependencies to run the code.
86+
87+
_NB: We could have used `jpm copy` instead of `jpm install` to copy the dependencies but that would not have created
88+
the `app.json` file._
1189
1290
### JBang
1391
@@ -22,7 +100,47 @@ jbang app install jpm@codejive
22100
See:
23101
24102
```shell
25-
jpm --help
103+
$ jpm --help
104+
Usage: jpm [-hV] [COMMAND]
105+
Simple command line tool for managing Maven artifacts
106+
-h, --help Show this help message and exit.
107+
-V, --version Print version information and exit.
108+
Commands:
109+
copy, c Resolves one or more artifacts and copies them and all their
110+
dependencies to a target directory. By default jpm will try to
111+
create symbolic links to conserve space.
112+
113+
Example:
114+
jpm copy org.apache.httpcomponents:httpclient:4.5.14
115+
116+
search, s Without arguments this command will start an interactive search
117+
asking the user to provide details of the artifact to look for
118+
and the actions to take. When provided with an argument this
119+
command finds and returns the names of those artifacts that
120+
match the given (partial) name.
121+
122+
Example:
123+
jpm search httpclient
124+
125+
install, i This adds the given artifacts to the list of dependencies
126+
available in the app.json file. It then behaves just like 'copy
127+
--sync' and copies all artifacts in that list and all their
128+
dependencies to the target directory while at the same time
129+
removing any artifacts that are no longer needed (ie the ones
130+
that are not mentioned in the app.json file). If no artifacts
131+
are passed the app.json file will be left untouched and only
132+
the existing dependencies in the file will be copied.
133+
134+
Example:
135+
jpm install org.apache.httpcomponents:httpclient:4.5.14
136+
137+
path, p Resolves one or more artifacts and prints the full classpath to
138+
standard output. If no artifacts are passed the classpath for
139+
the dependencies defined in the app.json file will be printed
140+
instead.
141+
142+
Example:
143+
jpm path org.apache.httpcomponents:httpclient:4.5.14
26144
```
27145
28146
## Development

pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@
2929

3030
<properties>
3131
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
32-
<maven.compiler.source>8</maven.compiler.source>
33-
<maven.compiler.target>8</maven.compiler.target>
32+
<maven.compiler.source>11</maven.compiler.source>
33+
<maven.compiler.target>11</maven.compiler.target>
3434
<mainClass>org.codejive.jpm.Main</mainClass>
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>
38+
<version.jline>3.29.0</version.jline>
3939
<version.slf4j>2.0.13</version.slf4j>
4040
<version.spotless>2.43.0</version.spotless>
4141
<version.google-java-format>1.22.0</version.google-java-format>

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

Lines changed: 71 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,15 @@
1717
import java.util.stream.Collectors;
1818
import org.codejive.jpm.util.SyncStats;
1919
import org.codejive.jpm.util.Version;
20+
import org.jline.consoleui.elements.InputValue;
21+
import org.jline.consoleui.elements.ListChoice;
22+
import org.jline.consoleui.elements.PageSizeType;
23+
import org.jline.consoleui.elements.PromptableElementIF;
24+
import org.jline.consoleui.elements.items.ListItemIF;
25+
import org.jline.consoleui.elements.items.impl.ListItem;
2026
import org.jline.consoleui.prompt.ConsolePrompt;
2127
import org.jline.consoleui.prompt.ListResult;
2228
import org.jline.consoleui.prompt.PromptResultItemIF;
23-
import org.jline.consoleui.prompt.builder.ListPromptBuilder;
2429
import org.jline.consoleui.prompt.builder.PromptBuilder;
2530
import org.jline.terminal.Terminal;
2631
import org.jline.terminal.TerminalBuilder;
@@ -81,7 +86,10 @@ public Integer call() throws Exception {
8186
name = "search",
8287
aliases = {"s"},
8388
description =
84-
"Finds and returns the names of those artifacts that match the given (partial) name.\n\n"
89+
"Without arguments this command will start an interactive search asking the user to "
90+
+ "provide details of the artifact to look for and the actions to take. When provided "
91+
+ "with an argument this command finds and returns the names of those artifacts that "
92+
+ "match the given (partial) name.\n\n"
8593
+ "Example:\n jpm search httpclient\n")
8694
static class Search implements Callable<Integer> {
8795
@Mixin QuietMixin quietMixin;
@@ -112,16 +120,13 @@ public Integer call() throws Exception {
112120
}
113121
try (Terminal terminal = TerminalBuilder.builder().build()) {
114122
while (true) {
115-
ConsolePrompt prompt = new ConsolePrompt(terminal);
116-
if (artifactPattern == null || artifactPattern.isEmpty()) {
117-
artifactPattern = askString(prompt, "Search for:");
123+
ConsolePrompt.UiConfig cfg = new ConsolePrompt.UiConfig();
124+
cfg.setCancellableFirstPrompt(true);
125+
ConsolePrompt prompt = new ConsolePrompt(null, terminal, cfg);
126+
Map<String, PromptResultItemIF> result = prompt.prompt(this::nextQuestion);
127+
if (result.isEmpty()) {
128+
break;
118129
}
119-
String[] artifactNames = search(artifactPattern);
120-
PromptBuilder promptBuilder = prompt.getPromptBuilder();
121-
addSelectItem(promptBuilder, "Select artifact:", artifactNames);
122-
addSelectArtifactAction(promptBuilder);
123-
Map<String, PromptResultItemIF> result =
124-
prompt.prompt(promptBuilder.build());
125130
String selectedArtifact = getSelectedId(result, "item");
126131
String artifactAction = getSelectedId(result, "action");
127132
if ("install".equals(artifactAction)) {
@@ -144,9 +149,6 @@ public Integer call() throws Exception {
144149
if (!quietMixin.quiet) {
145150
printStats(stats);
146151
}
147-
} else if ("version".equals(artifactAction)) {
148-
artifactPattern = selectedArtifact;
149-
continue;
150152
} else { // quit
151153
break;
152154
}
@@ -169,57 +171,73 @@ public Integer call() throws Exception {
169171
return (Integer) 0;
170172
}
171173

172-
String[] search(String artifactPattern) throws IOException {
173-
return Jpm.builder()
174-
.directory(copyMixin.directory)
175-
.noLinks(copyMixin.noLinks)
176-
.build()
177-
.search(artifactPattern, Math.min(max, 200));
174+
String[] search(String artifactPattern) {
175+
try {
176+
return Jpm.builder()
177+
.directory(copyMixin.directory)
178+
.noLinks(copyMixin.noLinks)
179+
.build()
180+
.search(artifactPattern, Math.min(max, 200));
181+
} catch (IOException e) {
182+
throw new UncheckedIOException(e);
183+
}
178184
}
179185

180-
String askString(ConsolePrompt prompt, String message) throws IOException {
181-
PromptBuilder promptBuilder = prompt.getPromptBuilder();
182-
promptBuilder.createInputPrompt().name("input").message(message).addPrompt();
183-
Map<String, PromptResultItemIF> result = prompt.prompt(promptBuilder.build());
184-
return result.get("input").getResult();
185-
}
186+
List<PromptableElementIF> nextQuestion(Map<String, PromptResultItemIF> results) {
187+
String pattern;
188+
if (artifactPattern == null || artifactPattern.isEmpty()) {
189+
if (!results.containsKey("input")) {
190+
return List.of(stringElement("Search for:"));
191+
}
192+
pattern = results.get("input").getResult();
193+
} else {
194+
pattern = artifactPattern;
195+
}
196+
197+
if (!results.containsKey("item")) {
198+
String[] artifactNames = search(pattern);
199+
return List.of(selectElement("Select artifact:", artifactNames));
200+
}
186201

187-
void addSelectItem(PromptBuilder promptBuilder, String message, String[] items)
188-
throws IOException {
189-
ListPromptBuilder artifactsList =
190-
promptBuilder.createListPrompt().name("item").message(message).pageSize(10);
191-
for (String artifactName : items) {
192-
artifactsList.newItem(artifactName).text(artifactName).add();
202+
if (!results.containsKey("action")) {
203+
return List.of(selectArtifactActionElement());
204+
} else if ("version".equals(getSelectedId(results, "action"))) {
205+
results.remove("action");
206+
pattern = getSelectedId(results, "item");
207+
String[] artifactNames = search(pattern);
208+
return List.of(selectElement("Select version:", artifactNames));
193209
}
194-
artifactsList.addPrompt();
210+
211+
return null;
195212
}
196213

197-
void addSelectArtifactAction(PromptBuilder promptBuilder) throws IOException {
198-
promptBuilder
199-
.createListPrompt()
200-
.name("action")
201-
.message("What to do:")
202-
.newItem("install")
203-
.text("Download & Install artifact")
204-
.add()
205-
.newItem("copy")
206-
.text("Download & Copy artifact")
207-
.add()
208-
.newItem("version")
209-
.text("Select different version")
210-
.add()
211-
.newItem("quit")
212-
.text("Quit")
213-
.add()
214-
.addPrompt();
214+
InputValue stringElement(String message) {
215+
return new InputValue("input", message);
216+
}
217+
218+
ListChoice selectElement(String message, String[] items) {
219+
List<ListItemIF> itemList =
220+
Arrays.stream(items)
221+
.map(it -> new ListItem(it, it))
222+
.collect(Collectors.toList());
223+
return new ListChoice(message, "item", 10, PageSizeType.ABSOLUTE, itemList);
224+
}
225+
226+
ListChoice selectArtifactActionElement() {
227+
List<ListItemIF> itemList = new ArrayList<>();
228+
itemList.add(new ListItem("Download & Install artifact", "install"));
229+
itemList.add(new ListItem("Download & Copy artifact", "copy"));
230+
itemList.add(new ListItem("Select different version", "version"));
231+
itemList.add(new ListItem("Quit", "quit"));
232+
return new ListChoice("What to do:", "action", 10, PageSizeType.ABSOLUTE, itemList);
215233
}
216234

217235
String selectFinalAction(ConsolePrompt prompt) throws IOException {
218236
PromptBuilder promptBuilder = prompt.getPromptBuilder();
219237
promptBuilder
220238
.createListPrompt()
221239
.name("action")
222-
.message("What to do:")
240+
.message("Next step:")
223241
.newItem("again")
224242
.text("Search again")
225243
.add()
@@ -242,7 +260,7 @@ private static String getSelectedId(
242260
aliases = {"i"},
243261
description =
244262
"This adds the given artifacts to the list of dependencies available in the app.json file. "
245-
+ "It then behaves just like 'sync' and copies all artifacts in that list and all their dependencies to the target directory while at the same time removing any artifacts that are no longer needed (ie the ones that are not mentioned in the app.json file)."
263+
+ "It then behaves just like 'copy --sync' and copies all artifacts in that list and all their dependencies to the target directory while at the same time removing any artifacts that are no longer needed (ie the ones that are not mentioned in the app.json file). "
246264
+ "If no artifacts are passed the app.json file will be left untouched and only the existing dependencies in the file will be copied.\n\n"
247265
+ "Example:\n jpm install org.apache.httpcomponents:httpclient:4.5.14\n")
248266
static class Install implements Callable<Integer> {

0 commit comments

Comments
 (0)