Repository: intellij-rust/intellij-rust
Issue: Move vec macro arguments not working when missing a comma

## Environment

* **IntelliJ Rust plugin version:** 0.4.152.3985-211-nightly
* **Rust toolchain version:** null
* **IDE name and version:** CLion 2021.1.3 (CL-211.7628.27)
* **Operating system:** Windows 10 10.0
* **Macro expansion engine:** new
* **Name resolution engine:** new

## Problem description

Moving `vec!` macro arguments down and then up when some arguments are in a single line and missing a comma, fails.

## Steps to reproduce
### Fails
```rust
fn main() {
   let v = vec![
      "a",
      "b",/*caret. Move statement down*/
      "c", "d"
   ];
}
```
```rust
fn main() {
   let v = vec![
      "a",
      "c", "d"
      "b",/*caret. Try to move statement up. Doesn't work.*/
   ];
}
```
### Works
A
```rust
fn main() {
   let v = vec![
      "a",
      "b",/*caret. Move statement down*/
      "c", "d",
   ];
}
```
```rust
fn main() {
   let v = vec![
      "a",
      "c", "d",
      "b",/*caret. Try to move statement up. Works.*/
   ];
}
```
B
```rust
fn main() {
   let v = vec![
      "a",
      "b",
      "c", /*caret. Move statement down*/
      "d"
   ];
}
```
```rust
fn main() {
   let v = vec![
      "a",
      "b",
      "d",
      "c",/*caret. Try to move statement up. Works.*/
   ];
}
```

Changed files:
src/main/kotlin/org/rust/ide/formatter/processors/RsTrailingCommaFormatProcessor.kt
src/test/kotlin/org/rust/ide/actions/mover/RsCommaListElementUpDownMoverTest.kt
src/main/kotlin/org/rust/ide/actions/mover/RsCommaListElementUpDownMover.kt

diff --git a/src/main/kotlin/org/rust/ide/actions/mover/RsCommaListElementUpDownMover.kt b/src/main/kotlin/org/rust/ide/actions/mover/RsCommaListElementUpDownMover.kt
index 34bca1cd2..763ad407f 100644
--- a/src/main/kotlin/org/rust/ide/actions/mover/RsCommaListElementUpDownMover.kt
+++ b/src/main/kotlin/org/rust/ide/actions/mover/RsCommaListElementUpDownMover.kt
@@ -12,7 +12,7 @@ import com.intellij.psi.PsiElement
 import org.rust.ide.actions.mover.RsLineMover.Companion.RangeEndpoint
 import org.rust.ide.formatter.impl.CommaList
 import org.rust.ide.formatter.processors.addTrailingCommaForElement
-import org.rust.ide.formatter.processors.isLastElement
+import org.rust.ide.formatter.processors.isOnSameLineAsLastElement
 import org.rust.lang.core.psi.RsBlockExpr
 import org.rust.lang.core.psi.RsElementTypes.COMMA
 import org.rust.lang.core.psi.RsMatchArm
