diff --git a/ChangeLog.md b/ChangeLog.md index abc414ea50a..d024b4a3174 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -246,6 +246,7 @@ Using 'this' as function argument in constructor of non-final class - [`KT-13157`](https://youtrack.jetbrains.com/issue/KT-13157) Extract Function: Automatically quote function name if necessary - [`KT-13010`](https://youtrack.jetbrains.com/issue/KT-13010) Extract Function: Fix generation of destructuring declarations - [`KT-13128`](https://youtrack.jetbrains.com/issue/KT-13128) Introduce Variable: Retain entered name after changing "Specify type explicitly" option +- [`KT-13054`](https://youtrack.jetbrains.com/issue/KT-13054) Introduce Variable: Skip leading/trailing comments inside selection ##### New features diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt index 81f613fe297..dab06e24bde 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt @@ -136,12 +136,12 @@ inline fun PsiElement.getChildrenOfType(): Array { return PsiTreeUtil.getChildrenOfType(this, T::class.java) ?: arrayOf() } -fun PsiElement.getNextSiblingIgnoringWhitespaceAndComments(): PsiElement? { - return siblings(withItself = false).filter { it !is PsiWhiteSpace && it !is PsiComment }.firstOrNull() +fun PsiElement.getNextSiblingIgnoringWhitespaceAndComments(withItself: Boolean = false): PsiElement? { + return siblings(withItself = withItself).filter { it !is PsiWhiteSpace && it !is PsiComment }.firstOrNull() } -fun PsiElement.getPrevSiblingIgnoringWhitespaceAndComments(): PsiElement? { - return siblings(withItself = false, forward = false).filter { it !is PsiWhiteSpace && it !is PsiComment }.firstOrNull() +fun PsiElement.getPrevSiblingIgnoringWhitespaceAndComments(withItself: Boolean = false): PsiElement? { + return siblings(withItself = withItself, forward = false).filter { it !is PsiWhiteSpace && it !is PsiComment }.firstOrNull() } inline fun T.nextSiblingOfSameType() = PsiTreeUtil.getNextSiblingOfType(this, T::class.java) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringUtil.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringUtil.java index 16012bfd69d..8d00b6a77c2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringUtil.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringUtil.java @@ -53,6 +53,7 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.IntroduceUtilKt; import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt; +import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; import org.jetbrains.kotlin.renderer.DescriptorRenderer; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; @@ -350,9 +351,17 @@ public class KotlinRefactoringUtil { if (editor.getSelectionModel().hasSelection()) { int selectionStart = editor.getSelectionModel().getSelectionStart(); int selectionEnd = editor.getSelectionModel().getSelectionEnd(); - String text = file.getText(); - while (selectionStart < selectionEnd && Character.isSpaceChar(text.charAt(selectionStart))) ++selectionStart; - while (selectionStart < selectionEnd && Character.isSpaceChar(text.charAt(selectionEnd - 1))) --selectionEnd; + + PsiElement firstElement = file.findElementAt(selectionStart); + PsiElement lastElement = file.findElementAt(selectionEnd - 1); + + if (PsiTreeUtil.getParentOfType(firstElement, KtLiteralStringTemplateEntry.class, KtEscapeStringTemplateEntry.class) == null + && PsiTreeUtil.getParentOfType(lastElement, KtLiteralStringTemplateEntry.class, KtEscapeStringTemplateEntry.class) == null) { + firstElement = PsiUtilsKt.getNextSiblingIgnoringWhitespaceAndComments(firstElement, true); + lastElement = PsiUtilsKt.getPrevSiblingIgnoringWhitespaceAndComments(lastElement, true); + selectionStart = firstElement.getTextRange().getStartOffset(); + selectionEnd = lastElement.getTextRange().getEndOffset(); + } for (CodeInsightUtils.ElementKind elementKind : elementKinds) { PsiElement element = findElement(file, selectionStart, selectionEnd, failOnEmptySuggestion, elementKind); diff --git a/idea/testData/refactoring/introduceVariable/commentSkipping.kt b/idea/testData/refactoring/introduceVariable/commentSkipping.kt new file mode 100644 index 00000000000..ee1a3ee37aa --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/commentSkipping.kt @@ -0,0 +1,7 @@ +fun foo(x: Boolean) { + +} + +fun bar() { + foo(/* 1 */true/* 2 */) +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/commentSkipping.kt.after b/idea/testData/refactoring/introduceVariable/commentSkipping.kt.after new file mode 100644 index 00000000000..87952ab1a3e --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/commentSkipping.kt.after @@ -0,0 +1,8 @@ +fun foo(x: Boolean) { + +} + +fun bar() { + val x = true + foo(/* 1 */x/* 2 */) +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/stringTemplates/extractTrueWithSpaces.kt b/idea/testData/refactoring/introduceVariable/stringTemplates/extractTrueWithSpaces.kt new file mode 100644 index 00000000000..e56ef95182e --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/stringTemplates/extractTrueWithSpaces.kt @@ -0,0 +1,7 @@ +fun foo(param: Int): String { + val x = "atrue123" + val y = "aTRUE123" + val z = true + val u = "123 true 456" + return "ab true def" +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/stringTemplates/extractTrueWithSpaces.kt.after b/idea/testData/refactoring/introduceVariable/stringTemplates/extractTrueWithSpaces.kt.after new file mode 100644 index 00000000000..50b597e01d8 --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/stringTemplates/extractTrueWithSpaces.kt.after @@ -0,0 +1,8 @@ +fun foo(param: Int): String { + val x = "atrue123" + val y = "aTRUE123" + val z = true + val s = " true " + val u = "123${s}456" + return "ab${s}def" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java index 1b28cc61e9c..51deba43f85 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java @@ -61,6 +61,12 @@ public class ExtractionTestGenerated extends AbstractExtractionTest { doIntroduceVariableTest(fileName); } + @TestMetadata("commentSkipping.kt") + public void testCommentSkipping() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/commentSkipping.kt"); + doIntroduceVariableTest(fileName); + } + @TestMetadata("ComplexCallee.kt") public void testComplexCallee() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/ComplexCallee.kt"); @@ -735,6 +741,12 @@ public class ExtractionTestGenerated extends AbstractExtractionTest { doIntroduceVariableTest(fileName); } + @TestMetadata("extractTrueWithSpaces.kt") + public void testExtractTrueWithSpaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/extractTrueWithSpaces.kt"); + doIntroduceVariableTest(fileName); + } + @TestMetadata("fullContent.kt") public void testFullContent() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/stringTemplates/fullContent.kt");