Skip to content

Commit 1dbf8b9

Browse files
committed
Cleanup Completion
1 parent bd6de10 commit 1dbf8b9

File tree

1 file changed

+130
-91
lines changed

1 file changed

+130
-91
lines changed

compiler/src/dotty/tools/dotc/interactive/Completion.scala

Lines changed: 130 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import dotty.tools.dotc.core.StdNames.nme
1515
import dotty.tools.dotc.core.TypeError
1616
import dotty.tools.dotc.core.Types.{NamedType, NameFilter, Type, takeAllFilter}
1717
import dotty.tools.dotc.printing.Texts._
18-
import dotty.tools.dotc.util.SourcePosition
18+
import dotty.tools.dotc.util.{NoSourcePosition, SourcePosition}
1919

2020
import scala.collection.mutable
2121

@@ -37,103 +37,137 @@ object Completion {
3737
val info = path match {
3838
case (Thicket(name :: _ :: Nil)) :: (imp: Import) :: _ =>
3939
if (name.pos.contains(pos.pos))
40-
CompletionInfo(name.asInstanceOf[Tree] :: Nil, pos, inImport = true)
41-
else CompletionInfo(path, pos, inImport = true)
40+
CompletionInfo(name.asInstanceOf[Tree], pos, inImport = true)
41+
else CompletionInfo.Empty // We cannot complete the renaming part
4242

4343
case (imp: Import) :: _ =>
4444
imp.selectors.find(_.pos.contains(pos.pos)) match {
45-
case None => new CompletionInfo(imp.expr.pos.point, "", Mode.Import)
46-
case Some(sel) => CompletionInfo(sel.asInstanceOf[Tree] :: Nil, pos, inImport = true)
45+
case None => new CompletionInfo(imp.expr.pos.point, pos, "", Mode.Import)
46+
case Some(sel) => CompletionInfo(sel.asInstanceOf[Tree], pos, inImport = true)
4747
}
4848

49-
case other =>
49+
case other :: _ =>
5050
CompletionInfo(other, pos, inImport = false)
51-
}
5251

53-
def accessibleMembers(site: Type, superAccess: Boolean = true): Seq[Symbol] = site match {
54-
case site: NamedType if site.symbol.is(Package) =>
55-
site.decls.toList.filter(info.include) // Don't look inside package members -- it's too expensive.
56-
case _ =>
57-
def appendMemberSyms(name: Name, buf: mutable.Buffer[SingleDenotation]): Unit =
58-
try buf ++= site.member(name).alternatives
59-
catch { case ex: TypeError => }
60-
site.memberDenots(takeAllFilter, appendMemberSyms).collect {
61-
case mbr if info.include(mbr.symbol) => mbr.accessibleFrom(site, superAccess).symbol
62-
case _ => NoSymbol
63-
}.filter(_.exists)
52+
case Nil =>
53+
CompletionInfo.Empty
6454
}
6555

66-
def addAccessibleMembers(site: Type, superAccess: Boolean = true): Unit =
67-
for (mbr <- accessibleMembers(site)) info.addMember(site, mbr.name)
68-
69-
def getImportCompletions(ictx: Context): Unit = {
70-
implicit val ctx = ictx
71-
val imp = ctx.importInfo
72-
if (imp != null) {
73-
def addImport(name: TermName) = {
74-
info.addMember(imp.site, name)
75-
info.addMember(imp.site, name.toTypeName)
76-
}
77-
// FIXME: We need to also take renamed items into account for completions,
78-
// That means we have to return list of a pairs (Name, Symbol) instead of a list
79-
// of symbols from `completions`.!=
80-
for (imported <- imp.originals if !imp.excluded.contains(imported)) addImport(imported)
81-
if (imp.isWildcardImport)
82-
for (mbr <- accessibleMembers(imp.site) if !imp.excluded.contains(mbr.name.toTermName))
83-
info.addMember(imp.site, mbr.name)
84-
}
56+
path match {
57+
case Select(qual, _) :: _ => addMemberCompletions(info, qual)
58+
case Import(expr, _) :: _ => addMemberCompletions(info, expr)
59+
case (_: Thicket) :: Import(expr, _) :: _ => addMemberCompletions(info, expr)
60+
case _ => addScopeCompletions(info)
8561
}
8662

87-
def getScopeCompletions(ictx: Context): Unit = {
88-
implicit val ctx = ictx
63+
val completionList = info.getCompletions
8964

90-
if (ctx.owner.isClass) {
91-
addAccessibleMembers(ctx.owner.thisType)
92-
ctx.owner.asClass.classInfo.selfInfo match {
93-
case selfSym: Symbol => info.add(selfSym)
94-
case _ =>
95-
}
96-
}
97-
else if (ctx.scope != null) ctx.scope.foreach(info.add)
65+
interactiv.println(i"completion with pos = $pos, prefix = $info.prefix, termOnly = $info.termOnly, typeOnly = $info.typeOnly = $completionList%, %")
66+
(info.offset, completionList)
67+
}
9868

99-
getImportCompletions(ctx)
69+
/**
70+
* Find all the members of `site` that are accessible and which should be included in `info`.
71+
*
72+
* @param info The CompletionInfo where the members should be added.
73+
* @param site The type to inspect.
74+
* @return The members of `site` that are accessible and pass the include filter of `info`.
75+
*/
76+
private def accessibleMembers(info: CompletionInfo, site: Type)(implicit ctx: Context): Seq[Symbol] = site match {
77+
case site: NamedType if site.symbol.is(Package) =>
78+
site.decls.toList.filter(info.include) // Don't look inside package members -- it's too expensive.
79+
case _ =>
80+
def appendMemberSyms(name: Name, buf: mutable.Buffer[SingleDenotation]): Unit =
81+
try buf ++= site.member(name).alternatives
82+
catch { case ex: TypeError => }
83+
site.memberDenots(takeAllFilter, appendMemberSyms).collect {
84+
case mbr if info.include(mbr.symbol) => mbr.accessibleFrom(site, superAccess = true).symbol
85+
case _ => NoSymbol
86+
}.filter(_.exists)
87+
}
10088

101-
var outer = ctx.outer
102-
while ((outer.owner `eq` ctx.owner) && (outer.scope `eq` ctx.scope)) {
103-
getImportCompletions(outer)
104-
outer = outer.outer
105-
}
106-
if (outer `ne` NoContext) getScopeCompletions(outer)
107-
}
89+
/** Add all the accessible members of `site` in `info`. */
90+
private def addAccessibleMembers(info: CompletionInfo, site: Type)(implicit ctx: Context): Unit =
91+
for (mbr <- accessibleMembers(info, site)) info.addMember(site, mbr.name)
10892

109-
def implicitConversionTargets(qual: Tree)(implicit ctx: Context): Set[Type] = {
110-
val typer = ctx.typer
111-
val conversions = new typer.ImplicitSearch(defn.AnyType, qual, pos.pos).allImplicits
112-
val targets = conversions.map(_.widen.finalResultType)
113-
interactiv.println(i"implicit conversion targets considered: ${targets.toList}%, %")
114-
targets
93+
/**
94+
* Add in `info` the symbols that are imported by `ctx.importInfo`. If this is a wildcard import,
95+
* all the accessible members of the import's `site` are included.
96+
*
97+
* @param info The CompletionInfo where the members should be added.
98+
*/
99+
private def addImportCompletions(info: CompletionInfo)(implicit ctx: Context): Unit = {
100+
val imp = ctx.importInfo
101+
if (imp != null) {
102+
def addImport(name: TermName) = {
103+
info.addMember(imp.site, name)
104+
info.addMember(imp.site, name.toTypeName)
105+
}
106+
// FIXME: We need to also take renamed items into account for completions,
107+
// That means we have to return list of a pairs (Name, Symbol) instead of a list
108+
// of symbols from `completions`.!=
109+
for (imported <- imp.originals if !imp.excluded.contains(imported)) addImport(imported)
110+
if (imp.isWildcardImport)
111+
for (mbr <- accessibleMembers(info, imp.site) if !imp.excluded.contains(mbr.name.toTermName))
112+
info.addMember(imp.site, mbr.name)
115113
}
114+
}
116115

117-
def getMemberCompletions(qual: Tree): Unit = {
118-
addAccessibleMembers(qual.tpe)
119-
if (!info.mode.is(Mode.Import)) {
120-
// Implicit conversions do not kick in when importing
121-
implicitConversionTargets(qual)(ctx.fresh.setExploreTyperState())
122-
.foreach(addAccessibleMembers(_))
116+
/**
117+
* Add symbols that are currently in scope to `info`: the members of the current class and the
118+
* symbols that have been imported.
119+
*
120+
* @param info The CompletionInfo where the members should be added.
121+
*/
122+
private def addScopeCompletions(info: CompletionInfo)(implicit ctx: Context): Unit = {
123+
if (ctx.owner.isClass) {
124+
addAccessibleMembers(info, ctx.owner.thisType)
125+
ctx.owner.asClass.classInfo.selfInfo match {
126+
case selfSym: Symbol => info.add(selfSym)
127+
case _ =>
123128
}
124129
}
130+
else if (ctx.scope != null) ctx.scope.foreach(info.add)
125131

126-
path match {
127-
case Select(qual, _) :: _ => getMemberCompletions(qual)
128-
case Import(expr, _) :: _ => getMemberCompletions(expr)
129-
case (_: Thicket) :: Import(expr, _) :: _ => getMemberCompletions(expr)
130-
case _ => getScopeCompletions(ctx)
132+
addImportCompletions(info)
133+
134+
var outer = ctx.outer
135+
while ((outer.owner `eq` ctx.owner) && (outer.scope `eq` ctx.scope)) {
136+
addImportCompletions(info)(outer)
137+
outer = outer.outer
131138
}
139+
if (outer `ne` NoContext) addScopeCompletions(info)(outer)
140+
}
132141

133-
val completionList = info.getCompletions
142+
/**
143+
* Given `qual` of type T, finds all the types S such that there exists an implicit conversion
144+
* from T to S.
145+
*
146+
* @param info The CompletionInfo where the members should be added.
147+
* @param qual The argument to which the implicit conversion should be applied.
148+
* @return The set of types that `qual` can be converted to.
149+
*/
150+
private def implicitConversionTargets(info: CompletionInfo, qual: Tree)(implicit ctx: Context): Set[Type] = {
151+
val typer = ctx.typer
152+
val conversions = new typer.ImplicitSearch(defn.AnyType, qual, info.position.pos).allImplicits
153+
val targets = conversions.map(_.widen.finalResultType)
154+
interactiv.println(i"implicit conversion targets considered: ${targets.toList}%, %")
155+
targets
156+
}
134157

135-
interactiv.println(i"completion with pos = $pos, prefix = $info.prefix, termOnly = $info.termOnly, typeOnly = $info.typeOnly = $completionList%, %")
136-
(info.offset, completionList)
158+
/**
159+
* Find all the members of `qual` and add the ones that pass the include filters to `info`.
160+
*
161+
* If `info.mode` is `Import`, the members added via implicit conversion on `qual` are not
162+
* considered.
163+
*/
164+
private def addMemberCompletions(info: CompletionInfo, qual: Tree)(implicit ctx: Context): Unit = {
165+
addAccessibleMembers(info, qual.tpe)
166+
if (!info.mode.is(Mode.Import)) {
167+
// Implicit conversions do not kick in when importing
168+
implicitConversionTargets(info, qual)(ctx.fresh.setExploreTyperState())
169+
.foreach(addAccessibleMembers(info, _))
170+
}
137171
}
138172

139173
/** Filter for names that should appear when looking for completions. */
@@ -145,26 +179,28 @@ object Completion {
145179
/**
146180
* The information about the current completion.
147181
*
148-
* @param offset The offset where the completion result should be inserted.
149-
* @param prefix A prefix that potential completion results must match.
150-
* @param mode The completion mode.
182+
* @param offset The offset where the completion result should be inserted.
183+
* @param position The source position where the completion request was made.
184+
* @param prefix A prefix that potential completion results must match.
185+
* @param mode The completion mode.
151186
*/
152-
private class CompletionInfo(val offset: Int, val prefix: String, val mode: Mode) {
187+
private class CompletionInfo(val offset: Int, val position: SourcePosition, val prefix: String, val mode: Mode) {
153188

154189
private[this] val completions = Scopes.newScope.openForMutations
155190

156-
/** Checks whether `sym` should be included, and adds it to the completions if so. */
157-
def enter(sym: Symbol)(implicit ctx: Context) =
158-
if (include(sym)) completions.enter(sym)
159-
160-
/** Checks whether `sym` should be included, and adds it to the completions if so. */
191+
/**
192+
* If `sym` exists, no symbol with the same name is already included, and it satisfies the
193+
* inclusion filter, then add it to the completions.
194+
*/
161195
def add(sym: Symbol)(implicit ctx: Context) =
162-
if (sym.exists && !completions.lookup(sym.name).exists) enter(sym)
196+
if (sym.exists && !completions.lookup(sym.name).exists && include(sym)) {
197+
completions.enter(sym)
198+
}
163199

164200
/** Lookup members `name` from `site`, and try to add them to the completion list. */
165201
def addMember(site: Type, name: Name)(implicit ctx: Context) =
166202
if (!completions.lookup(name).exists)
167-
for (alt <- site.member(name).alternatives) enter(alt.symbol)
203+
for (alt <- site.member(name).alternatives) add(alt.symbol)
168204

169205
/** Include in completion sets only symbols that
170206
* 1. start with given name prefix, and
@@ -211,15 +247,15 @@ object Completion {
211247
/**
212248
* Extract basic info about completion location and the kind of symbols to include.
213249
*
214-
* @param path The path to the position where completion happens
250+
* @param prefix The prefix of the completion (the beginning of the name to complete).
215251
* @param inImport If set, indicates that this is the completion of an import node. When
216252
* completing imports, both types and terms are always included.
217253
* @return The information about completion (offset, kinds of symbol, etc.)
218254
*/
219-
def apply(path: List[Tree], pos: SourcePosition, inImport: Boolean): CompletionInfo = path match {
220-
case (ref: RefTree) :: _ =>
255+
def apply(prefix: Tree, pos: SourcePosition, inImport: Boolean): CompletionInfo = prefix match {
256+
case ref: RefTree =>
221257
if (ref.name == nme.ERROR)
222-
new CompletionInfo(ref.pos.point, "", Mode.None)
258+
new CompletionInfo(ref.pos.point, pos, "", Mode.None)
223259
else {
224260
val mode =
225261
if (inImport) Mode.Import
@@ -228,13 +264,16 @@ object Completion {
228264

229265
new CompletionInfo(
230266
ref.pos.point,
267+
pos,
231268
ref.name.toString.take(pos.pos.point - ref.pos.point),
232269
mode)
233270
}
234271

235272
case _ =>
236-
new CompletionInfo(0, "", Mode.None)
273+
new CompletionInfo(0, pos, "", Mode.None)
237274
}
275+
276+
val Empty = CompletionInfo(EmptyTree, NoSourcePosition, inImport = false)
238277
}
239278

240279
/**

0 commit comments

Comments
 (0)