diff --git a/main-command/src/main/scala/sbt/BasicCommands.scala b/main-command/src/main/scala/sbt/BasicCommands.scala index 8d4b9c34d..f647c9478 100644 --- a/main-command/src/main/scala/sbt/BasicCommands.scala +++ b/main-command/src/main/scala/sbt/BasicCommands.scala @@ -155,13 +155,16 @@ object BasicCommands { } private[sbt] def multiParserImpl(state: Option[State]): Parser[List[String]] = { - val nonSemi = token(charClass(_ != ';', "not ';'").+, hide = const(true)) + val nonSemi = charClass(_ != ';', "not ';'") val semi = token(';' ~> OptSpace) - def commandParser = state.map(s => (s.combinedParser & nonSemi) | nonSemi).getOrElse(nonSemi) - val part = semi flatMap ( - _ => matched(commandParser) <~ token(OptSpace) + val nonQuote = charClass(_ != '"', label = "not '\"'") + val cmdPart = token( + ((nonSemi & nonQuote).map(_.toString) | StringEscapable.map(c => s""""$c"""")).+, + hide = const(true) ) - (part map (_.trim)).+ map (_.toList) + def commandParser = state.map(s => (s.combinedParser & cmdPart) | cmdPart).getOrElse(cmdPart) + val part = semi.flatMap(_ => matched(commandParser) <~ token(OptSpace)).map(_.trim) + part.+ map (_.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 e29eca571..06a30a179 100644 --- a/main-command/src/test/scala/sbt/MultiParserSpec.scala +++ b/main-command/src/test/scala/sbt/MultiParserSpec.scala @@ -33,4 +33,16 @@ class MultiParserSpec extends FlatSpec with Matchers { "; foo; bar".parse shouldBe Seq("foo", "bar") ";foo; bar".parse shouldBe Seq("foo", "bar") } + it should "parse command with string literal" in { + "; foo \"barbaz\"".parse shouldBe Seq("foo \"barbaz\"") + "; foo \"bar;baz\"".parse shouldBe Seq("foo \"bar;baz\"") + "; foo \"barbaz\"; bar".parse shouldBe Seq("foo \"barbaz\"", "bar") + "; foo \"barbaz\"; bar \"blah\"".parse shouldBe Seq("foo \"barbaz\"", "bar \"blah\"") + "; foo \"bar;baz\"; bar".parse shouldBe Seq("foo \"bar;baz\"", "bar") + "; foo \"bar;baz\"; bar \"buzz\"".parse shouldBe Seq("foo \"bar;baz\"", "bar \"buzz\"") + "; foo \"bar;baz\"; bar \"buzz;two\"".parse shouldBe Seq("foo \"bar;baz\"", "bar \"buzz;two\"") + """; foo "bar;\"baz\""; bar""".parse shouldBe Seq("""foo "bar;\"baz\""""", "bar") + """; setStringValue "foo;bar"; checkStringValue "foo;bar"""".parse shouldBe + Seq("""setStringValue "foo;bar"""", """checkStringValue "foo;bar"""") + } }