Repository: analysis-dev/diktat (kotlin)

Issue:
Not able to use simple enum formatting
## Describe the bug
```kotlin
package org.cqfn.realth.domain

enum class IssueType {
    VCS, PROJECT_STRUCTURE, TESTS
}
```
should be allowed, but is fixed into
```kotlin
package org.cqfn.diktat.org.cqfn.realth.domain

enum class IssueType {
    PROJECT,
    TESTS,
    VCS
}
```
and emits unfixed warnings for `ENUMS_SEPARATED`.

## Environment information
* diktat version: 0.3.0

Pull request description:
### What's done:
   Rewrote algorithm to swap element
  closes #721

Patch:
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/SortRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/SortRule.kt
index 8f9179558f..935e7805f7 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/SortRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/SortRule.kt
@@ -5,23 +5,28 @@ import org.cqfn.diktat.common.config.rules.RulesConfig
 import org.cqfn.diktat.common.config.rules.getRuleConfig
 import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_DECLARATIONS_ORDER
 import org.cqfn.diktat.ruleset.rules.DiktatRule
+import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
 import org.cqfn.diktat.ruleset.utils.hasChildOfType
 import org.cqfn.diktat.ruleset.utils.isClassEnum
 
-import com.pinterest.ktlint.core.ast.ElementType
 import com.pinterest.ktlint.core.ast.ElementType.CLASS_BODY
 import com.pinterest.ktlint.core.ast.ElementType.COMMA
 import com.pinterest.ktlint.core.ast.ElementType.CONST_KEYWORD
+import com.pinterest.ktlint.core.ast.ElementType.ENUM_ENTRY
+import com.pinterest.ktlint.core.ast.ElementType.EOL_COMMENT
 import com.pinterest.ktlint.core.ast.ElementType.IDENTIFIER
 import com.pinterest.ktlint.core.ast.ElementType.MODIFIER_LIST
 import com.pinterest.ktlint.core.ast.ElementType.PROPERTY
 import com.pinterest.ktlint.core.ast.ElementType.SEMICOLON
+import com.pinterest.ktlint.core.ast.isPartOfComment
+import com.pinterest.ktlint.core.ast.isWhiteSpace
 import com.pinterest.ktlint.core.ast.isWhiteSpaceWithNewline
 import com.pinterest.ktlint.core.ast.nextSibling
 import org.jetbrains.kotlin.com.intellij.lang.ASTNode
 import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
 import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl
 import org.jetbrains.kotlin.psi.KtObjectDeclaration
