@@ -36,9 +36,7 @@ import org.jetbrains.kotlin.lexer.KtTokens
3636import org.jetbrains.kotlin.lexer.KtKeywordToken
3737import org.jetbrains.kotlin.resolve.BindingContext
3838import 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.*
4240import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
4341import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter.Companion
4442import org.jetbrains.kotlin.resolve.scopes.HierarchicalScope
@@ -63,12 +61,17 @@ fun completions(file: CompiledFile, cursor: Int, index: SymbolIndex, config: Com
6361 val partial = findPartialIdentifier(file, cursor)
6462 LOG .debug(" Looking for completions that match '{}'" , partial)
6563
66- val (elementItems, isExhaustive, receiver ) = elementCompletionItems(file, cursor, config, partial)
64+ val (elementItems, element ) = elementCompletionItems(file, cursor, config, partial)
6765 val elementItemList = elementItems.toList()
6866 val elementItemLabels = elementItemList.mapNotNull { it.label }.toSet()
67+
68+ val isExhaustive = element !is KtNameReferenceExpression
69+ && element !is KtTypeElement
70+ && element !is KtQualifiedExpression
71+
6972 val items = (
7073 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())
7275 + (if (elementItemList.isEmpty()) keywordCompletionItems(partial) else emptySequence())
7376 )
7477 val itemList = items
@@ -80,8 +83,13 @@ fun completions(file: CompiledFile, cursor: Int, index: SymbolIndex, config: Com
8083 return CompletionList (isIncomplete, itemList)
8184}
8285
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+
8391/* * 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 > {
8593 val parsedFile = file.parse
8694 val imports = parsedFile.importDirectives
8795 // TODO: Deal with alias imports
@@ -93,11 +101,23 @@ private fun indexCompletionItems(file: CompiledFile, cursor: Int, receiver: KtEx
93101 val importedNames = imports
94102 .mapNotNull { it.importedFqName?.shortName() }
95103 .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+ }
98118
99119 return index
100- .query(partial, receiverTypeFqName , limit = MAX_COMPLETION_ITEMS )
120+ .query(partial, queryName , limit = MAX_COMPLETION_ITEMS )
101121 .asSequence()
102122 .filter { it.kind != Symbol .Kind .MODULE } // Ignore global module/package name completions for now, since they cannot be 'imported'
103123 .filter { it.fqName.shortName() !in importedNames && it.fqName.parent() !in wildcardPackages }
@@ -157,24 +177,19 @@ private fun keywordCompletionItems(partial: String): Sequence<CompletionItem> =
157177 kind = CompletionItemKind .Keyword
158178 } }
159179
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 )
161181
162182/* * Finds completions based on the element around the user's cursor. */
163183private 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())
165185 val completions = elementCompletions(file, cursor, surroundingElement)
166186
167187 val matchesName = completions.filter { containsCharactersInOrder(name(it), partial, caseSensitive = false ) }
168188 val sorted = matchesName.takeIf { partial.length >= MIN_SORT_LENGTH }?.sortedBy { stringDistance(name(it), partial) }
169189 ? : matchesName.sortedBy { if (name(it).startsWith(partial)) 0 else 1 }
170190 val visible = sorted.filter(isVisible(file, cursor))
171191
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)
178193}
179194
180195private val callPattern = Regex (" (.*)\\ ((?:\\ $\\ d+)?\\ )(?:\\ $0)?" )
@@ -244,6 +259,7 @@ private fun completableElement(file: CompiledFile, cursor: Int): KtElement? {
244259 // package x.y.?
245260 ? : el.findParent<KtPackageDirective >()
246261 // :?
262+ ? : el as ? KtUserType
247263 ? : el.parent as ? KtTypeElement
248264 // .?
249265 ? : el as ? KtQualifiedExpression
@@ -289,7 +305,7 @@ private fun elementCompletions(file: CompiledFile, cursor: Int, surroundingEleme
289305 val referenceTarget = file.referenceAtPoint(surroundingElement.qualifier!! .startOffset)?.second
290306 if (referenceTarget is ClassDescriptor ) {
291307 LOG .info(" Completing members of {}" , referenceTarget.fqNameSafe)
292- return referenceTarget.unsubstitutedInnerClassesScope.getContributedDescriptors().asSequence ()
308+ return referenceTarget.getDescriptors ()
293309 } else {
294310 LOG .warn(" No type reference in '{}'" , surroundingElement.text)
295311 return emptySequence()
@@ -349,7 +365,7 @@ private fun completeMembers(file: CompiledFile, cursor: Int, receiverExpr: KtExp
349365 val extensions = extensionFunctions(lexicalScope).filter { isExtensionFor(receiverType, it) }
350366 descriptors = members + extensions
351367
352- if (! isCompanionOfEnum(receiverType)) {
368+ if (! isCompanionOfEnum(receiverType) && ! isCompanionOfSealed(receiverType) ) {
353369 return descriptors
354370 }
355371 }
@@ -358,16 +374,24 @@ private fun completeMembers(file: CompiledFile, cursor: Int, receiverExpr: KtExp
358374 // JavaClass.?
359375 val referenceTarget = file.referenceAtPoint(receiverExpr.endOffset - 1 )?.second
360376 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()
365379 }
366380
367381 LOG .debug(" Can't find member scope for {}" , receiverExpr.text)
368382 return emptySequence()
369383}
370384
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+
371395private fun isCompanionOfEnum (kotlinType : KotlinType ): Boolean {
372396 val classDescriptor = TypeUtils .getClassDescriptor(kotlinType)
373397 val isCompanion = DescriptorUtils .isCompanionObject(classDescriptor)
@@ -377,6 +401,16 @@ private fun isCompanionOfEnum(kotlinType: KotlinType): Boolean {
377401 return DescriptorUtils .isEnumClass(classDescriptor?.containingDeclaration)
378402}
379403
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+
380414private fun findPartialIdentifier (file : CompiledFile , cursor : Int ): String {
381415 val line = file.lineBefore(cursor)
382416 if (line.matches(Regex (" .*\\ ." ))) return " "
0 commit comments