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,24 +124,30 @@ 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 ) {
136
- out.println()
137
- state
138
- }
139
- else {
140
- // readLine potentially destroys the run, so a new one is needed for the
141
- // rest of the interpretation:
142
- implicit val freshState = state.newRun(compiler, rootCtx)
143
- 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 ) {
137
+ out.println()
138
+ state
139
+ }
140
+ else {
141
+ // readLine potentially destroys the run, so a new one is needed for the
142
+ // rest of the interpretation:
143
+ implicit val freshState = state.newRun(compiler, rootCtx)
144
+ run(interpret(res))
145
+ }
144
146
}
147
+
148
+ val state = runBootstrapCommands(initialCommands)(initialState)
149
+ val userState = run(state)
150
+ runBootstrapCommands(cleanupCommands)(userState)
145
151
}
146
152
147
153
final def run (input : String )(implicit state : State ): State =
@@ -150,6 +156,12 @@ class ReplDriver(settings: Array[String],
150
156
final def run (res : ParseResult )(implicit state : State ): State =
151
157
interpret(res)
152
158
159
+ final def runBootstrapCommands (cmds : Array [String ])(implicit state : State ): State = {
160
+ cmds.map(ParseResult .apply(_)(rootCtx)).map(Silent .apply(_)).foldLeft(state) { (s, cmd) =>
161
+ interpret(cmd)(s)
162
+ }
163
+ }
164
+
153
165
/** Extract possible completions at the index of `cursor` in `expr` */
154
166
protected [this ] final def completions (cursor : Int , expr : String , state0 : State ): Completions = {
155
167
// TODO move some of this logic to `Interactive`
@@ -184,28 +196,34 @@ class ReplDriver(settings: Array[String],
184
196
private def extractImports (trees : List [untpd.Tree ])(implicit context : Context ): List [(untpd.Import , String )] =
185
197
trees.collect { case imp : untpd.Import => (imp, imp.show) }
186
198
187
- private def interpret (res : ParseResult )(implicit state : State ): State =
188
- res match {
199
+ private def interpret (res : Parsing )(implicit state : State ): State = {
200
+ val (parseResult, isSilent) = res match {
201
+ case Silent (x) => (x, true )
202
+ case x : ParseResult => (x, false )
203
+ }
204
+
205
+ parseResult match {
189
206
case parsed : Parsed if parsed.trees.nonEmpty =>
190
- compile(parsed)
207
+ compile(parsed, isSilent )
191
208
.withHistory(parsed.sourceCode :: state.history)
192
209
.newRun(compiler, rootCtx)
193
210
194
211
case SyntaxErrors (src, errs, _) =>
195
212
displayErrors(errs)
196
213
state.withHistory(src :: state.history)
197
214
198
- case cmd : Command => interpretCommand(cmd)
215
+ case cmd : Command => interpretCommand(cmd, isSilent )
199
216
200
217
case SigKill => // TODO
201
218
state
202
219
203
220
case _ => // new line, empty tree
204
221
state
205
222
}
223
+ }
206
224
207
225
/** Compile `parsed` trees and evolve `state` in accordance */
208
- protected [this ] final def compile (parsed : Parsed )(implicit state : State ): State = {
226
+ protected [this ] final def compile (parsed : Parsed , silent : Boolean = false )(implicit state : State ): State = {
209
227
import dotc .ast .Trees .PackageDef
210
228
import untpd .{ PackageDef => _ , _ }
211
229
def extractNewestWrapper (tree : Tree ): Name = tree match {
@@ -216,21 +234,50 @@ class ReplDriver(settings: Array[String],
216
234
compiler
217
235
.compile(parsed)
218
236
.fold(
219
- displayErrors,
237
+ {
238
+ case (errors : Errors ) =>
239
+ displayErrors(errors)
240
+ },
220
241
{
221
242
case (unit : CompilationUnit , newState : State ) => {
222
243
val newestWrapper = extractNewestWrapper(unit.untpdTree)
223
244
val newImports = newState.imports ++ extractImports(parsed.trees)(newState.run.runContext)
224
245
val newStateWithImports = newState.copy(imports = newImports)
225
246
226
- displayDefinitions(unit.tpdTree, newestWrapper)(newStateWithImports)
247
+ val (decls, optIndex, symbol) = getDefinitions(unit.tpdTree, newestWrapper)(newStateWithImports)
248
+ if (! silent) displayDefinitions(decls, symbol)(newStateWithImports)
249
+
250
+ optIndex.map(i => newStateWithImports.copy(valIndex = i)).getOrElse(newStateWithImports)
227
251
}
228
252
}
229
253
)
230
254
}
231
255
232
- /** Display definitions from `tree` */
233
- private def displayDefinitions (tree : tpd.Tree , newestWrapper : Name )(implicit state : State ): State = {
256
+ /** Display definitions from `symbol` */
257
+ private def displayDefinitions (decls : Seq [String ], symbol : Option [Symbol ])(implicit state : State ): Unit = {
258
+ implicit val ctx = state.run.runContext
259
+
260
+ def displayMembers (decls : Seq [String ]): Unit = {
261
+ decls.foreach(str => out.println(SyntaxHighlighting (str)))
262
+ }
263
+
264
+ def isSyntheticCompanion (sym : Symbol ) =
265
+ sym.is(Module ) && sym.is(Synthetic )
266
+
267
+ def displayTypeDefs (optSymbol : Option [Symbol ]): Unit = optSymbol.foreach(sym => sym.info.memberClasses
268
+ .collect {
269
+ case x if ! isSyntheticCompanion(x.symbol) && ! x.symbol.name.isReplWrapperName =>
270
+ x.symbol
271
+ }
272
+ .foreach { sym =>
273
+ out.println(SyntaxHighlighting (" // defined " + sym.showUser))
274
+ })
275
+ displayTypeDefs(symbol)
276
+ displayMembers(decls)
277
+ }
278
+
279
+ /** get definitions from `tree` */
280
+ private def getDefinitions (tree : tpd.Tree , newestWrapper : Name )(implicit state : State ): (Seq [String ], Option [Int ], Option [Symbol ]) = {
234
281
implicit val ctx = state.run.runContext
235
282
236
283
def resAndUnit (denot : Denotation ) = {
@@ -244,8 +291,7 @@ class ReplDriver(settings: Array[String],
244
291
name.startsWith(str.REPL_RES_PREFIX ) && hasValidNumber && sym.info == defn.UnitType
245
292
}
246
293
247
- def displayMembers (symbol : Symbol ) = if (tree.symbol.info.exists) {
248
- val info = symbol.info
294
+ def getDeclarationsAndIndex (info : Type ): (Seq [String ], Int ) = {
249
295
val defs =
250
296
info.bounds.hi.finalResultType
251
297
.membersBasedOnFlags(Method , Accessor | ParamAccessor | Synthetic | Private )
@@ -263,54 +309,40 @@ class ReplDriver(settings: Array[String],
263
309
val typeAliases =
264
310
info.bounds.hi.typeMembers.filter(_.symbol.info.isInstanceOf [TypeAlias ])
265
311
266
- (
267
- typeAliases.map(" // defined alias " + _.symbol.showUser) ++
312
+ val declarations = typeAliases.map(" // defined alias " + _.symbol.showUser) ++
268
313
defs.map(rendering.renderMethod) ++
269
314
vals.map(rendering.renderVal).flatten
270
- ).foreach(str => out.println(SyntaxHighlighting (str)))
271
315
272
- state.copy(valIndex = state.valIndex - vals.filter (resAndUnit).length )
316
+ (declarations, state.valIndex - vals.count (resAndUnit))
273
317
}
274
- else state
275
-
276
- def isSyntheticCompanion (sym : Symbol ) =
277
- sym.is(Module ) && sym.is(Synthetic )
278
-
279
- def displayTypeDefs (sym : Symbol ) = sym.info.memberClasses
280
- .collect {
281
- case x if ! isSyntheticCompanion(x.symbol) && ! x.symbol.name.isReplWrapperName =>
282
- x.symbol
283
- }
284
- .foreach { sym =>
285
- out.println(SyntaxHighlighting (" // defined " + sym.showUser))
286
- }
287
-
288
318
289
319
ctx.atPhase(ctx.typerPhase.next) { implicit ctx =>
290
320
291
- // Display members of wrapped module:
321
+ // get members of wrapped module:
292
322
tree.symbol.info.memberClasses
293
323
.find(_.symbol.name == newestWrapper.moduleClassName)
294
324
.map { wrapperModule =>
295
- displayTypeDefs(wrapperModule.symbol)
296
- displayMembers(wrapperModule.symbol)
325
+ if (tree.symbol.info.exists) {
326
+ val (decls, index) = getDeclarationsAndIndex(wrapperModule.symbol.info)
327
+ (decls, Some (index), Some (wrapperModule.symbol))
328
+ } else (Seq .empty, None , None )
297
329
}
298
330
.getOrElse {
299
331
// user defined a trait/class/object, so no module needed
300
- state
332
+ ( Seq .empty, None , None )
301
333
}
302
334
}
303
335
}
304
336
305
337
/** Interpret `cmd` to action and propagate potentially new `state` */
306
- private def interpretCommand (cmd : Command )(implicit state : State ): State = cmd match {
338
+ private def interpretCommand (cmd : Command , silent : Boolean )(implicit state : State ): State = cmd match {
307
339
case UnknownCommand (cmd) => {
308
- out.println(s """ Unknown command: " $cmd", run ":help" for a list of commands """ )
340
+ if ( ! silent) out.println(s """ Unknown command: " $cmd", run ":help" for a list of commands """ )
309
341
state.withHistory(s " $cmd" )
310
342
}
311
343
312
344
case Help => {
313
- out.println(Help .text)
345
+ if ( ! silent) out.println(Help .text)
314
346
state.withHistory(Help .command)
315
347
}
316
348
@@ -320,7 +352,7 @@ class ReplDriver(settings: Array[String],
320
352
}
321
353
322
354
case Imports => {
323
- state.imports foreach { case (_, i) => println(SyntaxHighlighting (i)) }
355
+ if ( ! silent) state.imports foreach { case (_, i) => println(SyntaxHighlighting (i)) }
324
356
state.withHistory(Imports .command)
325
357
}
326
358
@@ -330,23 +362,20 @@ class ReplDriver(settings: Array[String],
330
362
if (file.exists) {
331
363
val contents = scala.io.Source .fromFile(path).mkString
332
364
ParseResult (contents)(state.run.runContext) match {
333
- case parsed : Parsed =>
334
- compile(parsed).withHistory(loadCmd)
335
- case SyntaxErrors (_, errors, _) =>
336
- displayErrors(errors).withHistory(loadCmd)
337
- case _ =>
338
- state.withHistory(loadCmd)
365
+ case parsed : Parsed => compile(parsed, silent)
366
+ case SyntaxErrors (_, errors, _) => displayErrors(errors)
367
+ case _ => state
339
368
}
340
- }
369
+ }.withHistory(loadCmd)
341
370
else {
342
371
out.println(s """ Couldn't find file " ${file.getCanonicalPath}" """ )
343
- state.withHistory(loadCmd)
344
- }
372
+ state
373
+ }.withHistory(loadCmd)
345
374
346
375
case TypeOf (expr) => {
347
376
compiler.typeOf(expr).fold(
348
377
displayErrors,
349
- res => out.println(SyntaxHighlighting (res))
378
+ res => if ( ! silent) out.println(SyntaxHighlighting (res))
350
379
)
351
380
state.withHistory(s " ${TypeOf .command} $expr" )
352
381
}
0 commit comments