mirror of https://github.com/sbt/sbt.git
Buffer terminal lines
It is useful to store a buffer of the lines written to each terminal. We can use those lines to replay the terminal log lines to a different client. This is particularly nice when a remote client connects to sbt while it's booting. We can show the remote client all the lines displayed by the console prior to the client connecting.
This commit is contained in:
parent
6faf460a1b
commit
e1c9ed5a55
|
|
@ -8,16 +8,18 @@
|
|||
package sbt.internal.util
|
||||
|
||||
import java.io.PrintStream
|
||||
import java.util.concurrent.ArrayBlockingQueue
|
||||
import java.util.concurrent.atomic.{ AtomicInteger, AtomicReference }
|
||||
|
||||
import sbt.internal.util.ConsoleAppender.{
|
||||
ClearScreenAfterCursor,
|
||||
CursorLeft1000,
|
||||
DeleteLine,
|
||||
cursorUp,
|
||||
cursorUp
|
||||
}
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
private[sbt] final class ProgressState(
|
||||
val progressLines: AtomicReference[Seq[String]],
|
||||
|
|
@ -41,6 +43,9 @@ private[sbt] final class ProgressState(
|
|||
padding.set(0)
|
||||
currentLineBytes.set(new ArrayBuffer[Byte])
|
||||
}
|
||||
private[this] val lineBuffer = new ArrayBlockingQueue[String](300)
|
||||
private[util] def getLines: Seq[String] = lineBuffer.asScala.toVector
|
||||
private[this] def appendLine(line: String) = while (!lineBuffer.offer(line)) { lineBuffer.poll }
|
||||
private[util] def clearBytes(): Unit = {
|
||||
val pad = padding.get
|
||||
if (currentLineBytes.get.isEmpty && pad > 0) padding.decrementAndGet()
|
||||
|
|
@ -62,10 +67,12 @@ private[sbt] final class ProgressState(
|
|||
if (lines.contains(System.lineSeparator)) {
|
||||
currentLineBytes.set(new ArrayBuffer[Byte])
|
||||
if (!lines.endsWith(System.lineSeparator)) {
|
||||
lines
|
||||
.split(System.lineSeparator)
|
||||
.lastOption
|
||||
val allLines = lines.split(System.lineSeparator)
|
||||
allLines.dropRight(1).foreach(appendLine)
|
||||
allLines.lastOption
|
||||
.foreach(currentLineBytes.get ++= _.getBytes("UTF-8"))
|
||||
} else if (lines.contains(System.lineSeparator)) {
|
||||
lines.split(System.lineSeparator).foreach(appendLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,6 +109,16 @@ trait Terminal extends AutoCloseable {
|
|||
*/
|
||||
private[sbt] def getLastLine: Option[String]
|
||||
|
||||
/**
|
||||
* Returns the buffered lines that have been written to the terminal. The
|
||||
* main use case is to display the system startup log lines when a client
|
||||
* connects to a booting server. This could also be used to implement a more
|
||||
* tmux like experience where multiple clients connect to the same console.
|
||||
*
|
||||
* @return the lines
|
||||
*/
|
||||
private[sbt] def getLines: Seq[String]
|
||||
|
||||
private[sbt] def getBooleanCapability(capability: String, jline3: Boolean): Boolean
|
||||
private[sbt] def getNumericCapability(capability: String, jline3: Boolean): Integer
|
||||
private[sbt] def getStringCapability(capability: String, jline3: Boolean): String
|
||||
|
|
@ -328,6 +338,7 @@ object Terminal {
|
|||
override def close(): Unit = {}
|
||||
override private[sbt] def write(bytes: Int*): Unit = t.write(bytes: _*)
|
||||
override def getLastLine: Option[String] = t.getLastLine
|
||||
override def getLines: Seq[String] = t.getLines
|
||||
override private[sbt] def name: String = t.name
|
||||
}
|
||||
private[sbt] def get: Terminal = ProxyTerminal
|
||||
|
|
@ -731,7 +742,7 @@ object Terminal {
|
|||
}
|
||||
}
|
||||
|
||||
private[sbt] def console: Terminal = consoleTerminalHolder.get match {
|
||||
def console: Terminal = consoleTerminalHolder.get match {
|
||||
case null => throw new IllegalStateException("Uninitialized terminal.")
|
||||
case term => term
|
||||
}
|
||||
|
|
@ -808,6 +819,7 @@ object Terminal {
|
|||
}
|
||||
def throwIfClosed[R](f: => R): R = if (isStopped.get) throw new ClosedChannelException else f
|
||||
override def getLastLine: Option[String] = progressState.currentLine
|
||||
override def getLines: Seq[String] = progressState.getLines
|
||||
|
||||
private val combinedOutputStream = new OutputStream {
|
||||
override def write(b: Int): Unit = {
|
||||
|
|
@ -868,6 +880,7 @@ object Terminal {
|
|||
override def getBooleanCapability(capability: String, jline3: Boolean): Boolean = false
|
||||
override def getHeight: Int = 0
|
||||
override def getLastLine: Option[String] = None
|
||||
override def getLines: Seq[String] = Nil
|
||||
override def getLineHeightAndWidth(line: String): (Int, Int) = (0, 0)
|
||||
override def getNumericCapability(capability: String, jline3: Boolean): Integer = null
|
||||
override def getStringCapability(capability: String, jline3: Boolean): String = null
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import org.scalasbt.ipcsocket.UnixDomainSocket;
|
|||
import org.scalasbt.ipcsocket.Win32NamedPipeServerSocket;
|
||||
import org.scalasbt.ipcsocket.Win32NamedPipeSocket;
|
||||
import org.scalasbt.ipcsocket.Win32SecurityLevel;
|
||||
import sbt.internal.util.Terminal;
|
||||
import xsbti.AppConfiguration;
|
||||
|
||||
/**
|
||||
|
|
@ -102,6 +103,16 @@ public class BootServerSocket implements AutoCloseable {
|
|||
service.submit(
|
||||
() -> {
|
||||
try {
|
||||
Terminal.console()
|
||||
.getLines()
|
||||
.foreach(
|
||||
l -> {
|
||||
try {
|
||||
write((l + System.lineSeparator()).getBytes("UTF-8"));
|
||||
} catch (final IOException e) {
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
final InputStream inputStream = socket.getInputStream();
|
||||
while (alive.get()) {
|
||||
try {
|
||||
|
|
@ -134,6 +145,15 @@ public class BootServerSocket implements AutoCloseable {
|
|||
}
|
||||
}
|
||||
|
||||
private void write(final byte[] b) {
|
||||
try {
|
||||
if (alive.get()) socket.getOutputStream().write(b);
|
||||
} catch (final IOException e) {
|
||||
alive.set(false);
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
private void write(final byte[] b, final int offset, final int len) {
|
||||
try {
|
||||
if (alive.get()) socket.getOutputStream().write(b, offset, len);
|
||||
|
|
|
|||
Loading…
Reference in New Issue