Add intentions to put arguments / parameters on one line #KT-23266 Fixed

This commit is contained in:
Toshiaki Kameyama
2018-03-26 15:49:49 +03:00
committed by Mikhail Glukhikh
parent 96581f1ecb
commit 03902030ed
28 changed files with 285 additions and 33 deletions
@@ -0,0 +1,3 @@
fun foo() {
bar(<spot>1, "abcd", false</spot>)
}
@@ -0,0 +1,7 @@
fun foo() {
bar(
<spot>1,
"abcd",
false</spot>
)
}
@@ -0,0 +1,5 @@
<html>
<body>
This intention formats a function call placing all arguments on a single line.
</body>
</html>
@@ -0,0 +1,3 @@
fun foo(<spot>param1: String, param2: Int, param3: Any</spot>) {
bar()
}
@@ -0,0 +1,7 @@
fun foo(
<spot>param1: String,
param2: Int,
param3: Any</spot>
) {
bar()
}
@@ -0,0 +1,5 @@
<html>
<body>
This intention formats parameters of a declaration placing all parameters on a single line.
</body>
</html>
+10
View File
@@ -1572,6 +1572,16 @@
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.JoinParameterListIntention</className>
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.JoinArgumentListIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
displayName="Object literal can be converted to lambda"
groupPath="Kotlin"
@@ -29,9 +29,9 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jetbrains.kotlin.psi.psiUtil.startOffset
abstract class AbstractChopListIntention<TList : KtElement, TElement : KtElement>(
private val listClass: Class<TList>,
private val elementClass: Class<TElement>,
text: String
private val listClass: Class<TList>,
private val elementClass: Class<TElement>,
text: String
) : SelfTargetingOffsetIndependentIntention<TList>(listClass, text), LowPriorityAction {
override fun isApplicableTo(element: TList): Boolean {
@@ -41,20 +41,20 @@ abstract class AbstractChopListIntention<TList : KtElement, TElement : KtElement
return true
}
override fun applyTo(list: TList, editor: Editor?) {
val project = list.project
override fun applyTo(element: TList, editor: Editor?) {
val project = element.project
val document = editor!!.document
val startOffset = list.startOffset
val startOffset = element.startOffset
val elements = list.elements()
val elements = element.elements()
if (!hasLineBreakAfter(elements.last())) {
val rpar = list.allChildren.lastOrNull { it.node.elementType == KtTokens.RPAR }
val rpar = element.allChildren.lastOrNull { it.node.elementType == KtTokens.RPAR }
rpar?.startOffset?.let { document.insertString(it, "\n") }
}
for (element in elements.asReversed()) {
if (!hasLineBreakBefore(element)) {
document.insertString(element.startOffset, "\n")
for (e in elements.asReversed()) {
if (!hasLineBreakBefore(e)) {
document.insertString(e.startOffset, "\n")
}
}
@@ -65,35 +65,43 @@ abstract class AbstractChopListIntention<TList : KtElement, TElement : KtElement
CodeStyleManager.getInstance(project).adjustLineIndent(psiFile, newList.textRange)
}
private fun hasLineBreakAfter(element: TElement): Boolean {
return element
.siblings(withItself = false)
.takeWhile { !elementClass.isInstance(it) }
.any { it is PsiWhiteSpace && it.textContains('\n') }
protected fun hasLineBreakAfter(element: TElement): Boolean {
return nextBreak(element) != null
}
private fun hasLineBreakBefore(element: TElement): Boolean {
protected fun nextBreak(element: TElement): PsiWhiteSpace? {
return element
.siblings(withItself = false, forward = false)
.takeWhile { !elementClass.isInstance(it) }
.any { it is PsiWhiteSpace && it.textContains('\n') }
.siblings(withItself = false)
.takeWhile { !elementClass.isInstance(it) }
.firstOrNull { it is PsiWhiteSpace && it.textContains('\n') } as? PsiWhiteSpace
}
private fun TList.elements(): List<TElement> {
protected fun hasLineBreakBefore(element: TElement): Boolean {
return prevBreak(element) != null
}
protected fun prevBreak(element: TElement): PsiWhiteSpace? {
return element
.siblings(withItself = false, forward = false)
.takeWhile { !elementClass.isInstance(it) }
.firstOrNull { it is PsiWhiteSpace && it.textContains('\n') } as? PsiWhiteSpace
}
protected fun TList.elements(): List<TElement> {
return allChildren
.filter { elementClass.isInstance(it) }
.map {
@Suppress("UNCHECKED_CAST")
it as TElement
}
.toList()
.filter { elementClass.isInstance(it) }
.map {
@Suppress("UNCHECKED_CAST")
it as TElement
}
.toList()
}
}
class ChopParameterListIntention : AbstractChopListIntention<KtParameterList, KtParameter>(
KtParameterList::class.java,
KtParameter::class.java,
"Put parameters on separate lines"
KtParameterList::class.java,
KtParameter::class.java,
"Put parameters on separate lines"
) {
override fun isApplicableTo(element: KtParameterList): Boolean {
if (element.parent is KtFunctionLiteral) return false
@@ -102,7 +110,7 @@ class ChopParameterListIntention : AbstractChopListIntention<KtParameterList, Kt
}
class ChopArgumentListIntention : AbstractChopListIntention<KtValueArgumentList, KtValueArgument>(
KtValueArgumentList::class.java,
KtValueArgument::class.java,
"Put arguments on separate lines"
KtValueArgumentList::class.java,
KtValueArgument::class.java,
"Put arguments on separate lines"
)
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.intentions
import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.psi.*
abstract class AbstractJoinListIntention<TList : KtElement, TElement : KtElement>(
listClass: Class<TList>,
elementClass: Class<TElement>,
text: String
) : AbstractChopListIntention<TList, TElement>(listClass, elementClass, text) {
override fun isApplicableTo(element: TList): Boolean {
val elements = element.elements()
if (elements.isEmpty()) return false
return hasLineBreakBefore(elements.first()) || elements.any { hasLineBreakAfter(it) }
}
override fun applyTo(element: TList, editor: Editor?) {
val elements = element.elements()
prevBreak(elements.first())?.delete()
elements.forEach { nextBreak(it)?.delete() }
}
}
class JoinParameterListIntention : AbstractJoinListIntention<KtParameterList, KtParameter>(
KtParameterList::class.java,
KtParameter::class.java,
"Put parameters on one line"
) {
override fun isApplicableTo(element: KtParameterList): Boolean {
if (element.parent is KtFunctionLiteral) return false
return super.isApplicableTo(element)
}
}
class JoinArgumentListIntention : AbstractJoinListIntention<KtValueArgumentList, KtValueArgument>(
KtValueArgumentList::class.java,
KtValueArgument::class.java,
"Put arguments on one line"
)
+1
View File
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.intentions.JoinArgumentListIntention
@@ -0,0 +1,6 @@
fun foo(a: Int = 1, b: Int = 1, c: Int = 1) {}
fun bar(a: Int, b: Int, c: Int) {
foo(<caret>
a, b, c)
}
@@ -0,0 +1,5 @@
fun foo(a: Int = 1, b: Int = 1, c: Int = 1) {}
fun bar(a: Int, b: Int, c: Int) {
foo(a, b, c)
}
@@ -0,0 +1,11 @@
// INTENTION_TEXT: "Put arguments on one line"
fun foo(a: Int = 1, b: Int = 1, c: Int = 1) {}
fun bar(a: Int, b: Int, c: Int) {
foo(
a,
b,<caret>
c
)
}
@@ -0,0 +1,7 @@
// INTENTION_TEXT: "Put arguments on one line"
fun foo(a: Int = 1, b: Int = 1, c: Int = 1) {}
fun bar(a: Int, b: Int, c: Int) {
foo(a, b, c)
}
+7
View File
@@ -0,0 +1,7 @@
// IS_APPLICABLE: false
fun foo(a: Int = 1, b: Int = 1, c: Int = 1) {}
fun bar(a: Int, b: Int, c: Int) {
foo(<caret>)
}
@@ -0,0 +1,7 @@
// IS_APPLICABLE: false
fun foo(a: Int = 1, b: Int = 1, c: Int = 1) {}
fun bar(a: Int, b: Int, c: Int) {
foo(a, b, c<caret>)
}
+7
View File
@@ -0,0 +1,7 @@
fun foo(a: Int = 1, b: Int = 1, c: Int = 1) {}
fun bar(a: Int, b: Int, c: Int) {
foo(<caret>
a
)
}
@@ -0,0 +1,5 @@
fun foo(a: Int = 1, b: Int = 1, c: Int = 1) {}
fun bar(a: Int, b: Int, c: Int) {
foo(a)
}
+1
View File
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.intentions.JoinParameterListIntention
@@ -0,0 +1,2 @@
fun test(<caret>
a: Int, b: Int, c: Int) {}
@@ -0,0 +1 @@
fun test(a: Int, b: Int, c: Int) {}
@@ -0,0 +1,7 @@
// INTENTION_TEXT: "Put parameters on one line"
fun test(
a: Int,
b: Int,<caret>
c: Int
) {}
@@ -0,0 +1,3 @@
// INTENTION_TEXT: "Put parameters on one line"
fun test(a: Int, b: Int, c: Int) {}
@@ -0,0 +1,3 @@
// IS_APPLICABLE: false
fun test(a: Int, b: Int, c: Int<caret>) {}
+3
View File
@@ -0,0 +1,3 @@
// IS_APPLICABLE: false
fun test(<caret>) {}
@@ -0,0 +1,3 @@
fun test(<caret>
a: Int
) {}
@@ -0,0 +1 @@
fun test(a: Int) {}
@@ -10048,6 +10048,45 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
}
}
@TestMetadata("idea/testData/intentions/joinArgumentList")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class JoinArgumentList extends AbstractIntentionTest {
public void testAllFilesPresentInJoinArgumentList() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/joinArgumentList"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
}
@TestMetadata("hasLineBreakBeforeFirstArg.kt")
public void testHasLineBreakBeforeFirstArg() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/joinArgumentList/hasLineBreakBeforeFirstArg.kt");
doTest(fileName);
}
@TestMetadata("hasLineBreaks.kt")
public void testHasLineBreaks() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/joinArgumentList/hasLineBreaks.kt");
doTest(fileName);
}
@TestMetadata("noArg.kt")
public void testNoArg() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/joinArgumentList/noArg.kt");
doTest(fileName);
}
@TestMetadata("noLineBreak.kt")
public void testNoLineBreak() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/joinArgumentList/noLineBreak.kt");
doTest(fileName);
}
@TestMetadata("oneArg.kt")
public void testOneArg() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/joinArgumentList/oneArg.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/intentions/joinDeclarationAndAssignment")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -10201,6 +10240,45 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
}
}
@TestMetadata("idea/testData/intentions/joinParameterList")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class JoinParameterList extends AbstractIntentionTest {
public void testAllFilesPresentInJoinParameterList() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/joinParameterList"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
}
@TestMetadata("hasLineBreakBeforeFirstParam.kt")
public void testHasLineBreakBeforeFirstParam() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/joinParameterList/hasLineBreakBeforeFirstParam.kt");
doTest(fileName);
}
@TestMetadata("hasLineBreaks.kt")
public void testHasLineBreaks() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/joinParameterList/hasLineBreaks.kt");
doTest(fileName);
}
@TestMetadata("noLineBreak.kt")
public void testNoLineBreak() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/joinParameterList/noLineBreak.kt");
doTest(fileName);
}
@TestMetadata("noParam.kt")
public void testNoParam() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/joinParameterList/noParam.kt");
doTest(fileName);
}
@TestMetadata("oneParam.kt")
public void testOneParam() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/joinParameterList/oneParam.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/intentions/loopToCallChain")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)