Skip to content

Commit aefd7cf

Browse files
committed
Fix #3007: Add support for initial and cleanup command in the REPL
1 parent fbcd879 commit aefd7cf

File tree

4 files changed

+20
-25
lines changed

4 files changed

+20
-25
lines changed

compiler/src/dotty/tools/repl/ReplDriver.scala

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import org.jline.reader._
2727
import scala.annotation.tailrec
2828
import scala.collection.JavaConverters._
2929

30+
3031
/** The state of the REPL contains necessary bindings instead of having to have
3132
* mutation
3233
*
@@ -70,7 +71,7 @@ class ReplDriver(settings: Array[String],
7071
}
7172

7273
/** the initial, empty state of the REPL session */
73-
protected[this] def initState = State(0, 0, Nil, rootCtx)
74+
final def initialState = State(0, 0, Nil, rootCtx)
7475

7576
/** Reset state of repl to the initial state
7677
*
@@ -101,7 +102,7 @@ class ReplDriver(settings: Array[String],
101102
* observable outside of the CLI, for this reason, most helper methods are
102103
* `protected final` to facilitate testing.
103104
*/
104-
final def runUntilQuit(): State = {
105+
final def runUntilQuit(initialState: State = initialState): State = {
105106
val terminal = new JLineTerminal()
106107

107108
/** Blockingly read a line, getting back a parse result */
@@ -127,7 +128,7 @@ class ReplDriver(settings: Array[String],
127128
else loop(interpret(res)(state))
128129
}
129130

130-
try withRedirectedOutput { loop(initState) }
131+
try withRedirectedOutput { loop(initialState) }
131132
finally terminal.close()
132133
}
133134

@@ -136,6 +137,9 @@ class ReplDriver(settings: Array[String],
136137
interpret(parsed)
137138
}
138139

140+
// TODO: i5069
141+
final def bind(name: String, value: Any)(implicit state: State): State = state
142+
139143
private def withRedirectedOutput(op: => State): State =
140144
Console.withOut(out) { Console.withErr(out) { op } }
141145

@@ -308,7 +312,7 @@ class ReplDriver(settings: Array[String],
308312

309313
case Reset =>
310314
resetToInitial()
311-
initState
315+
initialState
312316

313317
case Imports =>
314318
state.imports.foreach(i => out.println(SyntaxHighlighting(i.show(state.context))))

compiler/test/dotty/tools/repl/ReplTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class ReplTest private (out: ByteArrayOutputStream) extends ReplDriver(
3232
storedOutput()
3333

3434
def fromInitialState[A](op: State => A): A =
35-
op(initState)
35+
op(initialState)
3636

3737
implicit class TestingState(state: State) {
3838
def andThen[A](op: State => A): A = op(state)

compiler/test/dotty/tools/repl/ScriptedTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class ScriptedTests extends ReplTest with MessageRendering {
6565
resetToInitial()
6666
val inputRes = extractInputs(prompt)
6767
val buf = new ArrayBuffer[String]
68-
inputRes.foldLeft(initState) { (state, input) =>
68+
inputRes.foldLeft(initialState) { (state, input) =>
6969
val (out, nstate) = evaluate(state, input, prompt)
7070
buf.append(out)
7171
nstate

sbt-bridge/src/xsbt/ConsoleInterface.scala

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,6 @@ class ConsoleInterface {
1919
def run(args: Array[String],
2020
bootClasspathString: String,
2121
classpathString: String,
22-
// TODO: initial commands needs to be run under some form of special
23-
// "silent" mode in the REPL. I.e. the effects should be had without
24-
// any visual output.
25-
//
26-
// To do this we can use the `run` interface to the `ReplDriver` and
27-
// pass it a special instance of `ParseResult` like `Silently(res: ParseResult)`
28-
// and then observe the effects without printing to `ReplDriver#out`
29-
//
30-
// This way, the REPL can offer feedback on invalid commands but
31-
// still function without stringly logic.
32-
//
33-
// This same principle can be applied to `cleanupCommands` and
34-
// `bindValues`
35-
//
36-
// Steps:
37-
//
38-
// 1. Introduce `case class Silent(res: ParseResult) extends ParseResult`
39-
// 2. Perform all steps in `interpret` as usual without printing to `out`
4022
initialCommands: String,
4123
cleanupCommands: String,
4224
loader: ClassLoader,
@@ -51,6 +33,15 @@ class ConsoleInterface {
5133
} ++
5234
Array("-classpath", classpathString)
5335

54-
new ReplDriver(completeArgs, classLoader = Some(loader)).runUntilQuit()
36+
val driver = new ReplDriver(completeArgs, classLoader = Some(loader))
37+
38+
val s0 = (bindNames, bindValues).zipped.foldLeft(driver.initialState) {
39+
case (state, (name, value)) => driver.bind(name, value)(state)
40+
}
41+
42+
val s1 = driver.run(initialCommands)(s0)
43+
// TODO handle failure during initialisation
44+
val s2 = driver.runUntilQuit(s1)
45+
driver.run(cleanupCommands)(s2)
5546
}
5647
}

0 commit comments

Comments
 (0)