From 4e9c8cee70d070269649f5429b0885351b9659fd Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Wed, 9 Jan 2019 20:11:30 +0300 Subject: [PATCH] Drop bunch code by introduction wrapper for PopupChooserBuilder --- .idea/inspectionProfiles/idea_default.xml | 1 + .../KotlinGenerateTestSupportActionBase.kt | 3 +- ...KotlinGenerateTestSupportActionBase.kt.181 | 261 ----- .../ImplementAbstractMemberIntention.kt | 3 +- .../ImplementAbstractMemberIntention.kt.181 | 268 ----- .../idea/refactoring/kotlinRefactoringUtil.kt | 7 +- .../refactoring/kotlinRefactoringUtil.kt.181 | 1023 ----------------- .../idea/util/PopupChooserBuilderWrapper.kt | 13 + .../util/PopupChooserBuilderWrapper.kt.181 | 13 + 9 files changed, 33 insertions(+), 1559 deletions(-) delete mode 100644 idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt.181 delete mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/ImplementAbstractMemberIntention.kt.181 delete mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt.181 create mode 100644 idea/src/org/jetbrains/kotlin/idea/util/PopupChooserBuilderWrapper.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/util/PopupChooserBuilderWrapper.kt.181 diff --git a/.idea/inspectionProfiles/idea_default.xml b/.idea/inspectionProfiles/idea_default.xml index 4b65c8d8754..b6352fcebe9 100644 --- a/.idea/inspectionProfiles/idea_default.xml +++ b/.idea/inspectionProfiles/idea_default.xml @@ -115,6 +115,7 @@ + diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt index 4e496e83e4b..e1294cb3eaa 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt @@ -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(*frameworks.toTypedArray()) list.cellRenderer = TestFrameworkListCellRenderer() - PopupChooserBuilder(list) + PopupChooserBuilderWrapper(list) .setFilteringEnabled { (it as TestFramework).name } .setTitle("Choose Framework") .setItemChoosenCallback { consumer(list.selectedValue as TestFramework) } diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt.181 b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt.181 deleted file mode 100644 index 4b260c14500..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt.181 +++ /dev/null @@ -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().firstOrNull { !it.isLocal } - } - - private fun chooseAndPerform(editor: Editor, frameworks: List, consumer: (TestFramework) -> Unit) { - frameworks.ifEmpty { return } - frameworks.singleOrNull()?.let { return consumer(it) } - - if (ApplicationManager.getApplication().isUnitTestMode) return consumer(frameworks.first()) - - val list = JBList(*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) - } - } - } - } -} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ImplementAbstractMemberIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ImplementAbstractMemberIntention.kt index 9d313534f05..8c86217908d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ImplementAbstractMemberIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ImplementAbstractMemberIntention.kt @@ -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(list) + val builder = PopupChooserBuilderWrapper(list) renderer.installSpeedSearch(builder) builder .setTitle(CodeInsightBundle.message("intention.implement.abstract.method.class.chooser.title")) diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ImplementAbstractMemberIntention.kt.181 b/idea/src/org/jetbrains/kotlin/idea/intentions/ImplementAbstractMemberIntention.kt.181 deleted file mode 100644 index f1f6c059788..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ImplementAbstractMemberIntention.kt.181 +++ /dev/null @@ -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::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 { - 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() - .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) { - 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() { - private val psiClassRenderer = PsiClassListCellRenderer() - - override fun getComparator(): Comparator { - 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) - } - .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) - } -} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt index f8b6fde95df..88381e78f33 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt @@ -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 getPsiElementPopup( } } - return with(PopupChooserBuilder(list)) { + return with(PopupChooserBuilderWrapper(list)) { title?.let { setTitle(it) } renderer.installSpeedSearch(this, true) setItemChoosenCallback { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt.181 b/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt.181 deleted file mode 100644 index 0142d5d84f3..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt.181 +++ /dev/null @@ -1,1023 +0,0 @@ -/* - * Copyright 2000-2018 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.refactoring - -import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils -import com.intellij.codeInsight.unwrap.RangeSplitter -import com.intellij.codeInsight.unwrap.UnwrapHandler -import com.intellij.ide.IdeBundle -import com.intellij.ide.util.PsiElementListCellRenderer -import com.intellij.lang.injection.InjectedLanguageManager -import com.intellij.lang.java.JavaLanguage -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.application.TransactionGuard -import com.intellij.openapi.command.CommandAdapter -import com.intellij.openapi.command.CommandEvent -import com.intellij.openapi.command.CommandProcessor -import com.intellij.openapi.editor.Editor -import com.intellij.openapi.editor.colors.EditorColors -import com.intellij.openapi.editor.colors.EditorColorsManager -import com.intellij.openapi.editor.markup.HighlighterTargetArea -import com.intellij.openapi.editor.markup.RangeHighlighter -import com.intellij.openapi.editor.markup.TextAttributes -import com.intellij.openapi.options.ConfigurationException -import com.intellij.openapi.project.Project -import com.intellij.openapi.roots.JavaProjectRootsUtil -import com.intellij.openapi.ui.DialogWrapper -import com.intellij.openapi.ui.Messages -import com.intellij.openapi.ui.popup.* -import com.intellij.openapi.util.Pass -import com.intellij.openapi.util.TextRange -import com.intellij.openapi.util.text.StringUtil -import com.intellij.openapi.vfs.LocalFileSystem -import com.intellij.openapi.vfs.VirtualFile -import com.intellij.psi.* -import com.intellij.psi.impl.light.LightElement -import com.intellij.psi.presentation.java.SymbolPresentationUtil -import com.intellij.psi.util.PsiTreeUtil -import com.intellij.refactoring.BaseRefactoringProcessor.ConflictsInTestsException -import com.intellij.refactoring.changeSignature.ChangeSignatureUtil -import com.intellij.refactoring.listeners.RefactoringEventData -import com.intellij.refactoring.listeners.RefactoringEventListener -import com.intellij.refactoring.ui.ConflictsDialog -import com.intellij.refactoring.util.ConflictsUtil -import com.intellij.refactoring.util.RefactoringUIUtil -import com.intellij.ui.components.JBList -import com.intellij.usageView.UsageViewTypeLocation -import com.intellij.util.VisibilityUtil -import com.intellij.util.containers.MultiMap -import org.jetbrains.kotlin.asJava.LightClassUtil -import org.jetbrains.kotlin.asJava.elements.KtLightMethod -import org.jetbrains.kotlin.asJava.getAccessorLightMethods -import org.jetbrains.kotlin.asJava.namedUnwrappedElement -import org.jetbrains.kotlin.asJava.toLightClass -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor -import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor -import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.idea.KotlinBundle -import org.jetbrains.kotlin.idea.KotlinFileType -import org.jetbrains.kotlin.idea.KotlinLanguage -import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall -import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny -import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor -import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaMemberDescriptor -import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde -import org.jetbrains.kotlin.idea.core.* -import org.jetbrains.kotlin.idea.core.util.showYesNoCancelDialog -import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices -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.string.collapseSpaces -import org.jetbrains.kotlin.j2k.ConverterSettings -import org.jetbrains.kotlin.j2k.JavaToKotlinConverter -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.FqNameUnsafe -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode -import org.jetbrains.kotlin.psi.psiUtil.* -import org.jetbrains.kotlin.renderer.DescriptorRenderer -import org.jetbrains.kotlin.resolve.* -import org.jetbrains.kotlin.resolve.calls.callUtil.getCallWithAssert -import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode -import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver -import org.jetbrains.kotlin.resolve.source.getPsi -import java.io.File -import java.lang.annotation.Retention -import java.util.* -import javax.swing.Icon - -val CHECK_SUPER_METHODS_YES_NO_DIALOG = "CHECK_SUPER_METHODS_YES_NO_DIALOG" - -@JvmOverloads -fun getOrCreateKotlinFile(fileName: String, - targetDir: PsiDirectory, - packageName: String? = targetDir.getPackage()?.qualifiedName): KtFile? = - (targetDir.findFile(fileName) ?: createKotlinFile(fileName, targetDir, packageName)) as? KtFile - -fun createKotlinFile(fileName: String, - targetDir: PsiDirectory, - packageName: String? = targetDir.getPackage()?.qualifiedName): KtFile { - targetDir.checkCreateFile(fileName) - val packageFqName = packageName?.let(::FqName) ?: FqName.ROOT - val file = PsiFileFactory.getInstance(targetDir.project).createFileFromText( - fileName, KotlinFileType.INSTANCE, if (!packageFqName.isRoot) "package ${packageFqName.quoteSegmentsIfNeeded()} \n\n" else "" - ) - - return targetDir.add(file) as KtFile -} - -fun File.toVirtualFile(): VirtualFile? = LocalFileSystem.getInstance().findFileByIoFile(this) - -fun File.toPsiFile(project: Project): PsiFile? = toVirtualFile()?.toPsiFile(project) - -fun File.toPsiDirectory(project: Project): PsiDirectory? { - return toVirtualFile()?.let { vfile -> PsiManager.getInstance(project).findDirectory(vfile) } -} - -fun VirtualFile.toPsiFile(project: Project): PsiFile? = PsiManager.getInstance(project).findFile(this) - -fun VirtualFile.toPsiDirectory(project: Project): PsiDirectory? = PsiManager.getInstance(project).findDirectory(this) - -fun VirtualFile.toPsiFileOrDirectory(project: Project): PsiFileSystemItem? = if (isDirectory) toPsiDirectory(project) else toPsiFile(project) - -fun PsiElement.getUsageContext(): PsiElement { - return when (this) { - is KtElement -> PsiTreeUtil.getParentOfType( - this, - KtPropertyAccessor::class.java, - KtProperty::class.java, - KtNamedFunction::class.java, - KtConstructor::class.java, - KtClassOrObject::class.java - ) ?: containingFile - else -> ConflictsUtil.getContainer(this) - } -} - -fun PsiElement.isInJavaSourceRoot(): Boolean = - !JavaProjectRootsUtil.isOutsideJavaSourceRoot(containingFile) - -fun KtFile.createTempCopy(text: String? = null): KtFile { - val tmpFile = KtPsiFactory(this).createAnalyzableFile(name, text ?: this.text ?: "", this) - tmpFile.originalFile = this - tmpFile.suppressDiagnosticsInDebugMode = suppressDiagnosticsInDebugMode - return tmpFile -} - -fun PsiElement.getAllExtractionContainers(strict: Boolean = true): List { - val containers = ArrayList() - - var objectOrNonInnerNestedClassFound = false - val parents = if (strict) parents else parentsWithSelf - for (element in parents) { - val isValidContainer = when (element) { - is KtFile -> true - is KtClassBody -> !objectOrNonInnerNestedClassFound || element.parent is KtObjectDeclaration - is KtBlockExpression -> !objectOrNonInnerNestedClassFound - else -> false - } - if (!isValidContainer) continue - - containers.add(element as KtElement) - - if (!objectOrNonInnerNestedClassFound) { - val bodyParent = (element as? KtClassBody)?.parent - objectOrNonInnerNestedClassFound = - (bodyParent is KtObjectDeclaration && !bodyParent.isObjectLiteral()) - || (bodyParent is KtClass && !bodyParent.isInner()) - } - } - - return containers -} - -fun PsiElement.getExtractionContainers(strict: Boolean = true, includeAll: Boolean = false): List { - fun getEnclosingDeclaration(element: PsiElement, strict: Boolean): PsiElement? { - return (if (strict) element.parents else element.parentsWithSelf) - .filter { - (it is KtDeclarationWithBody && it !is KtFunctionLiteral && !(it is KtNamedFunction && it.name == null)) - || it is KtAnonymousInitializer - || it is KtClassBody - || it is KtFile - } - .firstOrNull() - } - - if (includeAll) return getAllExtractionContainers(strict) - - val enclosingDeclaration = getEnclosingDeclaration(this, strict)?.let { - if (it is KtDeclarationWithBody || it is KtAnonymousInitializer) getEnclosingDeclaration(it, true) else it - } - - return when (enclosingDeclaration) { - is KtFile -> Collections.singletonList(enclosingDeclaration) - is KtClassBody -> getAllExtractionContainers(strict).filterIsInstance() - else -> { - val targetContainer = when (enclosingDeclaration) { - is KtDeclarationWithBody -> enclosingDeclaration.bodyExpression - is KtAnonymousInitializer -> enclosingDeclaration.body - else -> null - } - if (targetContainer is KtBlockExpression) Collections.singletonList(targetContainer) else Collections.emptyList() - } - } -} - -fun Project.checkConflictsInteractively( - conflicts: MultiMap, - onShowConflicts: () -> Unit = {}, - onAccept: () -> Unit) { - if (!conflicts.isEmpty) { - if (ApplicationManager.getApplication()!!.isUnitTestMode) throw ConflictsInTestsException(conflicts.values()) - - val dialog = ConflictsDialog(this, conflicts) { onAccept() } - dialog.show() - if (!dialog.isOK) { - if (dialog.isShowConflicts) { - onShowConflicts() - } - return - } - } - - onAccept() -} - -fun reportDeclarationConflict( - conflicts: MultiMap, - declaration: PsiElement, - message: (renderedDeclaration: String) -> String -) { - conflicts.putValue(declaration, message(RefactoringUIUtil.getDescription(declaration, true).capitalize())) -} - -fun getPsiElementPopup( - editor: Editor, - elements: List, - renderer: PsiElementListCellRenderer, - title: String?, - highlightSelection: Boolean, - toPsi: (T) -> E, - processor: (T) -> Boolean): JBPopup { - val highlighter = if (highlightSelection) SelectionAwareScopeHighlighter(editor) else null - - val list = JBList(elements.map(toPsi)) - list.cellRenderer = renderer - list.addListSelectionListener { - highlighter?.dropHighlight() - val index = list.selectedIndex - if (index >= 0) { - highlighter?.highlight(list.model!!.getElementAt(index) as PsiElement) - } - } - - return with(PopupChooserBuilder(list)) { - title?.let { setTitle(it) } - renderer.installSpeedSearch(this, true) - setItemChoosenCallback { - val index = list.selectedIndex - if (index >= 0) { - processor(elements[index]) - } - } - addListener(object : JBPopupAdapter() { - override fun onClosed(event: LightweightWindowEvent?) { - highlighter?.dropHighlight() - } - }) - - createPopup() - } -} - -class SelectionAwareScopeHighlighter(val editor: Editor) { - private val highlighters = ArrayList() - - private fun addHighlighter(r: TextRange, attr: TextAttributes) { - highlighters.add( - editor.markupModel.addRangeHighlighter( - r.startOffset, - r.endOffset, - UnwrapHandler.HIGHLIGHTER_LEVEL, - attr, - HighlighterTargetArea.EXACT_RANGE - ) - ) - } - - fun highlight(wholeAffected: PsiElement) { - dropHighlight() - - val affectedRange = wholeAffected.textRange ?: return - - val attributes = EditorColorsManager.getInstance().globalScheme.getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES)!! - val selectedRange = with(editor.selectionModel) { TextRange(selectionStart, selectionEnd) } - for (r in RangeSplitter.split(affectedRange, Collections.singletonList(selectedRange))) { - addHighlighter(r, attributes) - } - } - - fun dropHighlight() { - highlighters.forEach { it.dispose() } - highlighters.clear() - } -} - -fun PsiFile.getLineStartOffset(line: Int): Int? { - val doc = viewProvider.document ?: PsiDocumentManager.getInstance(project).getDocument(this) - if (doc != null && line >= 0 && line < doc.lineCount) { - val startOffset = doc.getLineStartOffset(line) - val element = findElementAt(startOffset) ?: return startOffset - - if (element is PsiWhiteSpace || element is PsiComment) { - return PsiTreeUtil.skipSiblingsForward(element, PsiWhiteSpace::class.java, PsiComment::class.java)?.startOffset ?: startOffset - } - return startOffset - } - - return null -} - -fun PsiFile.getLineEndOffset(line: Int): Int? { - val document = viewProvider.document ?: PsiDocumentManager.getInstance(project).getDocument(this) - return document?.getLineEndOffset(line) -} - -fun PsiElement.getLineNumber(start: Boolean = true): Int { - val document = containingFile.viewProvider.document ?: PsiDocumentManager.getInstance(project).getDocument(containingFile) - return document?.getLineNumber(if (start) this.startOffset else this.endOffset) ?: 0 -} - -fun PsiElement.getLineCount(): Int { - val doc = containingFile?.let { file -> PsiDocumentManager.getInstance(project).getDocument(file) } - if (doc != null) { - val spaceRange = textRange ?: TextRange.EMPTY_RANGE - - if (spaceRange.endOffset <= doc.textLength) { - val startLine = doc.getLineNumber(spaceRange.startOffset) - val endLine = doc.getLineNumber(spaceRange.endOffset) - - return endLine - startLine - } - } - - return (text ?: "").count { it == '\n' } + 1 -} - -fun PsiElement.isMultiLine(): Boolean = getLineCount() > 1 - -class SeparateFileWrapper(manager: PsiManager) : LightElement(manager, KotlinLanguage.INSTANCE) { - override fun toString() = "" -} - -fun chooseContainerElement( - containers: List, - editor: Editor, - title: String, - highlightSelection: Boolean, - toPsi: (T) -> PsiElement, - onSelect: (T) -> Unit) { - return getPsiElementPopup( - editor, - containers, - object : PsiElementListCellRenderer() { - private fun PsiElement.renderName(): String { - if (this is KtPropertyAccessor) { - return property.renderName() + if (isGetter) ".get" else ".set" - } - if (this is KtObjectDeclaration && this.isCompanion()) { - return "Companion object of ${getStrictParentOfType()?.renderName() ?: ""}" - } - return (this as? PsiNamedElement)?.name ?: "" - } - - private fun PsiElement.renderDeclaration(): String? { - if (this is KtFunctionLiteral || isFunctionalExpression()) return renderText() - - val descriptor = when { - this is KtFile -> name - this is KtElement -> analyze()[BindingContext.DECLARATION_TO_DESCRIPTOR, this] - this is PsiMember -> getJavaMemberDescriptor() - else -> null - } ?: return null - val name = renderName() - val params = (descriptor as? FunctionDescriptor)?.valueParameters - ?.map { DescriptorRenderer.Companion.SHORT_NAMES_IN_TYPES.renderType(it.type) } - ?.joinToString(", ", "(", ")") ?: "" - return "$name$params" - } - - private fun PsiElement.renderText(): String { - if (this is SeparateFileWrapper) return "Extract to separate file" - return StringUtil.shortenTextWithEllipsis(text!!.collapseSpaces(), 53, 0) - } - - private fun PsiElement.getRepresentativeElement(): PsiElement { - return when (this) { - is KtBlockExpression -> (parent as? KtDeclarationWithBody) ?: this - is KtClassBody -> parent as KtClassOrObject - else -> this - } - } - - override fun getElementText(element: PsiElement): String? { - val representativeElement = element.getRepresentativeElement() - return representativeElement.renderDeclaration() ?: representativeElement.renderText() - } - - override fun getContainerText(element: PsiElement, name: String?): String? = null - - override fun getIconFlags(): Int = 0 - - override fun getIcon(element: PsiElement): Icon? = - super.getIcon(element.getRepresentativeElement()) - }, - title, - highlightSelection, - toPsi, - { - onSelect(it) - true - } - ).showInBestPositionFor(editor) -} - -fun chooseContainerElementIfNecessary( - containers: List, - editor: Editor, - title: String, - highlightSelection: Boolean, - toPsi: (T) -> PsiElement, - onSelect: (T) -> Unit -) { - when { - containers.isEmpty() -> return - containers.size == 1 || ApplicationManager.getApplication()!!.isUnitTestMode -> onSelect(containers.first()) - else -> chooseContainerElement(containers, editor, title, highlightSelection, toPsi, onSelect) - } -} - -fun PsiElement.isTrueJavaMethod(): Boolean = this is PsiMethod && this !is KtLightMethod - -fun PsiElement.canRefactor(): Boolean { - if (!this.isValid) return false - - return when { - this is PsiPackage -> - directories.any { it.canRefactor() } - this is KtElement || - this is PsiMember && language == JavaLanguage.INSTANCE || - this is PsiDirectory -> - ProjectRootsUtil.isInProjectSource(this, includeScriptsOutsideSourceRoots = true) - else -> - false - } -} - -private fun copyModifierListItems(from: PsiModifierList, to: PsiModifierList, withPsiModifiers: Boolean = true) { - if (withPsiModifiers) { - for (modifier in PsiModifier.MODIFIERS) { - if (from.hasExplicitModifier(modifier)) { - to.setModifierProperty(modifier, true) - } - } - } - for (annotation in from.annotations) { - val annotationName = annotation.qualifiedName ?: continue - - if (Retention::class.java.name != annotationName) { - to.addAnnotation(annotationName) - } - } -} - -private fun copyTypeParameters( - from: T, - to: T, - inserter: (T, PsiTypeParameterList) -> Unit -) where T : PsiTypeParameterListOwner, T : PsiNameIdentifierOwner { - val factory = PsiElementFactory.SERVICE.getInstance((from as PsiElement).project) - val templateTypeParams = from.typeParameterList?.typeParameters ?: PsiTypeParameter.EMPTY_ARRAY - if (templateTypeParams.isNotEmpty()) { - inserter(to, factory.createTypeParameterList()) - val targetTypeParamList = to.typeParameterList - val newTypeParams = templateTypeParams.map { - factory.createTypeParameter(it.name, it.extendsList.referencedTypes) - } - ChangeSignatureUtil.synchronizeList( - targetTypeParamList, - newTypeParams, - { it!!.typeParameters.toList() }, - BooleanArray(newTypeParams.size) - ) - } -} - -fun createJavaMethod(function: KtFunction, targetClass: PsiClass): PsiMethod { - val template = LightClassUtil.getLightClassMethod(function) - ?: throw AssertionError("Can't generate light method: ${function.getElementTextWithContext()}") - return createJavaMethod(template, targetClass) -} - -fun createJavaMethod(template: PsiMethod, targetClass: PsiClass): PsiMethod { - val factory = PsiElementFactory.SERVICE.getInstance(template.project) - val methodToAdd = if (template.isConstructor) { - factory.createConstructor(template.name) - } - else { - factory.createMethod(template.name, template.returnType) - } - val method = targetClass.add(methodToAdd) as PsiMethod - - copyModifierListItems(template.modifierList, method.modifierList) - if (targetClass.isInterface) { - method.modifierList.setModifierProperty(PsiModifier.FINAL, false) - } - - copyTypeParameters(template, method) { method, typeParameterList -> - method.addAfter(typeParameterList, method.modifierList) - } - - val targetParamList = method.parameterList - val newParams = template.parameterList.parameters.map { - val param = factory.createParameter(it.name!!, it.type) - copyModifierListItems(it.modifierList!!, param.modifierList!!) - param - } - ChangeSignatureUtil.synchronizeList( - targetParamList, - newParams, - { it.parameters.toList() }, - BooleanArray(newParams.size) - ) - - if (template.modifierList.hasModifierProperty(PsiModifier.ABSTRACT) || targetClass.isInterface) { - method.body!!.delete() - } - else if (!template.isConstructor) { - CreateFromUsageUtils.setupMethodBody(method) - } - - return method -} - -fun createJavaField(property: KtNamedDeclaration, targetClass: PsiClass): PsiField { - val accessorLightMethods = property.getAccessorLightMethods() - val template = accessorLightMethods.getter - ?: throw AssertionError("Can't generate light method: ${property.getElementTextWithContext()}") - - val factory = PsiElementFactory.SERVICE.getInstance(template.project) - val field = targetClass.add(factory.createField(property.name!!, template.returnType!!)) as PsiField - - with(field.modifierList!!) { - val templateModifiers = template.modifierList - setModifierProperty(VisibilityUtil.getVisibilityModifier(templateModifiers), true) - if ((property as KtValVarKeywordOwner).valOrVarKeyword.toValVar() != KotlinValVar.Var || targetClass.isInterface) { - setModifierProperty(PsiModifier.FINAL, true) - } - copyModifierListItems(templateModifiers, this, false) - } - - return field -} - -fun createJavaClass(klass: KtClass, targetClass: PsiClass?, forcePlainClass: Boolean = false): PsiClass { - val kind = if (forcePlainClass) ClassKind.CLASS else (klass.unsafeResolveToDescriptor() as ClassDescriptor).kind - - val factory = PsiElementFactory.SERVICE.getInstance(klass.project) - val className = klass.name!! - val javaClassToAdd = when (kind) { - ClassKind.CLASS -> factory.createClass(className) - ClassKind.INTERFACE -> factory.createInterface(className) - ClassKind.ANNOTATION_CLASS -> factory.createAnnotationType(className) - ClassKind.ENUM_CLASS -> factory.createEnum(className) - else -> throw AssertionError("Unexpected class kind: ${klass.getElementTextWithContext()}") - } - val javaClass = (targetClass?.add(javaClassToAdd) ?: javaClassToAdd) as PsiClass - - val template = klass.toLightClass() ?: throw AssertionError("Can't generate light class: ${klass.getElementTextWithContext()}") - - copyModifierListItems(template.modifierList!!, javaClass.modifierList!!) - if (template.isInterface) { - javaClass.modifierList!!.setModifierProperty(PsiModifier.ABSTRACT, false) - } - - copyTypeParameters(template, javaClass) { klass, typeParameterList -> - klass.addAfter(typeParameterList, klass.nameIdentifier) - } - - // Turning interface to class - if (!javaClass.isInterface && template.isInterface) { - val implementsList = factory.createReferenceListWithRole( - template.extendsList?.referenceElements ?: PsiJavaCodeReferenceElement.EMPTY_ARRAY, - PsiReferenceList.Role.IMPLEMENTS_LIST - ) - implementsList?.let { javaClass.implementsList?.replace(it) } - } - else { - val extendsList = factory.createReferenceListWithRole( - template.extendsList?.referenceElements ?: PsiJavaCodeReferenceElement.EMPTY_ARRAY, - PsiReferenceList.Role.EXTENDS_LIST - ) - extendsList?.let { javaClass.extendsList?.replace(it) } - - val implementsList = factory.createReferenceListWithRole( - template.implementsList?.referenceElements ?: PsiJavaCodeReferenceElement.EMPTY_ARRAY, - PsiReferenceList.Role.IMPLEMENTS_LIST - ) - implementsList?.let { javaClass.implementsList?.replace(it) } - } - - for (method in template.methods) { - val hasParams = method.parameterList.parametersCount > 0 - val needSuperCall = !template.isEnum && - (template.superClass?.constructors ?: PsiMethod.EMPTY_ARRAY).all { - it.parameterList.parametersCount > 0 - } - if (method.isConstructor && !(hasParams || needSuperCall)) continue - with(createJavaMethod(method, javaClass)) { - if (isConstructor && needSuperCall) { - body!!.add(factory.createStatementFromText("super();", this)) - } - } - } - - return javaClass -} - -fun PsiElement.j2kText(): String? { - if (language != JavaLanguage.INSTANCE) return null - - val j2kConverter = JavaToKotlinConverter(project, - ConverterSettings.Companion.defaultSettings, - IdeaJavaToKotlinServices) - return j2kConverter.elementsToKotlin(listOf(this)).results.single()?.text ?: return null //TODO: insert imports -} - -fun PsiExpression.j2k(): KtExpression? { - val text = j2kText() ?: return null - return KtPsiFactory(project).createExpression(text) -} - -fun PsiMember.j2k(): KtNamedDeclaration? { - val text = j2kText() ?: return null - return KtPsiFactory(project).createDeclaration(text) -} - -internal fun broadcastRefactoringExit(project: Project, refactoringId: String) { - project.messageBus.syncPublisher(KotlinRefactoringEventListener.EVENT_TOPIC).onRefactoringExit(refactoringId) -} - -// IMPORTANT: Target refactoring must support KotlinRefactoringEventListener -internal abstract class CompositeRefactoringRunner( - val project: Project, - val refactoringId: String -) { - protected abstract fun runRefactoring() - - protected open fun onRefactoringDone() {} - protected open fun onExit() {} - - fun run() { - val connection = project.messageBus.connect() - connection.subscribe( - RefactoringEventListener.REFACTORING_EVENT_TOPIC, - object : RefactoringEventListener { - override fun undoRefactoring(refactoringId: String) { - - } - - override fun refactoringStarted(refactoringId: String, beforeData: RefactoringEventData?) { - - } - - override fun conflictsDetected(refactoringId: String, conflictsData: RefactoringEventData) { - - } - - override fun refactoringDone(refactoringId: String, afterData: RefactoringEventData?) { - if (refactoringId == this@CompositeRefactoringRunner.refactoringId) { - onRefactoringDone() - } - } - } - ) - connection.subscribe( - KotlinRefactoringEventListener.EVENT_TOPIC, - object : KotlinRefactoringEventListener { - override fun onRefactoringExit(refactoringId: String) { - if (refactoringId == this@CompositeRefactoringRunner.refactoringId) { - try { - onExit() - } finally { - connection.disconnect() - } - } - } - } - ) - runRefactoring() - } -} - -@Throws(ConfigurationException::class) fun KtElement?.validateElement(errorMessage: String) { - if (this == null) throw ConfigurationException(errorMessage) - - try { - AnalyzingUtils.checkForSyntacticErrors(this) - } - catch(e: Exception) { - throw ConfigurationException(errorMessage) - } -} - -fun invokeOnceOnCommandFinish(action: () -> Unit) { - val commandProcessor = CommandProcessor.getInstance() - val listener = object : CommandAdapter() { - override fun beforeCommandFinished(event: CommandEvent?) { - action() - commandProcessor.removeCommandListener(this) - } - } - commandProcessor.addCommandListener(listener) -} - -fun FqNameUnsafe.hasIdentifiersOnly(): Boolean = pathSegments().all { it.asString().quoteIfNeeded().isIdentifier() } - -fun FqName.hasIdentifiersOnly(): Boolean = pathSegments().all { it.asString().quoteIfNeeded().isIdentifier() } - -fun PsiNamedElement.isInterfaceClass(): Boolean = when (this) { - is KtClass -> isInterface() - is PsiClass -> isInterface - is KtPsiClassWrapper -> psiClass.isInterface - else -> false -} - -fun KtNamedDeclaration.isAbstract(): Boolean { - if (hasModifier(KtTokens.ABSTRACT_KEYWORD)) return true - if (!(containingClassOrObject?.isInterfaceClass() ?: false)) return false - return when (this) { - is KtProperty -> initializer == null && delegate == null && accessors.isEmpty() - is KtNamedFunction -> !hasBody() - else -> false - } -} - -fun KtNamedDeclaration.isConstructorDeclaredProperty() = this is KtParameter && ownerFunction is KtPrimaryConstructor && hasValOrVar() - -fun replaceListPsiAndKeepDelimiters( - originalList: ListType, - newList: ListType, - @Suppress("UNCHECKED_CAST") listReplacer: ListType.(ListType) -> ListType = { replace(it) as ListType }, - itemsFun: ListType.() -> List -): ListType { - originalList.children.takeWhile { it is PsiErrorElement }.forEach { it.delete() } - - val oldParameters = originalList.itemsFun().toMutableList() - val newParameters = newList.itemsFun() - val oldCount = oldParameters.size - val newCount = newParameters.size - - val commonCount = Math.min(oldCount, newCount) - for (i in 0..commonCount - 1) { - oldParameters[i] = oldParameters[i].replace(newParameters[i]) as KtElement - } - - if (commonCount == 0) return originalList.listReplacer(newList) - - val lastOriginalParameter = oldParameters.last() - - if (oldCount > commonCount) { - originalList.deleteChildRange(oldParameters[commonCount - 1].nextSibling, lastOriginalParameter) - } - else if (newCount > commonCount) { - val psiBeforeLastParameter = lastOriginalParameter.prevSibling - val withMultiline = (psiBeforeLastParameter is PsiWhiteSpace || psiBeforeLastParameter is PsiComment) && psiBeforeLastParameter.textContains('\n') - val extraSpace = if (withMultiline) KtPsiFactory(originalList).createNewLine() else null - originalList.addRangeAfter(newParameters[commonCount - 1].nextSibling, newParameters.last(), lastOriginalParameter) - if (extraSpace != null) { - val addedItems = originalList.itemsFun().subList(commonCount, newCount) - for (addedItem in addedItems) { - val elementBefore = addedItem.prevSibling - if ((elementBefore !is PsiWhiteSpace && elementBefore !is PsiComment) || !elementBefore.textContains('\n')) { - addedItem.parent.addBefore(extraSpace, addedItem) - } - } - } - } - - return originalList -} - -fun Pass(body: (T) -> Unit) = object : Pass() { - override fun pass(t: T) = body(t) -} - -fun KtExpression.removeTemplateEntryBracesIfPossible(): KtExpression { - val parent = parent as? KtBlockStringTemplateEntry ?: return this - val newEntry = if (parent.canDropBraces()) parent.dropBraces() else parent - return newEntry.expression!! -} - -fun dropOverrideKeywordIfNecessary(element: KtNamedDeclaration) { - val callableDescriptor = element.resolveToDescriptorIfAny() as? CallableDescriptor ?: return - if (callableDescriptor.overriddenDescriptors.isEmpty()) { - element.removeModifier(KtTokens.OVERRIDE_KEYWORD) - } -} - -fun dropOperatorKeywordIfNecessary(element: KtNamedDeclaration) { - val callableDescriptor = element.resolveToDescriptorIfAny() as? CallableDescriptor ?: return - val diagnosticHolder = BindingTraceContext() - OperatorModifierChecker.check(element, callableDescriptor, diagnosticHolder, element.languageVersionSettings) - if (diagnosticHolder.bindingContext.diagnostics.any { it.factory == Errors.INAPPLICABLE_OPERATOR_MODIFIER }) { - element.removeModifier(KtTokens.OPERATOR_KEYWORD) - } -} - -fun getQualifiedTypeArgumentList(initializer: KtExpression): KtTypeArgumentList? { - val call = initializer.resolveToCall() ?: return null - val typeArgumentMap = call.typeArguments - val typeArguments = call.candidateDescriptor.typeParameters.mapNotNull { typeArgumentMap[it] } - val renderedList = typeArguments.joinToString(prefix = "<", postfix = ">") { - IdeDescriptorRenderers.SOURCE_CODE_NOT_NULL_TYPE_APPROXIMATION.renderType(it) - } - return KtPsiFactory(initializer).createTypeArguments(renderedList) -} - -fun addTypeArgumentsIfNeeded(expression: KtExpression, typeArgumentList: KtTypeArgumentList) { - val context = expression.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS) - val call = expression.getCallWithAssert(context) - val callElement = call.callElement as? KtCallExpression ?: return - if (call.typeArgumentList != null) return - val callee = call.calleeExpression ?: return - if (context.diagnostics.forElement(callee).all { it.factory != Errors.TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER }) return - - callElement.addAfter(typeArgumentList, callElement.calleeExpression) - ShortenReferences.DEFAULT.process(callElement.typeArgumentList!!) -} - -internal fun DeclarationDescriptor.getThisLabelName(): String { - if (!name.isSpecial) return name.asString() - if (this is AnonymousFunctionDescriptor) { - val function = source.getPsi() as? KtFunction - val argument = function?.parent as? KtValueArgument - ?: (function?.parent as? KtLambdaExpression)?.parent as? KtValueArgument - val callElement = argument?.getStrictParentOfType() - val callee = callElement?.calleeExpression as? KtSimpleNameExpression - if (callee != null) return callee.text - } - return "" -} - -internal fun DeclarationDescriptor.explicateAsTextForReceiver(): String { - val labelName = getThisLabelName() - return if (labelName.isEmpty()) "this" else "this@$labelName" -} - -internal fun ImplicitReceiver.explicateAsText(): String { - return declarationDescriptor.explicateAsTextForReceiver() -} - -val PsiFile.isInjectedFragment: Boolean - get() = InjectedLanguageManager.getInstance(project).isInjectedFragment(this) - -val PsiElement.isInsideInjectedFragment: Boolean - get() = containingFile.isInjectedFragment - -fun checkSuperMethods( - declaration: KtDeclaration, - ignore: Collection?, - actionString: String -): List { - fun getClassDescriptions(overriddenElementsToDescriptor: Map): List { - return overriddenElementsToDescriptor.entries.map { entry -> - val (element, descriptor) = entry - val description = when (element) { - is KtNamedFunction, is KtProperty, is KtParameter -> formatClassDescriptor(descriptor.containingDeclaration) - is PsiMethod -> { - val psiClass = element.containingClass ?: error("Invalid element: ${element.getText()}") - formatPsiClass(psiClass, true, false) - } - else -> error("Unexpected element: ${element.getElementTextWithContext()}") - } - " $description\n" - } - } - - fun askUserForMethodsToSearch( - declarationDescriptor: CallableDescriptor, - overriddenElementsToDescriptor: Map - ): List { - val superClassDescriptions = getClassDescriptions(overriddenElementsToDescriptor) - - val message = KotlinBundle.message( - "x.overrides.y.in.class.list", - DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declarationDescriptor), - "\n${superClassDescriptions.joinToString(separator = "")}", - actionString - ) - - val exitCode = showYesNoCancelDialog( - CHECK_SUPER_METHODS_YES_NO_DIALOG, - declaration.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon(), Messages.YES) - return when (exitCode) { - Messages.YES -> overriddenElementsToDescriptor.keys.toList() - Messages.NO -> listOf(declaration) - else -> emptyList() - } - } - - - val declarationDescriptor = declaration.unsafeResolveToDescriptor() as CallableDescriptor - - if (declarationDescriptor is LocalVariableDescriptor) return listOf(declaration) - - val project = declaration.project - val overriddenElementsToDescriptor = HashMap() - for (overriddenDescriptor in DescriptorUtils.getAllOverriddenDescriptors(declarationDescriptor)) { - val overriddenDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, overriddenDescriptor) ?: continue - if (overriddenDeclaration is KtNamedFunction || overriddenDeclaration is KtProperty || overriddenDeclaration is PsiMethod || overriddenDeclaration is KtParameter) { - overriddenElementsToDescriptor[overriddenDeclaration] = overriddenDescriptor - } - } - if (ignore != null) { - overriddenElementsToDescriptor.keys.removeAll(ignore) - } - - if (overriddenElementsToDescriptor.isEmpty()) return listOf(declaration) - - return askUserForMethodsToSearch(declarationDescriptor, overriddenElementsToDescriptor) -} - -fun checkSuperMethodsWithPopup( - declaration: KtNamedDeclaration, - deepestSuperMethods: List, - actionString: String, - editor: Editor, - action: (List) -> Unit -) { - if (deepestSuperMethods.isEmpty()) return action(listOf(declaration)) - - val superMethod = deepestSuperMethods.first() - - val (superClass, isAbstract) = when (superMethod) { - is PsiMember -> superMethod.containingClass to superMethod.hasModifierProperty(PsiModifier.ABSTRACT) - is KtNamedDeclaration -> superMethod.containingClassOrObject to superMethod.isAbstract() - else -> null - } ?: return action(listOf(declaration)) - if (superClass == null) return action(listOf(declaration)) - - if (ApplicationManager.getApplication().isUnitTestMode) return action(deepestSuperMethods) - - val kind = when (declaration) { - is KtNamedFunction -> "function" - is KtProperty, is KtParameter -> "property" - else -> return - } - - val unwrappedSupers = deepestSuperMethods.mapNotNull { it.namedUnwrappedElement } - val hasJavaMethods = unwrappedSupers.any { it is PsiMethod } - val hasKtMembers = unwrappedSupers.any { it is KtNamedDeclaration } - val superKind = when { - hasJavaMethods && hasKtMembers -> "member" - hasJavaMethods -> "method" - else -> kind - } - - val renameBase = actionString + " base $superKind" + (if (deepestSuperMethods.size > 1) "s" else "") - val renameCurrent = actionString + " only current $kind" - val title = buildString { - append(declaration.name) - append(if (isAbstract) " implements " else " overrides ") - append(ElementDescriptionUtil.getElementDescription(superMethod, UsageViewTypeLocation.INSTANCE)) - append(" of ") - append(SymbolPresentationUtil.getSymbolPresentableText(superClass)) - } - val list = JBList(renameBase, renameCurrent) - JBPopupFactory.getInstance() - .createListPopupBuilder(list) - .setTitle(title) - .setMovable(false) - .setResizable(false) - .setRequestFocus(true) - .setItemChoosenCallback { - val value = list.selectedValue ?: return@setItemChoosenCallback - val chosenElements = if (value == renameBase) deepestSuperMethods + declaration else listOf(declaration) - action(chosenElements) - } - .createPopup() - .showInBestPositionFor(editor) -} - -fun KtNamedDeclaration.isCompanionMemberOf(klass: KtClassOrObject): Boolean { - val containingObject = containingClassOrObject as? KtObjectDeclaration ?: return false - return containingObject.isCompanion() && containingObject.containingClassOrObject == klass -} - -internal fun KtDeclaration.withExpectedActuals(): List { - val expect = liftToExpected() ?: return listOf(this) - val actuals = expect.actualsForExpected() - return listOf(expect) + actuals -} - -internal fun KtDeclaration.resolveToExpectedDescriptorIfPossible(): DeclarationDescriptor { - val descriptor = unsafeResolveToDescriptor() - return descriptor.liftToExpected() ?: descriptor -} - -fun DialogWrapper.showWithTransaction() { - TransactionGuard.submitTransaction(disposable, Runnable { show() }) -} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/util/PopupChooserBuilderWrapper.kt b/idea/src/org/jetbrains/kotlin/idea/util/PopupChooserBuilderWrapper.kt new file mode 100644 index 00000000000..2e07056337f --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/util/PopupChooserBuilderWrapper.kt @@ -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(list: JList): PopupChooserBuilder(list) \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/util/PopupChooserBuilderWrapper.kt.181 b/idea/src/org/jetbrains/kotlin/idea/util/PopupChooserBuilderWrapper.kt.181 new file mode 100644 index 00000000000..5e4cb710a27 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/util/PopupChooserBuilderWrapper.kt.181 @@ -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(list: JList) : PopupChooserBuilder(list) \ No newline at end of file