From c6ef3f286fd0208abe7efaa67e41213f44dc42d9 Mon Sep 17 00:00:00 2001
From: chylex <contact@chylex.com>
Date: Wed, 21 Dec 2022 04:34:56 +0100
Subject: [PATCH] Fix(VIM-696) Restore visual mode after undo/redo, and disable
 incompatible actions

---
 .../com/maddyhome/idea/vim/group/IjOptions.kt |  2 +-
 .../idea/vim/helper/UndoRedoHelper.kt         | 30 +++++++++++++------
 .../idea/vim/action/change/RedoAction.kt      |  2 +-
 .../idea/vim/action/change/UndoAction.kt      |  2 +-
 .../change/ChangeCaseLowerVisualAction.kt     |  3 +-
 .../change/ChangeCaseUpperVisualAction.kt     |  3 +-
 .../ksp-generated/engine_commands.json        | 15 ++++------
 7 files changed, 31 insertions(+), 26 deletions(-)

diff --git a/src/main/java/com/maddyhome/idea/vim/group/IjOptions.kt b/src/main/java/com/maddyhome/idea/vim/group/IjOptions.kt
index 97de3f8d0..d7e738541 100644
--- a/src/main/java/com/maddyhome/idea/vim/group/IjOptions.kt
+++ b/src/main/java/com/maddyhome/idea/vim/group/IjOptions.kt
@@ -143,7 +143,7 @@ object IjOptions {
     addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
   val commandOrMotionAnnotation: ToggleOption =
     addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
-  val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", false, isHidden = true))
+  val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true))
   val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
   val vimscriptFunctionAnnotation: ToggleOption =
     addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
diff --git a/src/main/java/com/maddyhome/idea/vim/helper/UndoRedoHelper.kt b/src/main/java/com/maddyhome/idea/vim/helper/UndoRedoHelper.kt
index f850019c1..9c8274ee3 100644
--- a/src/main/java/com/maddyhome/idea/vim/helper/UndoRedoHelper.kt
+++ b/src/main/java/com/maddyhome/idea/vim/helper/UndoRedoHelper.kt
@@ -19,6 +19,7 @@ import com.intellij.openapi.fileEditor.TextEditor
 import com.intellij.openapi.fileEditor.TextEditorWithPreview
 import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
 import com.intellij.openapi.util.registry.Registry
+import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.ExecutionContext
 import com.maddyhome.idea.vim.api.VimCaret
 import com.maddyhome.idea.vim.api.VimEditor
@@ -28,6 +29,8 @@ import com.maddyhome.idea.vim.common.InsertSequence
 import com.maddyhome.idea.vim.newapi.IjVimCaret
 import com.maddyhome.idea.vim.newapi.globalIjOptions
 import com.maddyhome.idea.vim.newapi.ij
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.inVisualMode
 import com.maddyhome.idea.vim.undo.VimTimestampBasedUndoService
 
 /**
@@ -75,15 +78,7 @@ internal class UndoRedoHelper : VimTimestampBasedUndoService {
       // TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
       editor.runWithChangeTracking {
         undoManager.undo(fileEditor)
-
-        // We execute undo one more time if the previous one just restored selection
-        if (!hasChanges && hasSelection(editor) && undoManager.isUndoAvailable(fileEditor)) {
-          undoManager.undo(fileEditor)
-        }
-      }
-
-      CommandProcessor.getInstance().runUndoTransparentAction {
-        removeSelections(editor)
+        restoreVisualMode(editor)
       }
     } else {
       runWithBooleanRegistryOption("ide.undo.transparent.caret.movement", true) {
@@ -230,4 +225,21 @@ internal class UndoRedoHelper : VimTimestampBasedUndoService {
     val hasChanges: Boolean
       get() = changeListener.hasChanged || initialPath != editor.getPath()
   }
+
+  private fun restoreVisualMode(editor: VimEditor) {
+    if (!editor.inVisualMode && editor.getSelectionModel().hasSelection()) {
+      val detectedMode = VimPlugin.getVisualMotion().detectSelectionType(editor)
+
+      // Visual block selection is restored into multiple carets, so multi-carets that form a block are always
+      // identified as visual block mode, leading to false positives.
+      // Since I use visual block mode much less often than multi-carets, this is a judgment call to never restore
+      // visual block mode.
+      val wantedMode = if (detectedMode == SelectionType.BLOCK_WISE)
+        SelectionType.CHARACTER_WISE
+      else
+        detectedMode
+
+      VimPlugin.getVisualMotion().enterVisualMode(editor, wantedMode)
+    }
+  }
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/RedoAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/RedoAction.kt
index ffa99d9ff..cc2b0de6c 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/RedoAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/RedoAction.kt
@@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.OperatorArguments
 import com.maddyhome.idea.vim.handler.VimActionHandler
 
-@CommandOrMotion(keys = ["<C-R>"], modes = [Mode.NORMAL])
+@CommandOrMotion(keys = ["U", "<C-R>"], modes = [Mode.NORMAL, Mode.VISUAL])
 class RedoAction : VimActionHandler.SingleExecution() {
   override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
 
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/UndoAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/UndoAction.kt
index d74266c98..67c28ab93 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/UndoAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/UndoAction.kt
@@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.OperatorArguments
 import com.maddyhome.idea.vim.handler.VimActionHandler
 
-@CommandOrMotion(keys = ["u", "<Undo>"], modes = [Mode.NORMAL])
+@CommandOrMotion(keys = ["u", "<Undo>"], modes = [Mode.NORMAL, Mode.VISUAL])
 class UndoAction : VimActionHandler.SingleExecution() {
   override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
 
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeCaseLowerVisualAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeCaseLowerVisualAction.kt
index 39c92aa3b..79a320981 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeCaseLowerVisualAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeCaseLowerVisualAction.kt
@@ -8,7 +8,6 @@
 package com.maddyhome.idea.vim.action.change.change
 
 import com.intellij.vim.annotations.CommandOrMotion
-import com.intellij.vim.annotations.Mode
 import com.maddyhome.idea.vim.api.ExecutionContext
 import com.maddyhome.idea.vim.api.VimCaret
 import com.maddyhome.idea.vim.api.VimChangeGroup
@@ -22,7 +21,7 @@ import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
 /**
  * @author vlan
  */
