diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala index 7b533ef61..fb7d6be16 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala @@ -658,6 +658,7 @@ private[sbt] object ProgressState { } } else { pe.command.toSeq.flatMap { cmd => + val tail = if (isWatch) Nil else "enter 'cancel' to stop evaluation" :: Nil s"sbt server is running '$cmd'" :: tail } } diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index cbabbb6a5..3d47351be 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -350,6 +350,15 @@ private[sbt] final class CommandExchange { commandQueue.add(exit) () } + private[this] def cancel(e: Exec): Unit = { + if (e.commandLine.startsWith("console")) { + val terminal = Terminal.get + terminal.write(13, 13, 13, 4) + terminal.printStream.println("\nconsole session killed by remote sbt client") + } else { + Util.ignoreResult(NetworkChannel.cancel(e.execId, e.execId.getOrElse("0"))) + } + } private[this] class MaintenanceThread extends Thread("sbt-command-exchange-maintenance") diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 5a50b3016..ecf4e1caa 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -698,6 +698,40 @@ object NetworkChannel { case object SingleLine extends ChannelState case object InHeader extends ChannelState case object InBody extends ChannelState + private[sbt] def cancel( + execID: Option[String], + id: String + ): Either[String, String] = { + + Option(EvaluateTask.currentlyRunningEngine.get) match { + case Some((state, runningEngine)) => + val runningExecId = state.currentExecId.getOrElse("") + + def checkId(): Boolean = { + if (runningExecId.startsWith("\u2668")) { + ( + Try { id.toLong }.toOption, + Try { runningExecId.substring(1).toLong }.toOption + ) match { + case (Some(id), Some(eid)) => id == eid + case _ => false + } + } else runningExecId == id + } + + // direct comparison on strings and + // remove hotspring unicode added character for numbers + if (checkId) { + runningEngine.cancelAndShutdown() + Right(runningExecId) + } else { + Left("Task ID not matched") + } + + case None => + Left("No tasks under execution") + } + } private[sbt] val disconnect: Command = Command.arb { s =>