@@ -67,7 +67,7 @@ class RsCommaListElementUpDownMover : RsLineMover() {
         for (element in listOfNotNull(info.toMove.firstElement, info.toMove.lastElement, info.toMove2.firstElement)) {
             val list = element.parent
             val commaList = CommaList.forElement(list.elementType) ?: continue
-            if (commaList.isLastElement(list, element)) {
+            if (commaList.isOnSameLineAsLastElement(list, element)) {
                 commaList.addTrailingCommaForElement(list)
             }
         }
diff --git a/src/main/kotlin/org/rust/ide/formatter/processors/RsTrailingCommaFormatProcessor.kt b/src/main/kotlin/org/rust/ide/formatter/processors/RsTrailingCommaFormatProcessor.kt
index ac8c07cae..294b8f5f2 100644
--- a/src/main/kotlin/org/rust/ide/formatter/processors/RsTrailingCommaFormatProcessor.kt
+++ b/src/main/kotlin/org/rust/ide/formatter/processors/RsTrailingCommaFormatProcessor.kt
@@ -18,9 +18,8 @@ import org.rust.lang.core.psi.RsBlockFields
 import org.rust.lang.core.psi.RsElementTypes.COMMA
 import org.rust.lang.core.psi.RsPsiFactory
 import org.rust.lang.core.psi.RsStructItem
-import org.rust.lang.core.psi.ext.elementType
-import org.rust.lang.core.psi.ext.getNextNonCommentSibling
-import org.rust.lang.core.psi.ext.getPrevNonCommentSibling
+import org.rust.lang.core.psi.ext.*
+import org.rust.openapiext.document
 
 object RsTrailingCommaFormatProcessor {
     fun processElement(source: PsiElement, settings: CodeStyleSettings): PsiElement {
@@ -97,10 +96,13 @@ fun CommaList.addTrailingCommaForElement(list: PsiElement): Boolean {
     return true
 }
 
-fun CommaList.isLastElement(list: PsiElement, element: PsiElement): Boolean {
+fun CommaList.isOnSameLineAsLastElement(list: PsiElement, element: PsiElement): Boolean {
     check(list.elementType == this.list && isElement(element))
     val rbrace = list.lastChild
     if (rbrace.elementType != closingBrace) return false
-    val lastElement = rbrace.getPrevNonCommentSibling() ?: return false
-    return lastElement == element
+    val lastElement = rbrace.getPrevNonCommentSibling()?.takeIf(isElement) ?: return false
+    return element == lastElement || element.containingFile.document?.let {
+        it.getLineNumber(element.endOffset) == it.getLineNumber(lastElement.endOffset)
+    } == true
+
 }
diff --git a/src/test/kotlin/org/rust/ide/actions/mover/RsCommaListElementUpDownMoverTest.kt b/src/test/kotlin/org/rust/ide/actions/mover/RsCommaListElementUpDownMoverTest.kt
index 08c18d7ee..46fd6c06a 100644
--- a/src/test/kotlin/org/rust/ide/actions/mover/RsCommaListElementUpDownMoverTest.kt
+++ b/src/test/kotlin/org/rust/ide/actions/mover/RsCommaListElementUpDownMoverTest.kt
@@ -42,6 +42,31 @@ class RsCommaListElementUpDownMoverTest : RsStatementUpDownMoverTestBase() {
         ) { }
     """)
 
+
+    fun `test function parameter adds comma 3`() = moveUp("""
+        fn foo(
+            x: i32,
+            /*caret*/y: i32, z: i32
+        ) { }
+    """, """
+        fn foo(
+            /*caret*/y: i32, z: i32,
+            x: i32,
+        ) { }
+    """)
+
+    fun `test function parameter adds comma 4`() = moveDown("""
+        fn foo(
+            /*caret*/ x: i32,
+            y: i32, z: i32
+        ) { }
+    """, """
+        fn foo(
+            y: i32, z: i32,
+            /*caret*/x: i32,
+        ) { }
+    """)
+
     fun `test self parameter`() = moveDownAndBackUp("""
         trait T {
             fn foo(
@@ -112,6 +137,38 @@ class RsCommaListElementUpDownMoverTest : RsStatementUpDownMoverTestBase() {
         }
     """)
 
+    fun `test function argument adds comma 3`() = moveUp("""
+        fn main() {
+            foo(
+                x,
+                /*caret*/y, z
+            );
+        }
+    """, """
+        fn main() {
+            foo(
+                /*caret*/y, z,
+                x,
+            );
+        }
+    """)
+
+    fun `test function argument adds comma 4`() = moveDown("""
+        fn main() {
+            foo(
+                /*caret*/x,
+                y, z
+            );
+        }
+    """, """
+        fn main() {
+            foo(
+                y, z,
+                /*caret*/x,
+            );
+        }
+    """)
+
     fun `test prevent step out of argument list`() = moveDownAndBackUp("""
         fn main() {
             foo(
@@ -162,6 +219,30 @@ class RsCommaListElementUpDownMoverTest : RsStatementUpDownMoverTestBase() {
         }
     """)
 
+    fun `test move struct adds comma 3`() = moveDown("""
+        struct S {
+            foo: u32,/*caret*/
+            bar: u32, baz: u32
+        }
+    """, """
+        struct S {
+            bar: u32, baz: u32,
+            foo: u32,/*caret*/
+        }
+    """)
+
+    fun `test move struct adds comma 4`() = moveUp("""
+        struct S {
+            foo: u32,
+            /*caret*/bar: u32, baz: u32
+        }
+    """, """
+        struct S {
+            /*caret*/bar: u32, baz: u32,
+            foo: u32,
+        }
+    """)
+
     fun `test move vec argument`() = moveDownAndBackUp("""
         fn foo() {
             bar(
@@ -215,4 +296,98 @@ class RsCommaListElementUpDownMoverTest : RsStatementUpDownMoverTestBase() {
             );
         }
     """, testmark = UpDownMoverTestMarks.moveOutOfBlock)
+
+    fun `test move vec adds comma 1`() = moveDown("""
+        fn main() {
+            let v = vec![
+                "a",
+                /*caret*/"b",
+                "c"
+            ];
+        }
+    """, """
+        fn main() {
+            let v = vec![
+                "a",
+                "c",
+                /*caret*/"b",
+            ];
+        }
+    """)
+
+    fun `test move vec adds comma 2`() = moveUp("""
+        fn main() {
+            let v = vec![
+                "a",
+                "b",
+                /*caret*/"c"
+            ];
+        }
+    """, """
+        fn main() {
+            let v = vec![
+                "a",
+                /*caret*/"c",
+                "b",
+            ];
+        }
+    """)
+
+    fun `test move vec adds comma 3`() = moveDown("""
+        fn main() {
+            let v = vec![
+                "a",
+                /*caret*/"b",
+                "c", "d"
+            ];
+        }
+    """, """
+        fn main() {
+            let v = vec![
+                "a",
+                "c", "d",
+                /*caret*/"b",
+            ];
+        }
+    """)
+
+    fun `test move vec adds comma 4`() = moveUp("""
+        fn main() {
+            let v = vec![
+                "a",
+                "b",
+                /*caret*/"c", "d"
+            ];
+        }
+    """, """
+        fn main() {
+            let v = vec![
+                "a",
+                /*caret*/"c", "d",
+                "b",
+            ];
+        }
+    """)
+
+    fun `test move vec adds comma 5`() = moveDown("""
+        fn main() {
+            let v = vec![
+                1,
+                2,/*caret*/
+                {
+                    3
+                }, { 4 }
+            ];
+        }
+    """, """
+        fn main() {
+            let v = vec![
+                1,
+                {
+                    3
+                }, { 4 },
+                2,/*caret*/
+            ];
+        }
+    """)
 }