summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/interactive/scala/tools/nsc/interactive/Global.scala34
-rw-r--r--src/repl/scala/tools/nsc/interpreter/PresentationCompilerCompleter.scala23
-rw-r--r--test/junit/scala/tools/nsc/interpreter/CompletionTest.scala11
3 files changed, 53 insertions, 15 deletions
diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala
index b9d06b3633..703da0b947 100644
--- a/src/interactive/scala/tools/nsc/interactive/Global.scala
+++ b/src/interactive/scala/tools/nsc/interactive/Global.scala
@@ -1159,8 +1159,14 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
/** The (possibly partial) detected that precedes the cursor */
def name: Name
def positionDelta: Int
- def matchingResults(matcher: (M, Name) => Boolean = CompletionResult.prefixMatcher): List[M] = {
- results filter (r => matcher(r, name))
+ def matchingResults(nameMatcher: (Name) => Name => Boolean = entered => candidate => candidate.startsWith(entered)): List[M] = {
+ val enteredName = if (name == nme.ERROR) nme.EMPTY else name
+ val matcher = nameMatcher(enteredName)
+ results filter { (member: Member) =>
+ val symbol = member.sym
+ def isStable = member.tpe.isStable || member.sym.isStable || member.sym.getterIn(member.sym.owner).isStable
+ member.accessible && !symbol.isConstructor && (name.isEmpty || matcher(member.sym.name) && (symbol.name.isTermName == name.isTermName || name.isTypeName && isStable))
+ }
}
}
object CompletionResult {
@@ -1176,12 +1182,24 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
override def positionDelta = 0
}
- val prefixMatcher = (member: Member, name: Name) => {
- val symbol = member.sym
- val prefix = if (name == nme.ERROR) nme.EMPTY else name
- def isStable = member.tpe.isStable || member.sym.isStable || member.sym.getterIn(member.sym.owner).isStable
- val nameMatches = !symbol.isConstructor && (prefix.isEmpty || symbol.name.startsWith(prefix) && (symbol.name.isTermName == prefix.isTermName || prefix.isTypeName && isStable))
- nameMatches && member.accessible
+ private val CamelRegex = "([A-Z][^A-Z]*)".r
+ private def camelComponents(s: String): List[String] = {
+ CamelRegex.findAllIn("X" + s).toList match { case head :: tail => head.drop(1) :: tail; case Nil => Nil }
+ }
+ def camelMatch(entered: Name): Name => Boolean = {
+ val chunks: List[String] = camelComponents(entered.toString)
+
+ (candidate: Name) => {
+ val candidateChunks = camelComponents(candidate.toString)
+ val exactCamelMatch =
+ (chunks corresponds candidateChunks.take(chunks.length))((x, y) => y.startsWith(x))
+ def beanCamelMatch = candidateChunks match {
+ case ("get" | "is") :: tail =>
+ (chunks corresponds tail.take(chunks.length))((x, y) => y.toLowerCase.startsWith(x.toLowerCase))
+ case _ => false
+ }
+ exactCamelMatch || beanCamelMatch
+ }
}
}
diff --git a/src/repl/scala/tools/nsc/interpreter/PresentationCompilerCompleter.scala b/src/repl/scala/tools/nsc/interpreter/PresentationCompilerCompleter.scala
index f6b6ff440b..7cff24cfa3 100644
--- a/src/repl/scala/tools/nsc/interpreter/PresentationCompilerCompleter.scala
+++ b/src/repl/scala/tools/nsc/interpreter/PresentationCompilerCompleter.scala
@@ -77,14 +77,23 @@ class PresentationCompilerCompleter(intp: IMain) extends Completion with ScalaCo
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(isInterpreterWrapperMember)
+ 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 {
- 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)
- }
+ // regular completion
+ val memberCompletions: List[String] = matching.map(_.symNameDropLocal.decoded).distinct.sorted
+ Candidates(cursor + r.positionDelta, memberCompletions)
}
}
lastCommonPrefixCompletion =
diff --git a/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala b/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala
index 75ab4c605f..a55a3c66c0 100644
--- a/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala
+++ b/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala
@@ -52,6 +52,17 @@ class CompletionTest {
}
@Test
+ def camelCompletions(): Unit = {
+ val intp = newIMain()
+ val completer = new PresentationCompilerCompleter(intp)
+ checkExact(completer, "object O { def theCatSatOnTheMat = 1 }; import O._; tCSO")("theCatSatOnTheMat")
+ checkExact(completer, "object O { def getBlerganator = 1 }; import O._; blerga")("getBlerganator")
+ checkExact(completer, "object O { def xxxxYyyyyZzzz = 1; def xxxxYyZeee = 1 }; import O._; xYZ")("", "xxxxYyyyyZzzz", "xxxxYyZeee")
+ checkExact(completer, "object O { def xxxxYyyyyZzzz = 1; def xxxxYyyyyZeee = 1 }; import O._; xYZ")("xxxxYyyyyZzzz", "xxxxYyyyyZeee")
+ checkExact(completer, "object O { class AbstractMetaFactoryFactory }; new O.AMFF")("AbstractMetaFactoryFactory")
+ }
+
+ @Test
def previousLineCompletions(): Unit = {
val intp = newIMain()
intp.interpret("class C { val x_y_z = 42 }")