+import org.jetbrains.kotlin.psi.psiUtil.siblings
 
 /**
  * Rule that sorts class properties and enum members alphabetically
@@ -67,17 +72,41 @@ class SortRule(configRules: List<RulesConfig>) : DiktatRule("sort-rule", configR
         }
     }
 
+    @OptIn(ExperimentalStdlibApi::class)
     private fun swapSortNodes(
         sortList: List<ASTNode>,
         nonSortList: List<ASTNode>,
         node: ASTNode) {
-        val nodeBefore: ASTNode? = nonSortList.last().treeNext
+        val isEnum = nonSortList.first().elementType == ENUM_ENTRY
+        val spaceBefore = if (node.findAllNodesWithSpecificType(EOL_COMMENT).isNotEmpty() && isEnum) {
+            nonSortList.last().run {
+                if (this.hasChildOfType(EOL_COMMENT) && !this.hasChildOfType(COMMA)) {
+                    this.addChild(LeafPsiElement(COMMA, ","), this.findChildByType(EOL_COMMENT))
+                }
+            }
+            buildList(nonSortList.size) {
+                add(null)
+                repeat(nonSortList.size - 1) {
+                    add(listOf(PsiWhiteSpaceImpl("\n")))
+                }
+            }
+        } else {
+            nonSortList.map { astNode ->
+                astNode
+                    .siblings(false)
+                    .toList()
+                    .takeWhile { it.isWhiteSpace() || it.isPartOfComment() }
+                    .ifEmpty { null }
+            }
+        }
+        val nodeInsertBefore: ASTNode? = nonSortList.last().treeNext
         node.removeRange(nonSortList.first(), nonSortList.last().treeNext)
-        sortList.forEachIndexed { nodeIndex, astNode ->
-            if (nodeIndex != 0) {
-                node.addChild(PsiWhiteSpaceImpl("\n"), nodeBefore)
+        sortList.mapIndexed { index, astNode ->
+            spaceBefore[index]?.let { prevList -> prevList.map { node.addChild(it, nodeInsertBefore) } }
+            if (!astNode.hasChildOfType(COMMA) && isEnum) {
+                astNode.addChild(LeafPsiElement(COMMA, ","), null)
             }
-            node.addChild(astNode, nodeBefore)
+            node.addChild(astNode, nodeInsertBefore)
         }
     }
 
@@ -99,7 +128,7 @@ class SortRule(configRules: List<RulesConfig>) : DiktatRule("sort-rule", configR
 
     @Suppress("UnsafeCallOnNullableType")
     private fun sortEnum(node: ASTNode) {
-        val enumEntryList = node.getChildren(null).filter { it.elementType == ElementType.ENUM_ENTRY }
+        val enumEntryList = node.getChildren(null).filter { it.elementType == ENUM_ENTRY }
         if (enumEntryList.size <= 1) {
             return
         }
@@ -110,8 +139,8 @@ class SortRule(configRules: List<RulesConfig>) : DiktatRule("sort-rule", configR
                 val hasTrailingComma = (sortList.last() != enumEntryList.last() && enumEntryList.last().hasChildOfType(COMMA))
                 swapSortNodes(sortList, enumEntryList, node)
                 if (!hasTrailingComma) {
-                    enumEntryList.last().addChild(LeafPsiElement(COMMA, ","), null)
-                    sortList.last().removeChild(sortList.last().findChildByType(COMMA)!!)
+                    val lastEntry = node.findAllNodesWithSpecificType(ENUM_ENTRY).last()
+                    lastEntry.removeChild(lastEntry.findChildByType(COMMA)!!)
                 }
                 if (isEndSpace) {
                     sortList.last().addChild(PsiWhiteSpaceImpl("\n"), null)


Test patch:
diff --git a/diktat-rules/src/test/resources/test/paragraph3/sort_error/ConstantsExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/sort_error/ConstantsExpected.kt
index ba8d1eedb5..c10d078c44 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/sort_error/ConstantsExpected.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/sort_error/ConstantsExpected.kt
@@ -3,8 +3,8 @@ package test.paragraph3.sort_error
 class Test {
     companion object {
         private const val B = 4
-private const val C = 4
-private const val D = 4
+        private const val C = 4
+        private const val D = 4
         private val SIMPLE_VALUE = listOf(IDENTIFIER, WHITE_SPACE, COMMA, SEMICOLON)
     }
 }
@@ -28,7 +28,7 @@ class Test2 {
         private const val D = 4
         private val SIMPLE_VALUE = listOf(IDENTIFIER, WHITE_SPACE, COMMA, SEMICOLON)
         private const val Ba = 4
-private const val Baa = 4
-private const val Bb = 4
+        private const val Baa = 4
+        private const val Bb = 4
     }
 }
diff --git a/diktat-rules/src/test/resources/test/paragraph3/sort_error/EnumSortExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/sort_error/EnumSortExpected.kt
index 348610a1a6..e48588d699 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/sort_error/EnumSortExpected.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/sort_error/EnumSortExpected.kt
@@ -2,8 +2,8 @@ package test.paragraph3.sort_error
 
 enum class Alp {
     BLUE(0x0000FF),
-GREEN(0x00FF00),
-RED(0xFF0000),
+    GREEN(0x00FF00),
+    RED(0xFF0000),
 ;
 }
 
@@ -11,7 +11,7 @@ enum class Warnings {
     TALKING {
         override fun signal() = TALKING
     },
-WAITING {
+    WAITING {
         override fun signal() = TALKING
     },
 ;
@@ -23,7 +23,7 @@ enum class Warnings {
     TALKING {
         override fun signal() = TALKING
     },
-WAITING {
+    WAITING {
         override fun signal() = TALKING
     };
 
@@ -32,13 +32,33 @@ WAITING {
 
 enum class Alp {
     BLUE(0x0000FF),
-GREEN(0x00FF00),
-RED(0xFF0000),
+    GREEN(0x00FF00),
+    RED(0xFF0000),
 }
 
 enum class Alp {
     BLUE(0x0000FF),
-GREEN(0x00FF00),
-RED(0xFF0000)
+    GREEN(0x00FF00),
+    RED(0xFF0000)
 ;
 }
+
+enum class IssueType {
+    PROJECT_STRUCTURE, TESTS, VCS
+}
+
+enum class IssueType {
+    PROJECT_STRUCTURE,TESTS,VCS
+}
+
+enum class IssueType {
+    PROJECT_STRUCTURE, // comment
+TESTS,
+VCS
+}
+
+enum class IssueType {
+    PROJECT_STRUCTURE ,// comment
+TESTS,
+VCS
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph3/sort_error/EnumSortTest.kt b/diktat-rules/src/test/resources/test/paragraph3/sort_error/EnumSortTest.kt
index e663ba99f7..1c87f8c520 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/sort_error/EnumSortTest.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/sort_error/EnumSortTest.kt
@@ -40,5 +40,22 @@ enum class Alp {
     RED(0xFF0000),
     GREEN(0x00FF00),
     BLUE(0x0000FF)
-    ;
+;
+}
+
+enum class IssueType {
+    VCS, PROJECT_STRUCTURE, TESTS
+}
+
+enum class IssueType {
+    VCS,PROJECT_STRUCTURE,TESTS
+}
+
+enum class IssueType {
+    VCS,PROJECT_STRUCTURE, // comment
+    TESTS
+}
+
+enum class IssueType {
+    VCS, TESTS, PROJECT_STRUCTURE // comment
 }
diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Expected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Expected.kt
index 943de52b8a..92c20ffb49 100644
--- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Expected.kt
+++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Expected.kt
@@ -35,6 +35,10 @@ class Example {
     }
 }
 
+enum class IssueType {
+    PROJECT_STRUCTURE, TESTS, VCS
+}
+
 class Foo {
     /**
      * @implNote lorem ipsum
diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Test.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Test.kt
index ddd801edf8..ee6d20b539 100644
--- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Test.kt
+++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Test.kt
@@ -41,6 +41,9 @@ class Example {
     }
 }
 
+enum class IssueType {
+    VCS, PROJECT_STRUCTURE, TESTS
+}
 
 class Foo {
     /**