Skip to content

Commit f21a593

Browse files
authored
Merge pull request #624 from scala/backport-lts-3.3-24127
Backport "Trap Ctrl-C in the REPL: if no command is running clear the prompt, if some command is running ask for confirmation before exiting" to 3.3 LTS
2 parents 1f6e3a3 + d381106 commit f21a593

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

compiler/src/dotty/tools/repl/JLineTerminal.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ class JLineTerminal extends java.io.Closeable {
8282

8383
def close(): Unit = terminal.close()
8484

85+
/** Register a signal handler and return the previous handler */
86+
def handle(signal: org.jline.terminal.Terminal.Signal, handler: org.jline.terminal.Terminal.SignalHandler): org.jline.terminal.Terminal.SignalHandler =
87+
terminal.handle(signal, handler)
88+
8589
/** Provide syntax highlighting */
8690
private class Highlighter(using Context) extends reader.Highlighter {
8791
def highlight(reader: LineReader, buffer: String): AttributedString = {

compiler/src/dotty/tools/repl/ReplDriver.scala

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,16 +195,44 @@ class ReplDriver(settings: Array[String],
195195
val line = terminal.readLine(completer)
196196
ParseResult(line)
197197
} catch {
198-
case _: EndOfFileException |
199-
_: UserInterruptException => // Ctrl+D or Ctrl+C
198+
case _: EndOfFileException => // Ctrl+D
200199
Quit
200+
case _: UserInterruptException => // Ctrl+C at prompt - clear and continue
201+
SigKill
201202
}
202203
}
203204

204205
@tailrec def loop(using state: State)(): State = {
206+
205207
val res = readLine()
206208
if (res == Quit) state
207-
else loop(using interpret(res))()
209+
// Ctrl-C pressed at prompt - just continue with same state (line is cleared by JLine)
210+
else if (res == SigKill) loop(using state)()
211+
else {
212+
// Set up interrupt handler for command execution
213+
var firstCtrlCEntered = false
214+
val thread = Thread.currentThread()
215+
val previousSignalHandler = terminal.handle(
216+
org.jline.terminal.Terminal.Signal.INT,
217+
(sig: org.jline.terminal.Terminal.Signal) => {
218+
if (!firstCtrlCEntered) {
219+
firstCtrlCEntered = true
220+
thread.interrupt()
221+
out.println("\nInterrupting running thread, Ctrl-C again to terminate the REPL Process")
222+
} else {
223+
out.println("\nTerminating REPL Process...")
224+
System.exit(130) // Standard exit code for SIGINT
225+
}
226+
}
227+
)
228+
229+
val newState =
230+
try interpret(res)
231+
// Restore previous handler
232+
finally terminal.handle(org.jline.terminal.Terminal.Signal.INT, previousSignalHandler)
233+
234+
loop(using newState)()
235+
}
208236
}
209237

210238
try runBody { loop() }

0 commit comments

Comments
 (0)