diff --git a/idea/resources/intentionDescriptions/JoinArgumentListIntention/after.kt.template b/idea/resources/intentionDescriptions/JoinArgumentListIntention/after.kt.template new file mode 100644 index 00000000000..d7c0cd5caa6 --- /dev/null +++ b/idea/resources/intentionDescriptions/JoinArgumentListIntention/after.kt.template @@ -0,0 +1,3 @@ +fun foo() { + bar(1, "abcd", false) +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/JoinArgumentListIntention/before.kt.template b/idea/resources/intentionDescriptions/JoinArgumentListIntention/before.kt.template new file mode 100644 index 00000000000..2d719eff48b --- /dev/null +++ b/idea/resources/intentionDescriptions/JoinArgumentListIntention/before.kt.template @@ -0,0 +1,7 @@ +fun foo() { + bar( + 1, + "abcd", + false + ) +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/JoinArgumentListIntention/description.html b/idea/resources/intentionDescriptions/JoinArgumentListIntention/description.html new file mode 100644 index 00000000000..ec693132c2c --- /dev/null +++ b/idea/resources/intentionDescriptions/JoinArgumentListIntention/description.html @@ -0,0 +1,5 @@ + + +This intention formats a function call placing all arguments on a single line. + + \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/JoinParameterListIntention/after.kt.template b/idea/resources/intentionDescriptions/JoinParameterListIntention/after.kt.template new file mode 100644 index 00000000000..7750583e605 --- /dev/null +++ b/idea/resources/intentionDescriptions/JoinParameterListIntention/after.kt.template @@ -0,0 +1,3 @@ +fun foo(param1: String, param2: Int, param3: Any) { + bar() +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/JoinParameterListIntention/before.kt.template b/idea/resources/intentionDescriptions/JoinParameterListIntention/before.kt.template new file mode 100644 index 00000000000..190a75155f9 --- /dev/null +++ b/idea/resources/intentionDescriptions/JoinParameterListIntention/before.kt.template @@ -0,0 +1,7 @@ +fun foo( + param1: String, + param2: Int, + param3: Any +) { + bar() +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/JoinParameterListIntention/description.html b/idea/resources/intentionDescriptions/JoinParameterListIntention/description.html new file mode 100644 index 00000000000..3729dc1d076 --- /dev/null +++ b/idea/resources/intentionDescriptions/JoinParameterListIntention/description.html @@ -0,0 +1,5 @@ + + +This intention formats parameters of a declaration placing all parameters on a single line. + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index de6b8dc6492..6b3675727a7 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1572,6 +1572,16 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.JoinParameterListIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.JoinArgumentListIntention + Kotlin + + ( - private val listClass: Class, - private val elementClass: Class, - text: String + private val listClass: Class, + private val elementClass: Class, + text: String ) : SelfTargetingOffsetIndependentIntention(listClass, text), LowPriorityAction { override fun isApplicableTo(element: TList): Boolean { @@ -41,20 +41,20 @@ abstract class AbstractChopListIntention { + 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 { 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::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( - KtValueArgumentList::class.java, - KtValueArgument::class.java, - "Put arguments on separate lines" + KtValueArgumentList::class.java, + KtValueArgument::class.java, + "Put arguments on separate lines" ) \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/JoinParameterListIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/JoinParameterListIntention.kt new file mode 100644 index 00000000000..d9b3791ebde --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/JoinParameterListIntention.kt @@ -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( + listClass: Class, + elementClass: Class, + text: String +) : AbstractChopListIntention(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::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::class.java, + KtValueArgument::class.java, + "Put arguments on one line" +) diff --git a/idea/testData/intentions/joinArgumentList/.intention b/idea/testData/intentions/joinArgumentList/.intention new file mode 100644 index 00000000000..2898110acae --- /dev/null +++ b/idea/testData/intentions/joinArgumentList/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.JoinArgumentListIntention diff --git a/idea/testData/intentions/joinArgumentList/hasLineBreakBeforeFirstArg.kt b/idea/testData/intentions/joinArgumentList/hasLineBreakBeforeFirstArg.kt new file mode 100644 index 00000000000..59a67a87ee0 --- /dev/null +++ b/idea/testData/intentions/joinArgumentList/hasLineBreakBeforeFirstArg.kt @@ -0,0 +1,6 @@ +fun foo(a: Int = 1, b: Int = 1, c: Int = 1) {} + +fun bar(a: Int, b: Int, c: Int) { + foo( + a, b, c) +} \ No newline at end of file diff --git a/idea/testData/intentions/joinArgumentList/hasLineBreakBeforeFirstArg.kt.after b/idea/testData/intentions/joinArgumentList/hasLineBreakBeforeFirstArg.kt.after new file mode 100644 index 00000000000..a74604d207b --- /dev/null +++ b/idea/testData/intentions/joinArgumentList/hasLineBreakBeforeFirstArg.kt.after @@ -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) +} \ No newline at end of file diff --git a/idea/testData/intentions/joinArgumentList/hasLineBreaks.kt b/idea/testData/intentions/joinArgumentList/hasLineBreaks.kt new file mode 100644 index 00000000000..b47b6402cdf --- /dev/null +++ b/idea/testData/intentions/joinArgumentList/hasLineBreaks.kt @@ -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, + c + ) +} \ No newline at end of file diff --git a/idea/testData/intentions/joinArgumentList/hasLineBreaks.kt.after b/idea/testData/intentions/joinArgumentList/hasLineBreaks.kt.after new file mode 100644 index 00000000000..8e332a8eb48 --- /dev/null +++ b/idea/testData/intentions/joinArgumentList/hasLineBreaks.kt.after @@ -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) +} \ No newline at end of file diff --git a/idea/testData/intentions/joinArgumentList/noArg.kt b/idea/testData/intentions/joinArgumentList/noArg.kt new file mode 100644 index 00000000000..a2a03cf08f8 --- /dev/null +++ b/idea/testData/intentions/joinArgumentList/noArg.kt @@ -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() +} \ No newline at end of file diff --git a/idea/testData/intentions/joinArgumentList/noLineBreak.kt b/idea/testData/intentions/joinArgumentList/noLineBreak.kt new file mode 100644 index 00000000000..a93dd7716ed --- /dev/null +++ b/idea/testData/intentions/joinArgumentList/noLineBreak.kt @@ -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) +} \ No newline at end of file diff --git a/idea/testData/intentions/joinArgumentList/oneArg.kt b/idea/testData/intentions/joinArgumentList/oneArg.kt new file mode 100644 index 00000000000..16016c2163a --- /dev/null +++ b/idea/testData/intentions/joinArgumentList/oneArg.kt @@ -0,0 +1,7 @@ +fun foo(a: Int = 1, b: Int = 1, c: Int = 1) {} + +fun bar(a: Int, b: Int, c: Int) { + foo( + a + ) +} \ No newline at end of file diff --git a/idea/testData/intentions/joinArgumentList/oneArg.kt.after b/idea/testData/intentions/joinArgumentList/oneArg.kt.after new file mode 100644 index 00000000000..8c23f9213b0 --- /dev/null +++ b/idea/testData/intentions/joinArgumentList/oneArg.kt.after @@ -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) +} \ No newline at end of file diff --git a/idea/testData/intentions/joinParameterList/.intention b/idea/testData/intentions/joinParameterList/.intention new file mode 100644 index 00000000000..8b7bd760e10 --- /dev/null +++ b/idea/testData/intentions/joinParameterList/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.JoinParameterListIntention diff --git a/idea/testData/intentions/joinParameterList/hasLineBreakBeforeFirstParam.kt b/idea/testData/intentions/joinParameterList/hasLineBreakBeforeFirstParam.kt new file mode 100644 index 00000000000..1cf293711d5 --- /dev/null +++ b/idea/testData/intentions/joinParameterList/hasLineBreakBeforeFirstParam.kt @@ -0,0 +1,2 @@ +fun test( + a: Int, b: Int, c: Int) {} \ No newline at end of file diff --git a/idea/testData/intentions/joinParameterList/hasLineBreakBeforeFirstParam.kt.after b/idea/testData/intentions/joinParameterList/hasLineBreakBeforeFirstParam.kt.after new file mode 100644 index 00000000000..7ad8b2ff6fb --- /dev/null +++ b/idea/testData/intentions/joinParameterList/hasLineBreakBeforeFirstParam.kt.after @@ -0,0 +1 @@ +fun test(a: Int, b: Int, c: Int) {} \ No newline at end of file diff --git a/idea/testData/intentions/joinParameterList/hasLineBreaks.kt b/idea/testData/intentions/joinParameterList/hasLineBreaks.kt new file mode 100644 index 00000000000..9a69c7b2325 --- /dev/null +++ b/idea/testData/intentions/joinParameterList/hasLineBreaks.kt @@ -0,0 +1,7 @@ +// INTENTION_TEXT: "Put parameters on one line" + +fun test( + a: Int, + b: Int, + c: Int +) {} \ No newline at end of file diff --git a/idea/testData/intentions/joinParameterList/hasLineBreaks.kt.after b/idea/testData/intentions/joinParameterList/hasLineBreaks.kt.after new file mode 100644 index 00000000000..9739b3502ed --- /dev/null +++ b/idea/testData/intentions/joinParameterList/hasLineBreaks.kt.after @@ -0,0 +1,3 @@ +// INTENTION_TEXT: "Put parameters on one line" + +fun test(a: Int, b: Int, c: Int) {} \ No newline at end of file diff --git a/idea/testData/intentions/joinParameterList/noLineBreak.kt b/idea/testData/intentions/joinParameterList/noLineBreak.kt new file mode 100644 index 00000000000..ebef9cbe491 --- /dev/null +++ b/idea/testData/intentions/joinParameterList/noLineBreak.kt @@ -0,0 +1,3 @@ +// IS_APPLICABLE: false + +fun test(a: Int, b: Int, c: Int) {} \ No newline at end of file diff --git a/idea/testData/intentions/joinParameterList/noParam.kt b/idea/testData/intentions/joinParameterList/noParam.kt new file mode 100644 index 00000000000..d35cb73b60e --- /dev/null +++ b/idea/testData/intentions/joinParameterList/noParam.kt @@ -0,0 +1,3 @@ +// IS_APPLICABLE: false + +fun test() {} \ No newline at end of file diff --git a/idea/testData/intentions/joinParameterList/oneParam.kt b/idea/testData/intentions/joinParameterList/oneParam.kt new file mode 100644 index 00000000000..4d36416cbae --- /dev/null +++ b/idea/testData/intentions/joinParameterList/oneParam.kt @@ -0,0 +1,3 @@ +fun test( + a: Int +) {} \ No newline at end of file diff --git a/idea/testData/intentions/joinParameterList/oneParam.kt.after b/idea/testData/intentions/joinParameterList/oneParam.kt.after new file mode 100644 index 00000000000..08511c22773 --- /dev/null +++ b/idea/testData/intentions/joinParameterList/oneParam.kt.after @@ -0,0 +1 @@ +fun test(a: Int) {} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index ce1271dc223..9a2de2e5997 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -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)