diff options
author | Paul Phillips <paulp@improving.org> | 2009-12-16 19:25:26 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2009-12-16 19:25:26 +0000 |
commit | 1a7200a1d20afb60bf5e1eb912e7e31c3156a851 (patch) | |
tree | ab31356647f8a357cfa998ccecbc6fe88f7a4dc9 /src/compiler | |
parent | eb46c9ab39c5b95724f17908225b2a3728e7b72d (diff) | |
download | scala-1a7200a1d20afb60bf5e1eb912e7e31c3156a851.tar.gz scala-1a7200a1d20afb60bf5e1eb912e7e31c3156a851.tar.bz2 scala-1a7200a1d20afb60bf5e1eb912e7e31c3156a851.zip |
REPL completion now understands type aliases de...
REPL completion now understands type aliases defined in package objects.
For instance try scala.List.<tab>. review by community.
Diffstat (limited to 'src/compiler')
5 files changed, 81 insertions, 20 deletions
diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala index b1065113ad..55e3b354e5 100644 --- a/src/compiler/scala/tools/nsc/Interpreter.scala +++ b/src/compiler/scala/tools/nsc/Interpreter.scala @@ -145,8 +145,8 @@ class Interpreter(val settings: Settings, out: PrintWriter) shadow the old ones, and old code objects refer to the old definitions. */ - private var classLoader: ScalaClassLoader = makeClassLoader() - private def makeClassLoader(): ScalaClassLoader = { + private var classLoader: AbstractFileClassLoader = makeClassLoader() + private def makeClassLoader(): AbstractFileClassLoader = { val parent = if (parentClassLoader == null) ScalaClassLoader fromURLs compilerClasspath else new URLClassLoader(compilerClasspath, parentClassLoader) @@ -907,13 +907,14 @@ class Interpreter(val settings: Settings, out: PrintWriter) /** Another entry point for tab-completion, ids in scope */ def unqualifiedIds(): List[String] = - allBoundNames . - map(_.toString) . - filter(!isSynthVarName(_)) + allBoundNames map (_.toString) filterNot isSynthVarName /** For static/object method completion */ def getClassObject(path: String): Option[Class[_]] = classLoader tryToLoadClass path + /** Parse the ScalaSig to find type aliases */ + def aliasForType(path: String) = ByteCode.aliasForType(path) + // debugging private var debuggingOutput = false def DBG(s: String) = if (debuggingOutput) out println s else () diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala index 1c5fa3a5b9..c821b9ed80 100644 --- a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala @@ -18,7 +18,7 @@ class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader) extends ClassLoader(parent) with ScalaClassLoader { - override def findClass(name: String): Class[_] = { + def getBytesForClass(name: String): Array[Byte] = { def onull[T](x: T): T = if (x == null) throw new ClassNotFoundException(name) else x var file: AbstractFile = root val pathParts = name.split("[./]").toList @@ -27,7 +27,11 @@ class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader) file = onull(file.lookupName(dirPart, true)) file = onull(file.lookupName(pathParts.last+".class", false)) - val bytes = file.toByteArray + file.toByteArray + } + + override def findClass(name: String): Class[_] = { + val bytes = getBytesForClass(name) defineClass(name, bytes, 0, bytes.length) } } diff --git a/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala b/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala new file mode 100644 index 0000000000..d66cbb7818 --- /dev/null +++ b/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala @@ -0,0 +1,36 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2010 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package interpreter + +import java.io.File +import java.util.jar.{ JarEntry, JarFile } +import java.util.concurrent.ConcurrentHashMap +import util.ScalaClassLoader.getSystemLoader + +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 + } + + def aliasesForPackage(pkg: String) = DECODE flatMap (_(pkg)) + + /** Use scalap to look through type aliases */ + def aliasForType(path: String): Option[String] = { + val (pkg, name) = (path lastIndexOf '.') match { + case -1 => return None + case idx => (path take idx, path drop (idx + 1)) + } + aliasesForPackage(pkg) flatMap (_ get name) + } +} diff --git a/src/compiler/scala/tools/nsc/interpreter/Completion.scala b/src/compiler/scala/tools/nsc/interpreter/Completion.scala index 3303ba3550..2ecafa974a 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Completion.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Completion.scala @@ -38,10 +38,7 @@ class Completion(val interpreter: Interpreter) extends Completor { // it takes a little while to look through the jars so we use a future and a concurrent map class CompletionAgent { - val dottedPaths = new ConcurrentHashMap[String, List[String]] - // TODO - type aliases defined in package objects, like scala.List.<tab> - // val typeAliases = new ConcurrentHashMap[String, String] - // val packageObjects = new ConcurrentHashMap[String, List[String]] + val dottedPaths = new ConcurrentHashMap[String, List[CompletionInfo]] val topLevelPackages = new DelayedLazyVal( () => enumToList(dottedPaths.keys) filterNot (_ contains '.'), getDottedPaths(dottedPaths, interpreter) @@ -121,14 +118,12 @@ class Completion(val interpreter: Interpreter) extends Completor { } } - def getOrElse[K, V](map: ConcurrentHashMap[K, V], key: K, value: => V) = - if (map containsKey key) map get key - else value - def isValidId(s: String) = interpreter.unqualifiedIds contains s def membersOfId(s: String) = interpreter membersOfIdentifier s def membersOfPath(s: String) = { - val xs = getOrElse(dottedPaths, s, Nil) + val xs = + if (dottedPaths containsKey s) dottedPaths get s map (_.visibleName) + else Nil s match { case "scala" => xs filterNot scalaToHide @@ -191,7 +186,11 @@ class Completion(val interpreter: Interpreter) extends Completor { // java style, static methods val js = getClassObject(path) map (getMembers(_, true)) getOrElse Nil // scala style, methods on companion object - val ss = getClassObject(path + "$") map (getMembers(_, false)) getOrElse Nil + // 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 } @@ -201,6 +200,7 @@ object Completion { 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" @@ -208,6 +208,24 @@ object Completion val IMPL_CLASS_SUFFIX ="$class" val INTERPRETER_VAR_PREFIX = "res" + case class CompletionInfo(visibleName: String, className: String, jar: String) { + lazy val jarfile = new JarFile(jar) + lazy val entry = jarfile getEntry className + + override def hashCode = visibleName.hashCode + override def equals(other: Any) = other match { + case x: CompletionInfo => visibleName == x.visibleName + case _ => false + } + + def getBytes(): Array[Byte] = { + if (entry == null) Array() else { + val x = new Streamable.Bytes { def inputStream() = jarfile getInputStream entry } + x.toByteArray() + } + } + } + def enumToList[T](e: java.util.Enumeration[T]): List[T] = enumToList(e, Nil) def enumToList[T](e: java.util.Enumeration[T], xs: List[T]): List[T] = if (e == null || !e.hasMoreElements) xs else enumToList(e, e.nextElement :: xs) @@ -233,7 +251,7 @@ object Completion // all the dotted path to classfiles we can find by poking through the jars def getDottedPaths( - map: ConcurrentHashMap[String, List[String]], + map: ConcurrentHashMap[String, List[CompletionInfo]], interpreter: Interpreter): Unit = { val cp = @@ -264,7 +282,9 @@ object Completion def oneJar(jar: String): Unit = { val classfiles = Completion getClassFiles jar - for (cl <- classfiles.removeDuplicates ; (k, v) <- subpaths(cl)) { + for (cl <- classfiles.removeDuplicates ; (k, _v) <- subpaths(cl)) { + val v = CompletionInfo(_v, cl, jar) + if (map containsKey k) { val vs = map.get(k) if (vs contains v) () diff --git a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala index 62f11c544c..95396dd95b 100644 --- a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala +++ b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala @@ -67,7 +67,7 @@ object ScalaClassLoader { def setContextLoader(cl: JavaClassLoader) = Thread.currentThread.setContextClassLoader(cl) def getContextLoader() = Thread.currentThread.getContextClassLoader() - def getSystemLoader() = JavaClassLoader.getSystemClassLoader() + def getSystemLoader(): ScalaClassLoader = new JavaClassLoader(JavaClassLoader.getSystemClassLoader()) with ScalaClassLoader def defaultParentClassLoader() = findExtClassLoader() def fromURLs(urls: Seq[URL]): URLClassLoader = |