@@ -11,6 +11,7 @@ import Contexts._, Flags._, Names._, NameOps._, Symbols._, SymDenotations._, Tre
1111import util .Positions ._ , util .SourcePosition
1212import core .Denotations .SingleDenotation
1313import NameKinds .SimpleNameKind
14+ import config .Printers .interactiv
1415
1516/** High-level API to get information out of typed trees, designed to be used by IDEs.
1617 *
@@ -36,6 +37,7 @@ object Interactive {
3637 if (path.isEmpty) NoType
3738 else path.head.tpe
3839 }
40+
3941 /** The closest enclosing tree with a symbol containing position `pos`.
4042 */
4143 def enclosingTree (trees : List [SourceTree ], pos : SourcePosition )(implicit ctx : Context ): Tree =
@@ -95,6 +97,7 @@ object Interactive {
9597 *
9698 * @return offset and list of symbols for possible completions
9799 */
100+ // deprecated
98101 def completions (trees : List [SourceTree ], pos : SourcePosition )(implicit ctx : Context ): (Int , List [Symbol ]) = {
99102 val path = pathTo(trees, pos)
100103 val boundary = enclosingDefinitionInPath(path).symbol
@@ -122,6 +125,104 @@ object Interactive {
122125 .getOrElse((0 , Nil ))
123126 }
124127
128+ /** Get possible completions from tree at `pos`
129+ *
130+ * @return offset and list of symbols for possible completions
131+ */
132+ def completions (pos : SourcePosition )(implicit ctx : Context ): (Int , List [Symbol ]) = {
133+ val path = pathTo(ctx.compilationUnit.tpdTree, pos.pos)
134+ computeCompletions(pos, path)(contextOfPath(path))
135+ }
136+
137+ private def computeCompletions (pos : SourcePosition , path : List [Tree ])(implicit ctx : Context ): (Int , List [Symbol ]) = {
138+ val completions = Scopes .newScope.openForMutations
139+
140+ val (completionPos, prefix) = path match {
141+ case (ref : RefTree ) :: _ =>
142+ (ref.pos.point, ref.name.toString.take(ref.pos.end - ref.pos.point - 1 ))
143+ case (id @ Ident (name)) :: _ =>
144+ if .pos
145+ getScopeCompletions(ctx)
146+ id.pos.point
147+ case _ =>
148+ getScopeCompletions(ctx)
149+ 0
150+
151+ def add (sym : Symbol ) =
152+ if (sym.exists && ! completions.lookup(sym.name).exists)
153+ completions.enter(sym)
154+
155+ def addMember (site : Type , name : Name ) =
156+ if (! completions.lookup(name).exists)
157+ for (alt <- site.member(name).alternatives)
158+ completions.enter(alt.symbol)
159+
160+ def allMembers (site : Type , superAccess : Boolean = true ) =
161+ site.membersBasedOnFlags(EmptyFlags , EmptyFlags ).map(_.accessibleFrom(site, superAccess))
162+
163+ def getImportCompletions (ictx : Context ): Unit = {
164+ implicit val ctx = ictx
165+ val imp = ctx.importInfo
166+ if (imp != null ) {
167+ def addImport (name : TermName ) = {
168+ addMember(imp.site, name)
169+ addMember(imp.site, name.toTypeName)
170+ }
171+ for (renamed <- imp.reverseMapping.keys) addImport(renamed)
172+ for (imported <- imp.originals if ! imp.excluded.contains(imported)) addImport(imported)
173+ if (imp.isWildcardImport)
174+ for (mbr <- allMembers(imp.site) if ! imp.excluded.contains(mbr.name.toTermName))
175+ addMember(imp.site, mbr.name)
176+ }
177+ }
178+
179+ def getScopeCompletions (ictx : Context ): Unit = {
180+ implicit val ctx = ictx
181+
182+ if (ctx.owner.isClass) {
183+ for (sym <- ctx.owner.info.decls) // decls in same class first
184+ addMember(ctx.owner.thisType, sym.name)
185+ if (! ctx.owner.is(Package )) {
186+ for (mbr <- allMembers(ctx.owner.thisType)) // all other members second
187+ addMember(ctx.owner.thisType, mbr.name)
188+ ctx.owner.asClass.classInfo.selfInfo match {
189+ case selfSym : Symbol => add(selfSym)
190+ case _ =>
191+ }
192+ }
193+ }
194+ else if (ctx.scope != null ) ctx.scope.foreach(add)
195+
196+ getImportCompletions(ctx)
197+
198+ var outer = ctx.outer
199+ while ((outer.owner `eq` ctx.owner) && (outer.scope `eq` ctx.scope)) {
200+ getImportCompletions(outer)
201+ outer = outer.outer
202+ }
203+ if (outer `ne` NoContext ) getScopeCompletions(outer)
204+ }
205+
206+ def getMemberCompletions (site : Type ): Unit = {
207+ for (mbr <- allMembers(site)) addMember(site, mbr.name)
208+ }
209+
210+ val completionPos = path match {
211+ case (sel @ Select (qual, name)) :: _ =>
212+ getMemberCompletions(qual.tpe)
213+ // When completing "`a.foo`, return the members of `a`
214+ sel.pos.point
215+ case (id : Ident ) :: _ =>
216+ getScopeCompletions(ctx)
217+ id.pos.point
218+ case _ =>
219+ getScopeCompletions(ctx)
220+ 0
221+ }
222+ interactiv.println(i " completion = ${completions.toList}%, % " )
223+ (completionPos, completions.toList)
224+ }
225+
125226 /** Possible completions of members of `prefix` which are accessible when called inside `boundary` */
126227 def completions (prefix : Type , boundary : Symbol )(implicit ctx : Context ): List [Symbol ] =
127228 safely {
@@ -131,7 +232,7 @@ object Interactive {
131232 def addMember (name : Name , buf : mutable.Buffer [SingleDenotation ]): Unit =
132233 buf ++= prefix.member(name).altsWith(d =>
133234 ! exclude(d) && d.symbol.isAccessibleFrom(prefix)(boundaryCtx))
134- prefix.memberDenots(completionsFilter, addMember).map(_.symbol).toList
235+ prefix.memberDenots(completionsFilter, addMember).map(_.symbol).toList
135236 }
136237 else Nil
137238 }
@@ -203,14 +304,72 @@ object Interactive {
203304 */
204305 def pathTo (trees : List [SourceTree ], pos : SourcePosition )(implicit ctx : Context ): List [Tree ] =
205306 trees.find(_.pos.contains(pos)) match {
206- case Some (tree) =>
207- // FIXME: We shouldn't need a cast. Change NavigateAST.pathTo to return a List of Tree?
208- val path = NavigateAST .pathTo(pos.pos, tree.tree, skipZeroExtent = true ). asInstanceOf [ List [untpd. Tree ]]
307+ case Some (tree) => pathTo(tree.tree, pos.pos)
308+ case None => Nil
309+ }
209310
210- path.dropWhile(! _.hasType).asInstanceOf [List [tpd.Tree ]]
211- case None =>
212- Nil
311+ def pathTo (tree : Tree , pos : Position )(implicit ctx : Context ): List [Tree ] =
312+ if (tree.pos.contains(pos)) {
313+ // FIXME: We shouldn't need a cast. Change NavigateAST.pathTo to return a List of Tree?
314+ val path = NavigateAST .pathTo(pos, tree, skipZeroExtent = true ).asInstanceOf [List [untpd.Tree ]]
315+ path.dropWhile(! _.hasType).asInstanceOf [List [tpd.Tree ]]
213316 }
317+ else Nil
318+
319+ def contextOfStat (stats : List [Tree ], stat : Tree , exprOwner : Symbol , ctx : Context ): Context = stats match {
320+ case Nil =>
321+ ctx
322+ case first :: _ if first eq stat =>
323+ ctx.exprContext(stat, exprOwner)
324+ case (imp : Import ) :: rest =>
325+ contextOfStat(rest, stat, exprOwner, ctx.importContext(imp, imp.symbol(ctx)))
326+ case _ =>
327+ ctx
328+ }
329+
330+ def contextOfPath (path : List [Tree ])(implicit ctx : Context ): Context = path match {
331+ case Nil | _ :: Nil =>
332+ ctx.run.runContext.fresh.setCompilationUnit(ctx.compilationUnit)
333+ case nested :: encl :: rest =>
334+ import typer .Typer ._
335+ val outer = contextOfPath(encl :: rest)
336+ encl match {
337+ case tree @ PackageDef (pkg, stats) =>
338+ assert(tree.symbol.exists)
339+ if (nested `eq` pkg) outer
340+ else contextOfStat(stats, nested, pkg.symbol.moduleClass, outer.packageContext(tree, tree.symbol))
341+ case tree : DefDef =>
342+ assert(tree.symbol.exists)
343+ val localCtx = outer.localContext(tree, tree.symbol).setNewScope
344+ for (tparam <- tree.tparams) localCtx.enter(tparam.symbol)
345+ for (vparams <- tree.vparamss; vparam <- vparams) localCtx.enter(vparam.symbol)
346+ // Note: this overapproximates visibility a bit, since value parameters are only visible
347+ // in subsequent parameter sections
348+ localCtx
349+ case tree : MemberDef =>
350+ assert(tree.symbol.exists)
351+ outer.localContext(tree, tree.symbol)
352+ case tree @ Block (stats, expr) =>
353+ val localCtx = outer.fresh.setNewScope
354+ stats.foreach {
355+ case stat : MemberDef => localCtx.enter(stat.symbol)
356+ case _ =>
357+ }
358+ contextOfStat(stats, nested, ctx.owner, localCtx)
359+ case tree @ CaseDef (pat, guard, rhs) if nested `eq` rhs =>
360+ val localCtx = outer.fresh.setNewScope
361+ pat.foreachSubTree {
362+ case bind : Bind => localCtx.enter(bind.symbol)
363+ case _ =>
364+ }
365+ localCtx
366+ case tree @ Template (constr, parents, self, _) =>
367+ if ((constr :: self :: parents).exists(nested `eq` _)) ctx
368+ else contextOfStat(tree.body, nested, tree.symbol, outer.inClassContext(self.symbol))
369+ case _ =>
370+ outer
371+ }
372+ }
214373
215374 /** The first tree in the path that is a definition. */
216375 def enclosingDefinitionInPath (path : List [Tree ])(implicit ctx : Context ): Tree =
0 commit comments