Repository: analysis-dev/diktat (kotlin)

Issue:
Move inline comments in long lines before the calling code
Consider following line
```kotlin
val accessors = node.findAllDescendantsWithSpecificType(PROPERTY_ACCESSOR).filter { it.hasChildOfType(BLOCK) }  // exclude get with expression body
```
In short lines usage of inline comments quite often is convenient, so we don't need to move such comments at any time.
However, in long lines it's reduces the readability. 
Also in this rule we can use other definition of long lines, and move
comments not when line > 180 symbols, but earlier.

```kotlin
// exclude get with expression body
val accessors = node.findAllDescendantsWithSpecificType(PROPERTY_ACCESSOR).filter { it.hasChildOfType(BLOCK) }
```

Pull request:
### What's done:
* [x] Changed logic of the fix long comment
* [x] Updated test
* [x] Closes #923

Patch:
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt
index 0981c113bf..5430a362b5 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt
@@ -41,6 +41,8 @@ import com.pinterest.ktlint.core.ast.ElementType.RPAR
 import com.pinterest.ktlint.core.ast.ElementType.SHORT_STRING_TEMPLATE_ENTRY
 import com.pinterest.ktlint.core.ast.ElementType.STRING_TEMPLATE
 import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
+import com.pinterest.ktlint.core.ast.isWhiteSpace
+import com.pinterest.ktlint.core.ast.isWhiteSpaceWithNewline
 import com.pinterest.ktlint.core.ast.nextSibling
 import com.pinterest.ktlint.core.ast.parent
 import com.pinterest.ktlint.core.ast.prevSibling
