@@ -96,15 +96,15 @@ object Interactive {
96
96
97
97
/** Get possible completions from tree at `pos`
98
98
*
99
- * @return offset and list of symbols for possible completions
99
+ * @return offset and list of (symbol, name in scope) for possible completions
100
100
*/
101
- def completions (pos : SourcePosition )(implicit ctx : Context ): (Int , List [Symbol ]) = {
101
+ def completions (pos : SourcePosition )(implicit ctx : Context ): (Int , List [( Symbol , Name ) ]) = {
102
102
val path = pathTo(ctx.compilationUnit.tpdTree, pos.pos)
103
103
computeCompletions(pos, path)(contextOfPath(path))
104
104
}
105
105
106
- private def computeCompletions (pos : SourcePosition , path : List [Tree ])(implicit ctx : Context ): (Int , List [Symbol ]) = {
107
- val completions = Scopes .newScope.openForMutations
106
+ private def computeCompletions (pos : SourcePosition , path : List [Tree ])(implicit ctx : Context ): (Int , List [( Symbol , Name ) ]) = {
107
+ val completions = new RenameAwareScope
108
108
109
109
val (completionPos, prefix, termOnly, typeOnly) = path match {
110
110
case (ref : RefTree ) :: _ =>
@@ -128,53 +128,53 @@ object Interactive {
128
128
* as completion results. However, if a user explicitly writes all '$' characters in an
129
129
* identifier, we should complete the rest.
130
130
*/
131
- def include (sym : Symbol ) =
132
- sym.name .startsWith(prefix) &&
133
- ! sym.name .toString.drop(prefix.length).contains('$' ) &&
131
+ def include (sym : Symbol , nameInScope : Name ) =
132
+ nameInScope .startsWith(prefix) &&
133
+ ! nameInScope .toString.drop(prefix.length).contains('$' ) &&
134
134
(! termOnly || sym.isTerm) &&
135
135
(! typeOnly || sym.isType)
136
136
137
- def enter (sym : Symbol ) =
138
- if (include(sym)) completions.enter(sym)
137
+ def enter (sym : Symbol , nameInScope : Name ) =
138
+ if (include(sym, nameInScope )) completions.enter(sym, nameInScope )
139
139
140
140
def add (sym : Symbol ) =
141
- if (sym.exists && ! completions.lookup(sym.name).exists) enter(sym)
141
+ if (sym.exists && ! completions.lookup(sym.name).exists) enter(sym, sym.name )
142
142
143
- def addMember (site : Type , name : Name ) =
144
- if (! completions.lookup(name ).exists)
145
- for (alt <- site.member(name).alternatives) enter(alt.symbol)
143
+ def addMember (site : Type , name : Name , nameInScope : Name ) =
144
+ if (! completions.lookup(nameInScope ).exists)
145
+ for (alt <- site.member(name).alternatives) enter(alt.symbol, nameInScope )
146
146
147
147
def accessibleMembers (site : Type , superAccess : Boolean = true ): Seq [Symbol ] = site match {
148
148
case site : NamedType if site.symbol.is(Package ) =>
149
- site.decls.toList.filter(include) // Don't look inside package members -- it's too expensive.
149
+ site.decls.toList.filter(sym => include(sym, sym.name) ) // Don't look inside package members -- it's too expensive.
150
150
case _ =>
151
151
def appendMemberSyms (name : Name , buf : mutable.Buffer [SingleDenotation ]): Unit =
152
152
try buf ++= site.member(name).alternatives
153
153
catch { case ex : TypeError => }
154
154
site.memberDenots(takeAllFilter, appendMemberSyms).collect {
155
- case mbr if include(mbr.symbol) => mbr.accessibleFrom(site, superAccess).symbol
155
+ case mbr if include(mbr.symbol, mbr.symbol.name ) => mbr.accessibleFrom(site, superAccess).symbol
156
156
case _ => NoSymbol
157
157
}.filter(_.exists)
158
158
}
159
159
160
160
def addAccessibleMembers (site : Type , superAccess : Boolean = true ): Unit =
161
- for (mbr <- accessibleMembers(site)) addMember(site, mbr.name)
161
+ for (mbr <- accessibleMembers(site)) addMember(site, mbr.name, mbr.name )
162
162
163
163
def getImportCompletions (ictx : Context ): Unit = {
164
164
implicit val ctx = ictx
165
165
val imp = ctx.importInfo
166
166
if (imp != null ) {
167
- def addImport (name : TermName ) = {
168
- addMember(imp.site, name)
169
- addMember(imp.site, name.toTypeName)
167
+ def addImport (original : TermName , nameInScope : TermName ) = {
168
+ addMember(imp.site, original, nameInScope)
169
+ addMember(imp.site, original.toTypeName, nameInScope.toTypeName)
170
+ }
171
+ imp.reverseMapping.foreachBinding { (nameInScope, original) =>
172
+ if (original != nameInScope || ! imp.excluded.contains(original))
173
+ addImport(original, nameInScope)
170
174
}
171
- // FIXME: We need to also take renamed items into account for completions,
172
- // That means we have to return list of a pairs (Name, Symbol) instead of a list
173
- // of symbols from `completions`.!=
174
- for (imported <- imp.originals if ! imp.excluded.contains(imported)) addImport(imported)
175
175
if (imp.isWildcardImport)
176
176
for (mbr <- accessibleMembers(imp.site) if ! imp.excluded.contains(mbr.name.toTermName))
177
- addMember(imp.site, mbr.name)
177
+ addMember(imp.site, mbr.name, mbr.name )
178
178
}
179
179
}
180
180
@@ -219,7 +219,7 @@ object Interactive {
219
219
case _ => getScopeCompletions(ctx)
220
220
}
221
221
222
- val completionList = completions.toList
222
+ val completionList = completions.toListWithNames
223
223
interactiv.println(i " completion with pos = $pos, prefix = $prefix, termOnly = $termOnly, typeOnly = $typeOnly = $completionList%, % " )
224
224
(completionPos, completionList)
225
225
}
@@ -233,7 +233,7 @@ object Interactive {
233
233
def addMember (name : Name , buf : mutable.Buffer [SingleDenotation ]): Unit =
234
234
buf ++= prefix.member(name).altsWith(sym =>
235
235
! exclude(sym) && sym.isAccessibleFrom(prefix)(boundaryCtx))
236
- prefix.memberDenots(completionsFilter, addMember).map(_.symbol).toList
236
+ prefix.memberDenots(completionsFilter, addMember).map(_.symbol).toList
237
237
}
238
238
else Nil
239
239
}
@@ -377,4 +377,24 @@ object Interactive {
377
377
/** The first tree in the path that is a definition. */
378
378
def enclosingDefinitionInPath (path : List [Tree ])(implicit ctx : Context ): Tree =
379
379
path.find(_.isInstanceOf [DefTree ]).getOrElse(EmptyTree )
380
+
381
+ /** A scope that tracks renames of the entered symbols.
382
+ * Useful for providing completions for renamed symbols
383
+ * in the REPL and the IDE.
384
+ */
385
+ private class RenameAwareScope extends Scopes .MutableScope {
386
+ private [this ] val renames : mutable.Map [Symbol , Name ] = mutable.Map .empty
387
+
388
+ /** Enter the symbol `sym` in this scope, recording a potential renaming. */
389
+ def enter [T <: Symbol ](sym : T , name : Name )(implicit ctx : Context ): T = {
390
+ if (name != sym.name) renames += sym -> name
391
+ newScopeEntry(name, sym)
392
+ sym
393
+ }
394
+
395
+ /** Lists the symbols in this scope along with the name associated with them. */
396
+ def toListWithNames (implicit ctx : Context ): List [(Symbol , Name )] =
397
+ toList.map(sym => (sym, renames.get(sym).getOrElse(sym.name)))
398
+ }
399
+
380
400
}
0 commit comments