diff --git a/main/CommandSupport.scala b/main/CommandSupport.scala index a50808d7f..261d2d3c8 100644 --- a/main/CommandSupport.scala +++ b/main/CommandSupport.scala @@ -33,6 +33,7 @@ object CommandSupport /** The prefix used to identify a request to execute the remaining input on source changes.*/ val ContinuousExecutePrefix = "~" val HelpCommand = "help" + val TasksCommand = "tasks" val ProjectCommand = "project" val ProjectsCommand = "projects" @@ -108,6 +109,15 @@ SetCommand + """ def continuousBriefHelp = (ContinuousExecutePrefix + " ", "Executes the specified command whenever source files change.") + def tasksPreamble = """ +This is a list of tasks defined for the current project. +It does not list the scopes the tasks are defined in; use the 'inspect' command for that. +Tasks produce values. Use the 'show' command to run the task and print the resulting value. +""" + + def tasksBrief = "Displays the tasks defined for the current project." + def tasksDetailed = TasksCommand + "\n\t" + tasksBrief + def helpBrief = (HelpCommand + " command*", "Displays this help message or prints detailed help on requested commands.") def helpDetailed = "If an argument is provided, this prints detailed help for that command.\nOtherwise, this prints a help summary." diff --git a/main/KeyIndex.scala b/main/KeyIndex.scala index 055c35f91..78955d9f3 100644 --- a/main/KeyIndex.scala +++ b/main/KeyIndex.scala @@ -6,7 +6,7 @@ package sbt import java.net.URI import Project.ScopedKey import complete.DefaultParsers.validID - import Types.idFun + import Types.{idFun, some} object KeyIndex { @@ -19,6 +19,7 @@ object KeyIndex def configs(proj: Option[ResolvedReference]) = concat(_.configs(proj)) def tasks(proj: Option[ResolvedReference], conf: Option[String]) = concat(_.tasks(proj, conf)) def tasks(proj: Option[ResolvedReference], conf: Option[String], key: String) = concat(_.tasks(proj, conf, key)) + def keys(proj: Option[ResolvedReference]) = concat(_.keys(proj)) def keys(proj: Option[ResolvedReference], conf: Option[String]) = concat(_.keys(proj, conf)) def keys(proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]]) = concat(_.keys(proj, conf, task)) def concat[T](f: KeyIndex => Set[T]): Set[T] = @@ -40,6 +41,7 @@ trait KeyIndex def configs(proj: Option[ResolvedReference]): Set[String] def tasks(proj: Option[ResolvedReference], conf: Option[String]): Set[AttributeKey[_]] def tasks(proj: Option[ResolvedReference], conf: Option[String], key: String): Set[AttributeKey[_]] + def keys(proj: Option[ResolvedReference]): Set[String] def keys(proj: Option[ResolvedReference], conf: Option[String]): Set[String] def keys(proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]]): Set[String] } @@ -84,7 +86,8 @@ private final class KeyIndex0(val data: BuildIndex) extends ExtendableKeyIndex def configs(project: Option[ResolvedReference]): Set[String] = confIndex(project).configs def tasks(proj: Option[ResolvedReference], conf: Option[String]): Set[AttributeKey[_]] = keyIndex(proj, conf).tasks def tasks(proj: Option[ResolvedReference], conf: Option[String], key: String): Set[AttributeKey[_]] = keyIndex(proj, conf).tasks(key) - def keys(project: Option[ResolvedReference], conf: Option[String]): Set[String] = keyIndex(project, conf).allKeys + def keys(proj: Option[ResolvedReference]): Set[String] = (Set.empty[String] /: optConfigs(proj)) { (s,c) => s ++ keys(proj, c) } + def keys(proj: Option[ResolvedReference], conf: Option[String]): Set[String] = keyIndex(proj, conf).allKeys def keys(proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]]): Set[String] = keyIndex(proj, conf).keys(task) def keyIndex(proj: Option[ResolvedReference], conf: Option[String]): AKeyIndex = @@ -101,6 +104,7 @@ private final class KeyIndex0(val data: BuildIndex) extends ExtendableKeyIndex case Some(BuildRef(uri)) => (Some(uri), None) case _ => (None, None) } + private[this] def optConfigs(project: Option[ResolvedReference]): Seq[Option[String]] = None +: (configs(project).toSeq map some.fn) def add(scoped: ScopedKey[_]): ExtendableKeyIndex = if(validID(scoped.key.label)) add0(scoped) else this diff --git a/main/Main.scala b/main/Main.scala index b2e82e74f..053f5ba17 100644 --- a/main/Main.scala +++ b/main/Main.scala @@ -85,7 +85,7 @@ object BuiltinCommands def ConsoleCommands: Seq[Command] = Seq(ignore, exit, IvyConsole.command, act, nop) def ScriptCommands: Seq[Command] = Seq(ignore, exit, Script.command, act, nop) def DefaultCommands: Seq[Command] = Seq(ignore, help, reboot, read, history, continuous, exit, loadProject, loadProjectImpl, loadFailed, Cross.crossBuild, Cross.switchVersion, - projects, project, setOnFailure, clearOnFailure, ifLast, multi, shell, set, inspect, eval, alias, append, last, lastGrep, nop, sessionCommand, act) + projects, project, setOnFailure, clearOnFailure, ifLast, multi, shell, set, tasks, inspect, eval, alias, append, last, lastGrep, nop, sessionCommand, act) def DefaultBootCommands: Seq[String] = LoadProject :: (IfLast + " " + Shell) :: Nil def nop = Command.custom(s => success(() => s)) @@ -108,13 +108,34 @@ object BuiltinCommands { val message = if(args.isEmpty) - h.map( _.brief match { case (a,b) => a + " : " + b } ).mkString("\n", "\n", "\n") + aligned(" ", " ", h.map(_.brief)).mkString("\n", "\n", "\n") else h flatMap detail(args) mkString("\n", "\n\n", "\n") System.out.println(message) s } + def tasks = Command.command(TasksCommand, tasksBrief, tasksDetailed) { s => + System.out.println(tasksPreamble) + System.out.println(tasksHelp(s)) + s + } + def tasksHelp(s: State): String = + { + val extracted = Project.extract(s) + import extracted._ + val index = structure.index + val pairs = index.keyIndex.keys(Some(currentRef)).toSeq map index.keyMap sortBy(_.label) flatMap taskStrings + aligned(" ", " ", pairs) mkString("\n", "\n", "") + } + def taskStrings(key: AttributeKey[_]): Option[(String, String)] = key.description map { d => (key.label, d) } + def aligned(pre: String, sep: String, in: Seq[(String, String)]): Seq[String] = + { + val width = in.map(_._1.length).max + in.map { case (a, b) => (" " + fill(a, width) + sep + b) } + } + def fill(s: String, size: Int) = s + " " * math.max(size - s.length, 0) + def alias = Command.make(AliasCommand, AliasBrief, AliasDetailed) { s => val name = token(OpOrID.examples( aliasNames(s) : _*) ) val assign = token(OptSpace ~ '=' ~ OptSpace)