more improvements to 'tasks' and 'settings' commands

This commit is contained in:
Mark Harrah 2012-04-07 18:10:23 -04:00
parent 75dfccf098
commit c358a8a5b0
5 changed files with 75 additions and 51 deletions

View File

@ -126,13 +126,19 @@ It does not list the scopes the %<s are defined in; use the 'inspect' command fo
def settingsBrief(label: String) = (label, "Displays the " + label + " defined for the current project.")
def settingsDetailed(label: String) =
"""%s -[v|vv|...|V]
"""%s [-(v|vv|...|V)] [filter]
Displays the %<s defined directly or indirectly for the current project.
Additional %<s may be displayed by providing -v, -vv, ... or -V for all %<s.
""".format(label)
Additional %<s may be displayed by providing -v, -vv, ... or providing
-V to display all %<s.
def moreAvailableMessage(label: String) = "More " + label + " may be viewed by increasing verbosity. See '" + BasicCommandStrings.HelpCommand + " " + label + "'.\n"
A filter may be given to restrict the %<s shown. The names of %<s are searched for an exact match against the filter, in which case only the description of the exact match is displayed. Otherwise, the filter is interpreted as a regular expression and all %<s whose name or description match the regular expression are displayed.
Note that this is an additional filter on top of the %<s selected by the -v style switches, so you must specify -V to search all tasks. Use the %s command to search all commands, tasks, and settings at once.
""".format(label, BasicCommandStrings.HelpCommand)
def moreAvailableMessage(label: String, search: Boolean) =
"More %s may be %s by increasing verbosity. See '%s %s'.\n".format(label, if(search) "searched" else "viewed", BasicCommandStrings.HelpCommand, label)
def aboutBrief = "Displays basic information about sbt and the build."
def aboutDetailed = aboutBrief

View File

