summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-01-24 00:31:38 +0000
committerPaul Phillips <paulp@improving.org>2010-01-24 00:31:38 +0000
commit895c271ead5bfe42a19610871037bfb5c90503e9 (patch)
treed869fc1bb5dc87e701317cc9b14ce7ba5b91fbe7
parenta0c0f0949797a563c9583d0f82c0f390a999ec7d (diff)
downloadscala-895c271ead5bfe42a19610871037bfb5c90503e9.tar.gz
scala-895c271ead5bfe42a19610871037bfb5c90503e9.tar.bz2
scala-895c271ead5bfe42a19610871037bfb5c90503e9.zip
Some minor polishing to the previous repl compl...
Some minor polishing to the previous repl completion patch, plus a few new features and improvements.
-rw-r--r--src/compiler/scala/tools/nsc/Interpreter.scala152
-rw-r--r--src/compiler/scala/tools/nsc/InterpreterLoop.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala19
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IdentCompletion.scala10
-rw-r--r--test/files/jvm/interpreter.check2
5 files changed, 133 insertions, 52 deletions
diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala
index 01ed5087ae..94aed81b61 100644
--- a/src/compiler/scala/tools/nsc/Interpreter.scala
+++ b/src/compiler/scala/tools/nsc/Interpreter.scala
@@ -194,8 +194,10 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
/** the previous requests this interpreter has processed */
private val prevRequests = new ArrayBuffer[Request]()
- private val prevNameMap = new HashMap[Name, Request]()
+ private val usedNameMap = new HashMap[Name, Request]()
private val boundNameMap = new HashMap[Name, Request]()
+ private def allHandlers = prevRequests.toList flatMap (_.handlers)
+ private def mostRecentHandler = prevRequests.last.handlers.last
def recordRequest(req: Request) {
def tripart[T](set1: Set[T], set2: Set[T]) = {
@@ -204,17 +206,22 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
}
prevRequests += req
- req.usedNames foreach (x => prevNameMap(x) = req)
+ req.usedNames foreach (x => usedNameMap(x) = req)
req.boundNames foreach (x => boundNameMap(x) = req)
// println("\n s1 = %s\n s2 = %s\n s3 = %s".format(
- // tripart(prevNameMap.keysIterator.toSet, boundNameMap.keysIterator.toSet): _*
+ // tripart(usedNameMap.keysIterator.toSet, boundNameMap.keysIterator.toSet): _*
// ))
}
- private def mostRecentHandler = prevRequests.last.handlers.last
- def allUsedNames = prevNameMap.keysIterator.toList
- def allBoundNames = prevRequests.toList.flatMap(_.boundNames).removeDuplicates
+ private def keyList[T](x: collection.Map[T, _]): List[T] = x.keysIterator.toList sortBy (_.toString)
+ def allUsedNames = keyList(usedNameMap)
+ def allBoundNames = keyList(boundNameMap)
+ def allSeenTypes = prevRequests.toList flatMap (_.typeOf.valuesIterator.toList) removeDuplicates
+ def allValueGeneratingNames = allHandlers flatMap (_.generatesValue)
+ def allImplicits = partialFlatMap(allHandlers) {
+ case x: MemberHandler if x.definesImplicit => x.boundNames
+ }
/** Generates names pre0, pre1, etc. via calls to apply method */
class NameCreator(pre: String) {
@@ -248,7 +255,10 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
/** Check if a name looks like it was generated by varNameCreator */
private def isGeneratedVarName(name: String): Boolean = varNameCreator didGenerate name
private def isSynthVarName(name: String): Boolean = synthVarNameCreator didGenerate name
+ private def isSynthVarName(name: Name): Boolean = synthVarNameCreator didGenerate name.toString
+
def getVarName = varNameCreator()
+ def getSynthVarName = synthVarNameCreator()
/** generate a string using a routine that wants to write on a stream */
private def stringFrom(writer: PrintWriter => Unit): String = {
@@ -346,16 +356,15 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
}
}
- val rhpairs = for {
- req <- prevRequests.toList.reverse
- handler <- req.handlers
- } yield ReqAndHandler(req, handler)
-
+ /** Flatten the handlers out and pair each with the original request */
+ val rhpairs = prevRequests.reverse.toList flatMap { req =>
+ req.handlers map (ReqAndHandler(req, _))
+ }
select(rhpairs, wanted).reverse
}
val code, trailingBraces, accessPath = new StringBuffer
- val currentImps = mutable.Set.empty[Name]
+ val currentImps = HashSet[Name]()
// add code for a new object to hold some imports
def addWrapper() {
@@ -462,7 +471,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
case member => new GenericHandler(member)
}
- private def requestFromLine(line: String): Either[IR.Result, Request] = {
+ private def requestFromLine(line: String, synthetic: Boolean): Either[IR.Result, Request] = {
// initialize the compiler
if (prevRequests.isEmpty) new compiler.Run()
@@ -473,12 +482,15 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
case Some(trees) => trees
}
+ // use synthetic vars to avoid filling up the resXX slots
+ def varName = if (synthetic) getSynthVarName else getVarName
+
// Treat a single bare expression specially. This is necessary due to it being hard to
// modify code at a textual level, and it being hard to submit an AST to the compiler.
if (trees.size == 1) trees.head match {
case _:Assign => // we don't want to include assignments
case _:TermTree | _:Ident | _:Select => // ... but do want these as valdefs.
- return requestFromLine("val %s =\n%s".format(varNameCreator(), line))
+ return requestFromLine("val %s =\n%s".format(varName, line), synthetic)
case _ =>
}
@@ -500,8 +512,9 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
* @param line ...
* @return ...
*/
- def interpret(line: String): IR.Result = {
- val req = requestFromLine(line) match {
+ def interpret(line: String): IR.Result = interpret(line, false)
+ def interpret(line: String, synthetic: Boolean): IR.Result = {
+ val req = requestFromLine(line, synthetic) match {
case Left(result) => return result
case Right(req) => req
}
@@ -515,7 +528,9 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
out print clean(result)
if (succeeded) {
- recordRequest(req) // book-keeping
+ if (!synthetic)
+ recordRequest(req) // book-keeping
+
IR.Success
}
else IR.Error
@@ -594,8 +609,6 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
ivt.importVars.toList
}
def boundNames: List[Name] = Nil
- def valAndVarNames: List[Name] = Nil
- def defNames: List[Name] = Nil
val definesImplicit = cond(member) {
case tree: MemberDef => tree.mods hasFlag Flags.IMPLICIT
}
@@ -615,7 +628,6 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
lazy val isLazy = mods hasFlag Flags.LAZY
override lazy val boundNames = List(vname)
- override def valAndVarNames = boundNames
override def generatesValue = Some(vname)
override def resultExtractionCode(req: Request, code: PrintWriter) {
@@ -643,7 +655,6 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
private class DefHandler(defDef: DefDef) extends MemberHandler(defDef) {
lazy val DefDef(mods, name, _, _, _, _) = defDef
override lazy val boundNames = List(name)
- override def defNames = boundNames
override def resultExtractionCode(req: Request, code: PrintWriter) =
if (mods.isPublic) code print codegenln(name, ": ", req.typeOf(name))
@@ -652,7 +663,6 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
private class AssignHandler(member: Assign) extends MemberHandler(member) {
val lhs = member.lhs.asInstanceOf[Ident] // an unfortunate limitation
val helperName = newTermName(synthVarNameCreator())
- override val valAndVarNames = List(helperName)
override def generatesValue = Some(helperName)
override def extraCodeToEvaluate(req: Request, code: PrintWriter) =
@@ -726,7 +736,14 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
val boundNames = handlers flatMap (_.boundNames)
/** list of names used by this expression */
- val usedNames: List[Name] = handlers.flatMap(_.usedNames)
+ val usedNames: List[Name] = handlers flatMap (_.usedNames)
+
+ /** def and val names */
+ def defNames = partialFlatMap(handlers) { case x: DefHandler => x.boundNames }
+ def valAndVarNames = partialFlatMap(handlers) {
+ case x: AssignHandler => List(x.helperName)
+ case x: ValHandler => boundNames
+ }
/** Code to import bound names from previous lines - accessPath is code to
* append to objectName to access anything bound by request.
@@ -836,8 +853,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
!reporter.hasErrors
}
- def valAndVarNames = handlers flatMap { _.valAndVarNames }
- def defNames = handlers flatMap { _.defNames }
+
def atNextPhase[T](op: => T): T = compiler.atPhase(objRun.typerPhase.next)(op)
/** The outermost wrapper object */
@@ -899,13 +915,17 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
/** These methods are exposed so REPL commands can access them.
* The command infrastructure is in InterpreterLoop.
*/
- def dumpState(xs: List[String]): String = {
- List(
- "allUsedNames = " + allUsedNames,
- "allBoundNames = " + allBoundNames,
- prevRequests.toList.map(req => " \"" + req.line + "\" => " + req.objectSourceCode)
- ).mkString("", "\n", "\n")
- }
+ def dumpState(xs: List[String]): String = """
+ | Names used: %s
+ |
+ | Identifiers: %s
+ |
+ | synthvars: %d
+ """.stripMargin.format(
+ allUsedNames mkString " ",
+ unqualifiedIds mkString " ",
+ allBoundNames filter isSynthVarName size
+ )
// very simple right now, will get more interesting
def dumpTrees(xs: List[String]): String = {
@@ -919,7 +939,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
beQuietDuring {
this.bind("interpreter", "scala.tools.nsc.Interpreter", this)
this.bind("global", "scala.tools.nsc.Global", compiler)
- interpret("""import interpreter.{ mkType, mkTree, mkTrees, eval }""")
+ interpret("""import interpreter.{ mkType, mkTree, mkTrees, eval }""", true)
}
"""** Power User mode enabled - BEEP BOOP **
@@ -947,6 +967,9 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
private def requestForIdent(line: String): Option[Request] =
requestForName(newTermName(line))
+ private def typeForIdent(id: String): Option[String] =
+ requestForIdent(id) map (_ typeOf newTermName(id))
+
def methodsOf(name: String) =
evalExpr[List[String]](methodsCode(name)) map (x => NameTransformer.decode(getOriginalName(x)))
@@ -956,7 +979,32 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
def extractionValueForIdent(id: String): AnyRef =
requestForIdent(id).get.extractionValue
- def implicitFor[T: Manifest] = evalExpr[T]("""manifest[%s]""".format(manifest[T]))
+ /** Executes code looking for a manifest of type T.
+ */
+ def manifestFor[T: Manifest] =
+ evalExpr[Manifest[T]]("""manifest[%s]""".format(manifest[T]))
+
+ /** Executes code looking for an implicit value of type T.
+ */
+ def implicitFor[T: Manifest] = {
+ val s = manifest[T].toString
+ evalExpr[Option[T]]("{ def f(implicit x: %s = null): %s = x ; Option(f) }".format(s, s))
+ // We don't use implicitly so as to fail without failing.
+ // evalExpr[T]("""implicitly[%s]""".format(manifest[T]))
+ }
+ /** Executes code looking for an implicit conversion from the type
+ * of the given identifier to CompletionAware.
+ */
+ def completionAwareImplicit[T](id: String) = {
+ val f1string = "%s => %s".format(typeForIdent(id).get, classOf[CompletionAware].getName)
+ val code = """{
+ | def f(implicit x: (%s) = null): %s = x
+ | val f1 = f
+ | if (f1 == null) None else Some(f1(%s))
+ |}""".stripMargin.format(f1string, f1string, id)
+
+ evalExpr[Option[CompletionAware]](code)
+ }
def clazzForIdent(id: String): Class[_] =
extractionValueForIdent(id).getClass
@@ -984,14 +1032,14 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
if (manifest[T] eq Manifest.Nothing)
evalError("Could not infer type: try 'eval[SomeType](%s)' instead".format(line))
- val lhs = varNameCreator()
+ val lhs = getSynthVarName
beQuietDuring { interpret("val " + lhs + " = { " + line + " } ") }
// TODO - can we meaningfully compare the inferred type T with
// the internal compiler Type assigned to lhs?
// def assignedType = prevRequests.last.typeOf(newTermName(lhs))
- val req = requestFromLine(lhs) match {
+ val req = requestFromLine(lhs, true) match {
case Left(result) => evalError(result.toString)
case Right(req) => req
}
@@ -1014,7 +1062,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
/** Another entry point for tab-completion, ids in scope */
def unqualifiedIds(): List[String] =
- allBoundNames map (_.toString) filterNot isSynthVarName
+ allValueGeneratingNames map (_.toString) sortWith (_ < _) filterNot isSynthVarName
/** For static/object method completion */
def getClassObject(path: String): Option[Class[_]] = classLoader tryToLoadClass path
@@ -1022,14 +1070,15 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
/** Parse the ScalaSig to find type aliases */
def aliasForType(path: String) = ByteCode.aliasForType(path)
- /** Artificial object */
- class ReplVars extends CompletionAware {
- def completions() = unqualifiedIds()
- override def follow(s: String) =
- if (completions contains s) completionAware(s)
- else None
- }
- def replVarsObject() = new ReplVars()
+ /** Artificial object demonstrating completion */
+ def replVarsObject() = CompletionAware(
+ Map[String, CompletionAware](
+ "ids" -> CompletionAware(() => unqualifiedIds, completionAware _),
+ "synthVars" -> CompletionAware(() => allBoundNames filter isSynthVarName map (_.toString)),
+ "types" -> CompletionAware(() => allSeenTypes map (_.toString)),
+ "implicits" -> CompletionAware(() => allImplicits map (_.toString))
+ )
+ )
// Coming soon
// implicit def string2liftedcode(s: String): LiftedCode = new LiftedCode(s)
@@ -1049,6 +1098,19 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
/** Utility methods for the Interpreter. */
object Interpreter {
+ import scala.collection.generic.CanBuildFrom
+ def partialFlatMap[A, B, CC[X] <: Traversable[X]]
+ (coll: CC[A])
+ (pf: PartialFunction[A, CC[B]])
+ (implicit bf: CanBuildFrom[CC[A], B, CC[B]]) =
+ {
+ val b = bf(coll)
+ for (x <- coll partialMap pf)
+ b ++= x
+
+ b.result
+ }
+
object DebugParam {
implicit def tuple2debugparam[T](x: (String, T))(implicit m: Manifest[T]): DebugParam[T] =
DebugParam(x._1, x._2)
diff --git a/src/compiler/scala/tools/nsc/InterpreterLoop.scala b/src/compiler/scala/tools/nsc/InterpreterLoop.scala
index 2d8a5b78ee..02d0393590 100644
--- a/src/compiler/scala/tools/nsc/InterpreterLoop.scala
+++ b/src/compiler/scala/tools/nsc/InterpreterLoop.scala
@@ -331,7 +331,7 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) {
if (in.history.isDefined)
interpreter.quietBind("history", "scala.collection.immutable.List[String]", in.historyList)
- interpreter.quietBind("repl", "scala.tools.nsc.Interpreter#ReplVars", interpreter.replVarsObject())
+ interpreter.quietBind("repl", "scala.tools.nsc.interpreter.CompletionAware", interpreter.replVarsObject())
}
def verbosity() = {
diff --git a/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala b/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala
index c60f402d3d..df110333dc 100644
--- a/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala
@@ -60,6 +60,8 @@ trait CompletionAware {
) filterNot filterNotFunction map mapFunction sortWith (sortFunction _)
}
+ /** TODO - unify this and completionsFor under a common traverser.
+ */
def executionFor(buf: String): Option[Any] = {
val parsed = new Parsed(buf)
import parsed._
@@ -79,6 +81,21 @@ object CompletionAware {
case _ => None
}
- def apply(xs: List[String]) = new CompletionAware { val completions = xs }
+ /** Create a CompletionAware object from the given functions.
+ * The first should generate the list of completions whenever queried,
+ * and the second should return Some(CompletionAware) object if
+ * subcompletions are possible.
+ */
+ def apply(terms: () => List[String], followFunction: String => Option[CompletionAware]): CompletionAware =
+ new CompletionAware {
+ def completions = terms()
+ override def follow(id: String) = followFunction(id)
+ }
+
+ /** Convenience factories.
+ */
+ def apply(terms: () => List[String]): CompletionAware = apply(terms, _ => None)
+ def apply(map: collection.Map[String, CompletionAware]): CompletionAware =
+ apply(() => map.keysIterator.toList, map.get _)
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/IdentCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/IdentCompletion.scala
index 5759abdb97..47a0cb80a0 100644
--- a/src/compiler/scala/tools/nsc/interpreter/IdentCompletion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/IdentCompletion.scala
@@ -12,8 +12,10 @@ package interpreter
class IdentCompletion(repl: Interpreter) extends CompletionAware {
def completions() = repl.unqualifiedIds
override def follow(id: String) =
- if (completions contains id)
- repl completionAware id orElse Some(new InstanceCompletion(repl clazzForIdent id))
- else
- None
+ if (completions contains id) {
+ (repl completionAware id) orElse
+ (repl completionAwareImplicit id) orElse
+ Some(new InstanceCompletion(repl clazzForIdent id))
+ }
+ else None
}
diff --git a/test/files/jvm/interpreter.check b/test/files/jvm/interpreter.check
index 6c89318470..f9f0d07c04 100644
--- a/test/files/jvm/interpreter.check
+++ b/test/files/jvm/interpreter.check
@@ -229,7 +229,7 @@ scala>
scala>
plusOne: (x: Int)Int
res0: Int = 6
-res0: java.lang.String = after reset
+res1: java.lang.String = after reset
<console>:5: error: not found: value plusOne
plusOne(5) // should be undefined now
^