From c358a8a5b03bb62e7310b69b6c2634b9d4135882 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Sat, 7 Apr 2012 18:10:23 -0400 Subject: [PATCH] more improvements to 'tasks' and 'settings' commands --- main/CommandStrings.scala | 14 ++++++++---- main/Keys.scala | 2 +- main/Main.scala | 37 +++++++++++++++++++++----------- main/command/BasicCommands.scala | 36 +++---------------------------- main/command/CommandUtil.scala | 37 ++++++++++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 51 deletions(-) diff --git a/main/CommandStrings.scala b/main/CommandStrings.scala index dbd87867e..687e0f6f9 100644 --- a/main/CommandStrings.scala +++ b/main/CommandStrings.scala @@ -126,13 +126,19 @@ It does not list the scopes the % 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 diff --git a/main/command/BasicCommands.scala b/main/command/BasicCommands.scala index 77a22db23..3a9128577 100644 --- a/main/command/BasicCommands.scala +++ b/main/command/BasicCommands.scala @@ -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)) diff --git a/main/command/CommandUtil.scala b/main/command/CommandUtil.scala index b600a5948..baaedd21a 100644 --- a/main/command/CommandUtil.scala +++ b/main/command/CommandUtil.scala @@ -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 + } \ No newline at end of file