@@ -9,7 +9,7 @@ package interpreter
99import Predef .{ println => _ , _ }
1010import java .io .{ BufferedReader , FileReader }
1111import session ._
12- import scala .util .Properties .{ jdkHome , javaVersion }
12+ import scala .util .Properties .{ jdkHome , javaVersion , versionString , javaVmName }
1313import scala .tools .util .{ Javap }
1414import util .{ ClassPath , Exceptional , stringFromWriter , stringFromStream }
1515import io .{ File , Directory }
@@ -20,6 +20,8 @@ import scala.tools.util._
2020import scala .language .{implicitConversions , existentials }
2121import scala .reflect .classTag
2222import scala .tools .reflect .StdRuntimeTags ._
23+ import scala .concurrent .{ ExecutionContext , Await , Future , future }
24+ import ExecutionContext .Implicits ._
2325
2426/** The Scala interactive shell. It provides a read-eval-print loop
2527 * around the Interpreter class.
@@ -36,64 +38,32 @@ import scala.tools.reflect.StdRuntimeTags._
3638class ILoop (in0 : Option [BufferedReader ], protected val out : JPrintWriter )
3739 extends AnyRef
3840 with LoopCommands
39- with ILoopInit
4041{
4142 def this (in0 : BufferedReader , out : JPrintWriter ) = this (Some (in0), out)
4243 def this () = this (None , new JPrintWriter (Console .out, true ))
4344
44- var in : InteractiveReader = _ // the input stream from which commands come
45- var settings : Settings = _
46- var intp : IMain = _
47-
4845 @ deprecated(" Use `intp` instead." , " 2.9.0" ) def interpreter = intp
4946 @ deprecated(" Use `intp` instead." , " 2.9.0" ) def interpreter_= (i : Interpreter ): Unit = intp = i
5047
51- /** Having inherited the difficult "var-ness" of the repl instance,
52- * I'm trying to work around it by moving operations into a class from
53- * which it will appear a stable prefix.
54- */
55- private def onIntp [T ](f : IMain => T ): T = f(intp)
56-
57- class IMainOps [T <: IMain ](val intp : T ) {
58- import intp ._
59- import global ._
60-
61- def printAfterTyper (msg : => String ) =
62- intp.reporter printUntruncatedMessage exitingTyper(msg)
63-
64- /** Strip NullaryMethodType artifacts. */
65- private def replInfo (sym : Symbol ) = {
66- sym.info match {
67- case NullaryMethodType (restpe) if sym.isAccessor => restpe
68- case info => info
69- }
70- }
71- def echoTypeStructure (sym : Symbol ) =
72- printAfterTyper(" " + deconstruct.show(replInfo(sym)))
48+ var in : InteractiveReader = _ // the input stream from which commands come
49+ var settings : Settings = _
50+ var intp : IMain = _
7351
74- def echoTypeSignature (sym : Symbol , verbose : Boolean ) = {
75- if (verbose) ILoop .this .echo(" // Type signature" )
76- printAfterTyper(" " + replInfo(sym))
52+ private var globalFuture : Future [Boolean ] = _
7753
78- if (verbose) {
79- ILoop .this .echo(" \n // Internal Type structure" )
80- echoTypeStructure(sym)
81- }
82- }
54+ /** Print a welcome message */
55+ def printWelcome () {
56+ echo(s """
57+ |Welcome to Scala $versionString ( $javaVmName, Java $javaVersion).
58+ |Type in expressions to have them evaluated.
59+ |Type :help for more information. """ .trim.stripMargin
60+ )
61+ replinfo(" [info] started at " + new java.util.Date )
8362 }
84- implicit def stabilizeIMain (intp : IMain ) = new IMainOps [intp.type ](intp)
8563
86- /** TODO -
87- * -n normalize
88- * -l label with case class parameter names
89- * -c complete - leave nothing out
90- */
91- private def typeCommandInternal (expr : String , verbose : Boolean ): Result = {
92- onIntp { intp =>
93- val sym = intp.symbolOfLine(expr)
94- if (sym.exists) intp.echoTypeSignature(sym, verbose)
95- else " "
96- }
64+ protected def asyncMessage (msg : String ) {
65+ if (isReplInfo || isReplPower)
66+ echoAndRefresh(msg)
9767 }
9868
9969 override def echoCommandMessage (msg : String ) {
@@ -251,7 +221,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
251221 historyCommand,
252222 cmd(" h?" , " <string>" , " search the history" , searchHistory),
253223 cmd(" imports" , " [name name ...]" , " show import history, identifying sources of names" , importsCommand),
254- cmd(" implicits" , " [-v]" , " show the implicits in scope" , implicitsCommand),
224+ cmd(" implicits" , " [-v]" , " show the implicits in scope" , intp. implicitsCommand),
255225 cmd(" javap" , " <path|class>" , " disassemble a file or class name" , javapCommand),
256226 cmd(" load" , " <path>" , " load and interpret a Scala file" , loadCommand),
257227 nullary(" paste" , " enter paste mode: all input up to ctrl-D compiled together" , pasteCommand),
@@ -294,63 +264,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
294264 }
295265 }
296266
297- private def implicitsCommand (line : String ): Result = onIntp { intp =>
298- import intp ._
299- import global ._
300-
301- def p (x : Any ) = intp.reporter.printMessage(" " + x)
302-
303- // If an argument is given, only show a source with that
304- // in its name somewhere.
305- val args = line split " \\ s+"
306- val filtered = intp.implicitSymbolsBySource filter {
307- case (source, syms) =>
308- (args contains " -v" ) || {
309- if (line == " " ) (source.fullName.toString != " scala.Predef" )
310- else (args exists (source.name.toString contains _))
311- }
312- }
313-
314- if (filtered.isEmpty)
315- return " No implicits have been imported other than those in Predef."
316-
317- filtered foreach {
318- case (source, syms) =>
319- p(" /* " + syms.size + " implicit members imported from " + source.fullName + " */" )
320-
321- // This groups the members by where the symbol is defined
322- val byOwner = syms groupBy (_.owner)
323- val sortedOwners = byOwner.toList sortBy { case (owner, _) => exitingTyper(source.info.baseClasses indexOf owner) }
324-
325- sortedOwners foreach {
326- case (owner, members) =>
327- // Within each owner, we cluster results based on the final result type
328- // if there are more than a couple, and sort each cluster based on name.
329- // This is really just trying to make the 100 or so implicits imported
330- // by default into something readable.
331- val memberGroups : List [List [Symbol ]] = {
332- val groups = members groupBy (_.tpe.finalResultType) toList
333- val (big, small) = groups partition (_._2.size > 3 )
334- val xss = (
335- (big sortBy (_._1.toString) map (_._2)) :+
336- (small flatMap (_._2))
337- )
338-
339- xss map (xs => xs sortBy (_.name.toString))
340- }
341-
342- val ownerMessage = if (owner == source) " defined in " else " inherited from "
343- p(" /* " + members.size + ownerMessage + owner.fullName + " */" )
344-
345- memberGroups foreach { group =>
346- group foreach (s => p(" " + intp.symbolDefString(s)))
347- p(" " )
348- }
349- }
350- p(" " )
351- }
352- }
353-
354267 private def findToolsJar () = {
355268 val jdkPath = Directory (jdkHome)
356269 val jar = jdkPath / " lib" / " tools.jar" toFile;
@@ -376,32 +289,12 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
376289 }
377290 }
378291
379- protected def newJavap () = new JavapClass (addToolsJarToLoader(), new IMain .ReplStrippingWriter (intp)) {
380- override def tryClass (path : String ): Array [Byte ] = {
381- val hd :: rest = path split '.' toList;
382- // If there are dots in the name, the first segment is the
383- // key to finding it.
384- if (rest.nonEmpty) {
385- intp optFlatName hd match {
386- case Some (flat) =>
387- val clazz = flat :: rest mkString NAME_JOIN_STRING
388- val bytes = super .tryClass(clazz)
389- if (bytes.nonEmpty) bytes
390- else super .tryClass(clazz + MODULE_SUFFIX_STRING )
391- case _ => super .tryClass(path)
392- }
393- }
394- else {
395- // Look for Foo first, then Foo$, but if Foo$ is given explicitly,
396- // we have to drop the $ to find object Foo, then tack it back onto
397- // the end of the flattened name.
398- def className = intp flatName path
399- def moduleName = (intp flatName path.stripSuffix(MODULE_SUFFIX_STRING )) + MODULE_SUFFIX_STRING
400-
401- val bytes = super .tryClass(className)
402- if (bytes.nonEmpty) bytes
403- else super .tryClass(moduleName)
404- }
292+ protected def newJavap () = {
293+ val intp = ILoop .this .intp
294+ import intp ._
295+
296+ new JavapClass (addToolsJarToLoader(), new IMain .ReplStrippingWriter (intp)) {
297+ override def tryClass (path : String ) = super .tryClass(translatePath(path) getOrElse path)
405298 }
406299 }
407300 private lazy val javap = substituteAndLog[Javap ](" javap" , NoJavap )(newJavap())
@@ -410,8 +303,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
410303 private def typeCommand (line0 : String ): Result = {
411304 line0.trim match {
412305 case " " => " :type [-v] <expression>"
413- case s if s startsWith " -v " => typeCommandInternal(s stripPrefix " -v " trim, true )
414- case s => typeCommandInternal(s, false )
306+ case s if s startsWith " -v " => intp. typeCommandInternal(s stripPrefix " -v " trim, true )
307+ case s => intp. typeCommandInternal(s, false )
415308 }
416309 }
417310
@@ -436,7 +329,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
436329 }
437330 }
438331
439- private def pathToPhaseWrapper = intp.pathToTerm(" $r" ) + " .phased.atCurrent"
332+ private def pathToPhaseWrapper = intp.originalPath(" $r" ) + " .phased.atCurrent"
333+
440334 private def phaseCommand (name : String ): Result = {
441335 val phased : Phased = power.phased
442336 import phased .NoPhaseName
@@ -495,33 +389,30 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
495389 true
496390 }
497391
392+ // return false if repl should exit
393+ def processLine (line : String ): Boolean = {
394+ import scala .concurrent .duration ._
395+ Await .ready(globalFuture, 60 .seconds)
396+
397+ (line ne null ) && (command(line) match {
398+ case Result (false , _) => false
399+ case Result (_, Some (line)) => addReplay(line) ; true
400+ case _ => true
401+ })
402+ }
403+
404+ private def readOneLine () = {
405+ out.flush()
406+ in readLine prompt
407+ }
408+
498409 /** The main read-eval-print loop for the repl. It calls
499410 * command() for each line of input, and stops when
500411 * command() returns false.
501412 */
502- def loop () {
503- def readOneLine () = {
504- out.flush()
505- in readLine prompt
506- }
507- // return false if repl should exit
508- def processLine (line : String ): Boolean = {
509- if (isAsync) {
510- if (! awaitInitialized()) return false
511- runThunks()
512- }
513- if (line eq null ) false // assume null means EOF
514- else command(line) match {
515- case Result (false , _) => false
516- case Result (_, Some (finalLine)) => addReplay(finalLine) ; true
517- case _ => true
518- }
519- }
520- def innerLoop () {
521- if ( try processLine(readOneLine()) catch crashRecovery )
522- innerLoop()
523- }
524- innerLoop()
413+ @ tailrec final def loop () {
414+ if ( try processLine(readOneLine()) catch crashRecovery )
415+ loop()
525416 }
526417
527418 /** interpret all lines from a specified file */
@@ -767,45 +658,40 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
767658 SimpleReader ()
768659 }
769660 }
770- def process (settings : Settings ): Boolean = savingContextLoader {
771- this .settings = settings
772- createInterpreter()
773661
774- // sets in to some kind of reader depending on environmental cues
775- in = in0 match {
776- case Some (reader) => SimpleReader (reader, out, true )
777- case None =>
778- // some post-initialization
779- chooseReader(settings) match {
780- case x : JLineReader => addThunk(x.consoleReader.postInit) ; x
781- case x => x
782- }
662+ private def loopPostInit () {
663+ in match {
664+ case x : JLineReader => x.consoleReader.postInit
665+ case _ =>
783666 }
784667 // Bind intp somewhere out of the regular namespace where
785668 // we can get at it in generated code.
786- addThunk(intp.quietBind(NamedParam [IMain ](" $intp" , intp)(tagOfIMain, classTag[IMain ])))
787- addThunk({
788- val autorun = replProps.replAutorunCode.option flatMap (f => io.File (f).safeSlurp())
789- if (autorun.isDefined) intp.quietRun(autorun.get)
790- })
791-
792- loadFiles(settings)
793- // it is broken on startup; go ahead and exit
794- if (intp.reporter.hasErrors)
795- return false
796-
797- // This is about the illusion of snappiness. We call initialize()
798- // which spins off a separate thread, then print the prompt and try
799- // our best to look ready. The interlocking lazy vals tend to
800- // inter-deadlock, so we break the cycle with a single asynchronous
801- // message to an actor.
802- if (isAsync) {
803- intp initialize initializedCallback()
804- createAsyncListener() // listens for signal to run postInitialization
669+ intp.quietBind(NamedParam [IMain ](" $intp" , intp)(tagOfIMain, classTag[IMain ]))
670+ // Auto-run code via some setting.
671+ ( replProps.replAutorunCode.option
672+ flatMap (f => io.File (f).safeSlurp())
673+ foreach (intp quietRun _)
674+ )
675+ // classloader and power mode setup
676+ intp.setContextClassLoader
677+ if (isReplPower) {
678+ replProps.power setValue true
679+ unleashAndSetPhase()
680+ asyncMessage(power.banner)
805681 }
806- else {
682+ }
683+ def process (settings : Settings ): Boolean = savingContextLoader {
684+ this .settings = settings
685+ createInterpreter()
686+ var thunks : List [() => Unit ] = Nil
687+
688+ // sets in to some kind of reader depending on environmental cues
689+ in = in0.fold(chooseReader(settings))(r => SimpleReader (r, out, true ))
690+ globalFuture = future {
807691 intp.initializeSynchronous()
808- postInitialization()
692+ loopPostInit()
693+ loadFiles(settings)
694+ ! intp.reporter.hasErrors
809695 }
810696 printWelcome()
811697
0 commit comments