From e3e23287af83cb1ccde17390346e6b7b146b5b12 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Thu, 20 Oct 2011 22:59:31 -0400 Subject: [PATCH] reorganize help, allow keys to be arguments to 'help' command --- main/Command.scala | 58 +++++++++++++++++++++++++-------------- main/CommandSupport.scala | 6 ++-- main/Main.scala | 37 +++++++++++++++---------- 3 files changed, 63 insertions(+), 38 deletions(-) diff --git a/main/Command.scala b/main/Command.scala index 929a757a7..2576ef158 100644 --- a/main/Command.scala +++ b/main/Command.scala @@ -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 { diff --git a/main/CommandSupport.scala b/main/CommandSupport.scala index 077ff063d..c743ad641 100644 --- a/main/CommandSupport.scala +++ b/main/CommandSupport.scala @@ -50,7 +50,7 @@ EvalCommand + """ Evaluates the given Scala expression and prints the result and type. """ - def actHelp = Help(ShowCommand, (ShowCommand + " ", actBrief), actDetailed) + def showHelp = Help(ShowCommand, (ShowCommand + " ", actBrief), actDetailed) def actBrief = "Displays the result of evaluating the setting or task associated with 'key'." def actDetailed = ShowCommand + """ @@ -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" diff --git a/main/Main.scala b/main/Main.scala index aca7195bc..14153f972 100644 --- a/main/Main.scala +++ b/main/Main.scala @@ -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)