@@ -147,15 +147,15 @@ object Interactive {
147
147
148
148
/** Get possible completions from tree at `pos`
149
149
*
150
- * @return offset and list of symbols for possible completions
150
+ * @return offset and list of (symbol, name in scope) for possible completions
151
151
*/
152
- def completions (pos : SourcePosition )(implicit ctx : Context ): (Int , List [Symbol ]) = {
152
+ def completions (pos : SourcePosition )(implicit ctx : Context ): (Int , List [( Symbol , Name ) ]) = {
153
153
val path = pathTo(ctx.compilationUnit.tpdTree, pos.pos)
154
154
computeCompletions(pos, path)(contextOfPath(path))
155
155
}
156
156
157
- private def computeCompletions (pos : SourcePosition , path : List [Tree ])(implicit ctx : Context ): (Int , List [Symbol ]) = {
158
- val completions = Scopes .newScope.openForMutations
157
+ private def computeCompletions (pos : SourcePosition , path : List [Tree ])(implicit ctx : Context ): (Int , List [( Symbol , Name ) ]) = {
158
+ val completions = new RenameAwareScope
159
159
160
160
val (completionPos, prefix, termOnly, typeOnly) = path match {
161
161
case (ref : RefTree ) :: _ =>
@@ -179,53 +179,53 @@ object Interactive {
179
179
* as completion results. However, if a user explicitly writes all '$' characters in an
180
180
* identifier, we should complete the rest.
181
181
*/
182
- def include (sym : Symbol ) =
183
- sym.name .startsWith(prefix) &&
184
- ! sym.name .toString.drop(prefix.length).contains('$' ) &&
182
+ def include (sym : Symbol , nameInScope : Name ) =
183
+ nameInScope .startsWith(prefix) &&
184
+ ! nameInScope .toString.drop(prefix.length).contains('$' ) &&
185
185
(! termOnly || sym.isTerm) &&
186
186
(! typeOnly || sym.isType)
187
187
188
- def enter (sym : Symbol ) =
189
- if (include(sym)) completions.enter(sym)
188
+ def enter (sym : Symbol , nameInScope : Name ) =
189
+ if (include(sym, nameInScope )) completions.enter(sym, nameInScope )
190
190
191
191
def add (sym : Symbol ) =
192
- if (sym.exists && ! completions.lookup(sym.name).exists) enter(sym)
192
+ if (sym.exists && ! completions.lookup(sym.name).exists) enter(sym, sym.name )
193
193
194
- def addMember (site : Type , name : Name ) =
195
- if (! completions.lookup(name ).exists)
196
- for (alt <- site.member(name).alternatives) enter(alt.symbol)
194
+ def addMember (site : Type , name : Name , nameInScope : Name ) =
195
+ if (! completions.lookup(nameInScope ).exists)
196
+ for (alt <- site.member(name).alternatives) enter(alt.symbol, nameInScope )
197
197
198
198
def accessibleMembers (site : Type , superAccess : Boolean = true ): Seq [Symbol ] = site match {
199
199
case site : NamedType if site.symbol.is(Package ) =>
200
- site.decls.toList.filter(include) // Don't look inside package members -- it's too expensive.
200
+ site.decls.toList.filter(sym => include(sym, sym.name) ) // Don't look inside package members -- it's too expensive.
201
201
case _ =>
202
202
def appendMemberSyms (name : Name , buf : mutable.Buffer [SingleDenotation ]): Unit =
203
203
try buf ++= site.member(name).alternatives
204
204
catch { case ex : TypeError => }
205
205
site.memberDenots(takeAllFilter, appendMemberSyms).collect {
206
- case mbr if include(mbr.symbol) => mbr.accessibleFrom(site, superAccess).symbol
206
+ case mbr if include(mbr.symbol, mbr.symbol.name ) => mbr.accessibleFrom(site, superAccess).symbol
207
207
case _ => NoSymbol
208
208
}.filter(_.exists)
209
209
}
210
210
211
211
def addAccessibleMembers (site : Type , superAccess : Boolean = true ): Unit =
212
- for (mbr <- accessibleMembers(site)) addMember(site, mbr.name)
212
+ for (mbr <- accessibleMembers(site)) addMember(site, mbr.name, mbr.name )
213
213
214
214
def getImportCompletions (ictx : Context ): Unit = {
215
215
implicit val ctx = ictx
216
216
val imp = ctx.importInfo
217
217
if (imp != null ) {
218
- def addImport (name : TermName ) = {
219
- addMember(imp.site, name)
220
- addMember(imp.site, name.toTypeName)
218
+ def addImport (original : TermName , nameInScope : TermName ) = {
219
+ addMember(imp.site, original, nameInScope)
220
+ addMember(imp.site, original.toTypeName, nameInScope.toTypeName)
221
+ }
222
+ imp.reverseMapping.foreachBinding { (nameInScope, original) =>
223
+ if (original != nameInScope || ! imp.excluded.contains(original))
224
+ addImport(original, nameInScope)
221
225
}
222
- // FIXME: We need to also take renamed items into account for completions,
223
- // That means we have to return list of a pairs (Name, Symbol) instead of a list
224
- // of symbols from `completions`.!=
225
- for (imported <- imp.originals if ! imp.excluded.contains(imported)) addImport(imported)
226
226
if (imp.isWildcardImport)
227
227
for (mbr <- accessibleMembers(imp.site) if ! imp.excluded.contains(mbr.name.toTermName))
228
- addMember(imp.site, mbr.name)
228
+ addMember(imp.site, mbr.name, mbr.name )
229
229
}
230
230
}
231
231
@@ -270,7 +270,7 @@ object Interactive {
270
270
case _ => getScopeCompletions(ctx)
271
271
}
272
272
273
- val completionList = completions.toList
273
+ val completionList = completions.toListWithNames
274
274
interactiv.println(i " completion with pos = $pos, prefix = $prefix, termOnly = $termOnly, typeOnly = $typeOnly = $completionList%, % " )
275
275
(completionPos, completionList)
276
276
}
@@ -284,7 +284,7 @@ object Interactive {
284
284
def addMember (name : Name , buf : mutable.Buffer [SingleDenotation ]): Unit =
285
285
buf ++= prefix.member(name).altsWith(sym =>
286
286
! exclude(sym) && sym.isAccessibleFrom(prefix)(boundaryCtx))
287
- prefix.memberDenots(completionsFilter, addMember).map(_.symbol).toList
287
+ prefix.memberDenots(completionsFilter, addMember).map(_.symbol).toList
288
288
}
289
289
else Nil
290
290
}
@@ -490,4 +490,23 @@ object Interactive {
490
490
}
491
491
}
492
492
493
+ /** A scope that tracks renames of the entered symbols.
494
+ * Useful for providing completions for renamed symbols
495
+ * in the REPL and the IDE.
496
+ */
497
+ private class RenameAwareScope extends Scopes .MutableScope {
498
+ private [this ] val renames : mutable.Map [Symbol , Name ] = mutable.Map .empty
499
+
500
+ /** Enter the symbol `sym` in this scope, recording a potential renaming. */
501
+ def enter [T <: Symbol ](sym : T , name : Name )(implicit ctx : Context ): T = {
502
+ if (name != sym.name) renames += sym -> name
503
+ newScopeEntry(name, sym)
504
+ sym
505
+ }
506
+
507
+ /** Lists the symbols in this scope along with the name associated with them. */
508
+ def toListWithNames (implicit ctx : Context ): List [(Symbol , Name )] =
509
+ toList.map(sym => (sym, renames.get(sym).getOrElse(sym.name)))
510
+ }
511
+
493
512
}
0 commit comments