@@ -36,9 +36,7 @@ import org.jetbrains.kotlin.lexer.KtTokens
36
36
import org.jetbrains.kotlin.lexer.KtKeywordToken
37
37
import org.jetbrains.kotlin.resolve.BindingContext
38
38
import org.jetbrains.kotlin.resolve.DescriptorUtils
39
- import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
40
- import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
41
- import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
39
+ import org.jetbrains.kotlin.resolve.descriptorUtil.*
42
40
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
43
41
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter.Companion
44
42
import org.jetbrains.kotlin.resolve.scopes.HierarchicalScope
@@ -63,12 +61,17 @@ fun completions(file: CompiledFile, cursor: Int, index: SymbolIndex, config: Com
63
61
val partial = findPartialIdentifier(file, cursor)
64
62
LOG .debug(" Looking for completions that match '{}'" , partial)
65
63
66
- val (elementItems, isExhaustive, receiver ) = elementCompletionItems(file, cursor, config, partial)
64
+ val (elementItems, element ) = elementCompletionItems(file, cursor, config, partial)
67
65
val elementItemList = elementItems.toList()
68
66
val elementItemLabels = elementItemList.mapNotNull { it.label }.toSet()
67
+
68
+ val isExhaustive = element !is KtNameReferenceExpression
69
+ && element !is KtTypeElement
70
+ && element !is KtQualifiedExpression
71
+
69
72
val items = (
70
73
elementItemList.asSequence()
71
- + (if (! isExhaustive) indexCompletionItems(file, cursor, receiver , index, partial).filter { it.label !in elementItemLabels } else emptySequence())
74
+ + (if (! isExhaustive) indexCompletionItems(file, cursor, element , index, partial).filter { it.label !in elementItemLabels } else emptySequence())
72
75
+ (if (elementItemList.isEmpty()) keywordCompletionItems(partial) else emptySequence())
73
76
)
74
77
val itemList = items
@@ -80,8 +83,13 @@ fun completions(file: CompiledFile, cursor: Int, index: SymbolIndex, config: Com
80
83
return CompletionList (isIncomplete, itemList)
81
84
}
82
85
86
+ private fun getQueryNameFromExpression (receiver : KtExpression ? , cursor : Int , file : CompiledFile ): FqName ? {
87
+ val receiverType = receiver?.let { expr -> file.scopeAtPoint(cursor)?.let { file.typeOfExpression(expr, it) } }
88
+ return receiverType?.constructor ?.declarationDescriptor?.fqNameSafe
89
+ }
90
+
83
91
/* * Finds completions in the global symbol index, for potentially unimported symbols. */
84
- private fun indexCompletionItems (file : CompiledFile , cursor : Int , receiver : KtExpression ? , index : SymbolIndex , partial : String ): Sequence <CompletionItem > {
92
+ private fun indexCompletionItems (file : CompiledFile , cursor : Int , element : KtElement ? , index : SymbolIndex , partial : String ): Sequence <CompletionItem > {
85
93
val parsedFile = file.parse
86
94
val imports = parsedFile.importDirectives
87
95
// TODO: Deal with alias imports
@@ -93,11 +101,23 @@ private fun indexCompletionItems(file: CompiledFile, cursor: Int, receiver: KtEx
93
101
val importedNames = imports
94
102
.mapNotNull { it.importedFqName?.shortName() }
95
103
.toSet()
96
- val receiverType = receiver?.let { expr -> file.scopeAtPoint(cursor)?.let { file.typeOfExpression(expr, it) } }
97
- val receiverTypeFqName = receiverType?.constructor ?.declarationDescriptor?.fqNameSafe
104
+
105
+ val queryName = when (element) {
106
+ is KtQualifiedExpression -> getQueryNameFromExpression(element.receiverExpression, element.receiverExpression.startOffset, file)
107
+ is KtSimpleNameExpression -> {
108
+ val receiver = element.getReceiverExpression()
109
+ when {
110
+ receiver != null -> getQueryNameFromExpression(receiver, receiver.startOffset, file)
111
+ else -> null
112
+ }
113
+ }
114
+ is KtUserType -> file.referenceAtPoint(element.qualifier?.startOffset ? : cursor)?.second?.fqNameSafe
115
+ is KtTypeElement -> file.referenceAtPoint(element.startOffsetInParent)?.second?.fqNameOrNull()
116
+ else -> null
117
+ }
98
118
99
119
return index
100
- .query(partial, receiverTypeFqName , limit = MAX_COMPLETION_ITEMS )
120
+ .query(partial, queryName , limit = MAX_COMPLETION_ITEMS )
101
121
.asSequence()
102
122
.filter { it.kind != Symbol .Kind .MODULE } // Ignore global module/package name completions for now, since they cannot be 'imported'
103
123
.filter { it.fqName.shortName() !in importedNames && it.fqName.parent() !in wildcardPackages }
@@ -157,24 +177,19 @@ private fun keywordCompletionItems(partial: String): Sequence<CompletionItem> =
157
177
kind = CompletionItemKind .Keyword
158
178
} }
159
179
160
- data class ElementCompletionItems (val items : Sequence <CompletionItem >, val isExhaustive : Boolean , val receiver : KtExpression ? = null )
180
+ data class ElementCompletionItems (val items : Sequence <CompletionItem >, val element : KtElement ? = null )
161
181
162
182
/* * Finds completions based on the element around the user's cursor. */
163
183
private fun elementCompletionItems (file : CompiledFile , cursor : Int , config : CompletionConfiguration , partial : String ): ElementCompletionItems {
164
- val surroundingElement = completableElement(file, cursor) ? : return ElementCompletionItems (emptySequence(), isExhaustive = true )
184
+ val surroundingElement = completableElement(file, cursor) ? : return ElementCompletionItems (emptySequence())
165
185
val completions = elementCompletions(file, cursor, surroundingElement)
166
186
167
187
val matchesName = completions.filter { containsCharactersInOrder(name(it), partial, caseSensitive = false ) }
168
188
val sorted = matchesName.takeIf { partial.length >= MIN_SORT_LENGTH }?.sortedBy { stringDistance(name(it), partial) }
169
189
? : matchesName.sortedBy { if (name(it).startsWith(partial)) 0 else 1 }
170
190
val visible = sorted.filter(isVisible(file, cursor))
171
191
172
- val isExhaustive = surroundingElement !is KtNameReferenceExpression
173
- && surroundingElement !is KtTypeElement
174
- && surroundingElement !is KtQualifiedExpression
175
- val receiver = (surroundingElement as ? KtQualifiedExpression )?.receiverExpression
176
-
177
- return ElementCompletionItems (visible.map { completionItem(it, surroundingElement, file, config) }, isExhaustive, receiver)
192
+ return ElementCompletionItems (visible.map { completionItem(it, surroundingElement, file, config) }, surroundingElement)
178
193
}
179
194
180
195
private val callPattern = Regex (" (.*)\\ ((?:\\ $\\ d+)?\\ )(?:\\ $0)?" )
@@ -244,6 +259,7 @@ private fun completableElement(file: CompiledFile, cursor: Int): KtElement? {
244
259
// package x.y.?
245
260
? : el.findParent<KtPackageDirective >()
246
261
// :?
262
+ ? : el as ? KtUserType
247
263
? : el.parent as ? KtTypeElement
248
264
// .?
249
265
? : el as ? KtQualifiedExpression
@@ -289,7 +305,7 @@ private fun elementCompletions(file: CompiledFile, cursor: Int, surroundingEleme
289
305
val referenceTarget = file.referenceAtPoint(surroundingElement.qualifier!! .startOffset)?.second
290
306
if (referenceTarget is ClassDescriptor ) {
291
307
LOG .info(" Completing members of {}" , referenceTarget.fqNameSafe)
292
- return referenceTarget.unsubstitutedInnerClassesScope.getContributedDescriptors().asSequence ()
308
+ return referenceTarget.getDescriptors ()
293
309
} else {
294
310
LOG .warn(" No type reference in '{}'" , surroundingElement.text)
295
311
return emptySequence()
@@ -349,7 +365,7 @@ private fun completeMembers(file: CompiledFile, cursor: Int, receiverExpr: KtExp
349
365
val extensions = extensionFunctions(lexicalScope).filter { isExtensionFor(receiverType, it) }
350
366
descriptors = members + extensions
351
367
352
- if (! isCompanionOfEnum(receiverType)) {
368
+ if (! isCompanionOfEnum(receiverType) && ! isCompanionOfSealed(receiverType) ) {
353
369
return descriptors
354
370
}
355
371
}
@@ -358,16 +374,24 @@ private fun completeMembers(file: CompiledFile, cursor: Int, receiverExpr: KtExp
358
374
// JavaClass.?
359
375
val referenceTarget = file.referenceAtPoint(receiverExpr.endOffset - 1 )?.second
360
376
if (referenceTarget is ClassDescriptor ) {
361
- LOG .debug(" Completing static members of '{}'" , referenceTarget.fqNameSafe)
362
- val statics = referenceTarget.staticScope.getContributedDescriptors().asSequence()
363
- val classes = referenceTarget.unsubstitutedInnerClassesScope.getContributedDescriptors().asSequence()
364
- return descriptors + statics + classes
377
+ LOG .debug(" Completing members of '{}'" , referenceTarget.fqNameSafe)
378
+ return descriptors + referenceTarget.getDescriptors()
365
379
}
366
380
367
381
LOG .debug(" Can't find member scope for {}" , receiverExpr.text)
368
382
return emptySequence()
369
383
}
370
384
385
+ private fun ClassDescriptor.getDescriptors (): Sequence <DeclarationDescriptor > {
386
+ val statics = staticScope.getContributedDescriptors().asSequence()
387
+ val classes = unsubstitutedInnerClassesScope.getContributedDescriptors().asSequence()
388
+ val types = unsubstitutedMemberScope.getContributedDescriptors().asSequence()
389
+ val companionDescriptors = if (hasCompanionObject && companionObjectDescriptor != null ) companionObjectDescriptor!! .getDescriptors() else emptySequence()
390
+
391
+ return (statics + classes + types + companionDescriptors).toSet().asSequence()
392
+
393
+ }
394
+
371
395
private fun isCompanionOfEnum (kotlinType : KotlinType ): Boolean {
372
396
val classDescriptor = TypeUtils .getClassDescriptor(kotlinType)
373
397
val isCompanion = DescriptorUtils .isCompanionObject(classDescriptor)
@@ -377,6 +401,16 @@ private fun isCompanionOfEnum(kotlinType: KotlinType): Boolean {
377
401
return DescriptorUtils .isEnumClass(classDescriptor?.containingDeclaration)
378
402
}
379
403
404
+ private fun isCompanionOfSealed (kotlinType : KotlinType ): Boolean {
405
+ val classDescriptor = TypeUtils .getClassDescriptor(kotlinType)
406
+ val isCompanion = DescriptorUtils .isCompanionObject(classDescriptor)
407
+ if (! isCompanion) {
408
+ return false
409
+ }
410
+
411
+ return DescriptorUtils .isSealedClass(classDescriptor?.containingDeclaration)
412
+ }
413
+
380
414
private fun findPartialIdentifier (file : CompiledFile , cursor : Int ): String {
381
415
val line = file.lineBefore(cursor)
382
416
if (line.matches(Regex (" .*\\ ." ))) return " "
0 commit comments