1
1
package dotty .tools
2
2
package repl
3
3
4
- import java .io .{ InputStream , PrintStream }
4
+ import java .io .{InputStream , PrintStream }
5
5
6
6
import scala .annotation .tailrec
7
-
8
7
import dotc .reporting .MessageRendering
9
8
import dotc .reporting .diagnostic .MessageContainer
10
9
import dotc .ast .untpd
11
10
import dotc .ast .tpd
12
- import dotc .interactive .{ SourceTree , Interactive }
11
+ import dotc .interactive .{Interactive , SourceTree }
13
12
import dotc .core .Contexts .Context
14
- import dotc .{ CompilationUnit , Run }
15
- import dotc .core .Mode
13
+ import dotc .{CompilationUnit , Run }
14
+ import dotc .core .{ Denotations , Mode }
16
15
import dotc .core .Flags ._
17
16
import dotc .core .Types ._
18
17
import dotc .core .StdNames ._
19
18
import dotc .core .Names .Name
20
19
import dotc .core .NameOps ._
21
- import dotc .core .Symbols .{ Symbol , NoSymbol , defn }
20
+ import dotc .core .Symbols .{NoSymbol , Symbol , defn }
22
21
import dotc .core .Denotations .Denotation
23
- import dotc .core .Types .{ ExprType , ConstantType }
22
+ import dotc .core .Types .{ConstantType , ExprType }
24
23
import dotc .core .NameKinds .SimpleNameKind
25
24
import dotc .config .CompilerCommand
26
- import dotc .{ Compiler , Driver }
25
+ import dotc .{Compiler , Driver }
27
26
import dotc .printing .SyntaxHighlighting
28
27
import dotc .util .Positions .Position
29
28
import io ._
30
-
31
29
import AmmoniteReader ._
32
30
import results ._
33
31
@@ -79,7 +77,9 @@ case class Completions(cursor: Int,
79
77
/** Main REPL instance, orchestrating input, compilation and presentation */
80
78
class ReplDriver (settings : Array [String ],
81
79
protected val out : PrintStream = System .out,
82
- protected val classLoader : Option [ClassLoader ] = None ) extends Driver {
80
+ protected val classLoader : Option [ClassLoader ] = None ,
81
+ initialCommands : Array [String ] = Array .empty,
82
+ cleanupCommands : Array [String ] = Array .empty) extends Driver {
83
83
84
84
/** Overridden to `false` in order to not have to give sources on the
85
85
* commandline
@@ -124,21 +124,27 @@ class ReplDriver(settings: Array[String],
124
124
resetToInitial()
125
125
126
126
/** Run REPL with `state` until `:quit` command found
127
- *
128
- * This method is the main entry point into the REPL. Its effects are not
129
- * observable outside of the CLI, for this reason, most helper methods are
130
- * `protected final` to facilitate testing.
131
- */
132
- @ tailrec final def runUntilQuit (state : State = initState): State = {
133
- val res = readLine()(state)
134
-
135
- if (res == Quit ) state
136
- else {
137
- // readLine potentially destroys the run, so a new one is needed for the
138
- // rest of the interpretation:
139
- implicit val freshState = state.newRun(compiler, rootCtx)
140
- runUntilQuit(interpret(res))
127
+ *
128
+ * This method is the main entry point into the REPL. Its effects are not
129
+ * observable outside of the CLI, for this reason, most helper methods are
130
+ * `protected final` to facilitate testing.
131
+ */
132
+ final def runUntilQuit (initialState : State = initState): State = {
133
+ @ tailrec def run (state : State = initState): State = {
134
+ val res = readLine()(state)
135
+
136
+ if (res == Quit ) state
137
+ else {
138
+ // readLine potentially destroys the run, so a new one is needed for the
139
+ // rest of the interpretation:
140
+ implicit val freshState = state.newRun(compiler, rootCtx)
141
+ run(interpret(res))
142
+ }
141
143
}
144
+
145
+ val state = runBootstrapCommands(initialCommands)(initialState)
146
+ val userState = run(state)
147
+ runBootstrapCommands(cleanupCommands)(userState)
142
148
}
143
149
144
150
final def run (input : String )(implicit state : State ): State =
@@ -147,6 +153,12 @@ class ReplDriver(settings: Array[String],
147
153
final def run (res : ParseResult )(implicit state : State ): State =
148
154
interpret(res)
149
155
156
+ final def runBootstrapCommands (cmds : Array [String ])(implicit state : State ): State = {
157
+ cmds.map(ParseResult .apply(_)(rootCtx)).map(Silent .apply(_)).foldLeft(state) { (s, cmd) =>
158
+ interpret(cmd)(s)
159
+ }
160
+ }
161
+
150
162
/** Extract possible completions at the index of `cursor` in `expr` */
151
163
protected [this ] final def completions (cursor : Int , expr : String , state0 : State ): Completions = {
152
164
// TODO move some of this logic to `Interactive`
@@ -181,10 +193,15 @@ class ReplDriver(settings: Array[String],
181
193
private def extractImports (trees : List [untpd.Tree ])(implicit context : Context ): List [(untpd.Import , String )] =
182
194
trees.collect { case imp : untpd.Import => (imp, imp.show) }
183
195
184
- private def interpret (res : ParseResult )(implicit state : State ): State =
185
- res match {
196
+ private def interpret (res : Parsing )(implicit state : State ): State = {
197
+ val (parseResult, isSilent) = res match {
198
+ case Silent (x) => (x, true )
199
+ case x : ParseResult => (x, false )
200
+ }
201
+
202
+ parseResult match {
186
203
case parsed : Parsed =>
187
- compile(parsed)
204
+ compile(parsed, isSilent )
188
205
.withHistory(parsed.sourceCode :: state.history)
189
206
.newRun(compiler, rootCtx)
190
207
@@ -194,11 +211,12 @@ class ReplDriver(settings: Array[String],
194
211
195
212
case Newline | SigKill => state
196
213
197
- case cmd : Command => interpretCommand(cmd)
214
+ case cmd : Command => interpretCommand(cmd, isSilent )
198
215
}
216
+ }
199
217
200
218
/** Compile `parsed` trees and evolve `state` in accordance */
201
- protected [this ] final def compile (parsed : Parsed )(implicit state : State ): State = {
219
+ protected [this ] final def compile (parsed : Parsed , silent : Boolean = false )(implicit state : State ): State = {
202
220
import dotc .ast .Trees .PackageDef
203
221
import untpd .{ PackageDef => _ , _ }
204
222
def extractNewestWrapper (tree : Tree ): Name = tree match {
@@ -209,21 +227,50 @@ class ReplDriver(settings: Array[String],
209
227
compiler
210
228
.compile(parsed)
211
229
.fold(
212
- displayErrors,
230
+ {
231
+ case (errors : Errors ) =>
232
+ displayErrors(errors)
233
+ },
213
234
{
214
235
case (unit : CompilationUnit , newState : State ) => {
215
236
val newestWrapper = extractNewestWrapper(unit.untpdTree)
216
237
val newImports = newState.imports ++ extractImports(parsed.trees)(newState.run.runContext)
217
238
val newStateWithImports = newState.copy(imports = newImports)
218
239
219
- displayDefinitions(unit.tpdTree, newestWrapper)(newStateWithImports)
240
+ val (decls, optIndex, symbol) = getDefinitions(unit.tpdTree, newestWrapper)(newStateWithImports)
241
+ if (! silent) displayDefinitions(decls, symbol)(newStateWithImports)
242
+
243
+ optIndex.map(i => newStateWithImports.copy(valIndex = i)).getOrElse(newStateWithImports)
220
244
}
221
245
}
222
246
)
223
247
}
224
248
225
- /** Display definitions from `tree` */
226
- private def displayDefinitions (tree : tpd.Tree , newestWrapper : Name )(implicit state : State ): State = {
249
+ /** Display definitions from `symbol` */
250
+ private def displayDefinitions (decls : Seq [String ], symbol : Option [Symbol ])(implicit state : State ): Unit = {
251
+ implicit val ctx = state.run.runContext
252
+
253
+ def displayMembers (decls : Seq [String ]): Unit = {
254
+ decls.foreach(str => out.println(SyntaxHighlighting (str)))
255
+ }
256
+
257
+ def isSyntheticCompanion (sym : Symbol ) =
258
+ sym.is(Module ) && sym.is(Synthetic )
259
+
260
+ def displayTypeDefs (optSymbol : Option [Symbol ]): Unit = optSymbol.foreach(sym => sym.info.memberClasses
261
+ .collect {
262
+ case x if ! isSyntheticCompanion(x.symbol) && ! x.symbol.name.isReplWrapperName =>
263
+ x.symbol
264
+ }
265
+ .foreach { sym =>
266
+ out.println(SyntaxHighlighting (" // defined " + sym.showUser))
267
+ })
268
+ displayTypeDefs(symbol)
269
+ displayMembers(decls)
270
+ }
271
+
272
+ /** get definitions from `tree` */
273
+ private def getDefinitions (tree : tpd.Tree , newestWrapper : Name )(implicit state : State ): (Seq [String ], Option [Int ], Option [Symbol ]) = {
227
274
implicit val ctx = state.run.runContext
228
275
229
276
def resAndUnit (denot : Denotation ) = {
@@ -237,8 +284,7 @@ class ReplDriver(settings: Array[String],
237
284
name.startsWith(str.REPL_RES_PREFIX ) && hasValidNumber && sym.info == defn.UnitType
238
285
}
239
286
240
- def displayMembers (symbol : Symbol ) = if (tree.symbol.info.exists) {
241
- val info = symbol.info
287
+ def getDeclarationsAndIndex (info : Type ): (Seq [String ], Int ) = {
242
288
val defs =
243
289
info.bounds.hi.finalResultType
244
290
.membersBasedOnFlags(Method , Accessor | ParamAccessor | Synthetic | Private )
@@ -256,54 +302,40 @@ class ReplDriver(settings: Array[String],
256
302
val typeAliases =
257
303
info.bounds.hi.typeMembers.filter(_.symbol.info.isInstanceOf [TypeAlias ])
258
304
259
- (
260
- typeAliases.map(" // defined alias " + _.symbol.showUser) ++
305
+ val declarations = typeAliases.map(" // defined alias " + _.symbol.showUser) ++
261
306
defs.map(rendering.renderMethod) ++
262
307
vals.map(rendering.renderVal).flatten
263
- ).foreach(str => out.println(SyntaxHighlighting (str)))
264
308
265
- state.copy(valIndex = state.valIndex - vals.filter (resAndUnit).length )
309
+ (declarations, state.valIndex - vals.count (resAndUnit))
266
310
}
267
- else state
268
-
269
- def isSyntheticCompanion (sym : Symbol ) =
270
- sym.is(Module ) && sym.is(Synthetic )
271
-
272
- def displayTypeDefs (sym : Symbol ) = sym.info.memberClasses
273
- .collect {
274
- case x if ! isSyntheticCompanion(x.symbol) && ! x.symbol.name.isReplWrapperName =>
275
- x.symbol
276
- }
277
- .foreach { sym =>
278
- out.println(SyntaxHighlighting (" // defined " + sym.showUser))
279
- }
280
-
281
311
282
312
ctx.atPhase(ctx.typerPhase.next) { implicit ctx =>
283
313
284
- // Display members of wrapped module:
314
+ // get members of wrapped module:
285
315
tree.symbol.info.memberClasses
286
316
.find(_.symbol.name == newestWrapper.moduleClassName)
287
317
.map { wrapperModule =>
288
- displayTypeDefs(wrapperModule.symbol)
289
- displayMembers(wrapperModule.symbol)
318
+ if (tree.symbol.info.exists) {
319
+ val (decls, index) = getDeclarationsAndIndex(wrapperModule.symbol.info)
320
+ (decls, Some (index), Some (wrapperModule.symbol))
321
+ } else (Seq .empty, None , None )
290
322
}
291
323
.getOrElse {
292
324
// user defined a trait/class/object, so no module needed
293
- state
325
+ ( Seq .empty, None , None )
294
326
}
295
327
}
296
328
}
297
329
298
330
/** Interpret `cmd` to action and propagate potentially new `state` */
299
- private def interpretCommand (cmd : Command )(implicit state : State ): State = cmd match {
331
+ private def interpretCommand (cmd : Command , silent : Boolean )(implicit state : State ): State = cmd match {
300
332
case UnknownCommand (cmd) => {
301
- out.println(s """ Unknown command: " $cmd", run ":help" for a list of commands """ )
333
+ if ( ! silent) out.println(s """ Unknown command: " $cmd", run ":help" for a list of commands """ )
302
334
state.withHistory(s " $cmd" )
303
335
}
304
336
305
337
case Help => {
306
- out.println(Help .text)
338
+ if ( ! silent) out.println(Help .text)
307
339
state.withHistory(Help .command)
308
340
}
309
341
@@ -313,7 +345,7 @@ class ReplDriver(settings: Array[String],
313
345
}
314
346
315
347
case Imports => {
316
- state.imports foreach { case (_, i) => println(SyntaxHighlighting (i)) }
348
+ if ( ! silent) state.imports foreach { case (_, i) => println(SyntaxHighlighting (i)) }
317
349
state.withHistory(Imports .command)
318
350
}
319
351
@@ -323,23 +355,20 @@ class ReplDriver(settings: Array[String],
323
355
if (file.exists) {
324
356
val contents = scala.io.Source .fromFile(path).mkString
325
357
ParseResult (contents)(state.run.runContext) match {
326
- case parsed : Parsed =>
327
- compile(parsed).withHistory(loadCmd)
328
- case SyntaxErrors (_, errors, _) =>
329
- displayErrors(errors).withHistory(loadCmd)
330
- case _ =>
331
- state.withHistory(loadCmd)
358
+ case parsed : Parsed => compile(parsed, silent)
359
+ case SyntaxErrors (_, errors, _) => displayErrors(errors)
360
+ case _ => state
332
361
}
333
- }
362
+ }.withHistory(loadCmd)
334
363
else {
335
364
out.println(s """ Couldn't find file " ${file.getCanonicalPath}" """ )
336
- state.withHistory(loadCmd)
337
- }
365
+ state
366
+ }.withHistory(loadCmd)
338
367
339
368
case TypeOf (expr) => {
340
369
compiler.typeOf(expr).fold(
341
370
displayErrors,
342
- res => out.println(SyntaxHighlighting (res))
371
+ res => if ( ! silent) out.println(SyntaxHighlighting (res))
343
372
)
344
373
state.withHistory(s " ${TypeOf .command} $expr" )
345
374
}
0 commit comments