From 08cf8065ea07e335e574f94791c9b407166ef3f7 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Thu, 28 May 2015 16:14:15 +0300 Subject: [PATCH] DeprecatedSymbolUsageFix to keep line breaks when possible --- .../kotlin/idea/core/CommentSaver.kt | 263 +++++++++++++----- .../ConvertForEachToForLoopIntention.kt | 2 +- .../ConvertToExpressionBodyIntention.kt | 2 +- .../ConvertToForEachFunctionCallIntention.kt | 2 +- .../idea/intentions/SplitIfIntention.kt | 4 +- .../quickfix/DeprecatedSymbolUsageFixBase.kt | 4 +- .../commentBeforeArgument.kt.after | 3 +- .../keepLineBreaks/addArgument.kt | 15 + .../keepLineBreaks/addArgument.kt.after | 14 + .../lineBreakAfterReceiverRuntime.kt | 14 + .../lineBreakAfterReceiverRuntime.kt.after | 14 + .../keepLineBreaks/simple.kt | 14 + .../keepLineBreaks/simple.kt.after | 14 + .../idea/quickfix/QuickFixTestGenerated.java | 27 ++ 14 files changed, 322 insertions(+), 70 deletions(-) create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/addArgument.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/addArgument.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/lineBreakAfterReceiverRuntime.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/lineBreakAfterReceiverRuntime.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/simple.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/simple.kt.after diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/CommentSaver.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/CommentSaver.kt index 4974d6cd92b..6355121573f 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/CommentSaver.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/CommentSaver.kt @@ -19,27 +19,30 @@ package org.jetbrains.kotlin.idea.core import com.intellij.openapi.util.Key import com.intellij.openapi.util.TextRange import com.intellij.psi.* +import com.intellij.psi.codeStyle.CodeStyleManager import org.jetbrains.kotlin.lexer.JetToken import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.JetPsiFactory import org.jetbrains.kotlin.psi.psiUtil.* import java.util.ArrayList import java.util.HashMap -import java.util.LinkedHashMap +import kotlin.properties.Delegates -public class CommentSaver(originalElements: PsiChildRange) { - public constructor(originalElement: PsiElement) : this(PsiChildRange.singleElement(originalElement)) +public class CommentSaver(originalElements: PsiChildRange, private val saveLineBreaks: Boolean = false/*TODO?*/) { + public constructor(originalElement: PsiElement, saveLineBreaks: Boolean = false/*TODO?*/) : this(PsiChildRange.singleElement(originalElement), saveLineBreaks) private val SAVED_TREE_KEY = Key("SAVED_TREE") + private val psiFactory = JetPsiFactory(originalElements.first!!) private abstract class TreeElement { public companion object { public fun create(element: PsiElement): TreeElement? { - val elementType = element.getNode().getElementType() - val treeElement = when (elementType) { - TokenType.WHITE_SPACE -> if (element.textContains('\n')) LineBreakTreeElement() else null - is JetToken -> if (element is PsiComment) CommentTreeElement.create(element) else TokenTreeElement(elementType) - else -> StandardTreeElement() + val tokenType = element.tokenType + val treeElement = when { + element is PsiWhiteSpace -> if (element.textContains('\n')) LineBreakTreeElement() else null + element is PsiComment -> CommentTreeElement.create(element) + tokenType != null -> TokenTreeElement(tokenType) + else -> if (element.getTextLength() > 0) StandardTreeElement() else null // don't save empty elements } // treeElement?.debugText = element.getText() return treeElement @@ -64,6 +67,46 @@ public class CommentSaver(originalElements: PsiChildRange) { val nextSiblings: Sequence get() = sequence({ next }, { it.next }) + val parents: Sequence + get() = sequence({ parent }, { it.parent }) + + val parentsWithSelf: Sequence + get() = sequence(this, { it.parent }) + + val firstLeafInside: TreeElement + get() { + var result = this + while (true) { + result = result.firstChild ?: break + } + return result + } + + val lastLeafInside: TreeElement + get() { + var result = this + while (true) { + result = result.lastChild ?: break + } + return result + } + + val prevLeaf: TreeElement? + get() { + return (prev ?: return parent?.prevLeaf).lastLeafInside + } + + val nextLeaf: TreeElement? + get() { + return (next ?: return parent?.nextLeaf).firstLeafInside + } + + val prevLeafs: Sequence + get() = sequence({ prevLeaf }, { it.prevLeaf }) + + val nextLeafs: Sequence + get() = sequence({ nextLeaf }, { it.nextLeaf }) + fun withDescendants(leftToRight: Boolean): Sequence { val children = if (leftToRight) children else reverseChildren return sequenceOf(this) + children.flatMap { it.withDescendants(leftToRight) } @@ -97,18 +140,13 @@ public class CommentSaver(originalElements: PsiChildRange) { } private val commentsToRestore = ArrayList() + private val lineBreaksToRestore = ArrayList() + private var toNewPsiElementMap by Delegates.notNull>>() + private var needAdjustIndentAfterRestore = false init { - if (originalElements.any { it.anyDescendantOfType() }) { + if (saveLineBreaks || originalElements.any { it.anyDescendantOfType() }) { originalElements.save(null) - - for (element in originalElements) { - element.accept(object : PsiRecursiveElementVisitor(){ - override fun visitComment(comment: PsiComment) { - commentsToRestore.add(comment.savedTreeElement as CommentTreeElement) - } - }) - } } } @@ -131,6 +169,11 @@ public class CommentSaver(originalElements: PsiChildRange) { first = savedChild } + when (savedChild) { + is CommentTreeElement -> commentsToRestore.add(savedChild) + is LineBreakTreeElement -> if (saveLineBreaks) lineBreaksToRestore.add(savedChild) + } + child.allChildren.save(savedChild) } @@ -179,12 +222,44 @@ public class CommentSaver(originalElements: PsiChildRange) { }) } - public fun restoreComments(resultElement: PsiElement) { - restoreComments(PsiChildRange.singleElement(resultElement)) + private fun putNewElementIntoMap(psiElement: PsiElement, treeElement: TreeElement) { + toNewPsiElementMap.getOrPut(treeElement) { ArrayList(1) }.add(psiElement) } - public fun restoreComments(resultElements: PsiChildRange) { - doRestoreComments(resultElements) + private fun bindNewElement(newPsiElement: PsiElement, treeElement: TreeElement) { + newPsiElement.savedTreeElement = treeElement + putNewElementIntoMap(newPsiElement, treeElement) + } + + public fun restore(resultElement: PsiElement) { + restore(PsiChildRange.singleElement(resultElement)) + } + + public fun restore(resultElements: PsiChildRange) { + assert(!resultElements.isEmpty) + + if (commentsToRestore.isEmpty() && lineBreaksToRestore.isEmpty()) return + + // remove comments that present inside resultElements from commentsToRestore + resultElements.forEach { deleteCommentsInside(it) } + if (commentsToRestore.isEmpty() && lineBreaksToRestore.isEmpty()) return + + toNewPsiElementMap = HashMap>() + for (element in resultElements) { + element.accept(object : PsiRecursiveElementVisitor() { + override fun visitElement(element: PsiElement) { + val treeElement = element.savedTreeElement + if (treeElement != null) { + putNewElementIntoMap(element, treeElement) + } + super.visitElement(element) + } + }) + } + + restoreComments(resultElements) + + restoreLineBreaks() // clear user data resultElements.forEach { @@ -195,74 +270,122 @@ public class CommentSaver(originalElements: PsiChildRange) { } }) } + + if (needAdjustIndentAfterRestore) { + val file = resultElements.first().getContainingFile() + val project = file.getProject() + val psiDocumentManager = PsiDocumentManager.getInstance(project) + psiDocumentManager.doPostponedOperationsAndUnblockDocument(psiDocumentManager.getDocument(file)!!) + CodeStyleManager.getInstance(project).adjustLineIndent(file, resultElements.textRange) + } } - private fun doRestoreComments(resultElements: PsiChildRange) { - assert(!resultElements.isEmpty) - - if (commentsToRestore.isEmpty()) return - - // remove comments that present inside resultElements from commentsToRestore - resultElements.forEach { deleteCommentsInside(it) } - if (commentsToRestore.isEmpty()) return - - val toNewPsiElementMap = HashMap>() - for (element in resultElements) { - element.accept(object : PsiRecursiveElementVisitor() { - override fun visitElement(element: PsiElement) { - val treeElement = element.savedTreeElement - if (treeElement != null) { - toNewPsiElementMap.getOrPut(treeElement) { ArrayList(1) }.add(element) - } - super.visitElement(element) - } - }) - } - - val psiFactory = JetPsiFactory(resultElements.first!!) + private fun restoreComments(resultElements: PsiChildRange) { var putAbandonedCommentsAfter = resultElements.last!! for (commentTreeElement in commentsToRestore) { val comment = psiFactory.createComment(commentTreeElement.commentText) - val anchorBefore = findAnchor(commentTreeElement, toNewPsiElementMap, before = true) - val anchorAfter = findAnchor(commentTreeElement, toNewPsiElementMap, before = false) + val anchorBefore = findAnchor(commentTreeElement, before = true) + val anchorAfter = findAnchor(commentTreeElement, before = false) val anchor = chooseAnchor(anchorBefore, anchorAfter) + val restored: PsiComment if (anchor != null) { val anchorElement = findFinalAnchorElement(anchor, comment) val parent = anchorElement.getParent() if (anchor.before) { - parent.addAfter(comment, anchorElement) + restored = parent.addAfter(comment, anchorElement) as PsiComment if (commentTreeElement.spaceBefore.isNotEmpty()) { parent.addAfter(psiFactory.createWhiteSpace(commentTreeElement.spaceBefore), anchorElement) } } else { - parent.addBefore(comment, anchorElement) + restored = parent.addBefore(comment, anchorElement) as PsiComment if (commentTreeElement.spaceAfter.isNotEmpty()) { parent.addBefore(psiFactory.createWhiteSpace(commentTreeElement.spaceAfter), anchorElement) } } } else { - putAbandonedCommentsAfter = putAbandonedCommentsAfter.getParent().addAfter(comment, putAbandonedCommentsAfter) + restored = putAbandonedCommentsAfter.getParent().addAfter(comment, putAbandonedCommentsAfter) as PsiComment + putAbandonedCommentsAfter = restored + } + + bindNewElement(restored, commentTreeElement) // will be used when restoring line breaks + + if (restored.tokenType == JetTokens.EOL_COMMENT) { + needAdjustIndentAfterRestore = true // TODO: do we really need it? } } } + private fun restoreLineBreaks() { + for (lineBreakElement in lineBreaksToRestore) { + if (toNewPsiElementMap[lineBreakElement] != null) continue + + val lineBreakParent = lineBreakElement.parent + + fun findRestored(leaf: TreeElement): PsiElement? { + if (leaf is LineBreakTreeElement) return null + return leaf.parentsWithSelf + .takeWhile { it != lineBreakParent } + .map { toNewPsiElementMap[it]?.first() } //TODO: what about multiple? + .filterNotNull() + .firstOrNull() + } + + + val tokensToMatch = arrayListOf() + for (leaf in lineBreakElement.prevLeafs) { + var psiElement = findRestored(leaf) + if (psiElement != null) { + psiElement = skipTokensForward(psiElement, tokensToMatch.reverse()) + psiElement?.restoreLineBreakAfter() + break + } + else { + if (leaf !is TokenTreeElement) break + tokensToMatch.add(leaf.tokenType) + } + } + } + } + + private fun skipTokensForward(psiElement: PsiElement, tokensToMatch: List): PsiElement? { + var currentPsiElement = psiElement + for (token in tokensToMatch) { + currentPsiElement = currentPsiElement.nextLeaf(nonSpaceAndNonEmptyFilter) ?: return null + if (currentPsiElement.tokenType != token) return null + } + return currentPsiElement + } + + private fun PsiElement.restoreLineBreakAfter() { + val addAfter = shiftNewLineAnchor(this).anchorToAddCommentOrSpace(before = false) + var whitespace = addAfter.getNextSibling() as? PsiWhiteSpace + //TODO: restore blank lines + if (whitespace != null && whitespace.getText().contains('\n')) return // line break is already there + + if (whitespace == null) { + addAfter.getParent().addAfter(psiFactory.createNewLine(), addAfter) + } + else { + whitespace.replace(psiFactory.createWhiteSpace("\n" + whitespace.getText())) + } + + needAdjustIndentAfterRestore = true + } + private class Anchor(val element: PsiElement, val treeElementsBetween: Collection, val before: Boolean) - private fun findAnchor(commentTreeElement: CommentTreeElement, toNewPsiElementMap: Map>, before: Boolean): Anchor? { + private fun findAnchor(commentTreeElement: CommentTreeElement, before: Boolean): Anchor? { val treeElementsBetween = ArrayList() val sequence = if (before) commentTreeElement.prevElements else commentTreeElement.nextElements for (treeElement in sequence) { val newPsiElements = toNewPsiElementMap[treeElement] if (newPsiElements != null) { - var psiElement = newPsiElements.first() //TODO: should we restore multiple? - psiElement = psiElement.parents() - .dropWhile { it.getParent() !is PsiFile && (if (before) it.getNextSibling() else it.getPrevSibling()) == null } - .first() + val psiElement = newPsiElements.first().anchorToAddCommentOrSpace(!before) //TODO: should we restore multiple? return Anchor(psiElement, treeElementsBetween, before) } if (treeElement.firstChild == null) { // we put only leafs into treeElementsBetween @@ -272,6 +395,12 @@ public class CommentSaver(originalElements: PsiChildRange) { return null } + private fun PsiElement.anchorToAddCommentOrSpace(before: Boolean): PsiElement { + return parents() + .dropWhile { it.getParent() !is PsiFile && (if (before) it.getPrevSibling() else it.getNextSibling()) == null } + .first() + } + private fun chooseAnchor(anchorBefore: Anchor?, anchorAfter: Anchor?): Anchor? { if (anchorBefore == null) return anchorAfter if (anchorAfter == null) return anchorBefore @@ -292,25 +421,35 @@ public class CommentSaver(originalElements: PsiChildRange) { val tokensBetween = anchor.treeElementsBetween.filterIsInstance() fun PsiElement.next(): PsiElement? { - val filter = { element: PsiElement -> element !is PsiWhiteSpace && element.getTextLength() > 0 } - return if (anchor.before) nextLeaf(filter) else prevLeaf(filter) + return if (anchor.before) nextLeaf(nonSpaceAndNonEmptyFilter) else prevLeaf(nonSpaceAndNonEmptyFilter) } var psiElement = anchor.element for (token in tokensBetween.reverse()) { val next = psiElement.next() ?: break - if (next.getNode().getElementType() != token.tokenType) break + if (next.tokenType != token.tokenType) break psiElement = next } // don't put end of line comment right before comma - if (anchor.before && comment.getNode().getElementType() == JetTokens.EOL_COMMENT) { - val next = psiElement.next() - if (next != null && next.getNode().getElementType() == JetTokens.COMMA) { - psiElement = next - } + if (anchor.before && comment.tokenType == JetTokens.EOL_COMMENT) { + psiElement = shiftNewLineAnchor(psiElement) } return psiElement } + + // don't put line break right before comma + private fun shiftNewLineAnchor(putAfter: PsiElement): PsiElement { + val next = putAfter.nextLeaf(nonSpaceAndNonEmptyFilter) + return if (next?.tokenType == JetTokens.COMMA) next!! else putAfter + } + + private val nonSpaceAndNonEmptyFilter = { element: PsiElement -> element !is PsiWhiteSpace && element.getTextLength() > 0 } + + companion object { + //TODO: making it private causes error on runtime + val PsiElement.tokenType: JetToken? + get() = getNode().getElementType() as? JetToken + } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertForEachToForLoopIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertForEachToForLoopIntention.kt index 8459a07073d..8d0a2966598 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertForEachToForLoopIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertForEachToForLoopIntention.kt @@ -43,7 +43,7 @@ public class ConvertForEachToForLoopIntention : JetSelfTargetingOffsetIndependen val loop = generateLoop(functionLiteral, receiver) val result = expressionToReplace.replace(loop) - commentSaver.restoreComments(result) + commentSaver.restore(result) } private data class Data( diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToExpressionBodyIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToExpressionBodyIntention.kt index 12c85d1cc86..b541aad4602 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToExpressionBodyIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToExpressionBodyIntention.kt @@ -77,7 +77,7 @@ public class ConvertToExpressionBodyIntention : JetSelfTargetingOffsetIndependen declaration.addBefore(JetPsiFactory(declaration).createEQ(), body) val newBody = body.replace(value) - commentSaver.restoreComments(newBody) + commentSaver.restore(newBody) if (omitType) { onFinish(declaration as JetCallableDeclaration) diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToForEachFunctionCallIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToForEachFunctionCallIntention.kt index bd43e91f610..0bc8076d28c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToForEachFunctionCallIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToForEachFunctionCallIntention.kt @@ -44,6 +44,6 @@ public class ConvertToForEachFunctionCallIntention : JetSelfTargetingIntention$2}", element.getLoopRange()!!, loopParameter, functionBodyArgument) val result = element.replace(foreachExpression) - commentSaver.restoreComments(result) + commentSaver.restore(result) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/SplitIfIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/SplitIfIntention.kt index 8b929264c9c..fe76a44eae1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/SplitIfIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/SplitIfIntention.kt @@ -69,7 +69,7 @@ public class SplitIfIntention : JetSelfTargetingIntention(javaCla val secondIf = container.addAfter(innerIf, ifExpression) container.addAfter(psiFactory.createNewLine(), ifExpression) val firstIf = ifExpression.replace(psiFactory.createIf(leftExpression, thenBranch)) - commentSaver.restoreComments(PsiChildRange(firstIf, secondIf)) + commentSaver.restore(PsiChildRange(firstIf, secondIf)) return } else { @@ -81,7 +81,7 @@ public class SplitIfIntention : JetSelfTargetingIntention(javaCla } val result = ifExpression.replace(newIf) - commentSaver.restoreComments(result) + commentSaver.restore(result) } private fun getRight(element: JetBinaryExpression, condition: JetExpression, commentSaver: CommentSaver): JetExpression { diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt index 268d65feaf2..4bcfaa2feca 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedSymbolUsageFixBase.kt @@ -136,7 +136,7 @@ public abstract class DeprecatedSymbolUsageFixBase( val qualifiedExpression = if (parent is JetQualifiedExpression && callExpression == parent.getSelectorExpression()) parent else null val expressionToBeReplaced = qualifiedExpression ?: callExpression - val commentSaver = CommentSaver(expressionToBeReplaced) + val commentSaver = CommentSaver(expressionToBeReplaced, saveLineBreaks = true) var receiver = element.getReceiverExpression()?.marked(USER_CODE_KEY) var receiverType = if (receiver != null) bindingContext.getType(receiver) else null @@ -231,7 +231,7 @@ public abstract class DeprecatedSymbolUsageFixBase( PsiChildRange.singleElement(result) else PsiChildRange(wrapper.addedStatements.first(), result) - commentSaver.restoreComments(resultRange) + commentSaver.restore(resultRange) return result } diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/keepComments/commentBeforeArgument.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/keepComments/commentBeforeArgument.kt.after index 7074f0505dc..9deeea4a612 100644 --- a/idea/testData/quickfix/deprecatedSymbolUsage/keepComments/commentBeforeArgument.kt.after +++ b/idea/testData/quickfix/deprecatedSymbolUsage/keepComments/commentBeforeArgument.kt.after @@ -8,5 +8,6 @@ fun newFun(p4: Int, p3: Int, p2: Int, p1: Int, p0: Int){} fun foo() { newFun(/* 4 */ 4, 3, 2, // 2 - /* 1 */ 1, /* 0 */ 0) + /* 1 */ 1, + /* 0 */ 0) } diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/addArgument.kt b/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/addArgument.kt new file mode 100644 index 00000000000..f73f4780d4c --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/addArgument.kt @@ -0,0 +1,15 @@ +// "Replace with 'newFun(p1, p2, 0)'" "true" + +interface X { + @deprecated("", ReplaceWith("newFun(p1, p2, 0)")) + fun oldFun(p1: Int, p2: Int) + + fun newFun(p1: Int, p2: Int, p3: Int) +} + +fun foo(x: X) { + x.oldFun( + 1, + 2 + ) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/addArgument.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/addArgument.kt.after new file mode 100644 index 00000000000..1844500399b --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/addArgument.kt.after @@ -0,0 +1,14 @@ +// "Replace with 'newFun(p1, p2, 0)'" "true" + +interface X { + @deprecated("", ReplaceWith("newFun(p1, p2, 0)")) + fun oldFun(p1: Int, p2: Int) + + fun newFun(p1: Int, p2: Int, p3: Int) +} + +fun foo(x: X) { + x.newFun(1, + 2, + 0) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/lineBreakAfterReceiverRuntime.kt b/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/lineBreakAfterReceiverRuntime.kt new file mode 100644 index 00000000000..1a67e22db02 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/lineBreakAfterReceiverRuntime.kt @@ -0,0 +1,14 @@ +// "Replace with 'newFun()'" "true" + +@deprecated("", ReplaceWith("newFun()")) +fun Int.oldFun(): Int = this + +fun Int.newFun(): Int = this + +fun foo(list: List): Int { + return list + .filter { it.isNotEmpty() } + .map { it.length() } + .first() + .oldFun() +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/lineBreakAfterReceiverRuntime.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/lineBreakAfterReceiverRuntime.kt.after new file mode 100644 index 00000000000..be6ead59c87 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/lineBreakAfterReceiverRuntime.kt.after @@ -0,0 +1,14 @@ +// "Replace with 'newFun()'" "true" + +@deprecated("", ReplaceWith("newFun()")) +fun Int.oldFun(): Int = this + +fun Int.newFun(): Int = this + +fun foo(list: List): Int { + return list + .filter { it.isNotEmpty() } + .map { it.length() } + .first() + .newFun() +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/simple.kt b/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/simple.kt new file mode 100644 index 00000000000..9af97d882e7 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/simple.kt @@ -0,0 +1,14 @@ +// "Replace with 'newFun(p1, p2, p3)'" "true" + +interface X { + @deprecated("", ReplaceWith("newFun(p1, p2, p3)")) + fun oldFun(p1: Int, p2: Int, p3: Int) + + fun newFun(p1: Int, p2: Int, p3: Int) +} + +fun foo(x: X) { + x.oldFun(1, + 2, + 3) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/simple.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/simple.kt.after new file mode 100644 index 00000000000..c5004fa12c2 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/simple.kt.after @@ -0,0 +1,14 @@ +// "Replace with 'newFun(p1, p2, p3)'" "true" + +interface X { + @deprecated("", ReplaceWith("newFun(p1, p2, p3)")) + fun oldFun(p1: Int, p2: Int, p3: Int) + + fun newFun(p1: Int, p2: Int, p3: Int) +} + +fun foo(x: X) { + x.newFun(1, + 2, + 3) +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 87e40f2d2ec..ef4e72166ef 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -3289,6 +3289,33 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class KeepLineBreaks extends AbstractQuickFixTest { + @TestMetadata("addArgument.kt") + public void testAddArgument() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/addArgument.kt"); + doTest(fileName); + } + + public void testAllFilesPresentInKeepLineBreaks() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("lineBreakAfterReceiverRuntime.kt") + public void testLineBreakAfterReceiverRuntime() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/lineBreakAfterReceiverRuntime.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)