summaryrefslogtreecommitdiff
path: root/src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala
diff options
context:
space:
mode:
authorSom Snytt <som.snytt@gmail.com>2016-02-25 13:41:20 -0800
committerSom Snytt <som.snytt@gmail.com>2016-05-20 17:01:34 -0700
commit99dad60d984d3f72338f3bad4c4fe905090edd51 (patch)
treec70abab26b1116869e02606abac1167232ab28f4 /src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala
parent0d0671ae10ef552d66861248fa087306c960520e (diff)
downloadscala-99dad60d984d3f72338f3bad4c4fe905090edd51.tar.gz
scala-99dad60d984d3f72338f3bad4c4fe905090edd51.tar.bz2
scala-99dad60d984d3f72338f3bad4c4fe905090edd51.zip
SI-7898 Read user input during REPL warmup
The compiler is created on main thread and user input is read on an aux thread (opposite to currently). Fixes completion when `-i` is supplied. Now `-i` means pasted and new option `-I` means line-by-line. The temporary reader uses postInit to swap in the underlying reader. Completion is disabled for the temporary reader, rather than blocking while it waits for a compiler. But manically hitting tab is one way of knowing exactly when completion is live.
Diffstat (limited to 'src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala')
-rw-r--r--src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala95
1 files changed, 95 insertions, 0 deletions
diff --git a/src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala b/src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala
index 71753a3e39..1f81d9965c 100644
--- a/src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala
+++ b/src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala
@@ -50,3 +50,98 @@ object InteractiveReader {
def createDefault(): InteractiveReader = apply() // used by sbt
}
+/** Collect one line of user input from the supplied reader.
+ * Runs on a new thread while the REPL is initializing on the main thread.
+ *
+ * The user can enter text or a `:paste` command.
+ */
+class SplashLoop(reader: InteractiveReader, prompt: String) extends Runnable {
+ import java.util.concurrent.SynchronousQueue
+ import scala.compat.Platform.EOL
+
+ private val result = new SynchronousQueue[Option[String]]
+ @volatile private var running: Boolean = _
+ private var thread: Thread = _
+
+ /** Read one line of input which can be retrieved with `line`. */
+ def run(): Unit = {
+ var line = ""
+ try
+ do {
+ line = reader.readLine(prompt)
+ if (line != null) {
+ line = process(line.trim)
+ }
+ } while (line != null && line.isEmpty && running)
+ finally {
+ result.put(Option(line))
+ }
+ }
+
+ /** Check for `:paste` command. */
+ private def process(line: String): String = {
+ def isPrefix(s: String, p: String, n: Int) = (
+ //s != null && p.inits.takeWhile(_.length >= n).exists(s == _)
+ s != null && s.length >= n && s.length <= p.length && s == p.take(s.length)
+ )
+ if (isPrefix(line, ":paste", 3)) {
+ // while collecting lines, check running flag
+ var help = f"// Entering paste mode (ctrl-D to finish)%n%n"
+ def readWhile(cond: String => Boolean) = {
+ Iterator continually reader.readLine(help) takeWhile { x =>
+ help = ""
+ x != null && cond(x)
+ }
+ }
+ val text = (readWhile(_ => running) mkString EOL).trim
+ val next =
+ if (text.isEmpty) "// Nothing pasted, nothing gained."
+ else "// Exiting paste mode, now interpreting."
+ Console println f"%n${next}%n"
+ text
+ } else {
+ line
+ }
+ }
+
+ def start(): Unit = result.synchronized {
+ require(thread == null, "Already started")
+ thread = new Thread(this)
+ running = true
+ thread.start()
+ }
+
+ def stop(): Unit = result.synchronized {
+ running = false
+ if (thread != null) thread.interrupt()
+ thread = null
+ }
+
+ /** Block for the result line, or null on ctl-D. */
+ def line: String = result.take getOrElse null
+}
+object SplashLoop {
+ def apply(reader: SplashReader, prompt: String): SplashLoop = new SplashLoop(reader, prompt)
+}
+
+/** Reader during splash. Handles splash-completion with a stub, otherwise delegates. */
+class SplashReader(reader: InteractiveReader, postIniter: InteractiveReader => Unit) extends InteractiveReader {
+ /** Invoke the postInit action with the underlying reader. */
+ override def postInit(): Unit = postIniter(reader)
+
+ override val interactive: Boolean = reader.interactive
+
+ override def reset(): Unit = reader.reset()
+ override def history: History = reader.history
+ override val completion: Completion = NoCompletion
+ override def redrawLine(): Unit = reader.redrawLine()
+
+ override protected[interpreter] def readOneLine(prompt: String): String = ??? // unused
+ override protected[interpreter] def readOneKey(prompt: String): Int = ??? // unused
+
+ override def readLine(prompt: String): String = reader.readLine(prompt)
+}
+object SplashReader {
+ def apply(reader: InteractiveReader)(postIniter: InteractiveReader => Unit) =
+ new SplashReader(reader, postIniter)
+}