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,51 @@ 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
+ state
234
+ },
213
235
{
214
236
case (unit : CompilationUnit , newState : State ) => {
215
237
val newestWrapper = extractNewestWrapper(unit.untpdTree)
216
238
val newImports = newState.imports ++ extractImports(parsed.trees)(newState.run.runContext)
217
239
val newStateWithImports = newState.copy(imports = newImports)
218
240
219
- displayDefinitions(unit.tpdTree, newestWrapper)(newStateWithImports)
241
+ val (decls, optIndex, symbol) = getDefinitions(unit.tpdTree, newestWrapper)(newStateWithImports)
242
+ if (! silent) displayDefinitions(decls, symbol)(newStateWithImports)
243
+
244
+ optIndex.map(i => newStateWithImports.copy(valIndex = i)).getOrElse(newStateWithImports)
220
245
}
221
246
}
222
247
)
223
248
}
224
249
225
- /** Display definitions from `tree` */
226
- private def displayDefinitions (tree : tpd.Tree , newestWrapper : Name )(implicit state : State ): State = {
250
+ /** Display definitions from `symbol` */
251
+ private def displayDefinitions (decls : Seq [String ], symbol : Option [Symbol ])(implicit state : State ): Unit = {
252
+ implicit val ctx = state.run.runContext
253
+
254
+ def displayMembers (decls : Seq [String ]): Unit = {
255
+ decls.foreach(str => out.println(SyntaxHighlighting (str)))
256
+ }
257
+
258
+ def isSyntheticCompanion (sym : Symbol ) =
259
+ sym.is(Module | Synthetic )
260
+
261
+ def displayTypeDefs (optSymbol : Option [Symbol ]): Unit = optSymbol.foreach(sym => sym.info.memberClasses
262
+ .collect {
263
+ case x if ! isSyntheticCompanion(x.symbol) && ! x.symbol.name.isReplWrapperName =>
264
+ x.symbol
265
+ }
266
+ .foreach { sym =>
267
+ out.println(SyntaxHighlighting (" // defined " + sym.showUser))
268
+ })
269
+ displayTypeDefs(symbol)
270
+ displayMembers(decls)
271
+ }
272
+
273
+ /** get definitions from `tree` */
274
+ private def getDefinitions (tree : tpd.Tree , newestWrapper : Name )(implicit state : State ): (Seq [String ], Option [Int ], Option [Symbol ]) = {
227
275
implicit val ctx = state.run.runContext
228
276
229
277
def resAndUnit (denot : Denotation ) = {
@@ -237,8 +285,7 @@ class ReplDriver(settings: Array[String],
237
285
name.startsWith(str.REPL_RES_PREFIX ) && hasValidNumber && sym.info == defn.UnitType
238
286
}
239
287
240
- def displayMembers (symbol : Symbol ) = if (tree.symbol.info.exists) {
241
- val info = symbol.info
288
+ def getDeclarationsAndIndex (info : Type ): (Seq [String ], Int ) = {
242
289
val defs =
243
290
info.bounds.hi.finalResultType
244
291
.membersBasedOnFlags(Method , Accessor | ParamAccessor | Synthetic | Private )
@@ -256,54 +303,40 @@ class ReplDriver(settings: Array[String],
256
303
val typeAliases =
257
304
info.bounds.hi.typeMembers.filter(_.symbol.info.isInstanceOf [TypeAlias ])
258
305
259
- (
260
- typeAliases.map(" // defined alias " + _.symbol.showUser) ++
306
+ val declarations = typeAliases.map(" // defined alias " + _.symbol.showUser) ++
261
307
defs.map(rendering.renderMethod) ++
262
308
vals.map(rendering.renderVal).flatten
263
- ).foreach(str => out.println(SyntaxHighlighting (str)))
264
309
265
- state.copy(valIndex = state.valIndex - vals.filter (resAndUnit).length )
310
+ (declarations, state.valIndex - vals.count (resAndUnit))
266
311
}
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
312
282
313
ctx.atPhase(ctx.typerPhase.next) { implicit ctx =>
283
314
284
- // Display members of wrapped module:
315
+ // get members of wrapped module:
285
316
tree.symbol.info.memberClasses
286
317
.find(_.symbol.name == newestWrapper.moduleClassName)
287
318
.map { wrapperModule =>
288
- displayTypeDefs(wrapperModule.symbol)
289
- displayMembers(wrapperModule.symbol)
319
+ if (tree.symbol.info.exists) {
320
+ val (decls, index) = getDeclarationsAndIndex(wrapperModule.symbol.info)
321
+ (decls, Some (index), Some (wrapperModule.symbol))
322
+ } else (Seq .empty, None , None )
290
323
}
291
324
.getOrElse {
292
325
// user defined a trait/class/object, so no module needed
293
- state
326
+ ( Seq .empty, None , None )
294
327
}
295
328
}
296
329
}
297
330
298
331
/** Interpret `cmd` to action and propagate potentially new `state` */
299
- private def interpretCommand (cmd : Command )(implicit state : State ): State = cmd match {
332
+ private def interpretCommand (cmd : Command , silent : Boolean )(implicit state : State ): State = cmd match {
300
333
case UnknownCommand (cmd) => {
301
- out.println(s """ Unknown command: " $cmd", run ":help" for a list of commands """ )
334
+ if ( ! silent) out.println(s """ Unknown command: " $cmd", run ":help" for a list of commands """ )
302
335
state.withHistory(s " $cmd" )
303
336
}
304
337
305
338
case Help => {
306
- out.println(Help .text)
339
+ if ( ! silent) out.println(Help .text)
307
340
state.withHistory(Help .command)
308
341
}
309
342
@@ -313,7 +346,7 @@ class ReplDriver(settings: Array[String],
313
346
}
314
347
315
348
case Imports => {
316
- state.imports foreach { case (_, i) => println(SyntaxHighlighting (i)) }
349
+ if ( ! silent) state.imports foreach { case (_, i) => println(SyntaxHighlighting (i)) }
317
350
state.withHistory(Imports .command)
318
351
}
319
352
@@ -324,22 +357,22 @@ class ReplDriver(settings: Array[String],
324
357
val contents = scala.io.Source .fromFile(path).mkString
325
358
ParseResult (contents)(state.run.runContext) match {
326
359
case parsed : Parsed =>
327
- compile(parsed).withHistory(loadCmd )
360
+ compile(parsed, silent )
328
361
case SyntaxErrors (_, errors, _) =>
329
- displayErrors(errors).withHistory(loadCmd)
330
- case _ =>
331
- state.withHistory(loadCmd)
362
+ displayErrors(errors)
363
+ state
364
+ case _ => state
332
365
}
333
- }
366
+ }.withHistory(loadCmd)
334
367
else {
335
368
out.println(s """ Couldn't find file " ${file.getCanonicalPath}" """ )
336
- state.withHistory(loadCmd)
337
- }
369
+ state
370
+ }.withHistory(loadCmd)
338
371
339
372
case TypeOf (expr) => {
340
373
compiler.typeOf(expr).fold(
341
374
displayErrors,
342
- res => out.println(SyntaxHighlighting (res))
375
+ res => if ( ! silent) out.println(SyntaxHighlighting (res))
343
376
)
344
377
state.withHistory(s " ${TypeOf .command} $expr" )
345
378
}
0 commit comments