-@CommandOrMotion(keys = ["u"], modes = [Mode.VISUAL])
+@CommandOrMotion(keys = [], modes = [])
 class ChangeCaseLowerVisualAction : VisualOperatorActionHandler.ForEachCaret() {
   override val type: Command.Type = Command.Type.CHANGE
 
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeCaseUpperVisualAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeCaseUpperVisualAction.kt
index 36d5f58c3..4782c578a 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeCaseUpperVisualAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeCaseUpperVisualAction.kt
@@ -8,7 +8,6 @@
 package com.maddyhome.idea.vim.action.change.change
 
 import com.intellij.vim.annotations.CommandOrMotion
-import com.intellij.vim.annotations.Mode
 import com.maddyhome.idea.vim.api.ExecutionContext
 import com.maddyhome.idea.vim.api.VimCaret
 import com.maddyhome.idea.vim.api.VimChangeGroup
@@ -22,7 +21,7 @@ import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
 /**
  * @author vlan
  */
-@CommandOrMotion(keys = ["U"], modes = [Mode.VISUAL])
+@CommandOrMotion(keys = [], modes = [])
 class ChangeCaseUpperVisualAction : VisualOperatorActionHandler.ForEachCaret() {
   override val type: Command.Type = Command.Type.CHANGE
 
diff --git a/vim-engine/src/main/resources/ksp-generated/engine_commands.json b/vim-engine/src/main/resources/ksp-generated/engine_commands.json
index 2bf8fce56..704a67cf5 100644
--- a/vim-engine/src/main/resources/ksp-generated/engine_commands.json
+++ b/vim-engine/src/main/resources/ksp-generated/engine_commands.json
@@ -417,7 +417,7 @@
     {
         "keys": "<C-R>",
         "class": "com.maddyhome.idea.vim.action.change.RedoAction",
-        "modes": "N"
+        "modes": "NX"
     },
     {
         "keys": "<C-R>",
@@ -957,7 +957,7 @@
     {
         "keys": "<Undo>",
         "class": "com.maddyhome.idea.vim.action.change.UndoAction",
-        "modes": "N"
+        "modes": "NX"
     },
     {
         "keys": "<Up>",
@@ -1176,8 +1176,8 @@
     },
     {
         "keys": "U",
-        "class": "com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperVisualAction",
-        "modes": "X"
+        "class": "com.maddyhome.idea.vim.action.change.RedoAction",
+        "modes": "NX"
     },
     {
         "keys": "V",
@@ -1962,12 +1962,7 @@
     {
         "keys": "u",
         "class": "com.maddyhome.idea.vim.action.change.UndoAction",
-        "modes": "N"
-    },
-    {
-        "keys": "u",
-        "class": "com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerVisualAction",
-        "modes": "X"
+        "modes": "NX"
     },
     {
         "keys": "v",