Make OverridesCompletionLookupElementDecorator available for FIR IDE

This commit is contained in:
Ilya Kirillov
2021-05-10 16:31:23 +02:00
committed by TeamCityServer
parent 8f399652d7
commit 49aba39ae8
2 changed files with 125 additions and 73 deletions
@@ -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>(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<KtNamedDeclaration>()!!
// 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)
@@ -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>(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<KtNamedDeclaration>()!!
// 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)
}
}
}