Skip to content

Commit

Permalink
Fix parsing and evaluation of 'all' and 'show'. See comment in evalua…
Browse files Browse the repository at this point in the history
…tingParser.
  • Loading branch information
harrah authored and eed3si9n committed Mar 21, 2014
1 parent 73c1450 commit 49cb7e8
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 8 deletions.
5 changes: 3 additions & 2 deletions main/src/main/scala/sbt/Act.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ package sbt
import CommandStrings.{MultiTaskCommand, ShowCommand}

final class ParsedKey(val key: ScopedKey[_], val mask: ScopeMask)

object Act
{
val GlobalString = "*"
Expand All @@ -25,8 +26,8 @@ object Act

// the index should be an aggregated index for proper tab completion
def scopedKeyAggregated(current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String], structure: BuildStructure): KeysParser =
for(selected <- scopedKeySelected(structure.index.aggregateKeyIndex, current, defaultConfigs, structure.index.keyMap, structure.data) ) yield
Aggregation.aggregate(selected.key, selected.mask, structure.extra)
for(selected <- scopedKeySelected(structure.index.aggregateKeyIndex, current, defaultConfigs, structure.index.keyMap, structure.data) ) yield
Aggregation.aggregate(selected.key, selected.mask, structure.extra)

def scopedKeySelected(index: KeyIndex, current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String],
keyMap: Map[String, AttributeKey[_]], data: Settings[Scope]): Parser[ParsedKey] =
Expand Down
50 changes: 44 additions & 6 deletions main/src/main/scala/sbt/Aggregation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,51 @@ final object Aggregation
}

def evaluatingParser(s: State, structure: BuildStructure, show: ShowConfig)(keys: Seq[KeyValue[_]])(implicit display: Show[ScopedKey[_]]): Parser[() => State] =
keys.toList match
{
case Nil => failure("No such setting/task")
case xs @ KeyValue(_, _: InputTask[t]) :: _ => applyDynamicTasks(s, structure, xs.asInstanceOf[Values[InputTask[t]]], show)
case xs @ KeyValue(_, _: Task[t]) :: _ => applyTasks(s, structure, maps(xs.asInstanceOf[Values[Task[t]]])(x => success(x)), show)
case xs => success(() => { if(show.settingValues) printSettings(xs, show.print); s} )
{
// to make the call sites clearer
def separate[L](in: Seq[KeyValue[_]])(f: KeyValue[_] => Either[KeyValue[L], KeyValue[_]]): (Seq[KeyValue[L]], Seq[KeyValue[_]]) =
Util.separate(in)(f)

val kvs = keys.toList
if(kvs.isEmpty)
failure("No such setting/task")
else {
val (inputTasks, other) = separate[InputTask[_]](kvs) {
case KeyValue(k,v: InputTask[_]) => Left(KeyValue(k,v))
case kv => Right(kv)
}
val (tasks, settings) = separate[Task[_]](other) {
case KeyValue(k, v: Task[_]) => Left(KeyValue(k,v))
case kv => Right(kv)
}
// currently, disallow input tasks to be mixed with normal tasks.
// This occurs in `all` or `show`, which support multiple tasks.
// Previously, multiple tasks could be run in one execution, but they were all for the same key, just in different scopes.
// When `all` was added, it allowed different keys and thus opened the possibility for mixing settings,
// tasks, and input tasks in the same call. The code below allows settings and tasks to be mixed, but not input tasks.
// One problem with input tasks in `all` is that many input tasks consume all input and would need syntactic delimiters.
// Once that is addressed, the tasks constructed by the input tasks would need to be combined with the explicit tasks.
if(inputTasks.size > 0) {
if(other.size > 0) {
val inputStrings = inputTasks.map(_.key).mkString("Input task(s):\n\t", "\n\t", "\n")
val otherStrings = other.map(_.key).mkString("Task(s)/setting(s):\n\t", "\n\t", "\n")
failure(s"Cannot mix input tasks with plain tasks/settings. $inputStrings $otherStrings")
} else
applyDynamicTasks(s, structure, maps(inputTasks)(castToAny), show)
} else {
val base = if(tasks.isEmpty) success( () => s ) else
applyTasks(s, structure, maps(tasks)(x => success( castToAny(x))), show)
base.map { res => () =>
val newState = res()
if(show.settingValues && !settings.isEmpty) printSettings(settings, show.print)
newState
}
}
}
}
// this is a hack to avoid duplicating method implementations
private[this] def castToAny[T[_]](t: T[_]): T[Any] = t.asInstanceOf[T[Any]]

private[this] def maps[T, S](vs: Values[T])(f: T => S): Values[S] =
vs map { case KeyValue(k,v) => KeyValue(k, f(v)) }

Expand Down

0 comments on commit 49cb7e8

Please sign in to comment.