Allow users to configure standard input setting

Certain tasks may prefer to have the input set to raw mode and/or have
echo off. The specific use case is that it is difficult to get the
ammonite console to work correctly with the thin client. The problem is
that the ammonite console runs some tty commands. These commands will
only work on the tty of the thin client when the thin client itself has
launched the sbt server session (since they share the same tty). Once
the thin client that launched the server exits, the ammonite console
will never work again with that server session. A workaround is to
launch sbt separately and leave that server session open. Then, if the
run task is configured with canonical input set to false and echo
disabled, the thin client will work. In the future, it's possible that
ammonite could be updated to not rely on calling stty commands and then
the thin client could work with the ammonite console even after the
initial thin client session has exited provided canonical input and echo
are disabled.
This commit is contained in:
Ethan Atkins 2020-09-18 12:19:35 -07:00
parent 0427e5f9c5
commit ddb626a9be
2 changed files with 24 additions and 4 deletions

View File

@ -386,6 +386,8 @@ object Defaults extends BuildCommon {
sys.env.contains("CI") || SysProp.ci,
// watch related settings
pollInterval :== Watch.defaultPollInterval,
canonicalInput :== true,
echoInput :== true,
) ++ LintUnused.lintSettings
++ DefaultBackgroundJobService.backgroundJobServiceSettings
++ RemoteCache.globalSettings
@ -1700,6 +1702,20 @@ object Defaults extends BuildCommon {
/** Implements `cleanFiles` task. */
private[sbt] def cleanFilesTask: Initialize[Task[Vector[File]]] = Def.task { Vector.empty[File] }
private[this] def termWrapper(canonical: Boolean, echo: Boolean): (() => Unit) => (() => Unit) =
(f: () => Unit) =>
() => {
val term = Terminal.get
if (!canonical) {
term.enterRawMode()
if (echo) term.setEchoEnabled(echo)
} else if (!echo) term.setEchoEnabled(false)
try f()
finally {
if (!canonical) term.exitRawMode()
if (!echo) term.setEchoEnabled(true)
}
}
def bgRunMainTask(
products: Initialize[Task[Classpath]],
classpath: Initialize[Task[Classpath]],
@ -1713,6 +1729,7 @@ object Defaults extends BuildCommon {
val service = bgJobService.value
val (mainClass, args) = parser.parsed
val hashClasspath = (bgHashClasspath in bgRunMain).value
val wrapper = termWrapper(canonicalInput.value, echoInput.value)
service.runInBackgroundWithLoader(resolvedScoped.value, state.value) { (logger, workingDir) =>
val files =
if (copyClasspath.value)
@ -1722,9 +1739,9 @@ object Defaults extends BuildCommon {
scalaRun.value match {
case r: Run =>
val loader = r.newLoader(cp)
(Some(loader), () => r.runWithLoader(loader, cp, mainClass, args, logger).get)
(Some(loader), wrapper(() => r.runWithLoader(loader, cp, mainClass, args, logger).get))
case sr =>
(None, () => sr.run(mainClass, cp, args, logger).get)
(None, wrapper(() => sr.run(mainClass, cp, args, logger).get))
}
}
}
@ -1743,6 +1760,7 @@ object Defaults extends BuildCommon {
val service = bgJobService.value
val mainClass = mainClassTask.value getOrElse sys.error("No main class detected.")
val hashClasspath = (bgHashClasspath in bgRun).value
val wrapper = termWrapper(canonicalInput.value, echoInput.value)
service.runInBackgroundWithLoader(resolvedScoped.value, state.value) { (logger, workingDir) =>
val files =
if (copyClasspath.value)
@ -1753,9 +1771,9 @@ object Defaults extends BuildCommon {
scalaRun.value match {
case r: Run =>
val loader = r.newLoader(cp)
(Some(loader), () => r.runWithLoader(loader, cp, mainClass, args, logger).get)
(Some(loader), wrapper(() => r.runWithLoader(loader, cp, mainClass, args, logger).get))
case sr =>
(None, () => sr.run(mainClass, cp, args, logger).get)
(None, wrapper(() => sr.run(mainClass, cp, args, logger).get))
}
}
}

View File

@ -68,6 +68,8 @@ object Keys {
val logBuffered = settingKey[Boolean]("True if logging should be buffered until work completes.").withRank(CSetting)
val sLog = settingKey[Logger]("Logger usable by settings during project loading.").withRank(CSetting)
val serverLog = taskKey[Unit]("A dummy task to set server log level using Global / serverLog / logLevel.").withRank(CTask)
val canonicalInput = settingKey[Boolean]("Toggles whether a task should use canonical input (line buffered with echo) or raw input").withRank(DSetting)
val echoInput = settingKey[Boolean]("Toggles whether a task should echo user input").withRank(DSetting)
// Project keys
val autoGeneratedProject = settingKey[Boolean]("If it exists, represents that the project (and name) were automatically created, rather than user specified.").withRank(DSetting)