From aefd7cf1d2e4f35b9e213c7afc75952a8f861c78 Mon Sep 17 00:00:00 2001 From: Szymon Pajzert Date: Sun, 26 Aug 2018 10:21:33 +0000 Subject: [PATCH] Fix #3007: Add support for initial and cleanup command in the REPL --- .../src/dotty/tools/repl/ReplDriver.scala | 12 +++++--- compiler/test/dotty/tools/repl/ReplTest.scala | 2 +- .../test/dotty/tools/repl/ScriptedTests.scala | 2 +- sbt-bridge/src/xsbt/ConsoleInterface.scala | 29 +++++++------------ 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 536c5fccd2c1..59732aa959b1 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -27,6 +27,7 @@ import org.jline.reader._ import scala.annotation.tailrec import scala.collection.JavaConverters._ + /** The state of the REPL contains necessary bindings instead of having to have * mutation * @@ -70,7 +71,7 @@ class ReplDriver(settings: Array[String], } /** the initial, empty state of the REPL session */ - protected[this] def initState = State(0, 0, Nil, rootCtx) + final def initialState = State(0, 0, Nil, rootCtx) /** Reset state of repl to the initial state * @@ -101,7 +102,7 @@ class ReplDriver(settings: Array[String], * observable outside of the CLI, for this reason, most helper methods are * `protected final` to facilitate testing. */ - final def runUntilQuit(): State = { + final def runUntilQuit(initialState: State = initialState): State = { val terminal = new JLineTerminal() /** Blockingly read a line, getting back a parse result */ @@ -127,7 +128,7 @@ class ReplDriver(settings: Array[String], else loop(interpret(res)(state)) } - try withRedirectedOutput { loop(initState) } + try withRedirectedOutput { loop(initialState) } finally terminal.close() } @@ -136,6 +137,9 @@ class ReplDriver(settings: Array[String], interpret(parsed) } + // TODO: i5069 + final def bind(name: String, value: Any)(implicit state: State): State = state + private def withRedirectedOutput(op: => State): State = Console.withOut(out) { Console.withErr(out) { op } } @@ -308,7 +312,7 @@ class ReplDriver(settings: Array[String], case Reset => resetToInitial() - initState + initialState case Imports => state.imports.foreach(i => out.println(SyntaxHighlighting(i.show(state.context)))) diff --git a/compiler/test/dotty/tools/repl/ReplTest.scala b/compiler/test/dotty/tools/repl/ReplTest.scala index a849da598ef3..921d16a651a3 100644 --- a/compiler/test/dotty/tools/repl/ReplTest.scala +++ b/compiler/test/dotty/tools/repl/ReplTest.scala @@ -32,7 +32,7 @@ class ReplTest private (out: ByteArrayOutputStream) extends ReplDriver( storedOutput() def fromInitialState[A](op: State => A): A = - op(initState) + op(initialState) implicit class TestingState(state: State) { def andThen[A](op: State => A): A = op(state) diff --git a/compiler/test/dotty/tools/repl/ScriptedTests.scala b/compiler/test/dotty/tools/repl/ScriptedTests.scala index d5ac633ffc4c..090d49949d08 100644 --- a/compiler/test/dotty/tools/repl/ScriptedTests.scala +++ b/compiler/test/dotty/tools/repl/ScriptedTests.scala @@ -65,7 +65,7 @@ class ScriptedTests extends ReplTest with MessageRendering { resetToInitial() val inputRes = extractInputs(prompt) val buf = new ArrayBuffer[String] - inputRes.foldLeft(initState) { (state, input) => + inputRes.foldLeft(initialState) { (state, input) => val (out, nstate) = evaluate(state, input, prompt) buf.append(out) nstate diff --git a/sbt-bridge/src/xsbt/ConsoleInterface.scala b/sbt-bridge/src/xsbt/ConsoleInterface.scala index a98de74ff76a..34004528fea4 100644 --- a/sbt-bridge/src/xsbt/ConsoleInterface.scala +++ b/sbt-bridge/src/xsbt/ConsoleInterface.scala @@ -19,24 +19,6 @@ class ConsoleInterface { def run(args: Array[String], bootClasspathString: String, classpathString: String, - // TODO: initial commands needs to be run under some form of special - // "silent" mode in the REPL. I.e. the effects should be had without - // any visual output. - // - // To do this we can use the `run` interface to the `ReplDriver` and - // pass it a special instance of `ParseResult` like `Silently(res: ParseResult)` - // and then observe the effects without printing to `ReplDriver#out` - // - // This way, the REPL can offer feedback on invalid commands but - // still function without stringly logic. - // - // This same principle can be applied to `cleanupCommands` and - // `bindValues` - // - // Steps: - // - // 1. Introduce `case class Silent(res: ParseResult) extends ParseResult` - // 2. Perform all steps in `interpret` as usual without printing to `out` initialCommands: String, cleanupCommands: String, loader: ClassLoader, @@ -51,6 +33,15 @@ class ConsoleInterface { } ++ Array("-classpath", classpathString) - new ReplDriver(completeArgs, classLoader = Some(loader)).runUntilQuit() + val driver = new ReplDriver(completeArgs, classLoader = Some(loader)) + + val s0 = (bindNames, bindValues).zipped.foldLeft(driver.initialState) { + case (state, (name, value)) => driver.bind(name, value)(state) + } + + val s1 = driver.run(initialCommands)(s0) + // TODO handle failure during initialisation + val s2 = driver.runUntilQuit(s1) + driver.run(cleanupCommands)(s2) } }