diff options
author | Paul Phillips <paulp@improving.org> | 2010-01-26 22:14:15 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2010-01-26 22:14:15 +0000 |
commit | f6183b63f28a199b7888949963a8d6984d54c3b1 (patch) | |
tree | 8eb557773e913669e7ebac6a720392602fe72481 /src/compiler/scala/tools/nsc/interpreter/Completion.scala | |
parent | 8856f21f59ff735db7951901a799300a5affd9cc (diff) | |
download | scala-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/scala/tools/nsc/interpreter/Completion.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/interpreter/Completion.scala | 168 |
1 files changed, 112 insertions, 56 deletions
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 + } } } |