@ -241,7 +241,7 @@ object Keys
val updateConfiguration = SettingKey[UpdateConfiguration]("update-configuration", "Configuration for resolving and retrieving managed dependencies.", DSetting)
val ivySbt = TaskKey[IvySbt]("ivy-sbt", "Provides the sbt interface to Ivy.", CTask)
val ivyModule = TaskKey[IvySbt#Module]("ivy-module", "Provides the sbt interface to a configured Ivy module.", CTask)
val update = TaskKey[UpdateReport]("update", "Resolves and optionally retrieves dependencies, producing a report.", ASetting)
val update = TaskKey[UpdateReport]("update", "Resolves and optionally retrieves dependencies, producing a report.", ATask)
val transitiveUpdate = TaskKey[Seq[UpdateReport]]("transitive-update", "UpdateReports for the internal dependencies of this project.", DTask)
val updateClassifiers = TaskKey[UpdateReport]("update-classifiers", "Resolves and optionally retrieves classified artifacts, such as javadocs and sources, for dependency definitions, transitively.", BPlusTask, update)
val transitiveClassifiers = SettingKey[Seq[String]]("transitive-classifiers", "List of classifiers used for transitively obtaining extra artifacts for sbt or declared dependencies.", BSetting)

View File

@ -131,21 +131,26 @@ object BuiltinCommands
def tasks = showSettingLike(TasksCommand, tasksPreamble, KeyRanks.MainTaskCutoff, key => isTask(key.manifest) )
def showSettingLike(command: String, preamble: String, cutoff: Int, keep: AttributeKey[_] => Boolean) =
Command(command, settingsBrief(command), settingsDetailed(command))(const(verbosityParser)) { (s: State, verbosity: Int) =>
System.out.println(preamble)
val prominentOnly = verbosity <= 1
val verboseFilter = if(prominentOnly) highPass(cutoff) else topNRanked(25*verbosity)
System.out.println(tasksHelp(s, keys => verboseFilter(keys filter keep) ))
System.out.println()
if(prominentOnly) System.out.println(moreAvailableMessage(command))
s
Command(command, settingsBrief(command), settingsDetailed(command))(showSettingParser(keep)) {
case (s: State, (verbosity: Int, selected: Option[String])) =>
if(selected.isEmpty) System.out.println(preamble)
val prominentOnly = verbosity <= 1
val verboseFilter = if(prominentOnly) highPass(cutoff) else topNRanked(25*verbosity)
System.out.println(tasksHelp(s, keys => verboseFilter(keys filter keep), selected ))
System.out.println()
if(prominentOnly) System.out.println(moreAvailableMessage(command, selected.isDefined))
s
}
def showSettingParser(keepKeys: AttributeKey[_] => Boolean)(s: State): Parser[(Int, Option[String])] =
verbosityParser ~ selectedParser(s, keepKeys).?
def selectedParser(s: State, keepKeys: AttributeKey[_] => Boolean): Parser[String] =
singleArgument( allTaskAndSettingKeys(s).filter(keepKeys).map(_.label).toSet )
def verbosityParser: Parser[Int] = success(1) | ((Space ~ "-") ~> (
'v'.id.+.map(_.size + 1) |
("V" ^^^ Int.MaxValue)
) )
def taskDetail(keys: Seq[AttributeKey[_]], filter: Seq[AttributeKey[_]] => Seq[AttributeKey[_]]): Seq[(String,String)] =
sortByLabel(filter(withDescription(keys))) flatMap taskStrings
def taskDetail(keys: Seq[AttributeKey[_]]): Seq[(String,String)] =
sortByLabel(withDescription(keys)) flatMap taskStrings
def allTaskAndSettingKeys(s: State): Seq[AttributeKey[_]] =
{
@ -163,8 +168,14 @@ object BuiltinCommands
def topNRanked(n: Int) = (keys: Seq[AttributeKey[_]]) => sortByRank(keys).take(n)
def highPass(rankCutoff: Int) = (keys: Seq[AttributeKey[_]]) => sortByRank(keys).takeWhile(_.rank <= rankCutoff)
def tasksHelp(s: State, filter: Seq[AttributeKey[_]] => Seq[AttributeKey[_]]): String =
aligned(" ", " ", taskDetail(allTaskAndSettingKeys(s), filter)) mkString("\n", "\n", "")
def tasksHelp(s: State, filter: Seq[AttributeKey[_]] => Seq[AttributeKey[_]], arg: Option[String]): String =
{
val commandAndDescription = taskDetail(filter(allTaskAndSettingKeys(s)))
arg match {
case Some(selected) =>detail(selected, commandAndDescription.toMap)
case None => aligned(" ", " ", commandAndDescription) mkString("\n", "\n", "")
}
}
def taskStrings(key: AttributeKey[_]): Option[(String, String)] = key.description map { d => (key.label, d) }
@ -314,7 +325,7 @@ object BuiltinCommands
def actHelp = (s: State) => CommandStrings.showHelp ++ keysHelp(s)
def keysHelp(s: State): Help =
if(Project.isProjectLoaded(s))
Help.detailOnly(taskDetail(allTaskAndSettingKeys(s), idFun))
Help.detailOnly(taskDetail(allTaskAndSettingKeys(s)))
else
Help.empty

View File

@ -12,7 +12,6 @@ package sbt
import BasicKeys._
import java.io.File
import java.util.regex.Pattern
object BasicCommands
{
@ -27,47 +26,18 @@ object BasicCommands
{
val h = (Help.empty /: s.definedCommands)(_ ++ _.help(s))
val helpCommands = h.detail.keySet
val arg = (NotSpaceClass ~ any.*) map { case (ns, s) => (ns +: s).mkString }
val spacedArg = (token(Space) ~> token( arg examples helpCommands )).?
val spacedArg = singleArgument(helpCommands).?
applyEffect(spacedArg)(runHelp(s, h))
}
def runHelp(s: State, h: Help)(arg: Option[String]): State =
{
val message = arg match {
case None =>
aligned(" ", " ", h.brief).mkString("\n", "\n", "\n")
case Some(x) =>
detail(x, h.detail)
case None => aligned(" ", " ", h.brief).mkString("\n", "\n", "\n")
case Some(x) => detail(x, h.detail)
}
System.out.println(message)
s
}
def detail(selected: String, detailMap: Map[String, String]): String =
detailMap.get(selected) match
{
case Some(exactDetail) =>exactDetail
case None => layoutDetails(searchHelp(selected, detailMap))
}
def searchHelp(selected: String, detailMap: Map[String, String]): Map[String, String] =
{
val pattern = Pattern.compile(selected, HelpPatternFlags)
detailMap flatMap { case (k,v) =>
val contentMatches = Highlight.showMatches(pattern)(v)
val keyMatches = Highlight.showMatches(pattern)(k)
val keyString = Highlight.bold(keyMatches getOrElse k)
val contentString = contentMatches getOrElse v
if(keyMatches.isDefined || contentMatches.isDefined)
(keyString, contentString) :: Nil
else
Nil
}
}
def layoutDetails(details: Map[String,String]): String =
details.map { case (k,v) => k + ":\n\n " + v } mkString("\n", "\n\n", "\n")
final val HelpPatternFlags = Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE
def multiParser(s: State): Parser[Seq[String]] =
{
val nonSemi = token(charClass(_ != ';').+, hide= const(true))

View File

@ -1,6 +1,10 @@
package sbt
import java.io.File
import java.util.regex.Pattern
import complete.Parser
import complete.DefaultParsers._
object CommandUtil
{
@ -29,4 +33,37 @@ object CommandUtil
case None => s.log.error(ifMissing); s.fail
case Some(nav) => f(nav)
}
def singleArgument(exampleStrings: Set[String]): Parser[String] =
{
val arg = (NotSpaceClass ~ any.*) map { case (ns, s) => (ns +: s).mkString }
token(Space) ~> token( arg examples exampleStrings )
}
def detail(selected: String, detailMap: Map[String, String]): String =
detailMap.get(selected) match
{
case Some(exactDetail) =>exactDetail
case None =>
val details = searchHelp(selected, detailMap)
if(details.isEmpty) "No matches for regular expression '" + selected + "'." else layoutDetails(details)
}
def searchHelp(selected: String, detailMap: Map[String, String]): Map[String, String] =
{
val pattern = Pattern.compile(selected, HelpPatternFlags)
detailMap flatMap { case (k,v) =>
val contentMatches = Highlight.showMatches(pattern)(v)
val keyMatches = Highlight.showMatches(pattern)(k)
val keyString = Highlight.bold(keyMatches getOrElse k)
val contentString = contentMatches getOrElse v
if(keyMatches.isDefined || contentMatches.isDefined)
(keyString, contentString) :: Nil
else
Nil
}
}
def layoutDetails(details: Map[String,String]): String =
details.map { case (k,v) => k + ":\n\n " + v } mkString("\n", "\n\n", "\n")
final val HelpPatternFlags = Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE
}