command cleanup, load project by default, prompt on load failure

This commit is contained in:
Mark Harrah 2011-02-19 22:22:09 -05:00
parent aadcd0c9fb
commit 04910baf2f
6 changed files with 63 additions and 28 deletions

View File

@ -86,8 +86,8 @@ object Act
processResult(result, logger(s), show) processResult(result, logger(s), show)
s s
} }
def actParser(s: State): Parser[() => State] = def actParser(s: State): Parser[() => State] = requireSession(s, actParser0(s))
if(s get Project.SessionKey isEmpty) failure("No project loaded") else actParser0(s)
private[this] def actParser0(state: State) = private[this] def actParser0(state: State) =
{ {
val extracted = Project extract state val extracted = Project extract state
@ -103,4 +103,8 @@ object Act
val defaultConf = (ref: ProjectRef) => if(Project.getProject(ref, structure).isDefined) defaultConfig(structure.data)(ref) else None val defaultConf = (ref: ProjectRef) => if(Project.getProject(ref, structure).isDefined) defaultConfig(structure.data)(ref) else None
scopedKey(structure.index.keyIndex, curi, cid, defaultConf, structure.index.keyMap) scopedKey(structure.index.keyIndex, curi, cid, defaultConf, structure.index.keyMap)
} }
def requireSession[T](s: State, p: => Parser[T]): Parser[T] =
if(s get Project.SessionKey isEmpty) failure("No project loaded") else p
} }

View File

@ -151,10 +151,10 @@ ProjectCommand +
def DefaultsBrief = (DefaultsCommand, DefaultsDetailed) def DefaultsBrief = (DefaultsCommand, DefaultsDetailed)
def DefaultsDetailed = "Registers default built-in commands" def DefaultsDetailed = "Registers default built-in commands"
def ReloadCommand = "reload" def RebootCommand = "reboot"
def ReloadBrief = "Reloads the session and then executes the remaining commands." def RebootBrief = "Reboots sbt and then executes the remaining commands."
def ReloadDetailed = def RebootDetailed =
ReloadCommand + """ RebootCommand + """
This command is equivalent to exiting, restarting, and running the This command is equivalent to exiting, restarting, and running the
remaining commands with the exception that the jvm is not shut down. remaining commands with the exception that the jvm is not shut down.
""" """
@ -232,14 +232,17 @@ CompileSyntax + """
Cached information about the compilation will be written to 'cache'. Cached information about the compilation will be written to 'cache'.
""" """
val FailureWall = "--" val FailureWall = "---"
def Load = "load" def Load = "load"
def LoadLabel = "a project" def LoadLabel = "a project"
def LoadCommand = "load-commands" def LoadCommand = "load-commands"
def LoadCommandLabel = "commands" def LoadCommandLabel = "commands"
def LoadProject = "loadp" def LoadFailed = "load-failed"
def LoadProjectImpl = "loadp"
def LoadProject = "reload"
def LoadProjectBrief = LoadProjectDetailed def LoadProjectBrief = LoadProjectDetailed
def LoadProjectDetailed = "Loads the project in the current directory" def LoadProjectDetailed = "Loads the project in the current directory"
@ -247,6 +250,7 @@ CompileSyntax + """
def ShellBrief = ShellDetailed def ShellBrief = ShellDetailed
def ShellDetailed = "Provides an interactive prompt from which commands can be run." def ShellDetailed = "Provides an interactive prompt from which commands can be run."
def ClearOnFailure = "--"
def OnFailure = "-" def OnFailure = "-"
def OnFailureBrief = (OnFailure + " command", "Registers 'command' to run if a command fails.") def OnFailureBrief = (OnFailure + " command", "Registers 'command' to run if a command fails.")
def OnFailureDetailed = def OnFailureDetailed =

View File

