@@ -11,6 +11,7 @@ import Contexts._, Flags._, Names._, NameOps._, Symbols._, SymDenotations._, Tre
11
11
import util .Positions ._ , util .SourcePosition
12
12
import core .Denotations .SingleDenotation
13
13
import NameKinds .SimpleNameKind
14
+ import config .Printers .interactiv
14
15
15
16
/** High-level API to get information out of typed trees, designed to be used by IDEs.
16
17
*
@@ -36,6 +37,7 @@ object Interactive {
36
37
if (path.isEmpty) NoType
37
38
else path.head.tpe
38
39
}
40
+
39
41
/** The closest enclosing tree with a symbol containing position `pos`.
40
42
*/
41
43
def enclosingTree (trees : List [SourceTree ], pos : SourcePosition )(implicit ctx : Context ): Tree =
@@ -95,6 +97,7 @@ object Interactive {
95
97
*
96
98
* @return offset and list of symbols for possible completions
97
99
*/
100
+ // deprecated
98
101
def completions (trees : List [SourceTree ], pos : SourcePosition )(implicit ctx : Context ): (Int , List [Symbol ]) = {
99
102
val path = pathTo(trees, pos)
100
103
val boundary = enclosingDefinitionInPath(path).symbol
@@ -122,6 +125,104 @@ object Interactive {
122
125
.getOrElse((0 , Nil ))
123
126
}
124
127
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
+
125
226
/** Possible completions of members of `prefix` which are accessible when called inside `boundary` */
126
227
def completions (prefix : Type , boundary : Symbol )(implicit ctx : Context ): List [Symbol ] =
127
228
safely {
@@ -131,7 +232,7 @@ object Interactive {
131
232
def addMember (name : Name , buf : mutable.Buffer [SingleDenotation ]): Unit =
132
233
buf ++= prefix.member(name).altsWith(d =>
133
234
! exclude(d) && d.symbol.isAccessibleFrom(prefix)(boundaryCtx))
134
- prefix.memberDenots(completionsFilter, addMember).map(_.symbol).toList
235
+ prefix.memberDenots(completionsFilter, addMember).map(_.symbol).toList
135
236
}
136
237
else Nil
137
238
}
@@ -203,14 +304,72 @@ object Interactive {
203
304
*/
204
305
def pathTo (trees : List [SourceTree ], pos : SourcePosition )(implicit ctx : Context ): List [Tree ] =
205
306
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
+ }
209
310
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 ]]
213
316
}
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
+ }
214
373
215
374
/** The first tree in the path that is a definition. */
216
375
def enclosingDefinitionInPath (path : List [Tree ])(implicit ctx : Context ): Tree =
0 commit comments