Fix watch command parser

I discovered that when I ran multi-commands with '~' that if there was a
space between the ';' and the command, then the parsing of the command
would fail and the watch would abort. To fix this, I refactor
Watched.watch to use the multi command parser and, if that parser fails,
we fallback on a single command.
This commit is contained in:
Ethan Atkins 2018-11-18 09:23:02 -08:00
parent 4281972f1a
commit 05e3a8609b
4 changed files with 51 additions and 4 deletions

View File

@ -21,7 +21,7 @@ import sbt.internal.LegacyWatched
import sbt.internal.inc.Stamper import sbt.internal.inc.Stamper
import sbt.internal.io.{ EventMonitor, Source, WatchState } import sbt.internal.io.{ EventMonitor, Source, WatchState }
import sbt.internal.util.Types.const import sbt.internal.util.Types.const
import sbt.internal.util.complete.DefaultParsers import sbt.internal.util.complete.{ DefaultParsers, Parser }
import sbt.internal.util.{ AttributeKey, JLine } import sbt.internal.util.{ AttributeKey, JLine }
import sbt.io.FileEventMonitor.{ Creation, Deletion, Event, Update } import sbt.io.FileEventMonitor.{ Creation, Deletion, Event, Update }
import sbt.io._ import sbt.io._
@ -279,9 +279,9 @@ object Watched {
onFailure = Some(Exec(failureCommandName, None)), onFailure = Some(Exec(failureCommandName, None)),
definedCommands = s0.definedCommands :+ onFail definedCommands = s0.definedCommands :+ onFail
) )
val commands = command.split(";") match { val commands = Parser.parse(command, BasicCommands.multiParserImpl(Some(s))) match {
case Array("", rest @ _*) => rest case Left(_) => command :: Nil
case Array(cmd) => Seq(cmd) case Right(c) => c
} }
val parser = Command.combine(s.definedCommands)(s) val parser = Command.combine(s.definedCommands)(s)
val tasks = commands.foldLeft(Nil: Seq[Either[String, () => Either[Exception, Boolean]]]) { val tasks = commands.foldLeft(Nil: Seq[Either[String, () => Either[Exception, Boolean]]]) {

View File

@ -0,0 +1,13 @@
import Build._
organization := "sbt"
name := "scripted-watch-parser"
setStringValue := setStringValueImpl.evaluated
checkStringValue := checkStringValueImpl.evaluated
watchSources += file("string.txt")
watchOnEvent := { _ => Watched.CancelWatch }

View File

@ -0,0 +1,16 @@
import sbt._
object Build {
private[this] var string: String = ""
private[this] val stringFile = file("string.txt")
val setStringValue = inputKey[Unit]("set a global string to a value")
val checkStringValue = inputKey[Unit]("check the value of a global")
def setStringValueImpl: Def.Initialize[InputTask[Unit]] = Def.inputTask {
string = Def.spaceDelimited().parsed.mkString(" ").trim
IO.write(stringFile, string)
}
def checkStringValueImpl: Def.Initialize[InputTask[Unit]] = Def.inputTask {
assert(string == Def.spaceDelimited().parsed.mkString(" ").trim)
assert(IO.read(stringFile) == string)
}
}

View File

@ -0,0 +1,18 @@
> ~; setStringValue foo; setStringValue bar
> checkStringValue bar
> ~;setStringValue foo;setStringValue bar; checkStringValue bar
> ~; setStringValue foo;setStringValue bar; checkStringValue bar
> ~; setStringValue foo; setStringValue bar; checkStringValue bar
> ~ setStringValue foo
> checkStringValue foo
# All of the other tests have involved input tasks, so include commands with regular tasks as well.
> ~; compile; setStringValue baz; checkStringValue baz
# No trailing semicolons are allowed
-> ~; compile; setStringValue baz; checkStringValue baz;