diff --git a/main-settings/src/main/scala/sbt/Def.scala b/main-settings/src/main/scala/sbt/Def.scala index a55ea4979..7db51382a 100644 --- a/main-settings/src/main/scala/sbt/Def.scala +++ b/main-settings/src/main/scala/sbt/Def.scala @@ -54,12 +54,14 @@ object Def extends Init[Scope] with TaskMacroExtra { keyNameColor: Option[String] = None, ): Show[ScopedKey[_]] = Show[ScopedKey[_]]( - key => - Scope.display( - key.scope, - withColor(key.key.label, keyNameColor), - ref => displayRelative2(current, ref) - ) + key => { + val color: String => String = withColor(_, keyNameColor) + key.scope.extra.toOption + .flatMap(_.get(Scope.customShowString).map(color)) + .getOrElse { + Scope.display(key.scope, color(key.key.label), ref => displayRelative2(current, ref)) + } + } ) private[sbt] def showShortKey( diff --git a/main-settings/src/main/scala/sbt/Scope.scala b/main-settings/src/main/scala/sbt/Scope.scala index d241dcdfa..fe8f261f3 100644 --- a/main-settings/src/main/scala/sbt/Scope.scala +++ b/main-settings/src/main/scala/sbt/Scope.scala @@ -178,6 +178,22 @@ object Scope { ): String = displayMasked(scope, sep, showProject, mask, false) + /** + * Allows the user to override the result of `Scope.display` or `Scope.displayMasked` for a + * particular scope. This can be used to enhance super shell and/or error reporting for tasks + * that use mangled names. For example, one might have: + * {{{ + * val mangledKey = TaskKey[Unit]("foo_slash_bar") + * val attributeMap = AttributeMap.empty.put(Scope.customShowString("foo/bar")) + * val sanitizedKey = mangledKey.copy(scope = mangledKey.copy(extra = Select(attributeMap))) + * sanitizedKey := { ... } + * }}} + * + * Now whenever the `foo_slash_bar` task specified by sanitizedKey is evaluated, it will display + * "foo/bar" in super shell progress and in the error message if an error is thrown. + */ + val customShowString = AttributeKey[String]("scope-custom-show-string") + /** * unified slash style introduced in sbt 1.1.0. * By default, sbt will no longer display the Zero-config, @@ -193,20 +209,22 @@ object Scope { showZeroConfig: Boolean ): String = { import scope.{ project, config, task, extra } - val zeroConfig = if (showZeroConfig) "Zero /" else "" - val configPrefix = config.foldStrict(display, zeroConfig, "./") - val taskPrefix = task.foldStrict(_.label + " /", "", "./") - val extras = extra.foldStrict(_.entries.map(_.toString).toList, Nil, Nil) - val postfix = if (extras.isEmpty) "" else extras.mkString("(", ", ", ")") - if (scope == GlobalScope) "Global / " + sep + postfix - else - mask.concatShow( - appendSpace(projectPrefix(project, showProject)), - appendSpace(configPrefix), - appendSpace(taskPrefix), - sep, - postfix - ) + extra.toOption.flatMap(_.get(customShowString)).getOrElse { + val zeroConfig = if (showZeroConfig) "Zero /" else "" + val configPrefix = config.foldStrict(display, zeroConfig, "./") + val taskPrefix = task.foldStrict(_.label + " /", "", "./") + val extras = extra.foldStrict(_.entries.map(_.toString).toList, Nil, Nil) + val postfix = if (extras.isEmpty) "" else extras.mkString("(", ", ", ")") + if (scope == GlobalScope) "Global / " + sep + postfix + else + mask.concatShow( + appendSpace(projectPrefix(project, showProject)), + appendSpace(configPrefix), + appendSpace(taskPrefix), + sep, + postfix + ) + } } private[sbt] def appendSpace(s: String): String = diff --git a/main-settings/src/test/scala/sbt/ScopeDisplaySpec.scala b/main-settings/src/test/scala/sbt/ScopeDisplaySpec.scala new file mode 100644 index 000000000..6c7798d67 --- /dev/null +++ b/main-settings/src/test/scala/sbt/ScopeDisplaySpec.scala @@ -0,0 +1,50 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt + +import org.scalatest.FlatSpec +import sbt.internal.util.{ AttributeKey, AttributeMap } +import sbt.io.syntax.file + +class ScopeDisplaySpec extends FlatSpec { + val project = ProjectRef(file("foo/bar"), "bar") + val mangledName = "bar_slash_blah_blah_blah" + val scopedKey = Def.ScopedKey(Scope.Global in project, AttributeKey[Task[String]](mangledName)) + val am = AttributeMap.empty.put(Scope.customShowString, "blah") + val sanitizedKey = scopedKey.copy(scope = scopedKey.scope.copy(extra = Select(am))) + "Def.displayRelative2" should "display mangled name" in { + assert(Def.showRelativeKey2(project, None).show(scopedKey) == mangledName) + } + it should "display sanitized name with extra setting" in { + assert(Def.showRelativeKey2(project, None).show(sanitizedKey) == "blah") + } + "Scope.display" should "display the full scope" in { + val full = Scope.display( + scopedKey.scope, + "/", + (ref: Reference) => + ref match { + case ProjectRef(_, n) => n + case _ => ??? + } + ) + assert(full == "bar /") + } + it should "display the sanitized scope" in { + val string = Scope.display( + sanitizedKey.scope, + "/", + (ref: Reference) => + ref match { + case ProjectRef(_, n) => n + case _ => ??? + } + ) + assert(string == "blah") + } +}