From 6a70b9f5656f7dd42156a3e2acec8b7004dfd965 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Tue, 30 Apr 2013 18:55:02 -0400 Subject: [PATCH] Proper support for stashing on-failure handlers. Fixes #732. --- .../src/main/scala/sbt/BasicCommandStrings.scala | 3 +++ main/command/src/main/scala/sbt/BasicCommands.scala | 8 +++++++- main/command/src/main/scala/sbt/BasicKeys.scala | 1 + main/command/src/main/scala/sbt/State.scala | 1 + main/src/main/scala/sbt/Main.scala | 11 +++++++++-- sbt/src/sbt-test/actions/multiple-with-error/test | 3 +++ 6 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 sbt/src/sbt-test/actions/multiple-with-error/test diff --git a/main/command/src/main/scala/sbt/BasicCommandStrings.scala b/main/command/src/main/scala/sbt/BasicCommandStrings.scala index fa6fc4b5d..94c031a51 100644 --- a/main/command/src/main/scala/sbt/BasicCommandStrings.scala +++ b/main/command/src/main/scala/sbt/BasicCommandStrings.scala @@ -110,6 +110,9 @@ AliasCommand + """ name= def Shell = "shell" def ShellDetailed = "Provides an interactive prompt from which commands can be run." + def StashOnFailure = "sbtStashOnFailure" + def PopOnFailure = "sbtPopOnFailure" + def ClearOnFailure = "--" def OnFailure = "-" def OnFailureDetailed = diff --git a/main/command/src/main/scala/sbt/BasicCommands.scala b/main/command/src/main/scala/sbt/BasicCommands.scala index b961df464..d83c6d37b 100644 --- a/main/command/src/main/scala/sbt/BasicCommands.scala +++ b/main/command/src/main/scala/sbt/BasicCommands.scala @@ -16,7 +16,7 @@ package sbt object BasicCommands { - lazy val allBasicCommands = Seq(nop, ignore, help, multi, ifLast, append, setOnFailure, clearOnFailure, reboot, call, exit, continuous, history, shell, read, alias) + lazy val allBasicCommands = Seq(nop, ignore, help, multi, ifLast, append, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, reboot, call, exit, continuous, history, shell, read, alias) def nop = Command.custom(s => success(() => s)) def ignore = Command.command(FailureWall)(idFun) @@ -74,6 +74,12 @@ object BasicCommands s.copy(onFailure = Some(arg)) } def clearOnFailure = Command.command(ClearOnFailure)(s => s.copy(onFailure = None)) + def stashOnFailure = Command.command(StashOnFailure)(s => s.copy(onFailure = None).update(OnFailureStack)(s.onFailure :: _.toList.flatten)) + def popOnFailure = Command.command(PopOnFailure) { s => + val stack = s.get(OnFailureStack).getOrElse(Nil) + val updated = if(stack.isEmpty) s.remove(OnFailureStack) else s.put(OnFailureStack, stack.tail) + updated.copy(onFailure = stack.headOption.flatten) + } def reboot = Command(RebootCommand, Help.more(RebootCommand, RebootDetailed))(rebootParser) { (s, full) => s.reboot(full) diff --git a/main/command/src/main/scala/sbt/BasicKeys.scala b/main/command/src/main/scala/sbt/BasicKeys.scala index 6447b36cf..df3858b58 100644 --- a/main/command/src/main/scala/sbt/BasicKeys.scala +++ b/main/command/src/main/scala/sbt/BasicKeys.scala @@ -9,4 +9,5 @@ object BasicKeys val watch = AttributeKey[Watched]("watch", "Continuous execution configuration.", 1000) private[sbt] val interactive = AttributeKey[Boolean]("interactive", "True if commands are currently being entered from an interactive environment.", 10) private[sbt] val classLoaderCache = AttributeKey[classpath.ClassLoaderCache]("class-loader-cache", "Caches class loaders based on the classpath entries and last modified times.", 10) + private[sbt] val OnFailureStack = AttributeKey[List[Option[String]]]("on-failure-stack", "Stack that remembers on-failure handlers.", 10) } diff --git a/main/command/src/main/scala/sbt/State.scala b/main/command/src/main/scala/sbt/State.scala index f0ee55165..a514b2ded 100644 --- a/main/command/src/main/scala/sbt/State.scala +++ b/main/command/src/main/scala/sbt/State.scala @@ -121,6 +121,7 @@ trait StateOps { object State { + /** Indicates where command execution should resume after a failure.*/ final val FailureWall = "---" /** Represents the next action for the command processor.*/ diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 0bd63c109..3a52a927c 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -77,7 +77,8 @@ object BuiltinCommands def ScriptCommands: Seq[Command] = Seq(ignore, exit, Script.command, act, nop) def DefaultCommands: Seq[Command] = Seq(ignore, help, about, tasks, settingsCommand, loadProject, projects, project, reboot, read, history, set, sessionCommand, inspect, loadProjectImpl, loadFailed, Cross.crossBuild, Cross.switchVersion, - setOnFailure, clearOnFailure, ifLast, multi, shell, continuous, eval, alias, append, last, lastGrep, export, boot, nop, call, exit, act) + setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, + ifLast, multi, shell, continuous, eval, alias, append, last, lastGrep, export, boot, nop, call, exit, act) def DefaultBootCommands: Seq[String] = LoadProject :: (IfLast + " " + Shell) :: Nil def boot = Command.make(BootCommand)(bootParser) @@ -415,7 +416,13 @@ object BuiltinCommands } } - def loadProjectCommands(arg: String) = (OnFailure + " " + LoadFailed) :: (LoadProjectImpl + " " + arg).trim :: ClearOnFailure :: State.FailureWall :: Nil + def loadProjectCommands(arg: String) = + StashOnFailure :: + (OnFailure + " " + LoadFailed) :: + (LoadProjectImpl + " " + arg).trim :: + PopOnFailure :: + State.FailureWall :: + Nil def loadProject = Command(LoadProject, LoadProjectBrief, LoadProjectDetailed)(_ => matched(Project.loadActionParser)) { (s,arg) => loadProjectCommands(arg) ::: s } def loadProjectImpl = Command(LoadProjectImpl)(_ => Project.loadActionParser)( doLoadProject ) diff --git a/sbt/src/sbt-test/actions/multiple-with-error/test b/sbt/src/sbt-test/actions/multiple-with-error/test new file mode 100644 index 000000000..ff83f1b3d --- /dev/null +++ b/sbt/src/sbt-test/actions/multiple-with-error/test @@ -0,0 +1,3 @@ +# Issue 732: reload followed by a command that results in an error exits in interactive mode +-> ;reload;not-a-command +> help