@ -24,10 +24,10 @@ class xMain extends xsbti.AppMain
{ {
final def run(configuration: xsbti.AppConfiguration): xsbti.MainResult = final def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
{ {
import BuiltinCommands.{initialize, defaults} import BuiltinCommands.{initialize, defaults, DefaultBootCommands}
import CommandSupport.{DefaultsCommand, InitCommand} import CommandSupport.{DefaultsCommand, InitCommand}
val initialCommandDefs = Seq(initialize, defaults) val initialCommandDefs = Seq(initialize, defaults)
val commands = DefaultsCommand :: InitCommand :: configuration.arguments.map(_.trim).toList val commands = DefaultsCommand +: InitCommand +: (DefaultBootCommands ++ configuration.arguments.map(_.trim))
val state = State( configuration, initialCommandDefs, Set.empty, None, commands, initialAttributes, Next.Continue ) val state = State( configuration, initialCommandDefs, Set.empty, None, commands, initialAttributes, Next.Continue )
run(state) run(state)
} }
@ -58,8 +58,9 @@ class xMain extends xsbti.AppMain
import CommandSupport._ import CommandSupport._
object BuiltinCommands object BuiltinCommands
{ {
def DefaultCommands: Seq[Command] = Seq(ignore, help, reload, read, history, continuous, exit, loadCommands, loadProject, compile, discover, def DefaultCommands: Seq[Command] = Seq(ignore, help, reboot, read, history, continuous, exit, loadCommands, loadProject, loadProjectImpl, loadFailed, compile, discover,
projects, project, setOnFailure, ifLast, multi, shell, set, inspect, eval, alias, append, last, lastGrep, nop, sessionCommand, act) projects, project, setOnFailure, clearOnFailure, ifLast, multi, shell, set, inspect, eval, alias, append, last, lastGrep, nop, sessionCommand, act)
def DefaultBootCommands: Seq[String] = LoadProject :: (IfLast + " " + Shell) :: Nil
def nop = Command.custom(s => success(() => s)) def nop = Command.custom(s => success(() => s))
def ignore = Command.command(FailureWall)(identity) def ignore = Command.command(FailureWall)(identity)
@ -135,8 +136,9 @@ object BuiltinCommands
def setOnFailure = Command.single(OnFailure, OnFailureBrief, OnFailureDetailed) { (s, arg) => def setOnFailure = Command.single(OnFailure, OnFailureBrief, OnFailureDetailed) { (s, arg) =>
s.copy(onFailure = Some(arg)) s.copy(onFailure = Some(arg))
} }
def clearOnFailure = Command.command(ClearOnFailure)(s => s.copy(onFailure = None))
def reload = Command.command(ReloadCommand, ReloadBrief, ReloadDetailed) { s => def reboot = Command.command(RebootCommand, RebootBrief, RebootDetailed) { s =>
s.runExitHooks().reload s.runExitHooks().reload
} }
@ -244,8 +246,8 @@ object BuiltinCommands
Output.lastGrep(sk.scope, sk.key, Project.structure(s).streams, pattern) Output.lastGrep(sk.scope, sk.key, Project.structure(s).streams, pattern)
s s
} }
val spacedKeyParser = (s: State) => token(Space) ~> Act.scopedKeyParser(s) val spacedKeyParser = (s: State) => Act.requireSession(s, token(Space) ~> Act.scopedKeyParser(s))
def lastGrepParser(s: State) = (token(Space) ~> token(NotSpace, "<pattern>")) ~ spacedKeyParser(s) def lastGrepParser(s: State) = Act.requireSession(s, (token(Space) ~> token(NotSpace, "<pattern>")) ~ spacedKeyParser(s))
def last = Command(LastCommand, lastBrief, lastDetailed)(spacedKeyParser) { (s,sk) => def last = Command(LastCommand, lastBrief, lastDetailed)(spacedKeyParser) { (s,sk) =>
Output.last(sk.scope, sk.key, Project.structure(s).streams) Output.last(sk.scope, sk.key, Project.structure(s).streams)
s s
@ -305,7 +307,29 @@ object BuiltinCommands
} catch { case e: xsbti.CompileFailed => s.fail /* already logged */ } } catch { case e: xsbti.CompileFailed => s.fail /* already logged */ }
} }
def loadProject = Command.command(LoadProject, LoadProjectBrief, LoadProjectDetailed) { s => def loadFailed = Command.command(LoadFailed)(handleLoadFailed)
@tailrec def handleLoadFailed(s: State): State =
{
val result = (SimpleReader.readLine("Project loading failed: (r)etry, (q)uit, or (i)gnore? ") getOrElse Quit).toLowerCase
def matches(s: String) = !result.isEmpty && (s startsWith result)
if(matches("retry"))
LoadProject :: s
else if(matches(Quit))
s.exit(ok = false)
else if(matches("ignore"))
s
else
{
println("Invalid response.")
handleLoadFailed(s)
}
}
def loadProjectCommands = (OnFailure + " " + LoadFailed) :: LoadProjectImpl :: ClearOnFailure :: FailureWall :: Nil
def loadProject = Command.command(LoadProject, LoadProjectBrief, LoadProjectDetailed) { loadProjectCommands ::: _ }
def loadProjectImpl = Command.command(LoadProjectImpl) { s =>
val (eval, structure) = Load.defaultLoad(s, logger(s)) val (eval, structure) = Load.defaultLoad(s, logger(s))
val session = Load.initialSession(structure, eval) val session = Load.initialSession(structure, eval)
Project.setProject(session, structure, s) Project.setProject(session, structure, s)

View File

@ -228,9 +228,11 @@ object SessionSettings
{ {
val project = Project.getProject(pref, structure).getOrElse(error("Invalid project reference " + pref)) val project = Project.getProject(pref, structure).getOrElse(error("Invalid project reference " + pref))
val appendTo: File = BuildPaths.configurationSources(project.base).headOption.getOrElse(new File(project.base, "build.sbt")) val appendTo: File = BuildPaths.configurationSources(project.base).headOption.getOrElse(new File(project.base, "build.sbt"))
val sbtAppend = settingStrings(settings).flatMap("" :: _ :: Nil) val baseAppend = settingStrings(settings).flatMap("" :: _ :: Nil)
IO.writeLines(appendTo, sbtAppend, append = true) val adjustedLines = if( hasTrailingBlank(IO.readLines(appendTo)) ) baseAppend else baseAppend
IO.writeLines(appendTo, adjustedLines, append = true)
} }
def hasTrailingBlank(lines: Seq[String]) = lines.takeRight(1).exists(_.trim.isEmpty)
def printAllSettings(s: State): State = def printAllSettings(s: State): State =
withSettings(s){ session => withSettings(s){ session =>
for( ((uri,id), settings) <- session.append if !settings.isEmpty) { for( ((uri,id), settings) <- session.append if !settings.isEmpty) {

View File

@ -65,16 +65,17 @@ object State
{ {
val remaining = s.commands.dropWhile(_ != FailureWall) val remaining = s.commands.dropWhile(_ != FailureWall)
if(remaining.isEmpty) if(remaining.isEmpty)
{ applyOnFailure(s, Nil, exit(ok = false))
else
applyOnFailure(s, remaining, s.copy(commands = remaining))
}
private[this] def applyOnFailure(s: State, remaining: Seq[String], noHandler: => State): State =
s.onFailure match s.onFailure match
{ {
case Some(c) => s.copy(commands = c :: Nil, onFailure = None) case Some(c) => s.copy(commands = c +: remaining, onFailure = None)
case None => exit(ok = false) case None => noHandler
}
}
else
s.copy(commands = remaining)
} }
def runExitHooks(): State = { def runExitHooks(): State = {
ExitHooks.runExitHooks(s.exitHooks.toSeq) ExitHooks.runExitHooks(s.exitHooks.toSeq)
s.copy(exitHooks = Set.empty) s.copy(exitHooks = Set.empty)

View File

@ -3,7 +3,7 @@
*/ */
package sbt package sbt
import CommandSupport.FailureWall import CommandSupport.{ClearOnFailure,FailureWall}
import annotation.tailrec import annotation.tailrec
trait Watched trait Watched
@ -41,7 +41,7 @@ object Watched
val (triggered, newWatchState) = SourceModificationWatch.watch(sourcesFinder, PollDelaySeconds, watchState)(shouldTerminate) val (triggered, newWatchState) = SourceModificationWatch.watch(sourcesFinder, PollDelaySeconds, watchState)(shouldTerminate)
if(triggered) if(triggered)
(next :: FailureWall :: repeat :: s).put(ContinuousState, newWatchState) (ClearOnFailure :: next :: FailureWall :: repeat :: s).put(ContinuousState, newWatchState)
else else
{ {
while (System.in.available() > 0) System.in.read() while (System.in.available() > 0) System.in.read()