summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-01-26 22:14:15 +0000
committerPaul Phillips <paulp@improving.org>2010-01-26 22:14:15 +0000
commitf6183b63f28a199b7888949963a8d6984d54c3b1 (patch)
tree8eb557773e913669e7ebac6a720392602fe72481 /src/compiler
parent8856f21f59ff735db7951901a799300a5affd9cc (diff)
downloadscala-f6183b63f28a199b7888949963a8d6984d54c3b1.tar.gz
scala-f6183b63f28a199b7888949963a8d6984d54c3b1.tar.bz2
scala-f6183b63f28a199b7888949963a8d6984d54c3b1.zip
Refinements to the recent repl patches.
few more things, like literals (1.<tab>, "abc".<tab>). A completion aware case class walker which leverages the names of the case fields for completion. For instance: :power val x = new ProductCompletion(mkTree("def f(x: Int, y: Int) = f(5, 10) + f(10, 20)") x.<tab> mods name rhs tparams tpt vparamss x.rhs.fun.<tab> name qualifier scala> x.rhs.fun.qualifier res3: scala.tools.nsc.ast.Trees$Apply = f(5, 10)
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/Interpreter.scala39
-rw-r--r--src/compiler/scala/tools/nsc/InterpreterLoop.scala27
-rw-r--r--src/compiler/scala/tools/nsc/InterpreterSettings.scala6
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ByteCode.scala33
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Completion.scala168
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala6
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IdentCompletion.scala12
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/JLineReader.scala18
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/LiteralCompletion.scala50
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/PackageCompletion.scala40
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Parsed.scala1
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ProductCompletion.scala42
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala147
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/package.scala19
14 files changed, 417 insertions, 191 deletions
diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala
index 94aed81b61..21b12e3b84 100644
--- a/src/compiler/scala/tools/nsc/Interpreter.scala
+++ b/src/compiler/scala/tools/nsc/Interpreter.scala
@@ -77,6 +77,13 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
/** the compiler to compile expressions with */
val compiler: Global = newCompiler(settings, reporter)
+ /** have to be careful completion doesn't start querying global before it's ready */
+ private var _initialized = false
+ def isInitialized = _initialized
+ private[nsc] def setInitialized = {
+ _initialized = true
+ }
+
import compiler.{ Traverser, CompilationUnit, Symbol, Name, Type }
import compiler.definitions
import compiler.{
@@ -188,6 +195,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
c.getMethod(name, classOf[Object])
protected def parentClassLoader: ClassLoader = this.getClass.getClassLoader()
+ def getInterpreterClassLoader() = classLoader
// Set the current Java "context" class loader to this interpreter's class loader
def setContextClassLoader() = classLoader.setAsContext()
@@ -939,7 +947,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 }""", true)
+ interpret("""import interpreter.{ mkType, mkTree, mkTrees, eval }""", false)
}
"""** Power User mode enabled - BEEP BOOP **
@@ -967,17 +975,20 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
private def requestForIdent(line: String): Option[Request] =
requestForName(newTermName(line))
- private def typeForIdent(id: String): Option[String] =
+ 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)))
- def completionAware(name: String) =
- evalExpr[Option[CompletionAware]](asCompletionAwareCode(name))
+ def completionAware(name: String) = {
+ // XXX working around "object is not a value" crash, i.e.
+ // import java.util.ArrayList ; ArrayList.<tab>
+ clazzForIdent(name) flatMap (_ => evalExpr[Option[CompletionAware]](asCompletionAwareCode(name)))
+ }
- def extractionValueForIdent(id: String): AnyRef =
- requestForIdent(id).get.extractionValue
+ def extractionValueForIdent(id: String): Option[AnyRef] =
+ requestForIdent(id) map (_.extractionValue)
/** Executes code looking for a manifest of type T.
*/
@@ -1006,8 +1017,8 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
evalExpr[Option[CompletionAware]](code)
}
- def clazzForIdent(id: String): Class[_] =
- extractionValueForIdent(id).getClass
+ def clazzForIdent(id: String): Option[Class[_]] =
+ extractionValueForIdent(id) map (_.getClass)
private def methodsCode(name: String) =
"%s.%s(%s)".format(classOf[ReflectionCompletion].getName, "methodsOf", name)
@@ -1061,8 +1072,16 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
}
/** Another entry point for tab-completion, ids in scope */
- def unqualifiedIds(): List[String] =
- allValueGeneratingNames map (_.toString) sortWith (_ < _) filterNot isSynthVarName
+ private def unqualifiedIdNames() = partialFlatMap(allHandlers) {
+ case x: AssignHandler => List(x.helperName)
+ case x: ValHandler => List(x.vname)
+ case x: ModuleHandler => List(x.name)
+ case x: DefHandler => List(x.name)
+ case x: ImportHandler => x.importedNames
+ } filterNot isSynthVarName
+
+ /** Another entry point for tab-completion, ids in scope */
+ def unqualifiedIds() = (unqualifiedIdNames() map (_.toString)).removeDuplicates sortWith (_ < _)
/** For static/object method completion */
def getClassObject(path: String): Option[Class[_]] = classLoader tryToLoadClass path
diff --git a/src/compiler/scala/tools/nsc/InterpreterLoop.scala b/src/compiler/scala/tools/nsc/InterpreterLoop.scala
index 02d0393590..9290986a74 100644
--- a/src/compiler/scala/tools/nsc/InterpreterLoop.scala
+++ b/src/compiler/scala/tools/nsc/InterpreterLoop.scala
@@ -218,6 +218,19 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) {
/** Available commands */
def commands: List[Command] = standardCommands ::: (if (powerUserOn) powerCommands else Nil)
+ /* For some reason, the first interpreted command always takes
+ * a second or two. So, wait until the welcome message
+ * has been printed before calling isettings. That way,
+ * the user can read the welcome message while this
+ * command executes.
+ */
+ private def initInterpreter() {
+ // forces something to be compiled
+ interpreter.isettings
+ // signals completion it's okay to proceed
+ interpreter.setInitialized
+ }
+
/** The main read-eval-print loop for the interpreter. It calls
* <code>command()</code> for each line of input, and stops when
* <code>command()</code> returns <code>false</code>.
@@ -238,12 +251,12 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) {
/* For some reason, the first interpreted command always takes
* a second or two. So, wait until the welcome message
- * has been printed before calling isettings. That way,
+ * has been printed before calling initInterpreter. That way,
* the user can read the welcome message while this
* command executes.
*/
val futLine = scala.concurrent.ops.future(readOneLine)
- interpreter.isettings // evaluates lazy val
+ initInterpreter()
if (!processLine(futLine()))
return
@@ -445,7 +458,15 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) {
} finally closeInterpreter()
}
- private def objName(x: Any) = x.asInstanceOf[AnyRef].getClass.getName
+ private def objClass(x: Any) = x.asInstanceOf[AnyRef].getClass
+ private def objName(x: Any) = {
+ val clazz = objClass(x)
+ val typeParams = clazz.getTypeParameters
+ val basename = clazz.getName
+ val tpString = if (typeParams.isEmpty) "" else "[%s]".format(typeParams map (_ => "_") mkString ", ")
+
+ basename + tpString
+ }
// injects one value into the repl; returns pair of name and class
def injectOne(name: String, obj: Any): Tuple2[String, String] = {
diff --git a/src/compiler/scala/tools/nsc/InterpreterSettings.scala b/src/compiler/scala/tools/nsc/InterpreterSettings.scala
index 82609cc2e5..a5e1c1e729 100644
--- a/src/compiler/scala/tools/nsc/InterpreterSettings.scala
+++ b/src/compiler/scala/tools/nsc/InterpreterSettings.scala
@@ -22,6 +22,11 @@ class InterpreterSettings(repl: Interpreter) {
*/
var maxPrintString = 800
+ /** The maximum number of completion candidates to print for tab
+ * completion without requiring confirmation.
+ */
+ var maxAutoprintCompletion = 250
+
/** String unwrapping can be disabled if it is causing issues.
* Settings this to false means you will see Strings like "$iw.$iw.".
*/
@@ -37,6 +42,7 @@ class InterpreterSettings(repl: Interpreter) {
def allSettings = Map(
"maxPrintString" -> maxPrintString,
+ "maxAutoprintCompletion" -> maxAutoprintCompletion,
"unwrapStrings" -> unwrapStrings,
"deprecation" -> deprecation
)
diff --git a/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala b/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala
index d66cbb7818..dc4e173fff 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala
@@ -7,6 +7,7 @@ package scala.tools.nsc
package interpreter
import java.io.File
+import java.lang.reflect
import java.util.jar.{ JarEntry, JarFile }
import java.util.concurrent.ConcurrentHashMap
import util.ScalaClassLoader.getSystemLoader
@@ -15,17 +16,33 @@ object ByteCode {
/** Until I figure out why I can't get scalap onto the classpath such
* that the compiler will bootstrap, we have to use reflection.
*/
- private lazy val DECODE: Option[String => Option[Map[String, String]]] =
- for (clazz <- getSystemLoader.tryToLoadClass[AnyRef]("scala.tools.scalap.Decode$")) yield {
- val module = clazz.getField("MODULE$").get()
- val method = clazz.getMethod("typeAliases", classOf[String])
- val map = method.invoke(module, _: String).asInstanceOf[Option[Map[String, String]]]
- map
+ private lazy val DECODER: Option[AnyRef] =
+ for (clazz <- getSystemLoader.tryToLoadClass[AnyRef]("scala.tools.scalap.Decode$")) yield
+ clazz.getField("MODULE$").get()
+
+ private def decoderMethod(name: String, args: Class[_]*): Option[reflect.Method] = {
+ for (decoder <- DECODER ; m <- Option(decoder.getClass.getMethod(name, args: _*))) yield m
+ }
+
+ private lazy val aliasMap = {
+ for (module <- DECODER ; method <- decoderMethod("typeAliases", classOf[String])) yield
+ method.invoke(module, _: String).asInstanceOf[Option[Map[String, String]]]
+ }
+
+ /** Attempts to retrieve case parameter names for given class name.
+ */
+ def caseParamNamesForPath(path: String) =
+ for {
+ module <- DECODER
+ method <- decoderMethod("caseParamNames", classOf[String])
+ names <- method.invoke(module, path).asInstanceOf[Option[List[String]]]
}
+ yield names
- def aliasesForPackage(pkg: String) = DECODE flatMap (_(pkg))
+ def aliasesForPackage(pkg: String) = aliasMap flatMap (_(pkg))
- /** Use scalap to look through type aliases */
+ /** Attempts to find type aliases in package objects.
+ */
def aliasForType(path: String): Option[String] = {
val (pkg, name) = (path lastIndexOf '.') match {
case -1 => return None
diff --git a/src/compiler/scala/tools/nsc/interpreter/Completion.scala b/src/compiler/scala/tools/nsc/interpreter/Completion.scala
index dc544ed657..f9e6b33763 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Completion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Completion.scala
@@ -21,10 +21,22 @@ package interpreter
import jline._
import java.net.URL
import java.util.{ List => JList }
+import java.lang.reflect
+
+trait ForwardingCompletion extends CompletionAware {
+ def forwardTo: Option[CompletionAware]
+
+ override def completions() = forwardTo map (_.completions()) getOrElse Nil
+ override def follow(s: String) = forwardTo flatMap (_ follow s)
+}
// REPL completor - queries supplied interpreter for valid
// completions based on current contents of buffer.
-class Completion(repl: Interpreter) extends Completor {
+class Completion(repl: Interpreter) {
+ self =>
+
+ import repl.isInitialized
+
private def asURLs(xs: List[String]) = xs map (x => io.File(x).toURL)
private def classPath = (
// compiler jars, scala-library.jar etc.
@@ -37,75 +49,119 @@ class Completion(repl: Interpreter) extends Completor {
val ids = new IdentCompletion(repl)
// the top level packages we know about
val pkgs = new PackageCompletion(classPath)
-
- // TODO - restore top level availability of scala.* java.lang.* scala.Predef
- // Old code:
- //
- // def membersOfPredef() = membersOfId("scala.Predef")
- //
- // def javaLangToHide(s: String) = (
- // (s endsWith "Exception") ||
- // (s endsWith "Error") ||
- // (s endsWith "Impl") ||
- // (s startsWith "CharacterData") ||
- // !existsAndPublic("java.lang." + s)
- // )
- //
- // def scalaToHide(s: String) =
- // (List("Tuple", "Product", "Function") exists (x => (x + """\d+""").r findPrefixMatchOf s isDefined)) ||
- // (List("Exception", "Error") exists (s endsWith _))
- //
- // import reflect.Modifier.{ isPrivate, isProtected, isPublic, isStatic }
- // private def isSingleton(x: Int, isJava: Boolean) = !isJava || isStatic(x)
- // private def existsAndPublic(s: String): Boolean =
- // (dottedPaths containsKey s) || {
- // val clazz =
- // try Class.forName(s)
- // catch { case _: ClassNotFoundException | _: SecurityException => return false }
- //
- // isPublic(clazz.getModifiers)
- // }
-
- // the high level analysis
- def analyze(_buffer: String, clist: JList[String]): Int = {
- val parsed = new Parsed(_buffer)
- import parsed._
-
- val candidates = List(ids, pkgs) flatMap (_ completionsFor buffer)
- candidates foreach (clist add _)
- position
+ // members of Predef
+ val predef = new StaticCompletion("scala.Predef") {
+ override def filterNotFunction(s: String) = (
+ (s contains "2") ||
+ (s startsWith "wrap") ||
+ (s endsWith "Wrapper") ||
+ (s endsWith "Ops")
+ )
+ }
+ // members of scala.*
+ val scalalang = new pkgs.SubCompletor("scala") with ForwardingCompletion {
+ def forwardTo = pkgs follow "scala"
+ val arityClasses = {
+ val names = List("Tuple", "Product", "Function")
+ val expanded = for (name <- names ; index <- 0 to 22 ; dollar <- List("", "$")) yield name + index + dollar
+
+ Set(expanded: _*)
+ }
+
+ override def filterNotFunction(s: String) = {
+ val parsed = new Parsed(s)
+
+ (arityClasses contains parsed.unqualifiedPart) ||
+ (s endsWith "Exception") ||
+ (s endsWith "Error")
+ }
+ }
+ // members of java.lang.*
+ val javalang = new pkgs.SubCompletor("java.lang") with ForwardingCompletion {
+ def forwardTo = pkgs follow "java.lang"
+ import reflect.Modifier.isPublic
+ private def existsAndPublic(s: String): Boolean = {
+ val name = if (s contains ".") s else "java.lang." + s
+ val clazz = classForName(name) getOrElse (return false)
+
+ isPublic(clazz.getModifiers)
+ }
+ override def filterNotFunction(s: String) = {
+ (s endsWith "Exception") ||
+ (s endsWith "Error") ||
+ (s endsWith "Impl") ||
+ (s startsWith "CharacterData")
+ }
+ override def completions() = super.completions() filter existsAndPublic
}
+ val literals = new LiteralCompletion {
+ val global = repl.compiler
+ val parent = self
+ }
+
+ // the list of completion aware objects which should be consulted
+ val topLevel: List[CompletionAware] = List(ids, pkgs, predef, scalalang, javalang, literals)
+ def topLevelFor(buffer: String) = topLevel flatMap (_ completionsFor buffer)
// chasing down results which won't parse
def execute(line: String): Option[Any] = {
val parsed = new Parsed(line)
import parsed._
- if (!isQualified)
- return None
-
- for (topLevel <- List(ids, pkgs) ; exec <- topLevel executionFor buffer)
- return Some(exec)
-
- None
+ if (!isQualified) None
+ else {
+ (ids executionFor buffer) orElse
+ (pkgs executionFor buffer)
+ }
}
// override if history is available
def lastCommand: Option[String] = None
- // For recording the buffer on the last tab hit
- private var lastTab: (String, String) = (null, null)
+ // jline's entry point
+ lazy val jline: ArgumentCompletor = {
+ // TODO - refine the delimiters
+ //
+ // public static interface ArgumentDelimiter {
+ // ArgumentList delimit(String buffer, int argumentPosition);
+ // boolean isDelimiter(String buffer, int pos);
+ // }
+ val delimiters = new ArgumentCompletor.AbstractArgumentDelimiter {
+ // val delimChars = "(){},`; \t".toArray
+ val delimChars = "{},`; \t".toArray
+ def isDelimiterChar(s: String, pos: Int) = delimChars contains s.charAt(pos)
+ }
+
+ returning(new ArgumentCompletor(new JLineCompletion, delimiters))(_ setStrict false)
+ }
+
+ class JLineCompletion extends Completor {
+ // For recording the buffer on the last tab hit
+ private var lastTab: (String, String) = (null, null)
+
+ // Does this represent two consecutive tabs?
+ def isConsecutiveTabs(buf: String) = (buf, lastCommand orNull) == lastTab
+
+ // verbosity goes up with consecutive tabs
+ // TODO - actually implement.
+ private var verbosity = 0
+
+ // This is jline's entry point for completion.
+ override def complete(_buffer: String, cursor: Int, candidates: JList[String]): Int = {
+ if (!isInitialized)
+ return cursor
- // Does this represent two consecutive tabs?
- def isConsecutiveTabs(buf: String) = (buf, lastCommand orNull) == lastTab
+ // println("_buffer = %s, cursor = %d".format(_buffer, cursor))
+ verbosity = if (isConsecutiveTabs(_buffer)) verbosity + 1 else 0
+ lastTab = (_buffer, lastCommand orNull)
- // This is jline's entry point for completion.
- override def complete(_buffer: String, cursor: Int, candidates: JList[String]): Int = {
- // println("_buffer = %s, cursor = %d".format(_buffer, cursor))
- val verbose = isConsecutiveTabs(_buffer)
- lastTab = (_buffer, lastCommand orNull)
+ // parse the command buffer
+ val parsed = new Parsed(_buffer)
+ import parsed._
- // modify the buffer in place and returns the cursor position
- analyze(_buffer, candidates)
+ // modify in place and return the position
+ topLevelFor(buffer) foreach (candidates add _)
+ position
+ }
}
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala b/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala
index df110333dc..c50c063035 100644
--- a/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala
@@ -20,7 +20,7 @@ trait CompletionAware {
/** Default filter to apply to completions.
*/
- def filterNotFunction(s: String): Boolean = ReflectionCompletion shouldHide s
+ def filterNotFunction(s: String): Boolean = false
/** Default sort.
*/
@@ -28,7 +28,7 @@ trait CompletionAware {
/** Default map.
*/
- def mapFunction(s: String): String = s
+ def mapFunction(s: String) = NameTransformer decode s
/** The next completor in the chain.
*/
@@ -76,6 +76,8 @@ trait CompletionAware {
}
object CompletionAware {
+ val Empty = new CompletionAware { val completions = Nil }
+
def unapply(that: Any): Option[CompletionAware] = that match {
case x: CompletionAware => Some((x))
case _ => None
diff --git a/src/compiler/scala/tools/nsc/interpreter/IdentCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/IdentCompletion.scala
index 47a0cb80a0..b0152dbbc6 100644
--- a/src/compiler/scala/tools/nsc/interpreter/IdentCompletion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/IdentCompletion.scala
@@ -10,12 +10,16 @@ package interpreter
* delegates to an InstanceCompletion.
*/
class IdentCompletion(repl: Interpreter) extends CompletionAware {
- def completions() = repl.unqualifiedIds
+ val INTERPRETER_VAR_PREFIX = "res"
+
+ def completions() = repl.unqualifiedIds ::: List("classOf")
override def follow(id: String) =
+ // XXX this will be nice but needs solidifying.
+ // (repl completionAwareImplicit id) orElse
if (completions contains id) {
- (repl completionAware id) orElse
- (repl completionAwareImplicit id) orElse
- Some(new InstanceCompletion(repl clazzForIdent id))
+ (repl completionAware id) orElse {
+ repl clazzForIdent id map (x => new InstanceCompletion(x))
+ }
}
else None
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala
index d9ce962ca3..34367eacea 100644
--- a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala
@@ -15,26 +15,14 @@ class JLineReader(interpreter: Interpreter) extends InteractiveReader {
def this() = this(null)
override lazy val history = Some(History(consoleReader))
- override lazy val completion =
- if (interpreter == null) None
- else Some(new Completion(interpreter))
+ override lazy val completion = Option(interpreter) map (x => new Completion(x))
val consoleReader = {
val r = new jline.ConsoleReader()
r setHistory (History().jhistory)
r setBellEnabled false
-
- if (interpreter != null) {
- // have to specify all delimiters for completion to work nicely
- val delims = new ArgumentCompletor.AbstractArgumentDelimiter {
- // val delimChars = "(){}[],`;'\" \t".toArray
- val delimChars = "(){},`; \t".toArray
- def isDelimiterChar(s: String, pos: Int) = delimChars contains s.charAt(pos)
- }
- val comp = new ArgumentCompletor(completion.get, delims)
- comp setStrict false
- r addCompletor comp
- // XXX make this use a setting
+ completion foreach { c =>
+ r addCompletor c.jline
r setAutoprintThreshhold 250
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/LiteralCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/LiteralCompletion.scala
new file mode 100644
index 0000000000..3b74549d27
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/LiteralCompletion.scala
@@ -0,0 +1,50 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package interpreter
+
+import util.BatchSourceFile
+
+/** Literals, so we can pretend they are objects with methods.
+ */
+abstract class LiteralCompletion extends CompletionAware {
+ val parent: Completion
+ val global: Global
+
+ import global._
+
+ // TODO - figure out how to enumerate available implicit conversions.
+ // def richInt = new InstanceCompletion(classOf[scala.runtime.RichInt])
+
+ class PrimitiveCompletion(x: Type) extends CompletionAware {
+ lazy val completions = x.nonPrivateMembers map (_.name.toString)
+ override def follow(s: String) = {
+ val member = x.nonPrivateMembers find (_.name.toString == s)
+ member flatMap (m => Option(m.tpe)) map (_.resultType) map (x => new PrimitiveCompletion(x))
+ }
+ }
+
+ def simpleParse(code: String): Tree = {
+ val unit = new CompilationUnit(new BatchSourceFile("<console>", code))
+ val scanner = new syntaxAnalyzer.UnitParser(unit)
+
+ // only single statements
+ scanner.templateStatSeq(false) match {
+ case (_, List(t)) => t
+ case (_, x) => EmptyTree
+ }
+ }
+
+ def completions() = Nil
+ override def follow(id: String) = simpleParse(id) match {
+ case Literal(c @ Constant(_)) => Some(new PrimitiveCompletion(c.tpe))
+ // TODO - more AST trees.
+ // case Apply(fn @ Ident(name), args) =>
+ // classForName(name.toString) map (x => new StaticCompletion(x))
+ // None
+ case x => None
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/interpreter/PackageCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/PackageCompletion.scala
index f1c6717b2b..20c613f5a5 100644
--- a/src/compiler/scala/tools/nsc/interpreter/PackageCompletion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/PackageCompletion.scala
@@ -35,15 +35,27 @@ class PackageCompletion(classpath: List[URL]) extends CompletionAware {
else None
class SubCompletor(root: String) extends CompletionAware {
- private def infos = dottedPaths get root
- def completions() = infos map (_.visibleName)
+ // Look for a type alias
+ private def aliasCompletor(path: String): Option[CompletionAware] =
+ for (name <- ByteCode aliasForType path ; clazz <- classForName(name + "$")) yield
+ new StaticCompletion(clazz)
+
+ lazy val pkgObject = classForName(root + ".package$") map (x => new InstanceCompletion(x))
+ def pkgObjectMembers = pkgObject map (_.completions) getOrElse Nil
+
+ private def infos = Option(dottedPaths get root) getOrElse Nil
+ def completions() = {
+ val xs = infos map (_.visibleName) filterNot (_ == "package")
+ xs ::: pkgObjectMembers
+ }
override def follow(segment: String): Option[CompletionAware] = {
PackageCompletion.this.follow(root + "." + segment) orElse {
for (CompletionInfo(`segment`, className, _) <- infos) {
return Some(new StaticCompletion(className))
}
- None
+
+ aliasCompletor(root + "." + segment)
}
}
}
@@ -54,17 +66,29 @@ object PackageCompletion {
import java.util.jar.{ JarEntry, JarFile }
import scala.tools.nsc.io.Streamable
+ val EXPAND_SEPARATOR_STRING = "$$"
+ val ANON_CLASS_NAME = "$anon"
+ val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$"
+ val IMPL_CLASS_SUFFIX ="$class"
+
+ def ignoreClassName(x: String) =
+ (x contains EXPAND_SEPARATOR_STRING) ||
+ (x contains ANON_CLASS_NAME) ||
+ (x contains TRAIT_SETTER_SEPARATOR_STRING) ||
+ (x endsWith IMPL_CLASS_SUFFIX) ||
+ (x matches """.*\$\d+$""")
+
def enumToList[T](e: java.util.Enumeration[T]): List[T] = enumToListInternal(e, Nil)
private def enumToListInternal[T](e: java.util.Enumeration[T], xs: List[T]): List[T] =
if (e == null || !e.hasMoreElements) xs else enumToListInternal(e, e.nextElement :: xs)
def getClassFiles(path: String): List[String] = {
def exists(path: String) = { new File(path) exists }
- if (!exists(path)) return Nil
-
- (enumToList(new JarFile(path).entries) map (_.getName)) .
- partialMap { case x: String if x endsWith ".class" => x dropRight 6 } .
- filterNot { ReflectionCompletion.shouldHide }
+ if (!exists(path)) Nil else {
+ (enumToList(new JarFile(path).entries) map (_.getName))
+ . partialMap { case x: String if x endsWith ".class" => x dropRight 6 }
+ . filterNot { ignoreClassName }
+ }
}
case class CompletionInfo(visibleName: String, className: String, jar: String) {
diff --git a/src/compiler/scala/tools/nsc/interpreter/Parsed.scala b/src/compiler/scala/tools/nsc/interpreter/Parsed.scala
index 0c0104b8fa..885893ee53 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Parsed.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Parsed.scala
@@ -14,6 +14,7 @@ class Parsed(_buf: String) {
lazy val hd :: tl = segments
def stub = firstDot + hd + "."
def remainder = buffer stripPrefix stub
+ def unqualifiedPart = segments.last
def isEmpty = segments.size == 0
def isUnqualified = segments.size == 1
diff --git a/src/compiler/scala/tools/nsc/interpreter/ProductCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/ProductCompletion.scala
new file mode 100644
index 0000000000..2aaa6114c2
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/ProductCompletion.scala
@@ -0,0 +1,42 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package interpreter
+
+class SeqCompletion[T](elems: Seq[T]) extends CompletionAware {
+ lazy val completions = elems.indices.toList map ("(%d)" format _)
+ private def elemAt(name: String) =
+ if (completions contains name) Some(elems(name drop 1 dropRight 1 toInt)) else None
+
+ override def execute(name: String) = elemAt(name)
+ override def follow(name: String) = elemAt(name) map (x => ProductCompletion(x))
+}
+
+/** TODO - deal with non-case products by giving them _1 _2 etc. */
+class ProductCompletion(root: Product) extends CompletionAware {
+ lazy val caseFields: List[Any] = root.productIterator.toList
+ lazy val caseNames: List[String] = ByteCode caseParamNamesForPath root.getClass.getName getOrElse Nil
+ private def isValid = caseFields.length == caseNames.length
+
+ private def fieldForName(s: String) = (completions indexOf s) match {
+ case idx if idx > -1 && isValid => Some(caseFields(idx))
+ case _ => None
+ }
+
+ lazy val completions = caseNames
+ override def execute(name: String) = fieldForName(name)
+ override def follow(name: String) = fieldForName(name) map (x => ProductCompletion(x))
+}
+
+object ProductCompletion {
+ /** TODO: other traversables. */
+ def apply(elem: Any): CompletionAware = elem match {
+ case x: Seq[_] => new SeqCompletion[Any](x)
+ case x: Product => new ProductCompletion(x)
+ // case x: Map[_, _] =>
+ case _ => CompletionAware.Empty
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala
index 9fe427addf..60f045d7a1 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala
@@ -7,120 +7,97 @@ package scala.tools.nsc
package interpreter
import java.lang.reflect
-import reflect.Modifier.{ isPrivate, isProtected, isPublic, isStatic }
+import reflect.{ Modifier, AccessibleObject }
+import Modifier.{ isPrivate, isProtected, isStatic }
import scala.util.NameTransformer
import scala.collection.mutable.HashMap
import ReflectionCompletion._
+trait ReflectionCompletion extends CompletionAware {
+ def clazz: Class[_]
+ protected def visibleMembers: List[AccessibleObject]
+ protected def memberCompletions = visibleMembers filter isPublic map reflectName
+
+ def reflectName(m: AccessibleObject) = m match {
+ case x: reflect.Method => x.getName
+ case x: reflect.Field => x.getName
+ case x => error(x.toString)
+ }
+ def isPublic(m: AccessibleObject) = m match {
+ case x: reflect.Method => Modifier isPublic x.getModifiers
+ case x: reflect.Field => Modifier isPublic x.getModifiers
+ case x => error(x.toString)
+ }
+
+ override def filterNotFunction(s: String): Boolean = {
+ (excludeMethods contains s) ||
+ (s contains "$$super") ||
+ (s == "MODULE$")
+ }
+
+ lazy val (staticMethods, instanceMethods) = clazz.getMethods.toList partition (x => isStatic(x.getModifiers))
+ lazy val (staticFields, instanceFields) = clazz.getFields.toList partition (x => isStatic(x.getModifiers))
+
+ def isScalaClazz(cl: Class[_]) = allInterfacesFor(cl) exists (_.getName == "scala.ScalaObject")
+ def allInterfacesFor(cl: Class[_]): List[Class[_]] = allInterfacesFor(cl, Nil)
+
+ // methods to leave out of completion
+ val excludeMethods = List("hashCode", "equals", "wait", "notify", "notifyAll")
+
+ private def allInterfacesFor(cl: Class[_], acc: List[Class[_]]): List[Class[_]] = {
+ if (cl == null) acc.removeDuplicates
+ else allInterfacesFor(cl.getSuperclass, acc ::: cl.getInterfaces.toList)
+ }
+}
+
/** A completion aware object representing a single instance of some class.
* It completes to instance fields and methods, and delegates to another
* InstanceCompletion object if it can determine the result type of the element.
*/
-class InstanceCompletion(clazz: Class[_]) extends CompletionAware {
- def methods = clazz.getMethods.toList filterNot (x => isStatic(x.getModifiers))
- def fields = clazz.getFields.toList filterNot (x => isStatic(x.getModifiers))
- val (zeroArg, otherArg) = methods partition (_.getParameterTypes.size == 0)
-
- lazy val completions = (methods ::: fields) map (_.getName)
- override def mapFunction(s: String) = NameTransformer decode s
-
- // TODO
- // def idExtras = List("isInstanceOf", "asInstanceOf", "toString")
+class InstanceCompletion(val clazz: Class[_]) extends ReflectionCompletion {
+ protected def visibleMembers = instanceMethods ::: instanceFields
+ def extras = List("isInstanceOf", "asInstanceOf", "toString")
+ lazy val completions = memberCompletions ::: extras
+ val (zeroArg, otherArg) = instanceMethods partition (_.getParameterTypes.size == 0)
override def follow(id: String) = {
val nextClazz = zeroArg find (m => m.getName == id) map (_.getReturnType)
if (nextClazz.isDefined) nextClazz map (x => new InstanceCompletion(x))
- else fields find (_.getName == id) map (x => new InstanceCompletion(x.getType))
+ else instanceFields find (_.getName == id) map (x => new InstanceCompletion(x.getType))
}
}
/** The complementary class to InstanceCompletion. It has logic to deal with
* java static members and scala companion object members.
*/
-class StaticCompletion(jarEntryName: String) extends CompletionAware {
- def className = jarEntryName.replace('/', '.')
- def isScalaClazz(cl: Class[_]) = allInterfaces(cl) exists (_.getName == "scala.ScalaObject")
- def isJava = !isScalaClazz(clazz)
+class StaticCompletion(val clazz: Class[_]) extends ReflectionCompletion {
+ def this(name: String) = this(classForName(name.replace('/', '.')).get)
- lazy val clazz: Class[_] = {
- val cl = Class.forName(className)
- if (className.last != '$' && isScalaClazz(cl)) {
- try Class.forName(className + "$")
- catch { case _: Exception => cl }
- }
- else cl
- }
+ protected def visibleMembers = whichMethods ::: whichFields
+ lazy val completions = memberCompletions
- def methodFilter: reflect.Method => Boolean =
- if (isJava) m => isStatic(m.getModifiers) && isPublic(m.getModifiers)
- else m => isPublic(m.getModifiers)
-
- def methods = clazz.getMethods.toList filter methodFilter
- def fields = clazz.getFields.toList
-
- lazy val completions = (methods ::: fields) map (_.getName)
- override def mapFunction(s: String) = NameTransformer decode s
-
- // TODO - old version.
- //
- // private def getClassObject(path: String): Option[Class[_]] = {
- // val cl = clazz.getClassLoader()
- // try Some(Class.forName(path, true, cl).asInstanceOf[Class[_]])
- // catch { case _ => None }
- // }
- //
- // def completeStaticMembers(path: String): List[String] = {
- // // java style, static methods
- // val js = getClassObject(path) map (getMembers(_, true)) getOrElse Nil
- // // scala style, methods on companion object
- // // if getClassObject fails, see if there is a type alias
- // val clazz = getClassObject(path + "$") orElse {
- // (ByteCode aliasForType path) flatMap (x => getClassObject(x + "$"))
- // }
- // val ss = clazz map (getMembers(_, false)) getOrElse Nil
- //
- // js ::: ss
- // }
-}
+ private def aliasForPath(path: String) = ByteCode aliasForType path flatMap (x => classForName(x + "$"))
+ def className = clazz.getName
+ def isJava = !isScalaClazz(clazz)
-// TODO
-class PackageObjectCompletion(packageName: String) extends CompletionAware {
- def completions() = error("TODO")
+ private def whichMethods = if (isJava) staticMethods else instanceMethods
+ private def whichFields = if (isJava) staticFields else instanceFields
+ val (zeroArg, otherArg) = whichMethods partition (_.getParameterTypes.size == 0)
+
+ override def follow(id: String) = {
+ val nextClazz = zeroArg find (m => m.getName == id) map (_.getReturnType)
+ if (nextClazz.isDefined) nextClazz map (x => new InstanceCompletion(x))
+ else staticFields find (_.getName == id) map (x => new InstanceCompletion(x.getType))
+ }
- // def completePackageMembers(path: String): List[String] =
- // getClassObject(path + "." + "package") map (getMembers(_, false)) getOrElse Nil
+ override def toString = "StaticCompletion(%s) => %s".format(clazz.getName, completions)
}
-class ReflectionCompletion { }
object ReflectionCompletion {
import java.io.File
import java.util.jar.{ JarEntry, JarFile }
import scala.tools.nsc.io.Streamable
- val EXPAND_SEPARATOR_STRING = "$$"
- val ANON_CLASS_NAME = "$anon"
- val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$"
- val IMPL_CLASS_SUFFIX ="$class"
- val INTERPRETER_VAR_PREFIX = "res"
-
- def allInterfaces(clazz: Class[_]): List[Class[_]] = allInterfaces(clazz, Nil)
- def allInterfaces(clazz: Class[_], acc: List[Class[_]]): List[Class[_]] = {
- if (clazz == null) acc.removeDuplicates
- else allInterfaces(clazz.getSuperclass, acc ::: clazz.getInterfaces.toList)
- }
-
- // methods to leave out of completion
- val excludeMethods = List("", "hashCode", "equals", "wait", "notify", "notifyAll")
-
- def shouldHide(x: String) =
- (excludeMethods contains x) ||
- (x contains EXPAND_SEPARATOR_STRING) || // XXX
- (x contains ANON_CLASS_NAME) ||
- (x contains TRAIT_SETTER_SEPARATOR_STRING) ||
- (x endsWith IMPL_CLASS_SUFFIX) ||
- (x == "MODULE$") ||
- (x matches """.*\$\d+$""")
-
// XXX at the moment this is imperfect because scala's protected semantics
// differ from java's, so protected methods appear public via reflection;
// yet scala enforces the protection. The result is that protected members
diff --git a/src/compiler/scala/tools/nsc/interpreter/package.scala b/src/compiler/scala/tools/nsc/interpreter/package.scala
new file mode 100644
index 0000000000..767bc9ca2f
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/package.scala
@@ -0,0 +1,19 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+
+package object interpreter {
+ /** Apply a function and return the passed value */
+ def returning[T](x: T)(f: T => Unit): T = { f(x) ; x }
+
+ /** Tracing */
+ def tracing[T](msg: String)(x: T): T = { println("(" + msg + ") " + x) ; x }
+
+ /** Class objects */
+ def classForName(name: String): Option[Class[_]] =
+ try Some(Class forName name)
+ catch { case _: ClassNotFoundException | _: SecurityException => None }
+}