@@ -33,34 +33,6 @@ object Completion {
33
33
}
34
34
35
35
private def computeCompletions (pos : SourcePosition , path : List [Tree ])(implicit ctx : Context ): (Int , List [Symbol ]) = {
36
- val completions = Scopes .newScope.openForMutations
37
-
38
- type Mode = Int
39
- object Mode {
40
- /** No symbol should be included */
41
- val None : Mode = 0
42
-
43
- /** Term symbols are allowed */
44
- val Term : Mode = 1
45
-
46
- /** Type symbols are allowed */
47
- val Type : Mode = 2
48
-
49
- /** Both term and type symbols are allowed */
50
- val Import : Mode = Term | Type
51
-
52
- /** Does `m0` include `m1`? */
53
- def is (m0 : Mode , m1 : Mode ): Boolean = (m0 & m1) == m1
54
- }
55
-
56
- /**
57
- * The information about the current completion.
58
- *
59
- * @param offset The offset where the completion result should be inserted.
60
- * @param prefix A prefix that potential completion results must match.
61
- * @param mode The completion mode.
62
- */
63
- case class CompletionInfo (offset : Int , prefix : String , mode : Mode )
64
36
65
37
/**
66
38
* Extract basic info about completion location and the kind of symbols to include.
@@ -106,70 +78,37 @@ object Completion {
106
78
completionInfo(other, /* inImport = */ false )
107
79
}
108
80
109
- /** Include in completion sets only symbols that
110
- * 1. start with given name prefix, and
111
- * 2. do not contain '$' except in prefix where it is explicitly written by user, and
112
- * 3. are not a primary constructor,
113
- * 4. are the module class in case of packages,
114
- * 5. are mutable accessors, to exclude setters for `var`,
115
- * 6. have same term/type kind as name prefix given so far
116
- *
117
- * The reason for (2) is that we do not want to present compiler-synthesized identifiers
118
- * as completion results. However, if a user explicitly writes all '$' characters in an
119
- * identifier, we should complete the rest.
120
- */
121
- def include (sym : Symbol ) = {
122
- sym.name.startsWith(info.prefix) &&
123
- ! sym.name.toString.drop(info.prefix.length).contains('$' ) &&
124
- ! sym.isPrimaryConstructor &&
125
- (! sym.is(Package ) || ! sym.moduleClass.exists) &&
126
- ! sym.is(allOf(Mutable , Accessor )) &&
127
- (
128
- (Mode .is(info.mode, Mode .Term ) && sym.isTerm)
129
- || (Mode .is(info.mode, Mode .Type ) && sym.isType)
130
- )
131
- }
132
-
133
- def enter (sym : Symbol ) =
134
- if (include(sym)) completions.enter(sym)
135
- def add (sym : Symbol ) =
136
- if (sym.exists && ! completions.lookup(sym.name).exists) enter(sym)
137
-
138
- def addMember (site : Type , name : Name ) =
139
- if (! completions.lookup(name).exists)
140
- for (alt <- site.member(name).alternatives) enter(alt.symbol)
141
-
142
81
def accessibleMembers (site : Type , superAccess : Boolean = true ): Seq [Symbol ] = site match {
143
82
case site : NamedType if site.symbol.is(Package ) =>
144
- site.decls.toList.filter(include) // Don't look inside package members -- it's too expensive.
83
+ site.decls.toList.filter(info. include) // Don't look inside package members -- it's too expensive.
145
84
case _ =>
146
85
def appendMemberSyms (name : Name , buf : mutable.Buffer [SingleDenotation ]): Unit =
147
86
try buf ++= site.member(name).alternatives
148
87
catch { case ex : TypeError => }
149
88
site.memberDenots(takeAllFilter, appendMemberSyms).collect {
150
- case mbr if include(mbr.symbol) => mbr.accessibleFrom(site, superAccess).symbol
89
+ case mbr if info. include(mbr.symbol) => mbr.accessibleFrom(site, superAccess).symbol
151
90
case _ => NoSymbol
152
91
}.filter(_.exists)
153
92
}
154
93
155
94
def addAccessibleMembers (site : Type , superAccess : Boolean = true ): Unit =
156
- for (mbr <- accessibleMembers(site)) addMember(site, mbr.name)
95
+ for (mbr <- accessibleMembers(site)) info. addMember(site, mbr.name)
157
96
158
97
def getImportCompletions (ictx : Context ): Unit = {
159
98
implicit val ctx = ictx
160
99
val imp = ctx.importInfo
161
100
if (imp != null ) {
162
101
def addImport (name : TermName ) = {
163
- addMember(imp.site, name)
164
- addMember(imp.site, name.toTypeName)
102
+ info. addMember(imp.site, name)
103
+ info. addMember(imp.site, name.toTypeName)
165
104
}
166
105
// FIXME: We need to also take renamed items into account for completions,
167
106
// That means we have to return list of a pairs (Name, Symbol) instead of a list
168
107
// of symbols from `completions`.!=
169
108
for (imported <- imp.originals if ! imp.excluded.contains(imported)) addImport(imported)
170
109
if (imp.isWildcardImport)
171
110
for (mbr <- accessibleMembers(imp.site) if ! imp.excluded.contains(mbr.name.toTermName))
172
- addMember(imp.site, mbr.name)
111
+ info. addMember(imp.site, mbr.name)
173
112
}
174
113
}
175
114
@@ -179,11 +118,11 @@ object Completion {
179
118
if (ctx.owner.isClass) {
180
119
addAccessibleMembers(ctx.owner.thisType)
181
120
ctx.owner.asClass.classInfo.selfInfo match {
182
- case selfSym : Symbol => add(selfSym)
121
+ case selfSym : Symbol => info. add(selfSym)
183
122
case _ =>
184
123
}
185
124
}
186
- else if (ctx.scope != null ) ctx.scope.foreach(add)
125
+ else if (ctx.scope != null ) ctx.scope.foreach(info. add)
187
126
188
127
getImportCompletions(ctx)
189
128
@@ -205,7 +144,7 @@ object Completion {
205
144
206
145
def getMemberCompletions (qual : Tree ): Unit = {
207
146
addAccessibleMembers(qual.tpe)
208
- if (! Mode .is( info.mode, Mode .Import )) {
147
+ if (! info.mode.is( Mode .Import )) {
209
148
// Implicit conversions do not kick in when importing
210
149
implicitConversionTargets(qual)(ctx.fresh.setExploreTyperState())
211
150
.foreach(addAccessibleMembers(_))
@@ -219,24 +158,103 @@ object Completion {
219
158
case _ => getScopeCompletions(ctx)
220
159
}
221
160
222
- val completionList =
223
- if (! Mode .is(info.mode, Mode .Import )) completions.toList
161
+ val completionList = info.getCompletions
162
+
163
+ interactiv.println(i " completion with pos = $pos, prefix = $info.prefix, termOnly = $info.termOnly, typeOnly = $info.typeOnly = $completionList%, % " )
164
+ (info.offset, completionList)
165
+ }
166
+
167
+ /** Filter for names that should appear when looking for completions. */
168
+ private [this ] object completionsFilter extends NameFilter {
169
+ def apply (pre : Type , name : Name )(implicit ctx : Context ): Boolean =
170
+ ! name.isConstructorName && name.toTermName.info.kind == SimpleNameKind
171
+ }
172
+
173
+ /**
174
+ * The information about the current completion.
175
+ *
176
+ * @param offset The offset where the completion result should be inserted.
177
+ * @param prefix A prefix that potential completion results must match.
178
+ * @param mode The completion mode.
179
+ */
180
+ private case class CompletionInfo (offset : Int , prefix : String , mode : Mode ) {
181
+
182
+ private [this ] val completions = Scopes .newScope.openForMutations
183
+
184
+ /** Checks whether `sym` should be included, and adds it to the completions if so. */
185
+ def enter (sym : Symbol )(implicit ctx : Context ) =
186
+ if (include(sym)) completions.enter(sym)
187
+
188
+ /** Checks whether `sym` should be included, and adds it to the completions if so. */
189
+ def add (sym : Symbol )(implicit ctx : Context ) =
190
+ if (sym.exists && ! completions.lookup(sym.name).exists) enter(sym)
191
+
192
+ /** Lookup members `name` from `site`, and try to add them to the completion list. */
193
+ def addMember (site : Type , name : Name )(implicit ctx : Context ) =
194
+ if (! completions.lookup(name).exists)
195
+ for (alt <- site.member(name).alternatives) enter(alt.symbol)
196
+
197
+ /** Include in completion sets only symbols that
198
+ * 1. start with given name prefix, and
199
+ * 2. do not contain '$' except in prefix where it is explicitly written by user, and
200
+ * 3. are not a primary constructor,
201
+ * 4. are the module class in case of packages,
202
+ * 5. are mutable accessors, to exclude setters for `var`,
203
+ * 6. have same term/type kind as name prefix given so far
204
+ *
205
+ * The reason for (2) is that we do not want to present compiler-synthesized identifiers
206
+ * as completion results. However, if a user explicitly writes all '$' characters in an
207
+ * identifier, we should complete the rest.
208
+ */
209
+ def include (sym : Symbol )(implicit ctx : Context ): Boolean =
210
+ sym.name.startsWith(prefix) &&
211
+ ! sym.name.toString.drop(prefix.length).contains('$' ) &&
212
+ ! sym.isPrimaryConstructor &&
213
+ (! sym.is(Package ) || ! sym.moduleClass.exists) &&
214
+ ! sym.is(allOf(Mutable , Accessor )) &&
215
+ (
216
+ (mode.is(Mode .Term ) && sym.isTerm)
217
+ || (mode.is(Mode .Type ) && sym.isType)
218
+ )
219
+
220
+ /**
221
+ * Return the list of symbols that shoudl be included in completion results.
222
+ *
223
+ * If the mode is `Import` and several symbols share the same name, the type symbols are
224
+ * preferred over term symbols.
225
+ */
226
+ def getCompletions (implicit ctx : Context ): List [Symbol ] = {
227
+ if (! mode.is(Mode .Import )) completions.toList
224
228
else {
225
229
// In imports, show only the type symbols when there are multiple options with the same name
226
230
completions.toList.groupBy(_.name.stripModuleClassSuffix.toSimpleName).mapValues {
227
231
case sym :: Nil => sym :: Nil
228
232
case syms => syms.filter(_.isType)
229
233
}.values.flatten.toList
230
234
}
235
+ }
236
+ }
231
237
232
- interactiv.println(i " completion with pos = $pos, prefix = $info.prefix, termOnly = $info.termOnly, typeOnly = $info.typeOnly = $completionList%, % " )
233
- (info.offset, completionList)
238
+ /**
239
+ * The completion mode: defines what kinds of symbols should be included in the completion
240
+ * results.
241
+ */
242
+ private class Mode (val bits : Int ) extends AnyVal {
243
+ def is (other : Mode ): Boolean = (bits & other.bits) == other.bits
244
+ def | (other : Mode ): Mode = new Mode (bits | other.bits)
234
245
}
246
+ private object Mode {
247
+ /** No symbol should be included */
248
+ val None : Mode = new Mode (0 )
235
249
236
- /** Filter for names that should appear when looking for completions. */
237
- private [this ] object completionsFilter extends NameFilter {
238
- def apply (pre : Type , name : Name )(implicit ctx : Context ): Boolean =
239
- ! name.isConstructorName && name.toTermName.info.kind == SimpleNameKind
250
+ /** Term symbols are allowed */
251
+ val Term : Mode = new Mode (1 )
252
+
253
+ /** Type symbols are allowed */
254
+ val Type : Mode = new Mode (2 )
255
+
256
+ /** Both term and type symbols are allowed */
257
+ val Import : Mode = Term | Type
240
258
}
241
259
242
260
}
0 commit comments