Intentions: Move member of companion object to corresponding class
#KT-9697 Fixed
This commit is contained in:
+5
@@ -0,0 +1,5 @@
|
|||||||
|
class A {
|
||||||
|
<spot>fun foo() = 1</spot>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() = A.foo()
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
class A {
|
||||||
|
companion object {
|
||||||
|
<spot>fun foo() = 1</spot>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() = A.foo()
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
This intention moves member of companion object to the corresponding class.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1169,6 +1169,11 @@
|
|||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
</intentionAction>
|
</intentionAction>
|
||||||
|
|
||||||
|
<intentionAction>
|
||||||
|
<className>org.jetbrains.kotlin.idea.intentions.MoveMemberOutOfCompanionObjectIntention</className>
|
||||||
|
<category>Kotlin</category>
|
||||||
|
</intentionAction>
|
||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
|
||||||
displayName="Convert object literal to lambda"
|
displayName="Convert object literal to lambda"
|
||||||
groupName="Kotlin"
|
groupName="Kotlin"
|
||||||
|
|||||||
+80
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* 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.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
|
import com.intellij.util.containers.MultiMap
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.runSynchronouslyWithProgress
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||||
|
|
||||||
|
class MoveMemberOutOfCompanionObjectIntention : SelfTargetingRangeIntention<KtNamedDeclaration>(KtNamedDeclaration::class.java,
|
||||||
|
"Move out of companion object") {
|
||||||
|
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(listOf(element),
|
||||||
|
KotlinMoveTargetForExistingElement(targetClass),
|
||||||
|
MoveDeclarationsDelegate.NestedClass())
|
||||||
|
MoveKotlinDeclarationsProcessor(project, moveDescriptor).run()
|
||||||
|
deleteCompanionIfEmpty()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val externalRefs = project.runSynchronouslyWithProgress("Searching for ${element.name}", true) {
|
||||||
|
ReferencesSearch.search(element).filter {
|
||||||
|
val refElement = it.element ?: return@filter false
|
||||||
|
!targetClass.isAncestor(refElement) || companionObject.isAncestor(refElement)
|
||||||
|
}
|
||||||
|
} ?: return
|
||||||
|
|
||||||
|
val conflicts = MultiMap<PsiElement, String>()
|
||||||
|
for (ref in externalRefs) {
|
||||||
|
val refElement = ref.element ?: continue
|
||||||
|
conflicts.putValue(refElement, "Class instance required: ${refElement.text}")
|
||||||
|
}
|
||||||
|
|
||||||
|
project.checkConflictsInteractively(conflicts) {
|
||||||
|
Mover.Default(element, targetClass)
|
||||||
|
deleteCompanionIfEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.idea.intentions.MoveMemberOutOfCompanionObjectIntention
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
class A {
|
||||||
|
companion object {
|
||||||
|
class <caret>B {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
A.Companion.B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class A {
|
||||||
|
class B {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
A.B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
class A {
|
||||||
|
companion object {
|
||||||
|
class B {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <caret>foo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
class A {
|
||||||
|
companion object {
|
||||||
|
class B {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// SHOULD_FAIL_WITH: Class instance required: foo, Class instance required: foo, Class instance required: foo, Class instance required: foo
|
||||||
|
class A {
|
||||||
|
companion object {
|
||||||
|
class B {
|
||||||
|
init {
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <caret>foo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
A.foo()
|
||||||
|
A.Companion.foo()
|
||||||
|
with(A) {
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
class A {
|
||||||
|
companion object {
|
||||||
|
class B {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val <caret>foo: Int = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
class A {
|
||||||
|
companion object {
|
||||||
|
class B {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
val foo: Int = 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// SHOULD_FAIL_WITH: Class instance required: foo, Class instance required: foo, Class instance required: foo, Class instance required: foo
|
||||||
|
class A {
|
||||||
|
companion object {
|
||||||
|
class B {
|
||||||
|
init {
|
||||||
|
foo + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val <caret>foo: Int = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
A.foo + 1
|
||||||
|
A.Companion.foo + 1
|
||||||
|
with(A) {
|
||||||
|
foo + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
|
||||||
|
class A {
|
||||||
|
object O {
|
||||||
|
class <caret>B {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
|
||||||
|
class A {
|
||||||
|
class <caret>B {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
class A {
|
||||||
|
companion object {
|
||||||
|
fun foo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
A.B()
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package b;
|
||||||
|
|
||||||
|
import a.A;
|
||||||
|
|
||||||
|
class J {
|
||||||
|
void foo() {
|
||||||
|
A.B b = new A.B();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
class A {
|
||||||
|
companion object {
|
||||||
|
fun foo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class <caret>B {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
A.Companion.B()
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package b;
|
||||||
|
|
||||||
|
import a.A;
|
||||||
|
|
||||||
|
class J {
|
||||||
|
void foo() {
|
||||||
|
A.Companion.B b = new A.Companion.B();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "a/A.kt",
|
||||||
|
"intentionClass": "org.jetbrains.kotlin.idea.intentions.MoveMemberOutOfCompanionObjectIntention",
|
||||||
|
"withRuntime": "true"
|
||||||
|
}
|
||||||
@@ -154,7 +154,7 @@ public abstract class AbstractIntentionTest extends KotlinCodeInsightTestCase {
|
|||||||
assertEquals("Intention text mismatch.", intentionTextString, intentionAction.getText());
|
assertEquals("Intention text mismatch.", intentionTextString, intentionAction.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
String shouldFailString = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// SHOULD_FAIL_WITH: ");
|
String shouldFailString = StringUtil.join(InTextDirectivesUtils.findListWithPrefixes(fileText, "// SHOULD_FAIL_WITH: "), ", ");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isApplicableExpected) {
|
if (isApplicableExpected) {
|
||||||
@@ -171,7 +171,7 @@ public abstract class AbstractIntentionTest extends KotlinCodeInsightTestCase {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
// Don't bother checking if it should have failed.
|
// Don't bother checking if it should have failed.
|
||||||
if (shouldFailString == null) {
|
if (shouldFailString.isEmpty()) {
|
||||||
for (Map.Entry<String, PsiFile> entry: pathToFile.entrySet()) {
|
for (Map.Entry<String, PsiFile> entry: pathToFile.entrySet()) {
|
||||||
//noinspection AssignmentToStaticFieldFromInstanceMethod
|
//noinspection AssignmentToStaticFieldFromInstanceMethod
|
||||||
myFile = entry.getValue();
|
myFile = entry.getValue();
|
||||||
@@ -186,7 +186,7 @@ public abstract class AbstractIntentionTest extends KotlinCodeInsightTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertNull("Expected test to fail.", shouldFailString);
|
assertEquals("Expected test to fail.", "", shouldFailString);
|
||||||
}
|
}
|
||||||
catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
|
catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
|
||||||
assertEquals("Failure message mismatch.", shouldFailString, StringUtil.join(e.getMessages(), ", "));
|
assertEquals("Failure message mismatch.", shouldFailString, StringUtil.join(e.getMessages(), ", "));
|
||||||
|
|||||||
Reference in New Issue
Block a user