reorganize help, allow keys to be arguments to 'help' command

This commit is contained in:
Mark Harrah 2011-10-20 22:59:31 -04:00
parent 078b72ee48
commit e3e23287af
3 changed files with 63 additions and 38 deletions

View File

@ -5,18 +5,20 @@ package sbt
import java.io.File
import complete.{DefaultParsers, EditDistance, Parser}
import Types.const
sealed trait Command {
def help: Seq[Help]
def help: State => Help
def parser: State => Parser[() => State]
def tags: AttributeMap
def tag[T](key: AttributeKey[T], value: T): Command
}
private[sbt] final class SimpleCommand(val name: String, val help: Seq[Help], val parser: State => Parser[() => State], val tags: AttributeMap) extends Command {
private[sbt] final class SimpleCommand(val name: String, help0: Help, val parser: State => Parser[() => State], val tags: AttributeMap) extends Command {
assert(Command validID name, "'" + name + "' is not a valid command name." )
def tag[T](key: AttributeKey[T], value: T): SimpleCommand = new SimpleCommand(name, help, parser, tags.put(key, value))
def tag[T](key: AttributeKey[T], value: T): SimpleCommand = new SimpleCommand(name, help0, parser, tags.put(key, value))
def help = const(help0)
}
private[sbt] final class ArbitraryCommand(val parser: State => Parser[() => State], val help: Seq[Help], val tags: AttributeMap) extends Command
private[sbt] final class ArbitraryCommand(val parser: State => Parser[() => State], val help: State => Help, val tags: AttributeMap) extends Command
{
def tag[T](key: AttributeKey[T], value: T): ArbitraryCommand = new ArbitraryCommand(parser, help, tags.put(key, value))
}
@ -27,32 +29,32 @@ object Command
import DefaultParsers._
def command(name: String)(f: State => State): Command = command(name, Nil)(f)
def command(name: String, briefHelp: String, detail: String)(f: State => State): Command = command(name, Help(name, (name, briefHelp), detail) :: Nil)(f)
def command(name: String, help: Seq[Help])(f: State => State): Command = make(name, help : _*)(state => success(() => f(state)))
def command(name: String, briefHelp: String, detail: String)(f: State => State): Command = command(name, Help(name, (name, briefHelp), detail))(f)
def command(name: String, help: Help = Help.empty)(f: State => State): Command = make(name, help)(state => success(() => f(state)))
def make(name: String, briefHelp: (String, String), detail: String)(parser: State => Parser[() => State]): Command =
make(name, Help(name, briefHelp, detail) )(parser)
def make(name: String, help: Help*)(parser: State => Parser[() => State]): Command = new SimpleCommand(name, help, parser, AttributeMap.empty)
def make(name: String, help: Help)(parser: State => Parser[() => State]): Command = new SimpleCommand(name, help, parser, AttributeMap.empty)
def apply[T](name: String, briefHelp: (String, String), detail: String)(parser: State => Parser[T])(effect: (State,T) => State): Command =
apply(name, Help(name, briefHelp, detail) )(parser)(effect)
def apply[T](name: String, help: Help*)(parser: State => Parser[T])(effect: (State,T) => State): Command =
make(name, help : _* )(applyEffect(parser)(effect) )
def apply[T](name: String, help: Help = Help.empty)(parser: State => Parser[T])(effect: (State,T) => State): Command =
make(name, help)(applyEffect(parser)(effect) )
def args(name: String, briefHelp: (String, String), detail: String, display: String)(f: (State, Seq[String]) => State): Command =
args(name, display, Help(name, briefHelp, detail) )(f)
def args(name: String, display: String, help: Help*)(f: (State, Seq[String]) => State): Command =
make(name, help : _*)( state => spaceDelimited(display) map apply1(f, state) )
def args(name: String, display: String, help: Help = Help.empty)(f: (State, Seq[String]) => State): Command =
make(name, help)( state => spaceDelimited(display) map apply1(f, state) )
def single(name: String, briefHelp: (String, String), detail: String)(f: (State, String) => State): Command =
single(name, Help(name, briefHelp, detail) )(f)
def single(name: String, help: Help*)(f: (State, String) => State): Command =
make(name, help : _*)( state => token(trimmed(spacedAny(name)) map apply1(f, state)) )
def single(name: String, help: Help = Help.empty)(f: (State, String) => State): Command =
make(name, help)( state => token(trimmed(spacedAny(name)) map apply1(f, state)) )
def custom(parser: State => Parser[() => State], help: Seq[Help] = Nil): Command = new ArbitraryCommand(parser, help, AttributeMap.empty)
def arb[T](parser: State => Parser[T], help: Help*)(effect: (State, T) => State): Command = custom(applyEffect(parser)(effect), help)
def custom(parser: State => Parser[() => State], help: Help = Help.empty): Command = customHelp(parser, const(help))
def customHelp(parser: State => Parser[() => State], help: State => Help): Command = new ArbitraryCommand(parser, help, AttributeMap.empty)
def arb[T](parser: State => Parser[T], help: Help = Help.empty)(effect: (State, T) => State): Command = custom(applyEffect(parser)(effect), help)
def validID(name: String) = DefaultParsers.matches(OpOrID, name)
@ -138,15 +140,29 @@ object Command
trait Help
{
def detail: (Set[String], String)
def brief: (String, String)
def detail: Map[String, String]
def brief: Seq[(String, String)]
def ++(o: Help): Help
}
private final class Help0(val brief: Seq[(String,String)], val detail: Map[String,String]) extends Help
{
def ++(h: Help): Help = new Help0(Help0.this.brief ++ h.brief, Help0.this.detail ++ h.detail)
}
object Help
{
def apply(name: String, briefHelp: (String, String), detail: String): Help = apply(briefHelp, (Set(name), detail))
val empty: Help = briefDetail(Nil)
def apply(briefHelp: (String, String), detailedHelp: (Set[String], String) = (Set.empty, "") ): Help =
new Help { def detail = detailedHelp; def brief = briefHelp }
def apply(name: String, briefHelp: (String, String), detail: String): Help = apply(briefHelp, Map( (name, detail) ) )
def apply(briefHelp: (String, String), detailedHelp: Map[String, String] = Map.empty ): Help =
apply(briefHelp :: Nil, detailedHelp)
def apply(briefHelp: Seq[(String,String)], detailedHelp: Map[String,String]): Help =
new Help0(briefHelp, detailedHelp)
def briefDetail(help: Seq[(String, String)]): Help = apply(help, help.toMap)
def briefOnly(help: Seq[(String, String)]): Help = apply(help, Map.empty[String,String])
def detailOnly(help: Seq[(String, String)]): Help = apply(Nil, help.toMap)
}
trait CommandDefinitions
{

View File

@ -50,7 +50,7 @@ EvalCommand + """ <expression>
Evaluates the given Scala expression and prints the result and type.
"""
def actHelp = Help(ShowCommand, (ShowCommand + " <key>", actBrief), actDetailed)
def showHelp = Help(ShowCommand, (ShowCommand + " <key>", actBrief), actDetailed)
def actBrief = "Displays the result of evaluating the setting or task associated with 'key'."
def actDetailed =
ShowCommand + """ <setting>
@ -160,9 +160,9 @@ ProjectCommand +
def projectsBrief = projectsDetailed
def projectsDetailed = "Displays the names of available projects."
def historyHelp = HistoryCommands.descriptions.map( d => Help(d) )
def historyHelp = Help.briefDetail(HistoryCommands.descriptions)
def exitBrief = (TerminateAction, "Terminates the build.")
def exitBrief = "Terminates the build."
def sbtrc = ".sbtrc"

View File

@ -106,27 +106,27 @@ object BuiltinCommands
def nop = Command.custom(s => success(() => s))
def ignore = Command.command(FailureWall)(idFun)
def detail(selected: Iterable[String])(h: Help): Option[String] =
h.detail match { case (commands, value) => if( selected exists commands ) Some(value) else None }
def detail(selected: Seq[String], detailMap: Map[String, String]): Seq[String] =
selected.distinct flatMap { detailMap get _ }
def help = Command.make(HelpCommand, helpBrief, helpDetailed)(helpParser)
def about = Command.command(AboutCommand, aboutBrief, aboutDetailed) { s => logger(s).info(aboutString(s)); s }
def helpParser(s: State) =
{
val h = s.definedCommands.flatMap(_.help)
val helpCommands = h.flatMap(_.detail._1)
val args = (token(Space) ~> token( OpOrID.examples(helpCommands : _*) )).*
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))
}
def runHelp(s: State, h: Seq[Help])(args: Seq[String]): State =
def runHelp(s: State, h: Help)(args: Seq[String]): State =
{
val message =
if(args.isEmpty)
aligned(" ", " ", h.map(_.brief)).mkString("\n", "\n", "\n")
aligned(" ", " ", h.brief).mkString("\n", "\n", "\n")
else
h flatMap detail(args) mkString("\n", "\n\n", "\n")
detail(args, h.detail) mkString("\n", "\n\n", "\n")
System.out.println(message)
s
}
@ -176,14 +176,17 @@ object BuiltinCommands
System.out.println(tasksHelp(s))
s
}
def tasksHelp(s: State): String =
def taskDetail(s: State): Seq[(String,String)] = taskKeys(s) flatMap taskStrings
def taskKeys(s: State): Seq[AttributeKey[_]] =
{
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", "")
index.keyIndex.keys(Some(currentRef)).toSeq map index.keyMap sortBy(_.label)
}
def tasksHelp(s: State): String =
aligned(" ", " ", taskDetail(s)) 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] =
{
@ -228,7 +231,7 @@ object BuiltinCommands
def multiApplied(s: State) =
Command.applyEffect( multiParser(s) )( _ ::: s )
def multi = Command.custom(multiApplied, Help(MultiBrief, (Set(Multi), MultiDetailed)) :: Nil )
def multi = Command.custom(multiApplied, Help(Multi, MultiBrief, MultiDetailed) )
lazy val otherCommandParser = (s: State) => token(OptSpace ~> matched(s.combinedParser) )
@ -391,7 +394,13 @@ object BuiltinCommands
for(id <- build.defined.keys.toSeq.sorted) log.info("\t" + prefix(id) + id)
}
def act = Command.custom(Act.actParser, actHelp :: Nil)
def act = Command.customHelp(Act.actParser, actHelp)
def actHelp = (s: State) => CommandSupport.showHelp ++ keysHelp(s)
def keysHelp(s: State): Help =
if(Project.isProjectLoaded(s))
Help.detailOnly(taskDetail(s))
else
Help.empty
def projects = Command.command(ProjectsCommand, projectsBrief, projectsDetailed ) { s =>
val extracted = Project extract s
@ -410,7 +419,7 @@ object BuiltinCommands
def project = Command.make(ProjectCommand, projectBrief, projectDetailed)(ProjectNavigation.command)
def exit = Command.command(TerminateAction, Help(exitBrief) :: Nil ) ( doExit )
def exit = Command.command(TerminateAction, exitBrief, exitBrief ) ( doExit )
def doExit(s: State): State = s.runExitHooks().exit(true)