Introduce Parameter/Lambda Parameter: Parenthesize binary expressions if operation reference starts with a new line

This commit is contained in:
Alexey Sedunov
2015-12-10 14:22:10 +03:00
parent 354e1dc337
commit ae2fc19fe5
22 changed files with 187 additions and 18 deletions
@@ -248,7 +248,7 @@ public class KotlinInplaceParameterIntroducer(
return descriptor.copy( return descriptor.copy(
originalRange = originalRange, originalRange = originalRange,
occurrencesToReplace = if (replaceAll) getOccurrences().map { it.toRange() } else originalRange.singletonList(), occurrencesToReplace = if (replaceAll) getOccurrences().map { it.toRange() } else originalRange.singletonList(),
newArgumentValue = getExpr()!! argumentValue = getExpr()!!
) )
} }
@@ -19,14 +19,11 @@ package org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter
import com.intellij.openapi.command.impl.FinishMarkAction import com.intellij.openapi.command.impl.FinishMarkAction
import com.intellij.openapi.command.impl.StartMarkAction import com.intellij.openapi.command.impl.StartMarkAction
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.options.ConfigurationException
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.refactoring.BaseRefactoringProcessor import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.refactoring.ui.NameSuggestionsField import com.intellij.refactoring.ui.NameSuggestionsField
import com.intellij.refactoring.ui.RefactoringDialog import com.intellij.refactoring.ui.RefactoringDialog
import com.intellij.ui.NonFocusableCheckBox import com.intellij.ui.NonFocusableCheckBox
import com.intellij.ui.TitledSeparator
import com.intellij.usageView.BaseUsageViewDescriptor import com.intellij.usageView.BaseUsageViewDescriptor
import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
@@ -38,13 +35,11 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ui.Kotlin
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.* import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.AnalyzingUtils
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import java.awt.GridBagConstraints import java.awt.GridBagConstraints
import java.awt.GridBagLayout import java.awt.GridBagLayout
import java.awt.Insets import java.awt.Insets
import java.util.Collections import java.util.*
import java.util.LinkedHashMap
import javax.swing.JCheckBox import javax.swing.JCheckBox
import javax.swing.JComponent import javax.swing.JComponent
import javax.swing.JLabel import javax.swing.JLabel
@@ -301,7 +296,7 @@ public class KotlinIntroduceParameterDialog private constructor(
val descriptorToRefactor = descriptor.copy( val descriptorToRefactor = descriptor.copy(
newParameterName = chosenName, newParameterName = chosenName,
newParameterTypeText = chosenType, newParameterTypeText = chosenType,
newArgumentValue = newArgumentValue, argumentValue = newArgumentValue,
withDefaultValue = defaultValueCheckBox!!.isSelected(), withDefaultValue = defaultValueCheckBox!!.isSelected(),
occurrencesToReplace = with(descriptor) { occurrencesToReplace = with(descriptor) {
if (replaceAllCheckBox?.isSelected() ?: true) { if (replaceAllCheckBox?.isSelected() ?: true) {
@@ -70,13 +70,22 @@ public data class IntroduceParameterDescriptor(
val callableDescriptor: FunctionDescriptor, val callableDescriptor: FunctionDescriptor,
val newParameterName: String, val newParameterName: String,
val newParameterTypeText: String, val newParameterTypeText: String,
val newArgumentValue: KtExpression, val argumentValue: KtExpression,
val withDefaultValue: Boolean, val withDefaultValue: Boolean,
val parametersUsages: MultiMap<KtElement, KtElement>, val parametersUsages: MultiMap<KtElement, KtElement>,
val occurrencesToReplace: List<KotlinPsiRange>, val occurrencesToReplace: List<KotlinPsiRange>,
val parametersToRemove: List<KtElement> = getParametersToRemove(withDefaultValue, parametersUsages, occurrencesToReplace), val parametersToRemove: List<KtElement> = getParametersToRemove(withDefaultValue, parametersUsages, occurrencesToReplace),
val occurrenceReplacer: IntroduceParameterDescriptor.(KotlinPsiRange) -> Unit = {} val occurrenceReplacer: IntroduceParameterDescriptor.(KotlinPsiRange) -> Unit = {}
) { ) {
val newArgumentValue: KtExpression by lazy {
if (argumentValue.mustBeParenthesizedInInitializerPosition()) {
KtPsiFactory(callable).createExpressionByPattern("($0)", argumentValue)
}
else {
argumentValue
}
}
val originalOccurrence: KotlinPsiRange val originalOccurrence: KotlinPsiRange
get() = occurrencesToReplace.first { it.getTextRange().intersects(originalRange.getTextRange()) } get() = occurrencesToReplace.first { it.getTextRange().intersects(originalRange.getTextRange()) }
val valVar: KotlinValVar val valVar: KotlinValVar
@@ -271,6 +280,7 @@ public open class KotlinIntroduceParameterHandler(
&& !isTestMode && !isTestMode
&& !haveLambdaArgumentsToReplace && !haveLambdaArgumentsToReplace
&& expression.extractableSubstringInfo == null && expression.extractableSubstringInfo == null
&& !expression.mustBeParenthesizedInInitializerPosition()
val originalExpression = KtPsiUtil.safeDeparenthesize(expression) val originalExpression = KtPsiUtil.safeDeparenthesize(expression)
val psiFactory = KtPsiFactory(project) val psiFactory = KtPsiFactory(project)
@@ -282,7 +292,7 @@ public open class KotlinIntroduceParameterHandler(
callableDescriptor = functionDescriptor, callableDescriptor = functionDescriptor,
newParameterName = suggestedNames.first(), newParameterName = suggestedNames.first(),
newParameterTypeText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(replacementType), newParameterTypeText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(replacementType),
newArgumentValue = originalExpression, argumentValue = originalExpression,
withDefaultValue = false, withDefaultValue = false,
parametersUsages = parametersUsages, parametersUsages = parametersUsages,
occurrencesToReplace = occurrencesToReplace, occurrencesToReplace = occurrencesToReplace,
@@ -435,7 +445,7 @@ public open class KotlinIntroduceLambdaParameterHandler(
callableDescriptor = callableDescriptor, callableDescriptor = callableDescriptor,
newParameterName = "", // to be chosen in the dialog newParameterName = "", // to be chosen in the dialog
newParameterTypeText = "", // to be chosen in the dialog newParameterTypeText = "", // to be chosen in the dialog
newArgumentValue = KtPsiFactory(project).createExpression("{}"), // substituted later argumentValue = KtPsiFactory(project).createExpression("{}"), // substituted later
withDefaultValue = false, withDefaultValue = false,
parametersUsages = findInternalUsagesOfParametersAndReceiver(callable, callableDescriptor), parametersUsages = findInternalUsagesOfParametersAndReceiver(callable, callableDescriptor),
occurrencesToReplace = listOf(originalRange), occurrencesToReplace = listOf(originalRange),
@@ -21,6 +21,7 @@ import com.intellij.openapi.editor.ScrollType
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
@@ -132,13 +132,6 @@ object KotlinIntroduceVariableHandler : KotlinIntroduceHandlerBase() {
return result return result
} }
private fun KtExpression.mustBeParenthesizedInInitializerPosition(): Boolean {
if (this !is KtBinaryExpression) return false
if (left?.mustBeParenthesizedInInitializerPosition() ?: false) return true
return PsiChildRange(left, operationReference).any { (it is PsiWhiteSpace) && it.textContains('\n') }
}
private fun runRefactoring ( private fun runRefactoring (
expression: KtExpression, expression: KtExpression,
commonContainer: PsiElement, commonContainer: PsiElement,
@@ -0,0 +1,6 @@
fun foo(i: Int) { }
fun test() {
foo(<selection>1
+ 2</selection>)
}
@@ -0,0 +1,8 @@
fun foo(i: Int) { }
fun test(i: () -> Int = {
(1
+ 2)
}) {
foo(i())
}
@@ -0,0 +1,6 @@
fun foo(i: Int) { }
fun test() {
foo(<selection>1 // abc
/*def*/ + 2</selection>)
}
@@ -0,0 +1,8 @@
fun foo(i: Int) { }
fun test(i: () -> Int = {
(1 // abc
/*def*/ + 2)
}) {
foo(i())
}
@@ -0,0 +1,6 @@
fun foo(i: Int) { }
fun test() {
foo(<selection>1
+ 2 - 3</selection>)
}
@@ -0,0 +1,8 @@
fun foo(i: Int) { }
fun test(i: () -> Int = {
(1
+ 2 - 3)
}) {
foo(i())
}
@@ -0,0 +1,6 @@
fun foo(i: Int) { }
fun test() {
foo(<selection>1 + 2
- 3</selection>)
}
@@ -0,0 +1,8 @@
fun foo(i: Int) { }
fun test(i: () -> Int = {
(1 + 2
- 3)
}) {
foo(i())
}
@@ -0,0 +1,6 @@
fun foo(i: Int) { }
fun test() {
foo(<selection>1
+ 2</selection>)
}
@@ -0,0 +1,6 @@
fun foo(i: Int) { }
fun test(i: Int = (1
+ 2)) {
foo(i)
}
@@ -0,0 +1,6 @@
fun foo(i: Int) { }
fun test() {
foo(<selection>1 // abc
/*def*/ + 2</selection>)
}
@@ -0,0 +1,6 @@
fun foo(i: Int) { }
fun test(i: Int = (1 // abc
/*def*/ + 2)) {
foo(i)
}
@@ -0,0 +1,6 @@
fun foo(i: Int) { }
fun test() {
foo(<selection>1
+ 2 - 3</selection>)
}
@@ -0,0 +1,6 @@
fun foo(i: Int) { }
fun test(i: Int = (1
+ 2 - 3)) {
foo(i)
}
@@ -0,0 +1,6 @@
fun foo(i: Int) { }
fun test() {
foo(<selection>1 + 2
- 3</selection>)
}
@@ -0,0 +1,6 @@
fun foo(i: Int) { }
fun test(i: Int = (1 + 2
- 3)) {
foo(i)
}
@@ -3502,6 +3502,39 @@ public class ExtractionTestGenerated extends AbstractExtractionTest {
doIntroduceSimpleParameterTest(fileName); doIntroduceSimpleParameterTest(fileName);
} }
@TestMetadata("idea/testData/refactoring/introduceParameter/multiline")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Multiline extends AbstractExtractionTest {
public void testAllFilesPresentInMultiline() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/introduceParameter/multiline"), Pattern.compile("^(.+)\\.(kt|kts)$"), true);
}
@TestMetadata("multilineBinaryExpression.kt")
public void testMultilineBinaryExpression() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/multiline/multilineBinaryExpression.kt");
doIntroduceSimpleParameterTest(fileName);
}
@TestMetadata("multilineBinaryExpressionWithComments.kt")
public void testMultilineBinaryExpressionWithComments() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/multiline/multilineBinaryExpressionWithComments.kt");
doIntroduceSimpleParameterTest(fileName);
}
@TestMetadata("multilineNestedBinaryExpression1.kt")
public void testMultilineNestedBinaryExpression1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/multiline/multilineNestedBinaryExpression1.kt");
doIntroduceSimpleParameterTest(fileName);
}
@TestMetadata("multilineNestedBinaryExpression2.kt")
public void testMultilineNestedBinaryExpression2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/multiline/multilineNestedBinaryExpression2.kt");
doIntroduceSimpleParameterTest(fileName);
}
}
@TestMetadata("idea/testData/refactoring/introduceParameter/script") @TestMetadata("idea/testData/refactoring/introduceParameter/script")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -3697,6 +3730,39 @@ public class ExtractionTestGenerated extends AbstractExtractionTest {
doIntroduceLambdaParameterTest(fileName); doIntroduceLambdaParameterTest(fileName);
} }
@TestMetadata("idea/testData/refactoring/introduceLambdaParameter/multiline")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Multiline extends AbstractExtractionTest {
public void testAllFilesPresentInMultiline() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/introduceLambdaParameter/multiline"), Pattern.compile("^(.+)\\.(kt|kts)$"), true);
}
@TestMetadata("multilineBinaryExpression.kt")
public void testMultilineBinaryExpression() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceLambdaParameter/multiline/multilineBinaryExpression.kt");
doIntroduceLambdaParameterTest(fileName);
}
@TestMetadata("multilineBinaryExpressionWithComments.kt")
public void testMultilineBinaryExpressionWithComments() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceLambdaParameter/multiline/multilineBinaryExpressionWithComments.kt");
doIntroduceLambdaParameterTest(fileName);
}
@TestMetadata("multilineNestedBinaryExpression1.kt")
public void testMultilineNestedBinaryExpression1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceLambdaParameter/multiline/multilineNestedBinaryExpression1.kt");
doIntroduceLambdaParameterTest(fileName);
}
@TestMetadata("multilineNestedBinaryExpression2.kt")
public void testMultilineNestedBinaryExpression2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceLambdaParameter/multiline/multilineNestedBinaryExpression2.kt");
doIntroduceLambdaParameterTest(fileName);
}
}
@TestMetadata("idea/testData/refactoring/introduceLambdaParameter/stringTemplates") @TestMetadata("idea/testData/refactoring/introduceLambdaParameter/stringTemplates")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)