diff --git a/compiler/src/dotty/tools/repl/JLineTerminal.scala b/compiler/src/dotty/tools/repl/JLineTerminal.scala index 2680704d326e..fc193667154d 100644 --- a/compiler/src/dotty/tools/repl/JLineTerminal.scala +++ b/compiler/src/dotty/tools/repl/JLineTerminal.scala @@ -82,6 +82,10 @@ class JLineTerminal extends java.io.Closeable { def close(): Unit = terminal.close() + /** Register a signal handler and return the previous handler */ + def handle(signal: org.jline.terminal.Terminal.Signal, handler: org.jline.terminal.Terminal.SignalHandler): org.jline.terminal.Terminal.SignalHandler = + terminal.handle(signal, handler) + /** Provide syntax highlighting */ private class Highlighter(using Context) extends reader.Highlighter { def highlight(reader: LineReader, buffer: String): AttributedString = { diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index befb3de9a941..e42773a916ac 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -211,16 +211,44 @@ class ReplDriver(settings: Array[String], val line = terminal.readLine(completer) ParseResult(line) } catch { - case _: EndOfFileException | - _: UserInterruptException => // Ctrl+D or Ctrl+C + case _: EndOfFileException => // Ctrl+D Quit + case _: UserInterruptException => // Ctrl+C at prompt - clear and continue + SigKill } } @tailrec def loop(using state: State)(): State = { + val res = readLine() if (res == Quit) state - else loop(using interpret(res))() + // Ctrl-C pressed at prompt - just continue with same state (line is cleared by JLine) + else if (res == SigKill) loop(using state)() + else { + // Set up interrupt handler for command execution + var firstCtrlCEntered = false + val thread = Thread.currentThread() + val previousSignalHandler = terminal.handle( + org.jline.terminal.Terminal.Signal.INT, + (sig: org.jline.terminal.Terminal.Signal) => { + if (!firstCtrlCEntered) { + firstCtrlCEntered = true + thread.interrupt() + out.println("\nInterrupting running thread, Ctrl-C again to terminate the REPL Process") + }else { + out.println("\nTerminating REPL Process...") + System.exit(130) // Standard exit code for SIGINT + } + } + ) + + val newState = + try interpret(res) + // Restore previous handler + finally terminal.handle(org.jline.terminal.Terminal.Signal.INT, previousSignalHandler) + + loop(using newState)() + } } try runBody { loop() }