Do not require leading semicolon for multi command

It has long been a frustration of mine that it is necessary to prepend
multiple commands with a ';'. In this commit, I relax that restriction.
I had to reorder the command definitions so that multi comes before act.
This was because if the multi command did not have a leading semicolon,
then it would be handled by the action parser before the multi command
parser had a shot at it. Sadness ensued.
This commit is contained in:
Ethan Atkins 2018-11-19 08:36:58 -08:00
parent 51d986d751
commit c00cc37953
5 changed files with 37 additions and 2 deletions

View File

@ -39,7 +39,6 @@ object BasicCommands {
ignore, ignore,
help, help,
completionsCommand, completionsCommand,
multi,
ifLast, ifLast,
append, append,
setOnFailure, setOnFailure,
@ -164,7 +163,10 @@ object BasicCommands {
) )
def commandParser = state.map(s => (s.combinedParser & cmdPart) | cmdPart).getOrElse(cmdPart) def commandParser = state.map(s => (s.combinedParser & cmdPart) | cmdPart).getOrElse(cmdPart)
val part = semi.flatMap(_ => matched(commandParser) <~ token(OptSpace)).map(_.trim) 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)) def multiParser(s: State): Parser[List[String]] = multiParserImpl(Some(s))

View File

@ -14,6 +14,7 @@ object MultiParserSpec {
val parser: Parser[Seq[String]] = BasicCommands.multiParserImpl(None) val parser: Parser[Seq[String]] = BasicCommands.multiParserImpl(None)
implicit class StringOps(val s: String) { implicit class StringOps(val s: String) {
def parse: Seq[String] = Parser.parse(s, parser).right.get def parse: Seq[String] = Parser.parse(s, parser).right.get
def parseEither: Either[String, Seq[String]] = Parser.parse(s, parser)
} }
} }
import MultiParserSpec._ import MultiParserSpec._
@ -45,4 +46,20 @@ class MultiParserSpec extends FlatSpec with Matchers {
"""; setStringValue "foo;bar"; checkStringValue "foo;bar"""".parse shouldBe """; setStringValue "foo;bar"; checkStringValue "foo;bar"""".parse shouldBe
Seq("""setStringValue "foo;bar"""", """checkStringValue "foo;bar"""") 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 ^")
}
} }

View File

@ -241,6 +241,7 @@ object BuiltinCommands {
export, export,
boot, boot,
initialize, initialize,
BasicCommands.multi,
act, act,
continuous, continuous,
flushFileTreeRepository flushFileTreeRepository

View File

@ -7,3 +7,15 @@
> checkStringValue bar > checkStringValue bar
> ; setStringValue foo; setStringValue bar; setStringValue baz; checkStringValue baz > ; 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

View File

@ -8,6 +8,9 @@
> ~; setStringValue foo; setStringValue bar; checkStringValue bar > ~; setStringValue foo; setStringValue bar; checkStringValue bar
# no leading semicolon
> ~ setStringValue foo; setStringValue bar; checkStringValue bar
> ~ setStringValue foo > ~ setStringValue foo
> checkStringValue foo > checkStringValue foo