Skip to content

Commit d66e804

Browse files
lihaoyitgodzik
authored andcommitted
.
[Cherry-picked e1aee77]
1 parent ace7c9a commit d66e804

File tree

2 files changed

+39
-3
lines changed

2 files changed

+39
-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: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ class ReplDriver(settings: Array[String],
159159
s"""Welcome to Scala $simpleVersionString ($javaVersion, Java $javaVmName).
160160
|Type in expressions for evaluation. Or try :help.""".stripMargin)
161161

162+
// Track the time of last Ctrl-C
163+
var lastCtrlCTime: Long = 0L
164+
val ctrlCWindowMs = 1000L // 1 second window for double Ctrl-C
165+
162166
/** Blockingly read a line, getting back a parse result */
163167
def readLine()(using state: State): ParseResult = {
164168
given Context = state.context
@@ -195,16 +199,44 @@ class ReplDriver(settings: Array[String],
195199
val line = terminal.readLine(completer)
196200
ParseResult(line)
197201
} catch {
198-
case _: EndOfFileException |
199-
_: UserInterruptException => // Ctrl+D or Ctrl+C
202+
case _: EndOfFileException => // Ctrl+D
200203
Quit
204+
case _: UserInterruptException => // Ctrl+C at prompt - clear and continue
205+
SigKill
201206
}
202207
}
203208

204209
@tailrec def loop(using state: State)(): State = {
205210
val res = readLine()
206211
if (res == Quit) state
207-
else loop(using interpret(res))()
212+
else if (res == SigKill) {
213+
// Ctrl-C pressed at prompt - just continue with same state (line is cleared by JLine)
214+
loop(using state)()
215+
} else {
216+
// Set up interrupt handler for command execution
217+
val thread = Thread.currentThread()
218+
val signalHandler = terminal.handle(
219+
org.jline.terminal.Terminal.Signal.INT,
220+
(sig: org.jline.terminal.Terminal.Signal) => {
221+
val now = System.currentTimeMillis()
222+
if (now - lastCtrlCTime < ctrlCWindowMs) {
223+
// Second Ctrl-C within window - interrupt the thread
224+
out.println("\nTerminating REPL...")
225+
thread.interrupt()
226+
System.exit(130) // Standard exit code for SIGINT
227+
} else {
228+
// First Ctrl-C - warn user
229+
lastCtrlCTime = now
230+
out.println("\nPress Ctrl-C again to terminate the process")
231+
}
232+
}
233+
)
234+
235+
val newState = interpret(res)
236+
// Restore previous handler
237+
terminal.handle(org.jline.terminal.Terminal.Signal.INT, signalHandler)
238+
loop(using newState)()
239+
}
208240
}
209241

210242
try runBody { loop() }

0 commit comments

Comments
 (0)