Refactoring & fix warnings

This commit is contained in:
Dmitry Gridin
2019-02-27 19:19:25 +03:00
parent bd2e51ded5
commit 29a63ae58c
3 changed files with 277 additions and 276 deletions
@@ -27,7 +27,6 @@ import com.intellij.psi.PsiNamedElement
import com.intellij.psi.PsiReference import com.intellij.psi.PsiReference
import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.util.containers.MultiMap import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
@@ -59,13 +58,14 @@ import org.jetbrains.kotlin.types.isError
import org.jetbrains.kotlin.types.typeUtil.supertypes import org.jetbrains.kotlin.types.typeUtil.supertypes
import java.util.* import java.util.*
class ConvertFunctionToPropertyIntention : SelfTargetingIntention<KtNamedFunction>(KtNamedFunction::class.java, "Convert function to property"), LowPriorityAction { class ConvertFunctionToPropertyIntention :
SelfTargetingIntention<KtNamedFunction>(KtNamedFunction::class.java, "Convert function to property"), LowPriorityAction {
private var KtNamedFunction.typeFqNameToAdd: String? by UserDataProperty(Key.create("TYPE_FQ_NAME_TO_ADD")) private var KtNamedFunction.typeFqNameToAdd: String? by UserDataProperty(Key.create("TYPE_FQ_NAME_TO_ADD"))
private inner class Converter( private inner class Converter(
project: Project, project: Project,
private val editor: Editor?, private val editor: Editor?,
descriptor: FunctionDescriptor descriptor: FunctionDescriptor
) : CallableRefactoring<FunctionDescriptor>(project, descriptor, text) { ) : CallableRefactoring<FunctionDescriptor>(project, descriptor, text) {
private val elementsToShorten = ArrayList<KtElement>() private val elementsToShorten = ArrayList<KtElement>()
@@ -86,8 +86,7 @@ class ConvertFunctionToPropertyIntention : SelfTargetingIntention<KtNamedFunctio
if (originalFunction.equalsToken != null) { if (originalFunction.equalsToken != null) {
getterExpression(originalFunction.bodyExpression!!.text, breakLine = originalFunction.typeReference != null) getterExpression(originalFunction.bodyExpression!!.text, breakLine = originalFunction.typeReference != null)
} } else {
else {
originalFunction.bodyBlockExpression?.let { body -> originalFunction.bodyBlockExpression?.let { body ->
transform { transform {
append("\nget() ") append("\nget() ")
@@ -121,26 +120,20 @@ class ConvertFunctionToPropertyIntention : SelfTargetingIntention<KtNamedFunctio
val functionDescriptor = callable.unsafeResolveToDescriptor(BodyResolveMode.PARTIAL) as FunctionDescriptor val functionDescriptor = callable.unsafeResolveToDescriptor(BodyResolveMode.PARTIAL) as FunctionDescriptor
val type = functionDescriptor.returnType val type = functionDescriptor.returnType
val typeToInsert = when { val typeToInsert = when {
type == null || type.isError -> null type == null || type.isError -> null
type.constructor.isDenotable -> type type.constructor.isDenotable -> type
else -> type.supertypes().firstOrNull { it.constructor.isDenotable } else -> type.supertypes().firstOrNull { it.constructor.isDenotable }
} ?: functionDescriptor.builtIns.nullableAnyType } ?: functionDescriptor.builtIns.nullableAnyType
callable.typeFqNameToAdd = IdeDescriptorRenderers.SOURCE_CODE.renderType(typeToInsert) callable.typeFqNameToAdd = IdeDescriptorRenderers.SOURCE_CODE.renderType(typeToInsert)
} }
callableDescriptor.getContainingScope() callableDescriptor.getContainingScope()
?.findVariable(callableDescriptor.name, NoLookupLocation.FROM_IDE) ?.findVariable(callableDescriptor.name, NoLookupLocation.FROM_IDE)
?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) } ?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) }
?.let { reportDeclarationConflict(conflicts, it) { "$it already exists" } } ?.let { reportDeclarationConflict(conflicts, it) { s -> "$s already exists" } }
} }
if (callable is PsiMethod) { if (callable is PsiMethod) callable.checkDeclarationConflict(getterName, conflicts, callables)
callable.containingClass
?.findMethodsByName(getterName, true)
// as is necessary here: see KT-10386
?.firstOrNull { it.parameterList.parametersCount == 0 && !callables.contains(it.namedUnwrappedElement as PsiElement?) }
?.let { reportDeclarationConflict(conflicts, it) { "$it already exists" } }
}
val usages = ReferencesSearch.search(callable) val usages = ReferencesSearch.search(callable)
for (usage in usages) { for (usage in usages) {
@@ -150,26 +143,24 @@ class ConvertFunctionToPropertyIntention : SelfTargetingIntention<KtNamedFunctio
if (callElement != null && expression.getStrictParentOfType<KtCallableReferenceExpression>() == null) { if (callElement != null && expression.getStrictParentOfType<KtCallableReferenceExpression>() == null) {
if (callElement.typeArguments.isNotEmpty()) { if (callElement.typeArguments.isNotEmpty()) {
conflicts.putValue( conflicts.putValue(
callElement, callElement,
"Type arguments will be lost after conversion: ${StringUtil.htmlEmphasize(callElement.text)}" "Type arguments will be lost after conversion: ${StringUtil.htmlEmphasize(callElement.text)}"
) )
} }
if (callElement.valueArguments.isNotEmpty()) { if (callElement.valueArguments.isNotEmpty()) {
conflicts.putValue( conflicts.putValue(
callElement, callElement,
"Call with arguments will be skipped: ${StringUtil.htmlEmphasize(callElement.text)}" "Call with arguments will be skipped: ${StringUtil.htmlEmphasize(callElement.text)}"
) )
continue continue
} }
kotlinCalls.add(callElement) kotlinCalls.add(callElement)
} } else {
else {
kotlinRefsToRename.add(usage) kotlinRefsToRename.add(usage)
} }
} } else {
else {
foreignRefs.add(usage) foreignRefs.add(usage)
} }
} }
@@ -25,7 +25,6 @@ import com.intellij.psi.*
import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.util.RefactoringUIUtil import com.intellij.refactoring.util.RefactoringUIUtil
import com.intellij.util.containers.MultiMap import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
@@ -51,11 +50,12 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.utils.findFunction import org.jetbrains.kotlin.resolve.scopes.utils.findFunction
import java.util.* import java.util.*
class ConvertPropertyToFunctionIntention : SelfTargetingIntention<KtProperty>(KtProperty::class.java, "Convert property to function"), LowPriorityAction { class ConvertPropertyToFunctionIntention : SelfTargetingIntention<KtProperty>(KtProperty::class.java, "Convert property to function"),
LowPriorityAction {
private inner class Converter( private inner class Converter(
project: Project, project: Project,
descriptor: CallableDescriptor descriptor: CallableDescriptor
): CallableRefactoring<CallableDescriptor>(project, descriptor, text) { ) : CallableRefactoring<CallableDescriptor>(project, descriptor, text) {
private val newName: String = JvmAbi.getterName(callableDescriptor.name.asString()) private val newName: String = JvmAbi.getterName(callableDescriptor.name.asString())
private fun convertProperty(originalProperty: KtProperty, psiFactory: KtPsiFactory) { private fun convertProperty(originalProperty: KtProperty, psiFactory: KtPsiFactory) {
@@ -69,14 +69,14 @@ class ConvertPropertyToFunctionIntention : SelfTargetingIntention<KtProperty>(Kt
if (property.initializer == null) { if (property.initializer == null) {
if (getter != null) { if (getter != null) {
val dropGetterTo = (getter.equalsToken ?: getter.bodyExpression) val dropGetterTo = (getter.equalsToken ?: getter.bodyExpression)
?.siblings(forward = false, withItself = false) ?.siblings(forward = false, withItself = false)
?.firstOrNull { it !is PsiWhiteSpace } ?.firstOrNull { it !is PsiWhiteSpace }
getter.deleteChildRange(getter.firstChild, dropGetterTo) getter.deleteChildRange(getter.firstChild, dropGetterTo)
val dropPropertyFrom = getter val dropPropertyFrom = getter
.siblings(forward = false, withItself = false) .siblings(forward = false, withItself = false)
.first { it !is PsiWhiteSpace } .first { it !is PsiWhiteSpace }
.nextSibling .nextSibling
property.deleteChildRange(dropPropertyFrom, getter.prevSibling) property.deleteChildRange(dropPropertyFrom, getter.prevSibling)
} }
} }
@@ -97,9 +97,9 @@ class ConvertPropertyToFunctionIntention : SelfTargetingIntention<KtProperty>(Kt
project.runSynchronouslyWithProgress("Looking for usages and conflicts...", true) { project.runSynchronouslyWithProgress("Looking for usages and conflicts...", true) {
runReadAction { runReadAction {
val progressStep = 1.0/callables.size val progressStep = 1.0 / callables.size
for ((i, callable) in callables.withIndex()) { for ((i, callable) in callables.withIndex()) {
ProgressManager.getInstance().progressIndicatorNullable!!.fraction = (i + 1)*progressStep ProgressManager.getInstance().progressIndicatorNullable!!.fraction = (i + 1) * progressStep
if (callable !is PsiNamedElement) continue if (callable !is PsiNamedElement) continue
@@ -110,17 +110,10 @@ class ConvertPropertyToFunctionIntention : SelfTargetingIntention<KtProperty>(Kt
if (callable is KtProperty) { if (callable is KtProperty) {
callableDescriptor.getContainingScope() callableDescriptor.getContainingScope()
?.findFunction(callableDescriptor.name, NoLookupLocation.FROM_IDE) { it.valueParameters.isEmpty() } ?.findFunction(callableDescriptor.name, NoLookupLocation.FROM_IDE) { it.valueParameters.isEmpty() }
?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) } ?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) }
?.let { reportDeclarationConflict(conflicts, it) { "$it already exists" } } ?.let { reportDeclarationConflict(conflicts, it) { s -> "$s already exists" } }
} } else if (callable is PsiMethod) callable.checkDeclarationConflict(propertyName, conflicts, callables)
else if (callable is PsiMethod) {
callable.containingClass
?.findMethodsByName(propertyName, true)
// as is necessary here: see KT-10386
?.firstOrNull { it.parameterList.parametersCount == 0 && !callables.contains(it.namedUnwrappedElement as PsiElement?) }
?.let { reportDeclarationConflict(conflicts, it) { "$it already exists" } }
}
val usages = ReferencesSearch.search(callable) val usages = ReferencesSearch.search(callable)
for (usage in usages) { for (usage in usages) {
@@ -128,18 +121,17 @@ class ConvertPropertyToFunctionIntention : SelfTargetingIntention<KtProperty>(Kt
if (usage is KtSimpleNameReference) { if (usage is KtSimpleNameReference) {
val expression = usage.expression val expression = usage.expression
if (expression.getCall(expression.analyze(BodyResolveMode.PARTIAL)) != null if (expression.getCall(expression.analyze(BodyResolveMode.PARTIAL)) != null
&& expression.getStrictParentOfType<KtCallableReferenceExpression>() == null) { && expression.getStrictParentOfType<KtCallableReferenceExpression>() == null
) {
kotlinRefsToReplaceWithCall.add(expression) kotlinRefsToReplaceWithCall.add(expression)
} } else if (nameChanged) {
else if (nameChanged) {
refsToRename.add(usage) refsToRename.add(usage)
} }
} } else {
else {
val refElement = usage.element val refElement = usage.element
conflicts.putValue( conflicts.putValue(
refElement, refElement,
"Unrecognized reference will be skipped: " + StringUtil.htmlEmphasize(refElement.text) "Unrecognized reference will be skipped: " + StringUtil.htmlEmphasize(refElement.text)
) )
} }
continue continue
@@ -157,8 +149,8 @@ class ConvertPropertyToFunctionIntention : SelfTargetingIntention<KtProperty>(Kt
} }
conflicts.putValue( conflicts.putValue(
refElement, refElement,
"Can't replace foreign reference with call expression: " + StringUtil.htmlEmphasize(refElement.text) "Can't replace foreign reference with call expression: " + StringUtil.htmlEmphasize(refElement.text)
) )
} }
} }
@@ -28,7 +28,10 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.JavaProjectRootsUtil import com.intellij.openapi.roots.JavaProjectRootsUtil
import com.intellij.openapi.ui.DialogWrapper import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.Messages import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.popup.* import com.intellij.openapi.ui.popup.JBPopup
import com.intellij.openapi.ui.popup.JBPopupAdapter
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.ui.popup.LightweightWindowEvent
import com.intellij.openapi.util.Pass import com.intellij.openapi.util.Pass
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
@@ -82,7 +85,6 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.*
@@ -95,21 +97,25 @@ import java.lang.annotation.Retention
import java.util.* import java.util.*
import javax.swing.Icon import javax.swing.Icon
val CHECK_SUPER_METHODS_YES_NO_DIALOG = "CHECK_SUPER_METHODS_YES_NO_DIALOG" const val CHECK_SUPER_METHODS_YES_NO_DIALOG = "CHECK_SUPER_METHODS_YES_NO_DIALOG"
@JvmOverloads @JvmOverloads
fun getOrCreateKotlinFile(fileName: String, fun getOrCreateKotlinFile(
targetDir: PsiDirectory, fileName: String,
packageName: String? = targetDir.getPackage()?.qualifiedName): KtFile? = targetDir: PsiDirectory,
(targetDir.findFile(fileName) ?: createKotlinFile(fileName, targetDir, packageName)) as? KtFile packageName: String? = targetDir.getPackage()?.qualifiedName
): KtFile? =
(targetDir.findFile(fileName) ?: createKotlinFile(fileName, targetDir, packageName)) as? KtFile
fun createKotlinFile(fileName: String, fun createKotlinFile(
targetDir: PsiDirectory, fileName: String,
packageName: String? = targetDir.getPackage()?.qualifiedName): KtFile { targetDir: PsiDirectory,
packageName: String? = targetDir.getPackage()?.qualifiedName
): KtFile {
targetDir.checkCreateFile(fileName) targetDir.checkCreateFile(fileName)
val packageFqName = packageName?.let(::FqName) ?: FqName.ROOT val packageFqName = packageName?.let(::FqName) ?: FqName.ROOT
val file = PsiFileFactory.getInstance(targetDir.project).createFileFromText( val file = PsiFileFactory.getInstance(targetDir.project).createFileFromText(
fileName, KotlinFileType.INSTANCE, if (!packageFqName.isRoot) "package ${packageFqName.quoteSegmentsIfNeeded()} \n\n" else "" fileName, KotlinFileType.INSTANCE, if (!packageFqName.isRoot) "package ${packageFqName.quoteSegmentsIfNeeded()} \n\n" else ""
) )
return targetDir.add(file) as KtFile return targetDir.add(file) as KtFile
@@ -127,24 +133,25 @@ fun VirtualFile.toPsiFile(project: Project): PsiFile? = PsiManager.getInstance(p
fun VirtualFile.toPsiDirectory(project: Project): PsiDirectory? = PsiManager.getInstance(project).findDirectory(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 VirtualFile.toPsiFileOrDirectory(project: Project): PsiFileSystemItem? =
if (isDirectory) toPsiDirectory(project) else toPsiFile(project)
fun PsiElement.getUsageContext(): PsiElement { fun PsiElement.getUsageContext(): PsiElement {
return when (this) { return when (this) {
is KtElement -> PsiTreeUtil.getParentOfType( is KtElement -> PsiTreeUtil.getParentOfType(
this, this,
KtPropertyAccessor::class.java, KtPropertyAccessor::class.java,
KtProperty::class.java, KtProperty::class.java,
KtNamedFunction::class.java, KtNamedFunction::class.java,
KtConstructor::class.java, KtConstructor::class.java,
KtClassOrObject::class.java KtClassOrObject::class.java
) ?: containingFile ) ?: containingFile
else -> ConflictsUtil.getContainer(this) else -> ConflictsUtil.getContainer(this)
} }
} }
fun PsiElement.isInJavaSourceRoot(): Boolean = fun PsiElement.isInJavaSourceRoot(): Boolean =
!JavaProjectRootsUtil.isOutsideJavaSourceRoot(containingFile) !JavaProjectRootsUtil.isOutsideJavaSourceRoot(containingFile)
fun KtFile.createTempCopy(text: String? = null): KtFile { fun KtFile.createTempCopy(text: String? = null): KtFile {
val tmpFile = KtPsiFactory(this).createAnalyzableFile(name, text ?: this.text ?: "", this) val tmpFile = KtPsiFactory(this).createAnalyzableFile(name, text ?: this.text ?: "", this)
@@ -171,8 +178,8 @@ fun PsiElement.getAllExtractionContainers(strict: Boolean = true): List<KtElemen
if (!objectOrNonInnerNestedClassFound) { if (!objectOrNonInnerNestedClassFound) {
val bodyParent = (element as? KtClassBody)?.parent val bodyParent = (element as? KtClassBody)?.parent
objectOrNonInnerNestedClassFound = objectOrNonInnerNestedClassFound =
(bodyParent is KtObjectDeclaration && !bodyParent.isObjectLiteral()) (bodyParent is KtObjectDeclaration && !bodyParent.isObjectLiteral())
|| (bodyParent is KtClass && !bodyParent.isInner()) || (bodyParent is KtClass && !bodyParent.isInner())
} }
} }
@@ -182,13 +189,13 @@ fun PsiElement.getAllExtractionContainers(strict: Boolean = true): List<KtElemen
fun PsiElement.getExtractionContainers(strict: Boolean = true, includeAll: Boolean = false): List<KtElement> { fun PsiElement.getExtractionContainers(strict: Boolean = true, includeAll: Boolean = false): List<KtElement> {
fun getEnclosingDeclaration(element: PsiElement, strict: Boolean): PsiElement? { fun getEnclosingDeclaration(element: PsiElement, strict: Boolean): PsiElement? {
return (if (strict) element.parents else element.parentsWithSelf) return (if (strict) element.parents else element.parentsWithSelf)
.filter { .filter {
(it is KtDeclarationWithBody && it !is KtFunctionLiteral && !(it is KtNamedFunction && it.name == null)) (it is KtDeclarationWithBody && it !is KtFunctionLiteral && !(it is KtNamedFunction && it.name == null))
|| it is KtAnonymousInitializer || it is KtAnonymousInitializer
|| it is KtClassBody || it is KtClassBody
|| it is KtFile || it is KtFile
} }
.firstOrNull() .firstOrNull()
} }
if (includeAll) return getAllExtractionContainers(strict) if (includeAll) return getAllExtractionContainers(strict)
@@ -212,9 +219,10 @@ fun PsiElement.getExtractionContainers(strict: Boolean = true, includeAll: Boole
} }
fun Project.checkConflictsInteractively( fun Project.checkConflictsInteractively(
conflicts: MultiMap<PsiElement, String>, conflicts: MultiMap<PsiElement, String>,
onShowConflicts: () -> Unit = {}, onShowConflicts: () -> Unit = {},
onAccept: () -> Unit) { onAccept: () -> Unit
) {
if (!conflicts.isEmpty) { if (!conflicts.isEmpty) {
if (ApplicationManager.getApplication()!!.isUnitTestMode) throw ConflictsInTestsException(conflicts.values()) if (ApplicationManager.getApplication()!!.isUnitTestMode) throw ConflictsInTestsException(conflicts.values())
@@ -232,21 +240,22 @@ fun Project.checkConflictsInteractively(
} }
fun reportDeclarationConflict( fun reportDeclarationConflict(
conflicts: MultiMap<PsiElement, String>, conflicts: MultiMap<PsiElement, String>,
declaration: PsiElement, declaration: PsiElement,
message: (renderedDeclaration: String) -> String message: (renderedDeclaration: String) -> String
) { ) {
conflicts.putValue(declaration, message(RefactoringUIUtil.getDescription(declaration, true).capitalize())) conflicts.putValue(declaration, message(RefactoringUIUtil.getDescription(declaration, true).capitalize()))
} }
fun <T, E : PsiElement> getPsiElementPopup( fun <T, E : PsiElement> getPsiElementPopup(
editor: Editor, editor: Editor,
elements: List<T>, elements: List<T>,
renderer: PsiElementListCellRenderer<E>, renderer: PsiElementListCellRenderer<E>,
title: String?, title: String?,
highlightSelection: Boolean, highlightSelection: Boolean,
toPsi: (T) -> E, toPsi: (T) -> E,
processor: (T) -> Boolean): JBPopup { processor: (T) -> Boolean
): JBPopup {
val highlighter = if (highlightSelection) SelectionAwareScopeHighlighter(editor) else null val highlighter = if (highlightSelection) SelectionAwareScopeHighlighter(editor) else null
val list = JBList(elements.map(toPsi)) val list = JBList(elements.map(toPsi))
@@ -283,13 +292,13 @@ class SelectionAwareScopeHighlighter(val editor: Editor) {
private fun addHighlighter(r: TextRange, attr: TextAttributes) { private fun addHighlighter(r: TextRange, attr: TextAttributes) {
highlighters.add( highlighters.add(
editor.markupModel.addRangeHighlighter( editor.markupModel.addRangeHighlighter(
r.startOffset, r.startOffset,
r.endOffset, r.endOffset,
UnwrapHandler.HIGHLIGHTER_LEVEL, UnwrapHandler.HIGHLIGHTER_LEVEL,
attr, attr,
HighlighterTargetArea.EXACT_RANGE HighlighterTargetArea.EXACT_RANGE
) )
) )
} }
@@ -359,84 +368,85 @@ class SeparateFileWrapper(manager: PsiManager) : LightElement(manager, KotlinLan
} }
fun <T> chooseContainerElement( fun <T> chooseContainerElement(
containers: List<T>, containers: List<T>,
editor: Editor, editor: Editor,
title: String, title: String,
highlightSelection: Boolean, highlightSelection: Boolean,
toPsi: (T) -> PsiElement, toPsi: (T) -> PsiElement,
onSelect: (T) -> Unit) { onSelect: (T) -> Unit
) {
return getPsiElementPopup( return getPsiElementPopup(
editor, editor,
containers, containers,
object : PsiElementListCellRenderer<PsiElement>() { object : PsiElementListCellRenderer<PsiElement>() {
private fun PsiElement.renderName(): String { private fun PsiElement.renderName(): String {
if (this is KtPropertyAccessor) { if (this is KtPropertyAccessor) {
return property.renderName() + if (isGetter) ".get" else ".set" return property.renderName() + if (isGetter) ".get" else ".set"
}
if (this is KtObjectDeclaration && this.isCompanion()) {
return "Companion object of ${getStrictParentOfType<KtClassOrObject>()?.renderName() ?: "<anonymous>"}"
}
return (this as? PsiNamedElement)?.name ?: "<anonymous>"
} }
if (this is KtObjectDeclaration && this.isCompanion()) {
private fun PsiElement.renderDeclaration(): String? { return "Companion object of ${getStrictParentOfType<KtClassOrObject>()?.renderName() ?: "<anonymous>"}"
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"
} }
return (this as? PsiNamedElement)?.name ?: "<anonymous>"
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
} }
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) ).showInBestPositionFor(editor)
} }
fun <T> chooseContainerElementIfNecessary( fun <T> chooseContainerElementIfNecessary(
containers: List<T>, containers: List<T>,
editor: Editor, editor: Editor,
title: String, title: String,
highlightSelection: Boolean, highlightSelection: Boolean,
toPsi: (T) -> PsiElement, toPsi: (T) -> PsiElement,
onSelect: (T) -> Unit onSelect: (T) -> Unit
) { ) {
when { when {
containers.isEmpty() -> return containers.isEmpty() -> return
@@ -454,8 +464,8 @@ fun PsiElement.canRefactor(): Boolean {
this is PsiPackage -> this is PsiPackage ->
directories.any { it.canRefactor() } directories.any { it.canRefactor() }
this is KtElement || this is KtElement ||
this is PsiMember && language == JavaLanguage.INSTANCE || this is PsiMember && language == JavaLanguage.INSTANCE ||
this is PsiDirectory -> this is PsiDirectory ->
ProjectRootsUtil.isInProjectSource(this, includeScriptsOutsideSourceRoots = true) ProjectRootsUtil.isInProjectSource(this, includeScriptsOutsideSourceRoots = true)
else -> else ->
false false
@@ -480,9 +490,9 @@ private fun copyModifierListItems(from: PsiModifierList, to: PsiModifierList, wi
} }
private fun <T> copyTypeParameters( private fun <T> copyTypeParameters(
from: T, from: T,
to: T, to: T,
inserter: (T, PsiTypeParameterList) -> Unit inserter: (T, PsiTypeParameterList) -> Unit
) where T : PsiTypeParameterListOwner, T : PsiNameIdentifierOwner { ) where T : PsiTypeParameterListOwner, T : PsiNameIdentifierOwner {
val factory = PsiElementFactory.SERVICE.getInstance((from as PsiElement).project) val factory = PsiElementFactory.SERVICE.getInstance((from as PsiElement).project)
val templateTypeParams = from.typeParameterList?.typeParameters ?: PsiTypeParameter.EMPTY_ARRAY val templateTypeParams = from.typeParameterList?.typeParameters ?: PsiTypeParameter.EMPTY_ARRAY
@@ -493,17 +503,17 @@ private fun <T> copyTypeParameters(
factory.createTypeParameter(it.name, it.extendsList.referencedTypes) factory.createTypeParameter(it.name, it.extendsList.referencedTypes)
} }
ChangeSignatureUtil.synchronizeList( ChangeSignatureUtil.synchronizeList(
targetTypeParamList, targetTypeParamList,
newTypeParams, newTypeParams,
{ it!!.typeParameters.toList() }, { it!!.typeParameters.toList() },
BooleanArray(newTypeParams.size) BooleanArray(newTypeParams.size)
) )
} }
} }
fun createJavaMethod(function: KtFunction, targetClass: PsiClass): PsiMethod { fun createJavaMethod(function: KtFunction, targetClass: PsiClass): PsiMethod {
val template = LightClassUtil.getLightClassMethod(function) val template = LightClassUtil.getLightClassMethod(function)
?: throw AssertionError("Can't generate light method: ${function.getElementTextWithContext()}") ?: throw AssertionError("Can't generate light method: ${function.getElementTextWithContext()}")
return createJavaMethod(template, targetClass) return createJavaMethod(template, targetClass)
} }
@@ -511,8 +521,7 @@ fun createJavaMethod(template: PsiMethod, targetClass: PsiClass): PsiMethod {
val factory = PsiElementFactory.SERVICE.getInstance(template.project) val factory = PsiElementFactory.SERVICE.getInstance(template.project)
val methodToAdd = if (template.isConstructor) { val methodToAdd = if (template.isConstructor) {
factory.createConstructor(template.name) factory.createConstructor(template.name)
} } else {
else {
factory.createMethod(template.name, template.returnType) factory.createMethod(template.name, template.returnType)
} }
val method = targetClass.add(methodToAdd) as PsiMethod val method = targetClass.add(methodToAdd) as PsiMethod
@@ -533,16 +542,15 @@ fun createJavaMethod(template: PsiMethod, targetClass: PsiClass): PsiMethod {
param param
} }
ChangeSignatureUtil.synchronizeList( ChangeSignatureUtil.synchronizeList(
targetParamList, targetParamList,
newParams, newParams,
{ it.parameters.toList() }, { it.parameters.toList() },
BooleanArray(newParams.size) BooleanArray(newParams.size)
) )
if (template.modifierList.hasModifierProperty(PsiModifier.ABSTRACT) || targetClass.isInterface) { if (template.modifierList.hasModifierProperty(PsiModifier.ABSTRACT) || targetClass.isInterface) {
method.body!!.delete() method.body!!.delete()
} } else if (!template.isConstructor) {
else if (!template.isConstructor) {
CreateFromUsageUtils.setupMethodBody(method) CreateFromUsageUtils.setupMethodBody(method)
} }
@@ -552,7 +560,7 @@ fun createJavaMethod(template: PsiMethod, targetClass: PsiClass): PsiMethod {
fun createJavaField(property: KtNamedDeclaration, targetClass: PsiClass): PsiField { fun createJavaField(property: KtNamedDeclaration, targetClass: PsiClass): PsiField {
val accessorLightMethods = property.getAccessorLightMethods() val accessorLightMethods = property.getAccessorLightMethods()
val template = accessorLightMethods.getter val template = accessorLightMethods.getter
?: throw AssertionError("Can't generate light method: ${property.getElementTextWithContext()}") ?: throw AssertionError("Can't generate light method: ${property.getElementTextWithContext()}")
val factory = PsiElementFactory.SERVICE.getInstance(template.project) val factory = PsiElementFactory.SERVICE.getInstance(template.project)
val field = targetClass.add(factory.createField(property.name!!, template.returnType!!)) as PsiField val field = targetClass.add(factory.createField(property.name!!, template.returnType!!)) as PsiField
@@ -597,21 +605,20 @@ fun createJavaClass(klass: KtClass, targetClass: PsiClass?, forcePlainClass: Boo
// Turning interface to class // Turning interface to class
if (!javaClass.isInterface && template.isInterface) { if (!javaClass.isInterface && template.isInterface) {
val implementsList = factory.createReferenceListWithRole( val implementsList = factory.createReferenceListWithRole(
template.extendsList?.referenceElements ?: PsiJavaCodeReferenceElement.EMPTY_ARRAY, template.extendsList?.referenceElements ?: PsiJavaCodeReferenceElement.EMPTY_ARRAY,
PsiReferenceList.Role.IMPLEMENTS_LIST PsiReferenceList.Role.IMPLEMENTS_LIST
) )
implementsList?.let { javaClass.implementsList?.replace(it) } implementsList?.let { javaClass.implementsList?.replace(it) }
} } else {
else {
val extendsList = factory.createReferenceListWithRole( val extendsList = factory.createReferenceListWithRole(
template.extendsList?.referenceElements ?: PsiJavaCodeReferenceElement.EMPTY_ARRAY, template.extendsList?.referenceElements ?: PsiJavaCodeReferenceElement.EMPTY_ARRAY,
PsiReferenceList.Role.EXTENDS_LIST PsiReferenceList.Role.EXTENDS_LIST
) )
extendsList?.let { javaClass.extendsList?.replace(it) } extendsList?.let { javaClass.extendsList?.replace(it) }
val implementsList = factory.createReferenceListWithRole( val implementsList = factory.createReferenceListWithRole(
template.implementsList?.referenceElements ?: PsiJavaCodeReferenceElement.EMPTY_ARRAY, template.implementsList?.referenceElements ?: PsiJavaCodeReferenceElement.EMPTY_ARRAY,
PsiReferenceList.Role.IMPLEMENTS_LIST PsiReferenceList.Role.IMPLEMENTS_LIST
) )
implementsList?.let { javaClass.implementsList?.replace(it) } implementsList?.let { javaClass.implementsList?.replace(it) }
} }
@@ -619,9 +626,9 @@ fun createJavaClass(klass: KtClass, targetClass: PsiClass?, forcePlainClass: Boo
for (method in template.methods) { for (method in template.methods) {
val hasParams = method.parameterList.parametersCount > 0 val hasParams = method.parameterList.parametersCount > 0
val needSuperCall = !template.isEnum && val needSuperCall = !template.isEnum &&
(template.superClass?.constructors ?: PsiMethod.EMPTY_ARRAY).all { (template.superClass?.constructors ?: PsiMethod.EMPTY_ARRAY).all {
it.parameterList.parametersCount > 0 it.parameterList.parametersCount > 0
} }
if (method.isConstructor && !(hasParams || needSuperCall)) continue if (method.isConstructor && !(hasParams || needSuperCall)) continue
with(createJavaMethod(method, javaClass)) { with(createJavaMethod(method, javaClass)) {
if (isConstructor && needSuperCall) { if (isConstructor && needSuperCall) {
@@ -636,9 +643,11 @@ fun createJavaClass(klass: KtClass, targetClass: PsiClass?, forcePlainClass: Boo
fun PsiElement.j2kText(): String? { fun PsiElement.j2kText(): String? {
if (language != JavaLanguage.INSTANCE) return null if (language != JavaLanguage.INSTANCE) return null
val j2kConverter = JavaToKotlinConverter(project, val j2kConverter = JavaToKotlinConverter(
ConverterSettings.Companion.defaultSettings, project,
IdeaJavaToKotlinServices) ConverterSettings.Companion.defaultSettings,
IdeaJavaToKotlinServices
)
return j2kConverter.elementsToKotlin(listOf(this)).results.single()?.text ?: return null //TODO: insert imports return j2kConverter.elementsToKotlin(listOf(this)).results.single()?.text ?: return null //TODO: insert imports
} }
@@ -669,52 +678,52 @@ internal abstract class CompositeRefactoringRunner(
fun run() { fun run() {
val connection = project.messageBus.connect() val connection = project.messageBus.connect()
connection.subscribe( connection.subscribe(
RefactoringEventListener.REFACTORING_EVENT_TOPIC, RefactoringEventListener.REFACTORING_EVENT_TOPIC,
object : RefactoringEventListener { object : RefactoringEventListener {
override fun undoRefactoring(refactoringId: String) { override fun undoRefactoring(refactoringId: String) {
} }
override fun refactoringStarted(refactoringId: String, beforeData: RefactoringEventData?) { override fun refactoringStarted(refactoringId: String, beforeData: RefactoringEventData?) {
} }
override fun conflictsDetected(refactoringId: String, conflictsData: RefactoringEventData) { override fun conflictsDetected(refactoringId: String, conflictsData: RefactoringEventData) {
} }
override fun refactoringDone(refactoringId: String, afterData: RefactoringEventData?) { override fun refactoringDone(refactoringId: String, afterData: RefactoringEventData?) {
if (refactoringId == this@CompositeRefactoringRunner.refactoringId) { if (refactoringId == this@CompositeRefactoringRunner.refactoringId) {
onRefactoringDone() onRefactoringDone()
}
} }
} }
}
) )
connection.subscribe( connection.subscribe(
KotlinRefactoringEventListener.EVENT_TOPIC, KotlinRefactoringEventListener.EVENT_TOPIC,
object : KotlinRefactoringEventListener { object : KotlinRefactoringEventListener {
override fun onRefactoringExit(refactoringId: String) { override fun onRefactoringExit(refactoringId: String) {
if (refactoringId == this@CompositeRefactoringRunner.refactoringId) { if (refactoringId == this@CompositeRefactoringRunner.refactoringId) {
try { try {
onExit() onExit()
} finally { } finally {
connection.disconnect() connection.disconnect()
}
} }
} }
} }
}
) )
runRefactoring() runRefactoring()
} }
} }
@Throws(ConfigurationException::class) fun KtElement?.validateElement(errorMessage: String) { @Throws(ConfigurationException::class)
fun KtElement?.validateElement(errorMessage: String) {
if (this == null) throw ConfigurationException(errorMessage) if (this == null) throw ConfigurationException(errorMessage)
try { try {
AnalyzingUtils.checkForSyntacticErrors(this) AnalyzingUtils.checkForSyntacticErrors(this)
} } catch (e: Exception) {
catch(e: Exception) {
throw ConfigurationException(errorMessage) throw ConfigurationException(errorMessage)
} }
} }
@@ -743,7 +752,7 @@ fun PsiNamedElement.isInterfaceClass(): Boolean = when (this) {
fun KtNamedDeclaration.isAbstract(): Boolean { fun KtNamedDeclaration.isAbstract(): Boolean {
if (hasModifier(KtTokens.ABSTRACT_KEYWORD)) return true if (hasModifier(KtTokens.ABSTRACT_KEYWORD)) return true
if (!(containingClassOrObject?.isInterfaceClass() ?: false)) return false if (containingClassOrObject?.isInterfaceClass() != true) return false
return when (this) { return when (this) {
is KtProperty -> initializer == null && delegate == null && accessors.isEmpty() is KtProperty -> initializer == null && delegate == null && accessors.isEmpty()
is KtNamedFunction -> !hasBody() is KtNamedFunction -> !hasBody()
@@ -754,10 +763,10 @@ fun KtNamedDeclaration.isAbstract(): Boolean {
fun KtNamedDeclaration.isConstructorDeclaredProperty() = this is KtParameter && ownerFunction is KtPrimaryConstructor && hasValOrVar() fun KtNamedDeclaration.isConstructorDeclaredProperty() = this is KtParameter && ownerFunction is KtPrimaryConstructor && hasValOrVar()
fun <ListType : KtElement> replaceListPsiAndKeepDelimiters( fun <ListType : KtElement> replaceListPsiAndKeepDelimiters(
originalList: ListType, originalList: ListType,
newList: ListType, newList: ListType,
@Suppress("UNCHECKED_CAST") listReplacer: ListType.(ListType) -> ListType = { replace(it) as ListType }, @Suppress("UNCHECKED_CAST") listReplacer: ListType.(ListType) -> ListType = { replace(it) as ListType },
itemsFun: ListType.() -> List<KtElement> itemsFun: ListType.() -> List<KtElement>
): ListType { ): ListType {
originalList.children.takeWhile { it is PsiErrorElement }.forEach { it.delete() } originalList.children.takeWhile { it is PsiErrorElement }.forEach { it.delete() }
@@ -767,7 +776,7 @@ fun <ListType : KtElement> replaceListPsiAndKeepDelimiters(
val newCount = newParameters.size val newCount = newParameters.size
val commonCount = Math.min(oldCount, newCount) val commonCount = Math.min(oldCount, newCount)
for (i in 0..commonCount - 1) { for (i in 0 until commonCount) {
oldParameters[i] = oldParameters[i].replace(newParameters[i]) as KtElement oldParameters[i] = oldParameters[i].replace(newParameters[i]) as KtElement
} }
@@ -777,10 +786,10 @@ fun <ListType : KtElement> replaceListPsiAndKeepDelimiters(
if (oldCount > commonCount) { if (oldCount > commonCount) {
originalList.deleteChildRange(oldParameters[commonCount - 1].nextSibling, lastOriginalParameter) originalList.deleteChildRange(oldParameters[commonCount - 1].nextSibling, lastOriginalParameter)
} } else if (newCount > commonCount) {
else if (newCount > commonCount) {
val psiBeforeLastParameter = lastOriginalParameter.prevSibling val psiBeforeLastParameter = lastOriginalParameter.prevSibling
val withMultiline = (psiBeforeLastParameter is PsiWhiteSpace || psiBeforeLastParameter is PsiComment) && psiBeforeLastParameter.textContains('\n') val withMultiline =
(psiBeforeLastParameter is PsiWhiteSpace || psiBeforeLastParameter is PsiComment) && psiBeforeLastParameter.textContains('\n')
val extraSpace = if (withMultiline) KtPsiFactory(originalList).createNewLine() else null val extraSpace = if (withMultiline) KtPsiFactory(originalList).createNewLine() else null
originalList.addRangeAfter(newParameters[commonCount - 1].nextSibling, newParameters.last(), lastOriginalParameter) originalList.addRangeAfter(newParameters[commonCount - 1].nextSibling, newParameters.last(), lastOriginalParameter)
if (extraSpace != null) { if (extraSpace != null) {
@@ -850,7 +859,7 @@ internal fun DeclarationDescriptor.getThisLabelName(): String {
if (this is AnonymousFunctionDescriptor) { if (this is AnonymousFunctionDescriptor) {
val function = source.getPsi() as? KtFunction val function = source.getPsi() as? KtFunction
val argument = function?.parent as? KtValueArgument val argument = function?.parent as? KtValueArgument
?: (function?.parent as? KtLambdaExpression)?.parent as? KtValueArgument ?: (function?.parent as? KtLambdaExpression)?.parent as? KtValueArgument
val callElement = argument?.getStrictParentOfType<KtCallElement>() val callElement = argument?.getStrictParentOfType<KtCallElement>()
val callee = callElement?.calleeExpression as? KtSimpleNameExpression val callee = callElement?.calleeExpression as? KtSimpleNameExpression
if (callee != null) return callee.text if (callee != null) return callee.text
@@ -874,9 +883,9 @@ val PsiElement.isInsideInjectedFragment: Boolean
get() = containingFile.isInjectedFragment get() = containingFile.isInjectedFragment
fun checkSuperMethods( fun checkSuperMethods(
declaration: KtDeclaration, declaration: KtDeclaration,
ignore: Collection<PsiElement>?, ignore: Collection<PsiElement>?,
actionString: String actionString: String
): List<PsiElement> { ): List<PsiElement> {
fun getClassDescriptions(overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>): List<String> { fun getClassDescriptions(overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>): List<String> {
return overriddenElementsToDescriptor.entries.map { entry -> return overriddenElementsToDescriptor.entries.map { entry ->
@@ -885,7 +894,7 @@ fun checkSuperMethods(
is KtNamedFunction, is KtProperty, is KtParameter -> formatClassDescriptor(descriptor.containingDeclaration) is KtNamedFunction, is KtProperty, is KtParameter -> formatClassDescriptor(descriptor.containingDeclaration)
is PsiMethod -> { is PsiMethod -> {
val psiClass = element.containingClass ?: error("Invalid element: ${element.getText()}") val psiClass = element.containingClass ?: error("Invalid element: ${element.getText()}")
formatPsiClass(psiClass, true, false) formatPsiClass(psiClass, markAsJava = true, inCode = false)
} }
else -> error("Unexpected element: ${element.getElementTextWithContext()}") else -> error("Unexpected element: ${element.getElementTextWithContext()}")
} }
@@ -894,21 +903,22 @@ fun checkSuperMethods(
} }
fun askUserForMethodsToSearch( fun askUserForMethodsToSearch(
declarationDescriptor: CallableDescriptor, declarationDescriptor: CallableDescriptor,
overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor> overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>
): List<PsiElement> { ): List<PsiElement> {
val superClassDescriptions = getClassDescriptions(overriddenElementsToDescriptor) val superClassDescriptions = getClassDescriptions(overriddenElementsToDescriptor)
val message = KotlinBundle.message( val message = KotlinBundle.message(
"x.overrides.y.in.class.list", "x.overrides.y.in.class.list",
DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declarationDescriptor), DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declarationDescriptor),
"\n${superClassDescriptions.joinToString(separator = "")}", "\n${superClassDescriptions.joinToString(separator = "")}",
actionString actionString
) )
val exitCode = showYesNoCancelDialog( val exitCode = showYesNoCancelDialog(
CHECK_SUPER_METHODS_YES_NO_DIALOG, CHECK_SUPER_METHODS_YES_NO_DIALOG,
declaration.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon(), Messages.YES) declaration.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon(), Messages.YES
)
return when (exitCode) { return when (exitCode) {
Messages.YES -> overriddenElementsToDescriptor.keys.toList() Messages.YES -> overriddenElementsToDescriptor.keys.toList()
Messages.NO -> listOf(declaration) Messages.NO -> listOf(declaration)
@@ -939,11 +949,11 @@ fun checkSuperMethods(
} }
fun checkSuperMethodsWithPopup( fun checkSuperMethodsWithPopup(
declaration: KtNamedDeclaration, declaration: KtNamedDeclaration,
deepestSuperMethods: List<PsiElement>, deepestSuperMethods: List<PsiElement>,
actionString: String, actionString: String,
editor: Editor, editor: Editor,
action: (List<PsiElement>) -> Unit action: (List<PsiElement>) -> Unit
) { ) {
if (deepestSuperMethods.isEmpty()) return action(listOf(declaration)) if (deepestSuperMethods.isEmpty()) return action(listOf(declaration))
@@ -974,7 +984,7 @@ fun checkSuperMethodsWithPopup(
} }
val renameBase = actionString + " base $superKind" + (if (deepestSuperMethods.size > 1) "s" else "") val renameBase = actionString + " base $superKind" + (if (deepestSuperMethods.size > 1) "s" else "")
val renameCurrent = actionString + " only current $kind" val renameCurrent = "$actionString only current $kind"
val title = buildString { val title = buildString {
append(declaration.name) append(declaration.name)
append(if (isAbstract) " implements " else " overrides ") append(if (isAbstract) " implements " else " overrides ")
@@ -984,18 +994,18 @@ fun checkSuperMethodsWithPopup(
} }
val list = JBList<String>(renameBase, renameCurrent) val list = JBList<String>(renameBase, renameCurrent)
JBPopupFactory.getInstance() JBPopupFactory.getInstance()
.createListPopupBuilder(list) .createListPopupBuilder(list)
.setTitle(title) .setTitle(title)
.setMovable(false) .setMovable(false)
.setResizable(false) .setResizable(false)
.setRequestFocus(true) .setRequestFocus(true)
.setItemChoosenCallback { .setItemChoosenCallback {
val value = list.selectedValue ?: return@setItemChoosenCallback val value = list.selectedValue ?: return@setItemChoosenCallback
val chosenElements = if (value == renameBase) deepestSuperMethods + declaration else listOf(declaration) val chosenElements = if (value == renameBase) deepestSuperMethods + declaration else listOf(declaration)
action(chosenElements) action(chosenElements)
} }
.createPopup() .createPopup()
.showInBestPositionFor(editor) .showInBestPositionFor(editor)
} }
fun KtNamedDeclaration.isCompanionMemberOf(klass: KtClassOrObject): Boolean { fun KtNamedDeclaration.isCompanionMemberOf(klass: KtClassOrObject): Boolean {
@@ -1016,4 +1026,12 @@ internal fun KtDeclaration.resolveToExpectedDescriptorIfPossible(): DeclarationD
fun DialogWrapper.showWithTransaction() { fun DialogWrapper.showWithTransaction() {
TransactionGuard.submitTransaction(disposable, Runnable { show() }) TransactionGuard.submitTransaction(disposable, Runnable { show() })
}
fun PsiMethod.checkDeclarationConflict(name: String, conflicts: MultiMap<PsiElement, String>, callables: List<PsiElement>) {
containingClass
?.findMethodsByName(name, true)
// as is necessary here: see KT-10386
?.firstOrNull { it.parameterList.parametersCount == 0 && !callables.contains(it.namedUnwrappedElement as PsiElement?) }
?.let { reportDeclarationConflict(conflicts, it) { s -> "$s already exists" } }
} }