diff --git a/main-settings/src/main/scala/sbt/Structure.scala b/main-settings/src/main/scala/sbt/Structure.scala index 78f5c995cf..7d94c85319 100644 --- a/main-settings/src/main/scala/sbt/Structure.scala +++ b/main-settings/src/main/scala/sbt/Structure.scala @@ -312,22 +312,30 @@ object Scoped: * one setting in order to define another setting. * @return currently bound value wrapped in `Initialize[Some[T]]`, or `Initialize[None]` if unbound. */ - final def ? : Initialize[Option[A1]] = Def.optional(scopedKey)(identity) + def option: Initialize[Option[A1]] = Def.optional(scopedKey)(identity) + + /** + * A symbolic alias for [[option]]. + */ + def ? : Initialize[Option[A1]] = option /** * Creates an [[Def.Initialize]] with value bound to this key, or returns `i` parameter if unbound. * @param i value to return if this setting doesn't have a value. * @return currently bound setting value, or `i` if unbound. */ - final def or[T >: A1](i: Initialize[T]): Initialize[T] = ?.zipWith(i)(_.getOrElse(_)) + infix def or[T >: A1](i: Initialize[T]): Initialize[T] = option.zipWith(i)(_.getOrElse(_)) /** - * Like [[?]], but with a call-by-name parameter rather than an existing [[Def.Initialize]]. + * Like [[option]], but with a call-by-name parameter rather than an existing [[Def.Initialize]]. * Useful when you want to have a value computed when no value is bound to this key. * @param or by-name expression evaluated when a value is needed. * @return currently bound setting value, or the result of `or` if unbound. */ - final def ??[T >: A1](or: => T): Initialize[T] = Def.optional(scopedKey)(_ getOrElse or) + final def getOrElse[A2 >: A1](or: => A2): Initialize[A2] = + Def.optional(scopedKey)(_ getOrElse or) + + final def ??[A2 >: A1](or: => A2): Initialize[A2] = getOrElse(or) } private[sbt] trait Syntax: @@ -450,27 +458,37 @@ object Scoped: inline def transform(f: A1 => A1): Setting[Task[A1]] = set(scopedKey(_ map f)) - // @deprecated( - // "No longer needed with new task syntax and SettingKey inheriting from Initialize.", - // "0.13.2" - // ) - // def task: SettingKey[Task[A1]] = scopedSetting(scope, key) - def toSettingKey: SettingKey[Task[A1]] = scopedSetting(scope, key) def get(settings: Settings[Scope]): Option[Task[A1]] = settings.get(scope, key) - def ? : Initialize[Task[Option[A1]]] = Def.optional(scopedKey) { + /** + * Creates an [[Def.Initialize]] with value `scala.None` if there was no previous definition of this key, + * and `scala.Some(value)` if a definition exists. Useful for when you want to use the ''existence'' of + * one setting in order to define another setting. + * @return currently bound value wrapped in `Initialize[Some[T]]`, or `Initialize[None]` if unbound. + */ + def option: Initialize[Task[Option[A1]]] = Def.optional(scopedKey) { case None => mktask { None } case Some(t) => t.map(Some.apply) } - def ??[T >: A1](or: => T): Initialize[Task[T]] = Def.optional(scopedKey)(_ getOrElse mktask(or)) + /** + * A symbolic alias for [[option]]. + */ + def ? : Initialize[Task[Option[A1]]] = option + + def getOrElse[A2 >: A1](or: => A2): Initialize[Task[A2]] = + Def.optional(scopedKey)(_.getOrElse(mktask(or))) + + def ??[A2 >: A1](or: => A2): Initialize[Task[A2]] = getOrElse(or) - // def or[A2 >: A1](i: Initialize[Task[A2]]): Initialize[Task[A2]] = - // this.?.zipWith(i) { (toa1: Task[Option[A1]], ta2: Task[A2]) => - // (toa1, ta2).map { case (oa1: Option[A1], a2: A2) => oa1 getOrElse b2 } - // } + infix def or[A2 >: A1](i: Initialize[Task[A2]]): Initialize[Task[A2]] = + this.?.zipWith(i) { (toa1: Task[Option[A1]], ta2: Task[A2]) => + (toa1, ta2).mapN { case (oa1: Option[A1], a2: A2) => + oa1.getOrElse(a2) + } + } } private def coerceToAnyTaskSeq(tasks: Seq[AnyInitTask]): Seq[Def.Initialize[Task[Any]]] = diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 5e0c9eba64..abad8e7f84 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -1111,8 +1111,8 @@ object Defaults extends BuildCommon { val interval = pollInterval.value val _antiEntropy = watchAntiEntropy.value val base = thisProjectRef.value - val msg = watchingMessage.?.value.getOrElse(Watched.defaultWatchingMessage) - val trigMsg = triggeredMessage.?.value.getOrElse(Watched.defaultTriggeredMessage) + val msg = watchingMessage.getOrElse(Watched.defaultWatchingMessage).value + val trigMsg = triggeredMessage.getOrElse(Watched.defaultTriggeredMessage).value new Watched { val scoped = (base / watchTransitiveSources) val key = scoped.scopedKey diff --git a/sbt-app/src/sbt-test/project/task/build.sbt b/sbt-app/src/sbt-test/project/task/build.sbt new file mode 100644 index 0000000000..d19ac10035 --- /dev/null +++ b/sbt-app/src/sbt-test/project/task/build.sbt @@ -0,0 +1,24 @@ +lazy val undefinedIntTask = taskKey[Int]("") +lazy val intTask1 = taskKey[Int]("") +lazy val orResultTask = taskKey[Int]("") +lazy val checkOption = taskKey[Unit]("") +lazy val checkGetOrElse = taskKey[Unit]("") +lazy val checkOr = taskKey[Unit]("") + +intTask1 := 1 + +checkOption := { + val x = undefinedIntTask.option.value + assert(x == None) +} + +checkGetOrElse := { + val x = undefinedIntTask.getOrElse(10).value + assert(x == 10) +} + +orResultTask := (undefinedIntTask or intTask1).value +checkOr := { + val x = orResultTask.value + assert(x == 1) +} diff --git a/sbt-app/src/sbt-test/project/task/test b/sbt-app/src/sbt-test/project/task/test new file mode 100644 index 0000000000..80c2ee2af6 --- /dev/null +++ b/sbt-app/src/sbt-test/project/task/test @@ -0,0 +1,5 @@ +> checkOption + +> checkGetOrElse + +> checkOr