Drop bunch code by introduction wrapper for PopupChooserBuilder
This commit is contained in:
+1
@@ -115,6 +115,7 @@
|
||||
<Problem reference="com.intellij.openapi.extensions.ExtensionPointName#extensions(com.intellij.openapi.extensions.AreaInstance)" reason="Absent in 182." />
|
||||
<Problem reference="com.intellij.openapi.extensions.ExtensionPointName#getPoint" reason="Absent in 182." />
|
||||
<Problem reference="com.intellij.openapi.extensions.ExtensionPointName#findExtensionOrFail" reason="Absent in 182." />
|
||||
<Problem reference="com.intellij.openapi.ui.popup.PopupChooserBuilder#PopupChooserBuilder(javax.swing.JList)" reason="Generified in 182. Use PopupChooserBuilderWrapper instead." />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
|
||||
+2
-1
@@ -48,6 +48,7 @@ import org.jetbrains.kotlin.idea.core.overrideImplement.generateUnsupportedOrSup
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.setupEditorSelection
|
||||
import org.jetbrains.kotlin.idea.refactoring.j2k
|
||||
import org.jetbrains.kotlin.idea.testIntegration.findSuitableFrameworks
|
||||
import org.jetbrains.kotlin.idea.util.PopupChooserBuilderWrapper
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -74,7 +75,7 @@ abstract class KotlinGenerateTestSupportActionBase(
|
||||
val list = JBList<TestFramework>(*frameworks.toTypedArray())
|
||||
list.cellRenderer = TestFrameworkListCellRenderer()
|
||||
|
||||
PopupChooserBuilder<TestFramework>(list)
|
||||
PopupChooserBuilderWrapper<TestFramework>(list)
|
||||
.setFilteringEnabled { (it as TestFramework).name }
|
||||
.setTitle("Choose Framework")
|
||||
.setItemChoosenCallback { consumer(list.selectedValue as TestFramework) }
|
||||
|
||||
-261
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.actions.generate
|
||||
|
||||
import com.intellij.codeInsight.generation.actions.GenerateActionPopupTemplateInjector
|
||||
import com.intellij.codeInsight.hint.HintManager
|
||||
import com.intellij.ide.fileTemplates.FileTemplateManager
|
||||
import com.intellij.ide.fileTemplates.impl.AllFileTemplatesConfigurable
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.InputValidator
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.ui.popup.PopupChooserBuilder
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElementFactory
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.testIntegration.JavaTestFramework
|
||||
import com.intellij.testIntegration.TestFramework
|
||||
import com.intellij.testIntegration.TestIntegrationUtils.MethodKind
|
||||
import com.intellij.ui.components.JBList
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.core.insertMember
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.BodyType
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.generateUnsupportedOrSuperCall
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.setupEditorSelection
|
||||
import org.jetbrains.kotlin.idea.refactoring.j2k
|
||||
import org.jetbrains.kotlin.idea.testIntegration.findSuitableFrameworks
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isIdentifier
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded
|
||||
import org.jetbrains.kotlin.utils.ifEmpty
|
||||
|
||||
abstract class KotlinGenerateTestSupportActionBase(
|
||||
private val methodKind : MethodKind
|
||||
) : KotlinGenerateActionBase(), GenerateActionPopupTemplateInjector {
|
||||
companion object {
|
||||
private fun findTargetClass(editor: Editor, file: PsiFile): KtClassOrObject? {
|
||||
val elementAtCaret = file.findElementAt(editor.caretModel.offset) ?: return null
|
||||
return elementAtCaret.parentsWithSelf.filterIsInstance<KtClassOrObject>().firstOrNull { !it.isLocal }
|
||||
}
|
||||
|
||||
private fun chooseAndPerform(editor: Editor, frameworks: List<TestFramework>, consumer: (TestFramework) -> Unit) {
|
||||
frameworks.ifEmpty { return }
|
||||
frameworks.singleOrNull()?.let { return consumer(it) }
|
||||
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) return consumer(frameworks.first())
|
||||
|
||||
val list = JBList<TestFramework>(*frameworks.toTypedArray())
|
||||
list.cellRenderer = TestFrameworkListCellRenderer()
|
||||
|
||||
PopupChooserBuilder(list)
|
||||
.setFilteringEnabled { (it as TestFramework).name }
|
||||
.setTitle("Choose Framework")
|
||||
.setItemChoosenCallback { consumer(list.selectedValue as TestFramework) }
|
||||
.setMovable(true)
|
||||
.createPopup()
|
||||
.showInBestPositionFor(editor)
|
||||
}
|
||||
|
||||
private val BODY_VAR = "\${BODY}"
|
||||
private val NAME_VAR = "\${NAME}"
|
||||
|
||||
private val NAME_VALIDATOR = object : InputValidator {
|
||||
override fun checkInput(inputString: String) = inputString.quoteIfNeeded().isIdentifier()
|
||||
override fun canClose(inputString: String) = true
|
||||
}
|
||||
}
|
||||
|
||||
class SetUp : KotlinGenerateTestSupportActionBase(MethodKind.SET_UP) {
|
||||
override fun isApplicableTo(framework: TestFramework, targetClass: KtClassOrObject): Boolean {
|
||||
return framework.findSetUpMethod(targetClass.toLightClass()!!) == null
|
||||
}
|
||||
}
|
||||
|
||||
class Test : KotlinGenerateTestSupportActionBase(MethodKind.TEST) {
|
||||
override fun isApplicableTo(framework: TestFramework, targetClass: KtClassOrObject) = true
|
||||
}
|
||||
|
||||
class Data : KotlinGenerateTestSupportActionBase(MethodKind.DATA) {
|
||||
override fun isApplicableTo(framework: TestFramework, targetClass: KtClassOrObject): Boolean {
|
||||
if (framework !is JavaTestFramework) return false
|
||||
return framework.findParametersMethod(targetClass.toLightClass()) == null
|
||||
}
|
||||
}
|
||||
|
||||
class TearDown : KotlinGenerateTestSupportActionBase(MethodKind.TEAR_DOWN) {
|
||||
override fun isApplicableTo(framework: TestFramework, targetClass: KtClassOrObject): Boolean {
|
||||
return framework.findTearDownMethod(targetClass.toLightClass()!!) == null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTargetClass(editor: Editor, file: PsiFile): KtClassOrObject? {
|
||||
return findTargetClass(editor, file)
|
||||
}
|
||||
|
||||
override fun isValidForClass(targetClass: KtClassOrObject): Boolean {
|
||||
return findSuitableFrameworks(targetClass).any { isApplicableTo(it, targetClass) }
|
||||
}
|
||||
|
||||
protected abstract fun isApplicableTo(framework: TestFramework, targetClass: KtClassOrObject): Boolean
|
||||
|
||||
override fun invoke(project: Project, editor: Editor, file: PsiFile) {
|
||||
val klass = findTargetClass(editor, file) ?: return
|
||||
|
||||
if (testFrameworkToUse != null) {
|
||||
val frameworkToUse = findSuitableFrameworks(klass).first { it.name == testFrameworkToUse }
|
||||
if (isApplicableTo(frameworkToUse, klass)) {
|
||||
doGenerate(editor, file, klass, frameworkToUse)
|
||||
}
|
||||
}
|
||||
else {
|
||||
val frameworks = findSuitableFrameworks(klass)
|
||||
.filter { methodKind.getFileTemplateDescriptor(it) != null && isApplicableTo(it, klass) }
|
||||
|
||||
chooseAndPerform(editor, frameworks) { doGenerate(editor, file, klass, it) }
|
||||
}
|
||||
}
|
||||
|
||||
var testFrameworkToUse: String? = null
|
||||
|
||||
private val DUMMY_NAME = "__KOTLIN_RULEZZZ__"
|
||||
|
||||
private fun doGenerate(editor: Editor, file: PsiFile, klass: KtClassOrObject, framework: TestFramework) {
|
||||
val project = file.project
|
||||
val commandName = "Generate test function"
|
||||
|
||||
val fileTemplateDescriptor = methodKind.getFileTemplateDescriptor(framework)
|
||||
val fileTemplate = FileTemplateManager.getInstance(project).getCodeTemplate(fileTemplateDescriptor.fileName)
|
||||
var templateText = fileTemplate.text.replace(BODY_VAR, "")
|
||||
var name: String? = null
|
||||
if (templateText.contains(NAME_VAR)) {
|
||||
name = if (templateText.contains("test$NAME_VAR")) "Name" else "name"
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
name = Messages.showInputDialog("Choose test name: ", commandName, null, name, NAME_VALIDATOR)
|
||||
?: return
|
||||
}
|
||||
|
||||
templateText = fileTemplate.text.replace(NAME_VAR, DUMMY_NAME)
|
||||
}
|
||||
|
||||
try {
|
||||
var errorHint: String? = null
|
||||
project.executeWriteCommand(commandName) {
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||
|
||||
val factory = PsiElementFactory.SERVICE.getInstance(project)
|
||||
val psiMethod = factory.createMethodFromText(templateText, null)
|
||||
psiMethod.throwsList.referenceElements.forEach { it.delete() }
|
||||
var function = psiMethod.j2k() as? KtNamedFunction ?: run {
|
||||
errorHint = "Couldn't convert Java template to Kotlin"
|
||||
return@executeWriteCommand
|
||||
}
|
||||
name?.let {
|
||||
function = substituteNewName(function, it)
|
||||
}
|
||||
val functionInPlace = insertMember(editor, klass, function)
|
||||
|
||||
val functionDescriptor = functionInPlace.unsafeResolveToDescriptor() as FunctionDescriptor
|
||||
val overriddenDescriptors = functionDescriptor.overriddenDescriptors
|
||||
val bodyText = when (overriddenDescriptors.size) {
|
||||
0 -> generateUnsupportedOrSuperCall(project, functionDescriptor, BodyType.FROM_TEMPLATE)
|
||||
1 -> generateUnsupportedOrSuperCall(project, overriddenDescriptors.single(), BodyType.SUPER)
|
||||
else -> generateUnsupportedOrSuperCall(project, overriddenDescriptors.first(), BodyType.QUALIFIED_SUPER)
|
||||
}
|
||||
functionInPlace.bodyExpression?.delete()
|
||||
functionInPlace.add(KtPsiFactory(project).createBlock(bodyText))
|
||||
|
||||
if (overriddenDescriptors.isNotEmpty()) {
|
||||
functionInPlace.addModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||
}
|
||||
|
||||
setupEditorSelection(editor, functionInPlace)
|
||||
}
|
||||
errorHint?.let { HintManager.getInstance().showErrorHint(editor, it) }
|
||||
}
|
||||
catch (e: IncorrectOperationException) {
|
||||
HintManager.getInstance().showErrorHint(editor, "Cannot generate method: " + e.message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun substituteNewName(function: KtNamedFunction, name: String): KtNamedFunction {
|
||||
val psiFactory = KtPsiFactory(function)
|
||||
|
||||
// First replace all DUMMY_NAME occurrences in names as they need special treatment due to quotation
|
||||
var function1 = function
|
||||
function1.accept(
|
||||
object : KtTreeVisitorVoid() {
|
||||
private fun getNewId(currentId: String): String? {
|
||||
if (!currentId.contains(DUMMY_NAME)) return null
|
||||
return currentId.replace(DUMMY_NAME, name).quoteIfNeeded()
|
||||
}
|
||||
|
||||
override fun visitNamedDeclaration(declaration: KtNamedDeclaration) {
|
||||
val nameIdentifier = declaration.nameIdentifier ?: return
|
||||
val newId = getNewId(nameIdentifier.text) ?: return
|
||||
declaration.setName(newId)
|
||||
}
|
||||
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
val newId = getNewId(expression.text) ?: return
|
||||
expression.replace(psiFactory.createSimpleName(newId))
|
||||
}
|
||||
}
|
||||
)
|
||||
// Then text-replace remaining occurrences (if any)
|
||||
val functionText = function1.text
|
||||
if (functionText.contains(DUMMY_NAME)) {
|
||||
function1 = psiFactory.createFunction(function1.text.replace(DUMMY_NAME, name))
|
||||
}
|
||||
return function1
|
||||
}
|
||||
|
||||
override fun createEditTemplateAction(dataContext: DataContext): AnAction? {
|
||||
val project = CommonDataKeys.PROJECT.getData(dataContext) ?: return null
|
||||
val editor = CommonDataKeys.EDITOR.getData(dataContext) ?: return null
|
||||
val file = CommonDataKeys.PSI_FILE.getData(dataContext) ?: return null
|
||||
|
||||
val targetClass = getTargetClass(editor, file) ?: return null
|
||||
val frameworks = findSuitableFrameworks(targetClass).ifEmpty { return null }
|
||||
|
||||
return object : AnAction("Edit Template") {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
chooseAndPerform(editor, frameworks) {
|
||||
val descriptor = methodKind.getFileTemplateDescriptor(it)
|
||||
if (descriptor == null) {
|
||||
HintManager.getInstance().showErrorHint(editor, "No template found for ${it.name}:${templatePresentation.text}")
|
||||
return@chooseAndPerform
|
||||
}
|
||||
|
||||
AllFileTemplatesConfigurable.editCodeTemplate(FileUtil.getNameWithoutExtension(descriptor.fileName), project)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ import org.jetbrains.kotlin.idea.refactoring.isAbstract
|
||||
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
|
||||
import org.jetbrains.kotlin.idea.util.PopupChooserBuilderWrapper
|
||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||
@@ -213,7 +214,7 @@ abstract class ImplementAbstractMemberIntentionBase :
|
||||
selectionMode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
|
||||
cellRenderer = renderer
|
||||
}
|
||||
val builder = PopupChooserBuilder<PsiElement>(list)
|
||||
val builder = PopupChooserBuilderWrapper<PsiElement>(list)
|
||||
renderer.installSpeedSearch(builder)
|
||||
builder
|
||||
.setTitle(CodeInsightBundle.message("intention.implement.abstract.method.class.chooser.title"))
|
||||
|
||||
-268
@@ -1,268 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightBundle
|
||||
import com.intellij.codeInsight.FileModificationService
|
||||
import com.intellij.codeInsight.generation.OverrideImplementUtil
|
||||
import com.intellij.ide.util.PsiClassListCellRenderer
|
||||
import com.intellij.ide.util.PsiElementListCellRenderer
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor
|
||||
import com.intellij.openapi.ui.popup.PopupChooserBuilder
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.ui.components.JBList
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||
import org.jetbrains.kotlin.asJava.toLightMethods
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideImplementMembersHandler
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
|
||||
import org.jetbrains.kotlin.idea.refactoring.isAbstract
|
||||
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
|
||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
|
||||
import org.jetbrains.kotlin.util.findCallableMemberBySignature
|
||||
import java.util.*
|
||||
import javax.swing.ListSelectionModel
|
||||
|
||||
abstract class ImplementAbstractMemberIntentionBase :
|
||||
SelfTargetingRangeIntention<KtNamedDeclaration>(KtNamedDeclaration::class.java, "", "Implement abstract member") {
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance("#${ImplementAbstractMemberIntentionBase::class.java.canonicalName}")
|
||||
}
|
||||
|
||||
protected fun findExistingImplementation(
|
||||
subClass: ClassDescriptor,
|
||||
superMember: CallableMemberDescriptor
|
||||
): CallableMemberDescriptor? {
|
||||
val superClass = superMember.containingDeclaration as? ClassDescriptor ?: return null
|
||||
val substitutor = getTypeSubstitutor(superClass.defaultType, subClass.defaultType) ?: TypeSubstitutor.EMPTY
|
||||
val signatureInSubClass = superMember.substitute(substitutor) as? CallableMemberDescriptor ?: return null
|
||||
val subMember = subClass.findCallableMemberBySignature(signatureInSubClass)
|
||||
return if (subMember?.kind?.isReal == true) subMember else null
|
||||
}
|
||||
|
||||
protected abstract fun acceptSubClass(subClassDescriptor: ClassDescriptor, memberDescriptor: CallableMemberDescriptor): Boolean
|
||||
|
||||
private fun findClassesToProcess(member: KtNamedDeclaration): Sequence<PsiElement> {
|
||||
val baseClass = member.containingClassOrObject as? KtClass ?: return emptySequence()
|
||||
val memberDescriptor = member.resolveToDescriptorIfAny() as? CallableMemberDescriptor ?: return emptySequence()
|
||||
|
||||
fun acceptSubClass(subClass: PsiElement): Boolean {
|
||||
val classDescriptor = when (subClass) {
|
||||
is KtLightClass -> subClass.kotlinOrigin?.resolveToDescriptorIfAny()
|
||||
is KtEnumEntry -> subClass.resolveToDescriptorIfAny()
|
||||
is PsiClass -> subClass.getJavaClassDescriptor()
|
||||
else -> null
|
||||
} ?: return false
|
||||
return acceptSubClass(classDescriptor, memberDescriptor)
|
||||
}
|
||||
|
||||
if (baseClass.isEnum()) {
|
||||
return baseClass.declarations
|
||||
.asSequence()
|
||||
.filterIsInstance<KtEnumEntry>()
|
||||
.filter(::acceptSubClass)
|
||||
}
|
||||
|
||||
return HierarchySearchRequest(baseClass, baseClass.useScope, false)
|
||||
.searchInheritors()
|
||||
.asSequence()
|
||||
.filter(::acceptSubClass)
|
||||
}
|
||||
|
||||
protected abstract fun computeText(element: KtNamedDeclaration): String?
|
||||
|
||||
override fun applicabilityRange(element: KtNamedDeclaration): TextRange? {
|
||||
if (!element.isAbstract()) return null
|
||||
|
||||
text = computeText(element) ?: return null
|
||||
|
||||
if (!findClassesToProcess(element).any()) return null
|
||||
|
||||
return element.nameIdentifier?.textRange
|
||||
}
|
||||
|
||||
protected abstract val preferConstructorParameters: Boolean
|
||||
|
||||
private fun implementInKotlinClass(editor: Editor?, member: KtNamedDeclaration, targetClass: KtClassOrObject) {
|
||||
val subClassDescriptor = targetClass.resolveToDescriptorIfAny() ?: return
|
||||
val superMemberDescriptor = member.resolveToDescriptorIfAny() as? CallableMemberDescriptor ?: return
|
||||
val superClassDescriptor = superMemberDescriptor.containingDeclaration as? ClassDescriptor ?: return
|
||||
val substitutor = getTypeSubstitutor(superClassDescriptor.defaultType, subClassDescriptor.defaultType)
|
||||
?: TypeSubstitutor.EMPTY
|
||||
val descriptorToImplement = superMemberDescriptor.substitute(substitutor) as CallableMemberDescriptor
|
||||
val chooserObject = OverrideMemberChooserObject.create(member.project,
|
||||
descriptorToImplement,
|
||||
descriptorToImplement,
|
||||
OverrideMemberChooserObject.BodyType.FROM_TEMPLATE,
|
||||
preferConstructorParameters)
|
||||
OverrideImplementMembersHandler.generateMembers(editor, targetClass, listOf(chooserObject), false)
|
||||
}
|
||||
|
||||
private fun implementInJavaClass(member: KtNamedDeclaration, targetClass: PsiClass) {
|
||||
member.toLightMethods().forEach { OverrideImplementUtil.overrideOrImplement(targetClass, it) }
|
||||
}
|
||||
|
||||
private fun implementInClass(member: KtNamedDeclaration, targetClasses: List<PsiElement>) {
|
||||
val project = member.project
|
||||
project.executeCommand(CodeInsightBundle.message("intention.implement.abstract.method.command.name")) {
|
||||
if (!FileModificationService.getInstance().preparePsiElementsForWrite(targetClasses)) return@executeCommand
|
||||
runWriteAction {
|
||||
for (targetClass in targetClasses) {
|
||||
try {
|
||||
val descriptor = OpenFileDescriptor(project, targetClass.containingFile.virtualFile)
|
||||
val targetEditor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true)!!
|
||||
when (targetClass) {
|
||||
is KtLightClass -> targetClass.kotlinOrigin?.let { implementInKotlinClass(targetEditor, member, it) }
|
||||
is KtEnumEntry -> implementInKotlinClass(targetEditor, member, targetClass)
|
||||
is PsiClass -> implementInJavaClass(member, targetClass)
|
||||
}
|
||||
}
|
||||
catch(e: IncorrectOperationException) {
|
||||
LOG.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ClassRenderer : PsiElementListCellRenderer<PsiElement>() {
|
||||
private val psiClassRenderer = PsiClassListCellRenderer()
|
||||
|
||||
override fun getComparator(): Comparator<PsiElement> {
|
||||
val baseComparator = psiClassRenderer.comparator
|
||||
return Comparator { o1, o2 ->
|
||||
when {
|
||||
o1 is KtEnumEntry && o2 is KtEnumEntry -> o1.name!!.compareTo(o2.name!!)
|
||||
o1 is KtEnumEntry -> -1
|
||||
o2 is KtEnumEntry -> 1
|
||||
o1 is PsiClass && o2 is PsiClass -> baseComparator.compare(o1, o2)
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getIconFlags() = 0
|
||||
|
||||
override fun getElementText(element: PsiElement?): String? {
|
||||
return when (element) {
|
||||
is KtEnumEntry -> element.name
|
||||
is PsiClass -> psiClassRenderer.getElementText(element)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getContainerText(element: PsiElement?, name: String?): String? {
|
||||
return when (element) {
|
||||
is KtEnumEntry -> element.containingClassOrObject?.fqName?.asString()
|
||||
is PsiClass -> PsiClassListCellRenderer.getContainerTextStatic(element)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun applyTo(element: KtNamedDeclaration, editor: Editor?) {
|
||||
if (editor == null) throw IllegalArgumentException("This intention requires an editor")
|
||||
val project = element.project
|
||||
|
||||
val classesToProcess = project.runSynchronouslyWithProgress(
|
||||
CodeInsightBundle.message("intention.implement.abstract.method.searching.for.descendants.progress"),
|
||||
true
|
||||
) { findClassesToProcess(element).toList() } ?: return
|
||||
if (classesToProcess.isEmpty()) return
|
||||
|
||||
classesToProcess.singleOrNull()?.let { return implementInClass(element, listOf(it)) }
|
||||
|
||||
val renderer = ClassRenderer()
|
||||
val sortedClasses = classesToProcess.sortedWith(renderer.comparator)
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) return implementInClass(element, sortedClasses)
|
||||
|
||||
val list = JBList(sortedClasses).apply {
|
||||
selectionMode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
|
||||
cellRenderer = renderer
|
||||
}
|
||||
val builder = PopupChooserBuilder(list)
|
||||
renderer.installSpeedSearch(builder)
|
||||
builder
|
||||
.setTitle(CodeInsightBundle.message("intention.implement.abstract.method.class.chooser.title"))
|
||||
.setItemChoosenCallback {
|
||||
val index = list.selectedIndex
|
||||
if (index < 0) return@setItemChoosenCallback
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
implementInClass(element, list.selectedValues.toList() as List<KtClassOrObject>)
|
||||
}
|
||||
.createPopup()
|
||||
.showInBestPositionFor(editor)
|
||||
}
|
||||
}
|
||||
|
||||
class ImplementAbstractMemberIntention : ImplementAbstractMemberIntentionBase() {
|
||||
override fun computeText(element: KtNamedDeclaration): String? {
|
||||
return when(element) {
|
||||
is KtProperty -> "Implement abstract property"
|
||||
is KtNamedFunction -> "Implement abstract function"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun acceptSubClass(subClassDescriptor: ClassDescriptor, memberDescriptor: CallableMemberDescriptor): Boolean {
|
||||
return subClassDescriptor.kind != ClassKind.INTERFACE && findExistingImplementation(subClassDescriptor, memberDescriptor) == null
|
||||
}
|
||||
|
||||
override val preferConstructorParameters: Boolean
|
||||
get() = false
|
||||
}
|
||||
|
||||
class ImplementAbstractMemberAsConstructorParameterIntention : ImplementAbstractMemberIntentionBase() {
|
||||
override fun computeText(element: KtNamedDeclaration): String? {
|
||||
if (element !is KtProperty) return null
|
||||
return "Implement as constructor parameter"
|
||||
}
|
||||
|
||||
override fun acceptSubClass(subClassDescriptor: ClassDescriptor, memberDescriptor: CallableMemberDescriptor): Boolean {
|
||||
val kind = subClassDescriptor.kind
|
||||
return (kind == ClassKind.CLASS || kind == ClassKind.ENUM_CLASS)
|
||||
&& subClassDescriptor !is JavaClassDescriptor
|
||||
&& findExistingImplementation(subClassDescriptor, memberDescriptor) == null
|
||||
}
|
||||
|
||||
override val preferConstructorParameters: Boolean
|
||||
get() = true
|
||||
|
||||
override fun applicabilityRange(element: KtNamedDeclaration): TextRange? {
|
||||
if (element !is KtProperty) return null
|
||||
return super.applicabilityRange(element)
|
||||
}
|
||||
}
|
||||
@@ -74,10 +74,7 @@ import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.toValVar
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KtPsiClassWrapper
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.idea.util.actualsForExpected
|
||||
import org.jetbrains.kotlin.idea.util.liftToExpected
|
||||
import org.jetbrains.kotlin.idea.util.*
|
||||
import org.jetbrains.kotlin.idea.util.string.collapseSpaces
|
||||
import org.jetbrains.kotlin.j2k.ConverterSettings
|
||||
import org.jetbrains.kotlin.j2k.JavaToKotlinConverter
|
||||
@@ -263,7 +260,7 @@ fun <T, E : PsiElement> getPsiElementPopup(
|
||||
}
|
||||
}
|
||||
|
||||
return with(PopupChooserBuilder<E>(list)) {
|
||||
return with(PopupChooserBuilderWrapper<E>(list)) {
|
||||
title?.let { setTitle(it) }
|
||||
renderer.installSpeedSearch(this, true)
|
||||
setItemChoosenCallback {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.util
|
||||
|
||||
import com.intellij.openapi.ui.popup.PopupChooserBuilder
|
||||
import javax.swing.JList
|
||||
|
||||
// BUNCH: 181
|
||||
@Suppress("IncompatibleAPI")
|
||||
class PopupChooserBuilderWrapper<T>(list: JList<T>): PopupChooserBuilder<T>(list)
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.util
|
||||
|
||||
import com.intellij.openapi.ui.popup.PopupChooserBuilder
|
||||
import javax.swing.JList
|
||||
|
||||
// BUNCH: 181
|
||||
@Suppress("IncompatibleAPI")
|
||||
class PopupChooserBuilderWrapper<T>(list: JList<T>) : PopupChooserBuilder(list)
|
||||
Reference in New Issue
Block a user