From 49aba39ae868a8cb3e348a01f66e5d145e1f67f8 Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Mon, 10 May 2021 16:31:23 +0200 Subject: [PATCH] Make OverridesCompletionLookupElementDecorator available for FIR IDE --- .../idea/completion/OverridesCompletion.kt | 89 +++----------- ...erridesCompletionLookupElementDecorator.kt | 109 ++++++++++++++++++ 2 files changed, 125 insertions(+), 73 deletions(-) create mode 100644 idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/completion/OverridesCompletionLookupElementDecorator.kt diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/OverridesCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/OverridesCompletion.kt index ff4a221b948..32563d976cd 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/OverridesCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/OverridesCompletion.kt @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject import org.jetbrains.kotlin.idea.core.moveCaret import org.jetbrains.kotlin.idea.core.moveCaretIntoGeneratedElement +import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMembersHandler import org.jetbrains.kotlin.idea.core.overrideImplement.generateMember import org.jetbrains.kotlin.idea.core.replaced @@ -42,6 +43,7 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import javax.swing.Icon class OverridesCompletion( private val collector: LookupElementsCollector, @@ -85,79 +87,20 @@ class OverridesCompletion( val baseClassDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(position.project, baseClass) val baseClassIcon = KotlinDescriptorIconProvider.getIcon(baseClass, baseClassDeclaration, 0) - lookupElement = object : LookupElementDecorator(lookupElement) { - override fun getLookupString() = - if (declaration == null) "override" else delegate.lookupString // don't use "override" as lookup string when already in the name of declaration - - override fun getAllLookupStrings() = setOf(lookupString, delegate.lookupString) - - override fun renderElement(presentation: LookupElementPresentation) { - super.renderElement(presentation) - - presentation.itemText = text - presentation.isItemTextBold = isImplement - presentation.icon = icon - presentation.clearTail() - presentation.setTypeText(baseClassName, baseClassIcon) - } - - override fun handleInsert(context: InsertionContext) { - val dummyMemberHead = when { - declaration != null -> "" - isConstructorParameter -> "override val " - else -> "override fun " - } - val dummyMemberTail = when { - isConstructorParameter || declaration is KtProperty -> "dummy: Dummy ,@" - else -> "dummy() {}" - } - val dummyMemberText = dummyMemberHead + dummyMemberTail - val override = KtTokens.OVERRIDE_KEYWORD.value - - tailrec fun calcStartOffset(startOffset: Int, diff: Int = 0): Int { - return when { - context.document.text[startOffset - 1].isWhitespace() -> calcStartOffset(startOffset - 1, diff + 1) - context.document.text.substring(startOffset - override.length, startOffset) == override -> { - startOffset - override.length - } - else -> diff + startOffset - } - } - - val startOffset = calcStartOffset(context.startOffset) - val tailOffset = context.tailOffset - context.document.replaceString(startOffset, tailOffset, dummyMemberText) - - val psiDocumentManager = PsiDocumentManager.getInstance(context.project) - psiDocumentManager.commitAllDocuments() - - val dummyMember = context.file.findElementAt(startOffset)!!.getStrictParentOfType()!! - - // keep original modifiers - val modifierList = KtPsiFactory(context.project).createModifierList(dummyMember.modifierList!!.text) - - val prototype = memberObject.generateMember(classOrObject, false) - prototype.modifierList!!.replace(modifierList) - val insertedMember = dummyMember.replaced(prototype) - if (memberObject.descriptor.isSuspend) insertedMember.addModifier(KtTokens.SUSPEND_KEYWORD) - - ShortenReferences.DEFAULT.process(insertedMember) - - if (isConstructorParameter) { - psiDocumentManager.doPostponedOperationsAndUnblockDocument(context.document) - - val offset = insertedMember.endOffset - val chars = context.document.charsSequence - val commaOffset = chars.indexOfSkippingSpace(',', offset)!! - val atCharOffset = chars.indexOfSkippingSpace('@', commaOffset + 1)!! - context.document.deleteString(offset, atCharOffset + 1) - - context.editor.moveCaret(offset) - } else { - moveCaretIntoGeneratedElement(context.editor, insertedMember) - } - } - } + lookupElement = OverridesCompletionLookupElementDecorator( + lookupElement, + declaration, + text, + isImplement, + icon, + baseClassName, + baseClassIcon, + isConstructorParameter, + classOrObject, + memberObject.descriptor.isSuspend, + memberObject::generateMember, + ShortenReferences.DEFAULT::process, + ) lookupElement.assignPriority(if (isImplement) ItemPriority.IMPLEMENT else ItemPriority.OVERRIDE) diff --git a/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/completion/OverridesCompletionLookupElementDecorator.kt b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/completion/OverridesCompletionLookupElementDecorator.kt new file mode 100644 index 00000000000..b99787dcfc1 --- /dev/null +++ b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/completion/OverridesCompletionLookupElementDecorator.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.completion + +import com.intellij.codeInsight.completion.InsertionContext +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.lookup.LookupElementDecorator +import com.intellij.codeInsight.lookup.LookupElementPresentation +import com.intellij.psi.PsiDocumentManager +import com.intellij.ui.RowIcon +import org.jetbrains.kotlin.idea.completion.handlers.indexOfSkippingSpace +import org.jetbrains.kotlin.idea.core.moveCaret +import org.jetbrains.kotlin.idea.core.moveCaretIntoGeneratedElement +import org.jetbrains.kotlin.idea.core.replaced +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import javax.swing.Icon + +class OverridesCompletionLookupElementDecorator( + lookupElement: LookupElement, + private val declaration: KtCallableDeclaration?, + private val text: String, + private val isImplement: Boolean, + private val icon: RowIcon, + private val baseClassName: String, + private val baseClassIcon: Icon?, + private val isConstructorParameter: Boolean, + private val classOrObject: KtClassOrObject, + private val isSuspend: Boolean, + private val generateMember: (targetClass: KtClassOrObject, copyDoc: Boolean) -> KtCallableDeclaration, + private val shortenReferences: (KtElement) -> Unit, +) : LookupElementDecorator(lookupElement) { + override fun getLookupString() = + if (declaration == null) "override" else delegate.lookupString // don't use "override" as lookup string when already in the name of declaration + + override fun getAllLookupStrings() = setOf(lookupString, delegate.lookupString) + + override fun renderElement(presentation: LookupElementPresentation) { + super.renderElement(presentation) + + presentation.itemText = text + presentation.isItemTextBold = isImplement + presentation.icon = icon + presentation.clearTail() + presentation.setTypeText(baseClassName, baseClassIcon) + } + + override fun handleInsert(context: InsertionContext) { + val dummyMemberHead = when { + declaration != null -> "" + isConstructorParameter -> "override val " + else -> "override fun " + } + val dummyMemberTail = when { + isConstructorParameter || declaration is KtProperty -> "dummy: Dummy ,@" + else -> "dummy() {}" + } + val dummyMemberText = dummyMemberHead + dummyMemberTail + val override = KtTokens.OVERRIDE_KEYWORD.value + + tailrec fun calcStartOffset(startOffset: Int, diff: Int = 0): Int { + return when { + context.document.text[startOffset - 1].isWhitespace() -> calcStartOffset(startOffset - 1, diff + 1) + context.document.text.substring(startOffset - override.length, startOffset) == override -> { + startOffset - override.length + } + else -> diff + startOffset + } + } + + val startOffset = calcStartOffset(context.startOffset) + val tailOffset = context.tailOffset + context.document.replaceString(startOffset, tailOffset, dummyMemberText) + + val psiDocumentManager = PsiDocumentManager.getInstance(context.project) + psiDocumentManager.commitAllDocuments() + + val dummyMember = context.file.findElementAt(startOffset)!!.getStrictParentOfType()!! + + // keep original modifiers + val modifierList = KtPsiFactory(context.project).createModifierList(dummyMember.modifierList!!.text) + + val prototype = generateMember(classOrObject, false) + prototype.modifierList!!.replace(modifierList) + val insertedMember = dummyMember.replaced(prototype) + if (isSuspend) insertedMember.addModifier(KtTokens.SUSPEND_KEYWORD) + + shortenReferences(insertedMember) + + if (isConstructorParameter) { + psiDocumentManager.doPostponedOperationsAndUnblockDocument(context.document) + + val offset = insertedMember.endOffset + val chars = context.document.charsSequence + val commaOffset = chars.indexOfSkippingSpace(',', offset)!! + val atCharOffset = chars.indexOfSkippingSpace('@', commaOffset + 1)!! + context.document.deleteString(offset, atCharOffset + 1) + + context.editor.moveCaret(offset) + } else { + moveCaretIntoGeneratedElement(context.editor, insertedMember) + } + } +} \ No newline at end of file