From 98569e2464292054c1b5629ad7eff4a4a3dd7381 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sun, 28 Nov 2010 05:01:03 +0000 Subject: Moved sigint handler to InterpreterLoop and mad... Moved sigint handler to InterpreterLoop and made it discriminate based on contents of current line. No review. --- src/compiler/scala/tools/nsc/Interpreter.scala | 18 ++++---------- src/compiler/scala/tools/nsc/InterpreterLoop.scala | 28 ++++++++++++++++++++++ .../tools/nsc/interpreter/InteractiveReader.scala | 1 + .../scala/tools/nsc/interpreter/JLineReader.scala | 5 +--- 4 files changed, 35 insertions(+), 17 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala index e9474e973c..dd775d1a23 100644 --- a/src/compiler/scala/tools/nsc/Interpreter.scala +++ b/src/compiler/scala/tools/nsc/Interpreter.scala @@ -19,7 +19,7 @@ import io.{ PlainFile, VirtualDirectory, spawn, callable, newDaemonThreadExecuto import reporters.{ ConsoleReporter, Reporter } import symtab.{ Flags, Names } import scala.tools.nsc.{ InterpreterResults => IR } -import scala.tools.util.{ PathResolver, SignalManager } +import scala.tools.util.PathResolver import scala.tools.nsc.util.{ ScalaClassLoader, Exceptional } import ScalaClassLoader.URLClassLoader import Exceptional.unwrap @@ -188,16 +188,8 @@ class Interpreter(val settings: Settings, out: PrintWriter) { /** An executor service which creates daemon threads. */ private lazy val lineExecutor = newDaemonThreadExecutor() - private var currentExecution: Future[String] = null - private def sigintHandler = { - if (currentExecution == null) System.exit(1) - else currentExecution.cancel(true) - } - /** Try to install sigint handler: false on failure. */ - def installSigIntHandler(): Boolean = { - try { SignalManager("INT") = sigintHandler ; true } - catch { case _: Exception => false } - } + private var _currentExecution: Future[String] = null + def currentExecution = _currentExecution /** interpreter settings */ lazy val isettings = new InterpreterSettings(this) @@ -1033,7 +1025,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { def loadAndRun: (String, Boolean) = { try { val resultValMethod = loadedResultObject getMethod "scala_repl_result" - currentExecution = lineExecutor submit callable(resultValMethod.invoke(loadedResultObject).toString) + _currentExecution = lineExecutor submit callable(resultValMethod.invoke(loadedResultObject).toString) while (!currentExecution.isDone) Thread.`yield` @@ -1052,7 +1044,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { } } finally { - currentExecution = null + _currentExecution = null } } diff --git a/src/compiler/scala/tools/nsc/InterpreterLoop.scala b/src/compiler/scala/tools/nsc/InterpreterLoop.scala index bdcd7b9f58..a7f70b12b0 100644 --- a/src/compiler/scala/tools/nsc/InterpreterLoop.scala +++ b/src/compiler/scala/tools/nsc/InterpreterLoop.scala @@ -10,7 +10,9 @@ import java.io.{ BufferedReader, FileReader, PrintWriter } import java.io.IOException import scala.tools.nsc.{ InterpreterResults => IR } +import scala.tools.util.SignalManager import scala.annotation.tailrec +import scala.util.control.Exception.{ ignoring } import scala.collection.mutable.ListBuffer import scala.concurrent.ops import util.{ ClassPath } @@ -102,6 +104,31 @@ class InterpreterLoop(in0: Option[BufferedReader], protected val out: PrintWrite /** Record a command for replay should the user request a :replay */ def addReplay(cmd: String) = replayCommandStack ::= cmd + /** Try to install sigint handler: ignore failure. Signal handler + * will interrupt current line execution if any is in progress. + * + * Attempting to protect the repl from accidental exit, we only honor + * a single ctrl-C if the current buffer is empty: otherwise we look + * for a second one within a short time. + */ + private def installSigIntHandler() { + def onExit() { + Console.println("") // avoiding "shell prompt in middle of line" syndrome + System.exit(1) + } + ignoring(classOf[Exception]) { + SignalManager("INT") = { + val exec = interpreter.currentExecution + if (exec != null) exec.cancel(true) + else if (in.currentLine != "") { + SignalManager("INT") = onExit() + io.timer(5)(installSigIntHandler()) // restore original + } + else onExit() + } + } + } + /** Close the interpreter and set the var to null. */ def closeInterpreter() { if (interpreter ne null) { @@ -121,6 +148,7 @@ class InterpreterLoop(in0: Option[BufferedReader], protected val out: PrintWrite settings.explicitParentLoader.getOrElse( classOf[InterpreterLoop].getClassLoader ) } interpreter.setContextClassLoader() + installSigIntHandler() // interpreter.quietBind("settings", "scala.tools.nsc.InterpreterSettings", interpreter.isettings) } diff --git a/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala b/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala index 7e4abd1d76..840e457dd4 100644 --- a/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala @@ -17,6 +17,7 @@ trait InteractiveReader { protected def readOneLine(prompt: String): String val interactive: Boolean def init(): Unit = () + def currentLine = "" // the current buffer contents, if available def readLine(prompt: String): String = { def handler: Catcher[String] = { diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala index f73df48fea..1da96de0d9 100644 --- a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala @@ -17,10 +17,6 @@ class JLineReader(interpreter: Interpreter) extends InteractiveReader { override lazy val completion = Option(interpreter) map (x => new Completion(x)) override def init() = consoleReader.getTerminal().initializeTerminal() - locally { - interpreter.installSigIntHandler() - } - val consoleReader = { val r = new jline.ConsoleReader() r setHistory (History().jhistory) @@ -33,6 +29,7 @@ class JLineReader(interpreter: Interpreter) extends InteractiveReader { r } + override def currentLine: String = consoleReader.getCursorBuffer.getBuffer.toString def readOneLine(prompt: String) = consoleReader readLine prompt val interactive = true } -- cgit v1.2.3