@@ -15,7 +15,7 @@ import dotty.tools.dotc.core.StdNames.nme
15
15
import dotty .tools .dotc .core .TypeError
16
16
import dotty .tools .dotc .core .Types .{NamedType , NameFilter , Type , takeAllFilter }
17
17
import dotty .tools .dotc .printing .Texts ._
18
- import dotty .tools .dotc .util .SourcePosition
18
+ import dotty .tools .dotc .util .{ NoSourcePosition , SourcePosition }
19
19
20
20
import scala .collection .mutable
21
21
@@ -37,103 +37,137 @@ object Completion {
37
37
val info = path match {
38
38
case (Thicket (name :: _ :: Nil )) :: (imp : Import ) :: _ =>
39
39
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
42
42
43
43
case (imp : Import ) :: _ =>
44
44
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 )
47
47
}
48
48
49
- case other =>
49
+ case other :: _ =>
50
50
CompletionInfo (other, pos, inImport = false )
51
- }
52
51
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
64
54
}
65
55
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)
85
61
}
86
62
87
- def getScopeCompletions (ictx : Context ): Unit = {
88
- implicit val ctx = ictx
63
+ val completionList = info.getCompletions
89
64
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
+ }
98
68
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
+ }
100
88
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)
108
92
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)
115
113
}
114
+ }
116
115
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 _ =>
123
128
}
124
129
}
130
+ else if (ctx.scope != null ) ctx.scope.foreach(info.add)
125
131
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
131
138
}
139
+ if (outer `ne` NoContext ) addScopeCompletions(info)(outer)
140
+ }
132
141
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
+ }
134
157
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
+ }
137
171
}
138
172
139
173
/** Filter for names that should appear when looking for completions. */
@@ -145,26 +179,28 @@ object Completion {
145
179
/**
146
180
* The information about the current completion.
147
181
*
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.
151
186
*/
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 ) {
153
188
154
189
private [this ] val completions = Scopes .newScope.openForMutations
155
190
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
+ */
161
195
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
+ }
163
199
164
200
/** Lookup members `name` from `site`, and try to add them to the completion list. */
165
201
def addMember (site : Type , name : Name )(implicit ctx : Context ) =
166
202
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)
168
204
169
205
/** Include in completion sets only symbols that
170
206
* 1. start with given name prefix, and
@@ -211,15 +247,15 @@ object Completion {
211
247
/**
212
248
* Extract basic info about completion location and the kind of symbols to include.
213
249
*
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).
215
251
* @param inImport If set, indicates that this is the completion of an import node. When
216
252
* completing imports, both types and terms are always included.
217
253
* @return The information about completion (offset, kinds of symbol, etc.)
218
254
*/
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 =>
221
257
if (ref.name == nme.ERROR )
222
- new CompletionInfo (ref.pos.point, " " , Mode .None )
258
+ new CompletionInfo (ref.pos.point, pos, " " , Mode .None )
223
259
else {
224
260
val mode =
225
261
if (inImport) Mode .Import
@@ -228,13 +264,16 @@ object Completion {
228
264
229
265
new CompletionInfo (
230
266
ref.pos.point,
267
+ pos,
231
268
ref.name.toString.take(pos.pos.point - ref.pos.point),
232
269
mode)
233
270
}
234
271
235
272
case _ =>
236
- new CompletionInfo (0 , " " , Mode .None )
273
+ new CompletionInfo (0 , pos, " " , Mode .None )
237
274
}
275
+
276
+ val Empty = CompletionInfo (EmptyTree , NoSourcePosition , inImport = false )
238
277
}
239
278
240
279
/**
0 commit comments