@@ -103,15 +103,15 @@ object Interactive {
103
103
104
104
/** Get possible completions from tree at `pos`
105
105
*
106
- * @return offset and list of symbols for possible completions
106
+ * @return offset and list of (symbol, name in scope) for possible completions
107
107
*/
108
- def completions (pos : SourcePosition )(implicit ctx : Context ): (Int , List [Symbol ]) = {
108
+ def completions (pos : SourcePosition )(implicit ctx : Context ): (Int , List [( Symbol , Name ) ]) = {
109
109
val path = pathTo(ctx.compilationUnit.tpdTree, pos.pos)
110
110
computeCompletions(pos, path)(contextOfPath(path))
111
111
}
112
112
113
- private def computeCompletions (pos : SourcePosition , path : List [Tree ])(implicit ctx : Context ): (Int , List [Symbol ]) = {
114
- val completions = Scopes .newScope.openForMutations
113
+ private def computeCompletions (pos : SourcePosition , path : List [Tree ])(implicit ctx : Context ): (Int , List [( Symbol , Name ) ]) = {
114
+ val completions = new RenameAwareScope
115
115
116
116
val (completionPos, prefix, termOnly, typeOnly) = path match {
117
117
case (ref : RefTree ) :: _ =>
@@ -135,53 +135,53 @@ object Interactive {
135
135
* as completion results. However, if a user explicitly writes all '$' characters in an
136
136
* identifier, we should complete the rest.
137
137
*/
138
- def include (sym : Symbol ) =
139
- sym.name .startsWith(prefix) &&
140
- ! sym.name .toString.drop(prefix.length).contains('$' ) &&
138
+ def include (sym : Symbol , nameInScope : Name ) =
139
+ nameInScope .startsWith(prefix) &&
140
+ ! nameInScope .toString.drop(prefix.length).contains('$' ) &&
141
141
(! termOnly || sym.isTerm) &&
142
142
(! typeOnly || sym.isType)
143
143
144
- def enter (sym : Symbol ) =
145
- if (include(sym)) completions.enter(sym)
144
+ def enter (sym : Symbol , nameInScope : Name ) =
145
+ if (include(sym, nameInScope )) completions.enter(sym, nameInScope )
146
146
147
147
def add (sym : Symbol ) =
148
- if (sym.exists && ! completions.lookup(sym.name).exists) enter(sym)
148
+ if (sym.exists && ! completions.lookup(sym.name).exists) enter(sym, sym.name )
149
149
150
- def addMember (site : Type , name : Name ) =
151
- if (! completions.lookup(name ).exists)
152
- for (alt <- site.member(name).alternatives) enter(alt.symbol)
150
+ def addMember (site : Type , name : Name , nameInScope : Name ) =
151
+ if (! completions.lookup(nameInScope ).exists)
152
+ for (alt <- site.member(name).alternatives) enter(alt.symbol, nameInScope )
153
153
154
154
def accessibleMembers (site : Type , superAccess : Boolean = true ): Seq [Symbol ] = site match {
155
155
case site : NamedType if site.symbol.is(Package ) =>
156
- site.decls.toList.filter(include) // Don't look inside package members -- it's too expensive.
156
+ site.decls.toList.filter(sym => include(sym, sym.name) ) // Don't look inside package members -- it's too expensive.
157
157
case _ =>
158
158
def appendMemberSyms (name : Name , buf : mutable.Buffer [SingleDenotation ]): Unit =
159
159
try buf ++= site.member(name).alternatives
160
160
catch { case ex : TypeError => }
161
161
site.memberDenots(takeAllFilter, appendMemberSyms).collect {
162
- case mbr if include(mbr.symbol) => mbr.accessibleFrom(site, superAccess).symbol
162
+ case mbr if include(mbr.symbol, mbr.symbol.name ) => mbr.accessibleFrom(site, superAccess).symbol
163
163
case _ => NoSymbol
164
164
}.filter(_.exists)
165
165
}
166
166
167
167
def addAccessibleMembers (site : Type , superAccess : Boolean = true ): Unit =
168
- for (mbr <- accessibleMembers(site)) addMember(site, mbr.name)
168
+ for (mbr <- accessibleMembers(site)) addMember(site, mbr.name, mbr.name )
169
169
170
170
def getImportCompletions (ictx : Context ): Unit = {
171
171
implicit val ctx = ictx
172
172
val imp = ctx.importInfo
173
173
if (imp != null ) {
174
- def addImport (name : TermName ) = {
175
- addMember(imp.site, name)
176
- addMember(imp.site, name.toTypeName)
174
+ def addImport (original : TermName , nameInScope : TermName ) = {
175
+ addMember(imp.site, original, nameInScope)
176
+ addMember(imp.site, original.toTypeName, nameInScope.toTypeName)
177
+ }
178
+ imp.reverseMapping.foreachBinding { (nameInScope, original) =>
179
+ if (original != nameInScope || ! imp.excluded.contains(original))
180
+ addImport(original, nameInScope)
177
181
}
178
- // FIXME: We need to also take renamed items into account for completions,
179
- // That means we have to return list of a pairs (Name, Symbol) instead of a list
180
- // of symbols from `completions`.!=
181
- for (imported <- imp.originals if ! imp.excluded.contains(imported)) addImport(imported)
182
182
if (imp.isWildcardImport)
183
183
for (mbr <- accessibleMembers(imp.site) if ! imp.excluded.contains(mbr.name.toTermName))
184
- addMember(imp.site, mbr.name)
184
+ addMember(imp.site, mbr.name, mbr.name )
185
185
}
186
186
}
187
187
@@ -226,7 +226,7 @@ object Interactive {
226
226
case _ => getScopeCompletions(ctx)
227
227
}
228
228
229
- val completionList = completions.toList
229
+ val completionList = completions.toListWithNames
230
230
interactiv.println(i " completion with pos = $pos, prefix = $prefix, termOnly = $termOnly, typeOnly = $typeOnly = $completionList%, % " )
231
231
(completionPos, completionList)
232
232
}
@@ -240,7 +240,7 @@ object Interactive {
240
240
def addMember (name : Name , buf : mutable.Buffer [SingleDenotation ]): Unit =
241
241
buf ++= prefix.member(name).altsWith(sym =>
242
242
! exclude(sym) && sym.isAccessibleFrom(prefix)(boundaryCtx))
243
- prefix.memberDenots(completionsFilter, addMember).map(_.symbol).toList
243
+ prefix.memberDenots(completionsFilter, addMember).map(_.symbol).toList
244
244
}
245
245
else Nil
246
246
}
@@ -413,4 +413,24 @@ object Interactive {
413
413
/** The first tree in the path that is a definition. */
414
414
def enclosingDefinitionInPath (path : List [Tree ])(implicit ctx : Context ): Tree =
415
415
path.find(_.isInstanceOf [DefTree ]).getOrElse(EmptyTree )
416
+
417
+ /** A scope that tracks renames of the entered symbols.
418
+ * Useful for providing completions for renamed symbols
419
+ * in the REPL and the IDE.
420
+ */
421
+ private class RenameAwareScope extends Scopes .MutableScope {
422
+ private [this ] val renames : mutable.Map [Symbol , Name ] = mutable.Map .empty
423
+
424
+ /** Enter the symbol `sym` in this scope, recording a potential renaming. */
425
+ def enter [T <: Symbol ](sym : T , name : Name )(implicit ctx : Context ): T = {
426
+ if (name != sym.name) renames += sym -> name
427
+ newScopeEntry(name, sym)
428
+ sym
429
+ }
430
+
431
+ /** Lists the symbols in this scope along with the name associated with them. */
432
+ def toListWithNames (implicit ctx : Context ): List [(Symbol , Name )] =
433
+ toList.map(sym => (sym, renames.get(sym).getOrElse(sym.name)))
434
+ }
435
+
416
436
}
0 commit comments