Skip to content

Commit

Permalink
Parser.failOnException method, don't let rhs of alias fail the parse. F…
Browse files Browse the repository at this point in the history
…ixes #572.

alias only parses the right hand side for tab completion help.
The assignment should happen whether or not the parse is successful because the
context may change by the time the alias is actually evaluated.
In particular, the 'set' command uses the loaded project for tab completion in 0.12.1.
When a .sbtrc file is processed, the project has not been loaded yet, so aliases
involving set fail.  Wrapping the rhs in failOnException addresses this.
  • Loading branch information
harrah committed Oct 15, 2012
1 parent d607227 commit 1612af8
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 1 deletion.
2 changes: 1 addition & 1 deletion main/command/BasicCommands.scala
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ object BasicCommands
val name = token(OpOrID.examples( aliasNames(s) : _*) )
val assign = token(OptSpace ~ '=' ~ OptSpace)
val sfree = removeAliases(s)
val to = matched(sfree.combinedParser, partial = true) | any.+.string
val to = matched(sfree.combinedParser, partial = true).failOnException | any.+.string
val base = (OptSpace ~> (name ~ (assign ~> to.?).?).?)
applyEffect(base)(t => runAlias(s, t) )
}
Expand Down
1 change: 1 addition & 0 deletions sbt/src/sbt-test/actions/aliasrc/.sbtrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
alias info= set logLevel := Level.Info
1 change: 1 addition & 0 deletions sbt/src/sbt-test/actions/aliasrc/test
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
> info
18 changes: 18 additions & 0 deletions util/complete/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ sealed trait RichParser[A]

/** Uses the specified message if the original Parser fails.*/
def !!!(msg: String): Parser[A]
/** If an exception is thrown by the original Parser,
* capture it and fail locally instead of allowing the exception to propagate up and terminate parsing.*/
def failOnException: Parser[A]

def unary_- : Parser[Unit]
def & (o: Parser[_]): Parser[A]
Expand Down Expand Up @@ -173,6 +176,8 @@ object Parser extends ParserMain

def onFailure[T](delegate: Parser[T], msg: String): Parser[T] =
if(delegate.valid) new OnFailure(delegate, msg) else failure(msg)
def trapAndFail[T](delegate: Parser[T]): Parser[T] =
delegate.ifValid( new TrapAndFail(delegate) )

def zeroOrMore[T](p: Parser[T]): Parser[Seq[T]] = repeat(p, 0, Infinite)
def oneOrMore[T](p: Parser[T]): Parser[Seq[T]] = repeat(p, 1, Infinite)
Expand Down Expand Up @@ -233,6 +238,7 @@ trait ParserMain
def <~[B](b: Parser[B]): Parser[A] = (a ~ b) map { case av ~ _ => av }
def ~>[B](b: Parser[B]): Parser[B] = (a ~ b) map { case _ ~ bv => bv }
def !!!(msg: String): Parser[A] = onFailure(a, msg)
def failOnException: Parser[A] = trapAndFail(a)

def unary_- = not(a)
def & (o: Parser[_]) = and(a, o)
Expand Down Expand Up @@ -425,6 +431,18 @@ private final case class Invalid(fail: Failure) extends Parser[Nothing]
def valid = false
def ifValid[S](p: => Parser[S]): Parser[S] = this
}

private final class TrapAndFail[A](a: Parser[A]) extends ValidParser[A]
{
def result = try { a.result } catch { case e: Exception => None }
def resultEmpty = try { a.resultEmpty } catch { case e: Exception => fail(e) }
def derive(c: Char) = try { trapAndFail(a derive c) } catch { case e: Exception => Invalid(fail(e)) }
def completions(level: Int) = try { a.completions(level) } catch { case e: Exception => Completions.nil }
override def toString = "trap(" + a + ")"
override def isTokenStart = a.isTokenStart
private[this] def fail(e: Exception): Failure = mkFailure(e.toString)
}

private final class OnFailure[A](a: Parser[A], message: String) extends ValidParser[A]
{
def result = a.result
Expand Down

0 comments on commit 1612af8

Please sign in to comment.