From 27de76521d3ee2e7685b88b986cecbeff4f028b2 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 14 Jun 2018 14:35:08 +0200 Subject: [PATCH] Provide a status bar, fixes #286 --- demo/etc/gosh_profile | 39 ++++++++ .../org/jline/reader/impl/LineReaderImpl.java | 10 ++ .../jline/terminal/impl/AbstractTerminal.java | 21 +++- .../src/main/java/org/jline/utils/Status.java | 96 +++++++++++++++++++ 4 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 terminal/src/main/java/org/jline/utils/Status.java diff --git a/demo/etc/gosh_profile b/demo/etc/gosh_profile index 3a0c56ae5..0bb2b7e63 100644 --- a/demo/etc/gosh_profile +++ b/demo/etc/gosh_profile @@ -3,6 +3,45 @@ if { $.reader } { source ((($.reader parser) class) getResource "/gosh_profile") } +status = { + p1 = $1 + if { "on" equals $p1 } { + __status_on + } { + if { "off" equals $p1 } { + ($.terminal status) update null + } { + echo "Error: use 'status on' or 'status off'." + } + } +} + +__status_on = { + sb = (new org.jline.utils.AttributedStringBuilder) + $sb style (($sb style) foreground 2) + cols = ($.terminal width) + each {1..$cols} { $sb append $'\u2500' } + s1 = ($sb toAttributedString) + $sb setLength 0 + $sb style (($sb style) foreground 0) + $sb append " " + $sb style (($sb style) inverse) + $sb append "^L" + $sb style (($sb style) inverseOff) + $sb append " Clear " + $sb style (($sb style) inverse) + $sb append "^D" + $sb style (($sb style) inverseOff) + $sb append " Exit " + $sb style (($sb style) inverse) + $sb append "^C" + $sb style (($sb style) inverseOff) + $sb append " Interrupt " + s2 = ($sb toAttributedString) + ($.terminal status) update [ $s1 $s2 ] +} + + __load_class_from = { (($1 class) classLoader) loadClass $2 } diff --git a/reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java b/reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java index f6e9cf2d3..11a1398a6 100644 --- a/reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java +++ b/reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java @@ -39,6 +39,7 @@ import org.jline.utils.InfoCmp.Capability; import org.jline.utils.Levenshtein; import org.jline.utils.Log; +import org.jline.utils.Status; import org.jline.utils.WCWidth; import static org.jline.keymap.KeyMap.alt; @@ -3460,6 +3461,11 @@ protected synchronized void redisplay(boolean flush) { return; } + Status status = Status.getStatus(terminal, false); + if (status != null) { + status.redraw(); + } + if (size.getRows() > 0 && size.getRows() < MIN_ROWS) { AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH); @@ -5180,6 +5186,10 @@ public boolean clear() { */ public boolean clearScreen() { if (terminal.puts(Capability.clear_screen)) { + Status status = Status.getStatus(terminal, false); + if (status != null) { + status.reset(); + } redrawLine(); } else { println(); diff --git a/terminal/src/main/java/org/jline/terminal/impl/AbstractTerminal.java b/terminal/src/main/java/org/jline/terminal/impl/AbstractTerminal.java index 6245b41e4..03ead209f 100644 --- a/terminal/src/main/java/org/jline/terminal/impl/AbstractTerminal.java +++ b/terminal/src/main/java/org/jline/terminal/impl/AbstractTerminal.java @@ -8,7 +8,6 @@ */ package org.jline.terminal.impl; -import java.io.IOError; import java.io.IOException; import java.io.InterruptedIOException; import java.nio.charset.Charset; @@ -32,6 +31,7 @@ import org.jline.utils.InfoCmp; import org.jline.utils.InfoCmp.Capability; import org.jline.utils.Log; +import org.jline.utils.Status; public abstract class AbstractTerminal implements Terminal { @@ -42,6 +42,7 @@ public abstract class AbstractTerminal implements Terminal { protected final Set bools = new HashSet<>(); protected final Map ints = new HashMap<>(); protected final Map strings = new HashMap<>(); + protected Status status; public AbstractTerminal(String name, String type) throws IOException { this(name, type, null, SignalHandler.SIG_DFL); @@ -56,6 +57,17 @@ public AbstractTerminal(String name, String type, Charset encoding, SignalHandle } } + public Status getStatus() { + return getStatus(true); + } + + public Status getStatus(boolean create) { + if (status == null && create) { + status = new Status(this); + } + return status; + } + public SignalHandler handle(Signal signal, SignalHandler handler) { Objects.requireNonNull(signal); Objects.requireNonNull(handler); @@ -68,9 +80,16 @@ public void raise(Signal signal) { if (handler != SignalHandler.SIG_DFL && handler != SignalHandler.SIG_IGN) { handler.handle(signal); } + if (status != null && signal == Signal.WINCH) { + status.resize(); + } } public void close() throws IOException { + if (status != null) { + status.update(null); + flush(); + } } protected void echoSignal(Signal signal) { diff --git a/terminal/src/main/java/org/jline/utils/Status.java b/terminal/src/main/java/org/jline/utils/Status.java new file mode 100644 index 000000000..ea011a6b2 --- /dev/null +++ b/terminal/src/main/java/org/jline/utils/Status.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2002-2018, the original author or authors. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * http://www.opensource.org/licenses/bsd-license.php + */ +package org.jline.utils; + +import java.util.Objects; +import java.util.Collections; +import java.util.ArrayList; +import java.util.List; +import org.jline.terminal.Terminal; +import org.jline.terminal.Terminal.Signal; +import org.jline.terminal.Terminal.SignalHandler; +import org.jline.terminal.impl.AbstractTerminal; +import org.jline.utils.InfoCmp.Capability; +import org.jline.terminal.Size; + +public class Status { + + protected final AbstractTerminal terminal; + protected final boolean supported; + protected List oldLines = Collections.emptyList(); + protected int rows; + protected int columns; + protected boolean force; + + public static Status getStatus(Terminal terminal) { + return getStatus(terminal, true); + } + + public static Status getStatus(Terminal terminal, boolean create) { + return terminal instanceof AbstractTerminal + ? ((AbstractTerminal) terminal).getStatus(create) + : null; + } + + + public Status(AbstractTerminal terminal) { + this.terminal = Objects.requireNonNull(terminal, "terminal can not be null"); + this.supported = terminal.getStringCapability(Capability.change_scroll_region) != null + && terminal.getStringCapability(Capability.save_cursor) != null + && terminal.getStringCapability(Capability.restore_cursor) != null + && terminal.getStringCapability(Capability.cursor_address) != null; + if (supported) { + resize(); + } + } + + public void resize() { + Size size = terminal.getSize(); + this.rows = size.getRows(); + this.columns = size.getColumns(); + this.force = true; + } + + public void reset() { + this.force = true; + } + + public void redraw() { + update(oldLines); + } + + public void update(List lines) { + if (lines == null) { + lines = Collections.emptyList(); + } + if (!supported || (oldLines.equals(lines) && !force)) { + return; + } + int nb = lines.size() - oldLines.size(); + if (nb > 0) { + for (int i = 0; i < nb; i++) { + terminal.puts(Capability.cursor_down); + } + for (int i = 0; i < nb; i++) { + terminal.puts(Capability.cursor_up); + } + } + terminal.puts(Capability.save_cursor); + terminal.puts(Capability.clr_eos); + for (int i = 0; i < lines.size(); i++) { + terminal.puts(Capability.cursor_address, rows - lines.size() + i, 0); + terminal.writer().write(lines.get(i).columnSubSequence(0, columns).toAnsi(terminal)); + } + terminal.puts(Capability.change_scroll_region, 0, rows - 1 - lines.size()); + terminal.puts(Capability.restore_cursor); + terminal.flush(); + oldLines = new ArrayList<>(lines); + force = false; + } +} \ No newline at end of file