From 31b3ea14c6ee82874d0f2fa48dab982fc476a396 Mon Sep 17 00:00:00 2001 From: mattirn Date: Mon, 3 Feb 2020 19:48:37 +0100 Subject: [PATCH 1/5] Command output redirection to variable --- .../java/org/jline/builtins/Builtins.java | 59 ++--- .../org/jline/builtins/CommandRegistry.java | 57 ++++- .../org/jline/builtins/ConsoleEngine.java | 4 +- .../org/jline/builtins/ConsoleEngineImpl.java | 64 +++-- .../org/jline/builtins/SystemRegistry.java | 60 ++++- .../jline/builtins/SystemRegistryImpl.java | 225 +++++++++++++----- .../test/java/org/jline/example/Example.java | 9 +- demo/src/main/java/org/jline/demo/Repl.java | 6 +- 8 files changed, 342 insertions(+), 142 deletions(-) diff --git a/builtins/src/main/java/org/jline/builtins/Builtins.java b/builtins/src/main/java/org/jline/builtins/Builtins.java index 6dccdc0f9..5a3fa4172 100644 --- a/builtins/src/main/java/org/jline/builtins/Builtins.java +++ b/builtins/src/main/java/org/jline/builtins/Builtins.java @@ -137,6 +137,7 @@ public boolean hasCommand(String name) { return false; } + @Override public SystemCompleter compileCompleters() { SystemCompleter out = new SystemCompleter(); for (Map.Entry entry: commandName.entrySet()) { @@ -163,31 +164,18 @@ private Command command(String name) { } @Override - public Object execute(String command, String[] args) throws Exception { - return execute(command, Arrays.asList(args)); - } - - private Object execute(String command, List args) throws Exception { - execute(command, args, System.in, System.out, System.err); - return null; - } - - public void execute(String command, List args, InputStream in, PrintStream out, PrintStream err) throws Exception { - execute(command, args.toArray(new String[0]), in, out, err); - } - - public void execute(String command, String[] args, InputStream in, PrintStream out, PrintStream err) throws Exception { + public Object execute(CommandRegistry.CommandSession session, String command, String[] args) throws Exception { exception = null; - commandExecute.get(command(command)).execute().accept(new CommandInput(args, in, out, err)); + commandExecute.get(command(command)).execute().accept(new CommandInput(args, session)); if (exception != null) { throw exception; } + return null; } private List commandOptions(String command) { - List args = Arrays.asList("--help"); try { - execute(command, args); + execute(new CommandRegistry.CommandSession(), command, new String[] {"--help"}); } catch (HelpException e) { return compileCommandOptions(e.getMessage()); } catch (Exception e) { @@ -196,13 +184,9 @@ private List commandOptions(String command) { return null; } - private Terminal terminal() { - return reader.getTerminal(); - } - private void less(CommandInput input) { try { - Commands.less(terminal(), input.in(), input.out(), input.err(), workDir.get(), input.args()); + Commands.less(input.terminal(), input.in(), input.out(), input.err(), workDir.get(), input.args()); } catch (Exception e) { this.exception = e; } @@ -210,7 +194,7 @@ private void less(CommandInput input) { private void nano(CommandInput input) { try { - Commands.nano(terminal(), input.out(), input.err(), workDir.get(), input.args(), configPath); + Commands.nano(input.terminal(), input.out(), input.err(), workDir.get(), input.args(), configPath); } catch (Exception e) { this.exception = e; } @@ -266,7 +250,7 @@ private void unsetopt(CommandInput input) { private void ttop(CommandInput input) { try { - TTop.ttop(terminal(), input.out(), input.err(), input.args()); + TTop.ttop(input.terminal(), input.out(), input.err(), input.args()); } catch (Exception e) { this.exception = e; } @@ -502,27 +486,32 @@ public static List compileCommandInfo(String helpMessage) { return out; } - public static class CommandInput{ + public static class CommandInput { String[] args; Object[] xargs; + Terminal terminal; InputStream in; PrintStream out; PrintStream err; - public CommandInput(String[] args) { - this(args, null, null, null); + public CommandInput(String[] args, CommandRegistry.CommandSession session) { + this(args, null, session); } - public CommandInput(Object[] xargs, boolean dumb) { - this.xargs = xargs; - this.args = new String[xargs.length]; - for (int i = 0; i < xargs.length; i++) { - args[i] = xargs[i] != null ? xargs[i].toString() : ""; + public CommandInput(String[] args, Object[] xargs, CommandRegistry.CommandSession session) { + this(args, session.terminal(), session.in(), session.out(), session.err()); + if (xargs != null) { + this.xargs = xargs; + this.args = new String[xargs.length]; + for (int i = 0; i < xargs.length; i++) { + this.args[i] = xargs[i] != null ? xargs[i].toString() : ""; + } } } - public CommandInput(String[] args, InputStream in, PrintStream out, PrintStream err) { + public CommandInput(String[] args, Terminal terminal, InputStream in, PrintStream out, PrintStream err) { this.args = args; + this.terminal = terminal; this.in = in; this.out = out; this.err = err; @@ -536,6 +525,10 @@ public Object[] xargs() { return xargs; } + public Terminal terminal() { + return terminal; + } + public InputStream in() { return in; } diff --git a/builtins/src/main/java/org/jline/builtins/CommandRegistry.java b/builtins/src/main/java/org/jline/builtins/CommandRegistry.java index daa48bb16..c271759de 100644 --- a/builtins/src/main/java/org/jline/builtins/CommandRegistry.java +++ b/builtins/src/main/java/org/jline/builtins/CommandRegistry.java @@ -11,7 +11,10 @@ import org.jline.builtins.Completers; import org.jline.builtins.Widgets; import org.jline.builtins.Options.HelpException; +import org.jline.terminal.Terminal; +import java.io.InputStream; +import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -71,7 +74,7 @@ default String name() { */ default List commandInfo(String command) { try { - invoke(command, new Object[] {"--help"}); + invoke(new CommandSession(), command, new Object[] {"--help"}); } catch (HelpException e) { return Builtins.compileCommandInfo(e.getMessage()); } catch (Exception e) { @@ -103,7 +106,7 @@ default List commandInfo(String command) { */ default Widgets.CmdDesc commandDescription(String command) { try { - invoke(command, new Object[] {"--help"}); + invoke(new CommandSession(), command, new Object[] {"--help"}); } catch (HelpException e) { return Builtins.compileCommandDescription(e.getMessage()); } catch (Exception e) { @@ -115,24 +118,26 @@ default Widgets.CmdDesc commandDescription(String command) { /** * Execute a command that have only string parameters and options. Implementation of the method is required * when aggregating command registries using SystemRegistry. + * @param session * @param command * @param args * @return result * @throws Exception */ - default Object execute(String command, String[] args) throws Exception { + default Object execute(CommandSession session, String command, String[] args) throws Exception { throw new IllegalArgumentException("CommandRegistry method execute(String command, String[] args) is not implemented!"); } /** * Execute a command. If command has other than string parameters a custom implementation is required. * This method will be called only when we have ConsoleEngine in SystemRegistry. + * @param session * @param command * @param args * @return result * @throws Exception */ - default Object invoke(String command, Object... args) throws Exception { + default Object invoke(CommandSession session, String command, Object... args) throws Exception { String[] _args = new String[args.length]; for (int i = 0; i < args.length; i++) { if (!(args[i] instanceof String)) { @@ -140,7 +145,49 @@ default Object invoke(String command, Object... args) throws Exception { } _args[i] = args[i].toString(); } - return execute(command, _args); + return execute(session, command, _args); + } + + public static class CommandSession { + private final Terminal terminal; + private final InputStream in; + private final PrintStream out; + private final PrintStream err; + + public CommandSession() { + this.in = System.in; + this.out = System.out; + this.err = System.err; + this.terminal = null; + } + + public CommandSession(Terminal terminal) { + this(terminal, terminal.input(), new PrintStream(terminal.output()), new PrintStream(terminal.output())); + } + + public CommandSession(Terminal terminal, InputStream in, PrintStream out, PrintStream err) { + this.terminal = terminal; + this.in = in; + this.out = out; + this.err = err; + } + + public Terminal terminal() { + return terminal; + } + + public InputStream in() { + return in; + } + + public PrintStream out() { + return out; + } + + public PrintStream err() { + return err; + } + } } diff --git a/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java b/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java index 4b3ccd161..e8eaba49b 100644 --- a/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java +++ b/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java @@ -123,7 +123,7 @@ default Object execute(File script) throws Exception { * @param result to process * @return processed result */ - Object postProcess(String line, Object result); + Object postProcess(String line, Object result, String output); /** * Displays object. @@ -152,6 +152,8 @@ default Object execute(File script) throws Exception { */ boolean executeWidget(Object function); + boolean isExecuting(); + static class WidgetCreator implements Widget { private ConsoleEngine consoleEngine; private Object function; diff --git a/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java b/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java index b2d7d086a..05c62de7e 100644 --- a/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java +++ b/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java @@ -71,6 +71,7 @@ public enum Command {SHOW private final Map aliases = new HashMap<>(); private Path aliasFile; private LineReader reader; + private boolean executing = false; @SuppressWarnings("unchecked") public ConsoleEngineImpl(ScriptEngine engine @@ -100,7 +101,7 @@ public ConsoleEngineImpl(ScriptEngine engine @Override public void setLineReader(LineReader reader) { - this.reader= reader; + this.reader = reader; } private Parser parser() { @@ -108,7 +109,11 @@ private Parser parser() { } private Terminal terminal() { - return reader.getTerminal(); + return systemRegistry.terminal(); + } + + public boolean isExecuting() { + return executing; } @Override @@ -210,6 +215,7 @@ public List scriptCompleters() { , this::commandOptions , 1) )); + out.add(new ArgumentCompleter(new StringsCompleter(aliases::keySet), NullCompleter.INSTANCE)); return out; } @@ -449,8 +455,9 @@ public boolean execute() throws Exception { private void internalExecute() throws Exception { if (isEngineScript()) { result = engine.execute(script, expandParameters(args)); - result = postProcess(cmdLine, result); + postProcess(cmdLine, result); } else if (isConsoleScript()) { + executing = true; boolean done = false; String line = ""; try (BufferedReader br = new BufferedReader(new FileReader(script))) { @@ -487,16 +494,20 @@ private void internalExecute() throws Exception { throw e; } catch (EndOfFileException e) { done = true; + executing = false; break; } catch (Exception e) { + executing = false; throw new IllegalArgumentException(line + "\n" + e.getMessage()); } } if (!done) { + executing = false; throw new IllegalArgumentException("Incompleted command: \n" + line); } + executing = false; result = engine.get("_return"); - result = postProcess(cmdLine, result); + postProcess(cmdLine, result); } } } @@ -601,24 +612,39 @@ public boolean executeWidget(Object function) { } @Override - public Object postProcess(String line, Object result) { + public Object postProcess(String line, Object result, String output) { Object out = result; + if (Parser.getVariable(line) != null && result != null) { + engine.put("output", output); + } + if (systemRegistry.hasCommand(Parser.getCommand(line))) { + out = postProcess(line, Parser.getVariable(line) != null && result == null ? output : result); + } else if (Parser.getVariable(line) != null) { + if (result == null) { + engine.put(Parser.getVariable(line), output); + } + out = null; + } + return out; + } + + private Object postProcess(String line, Object result) { + Object out = result instanceof String && ((String)result).trim().length() == 0 ? null : result; if (Parser.getVariable(line) != null) { engine.put(Parser.getVariable(line), result); out = null; - } else if (!Parser.getCommand(line).equals("show") && systemRegistry.hasCommand(Parser.getCommand(line)) - && result != null) { + } else if (!Parser.getCommand(line).equals("show") && result != null) { engine.put("_", result); } return out; } @Override - public Object invoke(String command, Object... args) throws Exception { + public Object invoke(CommandRegistry.CommandSession session, String command, Object... args) throws Exception { exception = null; Object out = null; if (hasCommand(command)) { - out = commandExecute.get(command(command)).executeFunction().apply(new Builtins.CommandInput(args, true)); + out = commandExecute.get(command(command)).executeFunction().apply(new Builtins.CommandInput(null, args, session)); } else { String[] _args = new String[args.length]; for (int i = 0; i < args.length; i++) { @@ -650,7 +676,7 @@ private Map defaultPrntOptions() { @Override public void println(Object object) { Map options = defaultPrntOptions(); - options.putIfAbsent("exception", "message"); + options.putIfAbsent("exception", "stack"); println(options, object); } @@ -667,21 +693,7 @@ public void println(Map options, Object object) { } else if (!style.isEmpty() && object instanceof String) { highlight(width, style, (String) object); } else if (object instanceof Exception) { - if (object instanceof Options.HelpException) { - Options.HelpException.highlight(((Exception)object).getMessage(), Options.HelpException.defaultStyle()).print(terminal()); - } else if (options.getOrDefault("exception", "stack").equals("stack")) { - ((Exception) object).printStackTrace(); - } else { - String message = ((Exception) object).getMessage(); - AttributedStringBuilder asb = new AttributedStringBuilder(); - if (message != null) { - asb.append(message, AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)); - } else { - asb.append("Caught exception: ", AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)); - asb.append(object.getClass().getCanonicalName(), AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)); - } - asb.toAttributedString().println(terminal()); - } + SystemRegistry.println(options.getOrDefault("exception", "stack").equals("stack"), terminal(), (Exception)object); } else if (object instanceof String) { highlight(AttributedStyle.YELLOW + AttributedStyle.BRIGHT, object); } else if (object instanceof Number) { @@ -879,7 +891,7 @@ private Object unalias(Builtins.CommandInput input) { private List commandOptions(String command) { try { - invoke(command, "--help"); + invoke(new CommandSession(), command, "--help"); } catch (HelpException e) { return Builtins.compileCommandOptions(e.getMessage()); } catch (Exception e) { diff --git a/builtins/src/main/java/org/jline/builtins/SystemRegistry.java b/builtins/src/main/java/org/jline/builtins/SystemRegistry.java index 3e43ff188..cef2e3ada 100644 --- a/builtins/src/main/java/org/jline/builtins/SystemRegistry.java +++ b/builtins/src/main/java/org/jline/builtins/SystemRegistry.java @@ -15,6 +15,8 @@ import org.jline.reader.Completer; import org.jline.reader.ParsedLine; import org.jline.terminal.Terminal; +import org.jline.utils.AttributedStringBuilder; +import org.jline.utils.AttributedStyle; /** * Aggregate command registries and dispatch command executions. @@ -25,19 +27,19 @@ public interface SystemRegistry extends CommandRegistry { /** * Set command registeries - * @param commandRegistries + * @param commandRegistries defined in app */ void setCommandRegistries(CommandRegistry... commandRegistries); /** * Initialize consoleEngine environment by executing console script - * @param script + * @param initialization script */ void initialize(File script); /** * Returns command completer that includes also console variable and script completion. - * @return complater + * @return command completer */ Completer completer(); @@ -51,11 +53,59 @@ public interface SystemRegistry extends CommandRegistry { /** * Execute a command, script or evaluate scriptEngine statement - * @param line - * @return result + * @param command line + * @return execution result * @throws Exception */ Object execute(String line) throws Exception; + + /** + * + * @return terminal + */ + Terminal terminal(); + + /** + * Execute command with args + * @param command to execute + * @param args command arguments + * @return execution result + * @throws Exception in case of error + */ + Object execute(String command, String[] args) throws Exception; + + /** + * Execute command with args + * @param command to execute + * @param args command arguments + * @return execution result + * @throws Exception in case of error + */ + Object invoke(String command, Object... args) throws Exception; + + /** + * Print exception + * @param print stack trace if stack true otherwise message + * @param JLine terminal + * @param exception to print + */ + static void println(boolean stack, Terminal terminal, Exception exception) { + if (exception instanceof Options.HelpException) { + Options.HelpException.highlight((exception).getMessage(), Options.HelpException.defaultStyle()).print(terminal); + } else if (stack) { + exception.printStackTrace(); + } else { + String message = exception.getMessage(); + AttributedStringBuilder asb = new AttributedStringBuilder(); + if (message != null) { + asb.append(message, AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)); + } else { + asb.append("Caught exception: ", AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)); + asb.append(exception.getClass().getCanonicalName(), AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)); + } + asb.toAttributedString().println(terminal); + } + } /** * @return systemRegistry of the current thread diff --git a/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java b/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java index b4adc4d4d..e49c1eaa5 100644 --- a/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java +++ b/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java @@ -8,7 +8,11 @@ */ package org.jline.builtins; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.File; +import java.io.OutputStream; +import java.io.PrintStream; import java.util.*; import java.util.stream.Collectors; @@ -29,7 +33,10 @@ import org.jline.reader.impl.completer.NullCompleter; import org.jline.reader.impl.completer.StringsCompleter; import org.jline.terminal.Terminal; +import org.jline.terminal.TerminalBuilder; import org.jline.utils.AttributedStringBuilder; +import org.jline.utils.AttributedStyle; +import org.jline.utils.InfoCmp.Capability; /** * Aggregate command registeries. @@ -37,27 +44,29 @@ * @author Matti Rinta-Nikkola */ public class SystemRegistryImpl implements SystemRegistry { - public enum Command {EXIT - , HELP}; - private static final Class[] BUILTIN_REGISTERIES = {Builtins.class, ConsoleEngineImpl.class}; + public enum Command { + EXIT, HELP + }; + + private static final Class[] BUILTIN_REGISTERIES = { Builtins.class, ConsoleEngineImpl.class }; private CommandRegistry[] commandRegistries; private Integer consoleId = null; - private Terminal terminal; private Parser parser; private ConfigurationPath configPath; - private Map commandName = new HashMap<>(); - private Map nameCommand = new HashMap<>(); - private Map aliasCommand = new HashMap<>(); - private final Map commandExecute = new HashMap<>(); + private Map commandName = new HashMap<>(); + private Map nameCommand = new HashMap<>(); + private Map aliasCommand = new HashMap<>(); + private final Map commandExecute = new HashMap<>(); private Map> commandInfos = new HashMap<>(); private Exception exception; + private CommandOutputStream outputStream; public SystemRegistryImpl(Parser parser, Terminal terminal, ConfigurationPath configPath) { this.parser = parser; - this.terminal = terminal; this.configPath = configPath; + outputStream = new CommandOutputStream(terminal); Set cmds = new HashSet<>(EnumSet.allOf(Command.class)); - for (Command c: cmds) { + for (Command c : cmds) { commandName.put(c, c.name().toLowerCase()); } doNameCommand(); @@ -66,9 +75,7 @@ public SystemRegistryImpl(Parser parser, Terminal terminal, ConfigurationPath co } private void doNameCommand() { - nameCommand = commandName.entrySet() - .stream() - .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); + nameCommand = commandName.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); } @Override @@ -142,12 +149,12 @@ private Command command(String name) { private List localCommandInfo(String command) { try { - localExecute(command, new String[] {"--help"}); + localExecute(command, new String[] { "--help" }); } catch (HelpException e) { exception = null; return Builtins.compileCommandInfo(e.getMessage()); } catch (Exception e) { - consoleEngine().println(e); + println(e); } return new ArrayList<>(); } @@ -182,7 +189,7 @@ private boolean isLocalCommand(String command) { public Completers.SystemCompleter compileCompleters() { Completers.SystemCompleter out = CommandRegistry.aggregateCompleters(commandRegistries); Completers.SystemCompleter local = new Completers.SystemCompleter(); - for (Map.Entry entry: commandName.entrySet()) { + for (Map.Entry entry : commandName.entrySet()) { local.add(entry.getValue(), commandExecute.get(entry.getKey()).compileCompleter().apply(entry.getValue())); } local.addAliases(aliasCommand); @@ -206,12 +213,12 @@ private Widgets.CmdDesc localCommandDescription(String command) { throw new IllegalArgumentException(); } try { - localExecute(command, new String[] {"--help"}); + localExecute(command, new String[] { "--help" }); } catch (HelpException e) { exception = null; return Builtins.compileCommandDescription(e.getMessage()); } catch (Exception e) { - consoleEngine().println(e); + println(e); } return null; } @@ -252,7 +259,7 @@ public Object invoke(String command, Object... args) throws Exception { command = ConsoleEngine.plainCommand(command); int id = registryId(command); if (id > -1) { - out = commandRegistries[id].invoke(command, args); + out = commandRegistries[id].invoke(commandSession(), command, args); } else if (isLocalCommand(command)) { String[] _args = new String[args.length]; for (int i = 0; i < args.length; i++) { @@ -263,7 +270,7 @@ public Object invoke(String command, Object... args) throws Exception { } out = localExecute(command, _args); } else if (consoleId != null) { - out = consoleEngine().invoke(command, args); + out = consoleEngine().invoke(commandSession(), command, args); } return out; } @@ -273,7 +280,7 @@ public Object execute(String command, String[] args) throws Exception { Object out = null; int id = registryId(command); if (id > -1) { - out = commandRegistries[id].execute(command, args); + out = commandRegistries[id].execute(commandSession(), command, args); } else if (isLocalCommand(command)) { out = localExecute(command, args); } @@ -284,19 +291,101 @@ public Object localExecute(String command, String[] args) throws Exception { if (!isLocalCommand(command)) { throw new IllegalArgumentException(); } - Object out = commandExecute.get(command(command)).executeFunction().apply(new Builtins.CommandInput(args)); + Object out = commandExecute.get(command(command)).executeFunction() + .apply(new Builtins.CommandInput(args, commandSession())); if (exception != null) { throw exception; } return out; } + public Terminal terminal() { + return commandSession().terminal(); + } + + private CommandSession commandSession() { + return outputStream.getCommandSession(); + } + + private static class CommandOutputStream { + private PrintStream origOut; + private PrintStream origErr; + private Terminal origTerminal; + private ByteArrayOutputStream byteOutputStream; + private PrintStream out; + private Terminal terminal; + private String output; + private CommandRegistry.CommandSession commandSession; + private boolean redirecting = false; + + public CommandOutputStream(Terminal terminal) { + this.origOut = System.out; + this.origErr = System.err; + this.origTerminal = terminal; + this.terminal = terminal; + PrintStream ps = new PrintStream(terminal.output()); + this.commandSession = new CommandRegistry.CommandSession(terminal, terminal.input(), ps, ps); + } + + public void redirect(String var) throws IOException { + byteOutputStream = new ByteArrayOutputStream(); + out = new PrintStream(byteOutputStream); + System.setOut(out); + System.setErr(out); + terminal = TerminalBuilder.builder().streams(System.in, out).type(Terminal.TYPE_DUMB).build(); + this.commandSession = new CommandRegistry.CommandSession(terminal, terminal.input(), out, out); + redirecting = true; + } + + public void flush() { + if (out == null) { + return; + } + try { + out.flush(); + if (out instanceof PrintStream && byteOutputStream != null) { + byteOutputStream.flush(); + output = byteOutputStream.toString(); + byteOutputStream.close(); + byteOutputStream = null; + } + out.close(); + out = null; + } catch (Exception e) { + + } + } + + public CommandRegistry.CommandSession getCommandSession() { + return commandSession; + } + + public String getOutput() { + return output; + } + + public void reset() { + if (redirecting) { + out = null; + byteOutputStream = null; + output = null; + System.setOut(origOut); + System.setErr(origErr); + terminal = origTerminal; + PrintStream ps = new PrintStream(terminal.output()); + this.commandSession = new CommandRegistry.CommandSession(terminal, terminal.input(), ps, ps); + redirecting = false; + } + } + } + @Override public Object execute(String line) throws Exception { ParsedLine pl = parser.parse(line, 0, ParseContext.ACCEPT_LINE); if (pl.line().isEmpty() || pl.line().trim().startsWith("#")) { return null; } + String var = consoleId != null ? Parser.getVariable(line) : null; String cmd = ConsoleEngine.plainCommand(Parser.getCommand(pl.word())); if (consoleId != null && consoleEngine().hasAlias(cmd)) { pl = parser.parse(line.replaceFirst(cmd, consoleEngine().getAlias(cmd)), 0, ParseContext.ACCEPT_LINE); @@ -305,28 +394,45 @@ public Object execute(String line) throws Exception { String[] argv = pl.words().subList(1, pl.words().size()).toArray(new String[0]); Object out = null; exception = null; - if (isLocalCommand(cmd)) { - out = localExecute(cmd, argv); - } else { - int id = registryId(cmd); - try { + try { + if (var != null && consoleId != null && !consoleEngine().isExecuting()) { + outputStream.redirect(var); + } + if (isLocalCommand(cmd)) { + out = localExecute(cmd, argv); + } else { + int id = registryId(cmd); if (id > -1) { if (consoleId != null) { - out = commandRegistries[id].invoke(cmd, consoleEngine().expandParameters(argv)); - out = consoleEngine().postProcess(pl.line(), out); + out = commandRegistries[id].invoke(outputStream.getCommandSession(), cmd, + consoleEngine().expandParameters(argv)); } else { - out = commandRegistries[id].execute(cmd, argv); + out = commandRegistries[id].execute(outputStream.getCommandSession(), cmd, argv); } } else if (consoleId != null) { out = consoleEngine().execute(pl); } - } catch (HelpException e) { - consoleEngine().println(e); + } + } catch (HelpException e) { + println(e); + } finally { + if (consoleId != null && !consoleEngine().isExecuting()) { + outputStream.flush(); + out = consoleEngine().postProcess(pl.line(), out, outputStream.getOutput()); + outputStream.reset(); } } return out; } + private void println(Exception exception) { + if (consoleId != null) { + consoleEngine().println(exception); + } else { + SystemRegistry.println(false, terminal(), exception); + } + } + private ConsoleEngine consoleEngine() { return consoleId != null ? (ConsoleEngine) commandRegistries[consoleId] : null; } @@ -345,7 +451,7 @@ private void printHeader(String header) { asb.append("\t"); asb.append(header, HelpException.defaultStyle().resolve(".ti")); asb.append(":"); - asb.toAttributedString().println(terminal); + asb.toAttributedString().println(terminal()); } private void printCommandInfo(String command, String info, int max) { @@ -354,8 +460,8 @@ private void printCommandInfo(String command, String info, int max) { asb.append(command, HelpException.defaultStyle().resolve(".co")); asb.append("\t"); asb.append(info); - asb.setLength(terminal.getWidth()); - asb.toAttributedString().println(terminal); + asb.setLength(terminal().getWidth()); + asb.toAttributedString().println(terminal()); } private void printCommands(Collection commands, int max) { @@ -368,8 +474,8 @@ private void printCommands(Collection commands, int max) { asb.append(c, HelpException.defaultStyle().resolve(".co")); asb.append("\t"); col += max; - if (col + max > terminal.getWidth()) { - asb.toAttributedString().println(terminal); + if (col + max > terminal().getWidth()) { + asb.toAttributedString().println(terminal()); asb = new AttributedStringBuilder().tabs(Arrays.asList(4, max + 4)); col = 0; asb.append("\t"); @@ -380,9 +486,9 @@ private void printCommands(Collection commands, int max) { } } if (!done) { - asb.toAttributedString().println(terminal); + asb.toAttributedString().println(terminal()); } - terminal.flush(); + terminal().flush(); } private String doCommandInfo(List info) { @@ -394,11 +500,8 @@ private boolean isInArgs(List args, String name) { } private Object help(Builtins.CommandInput input) { - final String[] usage = { - "help - command help", - "Usage: help [NAME...]", - " -? --help Displays command help", - }; + final String[] usage = { "help - command help", "Usage: help [NAME...]", + " -? --help Displays command help", }; Options opt = Options.compile(usage).parse(input.args()); if (opt.isSet("help")) { exception = new HelpException(opt.usage()); @@ -409,12 +512,12 @@ private Object help(Builtins.CommandInput input) { if (consoleId > -1) { commands.addAll(consoleEngine().scripts()); } - boolean withInfo = commands.size() < terminal.getHeight() || !opt.args().isEmpty() ? true : false; + boolean withInfo = commands.size() < terminal().getHeight() || !opt.args().isEmpty() ? true : false; int max = Collections.max(commands, Comparator.comparing(String::length)).length() + 1; - TreeMap builtinCommands = new TreeMap<>(); + TreeMap builtinCommands = new TreeMap<>(); for (CommandRegistry r : commandRegistries) { if (isBuiltinRegistry(r)) { - for (String c: r.commandNames()) { + for (String c : r.commandNames()) { builtinCommands.put(c, doCommandInfo(commandInfo(c))); } } @@ -450,22 +553,20 @@ private Object help(Builtins.CommandInput input) { if (consoleId > -1 && isInArgs(opt.args(), "Scripts")) { printHeader("Scripts"); if (withInfo) { - for (String c: consoleEngine().scripts()) { + for (String c : consoleEngine().scripts()) { printCommandInfo(c, doCommandInfo(commandInfo(c)), max); } } else { printCommands(consoleEngine().scripts(), max); } } + terminal().flush(); return null; } private Object exit(Builtins.CommandInput input) { - final String[] usage = { - "exit - exit from app/script", - "Usage: exit", - " -? --help Displays command help", - }; + final String[] usage = { "exit - exit from app/script", "Usage: exit", + " -? --help Displays command help", }; Options opt = Options.compile(usage).parse(input.args()); if (opt.isSet("help")) { exception = new HelpException(opt.usage()); @@ -477,12 +578,12 @@ private Object exit(Builtins.CommandInput input) { private List commandOptions(String command) { try { - localExecute(command, new String[] {"--help"}); + localExecute(command, new String[] { "--help" }); } catch (HelpException e) { exception = null; return Builtins.compileCommandOptions(e.getMessage()); } catch (Exception e) { - consoleEngine().println(e); + println(e); } return null; } @@ -504,21 +605,15 @@ private List registryNames() { private List helpCompleter(String command) { List completers = new ArrayList<>(); - completers.add(new ArgumentCompleter(NullCompleter.INSTANCE - , new OptionCompleter(new StringsCompleter(this::registryNames) - , this::commandOptions - , 1) - )); + completers.add(new ArgumentCompleter(NullCompleter.INSTANCE, + new OptionCompleter(new StringsCompleter(this::registryNames), this::commandOptions, 1))); return completers; } private List exitCompleter(String command) { List completers = new ArrayList<>(); - completers.add(new ArgumentCompleter(NullCompleter.INSTANCE - , new OptionCompleter(NullCompleter.INSTANCE - , this::commandOptions - , 1) - )); + completers.add(new ArgumentCompleter(NullCompleter.INSTANCE, + new OptionCompleter(NullCompleter.INSTANCE, this::commandOptions, 1))); return completers; } diff --git a/builtins/src/test/java/org/jline/example/Example.java b/builtins/src/test/java/org/jline/example/Example.java index eb1df7676..737f61a5d 100644 --- a/builtins/src/test/java/org/jline/example/Example.java +++ b/builtins/src/test/java/org/jline/example/Example.java @@ -295,9 +295,9 @@ public Completers.SystemCompleter compileCompleters() { return out; } - public Object execute(String command, String[] args) throws Exception { + public Object execute(CommandRegistry.CommandSession session, String command, String[] args) throws Exception { exception = null; - commandExecute.get(command(command)).execute().accept(new Builtins.CommandInput(args)); + commandExecute.get(command(command)).execute().accept(new Builtins.CommandInput(args, session)); if (exception != null) { throw exception; } @@ -722,6 +722,7 @@ public void complete(LineReader reader, ParsedLine line, List candida // // REPL-loop // + CommandRegistry.CommandSession session = new CommandRegistry.CommandSession(terminal); while (true) { try { String line = reader.readLine(prompt, rightPrompt, (MaskingCallback) null, null); @@ -752,10 +753,10 @@ public void complete(LineReader reader, ParsedLine line, List candida masterRegistry.help(); } else if (builtins.hasCommand(cmd)) { - builtins.execute(cmd, argv, System.in, System.out, System.err); + builtins.execute(session, cmd, argv); } else if (exampleCommands.hasCommand(cmd)) { - exampleCommands.execute(cmd, argv); + exampleCommands.execute(session, cmd, argv); } } catch (HelpException e) { diff --git a/demo/src/main/java/org/jline/demo/Repl.java b/demo/src/main/java/org/jline/demo/Repl.java index a7bbb0ece..be67d4108 100644 --- a/demo/src/main/java/org/jline/demo/Repl.java +++ b/demo/src/main/java/org/jline/demo/Repl.java @@ -113,9 +113,9 @@ public Completers.SystemCompleter compileCompleters() { return out; } - public Object execute(String command, String[] args) throws Exception { + public Object execute(CommandRegistry.CommandSession session, String command, String[] args) throws Exception { exception = null; - commandExecute.get(command(command)).execute().accept(new Builtins.CommandInput(args)); + commandExecute.get(command(command)).execute().accept(new Builtins.CommandInput(args, session)); if (exception != null) { throw exception; } @@ -199,7 +199,7 @@ private void clear(Builtins.CommandInput input) { private List commandOptions(String command) { try { - execute(command, new String[] {"--help"}); + execute(new CommandRegistry.CommandSession(), command, new String[] {"--help"}); } catch (HelpException e) { return Builtins.compileCommandOptions(e.getMessage()); } catch (Exception e) { From cea2ea9a73ecfc1b5db5ba8e877379513fad16aa Mon Sep 17 00:00:00 2001 From: mattirn Date: Tue, 4 Feb 2020 18:42:18 +0100 Subject: [PATCH 2/5] Improved javadoc comments --- .../org/jline/builtins/CommandRegistry.java | 37 +++++++------- .../org/jline/builtins/ConsoleEngine.java | 49 ++++++++++--------- .../org/jline/builtins/SystemRegistry.java | 45 +++++++++-------- 3 files changed, 68 insertions(+), 63 deletions(-) diff --git a/builtins/src/main/java/org/jline/builtins/CommandRegistry.java b/builtins/src/main/java/org/jline/builtins/CommandRegistry.java index c271759de..1492a6107 100644 --- a/builtins/src/main/java/org/jline/builtins/CommandRegistry.java +++ b/builtins/src/main/java/org/jline/builtins/CommandRegistry.java @@ -29,6 +29,7 @@ public interface CommandRegistry { /** * Aggregate SystemCompleters of commandRegisteries + * @param commandRegistries command registeries which completers is to be aggregated * @return uncompiled SystemCompleter */ static Completers.SystemCompleter aggregateCompleters(CommandRegistry ... commandRegistries) { @@ -41,6 +42,7 @@ static Completers.SystemCompleter aggregateCompleters(CommandRegistry ... comman /** * Aggregate and compile SystemCompleters of commandRegisteries + * @param commandRegistries command registeries which completers is to be aggregated and compile * @return compiled SystemCompleter */ static Completers.SystemCompleter compileCompleters(CommandRegistry ... commandRegistries) { @@ -51,7 +53,7 @@ static Completers.SystemCompleter compileCompleters(CommandRegistry ... commandR /** * Returns the name of this registry. - * @return name + * @return the name of the registry */ default String name() { return this.getClass().getSimpleName(); @@ -93,7 +95,6 @@ default List commandInfo(String command) { /** * Returns a {@code SystemCompleter} that can provide detailed completion * information for all registered commands. - * * @return a SystemCompleter that can provide command completion for all registered commands */ Completers.SystemCompleter compileCompleters(); @@ -118,11 +119,11 @@ default Widgets.CmdDesc commandDescription(String command) { /** * Execute a command that have only string parameters and options. Implementation of the method is required * when aggregating command registries using SystemRegistry. - * @param session - * @param command - * @param args - * @return result - * @throws Exception + * @param session the data of the current command session + * @param command the name of the command + * @param args arguments of the command + * @return result of the command execution + * @throws Exception in case of error */ default Object execute(CommandSession session, String command, String[] args) throws Exception { throw new IllegalArgumentException("CommandRegistry method execute(String command, String[] args) is not implemented!"); @@ -131,11 +132,11 @@ default Object execute(CommandSession session, String command, String[] args) th /** * Execute a command. If command has other than string parameters a custom implementation is required. * This method will be called only when we have ConsoleEngine in SystemRegistry. - * @param session - * @param command - * @param args - * @return result - * @throws Exception + * @param session the data of the current command session + * @param command the name of the command + * @param args arguments of the command + * @return result of the command execution + * @throws Exception in case of error */ default Object invoke(CommandSession session, String command, Object... args) throws Exception { String[] _args = new String[args.length]; @@ -147,13 +148,13 @@ default Object invoke(CommandSession session, String command, Object... args) th } return execute(session, command, _args); } - + public static class CommandSession { private final Terminal terminal; private final InputStream in; private final PrintStream out; private final PrintStream err; - + public CommandSession() { this.in = System.in; this.out = System.out; @@ -164,22 +165,22 @@ public CommandSession() { public CommandSession(Terminal terminal) { this(terminal, terminal.input(), new PrintStream(terminal.output()), new PrintStream(terminal.output())); } - + public CommandSession(Terminal terminal, InputStream in, PrintStream out, PrintStream err) { this.terminal = terminal; this.in = in; this.out = out; this.err = err; } - + public Terminal terminal() { return terminal; } - + public InputStream in() { return in; } - + public PrintStream out() { return out; } diff --git a/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java b/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java index e8eaba49b..a563eebd3 100644 --- a/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java +++ b/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java @@ -26,9 +26,9 @@ public interface ConsoleEngine extends CommandRegistry { /** - * Removes command first character if it is colon - * @param command name to complete - * @return command name without colon + * Removes the command name first character if it is colon + * @param command the name of the command to complete + * @return command name without starting colon */ static String plainCommand(String command) { return command.startsWith(":") ? command.substring(1) : command; @@ -42,47 +42,47 @@ static String plainCommand(String command) { /** * Sets systemRegistry - * @param systemRegistry + * @param systemRegistry SystemRegistry */ void setSystemRegistry(SystemRegistry systemRegistry); /** * Substituting args references with their values. - * @param args - * @return Substituted args + * @param args the arguments to be expanded + * @return expanded arguments * @throws Exception in case of error */ Object[] expandParameters(String[] args) throws Exception; /** * Returns all scripts found from PATH - * @return script names + * @return script file names */ List scripts(); /** * Sets file name extension used by console scripts - * @param console script file extension + * @param extension console script file extension */ public void setScriptExtension(String extension); /** * Returns true if alias 'name' exists - * @param alias name + * @param name alias name * @return true if alias exists */ boolean hasAlias(String name); /** * Returns alias 'name' value - * @param alias name + * @param name alias name * @return value of alias */ String getAlias(String name); /** * Returns script and variable completers - * @return script completers + * @return script and variable completers */ List scriptCompleters(); @@ -90,7 +90,7 @@ static String plainCommand(String command) { * Executes parsed line that does not contain known command by the system registry. * If parsed line is neither JLine or ScriptEngine script it will be evaluated * as ScriptEngine statement. - * @param parsed command line + * @param parsedLine parsed command line * @return command line execution result * @throws Exception in case of error */ @@ -98,7 +98,7 @@ static String plainCommand(String command) { /** * Executes either JLine or ScriptEngine script. - * @param script file + * @param script script file * @return script execution result * @throws Exception in case of error */ @@ -108,9 +108,9 @@ default Object execute(File script) throws Exception { /** * Executes either JLine or ScriptEngine script. - * @param script file + * @param script script file * @param cmdLine complete command line - * @param script arguments + * @param args script arguments * @return script execution result * @throws Exception in case of error */ @@ -119,28 +119,29 @@ default Object execute(File script) throws Exception { /** * Post processes execution result. If result is to be assigned to the console variable * then method will return null. - * @param command line - * @param result to process + * @param line command line + * @param result command result to process + * @param output command redirected output * @return processed result */ Object postProcess(String line, Object result, String output); /** * Displays object. - * @param object to print + * @param object object to print */ void println(Object object); /** * Displays object. * @param options println options - * @param object to print + * @param object object to print */ void println(Map options, Object object); /** * Get variable value - * @param name of variable + * @param name name of the variable * @return variable value */ Object getVariable(String name); @@ -152,8 +153,12 @@ default Object execute(File script) throws Exception { */ boolean executeWidget(Object function); + /** + * + * @return true if consoleEngine is executing script + */ boolean isExecuting(); - + static class WidgetCreator implements Widget { private ConsoleEngine consoleEngine; private Object function; @@ -169,7 +174,7 @@ public WidgetCreator(ConsoleEngine consoleEngine, String function) { public boolean apply() { return consoleEngine.executeWidget(function); } - + @Override public String toString() { return name; diff --git a/builtins/src/main/java/org/jline/builtins/SystemRegistry.java b/builtins/src/main/java/org/jline/builtins/SystemRegistry.java index cef2e3ada..27846acc7 100644 --- a/builtins/src/main/java/org/jline/builtins/SystemRegistry.java +++ b/builtins/src/main/java/org/jline/builtins/SystemRegistry.java @@ -13,7 +13,6 @@ import java.util.Map; import org.jline.reader.Completer; -import org.jline.reader.ParsedLine; import org.jline.terminal.Terminal; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; @@ -27,13 +26,13 @@ public interface SystemRegistry extends CommandRegistry { /** * Set command registeries - * @param commandRegistries defined in app + * @param commandRegistries command registeries used by the application */ void setCommandRegistries(CommandRegistry... commandRegistries); /** * Initialize consoleEngine environment by executing console script - * @param initialization script + * @param script initialization script */ void initialize(File script); @@ -45,49 +44,49 @@ public interface SystemRegistry extends CommandRegistry { /** * Returns a command, method or syntax description for use in the JLine Widgets framework. - * @param command line whose description to return + * @param line command line whose description to return * @return command description for JLine TailTipWidgets to be displayed * in the terminal status bar. */ Widgets.CmdDesc commandDescription(Widgets.CmdLine line); - + /** * Execute a command, script or evaluate scriptEngine statement - * @param command line + * @param line command line to be executed * @return execution result - * @throws Exception + * @throws Exception in case of error */ Object execute(String line) throws Exception; - + /** - * + * * @return terminal */ Terminal terminal(); /** - * Execute command with args - * @param command to execute - * @param args command arguments - * @return execution result + * Execute command with arguments + * @param command command to be executed + * @param args arguments of the command + * @return command execution result * @throws Exception in case of error */ Object execute(String command, String[] args) throws Exception; - + /** - * Execute command with args - * @param command to execute - * @param args command arguments - * @return execution result + * Execute command with arguments + * @param command command to be executed + * @param args arguments of the command + * @return command execution result * @throws Exception in case of error */ Object invoke(String command, Object... args) throws Exception; /** - * Print exception - * @param print stack trace if stack true otherwise message - * @param JLine terminal - * @param exception to print + * Print exception + * @param stack print stack trace if stack true otherwise message + * @param terminal JLine terminal + * @param exception exception to be printed */ static void println(boolean stack, Terminal terminal, Exception exception) { if (exception instanceof Options.HelpException) { @@ -104,7 +103,7 @@ static void println(boolean stack, Terminal terminal, Exception exception) { asb.append(exception.getClass().getCanonicalName(), AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)); } asb.toAttributedString().println(terminal); - } + } } /** From abdd432258ca6233e28604ff39847677e859b3d3 Mon Sep 17 00:00:00 2001 From: mattirn Date: Tue, 4 Feb 2020 18:52:01 +0100 Subject: [PATCH 3/5] Command output redirection to file --- .../org/jline/builtins/ConsoleEngine.java | 5 ++ .../org/jline/builtins/ConsoleEngineImpl.java | 13 ++- .../org/jline/builtins/SystemRegistry.java | 4 + .../jline/builtins/SystemRegistryImpl.java | 89 ++++++++++++++++--- demo/src/main/java/org/jline/demo/Repl.java | 2 +- 5 files changed, 96 insertions(+), 17 deletions(-) diff --git a/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java b/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java index a563eebd3..f7fc86677 100644 --- a/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java +++ b/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java @@ -146,6 +146,11 @@ default Object execute(File script) throws Exception { */ Object getVariable(String name); + /** + * Delete temporary console variables + */ + void purge(); + /** * Execute widget function * @param function to execute diff --git a/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java b/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java index 05c62de7e..64f950d1b 100644 --- a/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java +++ b/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java @@ -111,7 +111,7 @@ private Parser parser() { private Terminal terminal() { return systemRegistry.terminal(); } - + public boolean isExecuting() { return executing; } @@ -585,6 +585,11 @@ public Object execute(ParsedLine pl) throws Exception { } return out; } + + @Override + public void purge() { + engine.del("_*"); + } @Override public Object getVariable(String name) { @@ -624,10 +629,10 @@ public Object postProcess(String line, Object result, String output) { engine.put(Parser.getVariable(line), output); } out = null; - } + } return out; } - + private Object postProcess(String line, Object result) { Object out = result instanceof String && ((String)result).trim().length() == 0 ? null : result; if (Parser.getVariable(line) != null) { @@ -676,7 +681,7 @@ private Map defaultPrntOptions() { @Override public void println(Object object) { Map options = defaultPrntOptions(); - options.putIfAbsent("exception", "stack"); + options.putIfAbsent("exception", "message"); println(options, object); } diff --git a/builtins/src/main/java/org/jline/builtins/SystemRegistry.java b/builtins/src/main/java/org/jline/builtins/SystemRegistry.java index 27846acc7..f59484374 100644 --- a/builtins/src/main/java/org/jline/builtins/SystemRegistry.java +++ b/builtins/src/main/java/org/jline/builtins/SystemRegistry.java @@ -58,6 +58,10 @@ public interface SystemRegistry extends CommandRegistry { */ Object execute(String line) throws Exception; + /** + * Delete temporary console variables and reset output streams + */ + void cleanUp(); /** * * @return terminal diff --git a/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java b/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java index e49c1eaa5..f7093964c 100644 --- a/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java +++ b/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java @@ -8,10 +8,13 @@ */ package org.jline.builtins; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.File; +import java.io.InputStream; import java.io.OutputStream; +import java.io.File; +import java.io.FileOutputStream; import java.io.PrintStream; import java.util.*; import java.util.stream.Collectors; @@ -35,8 +38,6 @@ import org.jline.terminal.Terminal; import org.jline.terminal.TerminalBuilder; import org.jline.utils.AttributedStringBuilder; -import org.jline.utils.AttributedStyle; -import org.jline.utils.InfoCmp.Capability; /** * Aggregate command registeries. @@ -312,7 +313,9 @@ private static class CommandOutputStream { private PrintStream origErr; private Terminal origTerminal; private ByteArrayOutputStream byteOutputStream; + private FileOutputStream fileOutputStream; private PrintStream out; + private InputStream in; private Terminal terminal; private String output; private CommandRegistry.CommandSession commandSession; @@ -327,27 +330,64 @@ public CommandOutputStream(Terminal terminal) { this.commandSession = new CommandRegistry.CommandSession(terminal, terminal.input(), ps, ps); } - public void redirect(String var) throws IOException { + public void redirect() throws IOException { byteOutputStream = new ByteArrayOutputStream(); - out = new PrintStream(byteOutputStream); + doTerminal(byteOutputStream); + } + + public void redirect(File file, boolean append) throws IOException { + if (!file.exists()){ + try { + file.createNewFile(); + } catch(IOException e){ + (new File(file.getParent())).mkdirs(); + file.createNewFile(); + } + } + fileOutputStream = new FileOutputStream(file, append); + doTerminal(fileOutputStream); + } + + void doTerminal(OutputStream outputStream) throws IOException { + out = new PrintStream(outputStream); System.setOut(out); System.setErr(out); - terminal = TerminalBuilder.builder().streams(System.in, out).type(Terminal.TYPE_DUMB).build(); + in = new ByteArrayInputStream( "".getBytes() ); + terminal = TerminalBuilder.builder().streams(in, outputStream).type(Terminal.TYPE_DUMB).build(); this.commandSession = new CommandRegistry.CommandSession(terminal, terminal.input(), out, out); redirecting = true; } - + public void flush() { if (out == null) { return; } try { out.flush(); - if (out instanceof PrintStream && byteOutputStream != null) { + if (byteOutputStream != null) { byteOutputStream.flush(); output = byteOutputStream.toString(); + } else if (fileOutputStream != null) { + fileOutputStream.flush(); + } + } catch (Exception e) { + + } + } + + public void close() { + if (out == null) { + return; + } + try { + in.close(); + flush(); + if (byteOutputStream != null) { byteOutputStream.close(); byteOutputStream = null; + } else if (fileOutputStream != null) { + fileOutputStream.close(); + fileOutputStream = null; } out.close(); out = null; @@ -368,6 +408,7 @@ public void reset() { if (redirecting) { out = null; byteOutputStream = null; + fileOutputStream = null; output = null; System.setOut(origOut); System.setErr(origErr); @@ -391,12 +432,28 @@ public Object execute(String line) throws Exception { pl = parser.parse(line.replaceFirst(cmd, consoleEngine().getAlias(cmd)), 0, ParseContext.ACCEPT_LINE); cmd = ConsoleEngine.plainCommand(Parser.getCommand(pl.word())); } - String[] argv = pl.words().subList(1, pl.words().size()).toArray(new String[0]); + File toFile = null; + boolean append = false; + List words = pl.words(); + int lastArg = words.size(); + for (int i = 1; i < words.size() - 1; i++) { + if (words.get(i).equals(">") || words.get(i).equals(">>")) { + lastArg = i; + append = words.get(i).equals(">>"); + toFile = new File(words.get(i + 1)); + break; + } + } + String[] argv = words.subList(1, lastArg).toArray(new String[0]); Object out = null; exception = null; try { - if (var != null && consoleId != null && !consoleEngine().isExecuting()) { - outputStream.redirect(var); + if ((var != null || toFile != null) && consoleId != null && !consoleEngine().isExecuting()) { + if (toFile != null) { + outputStream.redirect(toFile, append); + } else { + outputStream.redirect(); + } } if (isLocalCommand(cmd)) { out = localExecute(cmd, argv); @@ -419,11 +476,19 @@ public Object execute(String line) throws Exception { if (consoleId != null && !consoleEngine().isExecuting()) { outputStream.flush(); out = consoleEngine().postProcess(pl.line(), out, outputStream.getOutput()); - outputStream.reset(); } } return out; } + + public void cleanUp() { + if (consoleId == null) { + return; + } + outputStream.close(); + outputStream.reset(); + consoleEngine().purge(); + } private void println(Exception exception) { if (consoleId != null) { diff --git a/demo/src/main/java/org/jline/demo/Repl.java b/demo/src/main/java/org/jline/demo/Repl.java index be67d4108..bb063fba6 100644 --- a/demo/src/main/java/org/jline/demo/Repl.java +++ b/demo/src/main/java/org/jline/demo/Repl.java @@ -296,7 +296,7 @@ public static void main(String[] args) { consoleEngine.println(terminal.getName()+": "+terminal.getType()); while (true) { try { - scriptEngine.del("_*"); // delete temporary variables + systemRegistry.cleanUp(); // delete temporary variables and reset output streams String line = reader.readLine("groovy-repl> "); Object result = systemRegistry.execute(line); consoleEngine.println(result); From a78b2346e8ecbbdccb5ed2f7b866ed9312874277 Mon Sep 17 00:00:00 2001 From: mattirn Date: Wed, 5 Feb 2020 18:36:20 +0100 Subject: [PATCH 4/5] Command output redirection to file, step II --- .../org/jline/builtins/ConsoleEngine.java | 28 ++++++++----- .../org/jline/builtins/ConsoleEngineImpl.java | 39 +++++++++++++----- .../org/jline/builtins/SystemRegistry.java | 8 +++- .../jline/builtins/SystemRegistryImpl.java | 40 +++++++++++++++---- demo/src/main/java/org/jline/demo/Repl.java | 3 +- 5 files changed, 87 insertions(+), 31 deletions(-) diff --git a/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java b/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java index f7fc86677..1717f85eb 100644 --- a/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java +++ b/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java @@ -15,7 +15,6 @@ import org.jline.builtins.CommandRegistry; import org.jline.reader.Completer; import org.jline.reader.LineReader; -import org.jline.reader.ParsedLine; import org.jline.reader.Widget; /** @@ -87,14 +86,16 @@ static String plainCommand(String command) { List scriptCompleters(); /** - * Executes parsed line that does not contain known command by the system registry. - * If parsed line is neither JLine or ScriptEngine script it will be evaluated + * Executes command line that does not contain known command by the system registry. + * If the line is neither JLine or ScriptEngine script it will be evaluated * as ScriptEngine statement. - * @param parsedLine parsed command line + * @param name parsed command/script name + * @param rawLine raw command line + * @param args parsed arguments of the command * @return command line execution result * @throws Exception in case of error */ - Object execute(ParsedLine parsedLine) throws Exception; + Object execute(String name, String rawLine, String[] args) throws Exception; /** * Executes either JLine or ScriptEngine script. @@ -109,12 +110,12 @@ default Object execute(File script) throws Exception { /** * Executes either JLine or ScriptEngine script. * @param script script file - * @param cmdLine complete command line + * @param rawLine raw command line * @param args script arguments * @return script execution result * @throws Exception in case of error */ - Object execute(File script, String cmdLine, String[] args) throws Exception; + Object execute(File script, String rawLine, String[] args) throws Exception; /** * Post processes execution result. If result is to be assigned to the console variable @@ -127,18 +128,25 @@ default Object execute(File script) throws Exception { Object postProcess(String line, Object result, String output); /** - * Displays object. + * Print object. * @param object object to print */ void println(Object object); /** - * Displays object. + * Print object. * @param options println options * @param object object to print */ void println(Map options, Object object); + /** + * Create console variable + * @param name name of the variable + * @param value value of the variable + */ + void putVariable(String name, Object value); + /** * Get variable value * @param name name of the variable @@ -150,7 +158,7 @@ default Object execute(File script) throws Exception { * Delete temporary console variables */ void purge(); - + /** * Execute widget function * @param function to execute diff --git a/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java b/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java index 64f950d1b..fc5471f04 100644 --- a/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java +++ b/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java @@ -51,6 +51,7 @@ public enum Command {SHOW , ALIAS , UNALIAS , SLURP}; + private static final String VAR_CONSOLE_OPTIONS = "CONSOLE_OPTIONS"; private static final String VAR_PRNT_OPTIONS = "PRNT_OPTIONS"; private static final String VAR_PATH = "PATH"; private static final String VAR_NANORC = "NANORC"; @@ -526,18 +527,16 @@ public Object execute(File script, String cmdLine, String[] args) throws Excepti } @Override - public Object execute(ParsedLine pl) throws Exception { - if (pl.line().trim().startsWith("#")) { + public Object execute(String cmd, String line, String[] args) throws Exception { + if (line.trim().startsWith("#")) { return null; } - String[] args = pl.words().subList(1, pl.words().size()).toArray(new String[0]); - String cmd = ConsoleEngine.plainCommand(Parser.getCommand(pl.word())); Object out = null; - ScriptFile file = new ScriptFile(cmd, pl.line(), args); + ScriptFile file = new ScriptFile(cmd, line, args); if (file.execute()) { out = file.getResult(); } else { - String line = pl.line().trim(); + line = line.trim(); if (isCodeBlock(line)) { StringBuilder sb = new StringBuilder(); for (String s: line.split("\n|\n\r")) { @@ -585,12 +584,17 @@ public Object execute(ParsedLine pl) throws Exception { } return out; } - + @Override public void purge() { engine.del("_*"); } + @Override + public void putVariable(String name, Object value) { + engine.put(name, value); + } + @Override public Object getVariable(String name) { if (!engine.hasVariable(name)) { @@ -616,17 +620,32 @@ public boolean executeWidget(Object function) { return true; } + @SuppressWarnings("unchecked") + private boolean splitCommandOutput() { + boolean out = true; + try { + if (engine.hasVariable(VAR_CONSOLE_OPTIONS)) { + out = (boolean) ((Map) engine.get(VAR_CONSOLE_OPTIONS)).getOrDefault("splitOutput", true); + } + } catch (Exception e) { + println(new Exception("Bad CONSOLE_OPTION value: " + e.getMessage())); + } + return out; + } + + @Override public Object postProcess(String line, Object result, String output) { Object out = result; + Object _output = output != null && splitCommandOutput() ? output.split("\n") : output; if (Parser.getVariable(line) != null && result != null) { - engine.put("output", output); + engine.put("output", _output); } if (systemRegistry.hasCommand(Parser.getCommand(line))) { - out = postProcess(line, Parser.getVariable(line) != null && result == null ? output : result); + out = postProcess(line, Parser.getVariable(line) != null && result == null ? _output : result); } else if (Parser.getVariable(line) != null) { if (result == null) { - engine.put(Parser.getVariable(line), output); + engine.put(Parser.getVariable(line), _output); } out = null; } diff --git a/builtins/src/main/java/org/jline/builtins/SystemRegistry.java b/builtins/src/main/java/org/jline/builtins/SystemRegistry.java index f59484374..015000e9a 100644 --- a/builtins/src/main/java/org/jline/builtins/SystemRegistry.java +++ b/builtins/src/main/java/org/jline/builtins/SystemRegistry.java @@ -62,8 +62,14 @@ public interface SystemRegistry extends CommandRegistry { * Delete temporary console variables and reset output streams */ void cleanUp(); + + /** + * Print exception on terminal + * @param exception exception to print on terminal + */ + void println(Exception exception); + /** - * * @return terminal */ Terminal terminal(); diff --git a/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java b/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java index f7093964c..f492c5bce 100644 --- a/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java +++ b/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java @@ -332,7 +332,6 @@ public CommandOutputStream(Terminal terminal) { public void redirect() throws IOException { byteOutputStream = new ByteArrayOutputStream(); - doTerminal(byteOutputStream); } public void redirect(File file, boolean append) throws IOException { @@ -345,10 +344,13 @@ public void redirect(File file, boolean append) throws IOException { } } fileOutputStream = new FileOutputStream(file, append); - doTerminal(fileOutputStream); } - void doTerminal(OutputStream outputStream) throws IOException { + public void open() throws IOException { + if (redirecting || (byteOutputStream == null && fileOutputStream == null)) { + return; + } + OutputStream outputStream = byteOutputStream != null ? byteOutputStream : fileOutputStream; out = new PrintStream(outputStream); System.setOut(out); System.setErr(out); @@ -357,7 +359,7 @@ void doTerminal(OutputStream outputStream) throws IOException { this.commandSession = new CommandRegistry.CommandSession(terminal, terminal.input(), out, out); redirecting = true; } - + public void flush() { if (out == null) { return; @@ -404,6 +406,14 @@ public String getOutput() { return output; } + public boolean isRedirecting() { + return redirecting; + } + + public boolean isByteStream() { + return redirecting && byteOutputStream != null; + } + public void reset() { if (redirecting) { out = null; @@ -444,9 +454,11 @@ public Object execute(String line) throws Exception { break; } } + String rawLine = lastArg < words.size() ? words.subList(0, lastArg).stream().collect(Collectors.joining(" ")) : pl.line(); String[] argv = words.subList(1, lastArg).toArray(new String[0]); Object out = null; exception = null; + boolean statement = false; try { if ((var != null || toFile != null) && consoleId != null && !consoleEngine().isExecuting()) { if (toFile != null) { @@ -454,6 +466,7 @@ public Object execute(String line) throws Exception { } else { outputStream.redirect(); } + outputStream.open(); } if (isLocalCommand(cmd)) { out = localExecute(cmd, argv); @@ -467,20 +480,25 @@ public Object execute(String line) throws Exception { out = commandRegistries[id].execute(outputStream.getCommandSession(), cmd, argv); } } else if (consoleId != null) { - out = consoleEngine().execute(pl); + if (outputStream.isByteStream() && !consoleEngine().scripts().contains(cmd)) { + outputStream.close(); + outputStream.reset(); + statement = true; + } + out = consoleEngine().execute(cmd, rawLine, argv); } } } catch (HelpException e) { println(e); } finally { - if (consoleId != null && !consoleEngine().isExecuting()) { + if (consoleId != null && !consoleEngine().isExecuting() && !statement) { outputStream.flush(); out = consoleEngine().postProcess(pl.line(), out, outputStream.getOutput()); } } return out; } - + public void cleanUp() { if (consoleId == null) { return; @@ -490,8 +508,14 @@ public void cleanUp() { consoleEngine().purge(); } - private void println(Exception exception) { + @Override + public void println(Exception exception) { if (consoleId != null) { + if (outputStream.isRedirecting()) { + outputStream.close(); + outputStream.reset(); + } + consoleEngine().putVariable("exception", exception); consoleEngine().println(exception); } else { SystemRegistry.println(false, terminal(), exception); diff --git a/demo/src/main/java/org/jline/demo/Repl.java b/demo/src/main/java/org/jline/demo/Repl.java index bb063fba6..e47bf200b 100644 --- a/demo/src/main/java/org/jline/demo/Repl.java +++ b/demo/src/main/java/org/jline/demo/Repl.java @@ -308,8 +308,7 @@ public static void main(String[] args) { break; } catch (Exception e) { - consoleEngine.println(e); - scriptEngine.put("exception", e); // save exception to console variable + systemRegistry.println(e); // print exception and save it to console variable } } } From 9bf0efea8ed303e3e07b7773f3f83eb2e629ed19 Mon Sep 17 00:00:00 2001 From: mattirn Date: Fri, 7 Feb 2020 18:44:51 +0100 Subject: [PATCH 5/5] Groovy-REPL: A few small improvements --- .../org/jline/builtins/ConsoleEngine.java | 2 +- .../org/jline/builtins/ConsoleEngineImpl.java | 11 +++--- .../jline/builtins/SystemRegistryImpl.java | 39 ++++++++++++------- .../java/org/jline/script/GroovyEngine.java | 15 ++++--- 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java b/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java index 1717f85eb..f2dda46c1 100644 --- a/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java +++ b/builtins/src/main/java/org/jline/builtins/ConsoleEngine.java @@ -63,7 +63,7 @@ static String plainCommand(String command) { * Sets file name extension used by console scripts * @param extension console script file extension */ - public void setScriptExtension(String extension); + void setScriptExtension(String extension); /** * Returns true if alias 'name' exists diff --git a/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java b/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java index fc5471f04..b8aa6e520 100644 --- a/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java +++ b/builtins/src/main/java/org/jline/builtins/ConsoleEngineImpl.java @@ -241,7 +241,7 @@ public List scripts() { out.add(name.substring(0, name.lastIndexOf("."))); } } catch (Exception e) { - println(e); + systemRegistry.println(e); } return out; } @@ -614,7 +614,7 @@ public boolean executeWidget(Object function) { } engine.execute("_widgetFunction()"); } catch (Exception e) { - println(e); + systemRegistry.println(e); return false; } return true; @@ -628,7 +628,7 @@ private boolean splitCommandOutput() { out = (boolean) ((Map) engine.get(VAR_CONSOLE_OPTIONS)).getOrDefault("splitOutput", true); } } catch (Exception e) { - println(new Exception("Bad CONSOLE_OPTION value: " + e.getMessage())); + systemRegistry.println(new Exception("Bad CONSOLE_OPTION value: " + e.getMessage())); } return out; } @@ -700,7 +700,6 @@ private Map defaultPrntOptions() { @Override public void println(Object object) { Map options = defaultPrntOptions(); - options.putIfAbsent("exception", "message"); println(options, object); } @@ -746,9 +745,9 @@ private void highlight(int width, String style, String object) { } SyntaxHighlighter highlighter = nanorc != null ? SyntaxHighlighter.build(nanorc, style) : null; - for (String s: object.split("\n")) { + for (String s: object.split("\\r?\\n")) { AttributedStringBuilder asb = new AttributedStringBuilder(); - asb.append(s).setLength(width); + asb.append(s).subSequence(0, width); // setLength(width) fill nul-chars at the end of line if (highlighter != null) { highlighter.highlight(asb).println(terminal()); } else { diff --git a/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java b/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java index f492c5bce..f7b5ba785 100644 --- a/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java +++ b/builtins/src/main/java/org/jline/builtins/SystemRegistryImpl.java @@ -35,9 +35,12 @@ import org.jline.reader.impl.completer.ArgumentCompleter; import org.jline.reader.impl.completer.NullCompleter; import org.jline.reader.impl.completer.StringsCompleter; +import org.jline.terminal.Attributes; +import org.jline.terminal.Attributes.InputFlag; import org.jline.terminal.Terminal; import org.jline.terminal.TerminalBuilder; import org.jline.utils.AttributedStringBuilder; +import org.jline.utils.OSUtils; /** * Aggregate command registeries. @@ -355,7 +358,14 @@ public void open() throws IOException { System.setOut(out); System.setErr(out); in = new ByteArrayInputStream( "".getBytes() ); - terminal = TerminalBuilder.builder().streams(in, outputStream).type(Terminal.TYPE_DUMB).build(); + Attributes attrs = new Attributes(); + if (OSUtils.IS_WINDOWS) { + attrs.setInputFlag(InputFlag.IGNCR, true); + } + terminal = TerminalBuilder.builder() + .streams(in, outputStream) + .attributes(attrs) + .type(Terminal.TYPE_DUMB).build(); this.commandSession = new CommandRegistry.CommandSession(terminal, terminal.input(), out, out); redirecting = true; } @@ -460,10 +470,10 @@ public Object execute(String line) throws Exception { exception = null; boolean statement = false; try { - if ((var != null || toFile != null) && consoleId != null && !consoleEngine().isExecuting()) { + if (var != null || toFile != null) { if (toFile != null) { outputStream.redirect(toFile, append); - } else { + } else if (consoleId != null && !consoleEngine().isExecuting()) { outputStream.redirect(); } outputStream.open(); @@ -500,23 +510,26 @@ public Object execute(String line) throws Exception { } public void cleanUp() { - if (consoleId == null) { - return; + if (outputStream.isRedirecting()) { + outputStream.close(); + outputStream.reset(); + } + if (consoleId != null) { + consoleEngine().purge(); } - outputStream.close(); - outputStream.reset(); - consoleEngine().purge(); } @Override public void println(Exception exception) { + if (outputStream.isRedirecting()) { + outputStream.close(); + outputStream.reset(); + } if (consoleId != null) { - if (outputStream.isRedirecting()) { - outputStream.close(); - outputStream.reset(); - } consoleEngine().putVariable("exception", exception); - consoleEngine().println(exception); + Map options = new HashMap<>(); + options.put("exception", "message"); + consoleEngine().println(options, exception); } else { SystemRegistry.println(false, terminal(), exception); } diff --git a/groovy/src/main/java/org/jline/script/GroovyEngine.java b/groovy/src/main/java/org/jline/script/GroovyEngine.java index c2fe6c93d..d08c1e5b9 100644 --- a/groovy/src/main/java/org/jline/script/GroovyEngine.java +++ b/groovy/src/main/java/org/jline/script/GroovyEngine.java @@ -339,7 +339,7 @@ private List internalHighlight(Map options, Ob asb.append("\t"); } if (asb.columnLength() > width) { - asb.setLength(width); + asb.subSequence(0, width); } out.add(asb.toAttributedString()); Integer row = 0; @@ -356,7 +356,7 @@ private List internalHighlight(Map options, Ob asb2.append("\t"); } if (asb2.columnLength() > width) { - asb2.setLength(width); + asb2.subSequence(0, width); } out.add(asb2.toAttributedString()); } @@ -391,7 +391,7 @@ private List internalHighlight(Map options, Ob asb.append("\t"); } if (asb.columnLength() > width) { - asb.setLength(width); + asb.subSequence(0, width); } out.add(asb.toAttributedString()); } @@ -407,7 +407,7 @@ private List internalHighlight(Map options, Ob } asb.append(Utils.toString(o)); if (asb.columnLength() > width) { - asb.setLength(width); + asb.subSequence(0, width); } out.add(asb.toAttributedString()); } @@ -435,13 +435,16 @@ private List highlightMap(Map map, int width) for (Map.Entry entry : map.entrySet()) { AttributedStringBuilder asb = new AttributedStringBuilder().tabs(Arrays.asList(0, max + 1)); asb.append(entry.getKey(), AttributedStyle.DEFAULT.foreground(AttributedStyle.BLUE + AttributedStyle.BRIGHT)); - for (String v : Utils.toString(entry.getValue()).split("\n")) { + for (String v : Utils.toString(entry.getValue()).split("\\r?\\n")) { asb.append("\t"); asb.append(v, AttributedStyle.DEFAULT.foreground(AttributedStyle.YELLOW)); if (asb.columnLength() > width) { - asb.setLength(width); + asb.subSequence(0, width); } out.add(asb.toAttributedString()); + if (map.size() > 1) { + break; + } asb = new AttributedStringBuilder().tabs(Arrays.asList(0, max + 1)); } }