-
Notifications
You must be signed in to change notification settings - Fork 418
Plans and Notes for Future Articles
Below is a TODO list of picocli articles to write to make the world a better place.
Shows how to use the picocli programmatic API to avoid reflection. When using the annotations API, picocli creates a model automatically, but it uses reflection to do so. Some applications want to avoid using reflection, and some developers don’t like the "magic" of annotation-based APIs.
Applications can use the picocli programmatic API to create a model manually. At a very basic level, this allows programmers to do everything they were able to do with other libraries that offer a programmatic API (without annotations) for command line applications, like Apache Commons CLI and Argparse4j.
This retains all the benefits picocli offers:
-
Convenient: parse user input and run your business logic with one line of code
-
Strongly typed everything - command line options as well as positional parameters
-
POSIX clustered short options (
<command> -xvfInputFile
as well as<command> -x -v -f InputFile
) -
Fine-grained control: an arity model that allows a minimum, maximum and variable number of parameters, e.g,
"1..*"
,"3..5"
-
Subcommands (can be nested to arbitrary depth)
-
Feature-rich: composable arg groups, splitting quoted args, repeatable subcommands, and many more
-
User-friendly: usage help message uses colors to contrast important elements like option names from the rest of the usage help to reduce the cognitive load on the user
-
Distribute your app as a GraalVM native image
-
Works with Java 5 and higher
Features from version 0.9.8 to 4.4, what was hard, what was easy. Mistakes, things we’re happy with.
Putting Java back on the map for command line applications.
Key point: Java devs want to be able to create a single executable.
Most libraries support only a certain programming style.
For example, Commons CLI, JOpt Simple and Argparse4j offer a programmatic API, while other libraries like JCommander, Args4J and Airline offer an annotations API. Some of these capture options in annotated fields, others use annotated methods. Perhaps you like JewelCLI for its annotated interface methods, or Tomitribe CREST's JAX-RS-like @Command
-annotated methods.
Picocli supports all of these styles.
Things possible with CliBuilder since Groovy 2.5.
-
colored usage help
-
autocompletion (try the built-in
AutoComplete.GenerateCompletion
subcommand) -
control over usage help message
-
@-files
-
PropertiesDefaultProvider
Create CLI apps that give your users more flexibility on whether command line args override the configuration or the other way around. See also prior art:
-
https://spotbugs.readthedocs.io/en/stable/running.html (the -userPrefs option)
Programmatic API makes it possible to apply picocli in a wide range of applications. One example is that it’s easy to build DSLs (Domain Specific Languages) like Groovy CliBuilder, or use picocli in applications where command line options are defined dynamically. 3.0 also adds support for reuse via Mixins, gives fine-grained control over STDOUT vs STDERR and exit codes. The programmatic API also facilitates the use of picocli in JVM languages where annotations and reflection are not supported or not convenient.
Lessons learned while migrating the Groovy command line tools from Commons CLI to picocli.
-
(partially done) https://docs.micronaut.io/snapshot/guide/index.html#commandLineApps
Java command line applications can now have TAB autocompletion when running on Bash. Now that Windows 10 natively supports Bash, that means pretty much anywhere! This is how you do it…
If you’re creating Kotlin command line applications, you may want to consider using this small library to parse command line options and output a smart-looking usage help message with ANSI colors and styles. Bonus: command line TAB autocompletion.
If you’re creating Scala command line applications, you may want to consider using this small library to parse command line options and output a smart-looking usage help message with ANSI colors and styles. Bonus: command line TAB autocompletion.
Walk through the steps of building a command line application with subcommands. Demonstrates some of the things that picocli makes easy, and some of the things that you need to be aware of. Emphasize the convenience methods and error handling. For example, how to have global options apply to all subcommands. (Related: https://github.com/remkop/picocli/issues/247, https://github.com/remkop/picocli/issues/248, https://github.com/remkop/picocli/issues/175)
How do you write unit tests for command line applications built with picocli?
Article on the idea to provide options with a similar prefix that give users different options for authenticating. See discussion in https://github.com/remkop/picocli/issues/176
Good timing for this article may be after https://github.com/remkop/picocli/issues/82 is implemented. We could suggest this in the user manual section on that feature as well.
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.concurrent.Callable;
import java.util.stream.Stream;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.reverseOrder;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;
@Command(name = "sort", mixinStandardHelpOptions = true, version = "sort 1.0",
description = "Sort input lines and write to standard output.")
public class Sort implements Callable<Void> {
@Parameters(description = "The path to read lines from")
Path path;
@Option(names = {"-u", "--unique"}, description = "Output only distinct values")
boolean unique;
@Option(names = {"-c", "--count"}, description = "Prefix values by the number of occurrences")
boolean count;
@Option(names = {"-r", "--reverse"}, description = "Reverse sort order")
boolean reverse;
@Option(names = {"-s", "--skip"}, paramLabel = "NUM", description = "Skip the first NUM values")
long skip;
@Override
public Void call() throws Exception {
Stream<String> sorted = Files.lines(path).skip(skip).sorted(
reverse ? reverseOrder() : naturalOrder());
if (unique) {
String format = count ? "%d\t%s%n" : "%2$s%n";
sorted.collect(groupingBy(identity(), LinkedHashMap::new, counting()))
.forEach((str, num) -> { System.out.printf(format, num, str); });
} else {
sorted.forEach(System.out::println);
}
return null;
}
public static void main(String[] args) throws Exception {
CommandLine.call(new Sort(), args);
}
}