From faadeda4724f51defc52dd3b1a6901e393f05e27 Mon Sep 17 00:00:00 2001 From: Sualeh Fatehi Date: Mon, 23 Nov 2020 16:05:39 -0500 Subject: [PATCH 1/7] Allow a built-in clear command to be included with the rest of the picocli commands --- .../picocli/shell/jline3/PicocliCommands.java | 52 ++++++++++++++++--- .../picocli/shell/jline3/example/Example.java | 25 +-------- 2 files changed, 46 insertions(+), 31 deletions(-) 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 f5c7d3575..8ab32af71 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 @@ -1,7 +1,15 @@ package picocli.shell.jline3; +import java.io.IOException; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -13,19 +21,16 @@ import org.jline.reader.Completer; import org.jline.reader.LineReader; import org.jline.reader.ParsedLine; +import org.jline.reader.impl.LineReaderImpl; import org.jline.reader.impl.completer.SystemCompleter; import org.jline.utils.AttributedString; + import picocli.CommandLine; +import picocli.CommandLine.Command; import picocli.CommandLine.Help; import picocli.CommandLine.Model.CommandSpec; import picocli.CommandLine.Model.OptionSpec; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - /** * Compiles SystemCompleter for command completion and implements a method commandDescription() that provides command descriptions * for JLine TailTipWidgets to be displayed in terminal status bar. @@ -35,6 +40,26 @@ * @since 4.1.2 */ public class PicocliCommands implements CommandRegistry { + + /** + * Command that clears the screen. + */ + @Command(name = "cls", aliases = "clear", mixinStandardHelpOptions = true, + description = "Clears the screen", version = "1.0") + static class ClearScreen implements Callable { + + private final LineReaderImpl reader; + + ClearScreen(LineReaderImpl reader) { + this.reader = reader; + } + + public Void call() throws IOException { + reader.clearScreen(); + return null; + } + } + private final Supplier workDir; private final CommandLine cmd; private final Set commands; @@ -47,7 +72,7 @@ public PicocliCommands(Path workDir, CommandLine cmd) { public PicocliCommands(Supplier workDir, CommandLine cmd) { this.workDir = workDir; this.cmd = cmd; - commands = cmd.getCommandSpec().subcommands().keySet(); + commands = new HashSet<>(cmd.getCommandSpec().subcommands().keySet()); for (String c: commands) { for (String a: cmd.getSubcommands().get(c).getCommandSpec().aliases()) { aliasCommand.put(a, c); @@ -55,6 +80,17 @@ public PicocliCommands(Supplier workDir, CommandLine cmd) { } } + public void includeClearScreenCommand(LineReader reader) { + if (reader == null) return; + ClearScreen clearScreen = new ClearScreen((LineReaderImpl) reader); + cmd.addSubcommand(clearScreen); + + commands.add("clear"); + + aliasCommand.put("clear", "clear"); + aliasCommand.put("cls", "clear"); + } + /** * * @param 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 6ed8cc052..d334bd000 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 @@ -42,18 +42,12 @@ public class Example { ""}, footer = {"", "Press Ctl-D to exit."}, subcommands = { - MyCommand.class, ClearScreen.class, CommandLine.HelpCommand.class}) + MyCommand.class, CommandLine.HelpCommand.class}) static class CliCommands implements Runnable { - LineReaderImpl reader; PrintWriter out; CliCommands() {} - public void setReader(LineReader reader){ - this.reader = (LineReaderImpl)reader; - out = reader.getTerminal().writer(); - } - public void run() { out.println(new CommandLine(this).getUsageMessage()); } @@ -128,21 +122,6 @@ public void subtract(@Option(names = {"-l", "--left"}, required = true) int left } } - /** - * Command that clears the screen. - */ - @Command(name = "cls", aliases = "clear", mixinStandardHelpOptions = true, - description = "Clears the screen", version = "1.0") - static class ClearScreen implements Callable { - - @ParentCommand CliCommands parent; - - public Void call() throws IOException { - parent.reader.clearScreen(); - return null; - } - } - private static Path workDir() { return Paths.get(System.getProperty("user.dir")); } @@ -172,7 +151,7 @@ public static void main(String[] args) { .variable(LineReader.LIST_MAX, 50) // max tab completion candidates .build(); builtins.setLineReader(reader); - commands.setReader(reader); + picocliCommands.includeClearScreenCommand(reader); TailTipWidgets widgets = new TailTipWidgets(reader, systemRegistry::commandDescription, 5, TailTipWidgets.TipType.COMPLETER); widgets.enable(); KeyMap keyMap = reader.getKeyMaps().get("main"); From ad726b5d08b0814f39287f62a25721d8e744860c Mon Sep 17 00:00:00 2001 From: Sualeh Fatehi Date: Mon, 23 Nov 2020 16:20:04 -0500 Subject: [PATCH 2/7] Allow jline help to be overridden by picocli help, which is much nicer --- .../src/main/java/picocli/shell/jline3/PicocliCommands.java | 6 ++++-- .../src/test/java/picocli/shell/jline3/example/Example.java | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) 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 8ab32af71..ed321f8d2 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 @@ -22,6 +22,8 @@ import org.jline.reader.LineReader; import org.jline.reader.ParsedLine; import org.jline.reader.impl.LineReaderImpl; +import org.jline.reader.impl.completer.ArgumentCompleter; +import org.jline.reader.impl.completer.NullCompleter; import org.jline.reader.impl.completer.SystemCompleter; import org.jline.utils.AttributedString; @@ -110,9 +112,9 @@ public SystemCompleter compileCompleters() { return out; } - private class PicocliCompleter implements Completer { + private class PicocliCompleter extends ArgumentCompleter implements Completer { - public PicocliCompleter() {} + public PicocliCompleter() { super(NullCompleter.INSTANCE); } @Override public void complete(LineReader reader, ParsedLine commandLine, List candidates) { 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 d334bd000..1fcdd71c2 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 @@ -143,6 +143,7 @@ public static void main(String[] args) { try (Terminal terminal = TerminalBuilder.builder().build()) { SystemRegistry systemRegistry = new SystemRegistryImpl(parser, terminal, Example::workDir, null); systemRegistry.setCommandRegistries(builtins, picocliCommands); + systemRegistry.register("help", picocliCommands); LineReader reader = LineReaderBuilder.builder() .terminal(terminal) From 42dcee9d2ffdffe7822d10332be98075dd1ed2c3 Mon Sep 17 00:00:00 2001 From: Sualeh Fatehi Date: Thu, 26 Nov 2020 17:13:20 -0500 Subject: [PATCH 3/7] Tabs to spaces --- .../picocli/shell/jline3/PicocliCommands.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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 ed321f8d2..12de09c7a 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 @@ -50,13 +50,13 @@ public class PicocliCommands implements CommandRegistry { description = "Clears the screen", version = "1.0") static class ClearScreen implements Callable { - private final LineReaderImpl reader; + private final LineReaderImpl reader; ClearScreen(LineReaderImpl reader) { - this.reader = reader; - } + this.reader = reader; + } - public Void call() throws IOException { + public Void call() throws IOException { reader.clearScreen(); return null; } @@ -83,14 +83,14 @@ public PicocliCommands(Supplier workDir, CommandLine cmd) { } public void includeClearScreenCommand(LineReader reader) { - if (reader == null) return; - ClearScreen clearScreen = new ClearScreen((LineReaderImpl) reader); - cmd.addSubcommand(clearScreen); + if (reader == null) return; + ClearScreen clearScreen = new ClearScreen((LineReaderImpl) reader); + cmd.addSubcommand(clearScreen); - commands.add("clear"); + commands.add("clear"); - aliasCommand.put("clear", "clear"); - aliasCommand.put("cls", "clear"); + aliasCommand.put("clear", "clear"); + aliasCommand.put("cls", "clear"); } /** From c026772f577bc7e6a994909ed10e6b490f8b34a5 Mon Sep 17 00:00:00 2001 From: Sualeh Fatehi Date: Mon, 30 Nov 2020 19:12:21 -0500 Subject: [PATCH 4/7] Include ClearScreen command declaratively --- .../picocli/shell/jline3/PicocliCommands.java | 36 +++++++++---------- .../picocli/shell/jline3/example/Example.java | 13 +++++-- 2 files changed, 28 insertions(+), 21 deletions(-) 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 12de09c7a..09875c9b4 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 @@ -5,7 +5,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -48,19 +47,31 @@ public class PicocliCommands implements CommandRegistry { */ @Command(name = "cls", aliases = "clear", mixinStandardHelpOptions = true, description = "Clears the screen", version = "1.0") - static class ClearScreen implements Callable { + public static class ClearScreen implements Callable { private final LineReaderImpl reader; - ClearScreen(LineReaderImpl reader) { - this.reader = reader; - } + ClearScreen(LineReader reader) { this.reader = (LineReaderImpl) reader; } public Void call() throws IOException { - reader.clearScreen(); + if (reader != null) { reader.clearScreen(); } return null; } } + + public static class PicocliCommandsFactory implements CommandLine.IFactory { + private LineReader reader; + + @SuppressWarnings("unchecked") + public K create(Class clazz) throws Exception { + if (ClearScreen.class == clazz) { return (K) new ClearScreen(reader); } + return CommandLine.defaultFactory().create(clazz); + } + + public void setLineReader(LineReader reader) { + this.reader = reader; + } + } private final Supplier workDir; private final CommandLine cmd; @@ -74,7 +85,7 @@ public PicocliCommands(Path workDir, CommandLine cmd) { public PicocliCommands(Supplier workDir, CommandLine cmd) { this.workDir = workDir; this.cmd = cmd; - commands = new HashSet<>(cmd.getCommandSpec().subcommands().keySet()); + commands = cmd.getCommandSpec().subcommands().keySet(); for (String c: commands) { for (String a: cmd.getSubcommands().get(c).getCommandSpec().aliases()) { aliasCommand.put(a, c); @@ -82,17 +93,6 @@ public PicocliCommands(Supplier workDir, CommandLine cmd) { } } - public void includeClearScreenCommand(LineReader reader) { - if (reader == null) return; - ClearScreen clearScreen = new ClearScreen((LineReaderImpl) reader); - cmd.addSubcommand(clearScreen); - - commands.add("clear"); - - aliasCommand.put("clear", "clear"); - aliasCommand.put("cls", "clear"); - } - /** * * @param 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 1fcdd71c2..310418a20 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 @@ -17,6 +17,7 @@ import picocli.CommandLine.Option; import picocli.CommandLine.ParentCommand; import picocli.shell.jline3.PicocliCommands; +import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory; import java.io.IOException; import java.io.PrintWriter; @@ -42,7 +43,7 @@ public class Example { ""}, footer = {"", "Press Ctl-D to exit."}, subcommands = { - MyCommand.class, CommandLine.HelpCommand.class}) + MyCommand.class, PicocliCommands.ClearScreen.class, CommandLine.HelpCommand.class}) static class CliCommands implements Runnable { PrintWriter out; @@ -136,7 +137,13 @@ public static void main(String[] args) { builtins.alias("bindkey", "keymap"); // set up picocli commands CliCommands commands = new CliCommands(); - CommandLine cmd = new CommandLine(commands); + + 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 + + CommandLine cmd = new CommandLine(commands, factory); PicocliCommands picocliCommands = new PicocliCommands(Example::workDir, cmd); Parser parser = new DefaultParser(); @@ -152,7 +159,7 @@ public static void main(String[] args) { .variable(LineReader.LIST_MAX, 50) // max tab completion candidates .build(); builtins.setLineReader(reader); - picocliCommands.includeClearScreenCommand(reader); + factory.setLineReader(reader); TailTipWidgets widgets = new TailTipWidgets(reader, systemRegistry::commandDescription, 5, TailTipWidgets.TipType.COMPLETER); widgets.enable(); KeyMap keyMap = reader.getKeyMaps().get("main"); From 7db506291a4a096b7940cc4f3bc94fcb3fcbea9f Mon Sep 17 00:00:00 2001 From: Sualeh Fatehi Date: Tue, 1 Dec 2020 17:11:48 -0500 Subject: [PATCH 5/7] 1. Add javadocs and other comments 2. Use LineReaderImpl instead of LineReader, and have the client/ caller do the cast 3. Add a constructor to allow factories to be chained --- .../picocli/shell/jline3/PicocliCommands.java | 60 +++++++++++++++++-- .../picocli/shell/jline3/example/Example.java | 8 +-- 2 files changed, 59 insertions(+), 9 deletions(-) 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"); From a0db671fbf065c088c95bdc93de1a274a31ee15e Mon Sep 17 00:00:00 2001 From: Sualeh Fatehi Date: Tue, 1 Dec 2020 17:56:28 -0500 Subject: [PATCH 6/7] Remove workDir from PicocliCommands --- .../java/picocli/shell/jline3/PicocliCommands.java | 10 +--------- .../java/picocli/shell/jline3/example/Example.java | 14 +++++--------- 2 files changed, 6 insertions(+), 18 deletions(-) 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 8802b7617..bec403f58 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 @@ -1,7 +1,6 @@ package picocli.shell.jline3; import java.io.IOException; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -9,7 +8,6 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; -import java.util.function.Supplier; import java.util.stream.Collectors; import org.jline.builtins.Options.HelpException; @@ -123,17 +121,11 @@ public void setLineReader(LineReaderImpl reader) { } } - private final Supplier workDir; private final CommandLine cmd; private final Set commands; private final Map aliasCommand = new HashMap<>(); - public PicocliCommands(Path workDir, CommandLine cmd) { - this(() -> workDir, cmd); - } - - public PicocliCommands(Supplier workDir, CommandLine cmd) { - this.workDir = workDir; + public PicocliCommands(CommandLine cmd) { this.cmd = cmd; commands = cmd.getCommandSpec().subcommands().keySet(); for (String c: commands) { 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 761aee19e..b41d72d95 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 @@ -19,12 +19,11 @@ import picocli.shell.jline3.PicocliCommands; import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory; -import java.io.IOException; import java.io.PrintWriter; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; /** * Example that demonstrates how to build an interactive shell with JLine3 and picocli. @@ -123,15 +122,12 @@ public void subtract(@Option(names = {"-l", "--left"}, required = true) int left } } - private static Path workDir() { - return Paths.get(System.getProperty("user.dir")); - } - public static void main(String[] args) { AnsiConsole.systemInstall(); try { + Supplier workDir = () -> Paths.get(System.getProperty("user.dir")); // set up JLine built-in commands - Builtins builtins = new Builtins(Example::workDir, null, null); + Builtins builtins = new Builtins(workDir, null, null); builtins.rename(Builtins.Command.TTOP, "top"); builtins.alias("zle", "widget"); builtins.alias("bindkey", "keymap"); @@ -144,11 +140,11 @@ public static void main(String[] args) { // PicocliCommandsFactory factory = new PicocliCommandsFactory(customFactory); // chain the factories CommandLine cmd = new CommandLine(commands, factory); - PicocliCommands picocliCommands = new PicocliCommands(Example::workDir, cmd); + PicocliCommands picocliCommands = new PicocliCommands(cmd); Parser parser = new DefaultParser(); try (Terminal terminal = TerminalBuilder.builder().build()) { - SystemRegistry systemRegistry = new SystemRegistryImpl(parser, terminal, Example::workDir, null); + SystemRegistry systemRegistry = new SystemRegistryImpl(parser, terminal, workDir, null); systemRegistry.setCommandRegistries(builtins, picocliCommands); systemRegistry.register("help", picocliCommands); From a0f35f5e67f16fbb17236daef2cc5241f5ccbea6 Mon Sep 17 00:00:00 2001 From: Sualeh Fatehi Date: Wed, 2 Dec 2020 19:27:26 -0500 Subject: [PATCH 7/7] Use Terminal to clear the screen without redraw, instead of using LineReaderImpl, as suggested in https://github.com/jline/jline3/issues/610 --- .../picocli/shell/jline3/PicocliCommands.java | 41 ++++++++++--------- .../picocli/shell/jline3/example/Example.java | 2 +- 2 files changed, 22 insertions(+), 21 deletions(-) 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 bec403f58..f3aaafa47 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 @@ -18,11 +18,12 @@ import org.jline.reader.Completer; import org.jline.reader.LineReader; import org.jline.reader.ParsedLine; -import org.jline.reader.impl.LineReaderImpl; import org.jline.reader.impl.completer.ArgumentCompleter; import org.jline.reader.impl.completer.NullCompleter; import org.jline.reader.impl.completer.SystemCompleter; +import org.jline.terminal.Terminal; import org.jline.utils.AttributedString; +import org.jline.utils.InfoCmp.Capability; import picocli.CommandLine; import picocli.CommandLine.Command; @@ -44,14 +45,14 @@ public class PicocliCommands implements CommandRegistry { /** * Command that clears the screen. *

- * WARNING: This subcommand needs a JLine {@code LineReaderImpl} to clear the screen. + * WARNING: This subcommand needs a JLine {@code Terminal} 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: + * and set the {@code Terminal} on that factory. For example: *

      * PicocliCommandsFactory factory = new PicocliCommandsFactory();
      * CommandLine cmd = new CommandLine(new MyApp(), factory);
-     * LineReaderImpl readerImpl = ... // create reader
-     * factory.setLineReader(readerImpl);
+     * // create terminal
+     * factory.setTerminal(terminal);
      * 
* * @since 4.6 @@ -60,31 +61,31 @@ public class PicocliCommands implements CommandRegistry { description = "Clears the screen", version = "1.0") public static class ClearScreen implements Callable { - private final LineReaderImpl reader; + private final Terminal terminal; - ClearScreen(LineReaderImpl reader) { this.reader = reader; } + ClearScreen(Terminal terminal) { this.terminal = terminal; } public Void call() throws IOException { - if (reader != null) { reader.clearScreen(); } + if (terminal != null) { terminal.puts(Capability.clear_screen); } 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. + * It allows chaining (or delegating) 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} + * with a {@code PicocliCommandsFactory}, and set the {@code Terminal} on that factory. Applications need + * to call the {@code setTerminal} method with a {@code Terminal}; 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);
+     * // create terminal
+     * factory.setTerminal(terminal);
      * 
* * Custom factories can be chained by passing them in to the constructor like this: @@ -97,27 +98,27 @@ public Void call() throws IOException { */ public static class PicocliCommandsFactory implements CommandLine.IFactory { private CommandLine.IFactory nextFactory; - private LineReaderImpl reader; + private Terminal terminal; public PicocliCommandsFactory() { - // nextFactory and line reader are null + // nextFactory and terminal are null } public PicocliCommandsFactory(IFactory nextFactory) { this.nextFactory = nextFactory; - // nextFactory is set (but may be null) and line reader is null + // nextFactory is set (but may be null) and terminal is null } @SuppressWarnings("unchecked") public K create(Class clazz) throws Exception { - if (ClearScreen.class == clazz) { return (K) new ClearScreen(reader); } + if (ClearScreen.class == clazz) { return (K) new ClearScreen(terminal); } if (nextFactory != null) { return nextFactory.create(clazz); } return CommandLine.defaultFactory().create(clazz); } - public void setLineReader(LineReaderImpl reader) { - this.reader = reader; - // reader may be null, so check before using it in ClearScreen command + public void setTerminal(Terminal terminal) { + this.terminal = terminal; + // terminal 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 b41d72d95..0ab01f2c8 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 @@ -155,7 +155,7 @@ public static void main(String[] args) { .variable(LineReader.LIST_MAX, 50) // max tab completion candidates .build(); builtins.setLineReader(reader); - factory.setLineReader((LineReaderImpl) reader); + factory.setTerminal(terminal); TailTipWidgets widgets = new TailTipWidgets(reader, systemRegistry::commandDescription, 5, TailTipWidgets.TipType.COMPLETER); widgets.enable(); KeyMap keyMap = reader.getKeyMaps().get("main");