Skip to content

Commit c6cf4c8

Browse files
Avoid reading input in watch mode when not necessary
1 parent a023f51 commit c6cf4c8

File tree

2 files changed

+50
-6
lines changed

2 files changed

+50
-6
lines changed

modules/cli/src/main/scala/scala/cli/commands/WatchUtil.scala

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package scala.cli.commands
22

3+
import scala.annotation.tailrec
4+
35
object WatchUtil {
46

57
lazy val isDevMode: Boolean =
@@ -17,10 +19,32 @@ object WatchUtil {
1719
def printWatchMessage(): Unit =
1820
System.err.println(waitMessage("Watching sources"))
1921

20-
def waitForCtrlC(onPressEnter: () => Unit = () => ()): Unit = {
22+
def waitForCtrlC(
23+
onPressEnter: () => Unit = () => (),
24+
shouldReadInput: () => Boolean = () => true
25+
): Unit = synchronized {
26+
27+
@tailrec
28+
def readNextChar(): Int =
29+
if (shouldReadInput())
30+
try System.in.read()
31+
catch {
32+
case _: InterruptedException =>
33+
// Actually never called, as System.in.read isn't interruptible…
34+
// That means we sometimes read input when we shouldn't.
35+
readNextChar()
36+
}
37+
else {
38+
try wait()
39+
catch {
40+
case _: InterruptedException =>
41+
}
42+
readNextChar()
43+
}
44+
2145
var readKey = -1
2246
while ({
23-
readKey = System.in.read()
47+
readKey = readNextChar()
2448
readKey != -1
2549
})
2650
if (readKey == '\n')

modules/cli/src/main/scala/scala/cli/commands/run/Run.scala

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,9 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
194194
)
195195

196196
if (options.sharedRun.watch.watchMode) {
197-
var processOpt = Option.empty[(Process, CompletableFuture[_])]
197+
var processOpt = Option.empty[(Process, CompletableFuture[_])]
198+
var shouldReadInput = false
199+
var mainThreadOpt = Option.empty[Thread]
198200
val watcher = Build.watch(
199201
inputs,
200202
initialBuildOptions,
@@ -216,6 +218,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
216218
for ((proc, _) <- processOpt if proc.isAlive)
217219
// If the process doesn't exit, send SIGKILL
218220
ProcUtil.forceKillProcess(proc, logger)
221+
shouldReadInput = false
222+
mainThreadOpt.foreach(_.interrupt())
219223
val maybeProcess = maybeRun(
220224
s,
221225
allowTerminate = false,
@@ -225,18 +229,34 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
225229
)
226230
.orReport(logger)
227231
.flatten
232+
.map {
233+
case (proc, onExit) =>
234+
if (options.sharedRun.watch.restart)
235+
onExit.thenApply { _ =>
236+
shouldReadInput = true
237+
mainThreadOpt.foreach(_.interrupt())
238+
}
239+
(proc, onExit)
240+
}
228241
s.copyOutput(options.shared)
229242
if (options.sharedRun.watch.restart)
230243
processOpt = maybeProcess
231-
else
244+
else {
232245
for ((proc, onExit) <- maybeProcess)
233246
ProcUtil.waitForProcess(proc, onExit)
247+
shouldReadInput = true
248+
mainThreadOpt.foreach(_.interrupt())
249+
}
234250
case _: Build.Failed =>
235251
System.err.println("Compilation failed")
236252
}
237253
}
238-
try WatchUtil.waitForCtrlC(() => watcher.schedule())
239-
finally watcher.dispose()
254+
mainThreadOpt = Some(Thread.currentThread())
255+
try WatchUtil.waitForCtrlC(() => watcher.schedule(), () => shouldReadInput)
256+
finally {
257+
mainThreadOpt = None
258+
watcher.dispose()
259+
}
240260
}
241261
else {
242262
val builds =

0 commit comments

Comments
 (0)