diff --git a/main/Main.scala b/main/Main.scala index 96b1ff7e0..9d28c5029 100644 --- a/main/Main.scala +++ b/main/Main.scala @@ -120,22 +120,23 @@ object BuiltinCommands } } - // TODO: this should nest Parsers for other commands - def multi = Command.single(Multi, MultiBrief, MultiDetailed) { (s,arg) => - arg.split(";").toSeq ::: s - } + def multiParser(s: State): Parser[Seq[String]] = + ( token(';' ~> OptSpace) flatMap { _ => token(matched(s.combinedParser) <~ OptSpace ) } ).+ + def multiApplied(s: State) = + Command.applyEffect( multiParser(s) )( _ ::: s ) + + def multi = Command.custom(multiApplied, Help(MultiBrief, (Set(Multi), MultiDetailed)) :: Nil ) - // TODO: nest - def ifLast = Command.single(IfLast, IfLastBrief, IfLastDetailed) { (s, arg) => + lazy val otherCommandParser = (s: State) => token(OptSpace ~> matched(s.combinedParser) ) + + def ifLast = Command(IfLast, IfLastBrief, IfLastDetailed)(otherCommandParser) { (s, arg) => if(s.commands.isEmpty) arg :: s else s } - // TODO: nest - def append = Command.single(Append, AppendLastBrief, AppendLastDetailed) { (s, arg) => + def append = Command(Append, AppendLastBrief, AppendLastDetailed)(otherCommandParser) { (s, arg) => s.copy(commands = s.commands :+ arg) } - // TODO: nest - def setOnFailure = Command.single(OnFailure, OnFailureBrief, OnFailureDetailed) { (s, arg) => + def setOnFailure = Command(OnFailure, OnFailureBrief, OnFailureDetailed)(otherCommandParser) { (s, arg) => s.copy(onFailure = Some(arg)) } def clearOnFailure = Command.command(ClearOnFailure)(s => s.copy(onFailure = None)) @@ -195,27 +196,28 @@ object BuiltinCommands } } - // TODO: nest def continuous = - Command.single(ContinuousExecutePrefix, Help(continuousBriefHelp) ) { (s, arg) => + Command(ContinuousExecutePrefix, Help(continuousBriefHelp) )(otherCommandParser) { (s, arg) => withAttribute(s, Watched.Configuration, "Continuous execution not configured.") { w => val repeat = ContinuousExecutePrefix + (if(arg.startsWith(" ")) arg else " " + arg) Watched.executeContinuously(w, s, arg, repeat) } } - def history = Command.command("!!")(s => s) - //TODO: convert - /*def history = Command.make( historyHelp: _* ) { case (in, s) if in.line startsWith "!" => - val logError = (msg: String) => CommandSupport.logger(s).error(msg) - HistoryCommands(in.line.substring(HistoryPrefix.length).trim, (s get historyPath.key) getOrElse None, 500/*JLine.MaxHistorySize*/, logError) match - { - case Some(commands) => - commands.foreach(println) //printing is more appropriate than logging - (commands ::: s).continue - case None => s.fail + def history = Command.custom(historyParser, historyHelp) + def historyParser(s: State): Parser[() => State] = + Command.applyEffect(HistoryCommands.actionParser) { histFun => + val logError = (msg: String) => CommandSupport.logger(s).error(msg) + val hp = s get historyPath.key getOrElse None + val lines = hp.toList.flatMap( p => IO.readLines(p) ).toIndexedSeq + histFun( complete.History(lines, hp, logError) ) match + { + case Some(commands) => + commands foreach println //printing is more appropriate than logging + (commands ::: s).continue + case None => s.fail + } } - }*/ def eval = Command.single(EvalCommand, evalBrief, evalDetailed) { (s, arg) => val log = logger(s) diff --git a/util/complete/History.scala b/util/complete/History.scala index e792454f7..9c36f2605 100644 --- a/util/complete/History.scala +++ b/util/complete/History.scala @@ -4,9 +4,10 @@ package sbt package complete -import History.number + import History.number + import java.io.File -final class History private(lines: IndexedSeq[String], error: String => Unit) extends NotNull +final class History private(val lines: IndexedSeq[String], val path: Option[File], error: String => Unit) extends NotNull { private def reversed = lines.reverse @@ -41,7 +42,7 @@ final class History private(lines: IndexedSeq[String], error: String => Unit) ex object History { - def apply(lines: Seq[String], error: String => Unit): History = new History(lines.toIndexedSeq, error) + def apply(lines: Seq[String], path: Option[File], error: String => Unit): History = new History(lines.toIndexedSeq, path, error) def number(s: String): Option[Int] = try { Some(s.toInt) } diff --git a/util/complete/HistoryCommands.scala b/util/complete/HistoryCommands.scala index 16a359f9a..906aa328a 100644 --- a/util/complete/HistoryCommands.scala +++ b/util/complete/HistoryCommands.scala @@ -39,63 +39,34 @@ object HistoryCommands def helpString = "History commands:\n " + (descriptions.map{ case (c,d) => c + " " + d}).mkString("\n ") def printHelp(): Unit = println(helpString) + def printHistory(history: complete.History, historySize: Int, show: Int): Unit = + history.list(historySize, show).foreach(println) - def apply(s: String, historyPath: Option[File], maxLines: Int, error: String => Unit): Option[List[String]] = - if(s.isEmpty) - { - printHelp() - Some(Nil) - } - else - { - val lines = historyPath.toList.flatMap( p => IO.readLines(p) ).toArray - if(lines.isEmpty) - { - error("No history") - None - } - else - { - val history = complete.History(lines, error) - if(s.startsWith(ListCommands)) - { - val rest = s.substring(ListCommands.length) - val show = complete.History.number(rest).getOrElse(lines.length) - printHistory(history, maxLines, show) - Some(Nil) - } - else - { - val command = historyCommand(history, s) - command.foreach(lines(lines.length - 1) = _) - historyPath foreach { h => IO.writeLines(h, lines) } - Some(command.toList) - } - } - } - def printHistory(history: complete.History, historySize: Int, show: Int): Unit = history.list(historySize, show).foreach(println) - def historyCommand(history: complete.History, s: String): Option[String] = - { - if(s == Last) - history !! - else if(s.startsWith(Contains)) - history !? s.substring(Contains.length) - else - history ! s + import DefaultParsers._ + + val MaxLines = 500 + lazy val num = token(NatBasic, "") + lazy val last = Last ^^^ { execute(_ !!) } + lazy val list = ListCommands ~> (num ?? Int.MaxValue) map { show => + (h: History) => { printHistory(h, MaxLines, show); Some(Nil) } } -/* - import parse.{Parser,Parsers} - import Parser._ - import Parsers._ - val historyParser: Parser[complete.History => Option[String]] = - { - Start ~> Specific) + lazy val execStr = flag('?') ~ token(any.+.string, "") map { case (contains, str) => + execute(h => if(contains) h !? str else h ! str) } - !! Execute the last command again - !: Show all previous commands - !:n Show the last n commands - !n Execute the command with index n, as shown by the !: command - !-n Execute the nth command before this one - !string Execute the most recent command starting with 'string' - !?string*/ + lazy val execInt = flag('-') ~ num map { case (neg, value) => + execute(h => if(neg) h !- value else h ! value) + } + lazy val help = success( (h: History) => { printHelp(); Some(Nil) } ) + + def execute(f: History => Option[String]): History => Option[List[String]] = (h: History) => + { + val command = f(h) + val lines = h.lines.toArray + command.foreach(lines(lines.length - 1) = _) + h.path foreach { h => IO.writeLines(h, lines) } + Some(command.toList) + } + + val actionParser: Parser[complete.History => Option[List[String]]] = + Start ~> (help | last | execInt | list | execStr ) // execStr must come last } \ No newline at end of file diff --git a/util/complete/Parsers.scala b/util/complete/Parsers.scala index 78cce0271..096614ad9 100644 --- a/util/complete/Parsers.scala +++ b/util/complete/Parsers.scala @@ -52,6 +52,8 @@ trait Parsers def spaceDelimited(display: String): Parser[Seq[String]] = (token(Space) ~> token(NotSpace, display)).* <~ SpaceClass.* + def flag[T](p: Parser[T]): Parser[Boolean] = (p ^^^ true) ?? false + def trimmed(p: Parser[String]) = p map { _.trim } def Uri(ex: Set[URI]) = mapOrFail(URIClass)( uri => new URI(uri)) examples(ex.map(_.toString)) }