From cac36627aabbcdff99d0df18cc85890e7fa6672f Mon Sep 17 00:00:00 2001
From: Matt Ellis <m.t.ellis@gmail.com>
Date: Fri, 12 Apr 2024 18:54:35 -0700
Subject: [PATCH] Support 0 in copy command to copy to top of file

---
 .../commands/CopyCommandTest.kt               | 167 +++++++++++++++++-
 .../model/commands/CopyTextCommand.kt         |  37 ++--
 2 files changed, 192 insertions(+), 12 deletions(-)

diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/CopyCommandTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/CopyCommandTest.kt
index 66ef776ae..2ea407441 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/CopyCommandTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/CopyCommandTest.kt
@@ -32,5 +32,170 @@ class CopyCommandTest : VimTestCase() {
     """.trimIndent())
   }
 
-  // TODO: Add another test with 'nostartofline'. This should maintain the cursor column
+  @VimBehaviorDiffers(
+    originalVimAfter = """
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+      Nunc tincidunt viverra ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+      Nunc tincidunt viverra ligula non ${c}scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+      Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+      accumsan vitae, facilisis ac nulla.
+    """,
+    description = "Caret placement is wrong",
+    shouldBeFixed = true
+  )
+  @Test
+  fun `test duplicate line below with 'nostartofline'`() {
+    doTest(
+      exCommand("copy ."),
+      """
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+        Nunc tincidunt viverra ligula non ${c}scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+        Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+        accumsan vitae, facilisis ac nulla.
+      """.trimIndent(),
+      """
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+        Nunc tincidunt viverra ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+        ${c}Nunc tincidunt viverra ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+        Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+        accumsan vitae, facilisis ac nulla.
+      """.trimIndent(),
+    ) {
+      enterCommand("set nostartofline")
+    }
+  }
+
+  @VimBehaviorDiffers(
+    originalVimAfter = """
+      Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+      ${c}accumsan vitae, facilisis ac nulla.
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+      Nunc tincidunt viverra ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+      Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+      accumsan vitae, facilisis ac nulla.
+    """,
+    description = "Caret placement is incorrect",
+    shouldBeFixed = true
+  )
+  @Test
+  fun `test copy range to above first line`() {
+    doTest(
+      exCommand("3,4copy 0"),
+      """
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+        Nunc tincidunt viverra ${c}ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+        Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+        accumsan vitae, facilisis ac nulla.
+      """.trimIndent(),
+      """
+        ${c}Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+        accumsan vitae, facilisis ac nulla.
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+        Nunc tincidunt viverra ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+        Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+        accumsan vitae, facilisis ac nulla.
+      """.trimIndent(),
+    )
+  }
+
+  @VimBehaviorDiffers(
+    originalVimAfter = """
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+      Nunc tincidunt viverra ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+      Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+      accumsan vitae, facilisis ac nulla.
+      Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+      ${c}accumsan vitae, facilisis ac nulla.
+    """,
+    description = "Caret placement is incorrect",
+    shouldBeFixed = true
+  )
+  @Test
+  fun `test copy range to below last line`() {
+    doTest(
+      exCommand("3,4copy $"),
+      """
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+        Nunc tincidunt viverra ${c}ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+        Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+        accumsan vitae, facilisis ac nulla.
+      """.trimIndent(),
+      """
+        |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+        |Nunc tincidunt viverra ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+        |Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+        |accumsan vitae, facilisis ac nulla.
+        |${c}Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+        |accumsan vitae, facilisis ac nulla.
+        |""".trimMargin(),
+    )
+  }
+
+  @VimBehaviorDiffers(
+    originalVimAfter = """
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+      Nunc tincidunt viverra ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+      Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+      accumsan vitae, facilisis ac nulla.
+      Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+      ${c}accumsan vitae, facilisis ac nulla.
+    """,
+    description = "Caret placement is incorrect",
+    shouldBeFixed = true
+  )
+  @Test
+  fun `test copy with no space between command and address`() {
+    doTest(
+      exCommand("3,4copy$"),
+      """
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+        Nunc tincidunt viverra ${c}ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+        Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+        accumsan vitae, facilisis ac nulla.
+      """.trimIndent(),
+      """
+        |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+        |Nunc tincidunt viverra ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+        |Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+        |accumsan vitae, facilisis ac nulla.
+        |${c}Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+        |accumsan vitae, facilisis ac nulla.
+        |""".trimMargin(),
+    )
+    assertPluginError(false)
+  }
+
+  @VimBehaviorDiffers(
+    originalVimAfter = """
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+      Nunc tincidunt viverra ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+      Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+      accumsan vitae, facilisis ac nulla.
+      Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+      ${c}accumsan vitae, facilisis ac nulla.
+    """,
+    description = "Caret placement is wrong",
+    shouldBeFixed = true
+  )
+  @Test
+  fun `test copy to below first address of range with no errors`() {
+    doTest(
+      exCommand("3,4copy 4,1,2,.,0"),
+      """
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+        Nunc tincidunt viverra ${c}ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+        Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+        accumsan vitae, facilisis ac nulla.
+      """.trimIndent(),
+      """
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas efficitur nec odio vel malesuada.
+        Nunc tincidunt viverra ligula non scelerisque. Aliquam erat volutpat. Praesent in fermentum orci. 
+        ${c}Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+        accumsan vitae, facilisis ac nulla.
+        Fusce sit amet mi ut purus volutpat vulputate vitae sed tortor. Aliquam felis neque, varius eu 
+        accumsan vitae, facilisis ac nulla.
+      """.trimIndent(),
+    )
+    assertPluginError(false)
+  }
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/CopyTextCommand.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/CopyTextCommand.kt
index def41bfeb..811845472 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/CopyTextCommand.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/CopyTextCommand.kt
@@ -14,9 +14,9 @@ import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.getText
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.ex.ranges.Range
 import com.maddyhome.idea.vim.put.PutData
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
 
 /**
@@ -41,16 +41,31 @@ public data class CopyTextCommand(val range: Range, val argument: String) : Comm
 
       val transferableData = injector.clipboardManager.getTransferableData(editor, range, text)
       val textData = PutData.TextData(text, SelectionType.LINE_WISE, transferableData, null)
-      val putData = PutData(
-        textData,
-        null,
-        1,
-        insertTextBeforeCaret = false,
-        rawIndent = true,
-        caretAfterInsertedText = false,
-        putToLine = address1 - 1,
-      )
-      injector.put.putTextForCaret(editor, caret, context, putData)
+      var mutableCaret = caret
+      val putData = if (address1 == 0) {
+        // TODO: This should maintain current column location
+        mutableCaret = mutableCaret.moveToOffset(0)
+        PutData(
+          textData,
+          null,
+          1,
+          insertTextBeforeCaret = true,
+          rawIndent = true,
+          caretAfterInsertedText = false,
+        )
+      }
+      else {
+        PutData(
+          textData,
+          null,
+          1,
+          insertTextBeforeCaret = false,
+          rawIndent = true,
+          caretAfterInsertedText = false,
+          putToLine = address1 - 1
+        )
+      }
+      injector.put.putTextForCaret(editor, mutableCaret, context, putData)
     }
     return ExecutionResult.Success
   }