From 1f88fe9d7c635ff62c42d5891c46dc8465aa5fa7 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Mon, 15 Oct 2012 12:42:27 -0400 Subject: [PATCH] Parser.failOnException method, don't let rhs of alias fail the parse. Fixes #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. --- util/complete/Parser.scala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/util/complete/Parser.scala b/util/complete/Parser.scala index 7f0fba5db..a10d1bdcb 100644 --- a/util/complete/Parser.scala +++ b/util/complete/Parser.scala @@ -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] @@ -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) @@ -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) @@ -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