SHL-74: Upgrade to JLine 2
This commit is contained in:
@@ -31,7 +31,8 @@ dependencies {
|
||||
compile "org.springframework:spring-core:$springVersion"
|
||||
compile "org.springframework:spring-context-support:$springVersion"
|
||||
compile "commons-io:commons-io:$commonsioVersion"
|
||||
compile "net.sourceforge.jline:jline:$jlineVersion"
|
||||
//compile "net.sourceforge.jline:jline:$jlineVersion"
|
||||
compile "jline:jline:2.11"
|
||||
compile "org.fusesource.jansi:jansi:$jansiVersion"
|
||||
compile "cglib:cglib:$cglibVersion"
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
package org.springframework.shell.commands;
|
||||
|
||||
import jline.ANSIBuffer;
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
|
||||
import org.fusesource.jansi.AnsiConsole;
|
||||
import org.springframework.shell.core.CommandMarker;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -25,15 +26,14 @@ import org.springframework.stereotype.Component;
|
||||
* Commands related to the manipulation of the jline console
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class ConsoleCommands implements CommandMarker {
|
||||
|
||||
@CliCommand(value = {"cls", "clear"}, help = "Clears the console")
|
||||
public class ConsoleCommands implements CommandMarker {
|
||||
|
||||
@CliCommand(value = { "cls", "clear" }, help = "Clears the console")
|
||||
public void clear() {
|
||||
System.out.print(ANSIBuffer.ANSICodes.clrscr());
|
||||
System.out.print(ANSIBuffer.ANSICodes.gotoxy(0, 0));
|
||||
AnsiConsole.out().println(ansi().eraseScreen().cursor(0, 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,17 +18,17 @@ package org.springframework.shell.core;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jline.Completor;
|
||||
import jline.console.completer.Completer;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An implementation of JLine's {@link Completor} interface that delegates to a {@link Parser}.
|
||||
*
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
public class JLineCompletorAdapter implements Completor {
|
||||
public class JLineCompletorAdapter implements Completer {
|
||||
|
||||
// Fields
|
||||
private final Parser parser;
|
||||
@@ -38,7 +38,7 @@ public class JLineCompletorAdapter implements Completor {
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes","unchecked"})
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public int complete(final String buffer, final int cursor, final List candidates) {
|
||||
int result;
|
||||
try {
|
||||
@@ -46,9 +46,12 @@ public class JLineCompletorAdapter implements Completor {
|
||||
List<Completion> completions = new ArrayList<Completion>();
|
||||
result = parser.completeAdvanced(buffer, cursor, completions);
|
||||
for (Completion completion : completions) {
|
||||
candidates.add(new jline.Completion(completion.getValue(), completion.getFormattedValue(), completion.getHeading()));
|
||||
// candidates.add(new jline.Completion(completion.getValue(), completion.getFormattedValue(), completion
|
||||
// .getHeading()));
|
||||
candidates.add(completion.getValue());
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
JLineLogHandler.prohibitRedraw();
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
import static org.fusesource.jansi.Ansi.Color.GREEN;
|
||||
import static org.fusesource.jansi.Ansi.Color.MAGENTA;
|
||||
import static org.fusesource.jansi.Ansi.Color.RED;
|
||||
import static org.springframework.shell.support.util.OsUtils.LINE_SEPARATOR;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -24,16 +28,16 @@ import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
import jline.ANSIBuffer;
|
||||
import jline.ConsoleReader;
|
||||
import jline.console.ConsoleReader;
|
||||
|
||||
import org.fusesource.jansi.Ansi;
|
||||
import org.fusesource.jansi.Ansi.Attribute;
|
||||
import org.springframework.shell.support.util.IOUtils;
|
||||
import org.springframework.shell.support.util.OsUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* JDK logging {@link Handler} that emits log messages to a JLine {@link ConsoleReader}.
|
||||
*
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 1.0
|
||||
*/
|
||||
@@ -41,18 +45,26 @@ public class JLineLogHandler extends Handler {
|
||||
|
||||
// Constants
|
||||
private static final boolean ROO_BRIGHT_COLORS = Boolean.getBoolean("roo.bright");
|
||||
private static final boolean SHELL_BRIGHT_COLORS = Boolean.getBoolean("spring.shell.bright");
|
||||
private static final boolean BRIGHT_COLORS = ROO_BRIGHT_COLORS || SHELL_BRIGHT_COLORS;
|
||||
|
||||
private static final boolean SHELL_BRIGHT_COLORS = Boolean.getBoolean("spring.shell.bright");
|
||||
|
||||
private static final boolean BRIGHT_COLORS = ROO_BRIGHT_COLORS || SHELL_BRIGHT_COLORS;
|
||||
|
||||
// Fields
|
||||
private ConsoleReader reader;
|
||||
|
||||
private ShellPromptAccessor shellPromptAccessor;
|
||||
|
||||
private static ThreadLocal<Boolean> redrawProhibit = new ThreadLocal<Boolean>();
|
||||
|
||||
private static String lastMessage;
|
||||
|
||||
private static boolean includeThreadName = false;
|
||||
|
||||
private boolean ansiSupported;
|
||||
|
||||
private String userInterfaceThreadName;
|
||||
|
||||
private static boolean suppressDuplicateMessages = true;
|
||||
|
||||
public JLineLogHandler(final ConsoleReader reader, final ShellPromptAccessor shellPromptAccessor) {
|
||||
@@ -61,7 +73,7 @@ public class JLineLogHandler extends Handler {
|
||||
this.reader = reader;
|
||||
this.shellPromptAccessor = shellPromptAccessor;
|
||||
this.userInterfaceThreadName = Thread.currentThread().getName();
|
||||
this.ansiSupported = reader.getTerminal().isANSISupported();
|
||||
this.ansiSupported = reader.getTerminal().isAnsiSupported();
|
||||
|
||||
setFormatter(new Formatter() {
|
||||
@Override
|
||||
@@ -77,8 +89,10 @@ public class JLineLogHandler extends Handler {
|
||||
pw = new PrintWriter(sw);
|
||||
record.getThrown().printStackTrace(pw);
|
||||
sb.append(sw.toString());
|
||||
} catch (Exception ex) {
|
||||
} finally {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
}
|
||||
finally {
|
||||
IOUtils.closeQuietly(pw);
|
||||
}
|
||||
}
|
||||
@@ -88,10 +102,12 @@ public class JLineLogHandler extends Handler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {}
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SecurityException {}
|
||||
public void close() throws SecurityException {
|
||||
}
|
||||
|
||||
public static void prohibitRedraw() {
|
||||
redrawProhibit.set(true);
|
||||
@@ -127,35 +143,35 @@ public class JLineLogHandler extends Handler {
|
||||
}
|
||||
lastMessage = toDisplay;
|
||||
|
||||
StringBuffer buffer = reader.getCursorBuffer().getBuffer();
|
||||
StringBuilder buffer = reader.getCursorBuffer().copy().buffer;
|
||||
int cursor = reader.getCursorBuffer().cursor;
|
||||
if (reader.getCursorBuffer().length() > 0) {
|
||||
// The user has semi-typed something, so put a new line in so the debug message is separated
|
||||
reader.printNewline();
|
||||
reader.println();
|
||||
|
||||
// We need to cancel whatever they typed (it's reset later on), so the line appears empty
|
||||
reader.getCursorBuffer().setBuffer(new StringBuffer());
|
||||
reader.getCursorBuffer().cursor = 0;
|
||||
reader.getCursorBuffer().clear();
|
||||
}
|
||||
|
||||
// This ensures nothing is ever displayed when redrawing the line
|
||||
reader.setDefaultPrompt("");
|
||||
reader.setPrompt("");
|
||||
reader.redrawLine();
|
||||
// Now restore the line formatting settings back to their original
|
||||
reader.setDefaultPrompt(shellPromptAccessor.getShellPrompt());
|
||||
reader.setPrompt(shellPromptAccessor.getShellPrompt());
|
||||
|
||||
reader.getCursorBuffer().setBuffer(buffer);
|
||||
reader.getCursorBuffer().write(buffer.toString());
|
||||
reader.getCursorBuffer().cursor = cursor;
|
||||
|
||||
reader.printString(toDisplay);
|
||||
reader.print(toDisplay);
|
||||
|
||||
Boolean prohibitingRedraw = redrawProhibit.get();
|
||||
if (prohibitingRedraw == null) {
|
||||
reader.redrawLine();
|
||||
}
|
||||
|
||||
reader.flushConsole();
|
||||
} catch (Exception e) {
|
||||
reader.flush();
|
||||
}
|
||||
catch (Exception e) {
|
||||
reportError("Could not publish log message", e, Level.SEVERE.intValue());
|
||||
}
|
||||
}
|
||||
@@ -165,7 +181,8 @@ public class JLineLogHandler extends Handler {
|
||||
|
||||
String threadName;
|
||||
String eventString;
|
||||
if (includeThreadName && !userInterfaceThreadName.equals(Thread.currentThread().getName()) && !"".equals(Thread.currentThread().getName())) {
|
||||
if (includeThreadName && !userInterfaceThreadName.equals(Thread.currentThread().getName())
|
||||
&& !"".equals(Thread.currentThread().getName())) {
|
||||
threadName = "[" + Thread.currentThread().getName() + "]";
|
||||
|
||||
// Build an event string that will indent nicely given the left hand side now contains a thread name
|
||||
@@ -174,26 +191,36 @@ public class JLineLogHandler extends Handler {
|
||||
lineSeparatorAndIndentingString.append(" ");
|
||||
}
|
||||
|
||||
eventString = " " + getFormatter().format(event).replace(LINE_SEPARATOR, LINE_SEPARATOR + lineSeparatorAndIndentingString.toString());
|
||||
eventString = " "
|
||||
+ getFormatter().format(event).replace(LINE_SEPARATOR,
|
||||
LINE_SEPARATOR + lineSeparatorAndIndentingString.toString());
|
||||
if (eventString.endsWith(lineSeparatorAndIndentingString.toString())) {
|
||||
eventString = eventString.substring(0, eventString.length() - lineSeparatorAndIndentingString.length());
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
threadName = "";
|
||||
eventString = getFormatter().format(event);
|
||||
}
|
||||
|
||||
if (ansiSupported) {
|
||||
Ansi ansi = ansi(sb);
|
||||
if (event.getLevel().intValue() >= Level.SEVERE.intValue()) {
|
||||
sb.append(getANSIBuffer().reverse(threadName).red(eventString));
|
||||
} else if (event.getLevel().intValue() >= Level.WARNING.intValue()) {
|
||||
sb.append(getANSIBuffer().reverse(threadName).magenta(eventString));
|
||||
} else if (event.getLevel().intValue() >= Level.INFO.intValue()) {
|
||||
sb.append(getANSIBuffer().reverse(threadName).green(eventString));
|
||||
} else {
|
||||
sb.append(getANSIBuffer().reverse(threadName).append(eventString));
|
||||
ansi.a(Attribute.NEGATIVE_ON).a(threadName).a(Attribute.NEGATIVE_OFF).fg(RED).a(eventString).reset();
|
||||
}
|
||||
} else {
|
||||
else if (event.getLevel().intValue() >= Level.WARNING.intValue()) {
|
||||
ansi.a(Attribute.NEGATIVE_ON).a(threadName).a(Attribute.NEGATIVE_OFF).fg(MAGENTA).a(eventString)
|
||||
.reset();
|
||||
}
|
||||
else if (event.getLevel().intValue() >= Level.INFO.intValue()) {
|
||||
ansi.a(Attribute.NEGATIVE_ON).a(threadName).a(Attribute.NEGATIVE_OFF).fg(GREEN).a(eventString).reset();
|
||||
}
|
||||
else {
|
||||
ansi.a(Attribute.NEGATIVE_ON).a(threadName).a(Attribute.NEGATIVE_OFF).a(eventString);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
sb.append(threadName).append(eventString);
|
||||
}
|
||||
|
||||
@@ -201,30 +228,30 @@ public class JLineLogHandler extends Handler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes text brighter if requested through system property 'roo.bright' and
|
||||
* works around issue on Windows in using reverse() in combination with the
|
||||
* Jansi lib, which leaves its 'negative' flag set unless reset explicitly.
|
||||
*
|
||||
* Makes text brighter if requested through system property 'roo.bright' and works around issue on Windows in using
|
||||
* reverse() in combination with the Jansi lib, which leaves its 'negative' flag set unless reset explicitly.
|
||||
*
|
||||
* @return new patched ANSIBuffer
|
||||
*/
|
||||
public static ANSIBuffer getANSIBuffer() {
|
||||
final char esc = (char) 27;
|
||||
return new ANSIBuffer() {
|
||||
@Override
|
||||
public ANSIBuffer reverse(final String str) {
|
||||
if (OsUtils.isWindows()) {
|
||||
return super.reverse(str).append(ANSICodes.attrib(esc));
|
||||
}
|
||||
return super.reverse(str);
|
||||
};
|
||||
@Override
|
||||
public ANSIBuffer attrib(final String str, final int code) {
|
||||
if (BRIGHT_COLORS && 30 <= code && code <= 37) {
|
||||
// This is a color code: add a 'bright' code
|
||||
return append(esc + "[" + code + ";1m").append(str).append(ANSICodes.attrib(0));
|
||||
}
|
||||
return super.attrib(str, code);
|
||||
}
|
||||
};
|
||||
}
|
||||
// public static ANSIBuffer getANSIBuffer() {
|
||||
// final char esc = (char) 27;
|
||||
// return new ANSIBuffer() {
|
||||
// @Override
|
||||
// public ANSIBuffer reverse(final String str) {
|
||||
// if (OsUtils.isWindows()) {
|
||||
// return super.reverse(str).append(ANSICodes.attrib(esc));
|
||||
// }
|
||||
// return super.reverse(str);
|
||||
// };
|
||||
//
|
||||
// @Override
|
||||
// public ANSIBuffer attrib(final String str, final int code) {
|
||||
// if (BRIGHT_COLORS && 30 <= code && code <= 37) {
|
||||
// // This is a color code: add a 'bright' code
|
||||
// return append(esc + "[" + code + ";1m").append(str).append(ANSICodes.attrib(0));
|
||||
// }
|
||||
// return super.attrib(str, code);
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -15,14 +15,15 @@
|
||||
*/
|
||||
package org.springframework.shell.core;
|
||||
|
||||
import static org.fusesource.jansi.Ansi.ansi;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -37,12 +38,16 @@ import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import jline.ANSIBuffer;
|
||||
import jline.ANSIBuffer.ANSICodes;
|
||||
import jline.ConsoleReader;
|
||||
import jline.WindowsTerminal;
|
||||
import jline.console.ConsoleReader;
|
||||
import jline.console.history.MemoryHistory;
|
||||
|
||||
import org.apache.commons.io.input.ReversedLinesFileReader;
|
||||
import org.fusesource.jansi.Ansi;
|
||||
import org.fusesource.jansi.Ansi.Attribute;
|
||||
import org.fusesource.jansi.Ansi.Color;
|
||||
import org.fusesource.jansi.Ansi.Erase;
|
||||
import org.fusesource.jansi.AnsiConsole;
|
||||
import org.springframework.shell.event.ShellStatus;
|
||||
import org.springframework.shell.event.ShellStatus.Status;
|
||||
import org.springframework.shell.event.ShellStatusListener;
|
||||
@@ -54,16 +59,16 @@ import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Uses the feature-rich <a href="http://sourceforge.net/projects/jline/">JLine</a> library to provide an interactive shell.
|
||||
*
|
||||
* Uses the feature-rich <a href="http://sourceforge.net/projects/jline/">JLine</a> library to provide an interactive
|
||||
* shell.
|
||||
*
|
||||
* <p>
|
||||
* Due to Windows' lack of color ANSI services out-of-the-box, this implementation automatically detects the classpath
|
||||
* presence of <a href="http://jansi.fusesource.org/">Jansi</a> and uses it if present. This library is not necessary
|
||||
* for *nix machines, which support colour ANSI without any special effort. This implementation has been written to
|
||||
* use reflection in order to avoid hard dependencies on Jansi.
|
||||
*
|
||||
* for *nix machines, which support colour ANSI without any special effort. This implementation has been written to use
|
||||
* reflection in order to avoid hard dependencies on Jansi.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Jarred Li
|
||||
* @since 1.0
|
||||
@@ -72,20 +77,31 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
|
||||
// Constants
|
||||
private static final String ANSI_CONSOLE_CLASSNAME = "org.fusesource.jansi.AnsiConsole";
|
||||
|
||||
private static final boolean JANSI_AVAILABLE = ClassUtils.isPresent(ANSI_CONSOLE_CLASSNAME,
|
||||
JLineShell.class.getClassLoader());
|
||||
|
||||
private static final char ESCAPE = 27;
|
||||
|
||||
private static final String BEL = "\007";
|
||||
|
||||
// Fields
|
||||
protected ConsoleReader reader;
|
||||
|
||||
private boolean developmentMode = false;
|
||||
|
||||
private FileWriter fileLog;
|
||||
|
||||
private final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
protected ShellStatusListener statusListener; // ROO-836
|
||||
|
||||
/** key: slot name, value: flashInfo instance */
|
||||
private final Map<String, FlashInfo> flashInfoMap = new HashMap<String, FlashInfo>();
|
||||
|
||||
/** key: row number, value: eraseLineFromPosition */
|
||||
private final Map<Integer, Integer> rowErasureMap = new HashMap<Integer, Integer>();
|
||||
|
||||
private boolean shutdownHookFired = false; // ROO-1599
|
||||
|
||||
private int historySize;
|
||||
@@ -101,7 +117,7 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
removeHandlers(mainLogger);
|
||||
mainLogger.addHandler(handler);
|
||||
|
||||
reader.addCompletor(new JLineCompletorAdapter(getParser()));
|
||||
reader.addCompleter(new JLineCompletorAdapter(getParser()));
|
||||
|
||||
reader.setBellEnabled(true);
|
||||
if (Boolean.getBoolean("jline.nobell")) {
|
||||
@@ -111,11 +127,11 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
// reader.setDebug(new PrintWriter(new FileWriter("writer.debug", true)));
|
||||
|
||||
openFileLogIfPossible();
|
||||
this.reader.getHistory().setMaxSize(getHistorySize());
|
||||
((MemoryHistory) this.reader.getHistory()).setMaxSize(getHistorySize());
|
||||
// Try to build previous command history from the project's log
|
||||
String[] filteredLogEntries = filterLogEntry();
|
||||
for (String logEntry : filteredLogEntries) {
|
||||
reader.getHistory().addToHistory(logEntry);
|
||||
reader.getHistory().add(logEntry);
|
||||
}
|
||||
|
||||
flashMessageRenderer();
|
||||
@@ -136,7 +152,8 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
shutdownHookFired = true;
|
||||
}
|
||||
}, getProductName() + " JLine Shutdown Hook"));
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
catch (Throwable t) {
|
||||
}
|
||||
|
||||
// Handle any "execute-then-quit" operation
|
||||
@@ -161,14 +178,14 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
|
||||
/**
|
||||
* read history commands from history log. the history size if determined by --histsize options.
|
||||
*
|
||||
*
|
||||
* @return history commands
|
||||
*/
|
||||
private String[] filterLogEntry() {
|
||||
ArrayList<String> entries = new ArrayList<String>();
|
||||
try {
|
||||
ReversedLinesFileReader reader = new ReversedLinesFileReader(
|
||||
new File(getHistoryFileName()),4096,Charset.forName("UTF-8"));
|
||||
ReversedLinesFileReader reader = new ReversedLinesFileReader(new File(getHistoryFileName()), 4096,
|
||||
Charset.forName("UTF-8"));
|
||||
int size = 0;
|
||||
String line = null;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
@@ -182,19 +199,19 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warning("read history file failed. Reason:"+ e.getMessage());
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.warning("read history file failed. Reason:" + e.getMessage());
|
||||
}
|
||||
Collections.reverse(entries);
|
||||
return entries.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new jline ConsoleReader. On Windows if jansi is available, uses
|
||||
* createAnsiWindowsReader(). Otherwise, always creates a default ConsoleReader.
|
||||
* Sub-classes of this class can plug in their version of ConsoleReader
|
||||
* by overriding this method, if required.
|
||||
*
|
||||
* Creates new jline ConsoleReader. On Windows if jansi is available, uses createAnsiWindowsReader(). Otherwise,
|
||||
* always creates a default ConsoleReader. Sub-classes of this class can plug in their version of ConsoleReader by
|
||||
* overriding this method, if required.
|
||||
*
|
||||
* @return a jline ConsoleReader instance
|
||||
*/
|
||||
protected ConsoleReader createConsoleReader() {
|
||||
@@ -202,16 +219,18 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
try {
|
||||
if (isJansiAvailable()) {
|
||||
try {
|
||||
consoleReader = createAnsiWindowsReader();
|
||||
} catch (Exception e) {
|
||||
consoleReader = createAnsiWindowsReader();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Try again using default ConsoleReader constructor
|
||||
logger.warning("Can't initialize jansi AnsiConsole, falling back to default: " + e);
|
||||
}
|
||||
}
|
||||
if (consoleReader == null) {
|
||||
consoleReader = new ConsoleReader();
|
||||
consoleReader = new ConsoleReader();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new IllegalStateException("Cannot start console class", ioe);
|
||||
}
|
||||
return consoleReader;
|
||||
@@ -244,19 +263,20 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
|
||||
@Override
|
||||
public void setPromptPath(final String path, final boolean overrideStyle) {
|
||||
if (reader.getTerminal().isANSISupported()) {
|
||||
ANSIBuffer ansi = JLineLogHandler.getANSIBuffer();
|
||||
if (reader.getTerminal().isAnsiSupported()) {
|
||||
// ANSIBuffer ansi = JLineLogHandler.getANSIBuffer();
|
||||
Ansi ansi = ansi();
|
||||
if (path == null || "".equals(path)) {
|
||||
shellPrompt = ansi.yellow(getPromptText()).toString();
|
||||
shellPrompt = ansi.fg(Color.YELLOW).a(getPromptText()).reset().toString();
|
||||
}
|
||||
else {
|
||||
if (overrideStyle) {
|
||||
ansi.append(path);
|
||||
ansi.a(path);
|
||||
}
|
||||
else {
|
||||
ansi.cyan(path);
|
||||
ansi.fg(Color.CYAN).a(path).reset();
|
||||
}
|
||||
shellPrompt = ansi.yellow(" " + getPromptText()).toString();
|
||||
shellPrompt = ansi.fg(Color.YELLOW).a(" " + getPromptText()).toString();
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -265,20 +285,20 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
}
|
||||
|
||||
// The shellPrompt is now correct; let's ensure it now gets used
|
||||
reader.setDefaultPrompt(JLineShell.shellPrompt);
|
||||
reader.setPrompt(AbstractShell.shellPrompt);
|
||||
}
|
||||
|
||||
protected ConsoleReader createAnsiWindowsReader() throws Exception {
|
||||
// Get decorated OutputStream that parses ANSI-codes
|
||||
final PrintStream ansiOut = (PrintStream) ClassUtils.forName(ANSI_CONSOLE_CLASSNAME,
|
||||
JLineShell.class.getClassLoader()).getMethod("out").invoke(null);
|
||||
final PrintStream ansiOut = (PrintStream) ClassUtils
|
||||
.forName(ANSI_CONSOLE_CLASSNAME, JLineShell.class.getClassLoader()).getMethod("out").invoke(null);
|
||||
WindowsTerminal ansiTerminal = new WindowsTerminal() {
|
||||
@Override
|
||||
public boolean isANSISupported() {
|
||||
public synchronized boolean isAnsiSupported() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
ansiTerminal.initializeTerminal();
|
||||
ansiTerminal.init();
|
||||
// Make sure to reset the original shell's colors on shutdown by closing the stream
|
||||
statusListener = new ShellStatusListener() {
|
||||
public void onShellStatusChange(final ShellStatus oldStatus, final ShellStatus newStatus) {
|
||||
@@ -289,14 +309,17 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
};
|
||||
addShellStatusListener(statusListener);
|
||||
|
||||
return new ConsoleReader(new FileInputStream(FileDescriptor.in), new PrintWriter(new OutputStreamWriter(
|
||||
ansiOut,
|
||||
// Default to Cp850 encoding for Windows console output (ROO-439)
|
||||
System.getProperty("jline.WindowsTerminal.output.encoding", "Cp850"))), null, ansiTerminal);
|
||||
// return new ConsoleReader(new FileInputStream(FileDescriptor.in), new PrintWriter(new OutputStreamWriter(
|
||||
// ansiOut,
|
||||
// // Default to Cp850 encoding for Windows console output (ROO-439)
|
||||
// System.getProperty("jline.WindowsTerminal.output.encoding", "Cp850"))), null, ansiTerminal);
|
||||
|
||||
OutputStream out = AnsiConsole.wrapOutputStream(ansiOut);
|
||||
return new ConsoleReader(new FileInputStream(FileDescriptor.in), out, ansiTerminal);
|
||||
}
|
||||
|
||||
private void flashMessageRenderer() {
|
||||
if (!reader.getTerminal().isANSISupported()) {
|
||||
if (!reader.getTerminal().isAnsiSupported()) {
|
||||
return;
|
||||
}
|
||||
// Setup a thread to ensure flash messages are displayed and cleared correctly
|
||||
@@ -326,7 +349,8 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
}
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
} catch (InterruptedException ignore) {
|
||||
}
|
||||
catch (InterruptedException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -341,25 +365,25 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
Assert.hasText(slot, "Slot name must be specified for a flash message");
|
||||
|
||||
if (Shell.WINDOW_TITLE_SLOT.equals(slot)) {
|
||||
if (reader != null && reader.getTerminal().isANSISupported()) {
|
||||
if (reader != null && reader.getTerminal().isAnsiSupported()) {
|
||||
// We can probably update the window title, as requested
|
||||
if (!StringUtils.hasText(message)) {
|
||||
System.out.println("No text");
|
||||
}
|
||||
|
||||
ANSIBuffer buff = JLineLogHandler.getANSIBuffer();
|
||||
buff.append(ESCAPE + "]0;").append(message).append(BEL);
|
||||
String stg = buff.toString();
|
||||
Ansi ansi = ansi();
|
||||
ansi.a(ESCAPE + "]0;").a(message).a(BEL);
|
||||
try {
|
||||
reader.printString(stg);
|
||||
reader.flushConsole();
|
||||
} catch (IOException ignored) {
|
||||
reader.print(ansi.toString());
|
||||
reader.flush();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if ((reader != null && !reader.getTerminal().isANSISupported())) {
|
||||
if ((reader != null && !reader.getTerminal().isAnsiSupported())) {
|
||||
super.flash(level, message, slot);
|
||||
return;
|
||||
}
|
||||
@@ -408,12 +432,12 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
|
||||
// Externally synchronized via the two calling methods having a mutex on flashInfoMap
|
||||
private void doAnsiFlash(final int row, final Level level, final String message) {
|
||||
ANSIBuffer buff = JLineLogHandler.getANSIBuffer();
|
||||
Ansi ansi = ansi();
|
||||
if (isAppleTerminal()) {
|
||||
buff.append(ESCAPE + "7");
|
||||
ansi.a(ESCAPE + "7");
|
||||
}
|
||||
else {
|
||||
buff.append(ANSICodes.save());
|
||||
ansi.saveCursorPosition();
|
||||
}
|
||||
|
||||
// Figure out the longest line we're presently displaying (or were) and erase the line from that position
|
||||
@@ -428,8 +452,8 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
// There is nothing to erase
|
||||
}
|
||||
else {
|
||||
buff.append(ANSICodes.gotoxy(row, mostFurtherLeftColNumber));
|
||||
buff.append(ANSICodes.clreol()); // Clear what was present on the line
|
||||
ansi.cursor(row, mostFurtherLeftColNumber);
|
||||
ansi.eraseLine(Erase.FORWARD); // Clear what was present on the line
|
||||
}
|
||||
|
||||
if (("".equals(message))) {
|
||||
@@ -442,27 +466,27 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
return; // ROO-1599
|
||||
}
|
||||
// They want some message displayed
|
||||
int startFrom = reader.getTermwidth() - message.length() + 1;
|
||||
int startFrom = reader.getTerminal().getWidth() - message.length() + 1;
|
||||
if (startFrom < 1) {
|
||||
startFrom = 1;
|
||||
}
|
||||
buff.append(ANSICodes.gotoxy(row, startFrom));
|
||||
buff.reverse(message);
|
||||
ansi.cursor(row, startFrom);
|
||||
ansi.a(Attribute.NEGATIVE_ON).a(message).a(Attribute.NEGATIVE_OFF);
|
||||
// Record we want to erase from this positioning next time (so we clean up after ourselves)
|
||||
rowErasureMap.put(row, startFrom);
|
||||
}
|
||||
if (isAppleTerminal()) {
|
||||
buff.append(ESCAPE + "8");
|
||||
ansi.a(ESCAPE + "8");
|
||||
}
|
||||
else {
|
||||
buff.append(ANSICodes.restore());
|
||||
ansi.reset();
|
||||
}
|
||||
|
||||
String stg = buff.toString();
|
||||
try {
|
||||
reader.printString(stg);
|
||||
reader.flushConsole();
|
||||
} catch (IOException ignored) {
|
||||
reader.print(ansi.toString());
|
||||
reader.flush();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,9 +511,10 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
prompt = newPrmpt;
|
||||
setPromptPath(null);
|
||||
}
|
||||
//System.out.println("executed command:" + line);
|
||||
// System.out.println("executed command:" + line);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new IllegalStateException("Shell line reading failure", ioe);
|
||||
}
|
||||
setShellStatus(Status.SHUTTING_DOWN);
|
||||
@@ -497,7 +522,8 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
|
||||
public void setDevelopmentMode(final boolean developmentMode) {
|
||||
JLineLogHandler.setIncludeThreadName(developmentMode);
|
||||
JLineLogHandler.setSuppressDuplicateMessages(!developmentMode); // We want to see duplicate messages during development time (ROO-1873)
|
||||
JLineLogHandler.setSuppressDuplicateMessages(!developmentMode); // We want to see duplicate messages during
|
||||
// development time (ROO-1873)
|
||||
this.developmentMode = developmentMode;
|
||||
}
|
||||
|
||||
@@ -509,14 +535,14 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
try {
|
||||
fileLog = new FileWriter(getHistoryFileName(), true);
|
||||
// First write, so let's record the date and time of the first user command
|
||||
fileLog.write("// " + getProductName() + " " + versionInfo() + " log opened at " + df.format(new Date()) + "\n");
|
||||
fileLog.write("// " + getProductName() + " " + versionInfo() + " log opened at " + df.format(new Date())
|
||||
+ "\n");
|
||||
fileLog.flush();
|
||||
} catch (IOException ignoreIt) {
|
||||
}
|
||||
catch (IOException ignoreIt) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void logCommandToOutput(final String processedLine) {
|
||||
if (fileLog == null) {
|
||||
@@ -531,17 +557,19 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
fileLog.flush(); // So tail -f will show it's working
|
||||
if (getExitShellRequest() != null) {
|
||||
// Shutting down, so close our file (we can always reopen it later if needed)
|
||||
fileLog.write("// " + getProductName() + " " + versionInfo() + " log closed at " + df.format(new Date()) + "\n");
|
||||
fileLog.write("// " + getProductName() + " " + versionInfo() + " log closed at "
|
||||
+ df.format(new Date()) + "\n");
|
||||
IOUtils.closeQuietly(fileLog);
|
||||
fileLog = null;
|
||||
}
|
||||
} catch (IOException ignoreIt) {
|
||||
}
|
||||
catch (IOException ignoreIt) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the "roo.home" from the system property, falling back to the current working directory if missing.
|
||||
*
|
||||
*
|
||||
* @return the 'roo.home' system property
|
||||
*/
|
||||
@Override
|
||||
@@ -550,7 +578,8 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
if (rooHome == null) {
|
||||
try {
|
||||
rooHome = new File(".").getCanonicalPath();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
@@ -561,7 +590,8 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
* Should be called by a subclass before deactivating the shell.
|
||||
*/
|
||||
protected void closeShell() {
|
||||
// Notify we're closing down (normally our status is already shutting_down, but if it was a CTRL+C via the o.s.r.bootstrap.Main hook)
|
||||
// Notify we're closing down (normally our status is already shutting_down, but if it was a CTRL+C via the
|
||||
// o.s.r.bootstrap.Main hook)
|
||||
setShellStatus(Status.SHUTTING_DOWN);
|
||||
if (statusListener != null) {
|
||||
removeShellStatusListener(statusListener);
|
||||
@@ -570,15 +600,18 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
|
||||
private static class FlashInfo {
|
||||
String flashMessage;
|
||||
|
||||
long flashMessageUntil;
|
||||
|
||||
Level flashLevel;
|
||||
|
||||
int rowNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* get history file name from provider. The provider has highest order
|
||||
* <link>org.springframework.core.Ordered.getOder</link> will win.
|
||||
*
|
||||
*
|
||||
* @return history file name
|
||||
*/
|
||||
abstract protected String getHistoryFileName();
|
||||
@@ -586,21 +619,21 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
/**
|
||||
* get prompt text from provider. The provider has highest order
|
||||
* <link>org.springframework.core.Ordered.getOder</link> will win.
|
||||
*
|
||||
*
|
||||
* @return prompt text
|
||||
*/
|
||||
abstract protected String getPromptText();
|
||||
|
||||
/**
|
||||
* get product name
|
||||
*
|
||||
*
|
||||
* @return Product Name
|
||||
*/
|
||||
abstract protected String getProductName();
|
||||
|
||||
/**
|
||||
* get version information
|
||||
*
|
||||
*
|
||||
* @return Version
|
||||
*/
|
||||
protected String getVersion() {
|
||||
@@ -620,11 +653,10 @@ public abstract class JLineShell extends AbstractShell implements Shell, Runnabl
|
||||
public void setHistorySize(int historySize) {
|
||||
this.historySize = historySize;
|
||||
}
|
||||
|
||||
private static boolean isAppleTerminal()
|
||||
{
|
||||
final String terminalName = System.getenv( "TERM_PROGRAM" );
|
||||
return ("Apple_Terminal".equalsIgnoreCase( terminalName ) || Boolean.getBoolean("is.apple.terminal"));
|
||||
|
||||
private static boolean isAppleTerminal() {
|
||||
final String terminalName = System.getenv("TERM_PROGRAM");
|
||||
return ("Apple_Terminal".equalsIgnoreCase(terminalName) || Boolean.getBoolean("is.apple.terminal"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user