Skip to content

Commit 92cda8a

Browse files
authored
Handle SIGINT When Reading User Input (#1082)
* Handle sigint when reading input * next work * signaller fix
1 parent 30a5cb8 commit 92cda8a

File tree

5 files changed

+34
-36
lines changed

5 files changed

+34
-36
lines changed

amm/repl/src/main/scala/ammonite/repl/AmmoniteFrontEnd.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ case class AmmoniteFrontEnd(extraFilters: Filter = Filter.empty) extends FrontEn
5050

5151
val autocompleteFilter: Filter = Filter.action(SpecialKeys.Tab){
5252
case TermState(rest, b, c, _) =>
53-
val (newCursor, completions, details) = compilerComplete(c, b.mkString)
53+
val (newCursor, completions, details) = TTY.withSttyOverride(TTY.restoreSigInt()) {
54+
compilerComplete(c, b.mkString)
55+
}
5456
val details2 = for (d <- details) yield {
5557

5658
Highlighter.defaultHighlight(

amm/repl/src/main/scala/ammonite/repl/Repl.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,14 @@ class Repl(input: InputStream,
173173
case ex => Res.Exception(ex, "")
174174
}
175175

176+
_ <- Signaller("INT") {
177+
// Put a fake `ThreadDeath` error in `lastException`, because `Thread#stop`
178+
// raises an error with the stack trace of *this interrupt thread*, rather
179+
// than the stack trace of *the mainThread*
180+
lastException = new ThreadDeath()
181+
lastException.setStackTrace(Repl.truncateStackTrace(interp.mainThread.getStackTrace))
182+
interp.mainThread.stop()
183+
}
176184
(code, stmts) <- frontEnd().action(
177185
input,
178186
reader,
@@ -186,14 +194,6 @@ class Repl(input: InputStream,
186194
history = history :+ code
187195
}
188196
)
189-
_ <- Signaller("INT") {
190-
// Put a fake `ThreadDeath` error in `lastException`, because `Thread#stop`
191-
// raises an error with the stack trace of *this interrupt thread*, rather
192-
// than the stack trace of *the mainThread*
193-
lastException = new ThreadDeath()
194-
lastException.setStackTrace(Repl.truncateStackTrace(interp.mainThread.getStackTrace))
195-
interp.mainThread.stop()
196-
}
197197
out <- interp.processLine(code, stmts, currentLine, false, () => currentLine += 1)
198198
} yield {
199199
printer.outStream.println()

amm/repl/src/main/scala/ammonite/repl/Signaller.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ case class Signaller(sigStr: String)(f: => Unit) extends Scoped{
2929
finally{
3030
val head::tail = handlers(sig)
3131
handlers(sig) = tail
32-
sun.misc.Signal.handle(sig, head)
32+
val handlerToRegister = tail.headOption.getOrElse(sun.misc.SignalHandler.SIG_DFL)
33+
sun.misc.Signal.handle(sig, handlerToRegister)
3334
}
3435
}
3536
}
@@ -41,6 +42,6 @@ case class Signaller(sigStr: String)(f: => Unit) extends Scoped{
4142
trait Scoped{
4243
def apply[T](t: => T): T
4344
def foreach[T](t: Unit => T): T = apply(t(()))
44-
def flatMap[T, M[_]](t: Unit => M[T]): M[T] = apply(t(()))
45+
def flatMap[T](t: Unit => T): T = apply(t(()))
4546
def map[T](t: Unit => T): T = apply(t(()))
4647
}

terminal/src/main/scala/ammonite/terminal/Terminal.scala

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,9 @@ object Terminal {
3636
filters: Filter,
3737
displayTransform: (Vector[Char], Int) => (fansi.Str, Int) = LineReader.noTransform)
3838
: Option[String] = {
39-
40-
41-
val initialConfig = TTY.init()
42-
try {
39+
TTY.withSttyOverride(TTY.readLineStty()) {
4340
new LineReader(ConsoleDim.width(), prompt, reader, writer, filters, displayTransform)
4441
.readChar(TermState(LazyList.continually(reader.read()), Vector.empty, 0, ""), 0)
45-
}finally{
46-
47-
// Don't close these! Closing these closes stdin/stdout,
48-
// which seems to kill the entire program
49-
50-
// reader.close()
51-
// writer.close()
52-
TTY.stty(initialConfig)
5342
}
5443
}
5544
}

terminal/src/main/scala/ammonite/terminal/Utils.scala

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,6 @@ object TTY{
9393
import sys.process._
9494
Seq("sh", "-c", s"$pathedTput $s 2> /dev/tty").!!.trim.toInt
9595
}
96-
def init() = {
97-
stty("-a")
98-
99-
val initialConfig = stty("-g").trim
100-
stty("-icanon min 1 -icrnl -inlcr -ixon")
101-
sttyFailTolerant("dsusp undef")
102-
stty("-echo")
103-
stty("intr undef")
104-
105-
initialConfig
106-
}
10796

10897
private def sttyCmd(s: String) = {
10998
import sys.process._
@@ -121,8 +110,25 @@ object TTY{
121110
def sttyFailTolerant(s: String) =
122111
sttyCmd(s ++ " 2> /dev/null").!
123112

124-
def restore(initialConfig: String) = {
125-
stty(initialConfig)
113+
def withSttyOverride[A](setupStty: => Unit)(f: => A) = {
114+
val initialConfig = stty("-g").trim
115+
try {
116+
setupStty
117+
f
118+
} finally {
119+
stty(initialConfig)
120+
}
121+
}
122+
123+
def readLineStty(): Unit = {
124+
stty("-icanon min 1 -icrnl -inlcr -ixon")
125+
sttyFailTolerant("dsusp undef")
126+
stty("-echo")
127+
stty("intr undef")
128+
}
129+
130+
def restoreSigInt(): Unit = {
131+
stty("intr ^C")
126132
}
127133
}
128134

0 commit comments

Comments
 (0)