@@ -166,13 +168,16 @@ class LineLength(configRules: List<RulesConfig>) : DiktatRule(
 
     private fun checkComment(wrongNode: ASTNode, configuration: LineLengthConfiguration): LongLineFixableCases {
         val leftOffset = positionByOffset(wrongNode.startOffset).second
-        val indexLastSpace = wrongNode.text.substring(0, configuration.lineLength.toInt() - leftOffset).lastIndexOf(' ')
-        // index == -1 indicates, that we didn't find any possible way to split this comment
-        // index == 2 indicates, that we found the white space after `//`, and shouldn't fix it
-        if (indexLastSpace == -1 || indexLastSpace == 2) {
+        val stringBeforeCommentContent = wrongNode.text.takeWhile { it == ' ' || it == '/' }
+        if (stringBeforeCommentContent.length >= configuration.lineLength.toInt() - leftOffset) {
             return LongLineFixableCases.None
         }
-        return LongLineFixableCases.Comment(wrongNode, indexLastSpace)
+        val indexLastSpace = wrongNode.text.substring(stringBeforeCommentContent.length, configuration.lineLength.toInt() - leftOffset).lastIndexOf(' ')
+        val isNewLine = wrongNode.treePrev?.isWhiteSpaceWithNewline() ?: wrongNode.treeParent?.treePrev?.isWhiteSpaceWithNewline() ?: false
+        if (isNewLine && indexLastSpace == -1) {
+            return LongLineFixableCases.None
+        }
+        return LongLineFixableCases.Comment(wrongNode, isNewLine, indexLastSpace + stringBeforeCommentContent.length)
     }
 
     private fun checkCondition(wrongNode: ASTNode, configuration: LineLengthConfiguration): LongLineFixableCases {
@@ -245,13 +250,25 @@ class LineLength(configRules: List<RulesConfig>) : DiktatRule(
 
     private fun fixComment(wrongComment: LongLineFixableCases.Comment) {
         val wrongNode = wrongComment.node
-        val indexLastSpace = wrongComment.indexLastSpace
-        val nodeText = "//${wrongNode.text.substring(indexLastSpace, wrongNode.text.length)}"
-        wrongNode.treeParent.run {
-            addChild(LeafPsiElement(EOL_COMMENT, wrongNode.text.substring(0, indexLastSpace)), wrongNode)
-            addChild(PsiWhiteSpaceImpl("\n"), wrongNode)
-            addChild(LeafPsiElement(EOL_COMMENT, nodeText), wrongNode)
-            removeChild(wrongNode)
+        if (wrongComment.hasNewLineBefore) {
+            val indexLastSpace = wrongComment.indexLastSpace
+            val nodeText = "//${wrongNode.text.substring(indexLastSpace, wrongNode.text.length)}"
+            wrongNode.treeParent.apply {
+                addChild(LeafPsiElement(EOL_COMMENT, wrongNode.text.substring(0, indexLastSpace)), wrongNode)
+                addChild(PsiWhiteSpaceImpl("\n"), wrongNode)
+                addChild(LeafPsiElement(EOL_COMMENT, nodeText), wrongNode)
+                removeChild(wrongNode)
+            }
+        } else {
+            wrongNode.treeParent.treeParent?.let {
+                val parent = wrongNode.treeParent
+                if (wrongNode.treePrev.isWhiteSpace()) {
+                    parent.removeChild(wrongNode.treePrev)
+                }
+                parent.removeChild(wrongNode)
+                it.addChild(wrongNode, parent)
+                it.addChild(PsiWhiteSpaceImpl("\n"), parent)
+            }
         }
     }
 
@@ -429,7 +446,16 @@ class LineLength(configRules: List<RulesConfig>) : DiktatRule(
     sealed class LongLineFixableCases {
         object None : LongLineFixableCases()
 
-        class Comment(val node: ASTNode, val indexLastSpace: Int) : LongLineFixableCases()
+        /**
+         * @property node node
+         * @property hasNewLineBefore flag to handle type of comment: ordinary comment(long part of which should be moved to the next line)
+         * and inline comments (which should be moved entirely to the previous line)
+         * @property indexLastSpace index of last space to substring comment
+         */
+        class Comment(
+            val node: ASTNode,
+            val hasNewLineBefore: Boolean,
+            val indexLastSpace: Int = 0) : LongLineFixableCases()
 
         class StringTemplate(val node: ASTNode, val delimiterIndex: Int) : LongLineFixableCases()
 
diff --git a/info/guide/guide-chapter-3.md b/info/guide/guide-chapter-3.md
index c54f979529..2cb16e9e7f 100644
--- a/info/guide/guide-chapter-3.md
+++ b/info/guide/guide-chapter-3.md
@@ -343,8 +343,93 @@ try {
 <!-- =============================================================================== -->
 ### <a name="c3.5"></a> 3.5 Line length
 
-Line length should be less than 120 symbols. The international code style prohibits `non-Latin` (`non-ASCII`) symbols.
-(See [Identifiers](#r1.1.1)) However, if you still intend on using them, follow the following convention:
+Line length should be less than 120 symbols. Otherwise, it should be split.
+
+If `complex property` initializing is too long, it should be split:
+
+**Invalid example:**
+```kotlin
+val complexProperty = 1 + 2 + 3 + 4
+```
+**Valid example:**
+```kotlin
+val complexProperty = 1 + 2
++ 3 + 4
+```
+
+If `annotation` is too long, it also should be split:
+
+**Invalid example:**
+```kotlin
+@Query(value = "select * from table where age = 10", nativeQuery = true)
+fun foo() {}
+```
+**Valid example:**
+```kotlin
+@Query(value = "select * from table " +
+        "where age = 10", nativeQuery = true)
+fun foo() {}
+```
+
+Long one line `function` should be split:
+
+**Invalid example:**
+```kotlin
+fun foo() = goo().write("TooLong")
+```
+**Valid example:**
+```kotlin
+fun foo() = 
+    goo().write("TooLong")
+```
+
+Long `binary expression` should be split:
+
+**Invalid example:**
+```kotlin
+if (( x >  100) || y < 100 && !isFoo()) {}
+```
+
+**Valid example:**
+```kotlin
+if (( x >  100) ||
+    y < 100 && !isFoo()) {}
+```
+
+`Eol comment` also can be split, but it depends on comment location.
+If this comment is on the same line with code it should be on line before:
+
+**Invalid example:**
+```kotlin
+fun foo() {
+    val name = "Nick" // this comment is too long
+}
+```
+**Valid example:**
+```kotlin
+fun foo() {
+    // this comment is too long
+    val name = "Nick"
+}
+```
+
+But if this comment is on new line - it should be split to several lines:
+
+**Invalid example:**
+```kotlin
+// This comment is too long. It should be on two lines.
+fun foo() {}
+```
+
+**Valid example:**
+```kotlin
+// This comment is too long.
+// It should be on two lines.
+fun foo() {}
+```
+
+The international code style prohibits `non-Latin` (`non-ASCII`) symbols. (See [Identifiers](#r1.1.1)) However, if you still intend on using them, follow
+the following convention:
 
 - One wide character occupies the width of two narrow characters.
 The "wide" and "narrow" parts of a character are defined by its [east Asian width Unicode attribute](https://unicode.org/reports/tr11/).


Test patch:
diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentExpected.kt
index bd48fd85a6..4afc22439d 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentExpected.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentExpected.kt
@@ -7,10 +7,16 @@ fun foo() {
     namesList.forEach { name ->
         if (name == "Nick") {
             namesList.map {
-                it.subSequence(0, 1) // This is
-// another comment inside map
+                // This is another comment inside map
+it.subSequence(0, 1)
                 it.split("this is long regex") // this comment start to the right of max length
             }
         }
     }
 }
+
+fun goo() {
+    val ok = true // short comment
+    // h k comment looong comment
+val someLongFieldName = "some string"
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentTest.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentTest.kt
index 8760b8720d..7988ca4369 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentTest.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentTest.kt
@@ -12,3 +12,8 @@ fun foo() {
         }
     }
 }
+
+fun goo() {
+    val ok = true // short comment
+    val someLongFieldName = "some string" // h k comment looong comment
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongStringTemplateExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongStringTemplateExpected.kt
index b15925bf85..68b5433d79 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongStringTemplateExpected.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongStringTemplateExpected.kt
@@ -1,5 +1,6 @@
 object Observables {
-    val someCode = 15 // looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongggggggggggggggggggggggggg
+    // looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongggggggggggggggggggggggggg
+val someCode = 15
     // Some
 // looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line
     @Deprecated("New type inference algorithm in Kotlin 1.4 makes this method obsolete. Method will be removed in" +