From 126effe46005673ca826045e9cb037096d68af90 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 16 Oct 2014 11:15:16 -0700 Subject: SI-8922 REPL load -v Verbose mode causes the familiar prompt and line echo so you can see what you just loaded. The quit message is pushed up a level in the process loop. This has the huge payoff that if you start the repl and immediately hit ctl-D, you don't have to wait for the compiler to init (yawn) before you get a shell prompt back. --- src/repl/scala/tools/nsc/interpreter/ILoop.scala | 65 +++++++++++++--------- .../scala/tools/nsc/interpreter/LoopCommands.scala | 4 +- .../scala/tools/nsc/interpreter/SimpleReader.scala | 29 +++++++--- 3 files changed, 64 insertions(+), 34 deletions(-) (limited to 'src/repl') diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala index 6e18682494..1c751c8a9f 100644 --- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala @@ -392,23 +392,23 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) true } + // after process line, OK continue, ERR break, or EOF all done + object LineResults extends Enumeration { + type LineResult = Value + val EOF, ERR, OK = Value + } + import LineResults.LineResult + // return false if repl should exit def processLine(line: String): Boolean = { import scala.concurrent.duration._ Await.ready(globalFuture, 10.minutes) // Long timeout here to avoid test failures under heavy load. - if (line eq null) { - // SI-4563: this means the console was properly interrupted (Ctrl+D usually) - // so we display the output message (which by default ends with - // a newline so as not to break the user's terminal) - if (in.interactive) out.print(Properties.shellInterruptedString) - - false - } else (command(line) match { + command(line) match { case Result(false, _) => false case Result(_, Some(line)) => addReplay(line) ; true case _ => true - }) + } } private def readOneLine() = { @@ -426,18 +426,22 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) * command() for each line of input, and stops when * command() returns false. */ - @tailrec final def loop() { - if ( try processLine(readOneLine()) catch crashRecovery ) - loop() + @tailrec final def loop(): LineResult = { + import LineResults._ + readOneLine() match { + case null => EOF + case line => if (try processLine(line) catch crashRecovery) loop() else ERR + } } /** interpret all lines from a specified file */ - def interpretAllFrom(file: File) { + def interpretAllFrom(file: File, verbose: Boolean = false) { savingReader { savingReplayStack { file applyReader { reader => - in = SimpleReader(reader, out, interactive = false) - echo("Loading " + file + "...") + in = if (verbose) new SimpleReader(reader, out, interactive = true) with EchoReader + else SimpleReader(reader, out, interactive = false) + echo(s"Loading $file...") loop() } } @@ -592,13 +596,17 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) res } - def loadCommand(arg: String) = { - var shouldReplay: Option[String] = None - withFile(arg)(f => { - interpretAllFrom(f) - shouldReplay = Some(":load " + arg) - }) - Result(keepRunning = true, shouldReplay) + def loadCommand(arg: String): Result = { + def run(file: String, verbose: Boolean) = withFile(file) { f => + interpretAllFrom(f, verbose) + Result recording s":load $arg" + } getOrElse Result.default + + words(arg) match { + case "-v" :: file :: Nil => run(file, verbose = true) + case file :: Nil => run(file, verbose = false) + case _ => echo("usage: :load -v file") ; Result.default + } } def saveCommand(filename: String): Result = ( @@ -685,13 +693,13 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) } val code = file match { case Some(name) => - withFile(name)(f => { + withFile(name) { f => shouldReplay = Some(s":paste $arg") val s = f.slurp.trim if (s.isEmpty) echo(s"File contains no code: $f") else echo(s"Pasting file $f...") s - }) getOrElse "" + } getOrElse "" case None => echo("// Entering paste mode (ctrl-D to finish)\n") val text = (readWhile(_ => true) mkString "\n").trim @@ -820,7 +828,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) ) catch { case ex @ (_: Exception | _: NoClassDefFoundError) => - echo("Failed to created JLineReader: " + ex + "\nFalling back to SimpleReader.") + echo(f"Failed to created JLineReader: ${ex}%nFalling back to SimpleReader.") SimpleReader() } } @@ -847,6 +855,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) case _ => } } + + // start an interpreter with the given settings def process(settings: Settings): Boolean = savingContextLoader { this.settings = settings createInterpreter() @@ -861,7 +871,10 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) loadFiles(settings) printWelcome() - try loop() + try loop() match { + case LineResults.EOF => out print Properties.shellInterruptedString + case _ => + } catch AbstractOrMissingHandler() finally closeInterpreter() diff --git a/src/repl/scala/tools/nsc/interpreter/LoopCommands.scala b/src/repl/scala/tools/nsc/interpreter/LoopCommands.scala index 12d6ee5112..f177816b30 100644 --- a/src/repl/scala/tools/nsc/interpreter/LoopCommands.scala +++ b/src/repl/scala/tools/nsc/interpreter/LoopCommands.scala @@ -76,6 +76,9 @@ trait LoopCommands { // the default result means "keep running, and don't record that line" val default = Result(keepRunning = true, None) + // "keep running, and record this line" + def recording(line: String) = Result(keepRunning = true, Option(line)) + // most commands do not want to micromanage the Result, but they might want // to print something to the console, so we accomodate Unit and String returns. implicit def resultFromUnit(x: Unit): Result = default @@ -85,4 +88,3 @@ trait LoopCommands { } } } - diff --git a/src/repl/scala/tools/nsc/interpreter/SimpleReader.scala b/src/repl/scala/tools/nsc/interpreter/SimpleReader.scala index 6634dc6944..49b8433a8c 100644 --- a/src/repl/scala/tools/nsc/interpreter/SimpleReader.scala +++ b/src/repl/scala/tools/nsc/interpreter/SimpleReader.scala @@ -22,14 +22,19 @@ extends InteractiveReader def reset() = () def redrawLine() = () - def readOneLine(prompt: String): String = { - if (interactive) { - out.print(prompt) - out.flush() - } - in.readLine() + + // InteractiveReader internals + protected def readOneLine(prompt: String): String = { + echo(prompt) + readOneLine() + } + protected def readOneKey(prompt: String) = sys.error("No char-based input in SimpleReader") + + protected def readOneLine(): String = in.readLine() + protected def echo(s: String): Unit = if (interactive) { + out.print(s) + out.flush() } - def readOneKey(prompt: String) = sys.error("No char-based input in SimpleReader") } object SimpleReader { @@ -39,3 +44,13 @@ object SimpleReader { def apply(in: BufferedReader = defaultIn, out: JPrintWriter = defaultOut, interactive: Boolean = true): SimpleReader = new SimpleReader(in, out, interactive) } + +// pretend we are a console for verbose purposes +trait EchoReader extends SimpleReader { + // if there is more input, then maybe echo the prompt and the input + override def readOneLine(prompt: String) = { + val input = readOneLine() + if (input != null) echo(f"$prompt$input%n") + input + } +} -- cgit v1.2.3