Set terminal properties during boot

Supershell does not work correctly when the sbt server is started by the
remote client on windows because it incorrectly calculates the terminal
dimensions. To work around this, we can pass in the dimensions from the
remote client as an environment variable. I tried to do this as a system
property but had all kinds of problems with windows stripping delimeters
from the command. It was much easier to get working with an environment
variable and should really only be set by the sbtc client anyway.
This commit is contained in:
Ethan Atkins 2020-06-26 16:42:02 -07:00
parent 27c1978087
commit 21664be3f7
2 changed files with 61 additions and 21 deletions

View File

@ -19,6 +19,7 @@ import sbt.internal.util.ConsoleAppender.{ ClearScreenAfterCursor, CursorLeft100
import scala.annotation.tailrec
import scala.collection.mutable.ArrayBuffer
import scala.util.Try
trait Terminal extends AutoCloseable {
@ -430,6 +431,38 @@ object Terminal {
}
}
/*
* When the server is booted by a remote client, it may not be able to accurately
* calculate the terminal properties. To work around this, we can set the
* properties via an environment property. It was too difficult to get system
* properties working correctly with windows.
*/
private class Props(
val width: Int,
val height: Int,
val ansi: Boolean,
val color: Boolean,
val supershell: Boolean
)
private[sbt] val TERMINAL_PROPS = "SBT_TERMINAL_PROPS"
private val props = System.getenv(TERMINAL_PROPS) match {
case null => None
case p =>
p.split(",") match {
case Array(width, height, ansi, color, supershell) =>
Try(
new Props(
width.toInt,
height.toInt,
ansi.toBoolean,
color.toBoolean,
supershell.toBoolean
)
).toOption
case _ => None
}
}
/**
* Creates an instance of [[Terminal]] that delegates most of its methods to an underlying
* jline.Terminal2 instance. In the long run, sbt should upgrade to jline3, which has a
@ -452,9 +485,9 @@ object Terminal {
override def restore(): Unit = if (alive) terminal.restore()
override def reset(): Unit = if (alive) terminal.reset()
override def isSupported: Boolean = terminal.isSupported
override def getWidth: Int = terminal.getWidth
override def getHeight: Int = terminal.getHeight
override def isAnsiSupported: Boolean = terminal.isAnsiSupported
override def getWidth: Int = props.map(_.width).getOrElse(terminal.getWidth)
override def getHeight: Int = props.map(_.height).getOrElse(terminal.getHeight)
override def isAnsiSupported: Boolean = props.map(_.ansi).getOrElse(terminal.isAnsiSupported)
override def wrapOutIfNeeded(out: OutputStream): OutputStream = terminal.wrapOutIfNeeded(out)
override def wrapInIfNeeded(in: InputStream): InputStream = terminal.wrapInIfNeeded(in)
override def hasWeirdWrap: Boolean = terminal.hasWeirdWrap
@ -549,13 +582,18 @@ object Terminal {
term.setEchoEnabled(true)
}
}
override def isColorEnabled: Boolean = ConsoleAppender.formatEnabledInEnv
override def isColorEnabled: Boolean =
props.map(_.color).getOrElse(ConsoleAppender.formatEnabledInEnv)
override def isSupershellEnabled: Boolean = System.getProperty("sbt.supershell") match {
case null => !(sys.env.contains("BUILD_NUMBER") || sys.env.contains("CI")) && isColorEnabled
case "true" => true
case _ => false
}
override def isSupershellEnabled: Boolean =
props
.map(_.supershell)
.getOrElse(System.getProperty("sbt.supershell") match {
case null =>
!(sys.env.contains("BUILD_NUMBER") || sys.env.contains("CI")) && isColorEnabled
case "true" => true
case _ => false
})
}
private[sbt] abstract class TerminalImpl private[sbt] (
val in: InputStream,

View File

@ -189,23 +189,25 @@ class NetworkClient(
*/
def forkServer(portfile: File, log: Boolean): Unit = {
if (log) console.appendLog(Level.Info, "server was not detected. starting an instance")
val color =
if (!arguments.sbtArguments.exists(_.startsWith("-Dsbt.color=")))
s"-Dsbt.color=${Terminal.console.isColorEnabled}" :: Nil
else Nil
val superShell =
if (!arguments.sbtArguments.exists(_.startsWith("-Dsbt.supershell=")))
s"-Dsbt.supershell=${Terminal.console.isColorEnabled}" :: Nil
else Nil
val term = Terminal.console
val props =
Seq(
term.getWidth,
term.getHeight,
term.isAnsiSupported,
term.isColorEnabled,
term.isSupershellEnabled
).mkString(",")
val args = color ++ superShell ++ arguments.sbtArguments
val cmd = arguments.sbtScript +: args :+ BasicCommandStrings.CloseIOStreams
val process =
val cmd = arguments.sbtScript +: arguments.sbtArguments :+ BasicCommandStrings.CloseIOStreams
val processBuilder =
new ProcessBuilder(cmd: _*)
.directory(arguments.baseDirectory)
.redirectInput(Redirect.PIPE)
.start()
processBuilder.environment.put(Terminal.TERMINAL_PROPS, props)
val process = processBuilder.start()
sbtProcess.set(process)
val hook = new Thread(() => Option(sbtProcess.get).foreach(_.destroyForcibly()))
Runtime.getRuntime.addShutdownHook(hook)
val stdout = process.getInputStream