diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2013-11-22 03:12:14 -0800 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2013-11-22 03:12:14 -0800 |
commit | 2ad9666ce8863814dd5d27f1d43739e89c8c286b (patch) | |
tree | fa69d8c89c2a80cae9dac2f04ffae4c8b84bd229 /src | |
parent | 38fdd3d1300761e50036c80e9e759873ea1d067b (diff) | |
parent | 053a2744c6dfa339e004fbdab8994dc435e60b45 (diff) | |
download | scala-2ad9666ce8863814dd5d27f1d43739e89c8c286b.tar.gz scala-2ad9666ce8863814dd5d27f1d43739e89c8c286b.tar.bz2 scala-2ad9666ce8863814dd5d27f1d43739e89c8c286b.zip |
Merge pull request #3166 from skyluc/issue/completion-import-object-7280-210
Backport of
Diffstat (limited to 'src')
5 files changed, 100 insertions, 37 deletions
diff --git a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala index b2568e34bd..4a61a98921 100644 --- a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala +++ b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala @@ -6,7 +6,7 @@ package scala.tools.nsc package interactive import scala.collection.mutable.ArrayBuffer -import scala.reflect.internal.util.Position +import scala.annotation.tailrec trait ContextTrees { self: Global => @@ -29,44 +29,59 @@ trait ContextTrees { self: Global => override def toString = "ContextTree("+pos+", "+children+")" } - /** Optionally returns the smallest context that contains given `pos`, or None if none exists. + /** Returns the most precise context possible for the given `pos`. + * + * It looks for the finest ContextTree containing `pos`, and then look inside + * this ContextTree for a child ContextTree located immediately before `pos`. + * If such a child exists, returns its context, otherwise returns the context of + * the parent ContextTree. + * + * This is required to always return a context which contains the all the imports + * declared up to `pos` (see SI-7280 for a test case). + * + * Can return None if `pos` is before any valid Scala code. */ def locateContext(contexts: Contexts, pos: Position): Option[Context] = synchronized { - def locateNearestContextTree(contexts: Contexts, pos: Position, recent: Array[ContextTree]): Option[ContextTree] = { - locateContextTree(contexts, pos) match { - case Some(x) => - recent(0) = x - locateNearestContextTree(x.children, pos, recent) - case None => recent(0) match { - case null => None - case x => Some(x) - } + @tailrec + def locateFinestContextTree(context: ContextTree): ContextTree = { + if (context.pos includes pos) { + locateContextTree(context.children, pos) match { + case Some(x) => + locateFinestContextTree(x) + case None => + context + } + } else { + context } } - locateNearestContextTree(contexts, pos, new Array[ContextTree](1)) map (_.context) + locateContextTree(contexts, pos) map locateFinestContextTree map (_.context) } + /** Returns the ContextTree containing `pos`, or the ContextTree positioned just before `pos`, + * or None if `pos` is located before all ContextTrees. + */ def locateContextTree(contexts: Contexts, pos: Position): Option[ContextTree] = { if (contexts.isEmpty) None else { - val hi = contexts.length - 1 - if ((contexts(hi).pos properlyPrecedes pos) || (pos properlyPrecedes contexts(0).pos)) None - else { - def loop(lo: Int, hi: Int): Option[ContextTree] = { + @tailrec + def loop(lo: Int, hi: Int, previousSibling: Option[ContextTree]): Option[ContextTree] = { + if (pos properlyPrecedes contexts(lo).pos) + previousSibling + else if (contexts(hi).pos properlyPrecedes pos) + Some(contexts(hi)) + else { val mid = (lo + hi) / 2 val midpos = contexts(mid).pos - if ((pos precedes midpos) && (mid < hi)) - loop(lo, mid) - else if ((midpos precedes pos) && (lo < mid)) - loop(mid, hi) - else if (midpos includes pos) + if (midpos includes pos) Some(contexts(mid)) - else if (contexts(mid+1).pos includes pos) - Some(contexts(mid+1)) - else None + else if (midpos properlyPrecedes pos) + loop(mid + 1, hi, Some(contexts(mid))) + else + loop(lo, mid, previousSibling) } - loop(0, hi) } + loop(0, contexts.length - 1, None) } } diff --git a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala index 597b9012ce..1c722ea3a0 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala @@ -69,7 +69,7 @@ abstract class InteractiveTest * Override this member if you need to change the default set of executed test actions. */ protected lazy val testActions: ListBuffer[PresentationCompilerTestDef] = { - ListBuffer(new CompletionAction(compiler), new TypeAction(compiler), new HyperlinkAction(compiler)) + ListBuffer(new TypeCompletionAction(compiler), new ScopeCompletionAction(compiler), new TypeAction(compiler), new HyperlinkAction(compiler)) } /** Add new presentation compiler actions to test. Presentation compiler's test diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/AskCommand.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/AskCommand.scala index 8d446cbbf8..4f9df6808f 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/AskCommand.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/AskCommand.scala @@ -42,7 +42,7 @@ trait AskParse extends AskCommand { import compiler.Tree /** `sources` need to be entirely parsed before running the test - * (else commands such as `AskCompletionAt` may fail simply because + * (else commands such as `AskTypeCompletionAt` may fail simply because * the source's AST is not yet loaded). */ def askParse(sources: Seq[SourceFile]) { @@ -72,10 +72,10 @@ trait AskReload extends AskCommand { } /** Ask the presentation compiler for completion at a given position. */ -trait AskCompletionAt extends AskCommand { +trait AskTypeCompletionAt extends AskCommand { import compiler.Member - private[tests] def askCompletionAt(pos: Position)(implicit reporter: Reporter): Response[List[Member]] = { + private[tests] def askTypeCompletionAt(pos: Position)(implicit reporter: Reporter): Response[List[Member]] = { reporter.println("\naskTypeCompletion at " + pos.source.file.name + ((pos.line, pos.column))) ask { @@ -84,6 +84,19 @@ trait AskCompletionAt extends AskCommand { } } +/** Ask the presentation compiler for scope completion at a given position. */ +trait AskScopeCompletionAt extends AskCommand { + import compiler.Member + + private[tests] def askScopeCompletionAt(pos: Position)(implicit reporter: Reporter): Response[List[Member]] = { + reporter.println("\naskScopeCompletion at " + pos.source.file.name + ((pos.line, pos.column))) + + ask { + compiler.askScopeCompletion(pos, _) + } + } +} + /** Ask the presentation compiler for type info at a given position. */ trait AskTypeAt extends AskCommand { import compiler.Tree diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala index c8e6b6ccce..214f7a4553 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala @@ -13,19 +13,19 @@ private[tests] trait CoreTestDefs /** Ask the presentation compiler for completion at all locations * (in all sources) where the defined `marker` is found. */ - class CompletionAction(override val compiler: Global) + class TypeCompletionAction(override val compiler: Global) extends PresentationCompilerTestDef - with AskCompletionAt { + with AskTypeCompletionAt { def memberPrinter(member: compiler.Member): String = "[accessible: %5s] ".format(member.accessible) + "`" + (member.sym.toString() + member.tpe.toString()).trim() + "`" override def runTest() { - askAllSources(CompletionMarker) { pos => - askCompletionAt(pos) + askAllSources(TypeCompletionMarker) { pos => + askTypeCompletionAt(pos) } { (pos, members) => withResponseDelimiter { - reporter.println("[response] aksTypeCompletion at " + format(pos)) + reporter.println("[response] askTypeCompletion at " + format(pos)) // we skip getClass because it changed signature between 1.5 and 1.6, so there is no // universal check file that we can provide for this to work reporter.println("retrieved %d members".format(members.size)) @@ -38,6 +38,39 @@ private[tests] trait CoreTestDefs } } + /** Ask the presentation compiler for completion at all locations + * (in all sources) where the defined `marker` is found. */ + class ScopeCompletionAction(override val compiler: Global) + extends PresentationCompilerTestDef + with AskScopeCompletionAt { + + def memberPrinter(member: compiler.Member): String = + "[accessible: %5s] ".format(member.accessible) + "`" + (member.sym.toString() + member.tpe.toString()).trim() + "`" + + override def runTest() { + askAllSources(ScopeCompletionMarker) { pos => + askScopeCompletionAt(pos) + } { (pos, members) => + withResponseDelimiter { + reporter.println("[response] askScopeCompletion at " + format(pos)) + try { + // exclude members not from source (don't have position), for more focussed and self contained tests. + def eligible(sym: compiler.Symbol) = sym.pos != compiler.NoPosition + val filtered = members.filter(member => eligible(member.sym)) + reporter.println("retrieved %d members".format(filtered.size)) + compiler ask { () => + reporter.println(filtered.map(memberPrinter).sortBy(_.toString()).mkString("\n")) + } + } catch { + case t: Throwable => + t.printStackTrace() + } + + } + } + } + } + /** Ask the presentation compiler for type info at all locations * (in all sources) where the defined `marker` is found. */ class TypeAction(override val compiler: Global) @@ -61,7 +94,7 @@ private[tests] trait CoreTestDefs class HyperlinkAction(override val compiler: Global) extends PresentationCompilerTestDef with AskTypeAt - with AskCompletionAt { + with AskTypeCompletionAt { override def runTest() { askAllSources(HyperlinkMarker) { pos => @@ -97,4 +130,4 @@ private[tests] trait CoreTestDefs } } } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/TestMarker.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/TestMarker.scala index ba1722382b..8698ada4ad 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/TestMarker.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/TestMarker.scala @@ -20,7 +20,9 @@ abstract case class TestMarker(val marker: String) { TestMarker.checkForDuplicate(this) } -object CompletionMarker extends TestMarker("/*!*/") +object TypeCompletionMarker extends TestMarker("/*!*/") + +object ScopeCompletionMarker extends TestMarker("/*_*/") object TypeMarker extends TestMarker("/*?*/") |