sbt passes raw string messages in `ConsoleAppenderFromLog4J` to `StringFormatterMessageFactory`. This is wrong because these strings are pre-formatted and should not be processed again for formatting. There is no way to pass parameters to them anyway. This causes problems when the raw strings contain characters that `StringFormatterMessageFactory` wants to interpret.
For example, when using `-Ystatistics`:
```
ERROR StatusConsoleListener Unable to format msg: nscprofiling : 1 spans, ()7.543ms (0.3%)
java.util.UnknownFormatConversionException: Conversion = ')'
at java.base/java.util.Formatter.checkText(Formatter.java:2732)
at java.base/java.util.Formatter.parse(Formatter.java:2718)
at java.base/java.util.Formatter.format(Formatter.java:2655)
at java.base/java.util.Formatter.format(Formatter.java:2609)
at java.base/java.lang.String.format(String.java:2938)
at org.apache.logging.log4j.message.StringFormattedMessage.formatMessage(StringFormattedMessage.java:121)
at org.apache.logging.log4j.message.StringFormattedMessage.getFormattedMessage(StringFormattedMessage.java:89)
at sbt.internal.util.Appender.$anonfun$appendMessage$1(ConsoleAppender.scala:522)
...
```
Previously, the list of logger names that have been created in a given
context with Log4J was being kept in a `Vector`. As a result, subsequent
calls to `logger` on a `Log4JLoggerContext` with the same logger name
would introduce duplicates in the list. Note that Log4J's logger
configuration would not allow duplicates, and only one configuration
object for a given logger name will exist. The duplicates in the list of
logger names are therefore unnecessary.
With this patch, the logger names will be kept in a `HashSet`.
Since the logger names are only used to remove the logger configurations
after the logger context is closed, and the logger configurations
contain no duplicates, removing duplicate logger names should not cause
any issue.
- remove unused type params
- use `withFilter` if possible
- use `collectFirst` instead of `collect` and `headOption`
- use `length` instead of `size` if `Array` or `String`
- use `foreach` instead of `map`
If the terminal supports ANSI control sequence,
this displays the template list in an interactive way.
The focused template is rendered reversed,
and arrow key can be used to move the focus up/down.
Looks like I missed this in https://github.com/sbt/sbt/pull/6874 and I
hit on it in Mill when I couldn't figure out why it was also empty, and
thanks to @adpi realized it was because of the `LoggedReporter` in zinc
not taking it into account. However before I can bump that this needs to
be bumped as well.
refs: https://github.com/lampepfl/dotty/issues/14904
If we use the ProxyTerminal in the background jobs, the logs
would be spread across different terminals, switching from active
client to active client. We want the logs to stick
to the client that started the job.
A new context is created and closed for each state of the MainLoop.
But the context of the backgroundJob must stay alive.
So we use a context that is owned by the BackgroundJobService.
It creates a new logger for each background job and cleans it when
the job stops.
Problem
-------
Console.systemOut is hooked up to Terminal.get, which internally calls
ProxyTerminal, which lets us deffer the wiring of terminal to
activeTerminal. This mechanism allows us to swap out the terminal
capable of standard out forwarding for sbtn.
However, as it stands this breaks the contract of being able to use
Console.systemOut with wrapped inside of `Terminal.withStreams() {...}`.
Solution
--------
Check if `activeTerminal.get` returns `null`, and if so initialize it to
the conventional `Terminal.SimpleTerminal`, which behaves as expected.
There are cases where sbt will incorrectly shutdown if the jline reader
is interrupted while filling the input buffer. To fix this we can throw
an InterruptedException instead of a ClosedException.
The repro for this was start `sbt`, input `~compile` and while sbt was
starting up, open a source file with vim using the metals bsp
integration. sbt server would end up shutting down everytime after a
single compilation iteration.
With the latest sbt code, the `last` command displayed all of the output
without line separators. This occurred because the logic for appending
bytes to System.out split the input bytes on the line separator but if
there was nothing but the line separator in the input bytes then the
result was empty.
When there are multiple console appenders for a logger, we inadvertently
evaluated the message thunk for each appender which would cause side
effects to be unexpectedely evaluated multiple times.
Fixes part of #6126.
In 85d17889b6, I attempted to fix
supershell messages getting interlaced with log lines. It turned out
that that approach didn't work with windows and was causing all of the
output to bet blown away. A better approach is to check if the bytes
we're writing contain one or more line separators. If so, we can wrap
the bytes in a string and split the string into lines. Then we can
append a ClearScreenAfterCursor before every newline.
I think the problem with windows was that the ClearScreenAfterCursor was
coming between the carraige return and the newline.
The boot server socket was not working correctly when the sbt server was
started by the thin client. This was because it is necessary for us to
create a ConsoleTerminal in order for System.out and System.err to be
properly forwarded to the clients connected over the boot server socket.
As a result, if you started a server instance of sbt with the thin
client, you wouldn't see any output util you connected to the server.
The fix is to just make sure that we create a console terminal if sbt is
run as a subprocess.
We no longer need to use the forked version of jline because they have
merged in our required changes. The latest version of jline does upgrade
jansi, however, and some of the apis we were relying on for windows were
removed so they had to be manually implemented. I verified that console
input still worked on my windows vm after this change.
In 3b09ff6af7, we stopped adding a clear
screen after curser after each log line. This inadvertently caused
supershell lines to get interlaced with log lines. This can be fixed by
writing a ClearScreenAfterCursor after every newline character that we
write to stdout.
As a bonus, I had also long noticed that supershell log lines would get
interlaced with the serverTestProj/test output and this change fixes
that as well.
On terminals with virtual io disabled, we'd spin up a thread for each
watch iteration that performed a blocking read from the terminal input
stream. This thread could not be joined which would cause the triggered
execution to be delayed by 1 second while sbt blocked trying to join
that thread. It also meant that input probably didn't work correctly
since the user would end up with many threads polling from system in.
The fix to this problem is to poll the terminal input stream if it is
unsafe to do a blocking read, which is the case for dumb terminals or if
virtual io is disabled.
With sbt 1.4.x, non-ascii utf-8 characters are not handled correctly in
the console. It was not clear from the jline 3 documentation but the
NonBlockingReader.read method is supposed to return unicode points
rather than utf8 bytes. To fix this, we can decode the input and return
the code point rather than the directy byte from the input stream.
When the sbt main loop is blocked by console, any other connected client
is prompted that they can kill the task by typing cancel. The
implementation for the console task is to write some input that will
cause the console to exit because the scala 2.12 console cannot be
safely killed with an interrupt. This input, however, was being blocked
from written to the console because the console input stream was holding
the readThread lock. We can be fix this and propagate the input to the
console we wish to terminate by synchronizing on a different lock
object. This should have no impact outside of cancelling the console
because that is the only place where we call the write method of
WriteableInputStream.
If a user runs sbt -Dsbt.ci=true with the latest code, sbt immediately
exits. This was because we were passing the SimpleTerminal into jline3
and jline 3 would end up exiting immediately. Instead we can still make
a console terminal if there is a console available. An alternative
approach would have been to use a dumb terminal with -Dsbt.ci=true, but
the dumb terminal experience is not great (tab completions don't work
for example), so I thought this was a better fix.
Tab completions did not work well in sbt 1.4.x when run with
-Dsbt.color=false. This was because we were stripping a bunch of ansi
codes, which caused some problems with the jline 3 completion engine.
Instead of stripping the ansi codes, we can set the jline max_colors
capability to 1 if color is disabled. With this change, the completions
are similar to 1.3.13 except that in jline 2 the user has to hit tab
twice to see all of the available candidates while in jline 3, the
candidates are immediately printed below the prompt.
Intellij import was broken in sbt 1.4.2 because we increased the
scenarios in which virtual io is used. The problem wasn't actually
virtual io but that we create a console terminal with jline3 and that
could cause issues reading input from System.in. This can be fixed by
only creating an instance of ConsoleTerminal if a console is actually
available for the sbt process. When hasConsole is false, the
consoleTerminalHolder will point to the SimpleTerminal whose inputStream
method just reads directly from System.in. After making this change, I
verified that intellij import worked again on windows.
We also don't want to make a console terminal if it is a dumb terminal
because jline 3 ends up having problems with the ConsoleTerminal.