diff --git a/main-command/src/main/scala/sbt/BasicCommands.scala b/main-command/src/main/scala/sbt/BasicCommands.scala index f647c9478..f5432333e 100644 --- a/main-command/src/main/scala/sbt/BasicCommands.scala +++ b/main-command/src/main/scala/sbt/BasicCommands.scala @@ -39,7 +39,6 @@ object BasicCommands { ignore, help, completionsCommand, - multi, ifLast, append, setOnFailure, @@ -164,7 +163,10 @@ object BasicCommands { ) def commandParser = state.map(s => (s.combinedParser & cmdPart) | cmdPart).getOrElse(cmdPart) val part = semi.flatMap(_ => matched(commandParser) <~ token(OptSpace)).map(_.trim) - part.+ map (_.toList) + (cmdPart.? ~ part.+).map { + case (Some(h), t) => h.mkString.trim +: t.toList + case (_, t) => t.toList + } } def multiParser(s: State): Parser[List[String]] = multiParserImpl(Some(s)) diff --git a/main-command/src/test/scala/sbt/MultiParserSpec.scala b/main-command/src/test/scala/sbt/MultiParserSpec.scala index 06a30a179..ec18b1c58 100644 --- a/main-command/src/test/scala/sbt/MultiParserSpec.scala +++ b/main-command/src/test/scala/sbt/MultiParserSpec.scala @@ -14,6 +14,7 @@ object MultiParserSpec { val parser: Parser[Seq[String]] = BasicCommands.multiParserImpl(None) implicit class StringOps(val s: String) { def parse: Seq[String] = Parser.parse(s, parser).right.get + def parseEither: Either[String, Seq[String]] = Parser.parse(s, parser) } } import MultiParserSpec._ @@ -45,4 +46,20 @@ class MultiParserSpec extends FlatSpec with Matchers { """; setStringValue "foo;bar"; checkStringValue "foo;bar"""".parse shouldBe Seq("""setStringValue "foo;bar"""", """checkStringValue "foo;bar"""") } + it should "parse commands without leading ';'" in { + "setStringValue foo; setStringValue bar".parse shouldBe Seq( + "setStringValue foo", + "setStringValue bar" + ) + "foo; bar".parse shouldBe Seq("foo", "bar") + "foo bar ;bar".parse shouldBe Seq("foo bar", "bar") + "foo \"a;b\"; bar".parse shouldBe Seq("foo \"a;b\"", "bar") + " foo ; bar \"b;c\"".parse shouldBe Seq("foo", "bar \"b;c\"") + } + it should "not parse single commands without leading ';'" in { + "foo".parseEither shouldBe Left("Expected ';'\nfoo\n ^") + "foo bar baz".parseEither shouldBe Left("Expected ';'\nfoo bar baz\n ^") + "foo bar baz;".parseEither shouldBe + Left("Expected not ';'\nExpected '\"'\nfoo bar baz;\n ^") + } } diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 03dd44844..23c4b232e 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -241,6 +241,7 @@ object BuiltinCommands { export, boot, initialize, + BasicCommands.multi, act, continuous, flushFileTreeRepository diff --git a/sbt/src/sbt-test/actions/multi-command/test b/sbt/src/sbt-test/actions/multi-command/test index a98fad58c..8bc828c03 100644 --- a/sbt/src/sbt-test/actions/multi-command/test +++ b/sbt/src/sbt-test/actions/multi-command/test @@ -7,3 +7,15 @@ > checkStringValue bar > ; setStringValue foo; setStringValue bar; setStringValue baz; checkStringValue baz + +> setStringValue foo; setStringValue bar + +> checkStringValue bar + +> setStringValue foo; setStringValue bar; setStringValue baz + +> checkStringValue baz + +-> setStringValue foo; taskThatFails; setStringValue bar + +> checkStringValue foo diff --git a/sbt/src/sbt-test/watch/watch-parser/test b/sbt/src/sbt-test/watch/watch-parser/test index f14bef5e1..63a58eb8a 100644 --- a/sbt/src/sbt-test/watch/watch-parser/test +++ b/sbt/src/sbt-test/watch/watch-parser/test @@ -8,6 +8,9 @@ > ~; setStringValue foo; setStringValue bar; checkStringValue bar +# no leading semicolon +> ~ setStringValue foo; setStringValue bar; checkStringValue bar + > ~ setStringValue foo > checkStringValue foo