From a471e7384d8d2e70073945e5a5e40749e6fa4c85 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Fri, 11 Sep 2020 10:50:20 -0700 Subject: [PATCH 1/2] Honor shellPrompt override sbt 1.4.0 generates the shell prompt using the terminal properties for the specific terminal for which the prompt is rendered. The mechanism for doing this broke the prompt for projects that overrode the shellPrompt key, notably the play plugin. After this change, the play custom prompt is correctly rendered with 1.4.0-SNAPSHOT. --- .../src/main/scala/sbt/internal/ui/UITask.scala | 17 ++++++++++++----- main/src/main/scala/sbt/Defaults.scala | 7 +++++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/main-command/src/main/scala/sbt/internal/ui/UITask.scala b/main-command/src/main/scala/sbt/internal/ui/UITask.scala index f47def3a6..614fd05df 100644 --- a/main-command/src/main/scala/sbt/internal/ui/UITask.scala +++ b/main-command/src/main/scala/sbt/internal/ui/UITask.scala @@ -44,6 +44,9 @@ private[sbt] trait UITask extends Runnable with AutoCloseable { } private[sbt] object UITask { + case object NoShellPrompt extends (State => String) { + override def apply(state: State): String = "" + } trait Reader extends AutoCloseable { def readLine(): Either[String, String] override def close(): Unit = {} @@ -93,11 +96,15 @@ private[sbt] object UITask { private[this] def history(s: State): Option[File] = s.get(historyPath).getOrElse(Some(new File(s.baseDir, ".history"))) private[sbt] def shellPrompt(terminal: Terminal, s: State): String = - s.get(terminalShellPrompt) match { - case Some(pf) => pf(terminal, s) - case None => - def ansi(s: String): String = if (terminal.isAnsiSupported) s"$s" else "" - s"${ansi(DeleteLine)}> ${ansi(ClearScreenAfterCursor)}" + s.get(sbt.BasicKeys.shellPrompt) match { + case Some(NoShellPrompt) | None => + s.get(terminalShellPrompt) match { + case Some(pf) => pf(terminal, s) + case None => + def ansi(s: String): String = if (terminal.isAnsiSupported) s"$s" else "" + s"${ansi(DeleteLine)}> ${ansi(ClearScreenAfterCursor)}" + } + case Some(p) => p(s) } private[sbt] class AskUserTask( state: State, diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 3b37e8ad1..241b7e0c1 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -2558,9 +2558,12 @@ object Classpaths { val base = ModuleID(id.groupID, id.name, sbtVersion.value).withCrossVersion(cross) CrossVersion(scalaVersion, binVersion)(base).withCrossVersion(Disabled()) }, - shellPrompt := shellPromptFromState, + shellPrompt := sbt.internal.ui.UITask.NoShellPrompt, terminalShellPrompt := { (t, s) => - shellPromptFromState(t)(s) + shellPrompt.value match { + case sbt.internal.ui.UITask.NoShellPrompt => shellPromptFromState(t)(s) + case p => p(s) + } }, dynamicDependency := { (): Unit }, transitiveClasspathDependency := { (): Unit }, From 476cfc6649448413de396c58ab79149f2c6d85fc Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Fri, 11 Sep 2020 11:47:35 -0700 Subject: [PATCH 2/2] Change terminalShellPrompt to colorShellPrompt I introduced the terminalShellPrompt so that we could generate a prompt that was colored only if the terminal supported color. Rather than expose the terminal implementation detail, we can just use a boolean flag that toggles whether or not color is enabled and sbt can pass in the value of terminal.isColorEnabled into the function. --- main-command/src/main/scala/sbt/BasicKeys.scala | 6 +++--- .../src/main/scala/sbt/internal/ui/UITask.scala | 10 +++++----- main/src/main/scala/sbt/Defaults.scala | 10 +++++----- main/src/main/scala/sbt/Keys.scala | 2 +- main/src/main/scala/sbt/Project.scala | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/main-command/src/main/scala/sbt/BasicKeys.scala b/main-command/src/main/scala/sbt/BasicKeys.scala index 9a85658f0..749adb30e 100644 --- a/main-command/src/main/scala/sbt/BasicKeys.scala +++ b/main-command/src/main/scala/sbt/BasicKeys.scala @@ -13,7 +13,7 @@ import com.github.ghik.silencer.silent import sbt.internal.inc.classpath.{ ClassLoaderCache => IncClassLoaderCache } import sbt.internal.classpath.ClassLoaderCache import sbt.internal.server.ServerHandler -import sbt.internal.util.{ AttributeKey, Terminal } +import sbt.internal.util.AttributeKey import sbt.librarymanagement.ModuleID import sbt.util.Level import scala.concurrent.duration.FiniteDuration @@ -36,8 +36,8 @@ object BasicKeys { "The function that constructs the command prompt from the current build state.", 10000 ) - val terminalShellPrompt = AttributeKey[(Terminal, State) => String]( - "new-shell-prompt", + val colorShellPrompt = AttributeKey[(Boolean, State) => String]( + "color-shell-prompt", "The function that constructs the command prompt from the current build state for a given terminal.", 10000 ) diff --git a/main-command/src/main/scala/sbt/internal/ui/UITask.scala b/main-command/src/main/scala/sbt/internal/ui/UITask.scala index 614fd05df..1ed500047 100644 --- a/main-command/src/main/scala/sbt/internal/ui/UITask.scala +++ b/main-command/src/main/scala/sbt/internal/ui/UITask.scala @@ -12,7 +12,7 @@ import java.nio.channels.ClosedChannelException import java.util.concurrent.atomic.AtomicBoolean import sbt.BasicCommandStrings.{ Cancel, TerminateAction, Shutdown } -import sbt.BasicKeys.{ historyPath, terminalShellPrompt } +import sbt.BasicKeys.{ historyPath, colorShellPrompt } import sbt.State import sbt.internal.CommandChannel import sbt.internal.util.ConsoleAppender.{ ClearPromptLine, ClearScreenAfterCursor, DeleteLine } @@ -98,11 +98,11 @@ private[sbt] object UITask { private[sbt] def shellPrompt(terminal: Terminal, s: State): String = s.get(sbt.BasicKeys.shellPrompt) match { case Some(NoShellPrompt) | None => - s.get(terminalShellPrompt) match { - case Some(pf) => pf(terminal, s) + s.get(colorShellPrompt) match { + case Some(pf) => pf(terminal.isColorEnabled, s) case None => - def ansi(s: String): String = if (terminal.isAnsiSupported) s"$s" else "" - s"${ansi(DeleteLine)}> ${ansi(ClearScreenAfterCursor)}" + def color(s: String): String = if (terminal.isColorEnabled) s"$s" else "" + s"${color(DeleteLine)}> ${color(ClearScreenAfterCursor)}" } case Some(p) => p(s) } diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 241b7e0c1..0498650b8 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -2559,9 +2559,9 @@ object Classpaths { CrossVersion(scalaVersion, binVersion)(base).withCrossVersion(Disabled()) }, shellPrompt := sbt.internal.ui.UITask.NoShellPrompt, - terminalShellPrompt := { (t, s) => + colorShellPrompt := { (c, s) => shellPrompt.value match { - case sbt.internal.ui.UITask.NoShellPrompt => shellPromptFromState(t)(s) + case sbt.internal.ui.UITask.NoShellPrompt => shellPromptFromState(c)(s) case p => p(s) } }, @@ -3864,12 +3864,12 @@ object Classpaths { } } - def shellPromptFromState: State => String = shellPromptFromState(Terminal.console) - def shellPromptFromState(terminal: Terminal): State => String = { s: State => + def shellPromptFromState: State => String = shellPromptFromState(Terminal.console.isColorEnabled) + def shellPromptFromState(isColorEnabled: Boolean): State => String = { s: State => val extracted = Project.extract(s) (name in extracted.currentRef).get(extracted.structure.data) match { case Some(name) => - s"sbt:$name" + Def.withColor(s"> ", Option(scala.Console.CYAN), terminal.isColorEnabled) + s"sbt:$name" + Def.withColor(s"> ", Option(scala.Console.CYAN), isColorEnabled) case _ => "> " } } diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index c15957b00..9c6a35017 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -93,7 +93,7 @@ object Keys { // Command keys val historyPath = SettingKey(BasicKeys.historyPath) val shellPrompt = SettingKey(BasicKeys.shellPrompt) - val terminalShellPrompt = SettingKey(BasicKeys.terminalShellPrompt) + val colorShellPrompt = SettingKey(BasicKeys.colorShellPrompt) val autoStartServer = SettingKey(BasicKeys.autoStartServer) val serverPort = SettingKey(BasicKeys.serverPort) val serverHost = SettingKey(BasicKeys.serverHost) diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index 914cb5bc7..a86dd155a 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -14,12 +14,12 @@ import Project._ import BasicKeys.serverLogLevel import Keys.{ stateBuildStructure, + colorShellPrompt, commands, configuration, historyPath, projectCommand, sessionSettings, - terminalShellPrompt, shellPrompt, templateResolverInfos, autoStartServer, @@ -512,7 +512,7 @@ object Project extends ProjectExtra { val allCommands = commandsIn(ref) ++ commandsIn(BuildRef(ref.build)) ++ (commands in Global get structure.data toList) val history = get(historyPath) flatMap idFun val prompt = get(shellPrompt) - val newPrompt = get(terminalShellPrompt) + val newPrompt = get(colorShellPrompt) val trs = (templateResolverInfos in Global get structure.data).toList.flatten val startSvr: Option[Boolean] = get(autoStartServer) val host: Option[String] = get(serverHost) @@ -541,7 +541,7 @@ object Project extends ProjectExtra { .put(historyPath.key, history) .put(templateResolverInfos.key, trs) .setCond(shellPrompt.key, prompt) - .setCond(terminalShellPrompt.key, newPrompt) + .setCond(colorShellPrompt.key, newPrompt) .setCond(serverLogLevel, srvLogLevel) .setCond(fullServerHandlers.key, hs) s.copy(