diff --git a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/kotlinSpacingRules.kt b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/kotlinSpacingRules.kt index 076e5fac084..772f03d270f 100644 --- a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/kotlinSpacingRules.kt +++ b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/kotlinSpacingRules.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.idea.formatter.KotlinSpacingBuilder.CustomSpacingBui import org.jetbrains.kotlin.idea.util.requireNode import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.children import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral import org.jetbrains.kotlin.psi.psiUtil.siblings import org.jetbrains.kotlin.psi.psiUtil.textRangeWithoutComments @@ -90,6 +91,14 @@ fun createSpacingBuilder(settings: CodeStyleSettings, builderUtil: KotlinSpacing inPosition(right = BLOCK_COMMENT).spacing(commentSpacing(0)) inPosition(right = EOL_COMMENT).spacing(commentSpacing(1)) + inPosition(parent = FUNCTION_LITERAL, right = BLOCK).customRule { _, _, right -> + when (right.node?.children()?.firstOrNull()?.elementType) { + BLOCK_COMMENT -> commentSpacing(0) + EOL_COMMENT -> commentSpacing(1) + else -> null + } + } + inPosition(leftSet = DECLARATIONS, rightSet = DECLARATIONS).customRule(fun( _: ASTBlock, _: ASTBlock, @@ -97,9 +106,12 @@ fun createSpacingBuilder(settings: CodeStyleSettings, builderUtil: KotlinSpacing ): Spacing? { val node = right.node ?: return null val elementStart = node.startOfDeclaration() ?: return null - return if (StringUtil.containsLineBreak(node.text.subSequence(0, elementStart.startOffset - node.startOffset).trimStart())) { + return if (StringUtil.containsLineBreak( + node.text.subSequence(0, elementStart.startOffset - node.startOffset).trimStart() + ) + ) createSpacing(0, minLineFeeds = 2) - } else + else null }) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/CommentSaver.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/CommentSaver.kt index 91c261cf259..7695c12a1f3 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/CommentSaver.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/CommentSaver.kt @@ -22,6 +22,7 @@ import com.intellij.psi.* import com.intellij.psi.codeStyle.CodeStyleManager import org.jetbrains.kotlin.lexer.KtToken import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtFunctionLiteral import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.psiUtil.* import java.util.* @@ -347,6 +348,11 @@ class CommentSaver(originalElements: PsiChildRange, private val saveLineBreaks: if (commentTreeElement.spaceAfter.isNotEmpty()) { parent.addBefore(psiFactory.createWhiteSpace(commentTreeElement.spaceAfter), anchorElement) } + + val functionLiteral = restored.parent as? KtFunctionLiteral + if (functionLiteral != null && commentTreeElement.spaceBefore.isNotEmpty()) { + functionLiteral.addBefore(psiFactory.createWhiteSpace(commentTreeElement.spaceBefore), restored) + } } } else { restored = putAbandonedCommentsAfter.parent.addBefore(comment, putAbandonedCommentsAfter) as PsiComment diff --git a/idea/testData/formatter/CommentInFunctionLiteral.after.kt b/idea/testData/formatter/CommentInFunctionLiteral.after.kt index 796410a85a9..a86b5cd16a2 100644 --- a/idea/testData/formatter/CommentInFunctionLiteral.after.kt +++ b/idea/testData/formatter/CommentInFunctionLiteral.after.kt @@ -6,4 +6,67 @@ fun foo() { // Some comment it } -} \ No newline at end of file + + test() { +// val a = 42 + } + + test() { +/* + val a = 42 +*/ + } + + test() { + /* + val a = 42 + */ + } + + test() { +// val a = 42 + val b = 44 + } +} + +val s = Shadow { -> // wdwd + val a = 42 +} + +val s2 = Shadow { // wdwd + val a = 42 +} + +val s3 = Shadow(fun() { // wdwd + val a = 42 +}) + +val s4 = Shadow(fun() { /* s */ + val a = 42 +}) + +val s5 = Shadow { -> + // wdwd + val a = 42 +} + +val s6 = Shadow { + // wdwd + val a = 42 +} + +val s7 = Shadow(fun() { + // wdwd + val a = 42 +}) + +val s8 = Shadow(fun() { + // wdwd + val a = 42 +}) + +val s9 = Shadow { -> /* s */ + val a = 42 +} + +class Shadow(callback: () -> Unit) diff --git a/idea/testData/formatter/CommentInFunctionLiteral.kt b/idea/testData/formatter/CommentInFunctionLiteral.kt index 0e29a6295a7..f2147592b48 100644 --- a/idea/testData/formatter/CommentInFunctionLiteral.kt +++ b/idea/testData/formatter/CommentInFunctionLiteral.kt @@ -6,4 +6,67 @@ fun foo() { // Some comment it } -} \ No newline at end of file + + test() { +// val a = 42 + } + + test() { +/* + val a = 42 +*/ + } + + test() { + /* + val a = 42 + */ + } + + test() { +// val a = 42 + val b = 44 + } +} + +val s = Shadow { -> // wdwd + val a = 42 +} + +val s2 = Shadow { // wdwd + val a = 42 +} + +val s3 = Shadow(fun() { // wdwd + val a = 42 +}) + +val s4 = Shadow(fun() { /* s */ + val a = 42 +}) + +val s5 = Shadow { -> + // wdwd + val a = 42 +} + +val s6 = Shadow { + // wdwd + val a = 42 +} + +val s7 = Shadow(fun() { + // wdwd + val a = 42 +}) + +val s8 = Shadow(fun() { + // wdwd + val a = 42 +}) + +val s9 = Shadow { -> /* s */ + val a = 42 +} + +class Shadow(callback: () -> Unit) diff --git a/idea/testData/intentions/loopToCallChain/firstOrNull/ifAssign_preserveComments.kt.after b/idea/testData/intentions/loopToCallChain/firstOrNull/ifAssign_preserveComments.kt.after index a5a9d334e7b..ad07469a7f8 100644 --- a/idea/testData/intentions/loopToCallChain/firstOrNull/ifAssign_preserveComments.kt.after +++ b/idea/testData/intentions/loopToCallChain/firstOrNull/ifAssign_preserveComments.kt.after @@ -4,8 +4,7 @@ fun foo(list: List) { // string should be non-empty // save it into result - val result: String? = list.firstOrNull { - // search for first non-empty string in the list + val result: String? = list.firstOrNull { // search for first non-empty string in the list it.length > 0 } } \ No newline at end of file diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/FormattingCollector.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/FormattingCollector.kt index 3b03ce8a5de..9e924756f64 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/FormattingCollector.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/FormattingCollector.kt @@ -5,14 +5,13 @@ package org.jetbrains.kotlin.nj2k -import com.intellij.psi.PsiComment -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiKeyword -import com.intellij.psi.PsiWhiteSpace +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.* import com.intellij.psi.javadoc.PsiDocComment import org.jetbrains.kotlin.idea.j2k.IdeaDocCommentConverter import org.jetbrains.kotlin.nj2k.tree.JKComment import org.jetbrains.kotlin.nj2k.tree.JKFormattingOwner +import org.jetbrains.kotlin.utils.addToStdlib.safeAs class FormattingCollector { private val commentCache = mutableMapOf() @@ -48,13 +47,21 @@ class FormattingCollector { this ) ) - is PsiComment -> JKComment(text) + is PsiComment -> JKComment(text, indent()) else -> null } ?: return null commentCache[this] = token return token } + private fun PsiComment.indent(): String? = takeIf { parent is PsiCodeBlock }?.prevSibling?.safeAs()?.let { space -> + val text = space.text + if (space.prevSibling is PsiStatement) + text.indexOfFirst(StringUtil::isLineBreak).takeIf { it != -1 }?.let { text.substring(it + 1) } ?: text + else + text + } + private fun Sequence.toComments(): List = takeWhile { it is PsiComment || it is PsiWhiteSpace || it.text == ";" } .mapNotNull { it.asComment() } diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCommentPrinter.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCommentPrinter.kt index fa034e9a399..44ad47a702a 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCommentPrinter.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCommentPrinter.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.nj2k.printing +import com.intellij.openapi.util.text.StringUtil import org.jetbrains.kotlin.nj2k.tree.JKComment import org.jetbrains.kotlin.nj2k.tree.JKDeclaration import org.jetbrains.kotlin.nj2k.tree.JKFormattingOwner @@ -32,7 +33,8 @@ internal class JKCommentPrinter(val printer: JKPrinter) { for (comment in this@createText) { if (comment.shouldBeDropped()) continue val text = comment.createText() ?: continue - if (needNewLine) appendln() else append(' ') + if (needNewLine && comment.indent?.let { StringUtil.containsLineBreak(it) } != true) appendln() + append(comment.indent ?: ' ') append(text) needNewLine = text.startsWith("//") || '\n' in text } diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/formatting.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/formatting.kt index 41a17c1f8e9..84ac092a9f0 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/formatting.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/formatting.kt @@ -8,7 +8,7 @@ package org.jetbrains.kotlin.nj2k.tree import org.jetbrains.kotlin.utils.SmartList -class JKComment(val text: String) { +class JKComment(val text: String, val indent: String? = null) { val isSingleline get() = text.startsWith("//") } diff --git a/nj2k/testData/newJ2k/annotations/jetbrainsNotNull.kt b/nj2k/testData/newJ2k/annotations/jetbrainsNotNull.kt index 3706bff22e5..9b3059351f1 100644 --- a/nj2k/testData/newJ2k/annotations/jetbrainsNotNull.kt +++ b/nj2k/testData/newJ2k/annotations/jetbrainsNotNull.kt @@ -4,7 +4,8 @@ package test class Test(str: String) { var myStr = "String2" - fun sout(str: String) { // UNNECESSARY_NOT_NULL_ASSERTION heuristic does not work any more, instead we can skip generating !! altogether + fun sout(str: String) { + // UNNECESSARY_NOT_NULL_ASSERTION heuristic does not work any more, instead we can skip generating !! altogether println(str) } diff --git a/nj2k/testData/newJ2k/comments/commentInsideCall.kt b/nj2k/testData/newJ2k/comments/commentInsideCall.kt index 80e95aacf06..aff82619757 100644 --- a/nj2k/testData/newJ2k/comments/commentInsideCall.kt +++ b/nj2k/testData/newJ2k/comments/commentInsideCall.kt @@ -5,7 +5,7 @@ object Test { fun main(args: Array) { println() // Comment foo() // Comment1 -// Comment2 + // Comment2 .indexOf("s") } diff --git a/nj2k/testData/newJ2k/comments/comments.kt b/nj2k/testData/newJ2k/comments/comments.kt index b011acace83..aad262277a3 100644 --- a/nj2k/testData/newJ2k/comments/comments.kt +++ b/nj2k/testData/newJ2k/comments/comments.kt @@ -6,16 +6,19 @@ package foo // we need ArrayList // let's declare a class: internal class A /* just a sample name*/ : Runnable /* let's implement Runnable */ { - fun foo /* again a sample name */(p: Int /* parameter p */, c: Char /* parameter c */) { // let's print something: + fun foo /* again a sample name */(p: Int /* parameter p */, c: Char /* parameter c */) { + // let's print something: println("1") // print 1 println("2") // print 2 println("3") // print 3 + // end of printing if (p > 0) { // do this only when p > 0 -// we print 4 and return + // we print 4 and return println("3") return // do not continue } + // some code to be added } } // end of class A diff --git a/nj2k/testData/newJ2k/comments/comments2.kt b/nj2k/testData/newJ2k/comments/comments2.kt index f77b6789d11..b4326c07c2f 100644 --- a/nj2k/testData/newJ2k/comments/comments2.kt +++ b/nj2k/testData/newJ2k/comments/comments2.kt @@ -3,8 +3,9 @@ package foo internal class A { - fun /* nothing to return */foo( /* no parameters at all */) { // let declare a variable -// with 2 comments before + fun /* nothing to return */foo( /* no parameters at all */) { + // let declare a variable + // with 2 comments before val /*int*/a /* it's a */ = 2 /* it's 2 */ + 1 /* it's 1 */ // variable a declared } // end of foo diff --git a/nj2k/testData/newJ2k/comments/comments3.java b/nj2k/testData/newJ2k/comments/comments3.java new file mode 100644 index 00000000000..06d37b39679 --- /dev/null +++ b/nj2k/testData/newJ2k/comments/comments3.java @@ -0,0 +1,21 @@ +public class Foo { + public void foo() { + System.out.println(); + // comment + // comment 2 + // comment 3 + } + + public void setState(Integer state) { + /*some comment 1/*/ + /*some comment 2/*/ + /*some comment 3/*/ + /*some comment 4/*/ + } + + public void setState2(Integer state) { /*some comment 1/*/ + /*some comment 2/*/ + /*some comment 3/*/ + /*some comment 4/*/ + } +} \ No newline at end of file diff --git a/nj2k/testData/newJ2k/comments/comments3.kt b/nj2k/testData/newJ2k/comments/comments3.kt new file mode 100644 index 00000000000..8c91cee8b97 --- /dev/null +++ b/nj2k/testData/newJ2k/comments/comments3.kt @@ -0,0 +1,21 @@ +class Foo { + fun foo() { + println() + // comment + // comment 2 + // comment 3 + } + + fun setState(state: Int?) { + /*some comment 1/ */ + /*some comment 2/ */ + /*some comment 3/ */ + /*some comment 4/ */ + } + + fun setState2(state: Int?) { /*some comment 1/ */ + /*some comment 2/ */ + /*some comment 3/ */ + /*some comment 4/ */ + } +} \ No newline at end of file diff --git a/nj2k/testData/newJ2k/comments/fieldWithEndOfLineComment2.java b/nj2k/testData/newJ2k/comments/fieldWithEndOfLineComment2.java new file mode 100644 index 00000000000..dcf38f88243 --- /dev/null +++ b/nj2k/testData/newJ2k/comments/fieldWithEndOfLineComment2.java @@ -0,0 +1,13 @@ +public class Foo { + private Integer someInt; + public void setState(Integer state) { + //some comment 1 + someInt = state; + //some comment 2 + if (state == 2) + System.out.println("2"); + } + public Integer getState() { + return someInt; + } +} \ No newline at end of file diff --git a/nj2k/testData/newJ2k/comments/fieldWithEndOfLineComment2.kt b/nj2k/testData/newJ2k/comments/fieldWithEndOfLineComment2.kt new file mode 100644 index 00000000000..586d345b6cc --- /dev/null +++ b/nj2k/testData/newJ2k/comments/fieldWithEndOfLineComment2.kt @@ -0,0 +1,12 @@ +class Foo { + var state: Int? = null + private set + + fun setState(state: Int) { + //some comment 1 + this.state = state + //some comment 2 + if (state == 2) println("2") + } + +} \ No newline at end of file diff --git a/nj2k/testData/newJ2k/formatting/nonStaticMembersWithComments.kt b/nj2k/testData/newJ2k/formatting/nonStaticMembersWithComments.kt index 36e1376442b..21905d6e6f7 100644 --- a/nj2k/testData/newJ2k/formatting/nonStaticMembersWithComments.kt +++ b/nj2k/testData/newJ2k/formatting/nonStaticMembersWithComments.kt @@ -1,10 +1,10 @@ internal class F { //c1 -/*c2*/ + /*c2*/ fun f1() {} //c3 -//c4 + //c4 fun f2() {} var i = 0 fun f3() {} //c5 diff --git a/nj2k/testData/newJ2k/formatting/staticAndNonStaticMembersWithComments.kt b/nj2k/testData/newJ2k/formatting/staticAndNonStaticMembersWithComments.kt index ac57dafa1ac..1d9b1197a73 100644 --- a/nj2k/testData/newJ2k/formatting/staticAndNonStaticMembersWithComments.kt +++ b/nj2k/testData/newJ2k/formatting/staticAndNonStaticMembersWithComments.kt @@ -1,13 +1,13 @@ internal class F { //c3 -//c4 + //c4 fun f2() {} fun f3() {} fun f4() {} companion object { //c1 -/*c2*/ + /*c2*/ fun f1() {} var i = 0 diff --git a/nj2k/testData/newJ2k/formatting/staticMembersWithComments.kt b/nj2k/testData/newJ2k/formatting/staticMembersWithComments.kt index c3d8e836c06..6acc7c08d24 100644 --- a/nj2k/testData/newJ2k/formatting/staticMembersWithComments.kt +++ b/nj2k/testData/newJ2k/formatting/staticMembersWithComments.kt @@ -1,10 +1,10 @@ internal object F { //c1 -/*c2*/ + /*c2*/ fun f1() {} //c3 -//c4 + //c4 fun f2() {} var i = 0 fun f3() {} //c5 diff --git a/nj2k/testData/newJ2k/function/externalKtFunctionalInterface.kt b/nj2k/testData/newJ2k/function/externalKtFunctionalInterface.kt index 6449c66c4c0..7c604c852dd 100644 --- a/nj2k/testData/newJ2k/function/externalKtFunctionalInterface.kt +++ b/nj2k/testData/newJ2k/function/externalKtFunctionalInterface.kt @@ -8,7 +8,8 @@ internal class Test { return x.toDouble() } - fun nya(): Double { //TODO explicitlly call apply here + fun nya(): Double { + //TODO explicitlly call apply here return foo(1, FunctionalI { x: Int -> this.toDouble(x) }) } } \ No newline at end of file diff --git a/nj2k/testData/newJ2k/issues/comments.kt b/nj2k/testData/newJ2k/issues/comments.kt index 264edf6427b..2a6a301e317 100644 --- a/nj2k/testData/newJ2k/issues/comments.kt +++ b/nj2k/testData/newJ2k/issues/comments.kt @@ -15,7 +15,8 @@ internal class C { /** * This is a function doc comment. */ - fun foo() { /* This is a function comment */ + fun foo() { + /* This is a function comment */ } //simple one line comment for function @@ -25,29 +26,29 @@ internal class C { var j = 0 //double c style -//comment before function + //comment before function fun f2() {} //double c style -//comment before field + //comment before field var k = 0 //combination /** of */ -// + // /** * different */ -//comments + //comments fun f3() {} //combination /** of */ -// + // /** * different */ -//comments + //comments var l = 0 /*two*/ /*comments*/ /*line*/ diff --git a/nj2k/testData/newJ2k/issues/kt-19348.kt b/nj2k/testData/newJ2k/issues/kt-19348.kt index a8f08e9a6df..3d4626566ab 100644 --- a/nj2k/testData/newJ2k/issues/kt-19348.kt +++ b/nj2k/testData/newJ2k/issues/kt-19348.kt @@ -14,6 +14,6 @@ class TestMutltipleCtorsWithJavadoc this.y = y } // --- -// Constructors -// + // Constructors + // } \ No newline at end of file diff --git a/nj2k/testData/newJ2k/tryWithResource/WithFinally.kt b/nj2k/testData/newJ2k/tryWithResource/WithFinally.kt index 75391e44ab9..7663476f980 100644 --- a/nj2k/testData/newJ2k/tryWithResource/WithFinally.kt +++ b/nj2k/testData/newJ2k/tryWithResource/WithFinally.kt @@ -10,7 +10,8 @@ class C { val c = stream.read() println(c) } - } finally { // dispose something else + } finally { + // dispose something else } } } \ No newline at end of file diff --git a/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java b/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java index 6595539b151..d9cdf027cab 100644 --- a/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java +++ b/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java @@ -1030,6 +1030,11 @@ public class NewJavaToKotlinConverterSingleFileTestGenerated extends AbstractNew runTest("nj2k/testData/newJ2k/comments/comments2.java"); } + @TestMetadata("comments3.java") + public void testComments3() throws Exception { + runTest("nj2k/testData/newJ2k/comments/comments3.java"); + } + @TestMetadata("commentsForConstructors.java") public void testCommentsForConstructors() throws Exception { runTest("nj2k/testData/newJ2k/comments/commentsForConstructors.java"); @@ -1040,6 +1045,11 @@ public class NewJavaToKotlinConverterSingleFileTestGenerated extends AbstractNew runTest("nj2k/testData/newJ2k/comments/fieldWithEndOfLineComment.java"); } + @TestMetadata("fieldWithEndOfLineComment2.java") + public void testFieldWithEndOfLineComment2() throws Exception { + runTest("nj2k/testData/newJ2k/comments/fieldWithEndOfLineComment2.java"); + } + @TestMetadata("fieldsInitializedFromParams.java") public void testFieldsInitializedFromParams() throws Exception { runTest("nj2k/testData/newJ2k/comments/fieldsInitializedFromParams.java");