diff --git a/picocli-shell-jline3/src/main/java/picocli/shell/jline3/PicocliCommands.java b/picocli-shell-jline3/src/main/java/picocli/shell/jline3/PicocliCommands.java index 09875c9b4..8802b7617 100644 --- a/picocli-shell-jline3/src/main/java/picocli/shell/jline3/PicocliCommands.java +++ b/picocli-shell-jline3/src/main/java/picocli/shell/jline3/PicocliCommands.java @@ -29,6 +29,7 @@ import picocli.CommandLine; import picocli.CommandLine.Command; import picocli.CommandLine.Help; +import picocli.CommandLine.IFactory; import picocli.CommandLine.Model.CommandSpec; import picocli.CommandLine.Model.OptionSpec; @@ -44,6 +45,18 @@ public class PicocliCommands implements CommandRegistry { /** * Command that clears the screen. + *

+ * WARNING: This subcommand needs a JLine {@code LineReaderImpl} to clear the screen. + * To accomplish this, construct the {@code CommandLine} with a {@code PicocliCommandsFactory}, + * and set the {@code LineReaderImpl} on that factory. For example: + *

+     * PicocliCommandsFactory factory = new PicocliCommandsFactory();
+     * CommandLine cmd = new CommandLine(new MyApp(), factory);
+     * LineReaderImpl readerImpl = ... // create reader
+     * factory.setLineReader(readerImpl);
+     * 
+ * + * @since 4.6 */ @Command(name = "cls", aliases = "clear", mixinStandardHelpOptions = true, description = "Clears the screen", version = "1.0") @@ -51,25 +64,62 @@ public static class ClearScreen implements Callable { private final LineReaderImpl reader; - ClearScreen(LineReader reader) { this.reader = (LineReaderImpl) reader; } + ClearScreen(LineReaderImpl reader) { this.reader = reader; } public Void call() throws IOException { if (reader != null) { reader.clearScreen(); } return null; } } - + + /** + * Command factory that is necessary for applications that want the use the {@code ClearScreen} subcommand. + * It allows chaining (or delegrating) to a custom factory. + *

+ * WARNING: If the application uses the {@code ClearScreen} subcommand, construct the {@code CommandLine} + * with a {@code PicocliCommandsFactory}, and set the {@code LineReaderImpl} on that factory. Applications need + * to call the setLineReader method with a {@code LineReaderImpl}; this will be passed to the {@code ClearScreen} + * subcommand. + * + * For example: + *

+     * PicocliCommandsFactory factory = new PicocliCommandsFactory();
+     * CommandLine cmd = new CommandLine(new MyApp(), factory);
+     * LineReaderImpl readerImpl = ... // create reader
+     * factory.setLineReader(readerImpl);
+     * 
+ * + * Custom factories can be chained by passing them in to the constructor like this: + *
+     * MyCustomFactory customFactory = createCustomFactory(); // your application custom factory
+     * PicocliCommandsFactory factory = new PicocliCommandsFactory(customFactory); // chain the factories
+     * 
+ * + * @since 4.6 + */ public static class PicocliCommandsFactory implements CommandLine.IFactory { - private LineReader reader; - + private CommandLine.IFactory nextFactory; + private LineReaderImpl reader; + + public PicocliCommandsFactory() { + // nextFactory and line reader are null + } + + public PicocliCommandsFactory(IFactory nextFactory) { + this.nextFactory = nextFactory; + // nextFactory is set (but may be null) and line reader is null + } + @SuppressWarnings("unchecked") public K create(Class clazz) throws Exception { if (ClearScreen.class == clazz) { return (K) new ClearScreen(reader); } + if (nextFactory != null) { return nextFactory.create(clazz); } return CommandLine.defaultFactory().create(clazz); } - public void setLineReader(LineReader reader) { + public void setLineReader(LineReaderImpl reader) { this.reader = reader; + // reader may be null, so check before using it in ClearScreen command } } diff --git a/picocli-shell-jline3/src/test/java/picocli/shell/jline3/example/Example.java b/picocli-shell-jline3/src/test/java/picocli/shell/jline3/example/Example.java index 310418a20..761aee19e 100644 --- a/picocli-shell-jline3/src/test/java/picocli/shell/jline3/example/Example.java +++ b/picocli-shell-jline3/src/test/java/picocli/shell/jline3/example/Example.java @@ -137,12 +137,12 @@ public static void main(String[] args) { builtins.alias("bindkey", "keymap"); // set up picocli commands CliCommands commands = new CliCommands(); - + PicocliCommandsFactory factory = new PicocliCommandsFactory(); // Or, if you have your own factory, you can chain them like this: // MyCustomFactory customFactory = createCustomFactory(); // your application custom factory - // PicocliCommands.Factory factory = new PicocliCommands.Factory(customFactory); // chain the factories - + // PicocliCommandsFactory factory = new PicocliCommandsFactory(customFactory); // chain the factories + CommandLine cmd = new CommandLine(commands, factory); PicocliCommands picocliCommands = new PicocliCommands(Example::workDir, cmd); @@ -159,7 +159,7 @@ public static void main(String[] args) { .variable(LineReader.LIST_MAX, 50) // max tab completion candidates .build(); builtins.setLineReader(reader); - factory.setLineReader(reader); + factory.setLineReader((LineReaderImpl) reader); TailTipWidgets widgets = new TailTipWidgets(reader, systemRegistry::commandDescription, 5, TailTipWidgets.TipType.COMPLETER); widgets.enable(); KeyMap keyMap = reader.getKeyMaps().get("main");