
scala.tools.nsc.interpreter.PresentationCompilerCompleter.scala Maven / Gradle / Ivy
/* NSC -- new Scala compiler
* Copyright 2005-2013 LAMP/EPFL
* @author Martin Odersky
*/
package scala.tools.nsc.interpreter
import scala.reflect.internal.util.StringOps
import scala.tools.nsc.interpreter.Completion.Candidates
import scala.util.control.NonFatal
class PresentationCompilerCompleter(intp: IMain) extends Completion {
import PresentationCompilerCompleter._
import intp.{PresentationCompileResult => Result}
private type Handler = Result => Candidates
private var lastRequest = NoRequest
private var tabCount = 0
private var lastCommonPrefixCompletion: Option[String] = None
def resetVerbosity(): Unit = { tabCount = 0 ; lastRequest = NoRequest }
// A convenience for testing
def complete(before: String, after: String = ""): Candidates = complete(before + after, before.length)
override def complete(buf: String, cursor: Int): Candidates = {
val request = Request(buf, cursor)
if (request == lastRequest)
tabCount += 1
else {
tabCount = 0
lastRequest = request
}
// secret handshakes
val slashPrint = """.*// *print *""".r
val slashTypeAt = """.*// *typeAt *(\d+) *(\d+) *""".r
val Cursor = IMain.DummyCursorFragment + " "
def print(result: Result) = {
val offset = result.preambleLength
val pos1 = result.unit.source.position(offset).withEnd(offset + buf.length)
import result.compiler._
val tree = new Locator(pos1) locateIn result.unit.body match {
case Template(_, _, constructor :: (rest :+ last)) => if (rest.isEmpty) last else Block(rest, last)
case t => t
}
val printed = showCode(tree) + " // : " + tree.tpe.safeToString
Candidates(cursor, "" :: printed :: Nil)
}
def typeAt(result: Result, start: Int, end: Int) = {
val tpString = result.compiler.exitingTyper(result.typedTreeAt(buf, start, end).tpe.toString)
Candidates(cursor, "" :: tpString :: Nil)
}
def candidates(result: Result): Candidates = {
import result.compiler._
import CompletionResult._
def defStringCandidates(matching: List[Member], name: Name): Candidates = {
val defStrings = for {
member <- matching
if member.symNameDropLocal == name
sym <- member.sym.alternatives
sugared = sym.sugaredSymbolOrSelf
} yield {
val tp = member.prefix memberType sym
sugared.defStringSeenAs(tp)
}
Candidates(cursor, "" :: defStrings.distinct)
}
val found = result.completionsAt(cursor) match {
case NoResults => Completion.NoCandidates
case r =>
def shouldHide(m: Member): Boolean = {
val isUniversal = definitions.isUniversalMember(m.sym)
def viaUniversalExtensionMethod = m match {
case t: TypeMember if t.implicitlyAdded && t.viaView.info.params.head.info.bounds.isEmptyBounds => true
case _ => false
}
(
isUniversal && nme.isReplWrapperName(m.prefix.typeSymbol.name)
|| isUniversal && tabCount == 0 && r.name.isEmpty
|| viaUniversalExtensionMethod && tabCount == 0 && r.name.isEmpty
)
}
val matching = r.matchingResults().filterNot(shouldHide)
val tabAfterCommonPrefixCompletion = lastCommonPrefixCompletion.contains(buf.substring(0, cursor)) && matching.exists(_.symNameDropLocal == r.name)
val doubleTab = tabCount > 0 && matching.forall(_.symNameDropLocal == r.name)
if (tabAfterCommonPrefixCompletion || doubleTab) defStringCandidates(matching, r.name)
else if (matching.isEmpty) {
// Lenient matching based on camel case and on eliding JavaBean "get" / "is" boilerplate
val camelMatches: List[Member] = r.matchingResults(CompletionResult.camelMatch(_)).filterNot(shouldHide)
val memberCompletions = camelMatches.map(_.symNameDropLocal.decoded).distinct.sorted
def allowCompletion = (
(memberCompletions.size == 1)
|| CompletionResult.camelMatch(r.name)(r.name.newName(StringOps.longestCommonPrefix(memberCompletions)))
)
if (memberCompletions.isEmpty) Completion.NoCandidates
else if (allowCompletion) Candidates(cursor - r.positionDelta, memberCompletions)
else Candidates(cursor, "" :: memberCompletions)
} else if (matching.nonEmpty && matching.forall(_.symNameDropLocal == r.name))
Completion.NoCandidates // don't offer completion if the only option has been fully typed already
else {
// regular completion
val memberCompletions: List[String] = matching.map(_.symNameDropLocal.decoded).distinct.sorted
Candidates(cursor - r.positionDelta, memberCompletions)
}
}
lastCommonPrefixCompletion =
if (found != Completion.NoCandidates && buf.length >= found.cursor)
Some(buf.substring(0, found.cursor) + StringOps.longestCommonPrefix(found.candidates))
else
None
found
}
val buf1 = buf.patch(cursor, Cursor, 0)
try {
intp.presentationCompile(buf1) match {
case Left(_) => Completion.NoCandidates
case Right(result) => try {
buf match {
case slashPrint() if cursor == buf.length => print(result)
case slashTypeAt(start, end) if cursor == buf.length => typeAt(result, start.toInt, end.toInt)
case _ => candidates(result)
}
} finally result.cleanup()
}
} catch {
case NonFatal(e) =>
if (isReplDebug) e.printStackTrace()
Completion.NoCandidates
}
}
}
object PresentationCompilerCompleter {
private case class Request(line: String, cursor: Int)
private val NoRequest = Request("", -1)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy