summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-04-26 20:42:03 +0000
committerPaul Phillips <paulp@improving.org>2010-04-26 20:42:03 +0000
commitcf552d7f27ce5aaf27d890b5f4068914aca76444 (patch)
tree423c48e48c7bbd2117010a65db151134c8f13094 /src
parentbc17cc6c0363a15fa28ea8c487e66bfb324569af (diff)
downloadscala-cf552d7f27ce5aaf27d890b5f4068914aca76444.tar.gz
scala-cf552d7f27ce5aaf27d890b5f4068914aca76444.tar.bz2
scala-cf552d7f27ce5aaf27d890b5f4068914aca76444.zip
Mostly finishing the ball that got rolling in r...
Mostly finishing the ball that got rolling in r21679. scala> String.cop<tab> scala> String.copyValueOf<tab> def copyValueOf(Array[Char]): String def copyValueOf(Array[Char], Int, Int): String scala> Nil.mkString<tab><tab> def mkString(sep: String): String def mkString(start: String, sep: String, end: String): String def mkString: String Lines which are not simply delimited don't work yet, so don't go expecting List(1).<tab> to do the right thing. Yet. No review.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Completion.scala144
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala33
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Parsed.scala12
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ProductCompletion.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala11
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/XMLCompletion.scala1
6 files changed, 146 insertions, 57 deletions
diff --git a/src/compiler/scala/tools/nsc/interpreter/Completion.scala b/src/compiler/scala/tools/nsc/interpreter/Completion.scala
index bfb375bf67..ca7bb54ab0 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Completion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Completion.scala
@@ -25,7 +25,7 @@ object Completion {
object Forwarder {
def apply(forwardTo: () => Option[CompletionAware]): CompletionAware = new CompletionAware {
- def completions() = forwardTo() map (_.completions()) getOrElse Nil
+ def completions(verbosity: Int) = forwardTo() map (_ completions verbosity) getOrElse Nil
override def follow(s: String) = forwardTo() flatMap (_ follow s)
}
}
@@ -41,10 +41,11 @@ class Completion(val repl: Interpreter) {
def isCompletionDebug = repl.isCompletionDebug
def DBG(msg: => Any) = if (isCompletionDebug) println(msg.toString)
+ def debugging[T](msg: String): T => T = (res: T) => returning[T](res)(x => DBG(msg + x))
lazy val global: repl.compiler.type = repl.compiler
import global._
- import definitions.{ PredefModule, RootClass, AnyClass, AnyRefClass, ScalaPackage, JavaLangPackage }
+ import definitions.{ PredefModule, RootClass, AnyClass, AnyRefClass, ScalaPackage, JavaLangPackage, isRepeatedParamType }
// XXX not yet used.
lazy val dottedPaths = {
@@ -68,6 +69,7 @@ class Completion(val repl: Interpreter) {
def tp: Type
def effectiveTp = tp match {
case MethodType(Nil, resType) => resType
+ case PolyType(Nil, resType) => resType
case _ => tp
}
@@ -76,10 +78,46 @@ class Completion(val repl: Interpreter) {
private def anyMembers = AnyClass.tpe.nonPrivateMembers
def anyRefMethodsToShow = List("isInstanceOf", "asInstanceOf", "toString")
+ /** Only prints the parameter names if they're not synthetic,
+ * since "x$1: Int" does not offer any more information than "Int".
+ */
+ def typeString(tp: Type): String = {
+ val str = tp.toString
+ val prefixes = List("java.lang.", "scala.collection.")
+
+ prefixes.foldLeft(str)(_ stripPrefix _)
+ }
+
+ def methodSignatureString(name: String, sym: Symbol) = atPhase(currentRun.typerPhase) {
+ def assembleParams(params: List[Symbol]): String = {
+ if (params.isEmpty)
+ return "()"
+ if (isRepeatedParamType(params.last.tpe)) // (
+ return assembleParams(params.init).init + "*)"
+
+ val xs =
+ if (params exists (_.isSynthetic)) params map (x => typeString(x.tpe))
+ else params map (_.defString)
+
+ xs.mkString("(", ", ", ")")
+ }
+
+ def assemble(paramPart: String, resType: Type): String =
+ "def " + name + paramPart + ": " + typeString(resType)
+
+ sym.info match {
+ case MethodType(params, resType) => assemble(assembleParams(params), resType)
+ case PolyType(tparams, resType) => assemble("", resType)
+ case x => x.toString
+ }
+ }
+
def tos(sym: Symbol) = sym.name.decode.toString
def memberNamed(s: String) = members find (x => tos(x) == s)
def hasMethod(s: String) = methods exists (x => tos(x) == s)
+ // XXX we'd like to say "filterNot (_.isDeprecated)" but this causes the
+ // compiler to crash for reasons not yet known.
def members = (effectiveTp.nonPrivateMembers ++ anyMembers) filter (_.isPublic)
def methods = members filter (_.isMethod)
def packages = members filter (_.isPackage)
@@ -99,7 +137,7 @@ class Completion(val repl: Interpreter) {
def imported(tp: Type) = new ImportCompletion(tp)
}
- class TypeMemberCompletion (val tp: Type) extends CompletionAware with CompilerCompletion {
+ class TypeMemberCompletion(val tp: Type) extends CompletionAware with CompilerCompletion {
def excludeEndsWith: List[String] = Nil
def excludeStartsWith: List[String] = List("<") // <byname>, <repeated>, etc.
def excludeNames: List[String] = anyref.methodNames -- anyRefMethodsToShow ++ List("_root_")
@@ -111,12 +149,19 @@ class Completion(val repl: Interpreter) {
(excludeStartsWith exists (name startsWith _))
)
def filtered(xs: List[String]) = xs filterNot exclude distinct
- def completions = {
- returning(filtered(memberNames))(xs => DBG("%s completions: %s".format(tp, xs)))
- }
+
+ def completions(verbosity: Int) =
+ debugging(tp + " completions ==> ")(filtered(memberNames))
override def follow(s: String): Option[CompletionAware] =
- memberNamed(s) map (x => TypeMemberCompletion(x.tpe))
+ debugging(tp + " -> '" + s + "' ==> ")(memberNamed(s) map (x => TypeMemberCompletion(x.tpe)))
+
+ override def alternativesFor(id: String): List[String] =
+ debugging(id + " alternatives ==> ") {
+ val alts = members filter (x => x.isMethod && tos(x) == id) map (sym => methodSignatureString(id, sym))
+
+ if (alts.nonEmpty) "" :: alts else Nil
+ }
override def toString = "TypeMemberCompletion(%s)".format(tp)
}
@@ -126,15 +171,17 @@ class Completion(val repl: Interpreter) {
}
class LiteralCompletion(lit: Literal) extends TypeMemberCompletion(lit.value.tpe) {
- private lazy val completions0 = filtered(memberNames)
- private lazy val completions1 = memberNames
- override def completions = if (verbosity == 0) completions0 else completions1
+ override def completions(verbosity: Int) = verbosity match {
+ case 0 => filtered(memberNames)
+ case _ => memberNames
+ }
}
class ImportCompletion(tp: Type) extends TypeMemberCompletion(tp) {
- private lazy val completions0 = filtered(methods filterNot (_.isSetter) map tos)
- private lazy val completions1 = super.completions
- override def completions = if (verbosity == 0) completions0 else completions1
+ override def completions(verbosity: Int) = verbosity match {
+ case 0 => filtered(methods filterNot (_.isSetter) map tos)
+ case _ => super.completions(verbosity)
+ }
}
// not for completion but for excluding
@@ -142,11 +189,11 @@ class Completion(val repl: Interpreter) {
// the unqualified vals/defs/etc visible in the repl
object ids extends CompletionAware {
- def completions() = repl.unqualifiedIds ::: List("classOf")
+ override def completions(verbosity: Int) = repl.unqualifiedIds ::: List("classOf")
// we try to use the compiler and fall back on reflection if necessary
// (which at present is for anything defined in the repl session.)
override def follow(id: String) =
- if (completions contains id) {
+ if (completions(0) contains id) {
for (clazz <- repl clazzForIdent id) yield {
(typeOf(clazz.getName) map TypeMemberCompletion.apply) getOrElse new InstanceCompletion(clazz)
}
@@ -167,7 +214,7 @@ class Completion(val repl: Interpreter) {
if (tss.size == 1) tss.head else EmptyTree
}
- val completions = Nil
+ def completions(verbosity: Int) = Nil
override def follow(id: String) = simpleParse(id) match {
case x: Literal => Some(new LiteralCompletion(x))
@@ -187,9 +234,10 @@ class Completion(val repl: Interpreter) {
(name contains "2")
)
- private lazy val completions0 = Nil
- private lazy val completions1 = super.completions
- override def completions = if (verbosity == 0) completions0 else completions1
+ override def completions(verbosity: Int) = verbosity match {
+ case 0 => Nil
+ case _ => super.completions(verbosity)
+ }
}
// members of scala.*
object scalalang extends PackageCompletion(ScalaPackage.tpe) {
@@ -199,18 +247,20 @@ class Completion(val repl: Interpreter) {
skipArity(name)
)
- private lazy val completions0 = filtered(packageNames ++ aliasNames)
- private lazy val completions1 = super.completions
- override def completions = if (verbosity == 0) completions0 else completions1
+ override def completions(verbosity: Int) = verbosity match {
+ case 0 => filtered(packageNames ++ aliasNames)
+ case _ => super.completions(verbosity)
+ }
}
// members of java.lang.*
object javalang extends PackageCompletion(JavaLangPackage.tpe) {
override lazy val excludeEndsWith = super.excludeEndsWith ++ List("Exception", "Error")
override lazy val excludeStartsWith = super.excludeStartsWith ++ List("CharacterData")
- private lazy val completions0 = filtered(packageNames)
- private lazy val completions1 = super.completions
- override def completions = if (verbosity == 0) completions0 else completions1
+ override def completions(verbosity: Int) = verbosity match {
+ case 0 => filtered(packageNames)
+ case _ => super.completions(verbosity)
+ }
}
// the list of completion aware objects which should be consulted
@@ -252,19 +302,30 @@ class Completion(val repl: Interpreter) {
lazy val jline: ArgumentCompletor =
returning(new ArgumentCompletor(new JLineCompletion, new JLineDelimiter))(_ setStrict false)
+ /** This gets a little bit hairy. It's no small feat delegating everything
+ * and also keeping track of exactly where the cursor is and where it's supposed
+ * to end up. The alternatives mechanism is a little hacky: if there is an empty
+ * string in the list of completions, that means we are expanding a unique
+ * completion, so don't update the "last" buffer because it'll be wrong.
+ */
class JLineCompletion extends Completor {
// For recording the buffer on the last tab hit
- private var lastTab: (String, Int) = ("", -1)
+ private var lastBuf: String = ""
+ private var lastCursor: Int = -1
// Does this represent two consecutive tabs?
- def isConsecutiveTabs(current: (String, Int)) = current == lastTab
+ def isConsecutiveTabs(buf: String, cursor: Int) = cursor == lastCursor && buf == lastBuf
- // This is jline's entry point for completion.
- override def complete(buf: String, cursor: Int, candidates: JList[String]): Int = {
- verbosity = if (isConsecutiveTabs((buf, cursor))) verbosity + 1 else 0
- DBG("complete(%s, %d), verbosity: %s".format(buf, cursor, verbosity))
+ // Longest common prefix
+ def commonPrefix(xs: List[String]) =
+ if (xs.isEmpty) ""
+ else xs.reduceLeft(_ zip _ takeWhile (x => x._1 == x._2) map (_._1) mkString)
- lastTab = (buf, cursor)
+ // This is jline's entry point for completion.
+ override def complete(_buf: String, cursor: Int, candidates: JList[String]): Int = {
+ val buf = onull(_buf)
+ verbosity = if (isConsecutiveTabs(buf, cursor)) verbosity + 1 else 0
+ DBG("complete(%s, %d) last = (%s, %d), verbosity: %s".format(buf, cursor, lastBuf, lastCursor, verbosity))
// we don't try lower priority completions unless higher ones return no results.
def tryCompletion(p: Parsed, completionFunction: Parsed => List[String]): Option[Int] = {
@@ -273,19 +334,30 @@ class Completion(val repl: Interpreter) {
case xs =>
// modify in place and return the position
xs foreach (candidates add _)
- // DBG("Completion candidates: " + (xs mkString " "))
- Some(p.position)
+ // update the last buffer unless this is an alternatives list
+ if (xs contains "") Some(p.cursor)
+ else {
+ val advance = commonPrefix(xs)
+ lastCursor = p.position + advance.length
+ lastBuf = (buf take p.position) + advance
+
+ DBG("tryCompletion(%s, _) lastBuf = %s, lastCursor = %s, p.position = %s".format(p, lastBuf, lastCursor, p.position))
+ Some(p.position)
+ }
}
}
+ def mkDotted = Parsed.dotted(buf, cursor) withVerbosity verbosity
+ def mkUndelimited = Parsed.undelimited(buf, cursor) withVerbosity verbosity
+
// a single dot is special cased to completion on the previous result
def lastResultCompletion =
if (!looksLikeInvocation(buf)) None
else tryCompletion(Parsed.dotted(buf drop 1, cursor), lastResultFor)
- def regularCompletion = tryCompletion(Parsed.dotted(buf, cursor), topLevelFor)
- def fileCompletion = tryCompletion(Parsed.undelimited(buf, cursor), FileCompletion completionsFor _.buffer)
+ def regularCompletion = tryCompletion(mkDotted, topLevelFor)
+ def fileCompletion = tryCompletion(mkUndelimited, FileCompletion completionsFor _.buffer)
(lastResultCompletion orElse regularCompletion orElse fileCompletion) getOrElse cursor
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala b/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala
index fe05ac70b5..cfd3b5e05f 100644
--- a/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala
@@ -21,8 +21,7 @@ trait CompletionAware {
/** The complete list of unqualified Strings to which this
* object will complete.
*/
- def completions(): List[String]
- def completions(start: String): List[String] = completions filter (_ startsWith start)
+ def completions(verbosity: Int): List[String]
/** Default filter to apply to completions.
*/
@@ -47,6 +46,19 @@ trait CompletionAware {
*/
def execute(id: String): Option[Any] = None
+ /** A list of useful information regarding a specific uniquely
+ * identified completion. This is specifically written for the
+ * following situation, but should be useful elsewhere too:
+ *
+ * x.y.z.methodName<tab>
+ *
+ * If "methodName" is among z's completions, and verbosity > 0
+ * indicating tab has been pressed twice consecutively, then we
+ * call alternativesFor and show a list of overloaded method
+ * signatures.
+ */
+ def alternativesFor(id: String): List[String] = Nil
+
/** Given string 'buf', return a list of all the strings
* to which it can complete. This may involve delegating
* to other CompletionAware objects.
@@ -54,12 +66,16 @@ trait CompletionAware {
def completionsFor(parsed: Parsed): List[String] = {
import parsed._
- def cs =
- if (isEmpty) completions()
- else if (isUnqualified && !isLastDelimiter) completions(buffer)
+ val comps = completions(verbosity) filter (_ startsWith buffer)
+ val results =
+ if (isEmpty) comps
+ else if (isUnqualified && !isLastDelimiter) {
+ if (verbosity > 0 && (comps contains buffer)) alternativesFor(buffer)
+ else comps
+ }
else follow(bufferHead) map (_ completionsFor bufferTail) getOrElse Nil
- cs filterNot filterNotFunction map mapFunction sortWith (sortFunction _)
+ results filterNot filterNotFunction map mapFunction sortWith (sortFunction _)
}
/** TODO - unify this and completionsFor under a common traverser.
@@ -67,14 +83,14 @@ trait CompletionAware {
def executionFor(parsed: Parsed): Option[Any] = {
import parsed._
- if (isUnqualified && !isLastDelimiter && (completions contains buffer)) execute(buffer)
+ if (isUnqualified && !isLastDelimiter && (completions(verbosity) contains buffer)) execute(buffer)
else if (!isQualified) None
else follow(bufferHead) flatMap (_ executionFor bufferTail)
}
}
object CompletionAware {
- val Empty = new CompletionAware { val completions = Nil }
+ val Empty = new CompletionAware { def completions(verbosity: Int) = Nil }
// class Forwarder(underlying: CompletionAware) extends CompletionAware {
// override def completions() = underlying.completions()
@@ -101,6 +117,7 @@ object CompletionAware {
def apply(terms: () => List[String], followFunction: String => Option[CompletionAware]): CompletionAware =
new CompletionAware {
def completions = terms()
+ def completions(verbosity: Int) = completions
override def follow(id: String) = followFunction(id)
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/Parsed.scala b/src/compiler/scala/tools/nsc/interpreter/Parsed.scala
index 810116d0cf..0b92608d88 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Parsed.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Parsed.scala
@@ -20,13 +20,17 @@ class Parsed private (
def isQualified = args.size > 1
def isAtStart = cursor <= 0
+ private var _verbosity = 0
+ def verbosity = _verbosity
+ def withVerbosity(v: Int): this.type = returning[this.type](this)(_ => _verbosity = v)
+
def args = toArgs(buffer take cursor).toList
def bufferHead = args.head
def headLength = bufferHead.length + 1
- def bufferTail = new Parsed(buffer drop headLength, cursor - headLength, delimited)
+ def bufferTail = new Parsed(buffer drop headLength, cursor - headLength, delimited) withVerbosity verbosity
- def prev = new Parsed(buffer, cursor - 1, delimited)
- def next = new Parsed(buffer, cursor + 1, delimited)
+ def prev = new Parsed(buffer, cursor - 1, delimited) withVerbosity verbosity
+ def next = new Parsed(buffer, cursor + 1, delimited) withVerbosity verbosity
def currentChar = buffer(cursor)
def currentArg = args.last
def position =
@@ -52,7 +56,7 @@ class Parsed private (
object Parsed {
def apply(s: String): Parsed = apply(onull(s), onull(s).length)
- def apply(s: String, cursor: Int): Parsed = apply(onull(s), cursor, "(){},`; \t" contains _)
+ def apply(s: String, cursor: Int): Parsed = apply(onull(s), cursor, "{},`; \t" contains _)
def apply(s: String, cursor: Int, delimited: Char => Boolean): Parsed =
new Parsed(onull(s), cursor, delimited)
diff --git a/src/compiler/scala/tools/nsc/interpreter/ProductCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/ProductCompletion.scala
index 2aaa6114c2..6c066580ae 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ProductCompletion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ProductCompletion.scala
@@ -8,6 +8,7 @@ package interpreter
class SeqCompletion[T](elems: Seq[T]) extends CompletionAware {
lazy val completions = elems.indices.toList map ("(%d)" format _)
+ def completions(verbosity: Int) = completions
private def elemAt(name: String) =
if (completions contains name) Some(elems(name drop 1 dropRight 1 toInt)) else None
@@ -27,6 +28,7 @@ class ProductCompletion(root: Product) extends CompletionAware {
}
lazy val completions = caseNames
+ def completions(verbosity: Int) = completions
override def execute(name: String) = fieldForName(name)
override def follow(name: String) = fieldForName(name) map (x => ProductCompletion(x))
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala
index 3d621cdbd9..f9ff894d59 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala
@@ -46,15 +46,6 @@ trait ReflectionCompletion extends CompletionAware {
}
}
-/** An instance completion which hides a few useless members.
- */
-class PackageObjectCompletion(clazz: Class[_]) extends InstanceCompletion(clazz) {
- override lazy val completions = memberCompletions
- override def filterNotFunction(s: String) = {
- super.filterNotFunction(s) || (s == "getClass") || (s == "toString")
- }
-}
-
/** 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.
@@ -63,6 +54,7 @@ class InstanceCompletion(val clazz: Class[_]) extends ReflectionCompletion {
protected def visibleMembers = instanceMethods ::: instanceFields
def extras = List("isInstanceOf", "asInstanceOf", "toString")
lazy val completions = memberCompletions ::: extras
+ def completions(verbosity: Int) = completions
val (zeroArg, otherArg) = instanceMethods partition (_.getParameterTypes.size == 0)
override def follow(id: String) = {
@@ -78,6 +70,7 @@ class InstanceCompletion(val clazz: Class[_]) extends ReflectionCompletion {
class StaticCompletion(val clazz: Class[_]) extends ReflectionCompletion {
protected def visibleMembers = whichMethods ::: whichFields
lazy val completions = memberCompletions
+ def completions(verbosity: Int) = completions
private def aliasForPath(path: String) = ByteCode aliasForType path flatMap (x => classForName(x + "$"))
def className = clazz.getName
diff --git a/src/compiler/scala/tools/nsc/interpreter/XMLCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/XMLCompletion.scala
index 67063192bd..f2af57cc36 100644
--- a/src/compiler/scala/tools/nsc/interpreter/XMLCompletion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/XMLCompletion.scala
@@ -33,6 +33,7 @@ class XMLCompletion(root: Node) extends CompletionAware {
s :: res
}).sorted
}
+ def completions(verbosity: Int) = completions
override def execute(id: String) = getNode(id)
override def follow(id: String) = getNode(id) map (x => new XMLCompletion(x))