-
-
Notifications
You must be signed in to change notification settings - Fork 970
Upgrade JLine to 3.30.6 and Jansi to 2.4.2 #15367
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 8.0.x
Are you sure you want to change the base?
Changes from 2 commits
b4e78a0
d86a398
0cf49b2
637ea00
a512c1b
65da396
640cc8e
dd91f44
17318e3
abb80cb
63b864d
4a83a75
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,39 +18,34 @@ | |
| */ | ||
| package grails.build.logging; | ||
|
|
||
| import java.io.ByteArrayOutputStream; | ||
| import java.io.File; | ||
| import java.io.Flushable; | ||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.io.PrintStream; | ||
| import java.io.PrintWriter; | ||
| import java.io.StringWriter; | ||
| import java.util.Collection; | ||
| import java.util.List; | ||
| import java.util.Stack; | ||
|
|
||
| import org.codehaus.groovy.runtime.DefaultGroovyMethods; | ||
| import org.codehaus.groovy.runtime.StackTraceUtils; | ||
| import org.codehaus.groovy.runtime.typehandling.NumberMath; | ||
|
|
||
| import jline.Terminal; | ||
| import jline.TerminalFactory; | ||
| import jline.UnixTerminal; | ||
| import jline.console.ConsoleReader; | ||
| import jline.console.completer.Completer; | ||
| import jline.console.history.FileHistory; | ||
| import jline.console.history.History; | ||
| import jline.internal.Log; | ||
| import jline.internal.ShutdownHooks; | ||
| import jline.internal.TerminalLineSettings; | ||
| import org.apache.tools.ant.BuildException; | ||
| import org.fusesource.jansi.Ansi; | ||
| import org.fusesource.jansi.Ansi.Color; | ||
| import org.fusesource.jansi.AnsiConsole; | ||
| import org.jline.reader.Completer; | ||
| import org.jline.reader.History; | ||
| import org.jline.reader.LineReader; | ||
| import org.jline.reader.LineReaderBuilder; | ||
| import org.jline.reader.impl.LineReaderImpl; | ||
| import org.jline.reader.impl.completer.AggregateCompleter; | ||
| import org.jline.reader.impl.history.DefaultHistory; | ||
| import org.jline.terminal.Terminal; | ||
| import org.jline.terminal.TerminalBuilder; | ||
|
|
||
| import grails.util.Environment; | ||
| import org.grails.build.interactive.CandidateListCompletionHandler; | ||
| import org.grails.build.logging.GrailsConsoleErrorPrintStream; | ||
| import org.grails.build.logging.GrailsConsolePrintStream; | ||
|
|
||
|
|
@@ -114,7 +109,7 @@ public class GrailsConsole implements ConsoleLogger { | |
| /** | ||
| * The reader to read info from the console | ||
| */ | ||
| ConsoleReader reader; | ||
| LineReader reader; | ||
|
|
||
| Terminal terminal; | ||
|
|
||
|
|
@@ -123,6 +118,11 @@ public class GrailsConsole implements ConsoleLogger { | |
|
|
||
| History history; | ||
|
|
||
| /** | ||
| * List of completers to be aggregated for tab completion | ||
| */ | ||
| private final List<Completer> completers = new java.util.ArrayList<>(); | ||
|
|
||
| /** | ||
| * The category of the current output | ||
| */ | ||
|
|
@@ -179,8 +179,8 @@ protected GrailsConsole() throws IOException { | |
| * @throws IOException | ||
| */ | ||
| public void reinitialize(InputStream systemIn, PrintStream systemOut, PrintStream systemErr) throws IOException { | ||
| if (reader != null) { | ||
| reader.shutdown(); | ||
| if (terminal != null) { | ||
| terminal.close(); | ||
| } | ||
| initialize(systemIn, systemOut, systemErr); | ||
| } | ||
|
|
@@ -190,25 +190,31 @@ protected void initialize(InputStream systemIn, PrintStream systemOut, PrintStre | |
|
|
||
| redirectSystemOutAndErr(true); | ||
|
|
||
| System.setProperty(ShutdownHooks.JLINE_SHUTDOWNHOOK, "false"); | ||
|
|
||
| if (isInteractiveEnabled()) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Behavior change from |
||
| reader = createConsoleReader(systemIn); | ||
| reader.setBellEnabled(false); | ||
| reader.setCompletionHandler(new CandidateListCompletionHandler()); | ||
| if (isActivateTerminal()) { | ||
| terminal = createTerminal(); | ||
| } | ||
|
|
||
| terminal = createTerminal(); | ||
| history = prepareHistory(); | ||
| if (history != null) { | ||
| reader.setHistory(history); | ||
| } | ||
| reader = createLineReader(terminal, history); | ||
| initializeHistory(); | ||
| } else if (isActivateTerminal()) { | ||
| terminal = createTerminal(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Initializes history by attaching it to the reader and loading existing entries. | ||
| * This must be called after the LineReader is fully constructed. | ||
| */ | ||
| private void initializeHistory() { | ||
| if (history instanceof DefaultHistory && reader != null) { | ||
| DefaultHistory defaultHistory = (DefaultHistory) history; | ||
| try { | ||
| defaultHistory.attach(reader); | ||
| } catch (Exception e) { | ||
| // History initialization failed, continue without persistent history | ||
| } | ||
| } | ||
| } | ||
|
|
||
| protected void bindSystemOutAndErr(PrintStream systemOut, PrintStream systemErr) { | ||
| originalSystemOut = unwrapPrintStream(systemOut); | ||
| out = originalSystemOut; | ||
|
|
@@ -251,51 +257,59 @@ private boolean readPropOrTrue(String prop) { | |
| return property == null ? true : Boolean.valueOf(property); | ||
| } | ||
|
|
||
| protected ConsoleReader createConsoleReader(InputStream systemIn) throws IOException { | ||
| // need to swap out the output to avoid logging during init | ||
| final PrintStream nullOutput = new PrintStream(new ByteArrayOutputStream()); | ||
| final PrintStream originalOut = Log.getOutput(); | ||
| try { | ||
| Log.setOutput(nullOutput); | ||
| ConsoleReader consoleReader = new ConsoleReader(systemIn, out); | ||
| consoleReader.setExpandEvents(false); | ||
| return consoleReader; | ||
| } finally { | ||
| Log.setOutput(originalOut); | ||
| protected LineReader createLineReader(Terminal terminal, History history) throws IOException { | ||
| LineReaderBuilder builder = LineReaderBuilder.builder() | ||
| .terminal(terminal) | ||
| .option(LineReader.Option.DISABLE_EVENT_EXPANSION, true); | ||
| if (history != null) { | ||
| builder.variable(LineReader.HISTORY_FILE, new File(System.getProperty("user.home"), HISTORYFILE).toPath()); | ||
| builder.history(history); | ||
| } | ||
| return builder.build(); | ||
| } | ||
|
|
||
| /** | ||
| * Creates the instance of Terminal used directly in GrailsConsole. Note that there is also | ||
| * another terminal instance created implicitly inside of ConsoleReader. That instance | ||
| * is controlled by the jline.terminal system property. | ||
| * Creates the instance of Terminal used directly in GrailsConsole. | ||
| */ | ||
| protected Terminal createTerminal() { | ||
| terminal = TerminalFactory.create(); | ||
| if (isWindows()) { | ||
| terminal.setEchoEnabled(true); | ||
| } | ||
| protected Terminal createTerminal() throws IOException { | ||
| Terminal terminal = TerminalBuilder.builder() | ||
| .system(true) | ||
| .build(); | ||
| return terminal; | ||
| } | ||
|
|
||
| public void resetCompleters() { | ||
| final ConsoleReader reader = getReader(); | ||
| if (reader != null) { | ||
| Collection<Completer> completers = reader.getCompleters(); | ||
| for (Completer completer : completers) { | ||
| reader.removeCompleter(completer); | ||
| } | ||
| completers.clear(); | ||
| updateCompleter(); | ||
| } | ||
|
|
||
| // for some unknown reason / bug in JLine you have to iterate over twice to clear the completers (WTF) | ||
| completers = reader.getCompleters(); | ||
| for (Completer completer : completers) { | ||
| reader.removeCompleter(completer); | ||
| } | ||
| public void addCompleter(Completer completer) { | ||
| if (completer != null) { | ||
| completers.add(completer); | ||
| updateCompleter(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Prepares a history file to be used by the ConsoleReader. This file | ||
| * Updates the LineReader completer using an AggregateCompleter when needed. | ||
| */ | ||
| private void updateCompleter() { | ||
| if (reader == null) { | ||
| return; | ||
| } | ||
| if (!(reader instanceof LineReaderImpl)) { | ||
| return; | ||
| } | ||
| LineReaderImpl lineReader = (LineReaderImpl) reader; | ||
| if (completers.isEmpty()) { | ||
| lineReader.setCompleter(null); | ||
| } else { | ||
| lineReader.setCompleter(new AggregateCompleter(completers)); | ||
| } | ||
| } | ||
jamesfredley marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Prepares a history file to be used by the LineReader. This file | ||
| * will live in the home directory of the user. | ||
| */ | ||
| protected History prepareHistory() throws IOException { | ||
|
|
@@ -307,7 +321,7 @@ protected History prepareHistory() throws IOException { | |
| // can't create the file, so no history for you | ||
| } | ||
| } | ||
| return file.canWrite() ? new FileHistory(file) : null; | ||
| return file.canWrite() ? new DefaultHistory() : null; | ||
jamesfredley marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| public boolean isWindows() { | ||
|
|
@@ -334,8 +348,12 @@ public static synchronized void removeInstance() { | |
| if (instance != null) { | ||
| instance.removeShutdownHook(); | ||
| instance.restoreOriginalSystemOutAndErr(); | ||
| if (instance.getReader() != null) { | ||
| instance.getReader().shutdown(); | ||
| if (instance.terminal != null) { | ||
| try { | ||
| instance.terminal.close(); | ||
| } catch (IOException e) { | ||
| // ignore | ||
| } | ||
| } | ||
| instance = null; | ||
| } | ||
|
|
@@ -348,24 +366,18 @@ public void beforeShutdown() { | |
|
|
||
| protected void restoreTerminal() { | ||
| try { | ||
| terminal.restore(); | ||
| if (terminal != null) { | ||
| terminal.close(); | ||
| } | ||
| } catch (Exception e) { | ||
| // ignore | ||
| } | ||
| if (terminal instanceof UnixTerminal) { | ||
| // workaround for GRAILS-11494 | ||
| try { | ||
| new TerminalLineSettings().set("sane"); | ||
| } catch (Exception e) { | ||
| // ignore | ||
| } | ||
| } | ||
| } | ||
|
|
||
| protected void persistHistory() { | ||
| if (history instanceof Flushable) { | ||
| if (history != null && reader != null) { | ||
| try { | ||
| ((Flushable) history).flush(); | ||
| history.save(); | ||
| } catch (Throwable e) { | ||
| // ignore exception | ||
| } | ||
|
|
@@ -442,7 +454,7 @@ public boolean isStacktrace() { | |
| */ | ||
| public InputStream getInput() { | ||
| assertAllowInput(); | ||
| return reader.getInput(); | ||
| return terminal != null ? terminal.input() : System.in; | ||
| } | ||
|
|
||
| private void assertAllowInput() { | ||
|
|
@@ -471,7 +483,7 @@ public void setLastMessage(String lastMessage) { | |
| this.lastMessage = lastMessage; | ||
| } | ||
|
|
||
| public ConsoleReader getReader() { | ||
| public LineReader getReader() { | ||
| return reader; | ||
| } | ||
|
|
||
|
|
@@ -674,7 +686,7 @@ private void logSimpleError(String msg) { | |
| } | ||
|
|
||
| public boolean isAnsiEnabled() { | ||
| return Ansi.isEnabled() && (terminal != null && terminal.isAnsiSupported()) && ansiEnabled; | ||
| return Ansi.isEnabled() && (terminal != null && !"dumb".equals(terminal.getType())) && ansiEnabled; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -875,10 +887,17 @@ private String readLine(String prompt, boolean secure) { | |
| assertAllowInput(prompt); | ||
| userInputActive = true; | ||
| try { | ||
| Character inputMask = secure ? SECURE_MASK_CHAR : defaultInputMask; | ||
| return reader.readLine(prompt, inputMask); | ||
| } catch (IOException e) { | ||
| throw new RuntimeException("Error reading input: " + e.getMessage()); | ||
| if (secure) { | ||
| return reader.readLine(prompt, SECURE_MASK_CHAR); | ||
| } else if (defaultInputMask == null) { | ||
| return reader.readLine(prompt); | ||
| } else { | ||
| return reader.readLine(prompt, defaultInputMask); | ||
| } | ||
| } catch (org.jline.reader.UserInterruptException e) { | ||
| return null; | ||
| } catch (org.jline.reader.EndOfFileException e) { | ||
| return null; | ||
| } finally { | ||
| userInputActive = false; | ||
| } | ||
|
|
@@ -1041,4 +1060,12 @@ public Character getDefaultInputMask() { | |
| public void setDefaultInputMask(Character defaultInputMask) { | ||
| this.defaultInputMask = defaultInputMask; | ||
| } | ||
|
|
||
| /** | ||
| * Gets the history for the LineReader | ||
| * @return the history | ||
| */ | ||
| public History getHistory() { | ||
| return history; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have 2 different 3.x versions? Shouldn't there only be one.