Refactoring: Split KotlinRefactoringUtil2.kt to several files

This commit is contained in:
Alexey Sedunov
2016-08-31 14:53:36 +03:00
parent b6a671c45f
commit f6d0fca57b
11 changed files with 248 additions and 221 deletions
@@ -22,7 +22,7 @@ import com.intellij.psi.PsiWhiteSpace;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.KotlinBundle;
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtil2Kt;
import org.jetbrains.kotlin.idea.refactoring.ElementSelectionUtilsKt;
import org.jetbrains.kotlin.psi.KtBlockExpression;
import org.jetbrains.kotlin.psi.KtElement;
import org.jetbrains.kotlin.psi.KtExpression;
@@ -44,7 +44,7 @@ public abstract class KotlinUnwrapRemoveBase extends AbstractUnwrapper<KotlinUnw
@Override
public String getDescription(PsiElement e) {
assert e instanceof KtElement;
return KotlinBundle.message(key, KotlinRefactoringUtil2Kt.getExpressionShortText((KtElement) e));
return KotlinBundle.message(key, ElementSelectionUtilsKt.getExpressionShortText((KtElement) e));
}
protected boolean canExtractExpression(@NotNull KtExpression expression, @NotNull KtElement parent) {
@@ -33,7 +33,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.asJava.LightClassUtilsKt;
import org.jetbrains.kotlin.idea.KotlinBundle;
import org.jetbrains.kotlin.idea.findUsages.KotlinClassFindUsagesOptions;
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtil2Kt;
import org.jetbrains.kotlin.idea.refactoring.RenderingUtilsKt;
import org.jetbrains.kotlin.psi.KtClass;
import org.jetbrains.kotlin.psi.KtClassOrObject;
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
@@ -154,7 +154,7 @@ public class KotlinFindClassUsagesDialog extends FindClassUsagesDialog {
: klass.getUserData(ORIGINAL_CLASS);
if (originalClass != null) {
coloredComponent.append(KotlinRefactoringUtil2Kt.formatClass(originalClass));
coloredComponent.append(RenderingUtilsKt.formatClass(originalClass));
}
}
@@ -26,7 +26,7 @@ import com.intellij.ui.SimpleColoredComponent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.KotlinBundle;
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions;
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtil2Kt;
import org.jetbrains.kotlin.idea.refactoring.RenderingUtilsKt;
import javax.swing.*;
@@ -50,7 +50,7 @@ public class KotlinFindFunctionUsagesDialog extends FindMethodUsagesDialog {
@Override
public void configureLabelComponent(@NotNull SimpleColoredComponent coloredComponent) {
coloredComponent.append(KotlinRefactoringUtil2Kt.formatJavaOrLightMethod((PsiMethod) myPsiElement));
coloredComponent.append(RenderingUtilsKt.formatJavaOrLightMethod((PsiMethod) myPsiElement));
}
@Override
@@ -17,41 +17,23 @@
package org.jetbrains.kotlin.idea.refactoring
import com.intellij.codeInsight.unwrap.ScopeHighlighter
import com.intellij.ide.IdeBundle
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.popup.JBPopupAdapter
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.ui.popup.LightweightWindowEvent
import com.intellij.psi.*
import com.intellij.psi.search.searches.OverridingMethodsSearch
import com.intellij.psi.util.PsiFormatUtil
import com.intellij.psi.util.PsiFormatUtilBase
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.ui.components.JBList
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.refactoring.introduce.findExpressionOrStringFragment
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import java.awt.Component
import java.lang.RuntimeException
@@ -60,192 +42,6 @@ import javax.swing.DefaultListCellRenderer
import javax.swing.DefaultListModel
import javax.swing.JList
fun wrapOrSkip(s: String, inCode: Boolean) = if (inCode) "<code>$s</code>" else s
fun formatClassDescriptor(classDescriptor: DeclarationDescriptor) = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(classDescriptor)
fun formatPsiClass(
psiClass: PsiClass,
markAsJava: Boolean,
inCode: Boolean
): String {
var description: String
val kind = if (psiClass.isInterface) "interface " else "class "
description = kind + PsiFormatUtil.formatClass(
psiClass,
PsiFormatUtilBase.SHOW_CONTAINING_CLASS or PsiFormatUtilBase.SHOW_NAME or PsiFormatUtilBase.SHOW_PARAMETERS or PsiFormatUtilBase.SHOW_TYPE
)
description = wrapOrSkip(description, inCode)
return if (markAsJava) "[Java] $description" else description
}
fun checkSuperMethods(
declaration: KtDeclaration,
ignore: Collection<PsiElement>?,
actionStringKey: String
): List<PsiElement> {
val declarationDescriptor = declaration.resolveToDescriptor() as CallableDescriptor
if (declarationDescriptor is LocalVariableDescriptor) return listOf(declaration)
val project = declaration.project
val overriddenElementsToDescriptor = HashMap<PsiElement, CallableDescriptor>()
for (overriddenDescriptor in DescriptorUtils.getAllOverriddenDescriptors(declarationDescriptor)) {
val overriddenDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, overriddenDescriptor) ?: continue
if (overriddenDeclaration is KtNamedFunction || overriddenDeclaration is KtProperty || overriddenDeclaration is PsiMethod) {
overriddenElementsToDescriptor[overriddenDeclaration] = overriddenDescriptor
}
}
if (ignore != null) {
overriddenElementsToDescriptor.keys.removeAll(ignore)
}
if (overriddenElementsToDescriptor.isEmpty()) return listOf(declaration)
return askUserForMethodsToSearch(declaration, declarationDescriptor, overriddenElementsToDescriptor, actionStringKey)
}
private fun askUserForMethodsToSearch(
declaration: KtDeclaration,
declarationDescriptor: CallableDescriptor,
overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>,
actionStringKey: String
): List<PsiElement> {
if (ApplicationManager.getApplication().isUnitTestMode) return overriddenElementsToDescriptor.keys.toList()
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 = "")}",
KotlinBundle.message(actionStringKey)
)
val exitCode = Messages.showYesNoCancelDialog(declaration.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon())
when (exitCode) {
Messages.YES -> return overriddenElementsToDescriptor.keys.toList()
Messages.NO -> return listOf(declaration)
else -> return emptyList()
}
}
private fun getClassDescriptions(overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>): List<String> {
return overriddenElementsToDescriptor.entries.map { entry ->
val (element, descriptor) = entry
val description = when (element) {
is KtNamedFunction, is KtProperty -> 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 formatClass(classDescriptor: DeclarationDescriptor, inCode: Boolean): String {
val element = DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor)
return if (element is PsiClass) {
formatPsiClass(element, false, inCode)
}
else {
wrapOrSkip(formatClassDescriptor(classDescriptor), inCode)
}
}
fun formatFunction(functionDescriptor: DeclarationDescriptor, inCode: Boolean): String {
val element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor)
return if (element is PsiMethod) {
formatPsiMethod(element, false, inCode)
}
else {
wrapOrSkip(formatFunctionDescriptor(functionDescriptor), inCode)
}
}
private fun formatFunctionDescriptor(functionDescriptor: DeclarationDescriptor) = DescriptorRenderer.COMPACT.render(functionDescriptor)
fun formatPsiMethod(
psiMethod: PsiMethod,
showContainingClass: Boolean,
inCode: Boolean
): String {
var options = PsiFormatUtilBase.SHOW_NAME or PsiFormatUtilBase.SHOW_PARAMETERS or PsiFormatUtilBase.SHOW_TYPE
if (showContainingClass) {
//noinspection ConstantConditions
options = options or PsiFormatUtilBase.SHOW_CONTAINING_CLASS
}
var description = PsiFormatUtil.formatMethod(psiMethod, PsiSubstitutor.EMPTY, options, PsiFormatUtilBase.SHOW_TYPE)
description = wrapOrSkip(description, inCode)
return "[Java] $description"
}
fun formatJavaOrLightMethod(method: PsiMethod): String {
val originalDeclaration = method.unwrapped
return if (originalDeclaration is KtDeclaration) {
formatFunctionDescriptor(originalDeclaration.resolveToDescriptor())
}
else {
formatPsiMethod(method, false, false)
}
}
fun formatClass(classOrObject: KtClassOrObject) = formatClassDescriptor(classOrObject.resolveToDescriptor() as ClassDescriptor)
fun checkParametersInMethodHierarchy(parameter: PsiParameter): Collection<PsiElement>? {
val method = parameter.declarationScope as PsiMethod
val parametersToDelete = collectParametersHierarchy(method, parameter)
if (parametersToDelete.size <= 1 || ApplicationManager.getApplication().isUnitTestMode) return parametersToDelete
val message = KotlinBundle.message("delete.param.in.method.hierarchy", formatJavaOrLightMethod(method))
val exitCode = Messages.showOkCancelDialog(parameter.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon())
return if (exitCode == Messages.OK) parametersToDelete else null
}
// TODO: generalize breadth-first search
private fun collectParametersHierarchy(method: PsiMethod, parameter: PsiParameter): Set<PsiElement> {
val queue = ArrayDeque<PsiMethod>()
val visited = HashSet<PsiMethod>()
val parametersToDelete = HashSet<PsiElement>()
queue.add(method)
while (!queue.isEmpty()) {
val currentMethod = queue.poll()
visited += currentMethod
addParameter(currentMethod, parametersToDelete, parameter)
currentMethod.findSuperMethods(true)
.filter { it !in visited }
.forEach { queue.offer(it) }
OverridingMethodsSearch.search(currentMethod)
.filter { it !in visited }
.forEach { queue.offer(it) }
}
return parametersToDelete
}
private fun addParameter(method: PsiMethod, result: MutableSet<PsiElement>, parameter: PsiParameter) {
val parameterIndex = parameter.unwrapped!!.parameterIndex()
if (method is KtLightMethod) {
val declaration = method.kotlinOrigin
if (declaration is KtFunction) {
result.add(declaration.valueParameters[parameterIndex])
}
}
else {
result.add(method.parameterList.parameters[parameterIndex])
}
}
@Throws(IntroduceRefactoringException::class)
fun selectElement(
editor: Editor,
@@ -19,6 +19,7 @@ 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
@@ -36,6 +37,7 @@ import com.intellij.openapi.options.ConfigurationException
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.JavaProjectRootsUtil
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.popup.JBPopup
import com.intellij.openapi.ui.popup.JBPopupAdapter
import com.intellij.openapi.ui.popup.LightweightWindowEvent
@@ -62,11 +64,14 @@ import org.jetbrains.kotlin.asJava.elements.KtLightMethod
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.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getJavaMemberDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.*
import org.jetbrains.kotlin.idea.intentions.RemoveCurlyBracesFromTemplateIntention
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
@@ -84,6 +89,7 @@ import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.AnalyzingUtils
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getCallWithAssert
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -795,3 +801,68 @@ val PsiFile.isInjectedFragment: Boolean
val PsiElement.isInsideInjectedFragment: Boolean
get() = containingFile.isInjectedFragment
fun checkSuperMethods(
declaration: KtDeclaration,
ignore: Collection<PsiElement>?,
actionStringKey: String
): List<PsiElement> {
fun getClassDescriptions(overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>): List<String> {
return overriddenElementsToDescriptor.entries.map { entry ->
val (element, descriptor) = entry
val description = when (element) {
is KtNamedFunction, is KtProperty -> 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<PsiElement, CallableDescriptor>
): List<PsiElement> {
if (ApplicationManager.getApplication().isUnitTestMode) return overriddenElementsToDescriptor.keys.toList()
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 = "")}",
KotlinBundle.message(actionStringKey)
)
val exitCode = Messages.showYesNoCancelDialog(declaration.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon())
when (exitCode) {
Messages.YES -> return overriddenElementsToDescriptor.keys.toList()
Messages.NO -> return listOf(declaration)
else -> return emptyList()
}
}
val declarationDescriptor = declaration.resolveToDescriptor() as CallableDescriptor
if (declarationDescriptor is LocalVariableDescriptor) return listOf(declaration)
val project = declaration.project
val overriddenElementsToDescriptor = HashMap<PsiElement, CallableDescriptor>()
for (overriddenDescriptor in DescriptorUtils.getAllOverriddenDescriptors(declarationDescriptor)) {
val overriddenDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, overriddenDescriptor) ?: continue
if (overriddenDeclaration is KtNamedFunction || overriddenDeclaration is KtProperty || overriddenDeclaration is PsiMethod) {
overriddenElementsToDescriptor[overriddenDeclaration] = overriddenDescriptor
}
}
if (ignore != null) {
overriddenElementsToDescriptor.keys.removeAll(ignore)
}
if (overriddenElementsToDescriptor.isEmpty()) return listOf(declaration)
return askUserForMethodsToSearch(declarationDescriptor, overriddenElementsToDescriptor)
}
@@ -0,0 +1,104 @@
/*
* 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.refactoring
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiSubstitutor
import com.intellij.psi.util.PsiFormatUtil
import com.intellij.psi.util.PsiFormatUtilBase
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
fun wrapOrSkip(s: String, inCode: Boolean) = if (inCode) "<code>$s</code>" else s
fun formatClassDescriptor(classDescriptor: DeclarationDescriptor) = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(classDescriptor)
fun formatPsiClass(
psiClass: PsiClass,
markAsJava: Boolean,
inCode: Boolean
): String {
var description: String
val kind = if (psiClass.isInterface) "interface " else "class "
description = kind + PsiFormatUtil.formatClass(
psiClass,
PsiFormatUtilBase.SHOW_CONTAINING_CLASS or PsiFormatUtilBase.SHOW_NAME or PsiFormatUtilBase.SHOW_PARAMETERS or PsiFormatUtilBase.SHOW_TYPE
)
description = wrapOrSkip(description, inCode)
return if (markAsJava) "[Java] $description" else description
}
fun formatClass(classDescriptor: DeclarationDescriptor, inCode: Boolean): String {
val element = DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor)
return if (element is PsiClass) {
formatPsiClass(element, false, inCode)
}
else {
wrapOrSkip(formatClassDescriptor(classDescriptor), inCode)
}
}
fun formatFunction(functionDescriptor: DeclarationDescriptor, inCode: Boolean): String {
val element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor)
return if (element is PsiMethod) {
formatPsiMethod(element, false, inCode)
}
else {
wrapOrSkip(formatFunctionDescriptor(functionDescriptor), inCode)
}
}
private fun formatFunctionDescriptor(functionDescriptor: DeclarationDescriptor) = DescriptorRenderer.COMPACT.render(functionDescriptor)
fun formatPsiMethod(
psiMethod: PsiMethod,
showContainingClass: Boolean,
inCode: Boolean
): String {
var options = PsiFormatUtilBase.SHOW_NAME or PsiFormatUtilBase.SHOW_PARAMETERS or PsiFormatUtilBase.SHOW_TYPE
if (showContainingClass) {
//noinspection ConstantConditions
options = options or PsiFormatUtilBase.SHOW_CONTAINING_CLASS
}
var description = PsiFormatUtil.formatMethod(psiMethod, PsiSubstitutor.EMPTY, options, PsiFormatUtilBase.SHOW_TYPE)
description = wrapOrSkip(description, inCode)
return "[Java] $description"
}
fun formatJavaOrLightMethod(method: PsiMethod): String {
val originalDeclaration = method.unwrapped
return if (originalDeclaration is KtDeclaration) {
formatFunctionDescriptor(originalDeclaration.resolveToDescriptor())
}
else {
formatPsiMethod(method, false, false)
}
}
fun formatClass(classOrObject: KtClassOrObject) = formatClassDescriptor(classOrObject.resolveToDescriptor() as ClassDescriptor)
@@ -37,7 +37,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.idea.KotlinBundle;
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtil2Kt;
import org.jetbrains.kotlin.idea.refactoring.RenderingUtilsKt;
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers;
import org.jetbrains.kotlin.psi.KtElement;
import org.jetbrains.kotlin.psi.KtNamedFunction;
@@ -110,7 +110,7 @@ class KotlinOverridingDialog extends DialogWrapper {
assert element instanceof PsiMethod
: "Method accepts only kotlin functions/properties and java methods, but '" + element.getText() + "' was found";
return KotlinRefactoringUtil2Kt.formatPsiMethod((PsiMethod) element, true, false);
return RenderingUtilsKt.formatPsiMethod((PsiMethod) element, true, false);
}
@Override
@@ -39,7 +39,6 @@ import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.deleteElementAndCleanParent
import org.jetbrains.kotlin.idea.refactoring.checkParametersInMethodHierarchy
import org.jetbrains.kotlin.idea.refactoring.checkSuperMethods
import org.jetbrains.kotlin.idea.refactoring.formatClass
import org.jetbrains.kotlin.idea.refactoring.formatFunction
@@ -16,13 +16,22 @@
package org.jetbrains.kotlin.idea.refactoring.safeDelete
import com.intellij.ide.IdeBundle
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.ui.Messages
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiParameter
import com.intellij.psi.search.searches.OverridingMethodsSearch
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.refactoring.formatJavaOrLightMethod
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
import java.util.*
fun PsiElement.canDeleteElement(): Boolean {
if (this is KtObjectDeclaration && isObjectLiteral()) return false
@@ -64,3 +73,51 @@ fun PsiMethod.cleanUpOverrides() {
}
}
}
fun checkParametersInMethodHierarchy(parameter: PsiParameter): Collection<PsiElement>? {
val method = parameter.declarationScope as PsiMethod
val parametersToDelete = collectParametersHierarchy(method, parameter)
if (parametersToDelete.size <= 1 || ApplicationManager.getApplication().isUnitTestMode) return parametersToDelete
val message = KotlinBundle.message("delete.param.in.method.hierarchy", formatJavaOrLightMethod(method))
val exitCode = Messages.showOkCancelDialog(parameter.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon())
return if (exitCode == Messages.OK) parametersToDelete else null
}
// TODO: generalize breadth-first search
private fun collectParametersHierarchy(method: PsiMethod, parameter: PsiParameter): Set<PsiElement> {
val queue = ArrayDeque<PsiMethod>()
val visited = HashSet<PsiMethod>()
val parametersToDelete = HashSet<PsiElement>()
queue.add(method)
while (!queue.isEmpty()) {
val currentMethod = queue.poll()
visited += currentMethod
addParameter(currentMethod, parametersToDelete, parameter)
currentMethod.findSuperMethods(true)
.filter { it !in visited }
.forEach { queue.offer(it) }
OverridingMethodsSearch.search(currentMethod)
.filter { it !in visited }
.forEach { queue.offer(it) }
}
return parametersToDelete
}
private fun addParameter(method: PsiMethod, result: MutableSet<PsiElement>, parameter: PsiParameter) {
val parameterIndex = parameter.unwrapped!!.parameterIndex()
if (method is KtLightMethod) {
val declaration = method.kotlinOrigin
if (declaration is KtFunction) {
result.add(declaration.valueParameters[parameterIndex])
}
}
else {
result.add(method.parameterList.parameters[parameterIndex])
}
}
@@ -23,7 +23,7 @@ import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils;
import org.jetbrains.kotlin.idea.refactoring.IntroduceRefactoringException;
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtil2Kt;
import org.jetbrains.kotlin.idea.refactoring.ElementSelectionUtilsKt;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.test.KotlinTestUtils;
@@ -36,7 +36,7 @@ public abstract class AbstractExpressionSelectionTest extends LightCodeInsightTe
final String expectedExpression = KotlinTestUtils.getLastCommentInFile((KtFile) getFile());
try {
KotlinRefactoringUtil2Kt.selectElement(
ElementSelectionUtilsKt.selectElement(
getEditor(),
(KtFile) getFile(),
Collections.singletonList(CodeInsightUtils.ElementKind.EXPRESSION),
@@ -20,7 +20,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.testFramework.LightCodeInsightTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils;
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtil2Kt;
import org.jetbrains.kotlin.idea.refactoring.ElementSelectionUtilsKt;
import org.jetbrains.kotlin.psi.KtElement;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.test.KotlinTestUtils;
@@ -34,12 +34,12 @@ public abstract class AbstractSmartSelectionTest extends LightCodeInsightTestCas
configureByFile(path);
String expectedResultText = KotlinTestUtils.getLastCommentInFile((KtFile) getFile());
List<KtElement> elements = KotlinRefactoringUtil2Kt.getSmartSelectSuggestions(
List<KtElement> elements = ElementSelectionUtilsKt.getSmartSelectSuggestions(
getFile(), getEditor().getCaretModel().getOffset(), CodeInsightUtils.ElementKind.EXPRESSION);
List<String> textualExpressions = new ArrayList<String>();
for (KtElement element : elements) {
textualExpressions.add(KotlinRefactoringUtil2Kt.getExpressionShortText(element));
textualExpressions.add(ElementSelectionUtilsKt.getExpressionShortText(element));
}
assertEquals(expectedResultText, StringUtil.join(textualExpressions, "\n"));
}