diff --git a/main-command/src/main/scala/sbt/BasicCommands.scala b/main-command/src/main/scala/sbt/BasicCommands.scala index e41ccd378..030458e21 100644 --- a/main-command/src/main/scala/sbt/BasicCommands.scala +++ b/main-command/src/main/scala/sbt/BasicCommands.scala @@ -194,30 +194,38 @@ object BasicCommands { } } - var askingAlready = false - val commandListers = Seq(new ConsoleListener(), new NetworkListener()) - val commandQueue: ConcurrentLinkedQueue[Option[String]] = new ConcurrentLinkedQueue() - - @tailrec def blockUntilNextCommand: Option[String] = + private[sbt] var askingAlready = false + private[sbt] val commandQueue: ConcurrentLinkedQueue[(String, Option[String])] = new ConcurrentLinkedQueue() + private[sbt] val commandListers = Seq(new ConsoleListener(commandQueue), new NetworkListener(commandQueue)) + @tailrec private[sbt] def blockUntilNextCommand: (String, Option[String]) = Option(commandQueue.poll) match { case Some(x) => x - case None => + case _ => Thread.sleep(50) blockUntilNextCommand } def server = Command.command(Server, Help.more(Server, ServerDetailed)) { s => - if (!askingAlready) { + if (askingAlready) { commandListers foreach { x => - x.run(commandQueue, CommandStatus(s, true)) + x.resume(CommandStatus(s, true)) + } + } else { + commandListers foreach { x => + x.run(CommandStatus(s, true)) } askingAlready = true } blockUntilNextCommand match { - case Some(line) => - // tell listern to be inactive . + case (source, Some(line)) => + if (source != "human") { + println(line) + } + commandListers foreach { x => + x.pause() + } val newState = s.copy(onFailure = Some(Server), remainingCommands = line +: Server +: s.remainingCommands).setInteractive(true) if (line.trim.isEmpty) newState else newState.clearGlobalLog - case None => s.setInteractive(false) + case _ => s.setInteractive(false) } } diff --git a/main/command/src/main/scala/sbt/CommandListener.scala b/main/command/src/main/scala/sbt/CommandListener.scala index fcc71cb85..320daf031 100644 --- a/main/command/src/main/scala/sbt/CommandListener.scala +++ b/main/command/src/main/scala/sbt/CommandListener.scala @@ -3,10 +3,11 @@ package sbt import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.atomic.AtomicBoolean -trait CommandListener { +abstract class CommandListener(queue: ConcurrentLinkedQueue[(String, Option[String])]) { // represents a loop that keeps asking an IO device for String input - def run(queue: ConcurrentLinkedQueue[Option[String]], - status: CommandStatus): Unit + def run(status: CommandStatus): Unit def shutdown(): Unit def setStatus(status: CommandStatus): Unit + def pause(): Unit + def resume(status: CommandStatus): Unit } diff --git a/main/command/src/main/scala/sbt/ConsoleListener.scala b/main/command/src/main/scala/sbt/ConsoleListener.scala index 1a2f7ae49..cff2577fd 100644 --- a/main/command/src/main/scala/sbt/ConsoleListener.scala +++ b/main/command/src/main/scala/sbt/ConsoleListener.scala @@ -1,31 +1,66 @@ package sbt +import sbt.internal.util._ import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference } +import BasicKeys._ +import java.io.File -class ConsoleListener extends CommandListener { +private[sbt] final class ConsoleListener(queue: ConcurrentLinkedQueue[(String, Option[String])]) extends CommandListener(queue) { + private var askUserThread: Option[Thread] = None + def makeAskUserThread(status: CommandStatus): Thread = new Thread("ask-user-thread") { + val s = status.state + val history = (s get historyPath) getOrElse Some(new File(s.baseDir, ".history")) + val prompt = (s get shellPrompt) match { case Some(pf) => pf(s); case None => "> " } + // val reader = new FullReader(history, s.combinedParser) + // val line = reader.readLine(prompt) + // line match { + // case Some(line) => + // val newState = s.copy(onFailure = Some(Shell), remainingCommands = line +: Shell +: s.remainingCommands).setInteractive(true) + // if (line.trim.isEmpty) newState else newState.clearGlobalLog + // case None => s.setInteractive(false) + // } + val reader = JLine.simple(None, false) + override def run(): Unit = { + try { + val line = reader.readLine(prompt) + line map { x => queue.add(("human", Some(x))) } + } catch { + case e: InterruptedException => + } + } + } - // val history = (s get historyPath) getOrElse Some(new File(s.baseDir, ".history")) - // val prompt = (s get shellPrompt) match { case Some(pf) => pf(s); case None => "> " } - // val reader = new FullReader(history, s.combinedParser) - // val line = reader.readLine(prompt) - // line match { - // case Some(line) => - // val newState = s.copy(onFailure = Some(Shell), remainingCommands = line +: Shell +: s.remainingCommands).setInteractive(true) - // if (line.trim.isEmpty) newState else newState.clearGlobalLog - // case None => s.setInteractive(false) - // } - - def run(queue: ConcurrentLinkedQueue[Option[String]], - status: CommandStatus): Unit = - { - // spawn thread and loop + def run(status: CommandStatus): Unit = + askUserThread match { + case Some(x) if x.isAlive => // + case _ => + val x = makeAskUserThread(status) + x.start + askUserThread = Some(x) } def shutdown(): Unit = - { - // interrupt and kill the thread + askUserThread match { + case Some(x) if x.isAlive => + x.interrupt + askUserThread = None + println("shutdown ask user thread") + case _ => () } - def setStatus(status: CommandStatus): Unit = ??? + def pause(): Unit = shutdown() + + def resume(status: CommandStatus): Unit = + askUserThread match { + case Some(x) if x.isAlive => // + println("resume??") + case _ => + val x = makeAskUserThread(status) + println("resume") + x.start + askUserThread = Some(x) + } + + def setStatus(status: CommandStatus): Unit = () } diff --git a/main/command/src/main/scala/sbt/NetworkListener.scala b/main/command/src/main/scala/sbt/NetworkListener.scala index 9f1c31b60..4c155ac00 100644 --- a/main/command/src/main/scala/sbt/NetworkListener.scala +++ b/main/command/src/main/scala/sbt/NetworkListener.scala @@ -4,16 +4,15 @@ import java.util.concurrent.ConcurrentLinkedQueue import sbt.server._ -private[sbt] final class NetworkListener extends CommandListener { +private[sbt] final class NetworkListener(queue: ConcurrentLinkedQueue[(String, Option[String])]) extends CommandListener(queue) { private var server: Option[ServerInstance] = None - def run(queue: ConcurrentLinkedQueue[Option[String]], - status: CommandStatus): Unit = + def run(status: CommandStatus): Unit = { def onCommand(command: sbt.server.Command): Unit = { command match { - case Execution(cmd) => queue.add(Some(cmd)) + case Execution(cmd) => queue.add(("network", Some(cmd))) } } @@ -26,6 +25,12 @@ private[sbt] final class NetworkListener extends CommandListener { server.foreach(_.shutdown()) } + // network doesn't pause or resume + def pause(): Unit = () + + // network doesn't pause or resume + def resume(status: CommandStatus): Unit = () + def setStatus(cmdStatus: CommandStatus): Unit = { server.foreach(server => server.publish( diff --git a/main/command/src/main/scala/sbt/server/ClientConnection.scala b/main/command/src/main/scala/sbt/server/ClientConnection.scala index 42f914693..d3de48152 100644 --- a/main/command/src/main/scala/sbt/server/ClientConnection.scala +++ b/main/command/src/main/scala/sbt/server/ClientConnection.scala @@ -33,7 +33,7 @@ abstract class ClientConnection(connection: Socket) { if (delimPos > 0) { val chunk = buffer.take(delimPos) buffer = buffer.drop(delimPos + 1) - + Serialization.deserialize(chunk).fold( errorDesc => println("Got invalid chunk from client: " + errorDesc), onCommand diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 279da153a..58c2ccc86 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -112,7 +112,7 @@ object BuiltinCommands { def DefaultCommands: Seq[Command] = Seq(ignore, help, completionsCommand, about, tasks, settingsCommand, loadProject, projects, project, reboot, read, history, set, sessionCommand, inspect, loadProjectImpl, loadFailed, Cross.crossBuild, Cross.switchVersion, Cross.crossRestoreSession, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, setLogLevel, plugin, plugins, - ifLast, multi, shell, continuous, eval, alias, append, last, lastGrep, export, boot, nop, call, exit, early, initialize, act) ++ + ifLast, multi, shell, BasicCommands.server, continuous, eval, alias, append, last, lastGrep, export, boot, nop, call, exit, early, initialize, act) ++ compatCommands def DefaultBootCommands: Seq[String] = LoadProject :: (IfLast + " " + Shell) :: Nil