From 4e4937e706c546c64bc74ba3a591c4baf303c483 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Mon, 12 Mar 2012 19:54:18 -0400 Subject: [PATCH] searchable help --- main/Output.scala | 16 +--------- main/command/BasicCommandStrings.scala | 17 +++++++--- main/command/BasicCommands.scala | 43 ++++++++++++++++++++------ main/command/Highlight.scala | 25 +++++++++++++++ 4 files changed, 73 insertions(+), 28 deletions(-) create mode 100644 main/command/Highlight.scala diff --git a/main/Output.scala b/main/Output.scala index 665e24a81..2964f87da 100644 --- a/main/Output.scala +++ b/main/Output.scala @@ -9,24 +9,11 @@ package sbt import Project.ScopedKey import Aggregation.{KeyValue, Values} import Types.idFun + import Highlight.{bold, showMatches} import annotation.tailrec - import scala.Console.{BOLD, RESET} object Output { - def showMatches(pattern: Pattern)(line: String): Option[String] = - { - val matcher = pattern.matcher(line) - if(ConsoleLogger.formatEnabled) - { - val highlighted = matcher.replaceAll(scala.Console.RED + "$0" + scala.Console.RESET) - if(highlighted == line) None else Some(highlighted) - } - else if(matcher.find) - Some(line) - else - None - } final val DefaultTail = "> " def last(keys: Values[_], streams: Streams, printLines: Seq[String] => Unit)(implicit display: Show[ScopedKey[_]]): Unit = @@ -54,7 +41,6 @@ object Output if(!single) bold(display(key)) +: flines else flines } } - def bold(s: String) = if(ConsoleLogger.formatEnabled) BOLD + s + RESET else s def lastLines(keys: Values[_], streams: Streams): Values[Seq[String]] = { diff --git a/main/command/BasicCommandStrings.scala b/main/command/BasicCommandStrings.scala index ba1c32010..81079f190 100644 --- a/main/command/BasicCommandStrings.scala +++ b/main/command/BasicCommandStrings.scala @@ -18,10 +18,19 @@ object BasicCommandStrings /** The command name to terminate the program.*/ val TerminateAction: String = Exit - 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. -Otherwise, this prints a help summary.""" + def helpBrief = (HelpCommand + " [command]", "Displays this help message or prints detailed help on requested commands.") + def helpDetailed = HelpCommand + """ + + Prints a help summary. + +""" + HelpCommand + """ + + Prints detailed help for command . + +""" + HelpCommand + """ + + Searches the help according to the provided regular expression. +""" def historyHelp = Help.briefDetail(HistoryCommands.descriptions) diff --git a/main/command/BasicCommands.scala b/main/command/BasicCommands.scala index 5d0c29e42..1d25e88db 100644 --- a/main/command/BasicCommands.scala +++ b/main/command/BasicCommands.scala @@ -12,6 +12,7 @@ package sbt import BasicKeys._ import java.io.File + import java.util.regex.Pattern object BasicCommands { @@ -26,22 +27,46 @@ object BasicCommands { val h = (Help.empty /: s.definedCommands)(_ ++ _.help(s)) val helpCommands = h.detail.keySet - val args = (token(Space) ~> token( NotSpace examples helpCommands )).* - applyEffect(args)(runHelp(s, h)) + val arg = (NotSpaceClass ~ any.*) map { case (ns, s) => (ns +: s).mkString } + val spacedArg = token(Space) ~> token( arg examples helpCommands ).? + applyEffect(spacedArg)(runHelp(s, h)) } - def runHelp(s: State, h: Help)(args: Seq[String]): State = + def runHelp(s: State, h: Help)(arg: Option[String]): State = { - val message = - if(args.isEmpty) + val message = arg match { + case None => aligned(" ", " ", h.brief).mkString("\n", "\n", "\n") - else - detail(args, h.detail) mkString("\n", "\n\n", "\n") + case Some(x) => + detail(x, h.detail) + } System.out.println(message) s } - def detail(selected: Seq[String], detailMap: Map[String, String]): Seq[String] = - selected.distinct flatMap { detailMap get _ } + 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]] = { diff --git a/main/command/Highlight.scala b/main/command/Highlight.scala new file mode 100644 index 000000000..0713017e0 --- /dev/null +++ b/main/command/Highlight.scala @@ -0,0 +1,25 @@ +package sbt + + import java.util.regex.Pattern + import scala.Console.{BOLD, RESET} + +object Highlight +{ + final val NormalIntensity = "\033[22m" + final val NormalTextColor = "\033[39m" + + def showMatches(pattern: Pattern)(line: String): Option[String] = + { + val matcher = pattern.matcher(line) + if(ConsoleLogger.formatEnabled) + { + val highlighted = matcher.replaceAll(scala.Console.RED + "$0" + NormalTextColor) + if(highlighted == line) None else Some(highlighted) + } + else if(matcher.find) + Some(line) + else + None + } + def bold(s: String) = if(ConsoleLogger.formatEnabled) BOLD + s + NormalIntensity else s +} \ No newline at end of file