Reply to sbt/exec w/ a Response/Error w/ error code

This commit is contained in:
Dale Wijnand 2018-03-12 19:00:47 +00:00
parent bde6365013
commit 98332c0891
No known key found for this signature in database
GPG Key ID: 4F256E3D151DF5EF
2 changed files with 60 additions and 3 deletions

View File

@ -15,6 +15,7 @@ import jline.TerminalFactory
import sbt.io.{ IO, Using }
import sbt.internal.util.{ ErrorHandling, GlobalLogBacking }
import sbt.internal.util.complete.DefaultParsers
import sbt.internal.langserver.ErrorCodes
import sbt.util.Logger
import sbt.protocol._
@ -155,13 +156,63 @@ object MainLoop {
state.log error errMsg
state.fail
}
StandardMain.exchange publishEventMessage ExecStatusEvent(
val doneEvent = ExecStatusEvent(
"Done",
channelName,
exec.execId,
newState.remainingCommands.toVector map (_.commandLine))
newState.remainingCommands.toVector map (_.commandLine),
exitCode(newState, state),
)
if (doneEvent.execId.isDefined) { // send back a response or error
import sbt.protocol.codec.JsonProtocol._
StandardMain.exchange publishEvent doneEvent
} else { // send back a notification
StandardMain.exchange publishEventMessage doneEvent
}
newState
}
def logFullException(e: Throwable, log: Logger): Unit = State.logFullException(e, log)
private[this] type ExitCode = Option[Long]
private[this] object ExitCode {
def apply(n: Long): ExitCode = Option(n)
val Success: ExitCode = ExitCode(0)
val Unknown: ExitCode = None
}
private[this] def exitCode(state: State, prevState: State): ExitCode = {
exitCodeFromStateNext(state) match {
case ExitCode.Success => exitCodeFromStateOnFailure(state, prevState)
case x => x
}
}
// State's "next" field indicates the next action for the command processor to take
// we'll use that to determine if the command failed
private[this] def exitCodeFromStateNext(state: State): ExitCode = {
state.next match {
case State.Continue => ExitCode.Success
case State.ClearGlobalLog => ExitCode.Success
case State.KeepLastLog => ExitCode.Success
case ret: State.Return =>
ret.result match {
case exit: xsbti.Exit => ExitCode(exit.code().toLong)
case _: xsbti.Continue => ExitCode.Success
case _: xsbti.Reboot => ExitCode.Success
case x =>
val clazz = if (x eq null) "" else " (class: " + x.getClass + ")"
state.log debug s"Unknown main result: $x$clazz"
ExitCode.Unknown
}
}
}
// the shell command specifies an onFailure so that if an exception is thrown
// it's handled by executing the shell again, instead of the state failing
// so we also use that to indicate that the execution failed
private[this] def exitCodeFromStateOnFailure(state: State, prevState: State): ExitCode =
if (prevState.onFailure.isDefined && state.onFailure.isEmpty) ExitCode(ErrorCodes.UnknownError)
else ExitCode.Success
}

View File

@ -240,7 +240,13 @@ final class NetworkChannel(val name: String,
if (isLanguageServerProtocol) {
event match {
case entry: StringEvent => logMessage(entry.level, entry.message)
case _ => langRespond(event, execId)
case entry: ExecStatusEvent =>
entry.exitCode match {
case None => langRespond(event, entry.execId)
case Some(0) => langRespond(event, entry.execId)
case Some(exitCode) => langError(entry.execId, exitCode, "")
}
case _ => langRespond(event, execId)
}
} else {
contentType match {