From 085deec71780d9bd7f7809dcee73bf7da093ed30 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Fri, 11 Mar 2016 12:50:53 +0300 Subject: [PATCH] Make J2K pay more attention to original formatting of comments. Better support for KEEP_FIRST_COLUMN_COMMENT #KT-4175 In Progress --- .../org/jetbrains/kotlin/j2k/CodeBuilder.kt | 47 +++++++++++++++---- .../fileOrElement/comments/comments.kt | 3 +- .../fileOrElement/comments/comments2.kt | 3 +- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/j2k/src/org/jetbrains/kotlin/j2k/CodeBuilder.kt b/j2k/src/org/jetbrains/kotlin/j2k/CodeBuilder.kt index 4b152cb8257..2f6e8e59c2c 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/CodeBuilder.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/CodeBuilder.kt @@ -58,25 +58,33 @@ class CodeBuilder(private val topElement: PsiElement?, private var docConverter: private val imports = LinkedHashSet() - infix fun append(text: String): CodeBuilder - = append(text, false) + infix fun append(text: String): CodeBuilder = append(text, NON_COMMENT_INFO) fun addImport(fqName: FqName) { imports.add(fqName) } - private fun appendCommentOrWhiteSpace(element: PsiElement) { + private class CommentInfo(element: PsiElement?, val isPostInsert: Boolean) { + val isComment: Boolean = element is PsiComment + val endOfLineComment: Boolean = element?.isEndOfLineComment() ?: false + val isCommentAtFirstColumn: Boolean = element?.isCommentAtFirstColumn() ?: false + val isFirstNonWhitespaceElementInLine: Boolean = element?.isFirstNonWhitespaceElementInLine() ?: false + } + + private val NON_COMMENT_INFO = CommentInfo(null, false) + + private fun appendCommentOrWhiteSpace(element: PsiElement, postInsert: Boolean = false) { if (element is PsiDocComment) { - append(docConverter.convertDocComment(element), false) + append(docConverter.convertDocComment(element)) } else { - append(element.text!!, element.isEndOfLineComment()) + append(element.text!!, CommentInfo(element, postInsert)) } } - private fun append(text: String, endOfLineComment: Boolean = false): CodeBuilder { + private fun append(text: String, commentInfo: CommentInfo = NON_COMMENT_INFO): CodeBuilder { if (text.isEmpty()) { - assert(!endOfLineComment) + assert(!commentInfo.endOfLineComment) return this } @@ -87,8 +95,21 @@ class CodeBuilder(private val topElement: PsiElement?, private var docConverter: endOfLineCommentAtEnd = false } + if (commentInfo.isComment) { + // Original comment was first in line, but there's no line break before the current one + if (!commentInfo.isPostInsert && commentInfo.isFirstNonWhitespaceElementInLine && + !builder.takeLastWhile { it.isWhitespace() }.contains('\n')) { + builder.append('\n') + } + + // Original comment wasn't at the first column, so we add a space before current one. Deal with KEEP_FIRST_COLUMN_COMMENT setting + if (!commentInfo.isCommentAtFirstColumn && builder.lastOrNull() == '\n') { + builder.append(" ") + } + } + builder.append(text) - endOfLineCommentAtEnd = endOfLineComment + endOfLineCommentAtEnd = commentInfo.endOfLineComment return this } @@ -133,7 +154,7 @@ class CodeBuilder(private val topElement: PsiElement?, private var docConverter: if (prefix.lineBreaksBefore > 0) { val lineBreaksToAdd = prefix.lineBreaksBefore - builder.trailingLineBreakCount() for (it in 1..lineBreaksToAdd) { - append("\n", false) + append("\n") } } @@ -147,7 +168,7 @@ class CodeBuilder(private val topElement: PsiElement?, private var docConverter: prototype.accept(object : JavaRecursiveElementVisitor(){ override fun visitComment(comment: PsiComment) { if (comment !in notInsideElements && commentsAndSpacesUsed.add(comment)) { - appendCommentOrWhiteSpace(comment) + appendCommentOrWhiteSpace(comment, true) } } }) @@ -342,6 +363,12 @@ class CodeBuilder(private val topElement: PsiElement?, private var docConverter: fun PsiElement.isEmptyElement() = firstChild == null && textLength == 0 + fun PsiElement.isCommentAtFirstColumn() = + this is PsiComment && (this.prevSibling?.let { it is PsiWhiteSpace && it.text.endsWith("\\n") } ?: false) + + fun PsiElement.isFirstNonWhitespaceElementInLine() = + this is PsiComment && (this.prevSibling?.let { it is PsiWhiteSpace && it.text.contains('\n') } ?: false) + fun PsiWhiteSpace.lineBreakCount() = StringUtil.getLineBreakCount(text) fun PsiWhiteSpace.hasLineBreaks() = StringUtil.containsLineBreak(text) diff --git a/j2k/testData/fileOrElement/comments/comments.kt b/j2k/testData/fileOrElement/comments/comments.kt index eccc046c894..633f5a3fb8a 100644 --- a/j2k/testData/fileOrElement/comments/comments.kt +++ b/j2k/testData/fileOrElement/comments/comments.kt @@ -17,8 +17,7 @@ internal class A /* just a sample name*/ : Runnable /* let's implement Runnable // end of printing - if (p > 0) { - // do this only when p > 0 + if (p > 0) { // do this only when p > 0 // we print 4 and return println("3") return // do not continue diff --git a/j2k/testData/fileOrElement/comments/comments2.kt b/j2k/testData/fileOrElement/comments/comments2.kt index 2a2c576497f..7caff5eaaaa 100644 --- a/j2k/testData/fileOrElement/comments/comments2.kt +++ b/j2k/testData/fileOrElement/comments/comments2.kt @@ -9,8 +9,7 @@ internal class A { val /*int*/ a /* it's a */ = 2 /* it's 2 */ + 1 /* it's 1 */ // variable a declared } // end of foo - fun /* we return int*/ foo(/*int*/ p: Int/* parameter p */): Int { - /* body is empty */ + fun /* we return int*/ foo(/*int*/ p: Int/* parameter p */): Int { /* body is empty */ } private /*it's private*/ val field = 0