Refactor common move member methods

(cherry picked from commit 2dd8148)
This commit is contained in:
shiraji
2017-10-20 15:49:33 +03:00
committed by Alexey Sedunov
parent 6adb62f3a2
commit 7044e46756
2 changed files with 103 additions and 50 deletions
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.idea.intentions
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.search.searches.ReferencesSearch
@@ -25,15 +24,12 @@ import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
import org.jetbrains.kotlin.idea.refactoring.getUsageContext
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.parents
@@ -42,47 +38,13 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import org.jetbrains.kotlin.util.findCallableMemberBySignature
class MoveMemberOutOfCompanionObjectIntention : SelfTargetingRangeIntention<KtNamedDeclaration>(KtNamedDeclaration::class.java,
"Move out of companion object") {
override fun startInWriteAction() = false
override fun applicabilityRange(element: KtNamedDeclaration): TextRange? {
if (element !is KtNamedFunction && element !is KtProperty && element !is KtClassOrObject) return null
val container = element.containingClassOrObject
if (!(container is KtObjectDeclaration && container.isCompanion())) return null
if (container.containingClassOrObject == null) return null
return element.nameIdentifier?.textRange
}
override fun applyTo(element: KtNamedDeclaration, editor: Editor?) {
val project = element.project
val companionObject = element.containingClassOrObject!!
val targetClass = companionObject.containingClassOrObject!!
fun deleteCompanionIfEmpty() {
if (companionObject.declarations.isEmpty()) {
companionObject.delete()
}
}
if (element is KtClassOrObject) {
val moveDescriptor = MoveDeclarationsDescriptor(project,
listOf(element),
KotlinMoveTargetForExistingElement(targetClass),
MoveDeclarationsDelegate.NestedClass())
runWriteAction {
MoveKotlinDeclarationsProcessor(moveDescriptor).run()
deleteCompanionIfEmpty()
}
return
}
class MoveMemberOutOfCompanionObjectIntention : MoveMemberOutOfObjectIntention("Move out of companion object") {
override fun addConflicts(element: KtNamedDeclaration, conflicts: MultiMap<PsiElement, String>) {
val targetClass = element.containingClassOrObject!!.containingClassOrObject!!
val targetClassDescriptor = runReadAction { targetClass.unsafeResolveToDescriptor() as ClassDescriptor }
val conflicts = MultiMap<PsiElement, String>()
val refsRequiringClassInstance = project.runSynchronouslyWithProgress("Searching for ${element.name}", true) {
val refsRequiringClassInstance = element.project.runSynchronouslyWithProgress("Searching for ${element.name}", true) {
runReadAction {
ReferencesSearch
.search(element)
@@ -113,17 +75,22 @@ class MoveMemberOutOfCompanionObjectIntention : SelfTargetingRangeIntention<KtNa
runReadAction {
val callableDescriptor = element.unsafeResolveToDescriptor() as CallableMemberDescriptor
targetClassDescriptor.findCallableMemberBySignature(callableDescriptor)?.let {
DescriptorToSourceUtilsIde.getAnyDeclaration(project, it)
DescriptorToSourceUtilsIde.getAnyDeclaration(element.project, it)
}?.let {
conflicts.putValue(it, "Class '${targetClass.name}' already contains ${RefactoringUIUtil.getDescription(it, false)}")
}
}
project.checkConflictsInteractively(conflicts) {
runWriteAction {
Mover.Default(element, targetClass)
deleteCompanionIfEmpty()
}
}
}
override fun getDestination(element: KtNamedDeclaration) = element.containingClassOrObject!!.containingClassOrObject!!
override fun applicabilityRange(element: KtNamedDeclaration): TextRange? {
if (element !is KtNamedFunction && element !is KtProperty && element !is KtClassOrObject) return null
val container = element.containingClassOrObject
if (!(container is KtObjectDeclaration && container.isCompanion())) return null
if (container.containingClassOrObject == null) return null
return element.nameIdentifier?.textRange
}
}
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2017 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.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.util.RefactoringUIUtil
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
import org.jetbrains.kotlin.idea.refactoring.getUsageContext
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.synthetics.findClassDescriptor
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import org.jetbrains.kotlin.util.findCallableMemberBySignature
abstract class MoveMemberOutOfObjectIntention(text: String) : SelfTargetingRangeIntention<KtNamedDeclaration>(KtNamedDeclaration::class.java, text) {
override fun startInWriteAction() = false
abstract fun getDestination(element: KtNamedDeclaration) : KtElement
abstract fun addConflicts(element: KtNamedDeclaration,conflicts: MultiMap<PsiElement, String>)
override fun applyTo(element: KtNamedDeclaration, editor: Editor?) {
val project = element.project
val classOrObject = element.containingClassOrObject!!
val destination = getDestination(element)
fun deleteClassOrObjectIfEmpty() {
if (classOrObject.declarations.isEmpty()) {
classOrObject.delete()
}
}
if (element is KtClassOrObject) {
val moveDescriptor = MoveDeclarationsDescriptor(project,
listOf(element),
KotlinMoveTargetForExistingElement(destination),
MoveDeclarationsDelegate.NestedClass())
runWriteAction {
MoveKotlinDeclarationsProcessor(moveDescriptor).run()
deleteClassOrObjectIfEmpty()
}
return
}
val conflicts = MultiMap<PsiElement, String>().apply { addConflicts(element, this) }
project.checkConflictsInteractively(conflicts) {
runWriteAction {
Mover.Default(element, destination)
deleteClassOrObjectIfEmpty()
}
}
}
}