@@ -47,6 +47,7 @@ import org.jetbrains.kotlin.types.KotlinType
4747import org.jetbrains.kotlin.types.TypeUtils
4848import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
4949import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
50+ import org.jetbrains.kotlin.utils.addToStdlib.applyIf
5051import java.util.concurrent.TimeUnit
5152
5253// The maximum number of completion items
@@ -159,12 +160,14 @@ data class ElementCompletionItems(val items: Sequence<CompletionItem>, val eleme
159160
160161/* * Finds completions based on the element around the user's cursor. */
161162private fun elementCompletionItems (file : CompiledFile , cursor : Int , config : CompletionConfiguration , partial : String ): ElementCompletionItems {
162- val surroundingElement = completableElement(file, cursor) ? : return ElementCompletionItems (emptySequence())
163- val completions = elementCompletions(file, cursor, surroundingElement)
164-
165- val matchesName = completions.filter { containsCharactersInOrder(name(it), partial, caseSensitive = false ) }
166- val sorted = matchesName.takeIf { partial.length >= MIN_SORT_LENGTH }?.sortedBy { stringDistance(name(it), partial) }
167- ? : matchesName.sortedBy { if (name(it).startsWith(partial)) 0 else 1 }
163+ val (surroundingElement, isGlobal) = completableElement(file, cursor) ? : return ElementCompletionItems (emptySequence())
164+ val completions = elementCompletions(file, cursor, surroundingElement, isGlobal)
165+ .applyIf(isGlobal) { filter { declarationIsInfix(it) } }
166+ .applyIf(surroundingElement.endOffset == cursor) {
167+ filter { containsCharactersInOrder(name(it), partial, caseSensitive = false ) }
168+ }
169+ val sorted = completions.takeIf { partial.length >= MIN_SORT_LENGTH }?.sortedBy { stringDistance(name(it), partial) }
170+ ? : completions.sortedBy { if (name(it).startsWith(partial)) 0 else 1 }
168171 val visible = sorted.filter(isVisible(file, cursor))
169172
170173 return ElementCompletionItems (visible.map { completionItem(it, surroundingElement, file, config) }, surroundingElement)
@@ -230,28 +233,59 @@ private fun isSetter(d: DeclarationDescriptor): Boolean =
230233 d.name.identifier.matches(Regex (" set[A-Z]\\ w+" )) &&
231234 d.valueParameters.size == 1
232235
233- private fun completableElement (file : CompiledFile , cursor : Int ): KtElement ? {
234- val el = file.parseAtPoint(cursor - 1 ) ? : return null
235- // import x.y.?
236- return el.findParent<KtImportDirective >()
237- // package x.y.?
238- ? : el.findParent<KtPackageDirective >()
239- // :?
240- ? : el as ? KtUserType
241- ? : el.parent as ? KtTypeElement
242- // .?
243- ? : el as ? KtQualifiedExpression
244- ? : el.parent as ? KtQualifiedExpression
245- // something::?
246- ? : el as ? KtCallableReferenceExpression
247- ? : el.parent as ? KtCallableReferenceExpression
248- // something.foo() with cursor in the method
249- ? : el.parent?.parent as ? KtQualifiedExpression
250- // ?
251- ? : el as ? KtNameReferenceExpression
236+ private fun isGlobalCall (el : KtElement ) = el is KtBlockExpression || el is KtClassBody || el.parent is KtBinaryExpression
237+
238+ private fun asGlobalCompletable (file : CompiledFile , cursor : Int , el : KtElement ): KtElement ? {
239+ val psi = file.parse.findElementAt(cursor) ? : return null
240+ val element = when (val e = psi.getPrevSiblingIgnoringWhitespace() ? : psi.parent) {
241+ is KtProperty -> e.children.lastOrNull()
242+ is KtBinaryExpression -> el
243+ else -> e
244+ }
245+ return element as ? KtReferenceExpression
246+ ? : element as ? KtQualifiedExpression
247+ ? : element as ? KtConstantExpression
248+ }
249+
250+ private fun KtElement.asKtClass (): KtElement ? {
251+ return this .findParent<KtImportDirective >() // import x.y.?
252+ // package x.y.?
253+ ? : this .findParent<KtPackageDirective >()
254+ // :?
255+ ? : this as ? KtUserType
256+ ? : this .parent as ? KtTypeElement
257+ // .?
258+ ? : this as ? KtQualifiedExpression
259+ ? : this .parent as ? KtQualifiedExpression
260+ // something::?
261+ ? : this as ? KtCallableReferenceExpression
262+ ? : this .parent as ? KtCallableReferenceExpression
263+ // something.foo() with cursor in the method
264+ ? : this .parent?.parent as ? KtQualifiedExpression
265+ // ?
266+ ? : this as ? KtNameReferenceExpression
267+ // x ? y (infix)
268+ ? : this .parent as ? KtBinaryExpression
269+ // x()
270+ ? : this as ? KtCallExpression
271+ // x (constant)
272+ ? : this as ? KtConstantExpression
273+ }
274+
275+ private fun completableElement (file : CompiledFile , cursor : Int ): Pair <KtElement , Boolean >? {
276+ val parsed = file.parseAtPoint(cursor - 1 ) ? : return null
277+ val asGlobal = isGlobalCall(parsed)
278+ val el = (
279+ if (asGlobal) asGlobalCompletable(file, cursor, parsed) else null
280+ ) ? : parsed
281+
282+ return el.asKtClass()?.let {
283+ Pair (it, asGlobal)
284+ }
252285}
253286
254- private fun elementCompletions (file : CompiledFile , cursor : Int , surroundingElement : KtElement ): Sequence <DeclarationDescriptor > {
287+ @Suppress(" LongMethod" , " ReturnCount" , " CyclomaticComplexMethod" )
288+ private fun elementCompletions (file : CompiledFile , cursor : Int , surroundingElement : KtElement , infixCall : Boolean ): Sequence <DeclarationDescriptor > {
255289 return when (surroundingElement) {
256290 // import x.y.?
257291 is KtImportDirective -> {
@@ -298,7 +332,8 @@ private fun elementCompletions(file: CompiledFile, cursor: Int, surroundingEleme
298332 // .?
299333 is KtQualifiedExpression -> {
300334 LOG .info(" Completing member expression '{}'" , surroundingElement.text)
301- completeMembers(file, cursor, surroundingElement.receiverExpression, surroundingElement is KtSafeQualifiedExpression )
335+ val exp = if (infixCall) surroundingElement else surroundingElement.receiverExpression
336+ completeMembers(file, cursor, exp, surroundingElement is KtSafeQualifiedExpression )
302337 }
303338 is KtCallableReferenceExpression -> {
304339 // something::?
@@ -316,8 +351,21 @@ private fun elementCompletions(file: CompiledFile, cursor: Int, surroundingEleme
316351 // ?
317352 is KtNameReferenceExpression -> {
318353 LOG .info(" Completing identifier '{}'" , surroundingElement.text)
319- val scope = file.scopeAtPoint(surroundingElement.startOffset) ? : return noResult(" No scope at ${file.describePosition(cursor)} " , emptySequence())
320- identifiers(scope)
354+ if (infixCall) {
355+ completeMembers(file, surroundingElement.startOffset, surroundingElement)
356+ } else {
357+ val scope = file.scopeAtPoint(surroundingElement.startOffset) ? : return noResult(" No scope at ${file.describePosition(cursor)} " , emptySequence())
358+ identifiers(scope)
359+ }
360+ }
361+ // x ? y (infix)
362+ is KtBinaryExpression -> {
363+ if (surroundingElement.operationToken == KtTokens .IDENTIFIER ) {
364+ completeMembers(file, cursor, surroundingElement.left!! )
365+ } else emptySequence()
366+ }
367+ is KtCallExpression , is KtConstantExpression -> {
368+ completeMembers(file, cursor, surroundingElement as KtExpression )
321369 }
322370 else -> {
323371 LOG .info(" {} {} didn't look like a type, a member, or an identifier" , surroundingElement::class .simpleName, surroundingElement.text)
@@ -370,6 +418,11 @@ private fun ClassDescriptor.getDescriptors(): Sequence<DeclarationDescriptor> {
370418
371419}
372420
421+ private fun declarationIsInfix (declaration : DeclarationDescriptor ): Boolean {
422+ val functionDescriptor = declaration as ? FunctionDescriptor ? : return false
423+ return functionDescriptor.isInfix
424+ }
425+
373426private fun isCompanionOfEnum (kotlinType : KotlinType ): Boolean {
374427 val classDescriptor = TypeUtils .getClassDescriptor(kotlinType)
375428 val isCompanion = DescriptorUtils .isCompanionObject(classDescriptor)
0 commit comments