diff options
Diffstat (limited to 'src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala')
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala | 351 |
1 files changed, 0 insertions, 351 deletions
diff --git a/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala b/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala deleted file mode 100644 index c2ccfc8064..0000000000 --- a/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala +++ /dev/null @@ -1,351 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import Completion._ -import scala.collection.mutable.ListBuffer -import scala.reflect.internal.util.StringOps.longestCommonPrefix -import scala.tools.nsc.interactive.Global - -// REPL completor - queries supplied interpreter for valid -// completions based on current contents of buffer. -// TODO: change class name to reflect it's not specific to jline (nor does it depend on it) -class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput { - val global: intp.global.type = intp.global - import global._ - import definitions._ - import rootMirror.{ RootClass, getModuleIfDefined } - import intp.{ debugging } - - // verbosity goes up with consecutive tabs - private var verbosity: Int = 0 - def resetVerbosity() = verbosity = 0 - - def getSymbol(name: String, isModule: Boolean) = ( - if (isModule) getModuleIfDefined(name) - else getModuleIfDefined(name) - ) - - trait CompilerCompletion { - def tp: Type - def effectiveTp = tp match { - case MethodType(Nil, resType) => resType - case NullaryMethodType(resType) => resType - case _ => tp - } - - // for some reason any's members don't show up in subclasses, which - // we need so 5.<tab> offers asInstanceOf etc. - private def anyMembers = AnyTpe.nonPrivateMembers - def anyRefMethodsToShow = Set("isInstanceOf", "asInstanceOf", "toString") - - def tos(sym: Symbol): String = sym.decodedName - def memberNamed(s: String) = exitingTyper(effectiveTp member newTermName(s)) - - // XXX we'd like to say "filterNot (_.isDeprecated)" but this causes the - // compiler to crash for reasons not yet known. - def members = exitingTyper((effectiveTp.nonPrivateMembers.toList ++ anyMembers) filter (_.isPublic)) - def methods = members.toList filter (_.isMethod) - def packages = members.toList filter (_.hasPackageFlag) - def aliases = members.toList filter (_.isAliasType) - - def memberNames = members map tos - def methodNames = methods map tos - def packageNames = packages map tos - def aliasNames = aliases map tos - } - - object NoTypeCompletion extends TypeMemberCompletion(NoType) { - override def memberNamed(s: String) = NoSymbol - override def members = Nil - override def follow(s: String) = None - override def alternativesFor(id: String) = Nil - } - - object TypeMemberCompletion { - def apply(tp: Type, runtimeType: Type, param: NamedParam): TypeMemberCompletion = { - new TypeMemberCompletion(tp) { - var upgraded = false - lazy val upgrade = { - intp rebind param - intp.reporter.printMessage("\nRebinding stable value %s from %s to %s".format(param.name, tp, param.tpe)) - upgraded = true - new TypeMemberCompletion(runtimeType) - } - override def completions(verbosity: Int) = { - super.completions(verbosity) ++ ( - if (verbosity == 0) Nil - else upgrade.completions(verbosity) - ) - } - override def follow(s: String) = super.follow(s) orElse { - if (upgraded) upgrade.follow(s) - else None - } - override def alternativesFor(id: String) = super.alternativesFor(id) ++ ( - if (upgraded) upgrade.alternativesFor(id) - else Nil - ) distinct - } - } - def apply(tp: Type): TypeMemberCompletion = { - if (tp eq NoType) NoTypeCompletion - else if (tp.typeSymbol.isPackageClass) new PackageCompletion(tp) - else new TypeMemberCompletion(tp) - } - def imported(tp: Type) = new ImportCompletion(tp) - } - - 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 filterNot anyRefMethodsToShow) :+ "_root_" - - def methodSignatureString(sym: Symbol) = { - IMain stripString exitingTyper(new MethodSymbolOutput(sym).methodString()) - } - - def exclude(name: String): Boolean = ( - (name contains "$") || - (excludeNames contains name) || - (excludeEndsWith exists (name endsWith _)) || - (excludeStartsWith exists (name startsWith _)) - ) - def filtered(xs: List[String]) = xs filterNot exclude distinct - - def completions(verbosity: Int) = - debugging(tp + " completions ==> ")(filtered(memberNames)) - - override def follow(s: String): Option[CompletionAware] = - debugging(tp + " -> '" + s + "' ==> ")(Some(TypeMemberCompletion(memberNamed(s).tpe)) filterNot (_ eq NoTypeCompletion)) - - override def alternativesFor(id: String): List[String] = - debugging(id + " alternatives ==> ") { - val alts = members filter (x => x.isMethod && tos(x) == id) map methodSignatureString - - if (alts.nonEmpty) "" :: alts else Nil - } - - override def toString = "%s (%d members)".format(tp, members.size) - } - - class PackageCompletion(tp: Type) extends TypeMemberCompletion(tp) { - override def excludeNames = anyref.methodNames - } - - class LiteralCompletion(lit: Literal) extends TypeMemberCompletion(lit.value.tpe) { - override def completions(verbosity: Int) = verbosity match { - case 0 => filtered(memberNames) - case _ => memberNames - } - } - - class ImportCompletion(tp: Type) extends TypeMemberCompletion(tp) { - override def completions(verbosity: Int) = verbosity match { - case 0 => filtered(members filterNot (_.isSetter) map tos) - case _ => super.completions(verbosity) - } - } - - // not for completion but for excluding - object anyref extends TypeMemberCompletion(AnyRefTpe) { } - - // the unqualified vals/defs/etc visible in the repl - object ids extends CompletionAware { - override def completions(verbosity: Int) = intp.unqualifiedIds ++ List("classOf") //, "_root_") - // now we use the compiler for everything. - override def follow(id: String): Option[CompletionAware] = { - if (!completions(0).contains(id)) - return None - - val tpe = intp typeOfExpression id - if (tpe == NoType) - return None - - def default = Some(TypeMemberCompletion(tpe)) - - // only rebinding vals in power mode for now. - if (!isReplPower) default - else intp runtimeClassAndTypeOfTerm id match { - case Some((clazz, runtimeType)) => - val sym = intp.symbolOfTerm(id) - if (sym.isStable) { - val param = new NamedParam.Untyped(id, intp valueOfTerm id orNull) - Some(TypeMemberCompletion(tpe, runtimeType, param)) - } - else default - case _ => - default - } - } - override def toString = "<repl ids> (%s)".format(completions(0).size) - } - - // user-issued wildcard imports like "import global._" or "import String._" - private def imported = intp.sessionWildcards map TypeMemberCompletion.imported - - // literal Ints, Strings, etc. - object literals extends CompletionAware { - def simpleParse(code: String): Option[Tree] = newUnitParser(code).parseStats().lastOption - def completions(verbosity: Int) = Nil - - override def follow(id: String) = simpleParse(id).flatMap { - case x: Literal => Some(new LiteralCompletion(x)) - case _ => None - } - } - - // top level packages - object rootClass extends TypeMemberCompletion(RootClass.tpe) { - override def completions(verbosity: Int) = super.completions(verbosity) :+ "_root_" - override def follow(id: String) = id match { - case "_root_" => Some(this) - case _ => super.follow(id) - } - } - // members of Predef - object predef extends TypeMemberCompletion(PredefModule.tpe) { - override def excludeEndsWith = super.excludeEndsWith ++ List("Wrapper", "ArrayOps") - override def excludeStartsWith = super.excludeStartsWith ++ List("wrap") - override def excludeNames = anyref.methodNames - - override def exclude(name: String) = super.exclude(name) || ( - (name contains "2") - ) - - override def completions(verbosity: Int) = verbosity match { - case 0 => Nil - case _ => super.completions(verbosity) - } - } - // members of scala.* - object scalalang extends PackageCompletion(ScalaPackage.tpe) { - def arityClasses = List("Product", "Tuple", "Function") - def skipArity(name: String) = arityClasses exists (x => name != x && (name startsWith x)) - override def exclude(name: String) = super.exclude(name) || ( - skipArity(name) - ) - - 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") - - 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 - // for top level unqualified, it's too noisy to let much in. - lazy val topLevelBase: List[CompletionAware] = List(ids, rootClass, predef, scalalang, javalang, literals) - def topLevel = topLevelBase ++ imported - def topLevelThreshold = 50 - - // the first tier of top level objects (doesn't include file completion) - def topLevelFor(parsed: Parsed): List[String] = { - val buf = new ListBuffer[String] - topLevel foreach { ca => - buf ++= (ca completionsFor parsed) - - if (buf.size > topLevelThreshold) - return buf.toList.sorted - } - buf.toList - } - - // the most recent result - def lastResult = Forwarder(() => ids follow intp.mostRecentVar) - - def lastResultFor(parsed: Parsed) = { - /** The logic is a little tortured right now because normally '.' is - * ignored as a delimiter, but on .<tab> it needs to be propagated. - */ - val xs = lastResult completionsFor parsed - if (parsed.isEmpty) xs map ("." + _) else xs - } - - def completer(): ScalaCompleter = new JLineTabCompletion - - /** 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 JLineTabCompletion extends ScalaCompleter { - // For recording the buffer on the last tab hit - private var lastBuf: String = "" - private var lastCursor: Int = -1 - - // Does this represent two consecutive tabs? - 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 = { - verbosity = if (isConsecutiveTabs(buf, cursor)) verbosity + 1 else 0 - repldbg(f"%ncomplete($buf, $cursor%d) last = ($lastBuf, $lastCursor%d), verbosity: $verbosity") - // we don't try lower priority completions unless higher ones return no results. - def tryCompletion(p: Parsed, completionFunction: Parsed => List[String]): Option[Candidates] = { - val winners = completionFunction(p) - if (winners.isEmpty) - return None - val newCursor = - if (winners contains "") p.cursor - else { - val advance = longestCommonPrefix(winners) - lastCursor = p.position + advance.length - lastBuf = (buf take p.position) + advance - repldbg(s"tryCompletion($p, _) lastBuf = $lastBuf, lastCursor = $lastCursor, p.position = ${p.position}") - p.position - } - - Some(Candidates(newCursor, winners)) - } - - def mkDotted = Parsed.dotted(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 tryAll = ( - lastResultCompletion - orElse tryCompletion(mkDotted, topLevelFor) - getOrElse Candidates(cursor, Nil) - ) - - /** - * This is the kickoff point for all manner of theoretically - * possible compiler unhappiness. The fault may be here or - * elsewhere, but we don't want to crash the repl regardless. - * The compiler makes it impossible to avoid catching Throwable - * with its unfortunate tendency to throw java.lang.Errors and - * AssertionErrors as the hats drop. We take two swings at it - * because there are some spots which like to throw an assertion - * once, then work after that. Yeah, what can I say. - */ - try tryAll - catch { case ex: Throwable => - repldbg("Error: complete(%s, %s) provoked".format(buf, cursor) + ex) - Candidates(cursor, - if (isReplDebug) List("<error:" + ex + ">") - else Nil - ) - } - } - } -} |