From 53c9b848580376544543b0732848005b37d97fd7 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sun, 30 Sep 2018 20:59:19 -0400 Subject: [PATCH] add sbt.color flag This implements a new sbt.color flag that takes always/auto/never/true/false value as a replacement of current sbt.log.format=true/false flag. When neither flags are set, the default behavior is to enable color when the terminal supports ANSI and it detects an stdout console (as opposed to redirects). Fixes https://github.com/sbt/sbt/issues/4284 --- .../sbt/internal/util/LogOption.scala | 15 +++++++ .../internal/util/codec/JsonProtocol.scala | 1 + .../util/codec/LogOptionFormats.scala | 31 +++++++++++++ .../src/main/contraband/logging.contra | 7 +++ .../sbt/internal/util/ConsoleAppender.scala | 44 +++++++++++++++++-- 5 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 internal/util-logging/src/main/contraband-scala/sbt/internal/util/LogOption.scala create mode 100644 internal/util-logging/src/main/contraband-scala/sbt/internal/util/codec/LogOptionFormats.scala diff --git a/internal/util-logging/src/main/contraband-scala/sbt/internal/util/LogOption.scala b/internal/util-logging/src/main/contraband-scala/sbt/internal/util/LogOption.scala new file mode 100644 index 000000000..769da7982 --- /dev/null +++ b/internal/util-logging/src/main/contraband-scala/sbt/internal/util/LogOption.scala @@ -0,0 +1,15 @@ +/** + * This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.util +/** value for logging options like color */ +sealed abstract class LogOption extends Serializable +object LogOption { + + + case object Always extends LogOption + case object Never extends LogOption + case object Auto extends LogOption +} diff --git a/internal/util-logging/src/main/contraband-scala/sbt/internal/util/codec/JsonProtocol.scala b/internal/util-logging/src/main/contraband-scala/sbt/internal/util/codec/JsonProtocol.scala index a94906dda..15e4d9cb2 100644 --- a/internal/util-logging/src/main/contraband-scala/sbt/internal/util/codec/JsonProtocol.scala +++ b/internal/util-logging/src/main/contraband-scala/sbt/internal/util/codec/JsonProtocol.scala @@ -9,4 +9,5 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol with sbt.internal.util.codec.TraceEventFormats with sbt.internal.util.codec.AbstractEntryFormats with sbt.internal.util.codec.SuccessEventFormats + with sbt.internal.util.codec.LogOptionFormats object JsonProtocol extends JsonProtocol \ No newline at end of file diff --git a/internal/util-logging/src/main/contraband-scala/sbt/internal/util/codec/LogOptionFormats.scala b/internal/util-logging/src/main/contraband-scala/sbt/internal/util/codec/LogOptionFormats.scala new file mode 100644 index 000000000..e52700c19 --- /dev/null +++ b/internal/util-logging/src/main/contraband-scala/sbt/internal/util/codec/LogOptionFormats.scala @@ -0,0 +1,31 @@ +/** + * This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.util.codec +import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +trait LogOptionFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val LogOptionFormat: JsonFormat[sbt.internal.util.LogOption] = new JsonFormat[sbt.internal.util.LogOption] { + override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.util.LogOption = { + jsOpt match { + case Some(js) => + unbuilder.readString(js) match { + case "Always" => sbt.internal.util.LogOption.Always + case "Never" => sbt.internal.util.LogOption.Never + case "Auto" => sbt.internal.util.LogOption.Auto + } + case None => + deserializationError("Expected JsString but found None") + } + } + override def write[J](obj: sbt.internal.util.LogOption, builder: Builder[J]): Unit = { + val str = obj match { + case sbt.internal.util.LogOption.Always => "Always" + case sbt.internal.util.LogOption.Never => "Never" + case sbt.internal.util.LogOption.Auto => "Auto" + } + builder.writeString(str) + } +} +} diff --git a/internal/util-logging/src/main/contraband/logging.contra b/internal/util-logging/src/main/contraband/logging.contra index 19b019c66..73d0b1a56 100644 --- a/internal/util-logging/src/main/contraband/logging.contra +++ b/internal/util-logging/src/main/contraband/logging.contra @@ -25,3 +25,10 @@ type TraceEvent implements sbt.internal.util.AbstractEntry { type SuccessEvent { message: String! } + +## value for logging options like color +enum LogOption { + Always + Never + Auto +} diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala index d5f6f7919..f070a1252 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala @@ -101,13 +101,49 @@ object ConsoleAppender { /** Hide stack trace altogether. */ val noSuppressedMessage = (_: SuppressedTraceContext) => None - /** Indicates whether formatting has been disabled in environment variables. */ + /** + * Indicates whether formatting has been disabled in environment variables. + * 1. -Dsbt.log.noformat=true means no formatting. + * 2. -Dsbt.color=always/auto/never/true/false + * 3. -Dsbt.colour=always/auto/never/true/false + * 4. -Dsbt.log.format=always/auto/never/true/false + */ val formatEnabledInEnv: Boolean = { - import java.lang.Boolean.{ getBoolean, parseBoolean } - val value = System.getProperty("sbt.log.format") - if (value eq null) (ansiSupported && !getBoolean("sbt.log.noformat")) else parseBoolean(value) + def useColorDefault: Boolean = { + // This approximates that both stdin and stdio are connected, + // so by default color will be turned off for pipes and redirects. + val hasConsole = Option(java.lang.System.console).isDefined + ansiSupported && hasConsole + } + sys.props.get("sbt.log.noformat") match { + case Some(_) => !java.lang.Boolean.getBoolean("sbt.log.noformat") + case _ => + sys.props + .get("sbt.color") + .orElse(sys.props.get("sbt.colour")) + .orElse(sys.props.get("sbt.log.format")) + .flatMap({ s => + parseLogOption(s) match { + case LogOption.Always => Some(true) + case LogOption.Never => Some(false) + case _ => None + } + }) + .getOrElse(useColorDefault) + } } + private[sbt] def parseLogOption(s: String): LogOption = + s.toLowerCase match { + case "always" => LogOption.Always + case "auto" => LogOption.Auto + case "never" => LogOption.Never + case "true" => LogOption.Always + case "false" => LogOption.Never + case "default" => LogOption.Auto + case _ => LogOption.Auto + } + private[this] val generateId: AtomicInteger = new AtomicInteger /**