diff --git a/src/main/java/com/maddyhome/idea/vim/action/VimShortcutKeyAction.kt b/src/main/java/com/maddyhome/idea/vim/action/VimShortcutKeyAction.kt
index 5f36c6055..5780ad2bc 100644
--- a/src/main/java/com/maddyhome/idea/vim/action/VimShortcutKeyAction.kt
+++ b/src/main/java/com/maddyhome/idea/vim/action/VimShortcutKeyAction.kt
@@ -37,7 +37,6 @@ import com.maddyhome.idea.vim.helper.inNormalMode
 import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
 import com.maddyhome.idea.vim.helper.isPrimaryEditor
 import com.maddyhome.idea.vim.helper.isTemplateActive
-import com.maddyhome.idea.vim.helper.mode
 import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
 import com.maddyhome.idea.vim.key.ShortcutOwner
 import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
@@ -45,6 +44,7 @@ import com.maddyhome.idea.vim.listener.AceJumpService
 import com.maddyhome.idea.vim.listener.AppCodeTemplates.appCodeTemplateCaptured
 import com.maddyhome.idea.vim.newapi.globalIjOptions
 import com.maddyhome.idea.vim.newapi.vim
+import com.maddyhome.idea.vim.state.mode.mode
 import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
 import java.awt.event.InputEvent
 import java.awt.event.KeyEvent
diff --git a/src/main/java/com/maddyhome/idea/vim/action/change/OperatorAction.kt b/src/main/java/com/maddyhome/idea/vim/action/change/OperatorAction.kt
index ae213bf7a..b5435ffdf 100644
--- a/src/main/java/com/maddyhome/idea/vim/action/change/OperatorAction.kt
+++ b/src/main/java/com/maddyhome/idea/vim/action/change/OperatorAction.kt
@@ -18,7 +18,7 @@ import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.common.argumentCaptured
 import com.maddyhome.idea.vim.group.MotionGroup
diff --git a/src/main/java/com/maddyhome/idea/vim/action/change/RepeatChangeAction.kt b/src/main/java/com/maddyhome/idea/vim/action/change/RepeatChangeAction.kt
index ad576b929..43c0df6f0 100644
--- a/src/main/java/com/maddyhome/idea/vim/action/change/RepeatChangeAction.kt
+++ b/src/main/java/com/maddyhome/idea/vim/action/change/RepeatChangeAction.kt
@@ -63,7 +63,7 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
             mot.count = 0
           }
         }
-        state.setExecutingCommand(lastCommand)
+        state.executingCommand = lastCommand
 
         val arguments = operatorArguments.copy(count0 = lastCommand.rawCount)
         injector.actionExecutor.executeVimAction(editor, lastCommand.action, context, arguments)
@@ -76,7 +76,7 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
     state.isDotRepeatInProgress = false
 
     // Restore state
-    if (save != null) state.setExecutingCommand(save)
+    if (save != null) state.executingCommand = save
     VimPlugin.getMotion().setLastFTCmd(lastFTCmd, lastFTChar)
     if (lastHandler != null) Extension.lastExtensionHandler = lastHandler
     VimRepeater.repeatHandler = repeatHandler
diff --git a/src/main/java/com/maddyhome/idea/vim/command/CommandState.kt b/src/main/java/com/maddyhome/idea/vim/command/CommandState.kt
index 1d5728544..16da1c071 100644
--- a/src/main/java/com/maddyhome/idea/vim/command/CommandState.kt
+++ b/src/main/java/com/maddyhome/idea/vim/command/CommandState.kt
@@ -11,6 +11,8 @@ package com.maddyhome.idea.vim.command
 import com.intellij.openapi.editor.Editor
 import com.maddyhome.idea.vim.helper.vimStateMachine
 import com.maddyhome.idea.vim.newapi.vim
+import com.maddyhome.idea.vim.state.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 
 /**
  * COMPATIBILITY-LAYER: Additional class
@@ -22,7 +24,18 @@ public class CommandState(private val machine: VimStateMachine) {
     get() = machine.isOperatorPending
 
   public val mode: CommandState.Mode
-    get() = machine.mode.ij
+    get() {
+      val myMode = machine.mode
+      return when (myMode) {
+        com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> CommandState.Mode.CMD_LINE
+        com.maddyhome.idea.vim.state.mode.Mode.INSERT -> CommandState.Mode.INSERT
+        is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> CommandState.Mode.COMMAND
+        is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> CommandState.Mode.OP_PENDING
+        com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> CommandState.Mode.REPLACE
+        is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> CommandState.Mode.SELECT
+        is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> CommandState.Mode.VISUAL
+      }
+    }
 
   public val commandBuilder: CommandBuilder
     get() = machine.commandBuilder
@@ -50,38 +63,10 @@ public class CommandState(private val machine: VimStateMachine) {
   }
 }
 
-public val CommandState.SubMode.engine: VimStateMachine.SubMode
+internal val CommandState.SubMode.engine: SelectionType
   get() = when (this) {
-    CommandState.SubMode.NONE -> VimStateMachine.SubMode.NONE
-    CommandState.SubMode.VISUAL_CHARACTER -> VimStateMachine.SubMode.VISUAL_CHARACTER
-    CommandState.SubMode.VISUAL_LINE -> VimStateMachine.SubMode.VISUAL_LINE
-    CommandState.SubMode.VISUAL_BLOCK -> VimStateMachine.SubMode.VISUAL_BLOCK
-  }
-
-public val CommandState.Mode.engine: VimStateMachine.Mode
-  get() = when (this) {
-    CommandState.Mode.COMMAND -> VimStateMachine.Mode.COMMAND
-    CommandState.Mode.VISUAL -> VimStateMachine.Mode.VISUAL
-    CommandState.Mode.SELECT -> VimStateMachine.Mode.SELECT
-    CommandState.Mode.INSERT -> VimStateMachine.Mode.INSERT
-    CommandState.Mode.CMD_LINE -> VimStateMachine.Mode.CMD_LINE
-    CommandState.Mode.OP_PENDING -> VimStateMachine.Mode.OP_PENDING
-    CommandState.Mode.REPLACE -> VimStateMachine.Mode.REPLACE
-    CommandState.Mode.INSERT_NORMAL -> VimStateMachine.Mode.INSERT_NORMAL
-    CommandState.Mode.INSERT_VISUAL -> VimStateMachine.Mode.INSERT_VISUAL
-    CommandState.Mode.INSERT_SELECT -> VimStateMachine.Mode.INSERT_SELECT
-  }
-
-public val VimStateMachine.Mode.ij: CommandState.Mode
-  get() = when (this) {
-    VimStateMachine.Mode.COMMAND -> CommandState.Mode.COMMAND
-    VimStateMachine.Mode.VISUAL -> CommandState.Mode.VISUAL
-    VimStateMachine.Mode.SELECT -> CommandState.Mode.SELECT
-    VimStateMachine.Mode.INSERT -> CommandState.Mode.INSERT
-    VimStateMachine.Mode.CMD_LINE -> CommandState.Mode.CMD_LINE
-    VimStateMachine.Mode.OP_PENDING -> CommandState.Mode.OP_PENDING
-    VimStateMachine.Mode.REPLACE -> CommandState.Mode.REPLACE
-    VimStateMachine.Mode.INSERT_NORMAL -> CommandState.Mode.INSERT_NORMAL
-    VimStateMachine.Mode.INSERT_VISUAL -> CommandState.Mode.INSERT_VISUAL
-    VimStateMachine.Mode.INSERT_SELECT -> CommandState.Mode.INSERT_SELECT
+    CommandState.SubMode.NONE -> error("Unexpected value")
+    CommandState.SubMode.VISUAL_CHARACTER -> SelectionType.CHARACTER_WISE
+    CommandState.SubMode.VISUAL_LINE -> SelectionType.LINE_WISE
+    CommandState.SubMode.VISUAL_BLOCK -> SelectionType.BLOCK_WISE
   }
diff --git a/src/main/java/com/maddyhome/idea/vim/extension/VimExtensionFacade.kt b/src/main/java/com/maddyhome/idea/vim/extension/VimExtensionFacade.kt
index 21457da62..1353dc89c 100644
--- a/src/main/java/com/maddyhome/idea/vim/extension/VimExtensionFacade.kt
+++ b/src/main/java/com/maddyhome/idea/vim/extension/VimExtensionFacade.kt
@@ -17,7 +17,7 @@ import com.maddyhome.idea.vim.api.ImmutableVimCaret
 import com.maddyhome.idea.vim.api.VimCaret
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.MappingMode
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.CommandAlias
 import com.maddyhome.idea.vim.common.CommandAliasHandler
 import com.maddyhome.idea.vim.helper.CommandLineHelper
diff --git a/src/main/java/com/maddyhome/idea/vim/extension/argtextobj/VimArgTextObjExtension.java b/src/main/java/com/maddyhome/idea/vim/extension/argtextobj/VimArgTextObjExtension.java
index faad607fb..81ff92e75 100644
--- a/src/main/java/com/maddyhome/idea/vim/extension/argtextobj/VimArgTextObjExtension.java
+++ b/src/main/java/com/maddyhome/idea/vim/extension/argtextobj/VimArgTextObjExtension.java
@@ -23,6 +23,8 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
 import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
 import com.maddyhome.idea.vim.newapi.IjVimCaret;
 import com.maddyhome.idea.vim.newapi.IjVimEditor;
+import com.maddyhome.idea.vim.state.VimStateMachine;
+import com.maddyhome.idea.vim.state.mode.Mode;
 import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString;
 import org.jetbrains.annotations.Nls;
 import org.jetbrains.annotations.NotNull;
@@ -244,7 +246,7 @@ public class VimArgTextObjExtension implements VimExtension {
     public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
 
       IjVimEditor vimEditor = (IjVimEditor) editor;
-      @NotNull VimStateMachine vimStateMachine = VimStateMachine.getInstance(vimEditor);
+      @NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(vimEditor);
       int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
 
       final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
@@ -254,7 +256,7 @@ public class VimArgTextObjExtension implements VimExtension {
           final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0);
           if (range != null) {
             try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
-              if (vimStateMachine.getMode() == VimStateMachine.Mode.VISUAL) {
+              if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
                 com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true);
               } else {
                 InlayHelperKt.moveToInlayAwareOffset(((IjVimCaret)caret).getCaret(), range.getStartOffset());
diff --git a/src/main/java/com/maddyhome/idea/vim/extension/commentary/CommentaryExtension.kt b/src/main/java/com/maddyhome/idea/vim/extension/commentary/CommentaryExtension.kt
index 243642681..a22e046d1 100644
--- a/src/main/java/com/maddyhome/idea/vim/extension/commentary/CommentaryExtension.kt
+++ b/src/main/java/com/maddyhome/idea/vim/extension/commentary/CommentaryExtension.kt
@@ -26,10 +26,10 @@ import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.MappingMode
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.command.TextObjectVisualType
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.common.CommandAliasHandler
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.ex.ranges.Ranges
@@ -61,7 +61,7 @@ internal class CommentaryExtension : VimExtension {
       resetCaret: Boolean,
     ): Boolean {
       val mode = editor.vimStateMachine.mode
-      if (mode !== VimStateMachine.Mode.VISUAL) {
+      if (mode !is Mode.VISUAL) {
         editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
       }
 
@@ -88,13 +88,13 @@ internal class CommentaryExtension : VimExtension {
     }
 
     private fun afterCommenting(
-      mode: VimStateMachine.Mode,
+      mode: Mode,
       editor: VimEditor,
       resetCaret: Boolean,
       range: TextRange,
     ) {
       // Remove the selection, if we added it
-      if (mode !== VimStateMachine.Mode.VISUAL) {
+      if (mode !is Mode.VISUAL) {
         editor.removeSelection()
       }
 
@@ -159,9 +159,9 @@ internal class CommentaryExtension : VimExtension {
     }
 
     // todo make it multicaret
-    override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean {
+    override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
       val range = injector.markService.getChangeMarks(editor.primaryCaret()) ?: return false
-      return doCommentary(editor, context, range, selectionType, true)
+      return doCommentary(editor, context, range, selectionType ?: SelectionType.CHARACTER_WISE, true)
     }
   }
 
diff --git a/src/main/java/com/maddyhome/idea/vim/extension/exchange/VimExchangeExtension.kt b/src/main/java/com/maddyhome/idea/vim/extension/exchange/VimExchangeExtension.kt
index 6c89327bc..fbaec59e6 100644
--- a/src/main/java/com/maddyhome/idea/vim/extension/exchange/VimExchangeExtension.kt
+++ b/src/main/java/com/maddyhome/idea/vim/extension/exchange/VimExchangeExtension.kt
@@ -23,8 +23,9 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.setChangeMarks
 import com.maddyhome.idea.vim.command.MappingMode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
+import com.maddyhome.idea.vim.state.mode.selectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.extension.ExtensionHandler
 import com.maddyhome.idea.vim.extension.VimExtension
@@ -35,9 +36,9 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
 import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
 import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister
 import com.maddyhome.idea.vim.helper.fileSize
+import com.maddyhome.idea.vim.state.mode.mode
 import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
 import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
-import com.maddyhome.idea.vim.helper.subMode
 import com.maddyhome.idea.vim.key.OperatorFunction
 import com.maddyhome.idea.vim.mark.Mark
 import com.maddyhome.idea.vim.mark.VimMarkConstants
@@ -86,7 +87,7 @@ internal class VimExchangeExtension : VimExtension {
     val EXCHANGE_KEY = Key<Exchange>("exchange")
 
     // End mark has always greater of eq offset than start mark
-    class Exchange(val type: VimStateMachine.SubMode, val start: Mark, val end: Mark, val text: String) {
+    class Exchange(val type: SelectionType, val start: Mark, val end: Mark, val text: String) {
       private var myHighlighter: RangeHighlighter? = null
       fun setHighlighter(highlighter: RangeHighlighter) {
         myHighlighter = highlighter
@@ -121,33 +122,32 @@ internal class VimExchangeExtension : VimExtension {
   private class VExchangeHandler : ExtensionHandler {
     override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
       runWriteAction {
-        val subMode = editor.subMode
+        val mode = editor.mode
         // Leave visual mode to create selection marks
         executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
-        Operator(true).apply(editor, context, SelectionType.fromSubMode(subMode))
+        Operator(true).apply(editor, context, mode.selectionType ?: CHARACTER_WISE)
       }
     }
   }
 
   private class Operator(private val isVisual: Boolean) : OperatorFunction {
     fun Editor.getMarkOffset(mark: Mark) = IjVimEditor(this).getOffset(mark.line, mark.col)
-    fun VimStateMachine.SubMode.getString() = when (this) {
-      VimStateMachine.SubMode.VISUAL_CHARACTER -> "v"
-      VimStateMachine.SubMode.VISUAL_LINE -> "V"
-      VimStateMachine.SubMode.VISUAL_BLOCK -> "\\<C-V>"
-      else -> error("Invalid SubMode: $this")
+    fun SelectionType.getString() = when (this) {
+      SelectionType.CHARACTER_WISE -> "v"
+      SelectionType.LINE_WISE -> "V"
+      SelectionType.BLOCK_WISE -> "\\<C-V>"
     }
 
-    override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean {
+    override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
       val ijEditor = editor.ij
       fun highlightExchange(ex: Exchange): RangeHighlighter {
         val attributes = ijEditor.colorsScheme.getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES)
         val hlArea = when (ex.type) {
-          VimStateMachine.SubMode.VISUAL_LINE -> HighlighterTargetArea.LINES_IN_RANGE
+          SelectionType.LINE_WISE -> HighlighterTargetArea.LINES_IN_RANGE
           // TODO: handle other modes
           else -> HighlighterTargetArea.EXACT_RANGE
         }
-        val isVisualLine = ex.type == VimStateMachine.SubMode.VISUAL_LINE
+        val isVisualLine = ex.type == SelectionType.LINE_WISE
         val endAdj = if (!(isVisualLine) && (hlArea == HighlighterTargetArea.EXACT_RANGE || (isVisual))) 1 else 0
         return ijEditor.markupModel.addRangeHighlighter(
           ijEditor.getMarkOffset(ex.start),
@@ -158,7 +158,7 @@ internal class VimExchangeExtension : VimExtension {
         )
       }
 
-      val currentExchange = getExchange(ijEditor, isVisual, selectionType)
+      val currentExchange = getExchange(ijEditor, isVisual, selectionType ?: CHARACTER_WISE)
       val exchange1 = ijEditor.getUserData(EXCHANGE_KEY)
       if (exchange1 == null) {
         val highlighter = highlightExchange(currentExchange)
@@ -203,7 +203,7 @@ internal class VimExchangeExtension : VimExtension {
           TextRange(editor.getMarkOffset(targetExchange.start), editor.getMarkOffset(targetExchange.end) + 1),
         )
         // do this instead of direct text manipulation to set change marks
-        setRegister('z', injector.parser.stringToKeys(sourceExchange.text), SelectionType.fromSubMode(sourceExchange.type))
+        setRegister('z', injector.parser.stringToKeys(sourceExchange.text), sourceExchange.type)
         executeNormalWithoutMapping(injector.parser.stringToKeys("`[${targetExchange.type.getString()}`]\"zp"), editor)
       }
 
@@ -269,7 +269,7 @@ internal class VimExchangeExtension : VimExtension {
           x.line - y.line
         }
 
-      return if (x.type == VimStateMachine.SubMode.VISUAL_BLOCK && y.type == VimStateMachine.SubMode.VISUAL_BLOCK) {
+      return if (x.type == SelectionType.BLOCK_WISE && y.type == SelectionType.BLOCK_WISE) {
         when {
           intersects(x, y) -> {
             ExchangeCompareResult.OVERLAP
@@ -348,9 +348,9 @@ internal class VimExchangeExtension : VimExtension {
       setRegister('+', plusRegText)
 
       return if (selectionStart.offset(editor.vim) <= selectionEnd.offset(editor.vim)) {
-        Exchange(selectionType.toSubMode(), selectionStart, selectionEnd, text)
+        Exchange(selectionType, selectionStart, selectionEnd, text)
       } else {
-        Exchange(selectionType.toSubMode(), selectionEnd, selectionStart, text)
+        Exchange(selectionType, selectionEnd, selectionStart, text)
       }
     }
   }
diff --git a/src/main/java/com/maddyhome/idea/vim/extension/multiplecursors/VimMultipleCursorsExtension.kt b/src/main/java/com/maddyhome/idea/vim/extension/multiplecursors/VimMultipleCursorsExtension.kt
index 74c61f1ce..fd99ca26f 100644
--- a/src/main/java/com/maddyhome/idea/vim/extension/multiplecursors/VimMultipleCursorsExtension.kt
+++ b/src/main/java/com/maddyhome/idea/vim/extension/multiplecursors/VimMultipleCursorsExtension.kt
@@ -23,7 +23,6 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.options
 import com.maddyhome.idea.vim.command.MappingMode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.extension.ExtensionHandler
 import com.maddyhome.idea.vim.extension.VimExtension
@@ -42,6 +41,7 @@ import com.maddyhome.idea.vim.helper.userData
 import com.maddyhome.idea.vim.newapi.IjVimEditor
 import com.maddyhome.idea.vim.newapi.ij
 import com.maddyhome.idea.vim.newapi.vim
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import kotlin.math.max
 import kotlin.math.min
 
@@ -312,7 +312,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
 
   private fun enterVisualMode(editor: VimEditor) {
     // We need to reset the key handler to make sure we pick up the fact that we're in visual mode
-    VimPlugin.getVisualMotion().enterVisualMode(editor, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    VimPlugin.getVisualMotion().enterVisualMode(editor, SelectionType.CHARACTER_WISE)
     KeyHandler.getInstance().reset(editor)
   }
 
diff --git a/src/main/java/com/maddyhome/idea/vim/extension/replacewithregister/ReplaceWithRegister.kt b/src/main/java/com/maddyhome/idea/vim/extension/replacewithregister/ReplaceWithRegister.kt
index cb1568bdc..b73bb6f10 100644
--- a/src/main/java/com/maddyhome/idea/vim/extension/replacewithregister/ReplaceWithRegister.kt
+++ b/src/main/java/com/maddyhome/idea/vim/extension/replacewithregister/ReplaceWithRegister.kt
@@ -16,10 +16,12 @@ import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.getLineEndOffset
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.MappingMode
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
-import com.maddyhome.idea.vim.command.isLine
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
+import com.maddyhome.idea.vim.state.mode.isLine
+import com.maddyhome.idea.vim.state.mode.selectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.extension.ExtensionHandler
 import com.maddyhome.idea.vim.extension.VimExtension
@@ -29,8 +31,7 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
 import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
 import com.maddyhome.idea.vim.group.visual.VimSelection
 import com.maddyhome.idea.vim.helper.exitVisualMode
-import com.maddyhome.idea.vim.helper.mode
-import com.maddyhome.idea.vim.helper.subMode
+import com.maddyhome.idea.vim.state.mode.mode
 import com.maddyhome.idea.vim.helper.vimStateMachine
 import com.maddyhome.idea.vim.key.OperatorFunction
 import com.maddyhome.idea.vim.newapi.IjVimEditor
@@ -56,7 +57,7 @@ internal class ReplaceWithRegister : VimExtension {
 
   private class RwrVisual : ExtensionHandler {
     override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
-      val typeInEditor = SelectionType.fromSubMode(editor.subMode)
+      val typeInEditor = editor.mode.selectionType ?: CHARACTER_WISE
       editor.sortedCarets().forEach { caret ->
         val selectionStart = caret.selectionStart
         val selectionEnd = caret.selectionEnd
@@ -103,7 +104,7 @@ internal class ReplaceWithRegister : VimExtension {
   }
 
   private class Operator : OperatorFunction {
-    override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean {
+    override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
       val ijEditor = (editor as IjVimEditor).editor
       val range = getRange(ijEditor) ?: return false
       val visualSelection = PutData.VisualSelection(
@@ -111,11 +112,11 @@ internal class ReplaceWithRegister : VimExtension {
           editor.primaryCaret() to VimSelection.create(
             range.startOffset,
             range.endOffset - 1,
-            selectionType,
+            selectionType ?: CHARACTER_WISE,
             editor,
           ),
         ),
-        selectionType,
+        selectionType ?: CHARACTER_WISE,
       )
       // todo multicaret
       doReplace(ijEditor, editor.primaryCaret(), visualSelection)
@@ -124,8 +125,8 @@ internal class ReplaceWithRegister : VimExtension {
 
     // todo make it work with multiple carets
     private fun getRange(editor: Editor): TextRange? = when (editor.vim.mode) {
-      VimStateMachine.Mode.COMMAND -> injector.markService.getChangeMarks(editor.caretModel.primaryCaret.vim)
-      VimStateMachine.Mode.VISUAL -> editor.caretModel.primaryCaret.run { TextRange(selectionStart, selectionEnd) }
+      is Mode.NORMAL -> injector.markService.getChangeMarks(editor.caretModel.primaryCaret.vim)
+      is Mode.VISUAL -> editor.caretModel.primaryCaret.run { TextRange(selectionStart, selectionEnd) }
       else -> null
     }
   }
@@ -173,7 +174,6 @@ internal class ReplaceWithRegister : VimExtension {
             editor.vimStateMachine?.isOperatorPending ?: false,
             0,
             editor.vim.mode,
-            editor.vim.subMode,
           ),
           saveToRegister = false
         )
diff --git a/src/main/java/com/maddyhome/idea/vim/extension/surround/VimSurroundExtension.kt b/src/main/java/com/maddyhome/idea/vim/extension/surround/VimSurroundExtension.kt
index e3fdbb00d..43c7cf020 100644
--- a/src/main/java/com/maddyhome/idea/vim/extension/surround/VimSurroundExtension.kt
+++ b/src/main/java/com/maddyhome/idea/vim/extension/surround/VimSurroundExtension.kt
@@ -18,9 +18,10 @@ import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.setChangeMarks
 import com.maddyhome.idea.vim.command.MappingMode
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.selectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.extension.ExtensionHandler
 import com.maddyhome.idea.vim.extension.VimExtension
@@ -32,8 +33,7 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMa
 import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
 import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
 import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
-import com.maddyhome.idea.vim.helper.mode
-import com.maddyhome.idea.vim.helper.subMode
+import com.maddyhome.idea.vim.state.mode.mode
 import com.maddyhome.idea.vim.key.OperatorFunction
 import com.maddyhome.idea.vim.newapi.ij
 import com.maddyhome.idea.vim.newapi.vim
@@ -122,7 +122,7 @@ internal class VimSurroundExtension : VimExtension {
     override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
       val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart
       // NB: Operator ignores SelectionType anyway
-      if (!Operator().apply(editor, context, SelectionType.fromSubMode(editor.subMode))) {
+      if (!Operator().apply(editor, context, editor.mode.selectionType)) {
         return
       }
       runWriteAction {
@@ -256,7 +256,7 @@ internal class VimSurroundExtension : VimExtension {
   }
 
   private class Operator : OperatorFunction {
-    override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean {
+    override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
       val ijEditor = editor.ij
       val c = getChar(ijEditor)
       if (c.code == 0) return true
@@ -274,8 +274,8 @@ internal class VimSurroundExtension : VimExtension {
       val editor = caret.editor
       val ijEditor = editor.ij
       return when (ijEditor.vim.mode) {
-        VimStateMachine.Mode.COMMAND -> injector.markService.getChangeMarks(caret)
-        VimStateMachine.Mode.VISUAL -> caret.run { TextRange(selectionStart, selectionEnd) }
+        is Mode.NORMAL -> injector.markService.getChangeMarks(caret)
+        is Mode.VISUAL -> caret.run { TextRange(selectionStart, selectionEnd) }
         else -> null
       }
     }
diff --git a/src/main/java/com/maddyhome/idea/vim/extension/textobjentire/VimTextObjEntireExtension.java b/src/main/java/com/maddyhome/idea/vim/extension/textobjentire/VimTextObjEntireExtension.java
index 1da5dec1a..2910b4220 100644
--- a/src/main/java/com/maddyhome/idea/vim/extension/textobjentire/VimTextObjEntireExtension.java
+++ b/src/main/java/com/maddyhome/idea/vim/extension/textobjentire/VimTextObjEntireExtension.java
@@ -23,6 +23,8 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
 import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
 import com.maddyhome.idea.vim.newapi.IjVimCaret;
 import com.maddyhome.idea.vim.newapi.IjVimEditor;
+import com.maddyhome.idea.vim.state.VimStateMachine;
+import com.maddyhome.idea.vim.state.mode.Mode;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -131,7 +133,7 @@ public class VimTextObjEntireExtension implements VimExtension {
 
     @Override
     public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
-      @NotNull VimStateMachine vimStateMachine = VimStateMachine.getInstance(editor);
+      @NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(editor);
       int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
 
       final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
@@ -141,7 +143,7 @@ public class VimTextObjEntireExtension implements VimExtension {
           final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, count, 0);
           if (range != null) {
             try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
-              if (vimStateMachine.getMode() == VimStateMachine.Mode.VISUAL) {
+              if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
                 com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
               } else {
                 InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
diff --git a/src/main/java/com/maddyhome/idea/vim/extension/textobjindent/VimIndentObject.java b/src/main/java/com/maddyhome/idea/vim/extension/textobjindent/VimIndentObject.java
index 945576996..647fa311f 100644
--- a/src/main/java/com/maddyhome/idea/vim/extension/textobjindent/VimIndentObject.java
+++ b/src/main/java/com/maddyhome/idea/vim/extension/textobjindent/VimIndentObject.java
@@ -24,6 +24,8 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
 import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
 import com.maddyhome.idea.vim.newapi.IjVimCaret;
 import com.maddyhome.idea.vim.newapi.IjVimEditor;
+import com.maddyhome.idea.vim.state.VimStateMachine;
+import com.maddyhome.idea.vim.state.mode.Mode;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -260,7 +262,7 @@ public class VimIndentObject implements VimExtension {
     @Override
     public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
       IjVimEditor vimEditor = (IjVimEditor)editor;
-      @NotNull VimStateMachine vimStateMachine = VimStateMachine.getInstance(vimEditor);
+      @NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(vimEditor);
       int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
 
       final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
@@ -270,7 +272,7 @@ public class VimIndentObject implements VimExtension {
           final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0);
           if (range != null) {
             try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
-              if (vimStateMachine.getMode() == VimStateMachine.Mode.VISUAL) {
+              if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
                 EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
               } else {
                 InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
diff --git a/src/main/java/com/maddyhome/idea/vim/group/ChangeGroup.java b/src/main/java/com/maddyhome/idea/vim/group/ChangeGroup.java
index 139062029..4eac48ccf 100644
--- a/src/main/java/com/maddyhome/idea/vim/group/ChangeGroup.java
+++ b/src/main/java/com/maddyhome/idea/vim/group/ChangeGroup.java
@@ -48,6 +48,8 @@ import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext;
 import com.maddyhome.idea.vim.newapi.IjEditorExecutionContextKt;
 import com.maddyhome.idea.vim.newapi.IjVimCaret;
 import com.maddyhome.idea.vim.newapi.IjVimEditor;
+import com.maddyhome.idea.vim.state.mode.Mode;
+import com.maddyhome.idea.vim.state.mode.SelectionType;
 import com.maddyhome.idea.vim.vimscript.model.commands.SortOption;
 import kotlin.Pair;
 import kotlin.Unit;
@@ -177,8 +179,8 @@ public class ChangeGroup extends VimChangeGroupBase {
     final int lines = VimChangeGroupBase.Companion.getLinesCountInVisualBlock(editor, range);
     final BufferPosition startPosition = editor.offsetToBufferPosition(range.getStartOffset());
 
-    boolean visualBlockMode = operatorArguments.getMode() == VimStateMachine.Mode.VISUAL &&
-                              operatorArguments.getSubMode() == VimStateMachine.SubMode.VISUAL_BLOCK;
+    boolean visualBlockMode =
+      operatorArguments.getMode() instanceof Mode.VISUAL mode && mode.getSelectionType() == SelectionType.BLOCK_WISE;
     for (VimCaret caret : editor.carets()) {
       final int line = startPosition.getLine();
       int column = startPosition.getColumn();
diff --git a/src/main/java/com/maddyhome/idea/vim/group/FileGroup.java b/src/main/java/com/maddyhome/idea/vim/group/FileGroup.java
index 726ade685..8f6a77cbc 100644
--- a/src/main/java/com/maddyhome/idea/vim/group/FileGroup.java
+++ b/src/main/java/com/maddyhome/idea/vim/group/FileGroup.java
@@ -30,7 +30,8 @@ import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.psi.search.ProjectScope;
 import com.maddyhome.idea.vim.VimPlugin;
 import com.maddyhome.idea.vim.api.*;
-import com.maddyhome.idea.vim.command.VimStateMachine;
+import com.maddyhome.idea.vim.state.mode.Mode;
+import com.maddyhome.idea.vim.state.VimStateMachine;
 import com.maddyhome.idea.vim.common.TextRange;
 import com.maddyhome.idea.vim.helper.EditorHelper;
 import com.maddyhome.idea.vim.helper.EditorHelperRt;
@@ -296,7 +297,7 @@ public class FileGroup extends VimFileBase {
     StringBuilder msg = new StringBuilder();
     Document doc = editor.getDocument();
 
-    if (VimStateMachine.getInstance(new IjVimEditor(editor)).getMode() != VimStateMachine.Mode.VISUAL) {
+    if (!(VimStateMachine.Companion.getInstance(new IjVimEditor(editor)).getMode() instanceof Mode.VISUAL)) {
       LogicalPosition lp = editor.getCaretModel().getLogicalPosition();
       int col = editor.getCaretModel().getOffset() - doc.getLineStartOffset(lp.line);
       int endoff = doc.getLineEndOffset(lp.line);
diff --git a/src/main/java/com/maddyhome/idea/vim/group/MotionGroup.kt b/src/main/java/com/maddyhome/idea/vim/group/MotionGroup.kt
index 006f1c9bf..692278764 100755
--- a/src/main/java/com/maddyhome/idea/vim/group/MotionGroup.kt
+++ b/src/main/java/com/maddyhome/idea/vim/group/MotionGroup.kt
@@ -46,8 +46,9 @@ import com.maddyhome.idea.vim.api.options
 import com.maddyhome.idea.vim.api.visualLineToBufferLine
 import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.MotionType
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.VimStateMachine
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.ex.ExOutputModel
 import com.maddyhome.idea.vim.handler.Motion
@@ -460,7 +461,7 @@ internal class MotionGroup : VimMotionGroupBase() {
         val editor = fileEditor.editor
         ExOutputModel.getInstance(editor).clear()
         editor.vim.let { vimEditor ->
-          if (VimStateMachine.getInstance(vimEditor).mode === VimStateMachine.Mode.VISUAL) {
+          if (VimStateMachine.getInstance(vimEditor).mode is Mode.VISUAL) {
             vimEditor.exitVisualMode()
             KeyHandler.getInstance().reset(vimEditor)
           }
diff --git a/src/main/java/com/maddyhome/idea/vim/group/ProcessGroup.java b/src/main/java/com/maddyhome/idea/vim/group/ProcessGroup.java
index c1571836a..5cb37ef0f 100644
--- a/src/main/java/com/maddyhome/idea/vim/group/ProcessGroup.java
+++ b/src/main/java/com/maddyhome/idea/vim/group/ProcessGroup.java
@@ -30,7 +30,8 @@ import com.maddyhome.idea.vim.api.VimEditor;
 import com.maddyhome.idea.vim.api.VimInjectorKt;
 import com.maddyhome.idea.vim.api.VimProcessGroupBase;
 import com.maddyhome.idea.vim.command.Command;
-import com.maddyhome.idea.vim.command.VimStateMachine;
+import com.maddyhome.idea.vim.state.mode.Mode;
+import com.maddyhome.idea.vim.state.VimStateMachine;
 import com.maddyhome.idea.vim.ex.ExException;
 import com.maddyhome.idea.vim.ex.InvalidCommandException;
 import com.maddyhome.idea.vim.helper.UiHelper;
@@ -81,7 +82,8 @@ public class ProcessGroup extends VimProcessGroupBase {
     if (editor.isOneLineMode()) return;
 
     String initText = getRange(((IjVimEditor) editor).getEditor(), cmd);
-    VimStateMachine.getInstance(editor).pushModes(VimStateMachine.Mode.CMD_LINE, VimStateMachine.SubMode.NONE);
+    injector.getMarkService().setVisualSelectionMarks(editor);
+    VimStateMachine.Companion.getInstance(editor).setMode(Mode.CMD_LINE.INSTANCE);
     ExEntryPanel panel = ExEntryPanel.getInstance();
     panel.activate(((IjVimEditor) editor).getEditor(), ((IjEditorExecutionContext) context).getContext(), ":", initText, 1);
   }
@@ -99,7 +101,7 @@ public class ProcessGroup extends VimProcessGroupBase {
       return true;
     }
     else {
-      VimStateMachine.getInstance(editor).popModes();
+      VimStateMachine.Companion.getInstance(editor).setMode(new Mode.NORMAL());
       KeyHandler.getInstance().reset(editor);
       return false;
     }
@@ -110,7 +112,7 @@ public class ProcessGroup extends VimProcessGroupBase {
     panel.deactivate(true);
     boolean res = true;
     try {
-      VimStateMachine.getInstance(editor).popModes();
+      VimStateMachine.Companion.getInstance(editor).setMode(new Mode.NORMAL());
 
       logger.debug("processing command");
 
@@ -143,11 +145,11 @@ public class ProcessGroup extends VimProcessGroupBase {
 
   // commands executed from map command / macro should not be added to history
   private boolean skipHistory(VimEditor editor) {
-    return VimStateMachine.getInstance(editor).getMappingState().isExecutingMap() || injector.getMacro().isExecutingMacro();
+    return VimStateMachine.Companion.getInstance(editor).getMappingState().isExecutingMap() || injector.getMacro().isExecutingMacro();
   }
 
   public void cancelExEntry(final @NotNull VimEditor editor, boolean resetCaret) {
-    VimStateMachine.getInstance(editor).popModes();
+    VimStateMachine.Companion.getInstance(editor).setMode(new Mode.NORMAL());
     KeyHandler.getInstance().reset(editor);
     ExEntryPanel panel = ExEntryPanel.getInstance();
     panel.deactivate(true, resetCaret);
@@ -156,14 +158,14 @@ public class ProcessGroup extends VimProcessGroupBase {
   @Override
   public void startFilterCommand(@NotNull VimEditor editor, ExecutionContext context, @NotNull Command cmd) {
     String initText = getRange(((IjVimEditor) editor).getEditor(), cmd) + "!";
-    VimStateMachine.getInstance(editor).pushModes(VimStateMachine.Mode.CMD_LINE, VimStateMachine.SubMode.NONE);
+    VimStateMachine.Companion.getInstance(editor).setMode(Mode.CMD_LINE.INSTANCE);
     ExEntryPanel panel = ExEntryPanel.getInstance();
     panel.activate(((IjVimEditor) editor).getEditor(), ((IjEditorExecutionContext) context).getContext(), ":", initText, 1);
   }
 
   private @NotNull String getRange(Editor editor, @NotNull Command cmd) {
     String initText = "";
-    if (VimStateMachine.getInstance(new IjVimEditor(editor)).getMode() == VimStateMachine.Mode.VISUAL) {
+    if (VimStateMachine.Companion.getInstance(new IjVimEditor(editor)).getMode() instanceof Mode.VISUAL) {
       initText = "'<,'>";
     }
     else if (cmd.getRawCount() > 0) {
diff --git a/src/main/java/com/maddyhome/idea/vim/group/RegisterGroup.java b/src/main/java/com/maddyhome/idea/vim/group/RegisterGroup.java
index 26333edb5..5c9afcca3 100644
--- a/src/main/java/com/maddyhome/idea/vim/group/RegisterGroup.java
+++ b/src/main/java/com/maddyhome/idea/vim/group/RegisterGroup.java
@@ -14,7 +14,7 @@ import com.intellij.openapi.components.State;
 import com.intellij.openapi.components.Storage;
 import com.intellij.openapi.diagnostic.Logger;
 import com.maddyhome.idea.vim.VimPlugin;
-import com.maddyhome.idea.vim.command.SelectionType;
+import com.maddyhome.idea.vim.state.mode.SelectionType;
 import com.maddyhome.idea.vim.register.Register;
 import com.maddyhome.idea.vim.register.VimRegisterGroupBase;
 import org.jdom.Element;
@@ -50,7 +50,7 @@ public class RegisterGroup extends VimRegisterGroupBase implements PersistentSta
       }
       final Element registerElement = new Element("register");
       registerElement.setAttribute("name", String.valueOf(key));
-      registerElement.setAttribute("type", Integer.toString(register.getType().getValue()));
+      registerElement.setAttribute("type", register.getType().name());
       final String text = register.getText();
       if (text != null) {
         logger.trace("Save register as 'text'");
@@ -95,7 +95,25 @@ public class RegisterGroup extends VimRegisterGroupBase implements PersistentSta
         final Register register;
         final Element textElement = registerElement.getChild("text");
         final String typeText = registerElement.getAttributeValue("type");
-        final SelectionType type = SelectionType.fromValue(Integer.parseInt(typeText));
+        SelectionType type;
+        try {
+          type = SelectionType.valueOf(typeText);
+        }
+        catch (IllegalArgumentException e) {
+          // This whole `if` keeps compatibility with the mode when SelectionType had numbers
+          if (Integer.toString(1 << 1).equals(typeText)) {
+            type = SelectionType.CHARACTER_WISE;
+          }
+          else if (Integer.toString(1 << 2).equals(typeText)) {
+            type = SelectionType.LINE_WISE;
+          }
+          else if (Integer.toString(1 << 3).equals(typeText)) {
+            type = SelectionType.BLOCK_WISE;
+          }
+          else {
+            type = SelectionType.CHARACTER_WISE;
+          }
+        }
         if (textElement != null) {
           logger.trace("Register has 'text' element");
           final String text = VimPlugin.getXML().getSafeXmlText(textElement);
diff --git a/src/main/java/com/maddyhome/idea/vim/group/copy/PutGroup.kt b/src/main/java/com/maddyhome/idea/vim/group/copy/PutGroup.kt
index cc262c7d9..5431b9f4d 100644
--- a/src/main/java/com/maddyhome/idea/vim/group/copy/PutGroup.kt
+++ b/src/main/java/com/maddyhome/idea/vim/group/copy/PutGroup.kt
@@ -27,11 +27,10 @@ import com.maddyhome.idea.vim.api.getLineEndOffset
 import com.maddyhome.idea.vim.api.globalOptions
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.setChangeMarks
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
-import com.maddyhome.idea.vim.command.isBlock
-import com.maddyhome.idea.vim.command.isChar
-import com.maddyhome.idea.vim.command.isLine
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.isBlock
+import com.maddyhome.idea.vim.state.mode.isChar
+import com.maddyhome.idea.vim.state.mode.isLine
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.diagnostic.debug
 import com.maddyhome.idea.vim.helper.EditorHelper
@@ -74,7 +73,7 @@ internal class PutGroup : VimPutBase() {
     vimEditor: VimEditor,
     vimContext: ExecutionContext,
     text: ProcessedTextData,
-    subMode: VimStateMachine.SubMode,
+    subMode: SelectionType,
     data: PutData,
     additionalData: Map<String, Any>,
   ) {
diff --git a/src/main/java/com/maddyhome/idea/vim/group/visual/IdeaSelectionControl.kt b/src/main/java/com/maddyhome/idea/vim/group/visual/IdeaSelectionControl.kt
index 841a8e304..940d77558 100644
--- a/src/main/java/com/maddyhome/idea/vim/group/visual/IdeaSelectionControl.kt
+++ b/src/main/java/com/maddyhome/idea/vim/group/visual/IdeaSelectionControl.kt
@@ -15,22 +15,22 @@ import com.maddyhome.idea.vim.KeyHandler
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.options
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.helper.exitSelectMode
 import com.maddyhome.idea.vim.helper.exitVisualMode
 import com.maddyhome.idea.vim.helper.hasVisualSelection
 import com.maddyhome.idea.vim.helper.inInsertMode
 import com.maddyhome.idea.vim.helper.inNormalMode
-import com.maddyhome.idea.vim.helper.inSelectMode
-import com.maddyhome.idea.vim.helper.inVisualMode
 import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
 import com.maddyhome.idea.vim.helper.isTemplateActive
-import com.maddyhome.idea.vim.helper.mode
-import com.maddyhome.idea.vim.helper.popAllModes
 import com.maddyhome.idea.vim.helper.vimStateMachine
 import com.maddyhome.idea.vim.listener.VimListenerManager
 import com.maddyhome.idea.vim.newapi.vim
 import com.maddyhome.idea.vim.options.OptionConstants
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.inNormalMode
+import com.maddyhome.idea.vim.state.mode.inSelectMode
+import com.maddyhome.idea.vim.state.mode.inVisualMode
+import com.maddyhome.idea.vim.state.mode.mode
 import com.maddyhome.idea.vim.vimscript.model.options.helpers.IdeaRefactorModeHelper
 import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeKeep
 import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeSelect
@@ -70,17 +70,17 @@ internal object IdeaSelectionControl {
           return@singleTask
         }
 
-        logger.debug("Some carets have selection. State before adjustment: ${editor.vim.vimStateMachine.toSimpleString()}")
+        logger.debug("Some carets have selection. State before adjustment: ${editor.vim.mode}")
 
-        editor.popAllModes()
+        editor.vim.vimStateMachine.mode = Mode.NORMAL()
 
         activateMode(editor, chooseSelectionMode(editor, selectionSource, true))
       } else {
-        logger.debug("None of carets have selection. State before adjustment: ${editor.vim.vimStateMachine.toSimpleString()}")
+        logger.debug("None of carets have selection. State before adjustment: ${editor.vim.mode}")
         if (editor.vim.inVisualMode) editor.vim.exitVisualMode()
         if (editor.vim.inSelectMode) editor.exitSelectMode(false)
 
-        if (editor.vim.mode.inNormalMode) {
+        if (editor.vim.inNormalMode) {
           activateMode(editor, chooseNonSelectionMode(editor))
         }
       }
@@ -100,7 +100,7 @@ internal object IdeaSelectionControl {
    * This method is created to improve user experience. It allows avoiding delay in some operations
    *   (because [controlNonVimSelectionChange] is not executed immediately)
    */
-  fun predictMode(editor: Editor, selectionSource: VimListenerManager.SelectionSource): VimStateMachine.Mode {
+  fun predictMode(editor: Editor, selectionSource: VimListenerManager.SelectionSource): Mode {
     if (editor.selectionModel.hasSelection(true)) {
       if (dontChangeMode(editor)) return editor.vim.mode
       return chooseSelectionMode(editor, selectionSource, false)
@@ -109,17 +109,14 @@ internal object IdeaSelectionControl {
     }
   }
 
-  private fun activateMode(editor: Editor, mode: VimStateMachine.Mode) {
+  private fun activateMode(editor: Editor, mode: Mode) {
     when (mode) {
-      VimStateMachine.Mode.VISUAL -> VimPlugin.getVisualMotion()
-        .enterVisualMode(editor.vim, VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim))
-      VimStateMachine.Mode.SELECT -> VimPlugin.getVisualMotion()
-        .enterSelectMode(editor.vim, VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim))
-      VimStateMachine.Mode.INSERT -> VimPlugin.getChange().insertBeforeCursor(
-        editor.vim,
-        injector.executionContextManager.onEditor(editor.vim),
-      )
-      VimStateMachine.Mode.COMMAND -> Unit
+      is Mode.VISUAL -> VimPlugin.getVisualMotion().enterVisualMode(editor.vim, mode.selectionType)
+      is Mode.SELECT -> VimPlugin.getVisualMotion().enterSelectMode(editor.vim, mode.selectionType)
+      is Mode.INSERT -> VimPlugin.getChange()
+        .insertBeforeCursor(editor.vim, injector.executionContextManager.onEditor(editor.vim))
+
+      is Mode.NORMAL -> Unit
       else -> error("Unexpected mode: $mode")
     }
   }
@@ -127,40 +124,40 @@ internal object IdeaSelectionControl {
   private fun dontChangeMode(editor: Editor): Boolean =
     editor.isTemplateActive() && (editor.vim.isIdeaRefactorModeKeep || editor.vim.mode.hasVisualSelection)
 
-  private fun chooseNonSelectionMode(editor: Editor): VimStateMachine.Mode {
+  private fun chooseNonSelectionMode(editor: Editor): Mode {
     val templateActive = editor.isTemplateActive()
     if (templateActive && editor.vim.mode.inNormalMode || editor.inInsertMode) {
-      return VimStateMachine.Mode.INSERT
+      return Mode.INSERT
     }
-    return VimStateMachine.Mode.COMMAND
+    return Mode.NORMAL()
   }
 
   private fun chooseSelectionMode(
     editor: Editor,
     selectionSource: VimListenerManager.SelectionSource,
     logReason: Boolean,
-  ): VimStateMachine.Mode {
+  ): Mode {
     val selectmode = injector.options(editor.vim).selectmode
     return when {
       editor.isOneLineMode -> {
         if (logReason) logger.debug("Enter select mode. Reason: one line mode")
-        VimStateMachine.Mode.SELECT
+        Mode.SELECT(VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim))
       }
       selectionSource == VimListenerManager.SelectionSource.MOUSE && OptionConstants.selectmode_mouse in selectmode -> {
         if (logReason) logger.debug("Enter select mode. Selection source is mouse and selectMode option has mouse")
-        VimStateMachine.Mode.SELECT
+        Mode.SELECT(VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim))
       }
       editor.isTemplateActive() && editor.vim.isIdeaRefactorModeSelect -> {
         if (logReason) logger.debug("Enter select mode. Template is active and selectMode has template")
-        VimStateMachine.Mode.SELECT
+        Mode.SELECT(VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim))
       }
       selectionSource == VimListenerManager.SelectionSource.OTHER && OptionConstants.selectmode_ideaselection in selectmode -> {
         if (logReason) logger.debug("Enter select mode. Selection source is OTHER and selectMode has refactoring")
-        VimStateMachine.Mode.SELECT
+        Mode.SELECT(VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim))
       }
       else -> {
         if (logReason) logger.debug("Enter visual mode")
-        VimStateMachine.Mode.VISUAL
+        Mode.VISUAL(VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim))
       }
     }
   }
diff --git a/src/main/java/com/maddyhome/idea/vim/group/visual/VimVisualTimer.kt b/src/main/java/com/maddyhome/idea/vim/group/visual/VimVisualTimer.kt
index 6fb3fcb26..2a9415b75 100644
--- a/src/main/java/com/maddyhome/idea/vim/group/visual/VimVisualTimer.kt
+++ b/src/main/java/com/maddyhome/idea/vim/group/visual/VimVisualTimer.kt
@@ -9,7 +9,7 @@
 package com.maddyhome.idea.vim.group.visual
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.group.visual.VimVisualTimer.mode
 import com.maddyhome.idea.vim.group.visual.VimVisualTimer.singleTask
 import com.maddyhome.idea.vim.newapi.globalIjOptions
@@ -55,9 +55,9 @@ import javax.swing.Timer
 internal object VimVisualTimer {
 
   var swingTimer: Timer? = null
-  var mode: VimStateMachine.Mode? = null
+  var mode: Mode? = null
 
-  inline fun singleTask(currentMode: VimStateMachine.Mode, crossinline task: (initialMode: VimStateMachine.Mode?) -> Unit) {
+  inline fun singleTask(currentMode: Mode, crossinline task: (initialMode: Mode?) -> Unit) {
     swingTimer?.stop()
 
     if (mode == null) mode = currentMode
@@ -79,7 +79,7 @@ internal object VimVisualTimer {
     }
   }
 
-  inline fun timerAction(task: (initialMode: VimStateMachine.Mode?) -> Unit) {
+  inline fun timerAction(task: (initialMode: Mode?) -> Unit) {
     task(mode)
     swingTimer = null
     mode = null
diff --git a/src/main/java/com/maddyhome/idea/vim/group/visual/VisualGroup.kt b/src/main/java/com/maddyhome/idea/vim/group/visual/VisualGroup.kt
index 5b012e674..708fb3abe 100644
--- a/src/main/java/com/maddyhome/idea/vim/group/visual/VisualGroup.kt
+++ b/src/main/java/com/maddyhome/idea/vim/group/visual/VisualGroup.kt
@@ -12,16 +12,16 @@ import com.intellij.openapi.editor.Caret
 import com.intellij.openapi.editor.Editor
 import com.maddyhome.idea.vim.api.getLineEndForOffset
 import com.maddyhome.idea.vim.api.getLineStartForOffset
-import com.maddyhome.idea.vim.command.VimStateMachine
-import com.maddyhome.idea.vim.helper.inBlockSubMode
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.inBlockSelection
 import com.maddyhome.idea.vim.helper.isEndAllowed
 import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
 import com.maddyhome.idea.vim.helper.vimSelectionStart
 import com.maddyhome.idea.vim.newapi.IjVimEditor
 import com.maddyhome.idea.vim.newapi.vim
 
-internal fun moveCaretOneCharLeftFromSelectionEnd(editor: Editor, predictedMode: VimStateMachine.Mode) {
-  if (predictedMode != VimStateMachine.Mode.VISUAL) {
+internal fun moveCaretOneCharLeftFromSelectionEnd(editor: Editor, predictedMode: Mode) {
+  if (predictedMode !is Mode.VISUAL) {
     if (!editor.vim.isEndAllowed(predictedMode)) {
       editor.caretModel.allCarets.forEach { caret ->
         val lineEnd = IjVimEditor(editor).getLineEndForOffset(caret.offset)
@@ -49,5 +49,5 @@ internal fun moveCaretOneCharLeftFromSelectionEnd(editor: Editor, predictedMode:
 internal fun Caret.vimSetSelection(start: Int, end: Int = start, moveCaretToSelectionEnd: Boolean = false) {
   vimSelectionStart = start
   setVisualSelection(start, end, this.vim)
-  if (moveCaretToSelectionEnd && !editor.vim.inBlockSubMode) moveToInlayAwareOffset(end)
+  if (moveCaretToSelectionEnd && !editor.vim.inBlockSelection) moveToInlayAwareOffset(end)
 }
diff --git a/src/main/java/com/maddyhome/idea/vim/group/visual/VisualMotionGroup.kt b/src/main/java/com/maddyhome/idea/vim/group/visual/VisualMotionGroup.kt
index 5e91b155d..e3797ca6d 100644
--- a/src/main/java/com/maddyhome/idea/vim/group/visual/VisualMotionGroup.kt
+++ b/src/main/java/com/maddyhome/idea/vim/group/visual/VisualMotionGroup.kt
@@ -13,7 +13,7 @@ import com.intellij.openapi.editor.Editor
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.VimVisualMotionGroupBase
 import com.maddyhome.idea.vim.command.CommandState
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.command.engine
 import com.maddyhome.idea.vim.newapi.ij
 import com.maddyhome.idea.vim.newapi.vim
@@ -22,11 +22,11 @@ import com.maddyhome.idea.vim.newapi.vim
  * @author Alex Plate
  */
 internal class VisualMotionGroup : VimVisualMotionGroupBase() {
-  override fun autodetectVisualSubmode(editor: VimEditor): VimStateMachine.SubMode {
+  override fun autodetectVisualSubmode(editor: VimEditor): SelectionType {
     // IJ specific. See https://youtrack.jetbrains.com/issue/VIM-1924.
     val project = editor.ij.project
     if (project != null && FindManager.getInstance(project).selectNextOccurrenceWasPerformed()) {
-      return VimStateMachine.SubMode.VISUAL_CHARACTER
+      return SelectionType.CHARACTER_WISE
     }
 
     return super.autodetectVisualSubmode(editor)
diff --git a/src/main/java/com/maddyhome/idea/vim/helper/CaretVisualAttributesHelper.kt b/src/main/java/com/maddyhome/idea/vim/helper/CaretVisualAttributesHelper.kt
index fb61850c0..5dca3357a 100644
--- a/src/main/java/com/maddyhome/idea/vim/helper/CaretVisualAttributesHelper.kt
+++ b/src/main/java/com/maddyhome/idea/vim/helper/CaretVisualAttributesHelper.kt
@@ -16,13 +16,14 @@ import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.globalOptions
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.newapi.ij
 import com.maddyhome.idea.vim.newapi.vim
 import com.maddyhome.idea.vim.options.EffectiveOptionValueChangeListener
 import com.maddyhome.idea.vim.options.helpers.GuiCursorMode
 import com.maddyhome.idea.vim.options.helpers.GuiCursorOptionHelper
 import com.maddyhome.idea.vim.options.helpers.GuiCursorType
+import com.maddyhome.idea.vim.state.mode.inBlockSelection
+import com.maddyhome.idea.vim.state.mode.mode
 import org.jetbrains.annotations.TestOnly
 import java.awt.Color
 
@@ -66,28 +67,7 @@ internal object GuicursorChangeListener : EffectiveOptionValueChangeListener {
 }
 
 private fun Editor.guicursorMode(): GuiCursorMode {
-  if (this.vim.vimStateMachine.isReplaceCharacter) {
-    // Can be true for NORMAL and VISUAL
-    return GuiCursorMode.REPLACE
-  }
-
-  // Note that Vim does not change the caret for SELECT mode and continues to use VISUAL or VISUAL_EXCLUSIVE. IdeaVim
-  // makes much more use of SELECT than Vim does (e.g. it's the default for idearefactormode) so it makes sense for us
-  // to more visually distinguish VISUAL and SELECT. So we use INSERT; a selection and the insert caret is intuitively
-  // the same as SELECT
-  return when (vim.mode) {
-    VimStateMachine.Mode.COMMAND -> GuiCursorMode.NORMAL
-    VimStateMachine.Mode.VISUAL -> GuiCursorMode.VISUAL // TODO: VISUAL_EXCLUSIVE
-    VimStateMachine.Mode.SELECT -> GuiCursorMode.INSERT
-    VimStateMachine.Mode.INSERT -> GuiCursorMode.INSERT
-    VimStateMachine.Mode.OP_PENDING -> GuiCursorMode.OP_PENDING
-    VimStateMachine.Mode.REPLACE -> GuiCursorMode.REPLACE
-    // This doesn't handle ci and cr, but we don't care - our CMD_LINE will never call this
-    VimStateMachine.Mode.CMD_LINE -> GuiCursorMode.CMD_LINE
-    VimStateMachine.Mode.INSERT_NORMAL -> GuiCursorMode.NORMAL
-    VimStateMachine.Mode.INSERT_VISUAL -> GuiCursorMode.VISUAL
-    VimStateMachine.Mode.INSERT_SELECT -> GuiCursorMode.INSERT
-  }
+  return GuiCursorMode.fromMode(vim.mode, vim.vimStateMachine.isReplaceCharacter)
 }
 
 /**
@@ -107,7 +87,7 @@ private fun Editor.updatePrimaryCaretVisualAttributes() {
 
 private fun Editor.updateSecondaryCaretsVisualAttributes() {
   // IntelliJ simulates visual block with multiple carets with selections. Do our best to hide them
-  val attributes = if (this.vim.inBlockSubMode) HIDDEN else AttributesCache.getCaretVisualAttributes(this)
+  val attributes = if (this.vim.inBlockSelection) HIDDEN else AttributesCache.getCaretVisualAttributes(this)
   this.caretModel.allCarets.forEach {
     if (it != this.caretModel.primaryCaret) {
       it.visualAttributes = attributes
diff --git a/src/main/java/com/maddyhome/idea/vim/helper/CommandStateExtensions.kt b/src/main/java/com/maddyhome/idea/vim/helper/CommandStateExtensions.kt
index f15cceba6..6db61da5d 100644
--- a/src/main/java/com/maddyhome/idea/vim/helper/CommandStateExtensions.kt
+++ b/src/main/java/com/maddyhome/idea/vim/helper/CommandStateExtensions.kt
@@ -15,20 +15,17 @@ import com.maddyhome.idea.vim.api.Options
 import com.maddyhome.idea.vim.api.hasValue
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.CommandState
-import com.maddyhome.idea.vim.command.VimStateMachine
-import com.maddyhome.idea.vim.command.engine
-import com.maddyhome.idea.vim.command.ij
 import com.maddyhome.idea.vim.newapi.vim
 import com.maddyhome.idea.vim.options.OptionConstants
 import com.maddyhome.idea.vim.options.OptionScope
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.inVisualMode
+import com.maddyhome.idea.vim.state.mode.mode
 
-internal val VimStateMachine.Mode.hasVisualSelection
+internal val Mode.hasVisualSelection
   get() = when (this) {
-    VimStateMachine.Mode.VISUAL, VimStateMachine.Mode.SELECT -> true
-    VimStateMachine.Mode.REPLACE, VimStateMachine.Mode.CMD_LINE, VimStateMachine.Mode.COMMAND, VimStateMachine.Mode.INSERT, VimStateMachine.Mode.OP_PENDING -> false
-    VimStateMachine.Mode.INSERT_NORMAL -> false
-    VimStateMachine.Mode.INSERT_VISUAL -> true
-    VimStateMachine.Mode.INSERT_SELECT -> true
+    is Mode.VISUAL, is Mode.SELECT -> true
+    else -> false
   }
 
 /**
@@ -36,7 +33,18 @@ internal val VimStateMachine.Mode.hasVisualSelection
  * Please see: https://jb.gg/zo8n0r
  */
 public val Editor.mode: CommandState.Mode
-  get() = this.vim.vimStateMachine.mode.ij
+  get() {
+    val mode = this.vim.vimStateMachine.mode
+    return when (mode) {
+      Mode.CMD_LINE -> CommandState.Mode.CMD_LINE
+      Mode.INSERT -> CommandState.Mode.INSERT
+      is Mode.NORMAL -> CommandState.Mode.COMMAND
+      is Mode.OP_PENDING -> CommandState.Mode.OP_PENDING
+      Mode.REPLACE -> CommandState.Mode.REPLACE
+      is Mode.SELECT -> CommandState.Mode.SELECT
+      is Mode.VISUAL -> CommandState.Mode.VISUAL
+    }
+  }
 
 /**
  * COMPATIBILITY-LAYER: New method
@@ -51,21 +59,24 @@ public val CommandState.Mode.isEndAllowed: Boolean
       return injector.optionGroup.hasValue(Options.virtualedit, OptionScope.GLOBAL, OptionConstants.virtualedit_onemore)
     }
 
-    return when (this.engine) {
-      VimStateMachine.Mode.INSERT, VimStateMachine.Mode.VISUAL, VimStateMachine.Mode.SELECT -> true
-      VimStateMachine.Mode.COMMAND, VimStateMachine.Mode.CMD_LINE, VimStateMachine.Mode.REPLACE, VimStateMachine.Mode.OP_PENDING -> possiblyUsesVirtualSpace()
-      VimStateMachine.Mode.INSERT_NORMAL, VimStateMachine.Mode.INSERT_VISUAL, VimStateMachine.Mode.INSERT_SELECT -> possiblyUsesVirtualSpace()
+    return when (this) {
+      CommandState.Mode.INSERT, CommandState.Mode.VISUAL, CommandState.Mode.SELECT -> true
+      CommandState.Mode.COMMAND, CommandState.Mode.CMD_LINE, CommandState.Mode.REPLACE, CommandState.Mode.OP_PENDING -> possiblyUsesVirtualSpace()
+      CommandState.Mode.INSERT_NORMAL, CommandState.Mode.INSERT_VISUAL, CommandState.Mode.INSERT_SELECT -> possiblyUsesVirtualSpace()
     }
   }
 
-@get:JvmName("inNormalMode")
-public val VimStateMachine.Mode.inNormalMode: Boolean
-  get() = this == VimStateMachine.Mode.COMMAND || this == VimStateMachine.Mode.INSERT_NORMAL
+public val Mode.inNormalMode: Boolean
+  get() = this is Mode.NORMAL
 
 @get:JvmName("inInsertMode")
 public val Editor.inInsertMode: Boolean
-  get() = this.vim.mode == VimStateMachine.Mode.INSERT || this.vim.mode == VimStateMachine.Mode.REPLACE
+  get() = this.vim.mode == Mode.INSERT || this.vim.mode == Mode.REPLACE
 
 @get:JvmName("inVisualMode")
 public val Editor.inVisualMode: Boolean
-  get() = this.vim.mode.inVisualMode
+  get() = this.vim.inVisualMode
+
+@get:JvmName("inExMode")
+internal val Editor.inExMode
+  get() = this.vim.vimStateMachine.mode is Mode.CMD_LINE
diff --git a/src/main/java/com/maddyhome/idea/vim/helper/Helper.kt b/src/main/java/com/maddyhome/idea/vim/helper/Helper.kt
index d77b6590a..d3c71aae8 100644
--- a/src/main/java/com/maddyhome/idea/vim/helper/Helper.kt
+++ b/src/main/java/com/maddyhome/idea/vim/helper/Helper.kt
@@ -20,6 +20,7 @@ import com.intellij.openapi.project.Project
 import com.intellij.openapi.util.Key
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.newapi.vim
+import com.maddyhome.idea.vim.state.mode.inBlockSelection
 import java.util.stream.Collectors
 
 /**
@@ -63,7 +64,7 @@ internal fun <T : Comparable<T>> sort(a: T, b: T) = if (a > b) b to a else a to
 
 // TODO Should be replaced with VimEditor.carets()
 internal inline fun Editor.vimForEachCaret(action: (caret: Caret) -> Unit) {
-  if (this.vim.inBlockSubMode) {
+  if (this.vim.inBlockSelection) {
     action(this.caretModel.primaryCaret)
   } else {
     this.caretModel.allCarets.forEach(action)
diff --git a/src/main/java/com/maddyhome/idea/vim/helper/ModeExtensions.kt b/src/main/java/com/maddyhome/idea/vim/helper/ModeExtensions.kt
index fd9cfb250..730111c7b 100644
--- a/src/main/java/com/maddyhome/idea/vim/helper/ModeExtensions.kt
+++ b/src/main/java/com/maddyhome/idea/vim/helper/ModeExtensions.kt
@@ -17,28 +17,34 @@ import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.getLineEndForOffset
 import com.maddyhome.idea.vim.api.getLineStartForOffset
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
 import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
 import com.maddyhome.idea.vim.newapi.IjVimCaret
 import com.maddyhome.idea.vim.newapi.IjVimEditor
 import com.maddyhome.idea.vim.newapi.vim
-
-/**
- * Pop all modes, but leave editor state. E.g. editor selection is not removed.
- */
-internal fun Editor.popAllModes() {
-  val commandState = this.vim.vimStateMachine
-  while (commandState.mode != VimStateMachine.Mode.COMMAND) {
-    commandState.popModes()
-  }
-}
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.ReturnTo
+import com.maddyhome.idea.vim.state.mode.inSelectMode
+import com.maddyhome.idea.vim.state.mode.returnTo
 
 /** [adjustCaretPosition] - if true, caret will be moved one char left if it's on the line end */
 internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) {
   if (!this.vim.inSelectMode) return
 
-  this.vim.vimStateMachine.popModes()
+  val returnTo = this.vim.vimStateMachine.mode.returnTo
+  when (returnTo) {
+    ReturnTo.INSERT -> {
+      this.vim.vimStateMachine.mode = Mode.INSERT
+    }
+
+    ReturnTo.REPLACE -> {
+      this.vim.vimStateMachine.mode = Mode.REPLACE
+    }
+
+    null -> {
+      this.vim.vimStateMachine.mode = Mode.NORMAL()
+    }
+  }
   SelectionVimListenerSuppressor.lock().use {
     this.caretModel.allCarets.forEach {
       it.removeSelection()
@@ -58,7 +64,20 @@ internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) {
 internal fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) {
   if (!this.inSelectMode) return
 
-  this.vimStateMachine.popModes()
+  val returnTo = this.vimStateMachine.mode.returnTo
+  when (returnTo) {
+    ReturnTo.INSERT -> {
+      this.vimStateMachine.mode = Mode.INSERT
+    }
+
+    ReturnTo.REPLACE -> {
+      this.vimStateMachine.mode = Mode.REPLACE
+    }
+
+    null -> {
+      this.vimStateMachine.mode = Mode.NORMAL()
+    }
+  }
   SelectionVimListenerSuppressor.lock().use {
     this.carets().forEach { vimCaret ->
       val caret = (vimCaret as IjVimCaret).caret
diff --git a/src/main/java/com/maddyhome/idea/vim/helper/ScrollViewHelper.kt b/src/main/java/com/maddyhome/idea/vim/helper/ScrollViewHelper.kt
index 3447a2461..bb7b07ad7 100644
--- a/src/main/java/com/maddyhome/idea/vim/helper/ScrollViewHelper.kt
+++ b/src/main/java/com/maddyhome/idea/vim/helper/ScrollViewHelper.kt
@@ -14,7 +14,7 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.normalizeVisualColumn
 import com.maddyhome.idea.vim.api.options
 import com.maddyhome.idea.vim.command.CommandFlags
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.VimStateMachine
 import com.maddyhome.idea.vim.helper.EditorHelper.getApproximateScreenHeight
 import com.maddyhome.idea.vim.helper.EditorHelper.getApproximateScreenWidth
 import com.maddyhome.idea.vim.helper.EditorHelper.getNonNormalizedVisualLineAtBottomOfScreen
diff --git a/src/main/java/com/maddyhome/idea/vim/helper/SearchHelper.java b/src/main/java/com/maddyhome/idea/vim/helper/SearchHelper.java
index abbd01314..48d84cd34 100644
--- a/src/main/java/com/maddyhome/idea/vim/helper/SearchHelper.java
+++ b/src/main/java/com/maddyhome/idea/vim/helper/SearchHelper.java
@@ -23,7 +23,8 @@ import com.intellij.psi.util.PsiTreeUtil;
 import com.maddyhome.idea.vim.VimPlugin;
 import com.maddyhome.idea.vim.api.EngineEditorHelperKt;
 import com.maddyhome.idea.vim.api.VimEditor;
-import com.maddyhome.idea.vim.command.VimStateMachine;
+import com.maddyhome.idea.vim.state.mode.Mode;
+import com.maddyhome.idea.vim.state.VimStateMachine;
 import com.maddyhome.idea.vim.common.CharacterPosition;
 import com.maddyhome.idea.vim.common.Direction;
 import com.maddyhome.idea.vim.common.TextRange;
@@ -858,8 +859,8 @@ public class SearchHelper {
         selectionEndWithoutNewline++;
       }
 
-      final VimStateMachine.Mode mode = VimStateMachine.getInstance(new IjVimEditor(editor)).getMode();
-      if (mode == VimStateMachine.Mode.VISUAL) {
+      final Mode mode = VimStateMachine.Companion.getInstance(new IjVimEditor(editor)).getMode();
+      if (mode instanceof Mode.VISUAL) {
         if (closingTagTextRange.getStartOffset() == selectionEndWithoutNewline &&
           openingTag.getEndOffset() == selectionStart) {
           // Special case: if the inner tag is already selected we should like isOuter is active
diff --git a/src/main/java/com/maddyhome/idea/vim/helper/UserDataManager.kt b/src/main/java/com/maddyhome/idea/vim/helper/UserDataManager.kt
index a011f8921..8aaf50caa 100644
--- a/src/main/java/com/maddyhome/idea/vim/helper/UserDataManager.kt
+++ b/src/main/java/com/maddyhome/idea/vim/helper/UserDataManager.kt
@@ -21,8 +21,9 @@ import com.intellij.openapi.util.UserDataHolder
 import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
 import com.maddyhome.idea.vim.api.LocalMarkStorage
 import com.maddyhome.idea.vim.api.SelectionInfo
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.VimStateMachine
 import com.maddyhome.idea.vim.ex.ExOutputModel
 import com.maddyhome.idea.vim.group.visual.VisualChange
 import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset
@@ -127,7 +128,7 @@ internal var Editor.vimTestInputModel: TestInputModel? by userData()
  * Checks whether a keeping visual mode visual operator action is performed on editor.
  */
 internal var Editor.vimKeepingVisualOperatorAction: Boolean by userDataOr { false }
-internal var Editor.vimChangeActionSwitchMode: VimStateMachine.Mode? by userData()
+internal var Editor.vimChangeActionSwitchMode: Mode? by userData()
 
 /**
  * Function for delegated properties.
diff --git a/src/main/java/com/maddyhome/idea/vim/listener/IdeaSpecifics.kt b/src/main/java/com/maddyhome/idea/vim/listener/IdeaSpecifics.kt
index 74b034f1c..180637267 100644
--- a/src/main/java/com/maddyhome/idea/vim/listener/IdeaSpecifics.kt
+++ b/src/main/java/com/maddyhome/idea/vim/listener/IdeaSpecifics.kt
@@ -33,14 +33,13 @@ import com.maddyhome.idea.vim.KeyHandler
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.action.VimShortcutKeyAction
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.group.NotificationService
-import com.maddyhome.idea.vim.helper.inNormalMode
 import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
-import com.maddyhome.idea.vim.helper.mode
 import com.maddyhome.idea.vim.helper.vimStateMachine
 import com.maddyhome.idea.vim.newapi.globalIjOptions
 import com.maddyhome.idea.vim.newapi.vim
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.inNormalMode
 import com.maddyhome.idea.vim.vimscript.model.options.helpers.IdeaRefactorModeHelper
 import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeKeep
 import org.jetbrains.annotations.NonNls
@@ -125,9 +124,7 @@ internal object IdeaSpecifics {
       ) {
         editor?.let {
           val commandState = it.vim.vimStateMachine
-          while (commandState.mode != VimStateMachine.Mode.COMMAND) {
-            commandState.popModes()
-          }
+          commandState.mode = Mode.NORMAL()
           VimPlugin.getChange().insertBeforeCursor(it.vim, event.dataContext.vim)
           KeyHandler.getInstance().reset(it.vim)
         }
@@ -163,7 +160,7 @@ internal object IdeaSpecifics {
         if (!editor.selectionModel.hasSelection()) {
           // Enable insert mode if there is no selection in template
           // Template with selection is handled by [com.maddyhome.idea.vim.group.visual.VisualMotionGroup.controlNonVimSelectionChange]
-          if (editor.vim.mode.inNormalMode) {
+          if (editor.vim.inNormalMode) {
             VimPlugin.getChange().insertBeforeCursor(
               editor.vim,
               injector.executionContextManager.onEditor(editor.vim),
diff --git a/src/main/java/com/maddyhome/idea/vim/listener/VimListenerManager.kt b/src/main/java/com/maddyhome/idea/vim/listener/VimListenerManager.kt
index f3a125714..bb5363ad7 100644
--- a/src/main/java/com/maddyhome/idea/vim/listener/VimListenerManager.kt
+++ b/src/main/java/com/maddyhome/idea/vim/listener/VimListenerManager.kt
@@ -44,7 +44,6 @@ import com.maddyhome.idea.vim.api.Options
 import com.maddyhome.idea.vim.api.getLineEndForOffset
 import com.maddyhome.idea.vim.api.getLineStartForOffset
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.ex.ExOutputModel
 import com.maddyhome.idea.vim.group.EditorGroup
 import com.maddyhome.idea.vim.group.FileGroup
@@ -61,14 +60,12 @@ import com.maddyhome.idea.vim.helper.VimStandalonePluginUpdateChecker
 import com.maddyhome.idea.vim.helper.exitSelectMode
 import com.maddyhome.idea.vim.helper.exitVisualMode
 import com.maddyhome.idea.vim.helper.forceBarCursor
-import com.maddyhome.idea.vim.helper.inSelectMode
 import com.maddyhome.idea.vim.helper.inVisualMode
 import com.maddyhome.idea.vim.helper.isEndAllowed
 import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
 import com.maddyhome.idea.vim.helper.localEditors
 import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
 import com.maddyhome.idea.vim.helper.resetVimLastColumn
-import com.maddyhome.idea.vim.helper.subMode
 import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
 import com.maddyhome.idea.vim.helper.vimDisabled
 import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipEvents
@@ -76,6 +73,9 @@ import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipNDragEvents
 import com.maddyhome.idea.vim.listener.VimListenerManager.EditorListeners.add
 import com.maddyhome.idea.vim.newapi.IjVimEditor
 import com.maddyhome.idea.vim.newapi.vim
+import com.maddyhome.idea.vim.state.mode.inSelectMode
+import com.maddyhome.idea.vim.state.mode.mode
+import com.maddyhome.idea.vim.state.mode.selectionType
 import com.maddyhome.idea.vim.ui.ShowCmdOptionChangeListener
 import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
 import java.awt.event.MouseAdapter
@@ -447,7 +447,7 @@ internal object VimListenerManager {
         ExOutputModel.getInstance(editor).clear()
 
         val caretModel = editor.caretModel
-        if (editor.vim.subMode != VimStateMachine.SubMode.NONE) {
+        if (editor.vim.mode.selectionType != null) {
           caretModel.removeSecondaryCarets()
         }
 
diff --git a/src/main/java/com/maddyhome/idea/vim/newapi/IjVimCaret.kt b/src/main/java/com/maddyhome/idea/vim/newapi/IjVimCaret.kt
index 414383b08..59bd4cac8 100644
--- a/src/main/java/com/maddyhome/idea/vim/newapi/IjVimCaret.kt
+++ b/src/main/java/com/maddyhome/idea/vim/newapi/IjVimCaret.kt
@@ -22,7 +22,7 @@ import com.maddyhome.idea.vim.api.VimCaret
 import com.maddyhome.idea.vim.api.VimCaretBase
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.VimVisualPosition
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.EditorLine
 import com.maddyhome.idea.vim.common.LiveRange
 import com.maddyhome.idea.vim.common.Offset
diff --git a/src/main/java/com/maddyhome/idea/vim/newapi/IjVimEditor.kt b/src/main/java/com/maddyhome/idea/vim/newapi/IjVimEditor.kt
index 464cb2fa0..3e8c6ae29 100644
--- a/src/main/java/com/maddyhome/idea/vim/newapi/IjVimEditor.kt
+++ b/src/main/java/com/maddyhome/idea/vim/newapi/IjVimEditor.kt
@@ -34,9 +34,9 @@ import com.maddyhome.idea.vim.api.VimScrollingModel
 import com.maddyhome.idea.vim.api.VimSelectionModel
 import com.maddyhome.idea.vim.api.VimVisualPosition
 import com.maddyhome.idea.vim.api.VirtualFile
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.EditorLine
 import com.maddyhome.idea.vim.common.IndentConfig
 import com.maddyhome.idea.vim.common.LiveRange
@@ -50,7 +50,8 @@ import com.maddyhome.idea.vim.helper.exitInsertMode
 import com.maddyhome.idea.vim.helper.exitSelectMode
 import com.maddyhome.idea.vim.helper.fileSize
 import com.maddyhome.idea.vim.helper.getTopLevelEditor
-import com.maddyhome.idea.vim.helper.inBlockSubMode
+import com.maddyhome.idea.vim.state.mode.inBlockSelection
+import com.maddyhome.idea.vim.helper.inExMode
 import com.maddyhome.idea.vim.helper.isTemplateActive
 import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
 import com.maddyhome.idea.vim.helper.updateCaretsVisualPosition
@@ -70,7 +71,7 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
   val originalEditor = editor
 
   override val lfMakesNewLine: Boolean = true
-  override var vimChangeActionSwitchMode: VimStateMachine.Mode?
+  override var vimChangeActionSwitchMode: Mode?
     get() = editor.vimChangeActionSwitchMode
     set(value) {
       editor.vimChangeActionSwitchMode = value
@@ -139,7 +140,7 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
   }
 
   override fun carets(): List<VimCaret> {
-    return if (editor.vim.inBlockSubMode) {
+    return if (editor.vim.inBlockSelection || (editor.inExMode && editor.vim.vimLastSelectionType == SelectionType.BLOCK_WISE)) {
       listOf(IjVimCaret(editor.caretModel.primaryCaret))
     } else {
       editor.caretModel.allCarets.map { IjVimCaret(it) }
@@ -152,7 +153,7 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
 
   @Suppress("ideavimRunForEachCaret")
   override fun forEachCaret(action: (VimCaret) -> Unit) {
-    if (editor.vim.inBlockSubMode) {
+    if (editor.vim.inBlockSelection) {
       action(IjVimCaret(editor.caretModel.primaryCaret))
     } else {
       editor.caretModel.runForEachCaret({ action(IjVimCaret(it)) }, false)
diff --git a/src/main/java/com/maddyhome/idea/vim/newapi/IjVimInjector.kt b/src/main/java/com/maddyhome/idea/vim/newapi/IjVimInjector.kt
index 135c671f3..b2d07f5e0 100644
--- a/src/main/java/com/maddyhome/idea/vim/newapi/IjVimInjector.kt
+++ b/src/main/java/com/maddyhome/idea/vim/newapi/IjVimInjector.kt
@@ -53,7 +53,6 @@ import com.maddyhome.idea.vim.api.VimrcFileState
 import com.maddyhome.idea.vim.api.VimscriptExecutor
 import com.maddyhome.idea.vim.api.VimscriptFunctionService
 import com.maddyhome.idea.vim.api.VimscriptParser
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.diagnostic.VimLogger
 import com.maddyhome.idea.vim.ex.ExOutputModel
 import com.maddyhome.idea.vim.extension.VimExtensionRegistrar
@@ -82,6 +81,8 @@ import com.maddyhome.idea.vim.history.VimHistory
 import com.maddyhome.idea.vim.macro.VimMacro
 import com.maddyhome.idea.vim.put.VimPut
 import com.maddyhome.idea.vim.register.VimRegisterGroup
+import com.maddyhome.idea.vim.state.VimStateMachine
+import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
 import com.maddyhome.idea.vim.ui.VimRcFileState
 import com.maddyhome.idea.vim.undo.VimUndoRedo
 import com.maddyhome.idea.vim.vimscript.Executor
@@ -197,7 +198,7 @@ internal class IjVimInjector : VimInjectorBase() {
   override fun commandStateFor(editor: VimEditor): VimStateMachine {
     var res = editor.ij.vimStateMachine
     if (res == null) {
-      res = VimStateMachine(editor)
+      res = VimStateMachineImpl(editor)
       editor.ij.vimStateMachine = res
     }
     return res
diff --git a/src/main/java/com/maddyhome/idea/vim/vimscript/model/functions/handlers/ColLineFunctionHandler.kt b/src/main/java/com/maddyhome/idea/vim/vimscript/model/functions/handlers/ColLineFunctionHandler.kt
index b5deac4a7..f341041f1 100644
--- a/src/main/java/com/maddyhome/idea/vim/vimscript/model/functions/handlers/ColLineFunctionHandler.kt
+++ b/src/main/java/com/maddyhome/idea/vim/vimscript/model/functions/handlers/ColLineFunctionHandler.kt
@@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.api.lineLength
 import com.maddyhome.idea.vim.api.options
 import com.maddyhome.idea.vim.api.visualLineToBufferLine
 import com.maddyhome.idea.vim.helper.EditorHelper
-import com.maddyhome.idea.vim.helper.inVisualMode
+import com.maddyhome.idea.vim.state.mode.inVisualMode
 import com.maddyhome.idea.vim.helper.vimLine
 import com.maddyhome.idea.vim.newapi.ij
 import com.maddyhome.idea.vim.vimscript.model.VimLContext
diff --git a/src/main/java/com/maddyhome/idea/vim/vimscript/model/options/helpers/IdeaRefactorModeHelper.kt b/src/main/java/com/maddyhome/idea/vim/vimscript/model/options/helpers/IdeaRefactorModeHelper.kt
index 788a9b535..ab24fa552 100644
--- a/src/main/java/com/maddyhome/idea/vim/vimscript/model/options/helpers/IdeaRefactorModeHelper.kt
+++ b/src/main/java/com/maddyhome/idea/vim/vimscript/model/options/helpers/IdeaRefactorModeHelper.kt
@@ -20,11 +20,13 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.group.IjOptionConstants
 import com.maddyhome.idea.vim.helper.hasBlockOrUnderscoreCaret
 import com.maddyhome.idea.vim.helper.hasVisualSelection
-import com.maddyhome.idea.vim.helper.mode
-import com.maddyhome.idea.vim.helper.subMode
+import com.maddyhome.idea.vim.helper.vimStateMachine
 import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
 import com.maddyhome.idea.vim.newapi.ijOptions
 import com.maddyhome.idea.vim.newapi.vim
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.mode
+import com.maddyhome.idea.vim.state.mode.selectionType
 
 public val VimEditor.isIdeaRefactorModeKeep: Boolean
   get() = injector.ijOptions(this).idearefactormode.contains(IjOptionConstants.idearefactormode_keep)
@@ -36,16 +38,22 @@ internal object IdeaRefactorModeHelper {
 
   fun correctSelection(editor: Editor) {
     val action: () -> Unit = {
-      if (!editor.vim.mode.hasVisualSelection && editor.selectionModel.hasSelection()) {
+      val mode = editor.vim.mode
+      if (!mode.hasVisualSelection && editor.selectionModel.hasSelection()) {
         SelectionVimListenerSuppressor.lock().use {
           editor.selectionModel.removeSelection()
         }
       }
-      if (editor.vim.mode.hasVisualSelection && editor.selectionModel.hasSelection()) {
+      if (mode.hasVisualSelection && editor.selectionModel.hasSelection()) {
         val autodetectedSubmode = VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim)
-        if (editor.vim.subMode != autodetectedSubmode) {
+        if (mode.selectionType != autodetectedSubmode) {
           // Update the submode
-          editor.vim.subMode = autodetectedSubmode
+          val newMode = when (mode) {
+            is Mode.SELECT -> mode.copy(selectionType = autodetectedSubmode)
+            is Mode.VISUAL -> mode.copy(selectionType = autodetectedSubmode)
+            else -> error("IdeaVim should be either in visual or select modes")
+          }
+          editor.vim.vimStateMachine.mode = newMode
         }
       }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/NeovimTesting.kt b/src/test/java/org/jetbrains/plugins/ideavim/NeovimTesting.kt
index bc0600232..ce14b260a 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/NeovimTesting.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/NeovimTesting.kt
@@ -15,7 +15,6 @@ import com.ensarsarajcic.neovim.java.corerpc.client.ProcessRpcConnection
 import com.intellij.openapi.editor.Editor
 import com.intellij.openapi.editor.LogicalPosition
 import com.maddyhome.idea.vim.VimPlugin
-import com.maddyhome.idea.vim.command.SelectionType
 import com.maddyhome.idea.vim.common.CharacterPosition
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import com.maddyhome.idea.vim.helper.vimStateMachine
@@ -28,6 +27,8 @@ import com.maddyhome.idea.vim.register.RegisterConstants.EXPRESSION_BUFFER_REGIS
 import com.maddyhome.idea.vim.register.RegisterConstants.LAST_INSERTED_TEXT_REGISTER
 import com.maddyhome.idea.vim.register.RegisterConstants.LAST_SEARCH_REGISTER
 import com.maddyhome.idea.vim.register.RegisterConstants.VALID_REGISTERS
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.toVimNotation
 import org.junit.Assert.assertEquals
 import org.junit.jupiter.api.TestInfo
 
@@ -159,9 +160,11 @@ internal object NeovimTesting {
     assertEquals(neovimContent, editor.document.text)
   }
 
+  public fun vimMode() = neovimApi.mode.get().mode
+
   private fun assertMode(editor: Editor) {
-    val ideavimState = editor.vim.vimStateMachine.toVimNotation()
-    val neovimState = neovimApi.mode.get().mode
+    val ideavimState = editor.vim.vimStateMachine.mode.toVimNotation()
+    val neovimState = vimMode()
     assertEquals(neovimState, ideavimState)
   }
 
@@ -178,7 +181,7 @@ internal object NeovimTesting {
     for (register in VALID_REGISTERS) {
       if (register in nonCheckingRegisters) continue
       if (register in VimTestCase.Checks.neoVim.ignoredRegisters) continue
-      val neovimRegister = neovimApi.callFunction("getreg", listOf(register)).get().toString()
+      val neovimRegister = getRegister(register)
       val vimPluginRegister = VimPlugin.getRegister().getRegister(register)
       val ideavimRegister = vimPluginRegister?.text ?: ""
       assertEquals("Register '$register'", neovimRegister, ideavimRegister)
@@ -198,6 +201,9 @@ internal object NeovimTesting {
       }
     }
   }
+
+  public fun getRegister(register: Char) = neovimApi.callFunction("getreg", listOf(register)).get().toString()
+  public fun getMark(register: String) = neovimApi.callFunction("getpos", listOf(register)).get().toString()
 }
 
 annotation class TestWithoutNeovim(val reason: SkipNeovimReason, val description: String = "")
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/RegisterActionsTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/RegisterActionsTest.kt
index 5743fcf33..883028dd2 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/RegisterActionsTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/RegisterActionsTest.kt
@@ -11,7 +11,7 @@ package org.jetbrains.plugins.ideavim
 import com.maddyhome.idea.vim.RegisterActions.VIM_ACTIONS_EP
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.command.MappingMode
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.handler.ActionBeanClass
 import com.maddyhome.idea.vim.key.CommandNode
 import com.maddyhome.idea.vim.key.CommandPartNode
@@ -29,7 +29,7 @@ class RegisterActionsTest : VimTestCase() {
   fun `test simple action`() {
     val before = "I ${c}found it in a legendary land"
     val after = "I f${c}ound it in a legendary land"
-    doTest("l", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("l", before, after, Mode.NORMAL())
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.EDITOR_MODIFICATION)
@@ -41,7 +41,7 @@ class RegisterActionsTest : VimTestCase() {
       }
       val before = "I ${c}found it in a legendary land"
       val after = "I jklwB${c}found it in a legendary land"
-      doTest("jklwB", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE) {
+      doTest("jklwB", before, after, Mode.NORMAL()) {
         VimPlugin.setEnabled(false)
       }
     } finally {
@@ -57,7 +57,7 @@ class RegisterActionsTest : VimTestCase() {
   fun `test turn plugin off and on`() {
     val before = "I ${c}found it in a legendary land"
     val after = "I f${c}ound it in a legendary land"
-    doTest("l", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE) {
+    doTest("l", before, after, Mode.NORMAL()) {
       VimPlugin.setEnabled(false)
       VimPlugin.setEnabled(true)
     }
@@ -71,7 +71,7 @@ class RegisterActionsTest : VimTestCase() {
   fun `test enable twice`() {
     val before = "I ${c}found it in a legendary land"
     val after = "I f${c}ound it in a legendary land"
-    doTest("l", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE) {
+    doTest("l", before, after, Mode.NORMAL()) {
       VimPlugin.setEnabled(false)
       VimPlugin.setEnabled(true)
       VimPlugin.setEnabled(true)
@@ -87,7 +87,7 @@ class RegisterActionsTest : VimTestCase() {
     val before = "I ${c}found it in a legendary land"
     val after = "I f${c}ound it in a legendary land"
     var motionRightAction: ActionBeanClass? = null
-    doTest("l", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE) {
+    doTest("l", before, after, Mode.NORMAL()) {
       motionRightAction =
         VIM_ACTIONS_EP.getExtensionList(null).first { it.actionId == "VimPreviousTabAction" }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/TestHelper.kt b/src/test/java/org/jetbrains/plugins/ideavim/TestHelper.kt
index 5a34d1584..1ed848ada 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/TestHelper.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/TestHelper.kt
@@ -15,9 +15,9 @@ import com.intellij.testFramework.EditorTestUtil
 import com.intellij.testFramework.fixtures.CodeInsightTestFixture
 import com.intellij.util.containers.toArray
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.common.TextRange
-import com.maddyhome.idea.vim.helper.mode
+import com.maddyhome.idea.vim.state.mode.mode
 import com.maddyhome.idea.vim.newapi.globalIjOptions
 import com.maddyhome.idea.vim.newapi.vim
 import org.junit.jupiter.params.provider.Arguments
@@ -68,7 +68,7 @@ inline fun waitAndAssert(timeInMillis: Int = 1000, condition: () -> Boolean) {
 
 fun waitAndAssertMode(
   fixture: CodeInsightTestFixture,
-  mode: VimStateMachine.Mode,
+  mode: Mode,
   timeInMillis: Int? = null,
 ) {
   val timeout = timeInMillis ?: (injector.globalIjOptions().visualdelay + 1000)
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/VimTestCase.kt b/src/test/java/org/jetbrains/plugins/ideavim/VimTestCase.kt
index d44252f20..5a3b53133 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/VimTestCase.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/VimTestCase.kt
@@ -48,8 +48,7 @@ import com.maddyhome.idea.vim.api.options
 import com.maddyhome.idea.vim.api.setToggleOption
 import com.maddyhome.idea.vim.api.visualLineToBufferLine
 import com.maddyhome.idea.vim.command.MappingMode
-import com.maddyhome.idea.vim.command.VimStateMachine
-import com.maddyhome.idea.vim.command.VimStateMachine.SubMode
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.ex.ExException
 import com.maddyhome.idea.vim.ex.ExOutputModel.Companion.getInstance
 import com.maddyhome.idea.vim.group.GlobalIjOptions
@@ -59,9 +58,8 @@ import com.maddyhome.idea.vim.helper.EditorHelper
 import com.maddyhome.idea.vim.helper.RunnableHelper.runWriteCommand
 import com.maddyhome.idea.vim.helper.TestInputModel
 import com.maddyhome.idea.vim.helper.getGuiCursorMode
-import com.maddyhome.idea.vim.helper.inBlockSubMode
-import com.maddyhome.idea.vim.helper.mode
-import com.maddyhome.idea.vim.helper.subMode
+import com.maddyhome.idea.vim.state.mode.inBlockSelection
+import com.maddyhome.idea.vim.state.mode.mode
 import com.maddyhome.idea.vim.key.MappingOwner
 import com.maddyhome.idea.vim.key.ToKeysMappingInfo
 import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
@@ -263,6 +261,13 @@ abstract class VimTestCase {
     return fixture.editor
   }
 
+  public fun configureByTextX(fileName: String, content: String): Editor {
+    fixture.configureByText(fileName, content)
+    NeovimTesting.setupEditor(fixture.editor, testInfo)
+    setEditorVisibleSize(screenWidth, screenHeight)
+    return fixture.editor
+  }
+
   protected fun configureByFileName(fileName: String): Editor {
     fixture.configureByText(fileName, "\n")
     NeovimTesting.setupEditor(fixture.editor, testInfo)
@@ -393,9 +398,16 @@ abstract class VimTestCase {
     NeovimTesting.assertState(fixture.editor, testInfo)
   }
 
-  protected fun assertState(modeAfter: VimStateMachine.Mode, subModeAfter: SubMode) {
+  fun mode(): String? {
+    return NeovimTesting.vimMode()
+  }
+
+  fun register(char: String): String? {
+    return NeovimTesting.getMark(char)
+  }
+
+  protected fun assertState(modeAfter: Mode) {
     assertMode(modeAfter)
-    assertSubMode(subModeAfter)
     assertCaretsVisualAttributes()
   }
 
@@ -517,16 +529,11 @@ abstract class VimTestCase {
     }
   }
 
-  fun assertMode(expectedMode: VimStateMachine.Mode) {
+  fun assertMode(expectedMode: Mode) {
     val mode = fixture.editor.vim.mode
     assertEquals(expectedMode, mode)
   }
 
-  fun assertSubMode(expectedSubMode: SubMode) {
-    val subMode = fixture.editor.vim.subMode
-    assertEquals(expectedSubMode, subMode)
-  }
-
   fun assertSelection(expected: String?) {
     val selected = fixture.editor.selectionModel.selectedText
     assertEquals(expected, selected)
@@ -565,7 +572,7 @@ abstract class VimTestCase {
 
     editor.caretModel.allCarets.forEach { caret ->
       // All carets should be the same except when in block sub mode, where we "hide" them (by drawing a zero width bar)
-      if (caret !== editor.caretModel.primaryCaret && editor.vim.inBlockSubMode) {
+      if (caret !== editor.caretModel.primaryCaret && editor.vim.inBlockSelection) {
         assertEquals(CaretVisualAttributes.Shape.BAR, caret.visualAttributes.shape)
         assertEquals(0F, caret.visualAttributes.thickness)
       } else {
@@ -591,8 +598,7 @@ abstract class VimTestCase {
     keys: List<String>,
     before: String,
     after: String,
-    modeAfter: VimStateMachine.Mode = VimStateMachine.Mode.COMMAND,
-    subModeAfter: SubMode = SubMode.NONE,
+    modeAfter: Mode = Mode.NORMAL(),
     fileType: FileType? = null,
     fileName: String? = null,
     afterEditorInitialized: ((Editor) -> Unit)? = null,
@@ -602,7 +608,6 @@ abstract class VimTestCase {
       before,
       after,
       modeAfter,
-      subModeAfter,
       fileType,
       fileName,
       afterEditorInitialized,
@@ -614,8 +619,7 @@ abstract class VimTestCase {
     keys: String,
     before: String,
     after: String,
-    modeAfter: VimStateMachine.Mode = VimStateMachine.Mode.COMMAND,
-    subModeAfter: SubMode = SubMode.NONE,
+    modeAfter: Mode = Mode.NORMAL(),
     fileType: FileType? = null,
     fileName: String? = null,
     afterEditorInitialized: ((Editor) -> Unit)? = null,
@@ -628,14 +632,14 @@ abstract class VimTestCase {
       configureByText(before)
     }
     afterEditorInitialized?.invoke(fixture.editor)
-    performTest(keys, after, modeAfter, subModeAfter)
+    performTest(keys, after, modeAfter)
   }
 
-  protected fun performTest(keys: String, after: String, modeAfter: VimStateMachine.Mode, subModeAfter: SubMode) {
+  protected fun performTest(keys: String, after: String, modeAfter: Mode) {
     typeText(keys)
     PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue()
     assertState(after)
-    assertState(modeAfter, subModeAfter)
+    assertState(modeAfter)
   }
 
   protected fun setRegister(register: Char, keys: String) {
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/ChangeActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/ChangeActionTest.kt
index fdc8fb708..8a8d67546 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/ChangeActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/ChangeActionTest.kt
@@ -10,7 +10,9 @@ package org.jetbrains.plugins.ideavim.action
 import com.intellij.codeInsight.folding.CodeFoldingManager
 import com.intellij.codeInsight.folding.impl.FoldingUtil
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.ReturnTo
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -29,8 +31,7 @@ class ChangeActionTest : VimTestCase() {
       listOf("i", "<C-O>", "a", "123", "<Esc>", "x"),
       "abc${c}d\n",
       "abcd12\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -41,8 +42,7 @@ class ChangeActionTest : VimTestCase() {
       listOf("i", "<C-O>", "o", "123", "<Esc>", "x"),
       "abc${c}d\n",
       "abcd\n12\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -53,8 +53,7 @@ class ChangeActionTest : VimTestCase() {
       listOf("i", "<C-O>", "v"),
       "12${c}345",
       "12${s}${c}3${se}45",
-      VimStateMachine.Mode.INSERT_VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE, ReturnTo.INSERT)
     )
   }
 
@@ -65,8 +64,7 @@ class ChangeActionTest : VimTestCase() {
       listOf("i", "<C-O>", "v", "<esc>"),
       "12${c}345",
       "12${c}345",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -77,8 +75,7 @@ class ChangeActionTest : VimTestCase() {
       listOf("i", "<C-O>", "v", "d"),
       "12${c}345",
       "12${c}45",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -90,8 +87,7 @@ class ChangeActionTest : VimTestCase() {
       listOf("i", "<C-O>", "v", "<C-G>"),
       "12${c}345",
       "12${s}3${c}${se}45",
-      VimStateMachine.Mode.INSERT_SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE, ReturnTo.INSERT),
     )
   }
 
@@ -103,8 +99,7 @@ class ChangeActionTest : VimTestCase() {
       listOf("i", "<C-O>", "gh"),
       "12${c}345",
       "12${s}3${c}${se}45",
-      VimStateMachine.Mode.INSERT_SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE, ReturnTo.INSERT),
     )
   }
 
@@ -116,8 +111,7 @@ class ChangeActionTest : VimTestCase() {
       listOf("i", "<C-O>", "gh", "<esc>"),
       "12${c}345",
       "123${c}45",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -130,8 +124,7 @@ class ChangeActionTest : VimTestCase() {
       listOf("i", "<C-O>", "gh", "d"),
       "12${c}345",
       "12d${c}45",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -142,32 +135,31 @@ class ChangeActionTest : VimTestCase() {
       listOf("i", "def", "<C-O>", "d2h", "x"),
       "abc$c.\n",
       "abcdx.\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
   // VIM-321 |d| |count|
   @Test
   fun testDeleteEmptyRange() {
-    doTest("d0", "${c}hello\n", "hello\n", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("d0", "${c}hello\n", "hello\n", Mode.NORMAL())
   }
 
   // VIM-157 |~|
   @Test
   fun testToggleCharCase() {
-    doTest("~~", "${c}hello world\n", "HEllo world\n", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("~~", "${c}hello world\n", "HEllo world\n", Mode.NORMAL())
   }
 
   // VIM-157 |~|
   @Test
   fun testToggleCharCaseLineEnd() {
-    doTest("~~", "hello wor${c}ld\n", "hello worLD\n", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("~~", "hello wor${c}ld\n", "hello worLD\n", Mode.NORMAL())
   }
 
   @Test
   fun testToggleCaseMotion() {
-    doTest("g~w", "${c}FooBar Baz\n", "fOObAR Baz\n", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("g~w", "${c}FooBar Baz\n", "fOObAR Baz\n", Mode.NORMAL())
   }
 
   @Test
@@ -176,14 +168,13 @@ class ChangeActionTest : VimTestCase() {
       "gUw",
       "${c}FooBar Baz\n",
       "FOOBAR Baz\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
   @Test
   fun testChangeLowerCase() {
-    doTest("guw", "${c}FooBar Baz\n", "foobar Baz\n", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("guw", "${c}FooBar Baz\n", "foobar Baz\n", Mode.NORMAL())
   }
 
   @Test
@@ -192,8 +183,7 @@ class ChangeActionTest : VimTestCase() {
       "ve~",
       "${c}FooBar Baz\n",
       "fOObAR Baz\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -203,8 +193,7 @@ class ChangeActionTest : VimTestCase() {
       "veU",
       "${c}FooBar Baz\n",
       "FOOBAR Baz\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -214,8 +203,7 @@ class ChangeActionTest : VimTestCase() {
       "veu",
       "${c}FooBar Baz\n",
       "foobar Baz\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -236,8 +224,7 @@ class ChangeActionTest : VimTestCase() {
    four
    
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -256,8 +243,7 @@ class ChangeActionTest : VimTestCase() {
         
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertOffset(4)
   }
@@ -277,8 +263,7 @@ class ChangeActionTest : VimTestCase() {
    three
    
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -299,8 +284,7 @@ class ChangeActionTest : VimTestCase() {
    three
    
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -315,8 +299,7 @@ class ChangeActionTest : VimTestCase() {
       """one 
  three
 """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertOffset(3)
   }
@@ -332,8 +315,7 @@ class ChangeActionTest : VimTestCase() {
    
       """.trimIndent(),
       "one four\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -344,8 +326,7 @@ class ChangeActionTest : VimTestCase() {
       "d2w",
       "on${c}e two three\n",
       "on${c}three\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -360,8 +341,7 @@ class ChangeActionTest : VimTestCase() {
       """foo
   , baz
 """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -380,8 +360,7 @@ class ChangeActionTest : VimTestCase() {
    baz
    
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -400,8 +379,7 @@ class ChangeActionTest : VimTestCase() {
         bar
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertOffset(1)
   }
@@ -417,8 +395,7 @@ class ChangeActionTest : VimTestCase() {
    
       """.trimIndent(),
       "two\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -430,8 +407,7 @@ class ChangeActionTest : VimTestCase() {
       listOf("A", ", ", "<C-R>", "a", "!"),
       "${c}Hello\n",
       "Hello, World!\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -442,8 +418,7 @@ class ChangeActionTest : VimTestCase() {
       listOf("O", "bar"),
       "fo${c}o\n",
       "bar\nfoo\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -454,8 +429,7 @@ class ChangeActionTest : VimTestCase() {
       listOf("v", "k\$d"),
       "foo\n${c}bar\n",
       "fooar\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -478,8 +452,7 @@ class ChangeActionTest : VimTestCase() {
         quux
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -501,8 +474,7 @@ class ChangeActionTest : VimTestCase() {
         quux
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -518,8 +490,7 @@ quux
       """    a 1 b 2 c 3
 quux
 """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -535,8 +506,7 @@ quux
       """    a 1    b 2    c 3
 quux
 """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -551,8 +521,7 @@ quux
         bar
       """.dotToSpace().trimIndent(),
       "foo bar",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -565,8 +534,7 @@ quux
         bar
       """.dotToSpace().trimIndent(),
       "foo  bar",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -582,8 +550,7 @@ quux
       """    a 1 b 2 c 3
 quux
 """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -599,8 +566,7 @@ quux
       """    a 1    b 2    c 3
 quux
 """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -610,8 +576,7 @@ quux
       listOf("<C-V>", "x"),
       "fo${c}o\n",
       "fo\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -624,8 +589,7 @@ quux
       listOf("<C-V>", "j", "x"),
       "\n\n",
       "\n\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -646,8 +610,7 @@ quux
         br
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -669,8 +632,7 @@ quux
         br
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -697,20 +659,20 @@ quux
   // |r|
   @Test
   fun testReplaceOneChar() {
-    doTest("rx", "b${c}ar\n", "b${c}xr\n", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("rx", "b${c}ar\n", "b${c}xr\n", Mode.NORMAL())
   }
 
   // |r|
   @VimBehaviorDiffers(originalVimAfter = "foXX${c}Xr\n")
   @Test
   fun testReplaceMultipleCharsWithCount() {
-    doTest("3rX", "fo${c}obar\n", "fo${c}XXXr\n", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("3rX", "fo${c}obar\n", "fo${c}XXXr\n", Mode.NORMAL())
   }
 
   // |r|
   @Test
   fun testReplaceMultipleCharsWithCountPastEndOfLine() {
-    doTest("6rX", "fo${c}obar\n", "fo${c}obar\n", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("6rX", "fo${c}obar\n", "fo${c}obar\n", Mode.NORMAL())
   }
 
   // |r|
@@ -729,8 +691,7 @@ quux
         ZZZZZz
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -746,8 +707,7 @@ foobaz
     bar
 foobaz
 """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -764,15 +724,14 @@ foobaz
     r
 foobaz
 """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
   // |s|
   @Test
   fun testReplaceOneCharWithText() {
-    doTest("sxy<Esc>", "b${c}ar\n", "bx${c}yr\n", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("sxy<Esc>", "b${c}ar\n", "bx${c}yr\n", Mode.NORMAL())
   }
 
   // |s|
@@ -782,8 +741,7 @@ foobaz
       "3sxy<Esc>",
       "fo${c}obar\n",
       "fox${c}yr\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -802,15 +760,14 @@ foobaz
         biff
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
   // |R|
   @Test
   fun testReplaceMode() {
-    doTest("Rbaz<Esc>", "foo${c}bar\n", "fooba${c}z\n", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("Rbaz<Esc>", "foo${c}bar\n", "fooba${c}z\n", Mode.NORMAL())
   }
 
   // |R| |i_<Insert>|
@@ -821,8 +778,7 @@ foobaz
       "RXXX<Ins>YYY<Ins>ZZZ<Esc>",
       "aaa${c}bbbcccddd\n",
       "aaaXXXYYYZZ${c}Zddd\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -834,8 +790,7 @@ foobaz
       "iXXX<Ins>YYY<Ins>ZZZ<Esc>",
       "aaa${c}bbbcccddd\n",
       "aaaXXXYYYZZ${c}Zcccddd\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -855,8 +810,7 @@ foobaz
         fo${c}o quux
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -1074,8 +1028,7 @@ and some text after""",
       "fXcfYPATATA<Esc>fX.;.",
       "${c}aaaaXBBBBYaaaaaaaXBBBBYaaaaaaXBBBBYaaaaaaaa\n",
       "aaaaPATATAaaaaaaaPATATAaaaaaaPATATAaaaaaaaa\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -1083,10 +1036,10 @@ and some text after""",
   fun testRepeatReplace() {
     configureByText("${c}foobarbaz spam\n")
     typeText(injector.parser.parseKeys("R"))
-    assertMode(VimStateMachine.Mode.REPLACE)
+    assertMode(Mode.REPLACE)
     typeText(injector.parser.parseKeys("FOO" + "<Esc>" + "l" + "2."))
     assertState("FOOFOOFO${c}O spam\n")
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @Test
@@ -1101,8 +1054,7 @@ and some text after""",
         psum dolor sit amet
         ${c}Lorem Ipsumm dolor sit amet
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -1118,8 +1070,7 @@ and some text after""",
         ipsum dolor sit amet
         ${c}Lorem Ipsumm dolor sit amet
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -1135,8 +1086,7 @@ and some text after""",
         ipsum dolor sit amet
         ${c}Lorem Ipsumm dolor sit amet
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -1152,8 +1102,7 @@ and some text after""",
         ipsum dolor sit amet
         ${c}Lorem Ipsumm dolor sit amet
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -1169,8 +1118,7 @@ and some text after""",
         ${c}Lorem Ipsumm dolor sit amet
         psum dolor sit amet
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -1186,8 +1134,7 @@ and some text after""",
         ${c}Lorem Ipsumm dolor sit amet
         ipsum dolor sit amet
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -1206,8 +1153,7 @@ and some text after""",
         ${c}lorem ipsum dolor sit amet
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -1227,8 +1173,7 @@ and some text after""",
         gaganis ${c}gaganis gaganis
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -1245,8 +1190,7 @@ and some text after""",
         line 1
         ${c}line 3
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/ChangeNumberActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/ChangeNumberActionTest.kt
index 6321fe4a0..3dcd80677 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/ChangeNumberActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/ChangeNumberActionTest.kt
@@ -8,7 +8,7 @@
 package org.jetbrains.plugins.ideavim.action
 
 import com.google.common.collect.Lists
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.TestOptionConstants
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.jetbrains.plugins.ideavim.impl.OptionTest
@@ -18,27 +18,27 @@ import org.junit.jupiter.api.Test
 class ChangeNumberActionTest : VimTestCase() {
   @Test
   fun testIncrementDecimalZero() {
-    doTest("<C-A>", "0", "1", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-A>", "0", "1", Mode.NORMAL())
   }
 
   @Test
   fun testIncrementHexZero() {
-    doTest("<C-A>", "0x0", "0x1", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-A>", "0x0", "0x1", Mode.NORMAL())
   }
 
   @Test
   fun testDecrementZero() {
-    doTest("<C-X>", "0", "-1", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-X>", "0", "-1", Mode.NORMAL())
   }
 
   @Test
   fun testIncrementDecimal() {
-    doTest("<C-A>", "199", "200", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-A>", "199", "200", Mode.NORMAL())
   }
 
   @Test
   fun testDecrementDecimal() {
-    doTest("<C-X>", "1000", "999", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-X>", "1000", "999", Mode.NORMAL())
   }
 
   @Test
@@ -47,8 +47,7 @@ class ChangeNumberActionTest : VimTestCase() {
       Lists.newArrayList(":set nf=octal<Enter>", "<C-A>"),
       "0477",
       "0500",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -58,29 +57,28 @@ class ChangeNumberActionTest : VimTestCase() {
       Lists.newArrayList(":set nf=octal<Enter>", "<C-X>"),
       "010",
       "007",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
   @Test
   fun testIncrementHex() {
-    doTest("<C-A>", "0xff", "0x100", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-A>", "0xff", "0x100", Mode.NORMAL())
   }
 
   @Test
   fun testDecrementHex() {
-    doTest("<C-X>", "0xa100", "0xa0ff", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-X>", "0xa100", "0xa0ff", Mode.NORMAL())
   }
 
   @Test
   fun testIncrementNegativeDecimal() {
-    doTest("<C-A>", "-199", "-198", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-A>", "-199", "-198", Mode.NORMAL())
   }
 
   @Test
   fun testDecrementNegativeDecimal() {
-    doTest("<C-X>", "-1000", "-1001", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-X>", "-1000", "-1001", Mode.NORMAL())
   }
 
   @Test
@@ -90,8 +88,7 @@ class ChangeNumberActionTest : VimTestCase() {
       Lists.newArrayList(":set nf=octal<Enter>", "<C-A>"),
       "-0477",
       "-0500",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -102,34 +99,33 @@ class ChangeNumberActionTest : VimTestCase() {
       Lists.newArrayList(":set nf=octal<Enter>", "<C-X>"),
       "-010",
       "-007",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
   @Test
   fun testIncrementNegativeHex() {
-    doTest("<C-A>", "-0xff", "-0x100", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-A>", "-0xff", "-0x100", Mode.NORMAL())
   }
 
   @Test
   fun testDecrementNegativeHex() {
-    doTest("<C-X>", "-0xa100", "-0xa0ff", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-X>", "-0xa100", "-0xa0ff", Mode.NORMAL())
   }
 
   @Test
   fun testIncrementWithCount() {
-    doTest("123<C-A>", "456", "579", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("123<C-A>", "456", "579", Mode.NORMAL())
   }
 
   @Test
   fun testDecrementWithCount() {
-    doTest("200<C-X>", "100", "-100", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("200<C-X>", "100", "-100", Mode.NORMAL())
   }
 
   @Test
   fun testIncrementAlphaWithoutNumberFormatAlpha() {
-    doTest("<C-A>", "foo", "foo", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-A>", "foo", "foo", Mode.NORMAL())
   }
 
   @Test
@@ -138,8 +134,7 @@ class ChangeNumberActionTest : VimTestCase() {
       Lists.newArrayList(":set nf=alpha<Enter>", "<C-A>"),
       "foo",
       "goo",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -149,8 +144,7 @@ class ChangeNumberActionTest : VimTestCase() {
       Lists.newArrayList(":set nf=alpha<Enter>", "<C-A>"),
       "zzz",
       "zzz",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -160,8 +154,7 @@ class ChangeNumberActionTest : VimTestCase() {
       Lists.newArrayList(":set nf=alpha<Enter>", "<C-A>"),
       "0<caret>x1",
       "0y1",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -171,8 +164,7 @@ class ChangeNumberActionTest : VimTestCase() {
       Lists.newArrayList(":set nf=alpha,hex<Enter>", "<C-A>"),
       "0<caret>x1",
       "0x2",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -182,8 +174,7 @@ class ChangeNumberActionTest : VimTestCase() {
       Lists.newArrayList(":set nf=octal<Enter>", "<C-A>"),
       "0x42",
       "1x42",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -193,8 +184,7 @@ class ChangeNumberActionTest : VimTestCase() {
       Lists.newArrayList(":set nf=hex<Enter>", "<C-A>"),
       "077",
       "078",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -204,19 +194,18 @@ class ChangeNumberActionTest : VimTestCase() {
       Lists.newArrayList(":set nf=hex<Enter>", "<C-A>"),
       "-077",
       "-076",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
   @Test
   fun testIncrementHexPreservesCaseOfX() {
-    doTest("<C-A>", "0X88", "0X89", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-A>", "0X88", "0X89", Mode.NORMAL())
   }
 
   @Test
   fun testIncrementHexTakesCaseFromLastLetter() {
-    doTest("<C-A>", "0xaB0", "0xAB1", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-A>", "0xaB0", "0xAB1", Mode.NORMAL())
   }
 
   @Test
@@ -225,8 +214,7 @@ class ChangeNumberActionTest : VimTestCase() {
       "<C-A>",
       "foo ->* bar 123\n",
       "foo ->* bar 12<caret>4\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/CopyActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/CopyActionTest.kt
index fbc2ec6a9..33cd3fb94 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/CopyActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/CopyActionTest.kt
@@ -10,7 +10,7 @@ package org.jetbrains.plugins.ideavim.action
 import com.intellij.idea.TestFor
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -227,7 +227,7 @@ class CopyActionTest : VimTestCase() {
       """.trimIndent(),
     )
     assertOffset(0)
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     assertSelection(null)
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/GuardedBlocksTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/GuardedBlocksTest.kt
index 1fb51bd6a..d479be764 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/GuardedBlocksTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/GuardedBlocksTest.kt
@@ -9,7 +9,7 @@
 package org.jetbrains.plugins.ideavim.action
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -151,7 +151,7 @@ class GuardedBlocksTest : VimTestCase() {
       1234567890
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.INSERT)
+    assertMode(Mode.INSERT)
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.GUARDED_BLOCKS)
@@ -173,7 +173,7 @@ class GuardedBlocksTest : VimTestCase() {
       1234567890
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.INSERT)
+    assertMode(Mode.INSERT)
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.GUARDED_BLOCKS)
@@ -195,7 +195,7 @@ class GuardedBlocksTest : VimTestCase() {
       1234567890
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.INSERT)
+    assertMode(Mode.INSERT)
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.GUARDED_BLOCKS)
@@ -217,7 +217,7 @@ class GuardedBlocksTest : VimTestCase() {
       1234567890
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.INSERT)
+    assertMode(Mode.INSERT)
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.GUARDED_BLOCKS)
@@ -239,7 +239,7 @@ class GuardedBlocksTest : VimTestCase() {
       1234567890
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.INSERT)
+    assertMode(Mode.INSERT)
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.GUARDED_BLOCKS)
@@ -259,7 +259,7 @@ class GuardedBlocksTest : VimTestCase() {
       $c
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.INSERT)
+    assertMode(Mode.INSERT)
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.GUARDED_BLOCKS)
@@ -278,7 +278,7 @@ class GuardedBlocksTest : VimTestCase() {
       1234567890
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.GUARDED_BLOCKS)
@@ -299,7 +299,7 @@ class GuardedBlocksTest : VimTestCase() {
       1234567890
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.GUARDED_BLOCKS)
@@ -320,7 +320,7 @@ class GuardedBlocksTest : VimTestCase() {
       1234567890
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.INSERT)
+    assertMode(Mode.INSERT)
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.GUARDED_BLOCKS)
@@ -340,6 +340,6 @@ class GuardedBlocksTest : VimTestCase() {
       1234567890
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/MarkTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/MarkTest.kt
index 491d3c02e..3fdf38cbc 100755
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/MarkTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/MarkTest.kt
@@ -10,7 +10,7 @@ package org.jetbrains.plugins.ideavim.action
 import com.google.common.collect.Lists
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.handler.enableOctopus
 import com.maddyhome.idea.vim.newapi.IjVimEditor
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
@@ -293,8 +293,7 @@ class MarkTest : VimTestCase() {
                 four five
                 
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/MotionActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/MotionActionTest.kt
index a0e0a7fc1..f4b4b07e8 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/MotionActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/MotionActionTest.kt
@@ -9,7 +9,8 @@ package org.jetbrains.plugins.ideavim.action
 
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
 import com.maddyhome.idea.vim.helper.vimStateMachine
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
@@ -24,7 +25,7 @@ class MotionActionTest : VimTestCase() {
   @Test
   fun testDoubleToggleVisual() {
     val contents = "one tw${c}o\n"
-    doTest("vv", contents, contents, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("vv", contents, contents, Mode.NORMAL())
   }
 
   // VIM-198 |v_iw|
@@ -35,8 +36,7 @@ class MotionActionTest : VimTestCase() {
       "viw",
       fileContents,
       "one ${s}two${se}\n",
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -45,7 +45,7 @@ class MotionActionTest : VimTestCase() {
   fun testVisualMotionInnerBigWord() {
     val fileContents = "one tw${c}o.three four\n"
     val fileContentsAfter = "one ${s}two.thre${c}e$se four\n"
-    doTest("viW", fileContents, fileContentsAfter, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest("viW", fileContents, fileContentsAfter, Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertSelection("two.three")
   }
 
@@ -56,7 +56,7 @@ class MotionActionTest : VimTestCase() {
      three
      
     """.trimIndent()
-    doTest(listOf("f", "<Esc>", "<Esc>"), content, content, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("f", "<Esc>", "<Esc>"), content, content, Mode.NORMAL())
     assertPluginError(true)
     assertOffset(2)
   }
@@ -68,7 +68,7 @@ class MotionActionTest : VimTestCase() {
      three
      
     """.trimIndent()
-    doTest(listOf("12", "<Esc>"), content, content, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("12", "<Esc>"), content, content, Mode.NORMAL())
     assertPluginError(false)
     val vimCommandState = fixture.editor.vimStateMachine
     kotlin.test.assertNotNull(vimCommandState)
@@ -80,7 +80,7 @@ class MotionActionTest : VimTestCase() {
   fun testLeftRightMove() {
     val before = "on${c}e two three four five six seven\n"
     val after = "one two three ${c}four five six seven\n"
-    doTest(listOf("14l", "2h"), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("14l", "2h"), before, after, Mode.NORMAL())
   }
 
   // |j| |k|
@@ -100,7 +100,7 @@ class MotionActionTest : VimTestCase() {
      four
      
     """.trimIndent()
-    doTest(listOf("2j", "k"), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("2j", "k"), before, after, Mode.NORMAL())
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.UNCLEAR)
@@ -115,7 +115,7 @@ class MotionActionTest : VimTestCase() {
   fun testForwardToTab() {
     val before = "on${c}e two\tthree\nfour\n"
     val after = "one two${c}\tthree\nfour\n"
-    doTest(listOf("f<Tab>"), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("f<Tab>"), before, after, Mode.NORMAL())
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.UNCLEAR)
@@ -123,7 +123,7 @@ class MotionActionTest : VimTestCase() {
   fun testIllegalCharArgument() {
     typeTextInFile(injector.parser.parseKeys("f<Insert>"), "on${c}e two three four five six seven\n")
     assertOffset(2)
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   // |F| |i_CTRL-K|
@@ -132,7 +132,7 @@ class MotionActionTest : VimTestCase() {
     val before = "Hallo, Öster${c}reich!\n"
     val after = "Hallo, ${c}Österreich!\n"
     val keys = listOf("F<C-K>O:")
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-771 |t| |;|
@@ -141,7 +141,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("t:;")
     val before = "$c 1:a 2:b 3:c \n"
     val after = " 1:a ${c}2:b 3:c \n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-771 |t| |;|
@@ -150,7 +150,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("t:;")
     val before = "$c 1:a 2:b 3:c \n"
     val after = " 1:a ${c}2:b 3:c \n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-771 |t| |;|
@@ -159,7 +159,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("t:2;")
     val before = "$c 1:a 2:b 3:c \n"
     val after = " 1:a ${c}2:b 3:c \n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-771 |t| |;|
@@ -168,7 +168,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("t:3;")
     val before = "$c 1:a 2:b 3:c \n"
     val after = " 1:a 2:b ${c}3:c \n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-771 |t| |,|
@@ -177,7 +177,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("t:,,")
     val before = " 1:a 2:b$c 3:c \n"
     val after = " 1:${c}a 2:b 3:c \n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-771 |t| |,|
@@ -186,7 +186,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("t:,2,")
     val before = " 1:a 2:b$c 3:c \n"
     val after = " 1:${c}a 2:b 3:c \n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-771 |t| |,|
@@ -195,7 +195,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("t:,3,")
     val before = " 0:_ 1:a 2:b$c 3:c \n"
     val after = " 0:${c}_ 1:a 2:b 3:c \n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-314 |d| |v_iB|
@@ -204,7 +204,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di{")
     val before = "{foo, b${c}ar, baz}\n"
     val after = "{}\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-314 |d| |v_iB|
@@ -213,7 +213,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di{")
     val before = "{foo, ${c}\"bar\", baz}\n"
     val after = "{}\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_aB|
@@ -222,7 +222,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("da{")
     val before = "x = {foo, b${c}ar, baz};\n"
     val after = "x = ;\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-261 |c| |v_iB|
@@ -253,7 +253,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("daw")
     val before = "one t${c}wo three\n"
     val after = "one three\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_aW|
@@ -262,7 +262,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("daW")
     val before = "one \"t${c}wo\" three\n"
     val after = "one three\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_is|
@@ -271,7 +271,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("dis")
     val before = "Hello World! How a${c}re you? Bye.\n"
     val after = "Hello World!  Bye.\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_as|
@@ -280,7 +280,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("das")
     val before = "Hello World! How a${c}re you? Bye.\n"
     val after = "Hello World! Bye.\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |v_as|
@@ -299,7 +299,7 @@ class MotionActionTest : VimTestCase() {
      P$c.
      
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_ip|
@@ -322,7 +322,7 @@ class MotionActionTest : VimTestCase() {
     Bye.
     
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_ap|
@@ -344,7 +344,7 @@ class MotionActionTest : VimTestCase() {
     Bye.
     
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_a]|
@@ -358,7 +358,7 @@ class MotionActionTest : VimTestCase() {
 ];
 """
     val after = "foo = ;\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_i]|
@@ -367,7 +367,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di]")
     val before = "foo = [one, t${c}wo];\n"
     val after = "foo = [];\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-1287 |d| |v_i(|
@@ -376,7 +376,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di(")
     val before = "(text \"with quotes(and ${c}braces)\")"
     val after = "(text \"with quotes()\")"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-1287 |d| |v_i{|
@@ -385,7 +385,7 @@ class MotionActionTest : VimTestCase() {
     val before = "{\"{foo, ${c}bar\", baz}}"
     val keys = listOf("di{")
     val after = "{\"{foo, ${c}bar\", baz}}"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-1287 |d| |v_i{|
@@ -394,7 +394,7 @@ class MotionActionTest : VimTestCase() {
     val before = "a{\"{foo}, ${c}bar\", baz}b}"
     val keys = listOf("di{")
     val after = "a{$c}b}"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-1008 |c| |v_i{|
@@ -403,7 +403,7 @@ class MotionActionTest : VimTestCase() {
     val before = "\"{do${c}esn't work}\""
     val keys = listOf("ci{")
     val after = "\"{$c}\""
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
   }
 
   // VIM-1008 |c| |v_i{|
@@ -412,7 +412,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("ci{")
     val before = "'{does n${c}ot work}'"
     val after = "'{$c}'"
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
   }
 
   // VIM-1008 |c| |v_i{|
@@ -421,7 +421,7 @@ class MotionActionTest : VimTestCase() {
     val before = "<p class=\"{{ \$ctrl.so${c}meClassName }}\"></p>"
     val keys = listOf("ci{")
     val after = "<p class=\"{{$c}}\"></p>"
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
   }
 
   // |d| |v_i>|
@@ -430,7 +430,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di>")
     val before = "Foo<Foo, B${c}ar> bar\n"
     val after = "Foo<> bar\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_a>|
@@ -439,7 +439,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("da>")
     val before = "Foo<Foo, B${c}ar> bar\n"
     val after = "Foo bar\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-132 |d| |v_i"|
@@ -448,7 +448,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di\"")
     val before = "foo = \"bar b${c}az\";\n"
     val after = "foo = \"\";\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-132 |d| |v_a"|
@@ -458,7 +458,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("da\"")
     val before = "foo = \"bar b${c}az\";\n"
     val after = "foo = ;\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-132 |d| |v_i"|
@@ -467,7 +467,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di\"")
     val before = "foo = [\"one\", ${c}\"two\", \"three\"];\n"
     val after = "foo = [\"one\", \"\", \"three\"];\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-132 |d| |v_i"|
@@ -476,7 +476,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di\"")
     val before = "foo = [\"one\", \"two${c}\", \"three\"];\n"
     val after = "foo = [\"one\", \"\", \"three\"];\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-132 |d| |v_i"|
@@ -485,7 +485,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di\"")
     val before = "foo = \"fo\\\"o b${c}ar\";\n"
     val after = "foo = \"\";\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-132 |d| |v_i"|
@@ -494,7 +494,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di\"")
     val before = "f${c}oo = [\"one\", \"two\", \"three\"];\n"
     val after = "foo = [\"\", \"two\", \"three\"];\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -502,7 +502,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di\"")
     val before = "abc\"def${c}\"gh\"i"
     val after = "abc\"\"gh\"i"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -510,7 +510,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di\"")
     val before = "abc\"def\"g${c}h\"ijk\"l"
     val after = "abc\"def\"\"ijk\"l"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -518,7 +518,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di\"")
     val before = "abcdef\"gh\"ij${c}\"kl"
     val after = "abcdef\"gh\"ij\"kl"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -526,7 +526,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di\"")
     val before = "abc\"def\"gh\"ij${c}\"kl"
     val after = "abc\"def\"gh\"\"kl"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-132 |v_i"|
@@ -535,7 +535,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("vi\"")
     val before = "foo = [\"o${c}ne\", \"two\"];\n"
     val after = "foo = [\"${s}on${c}e${se}\", \"two\"];\n"
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   // |c| |v_i"|
@@ -544,7 +544,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("ci\"")
     val before = "foo = \"${c}\";\n"
     val after = "foo = \"\";\n"
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
   }
 
   // VIM-132 |d| |v_i'|
@@ -553,7 +553,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di'")
     val before = "foo = 'bar b${c}az';\n"
     val after = "foo = '';\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-132 |d| |v_i`|
@@ -562,7 +562,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("di`")
     val before = "foo = `bar b${c}az`;\n"
     val after = "foo = ``;\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-132 |d| |v_a'|
@@ -572,7 +572,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("da'")
     val before = "foo = 'bar b${c}az';\n"
     val after = "foo = ;\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-132 |d| |v_a`|
@@ -582,7 +582,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("da`")
     val before = "foo = `bar b${c}az`;\n"
     val after = "foo = ;\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-2733
@@ -599,7 +599,7 @@ class MotionActionTest : VimTestCase() {
 
       print($c)
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
 // VIM-2733
@@ -616,7 +616,7 @@ class MotionActionTest : VimTestCase() {
 
       print($c)
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-2733
@@ -633,7 +633,7 @@ class MotionActionTest : VimTestCase() {
 
       print($c)
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-2733
@@ -650,7 +650,7 @@ class MotionActionTest : VimTestCase() {
 
       print($c)
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
 // VIM-1427
@@ -659,7 +659,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("d2at")
     val before = "<a><b><c>$c</c></b></a>"
     val after = "<a></a>"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-2113
@@ -668,7 +668,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("cit")
     val before = "<a><c>$c</c></a>"
     val after = "<a><c></c></a>"
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
   }
 
   @Test
@@ -676,7 +676,7 @@ class MotionActionTest : VimTestCase() {
     val keys = listOf("d/<C-K>O:<CR>")
     val before = "ab${c}cdÖef"
     val after = "abÖef"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |[(|
@@ -948,7 +948,7 @@ two
     val keys = listOf("viw", "<Esc>", "0", "viw", "gv", "d")
     val before = "foo ${c}bar\n"
     val after = "foo \n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |CTRL-V|
@@ -965,7 +965,7 @@ two
     ${s}ba${se}r
     
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_BLOCK)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.BLOCK_WISE))
   }
 
   // |CTRL-V|
@@ -982,7 +982,7 @@ two
     b${s}ar$se
     
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_BLOCK)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.BLOCK_WISE))
   }
 
   // |CTRL-V|
@@ -1001,7 +1001,7 @@ two
     a${s}b$se
     
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_BLOCK)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.BLOCK_WISE))
   }
 
   // |v_o|
@@ -1010,7 +1010,7 @@ two
     val keys = listOf("v", "l", "o", "l", "d")
     val before = "${c}foo\n"
     val after = "fo\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-564 |g_|
@@ -1032,8 +1032,7 @@ two
                 four  
                 
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -1056,8 +1055,7 @@ two
                 four  
                 
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -1078,7 +1076,7 @@ two
     bar
     
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |v_>| |gv|
@@ -1103,7 +1101,7 @@ two
       """.trimIndent(),
     )
     typeText(injector.parser.parseKeys(">"))
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     assertState(
       """    foo
     bar
@@ -1118,7 +1116,7 @@ two
 """,
     )
     typeText(injector.parser.parseKeys(">"))
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     assertState(
       """        foo
         bar
@@ -1148,7 +1146,7 @@ two
     bar
     
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @Test
@@ -1163,7 +1161,7 @@ two
      
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertSelection(
       """
     bar
@@ -1187,7 +1185,7 @@ two
      
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertSelection(
       """
     bar
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/MultipleCaretsTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/MultipleCaretsTest.kt
index e9dc35ce3..5d51dcd9b 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/MultipleCaretsTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/MultipleCaretsTest.kt
@@ -11,7 +11,7 @@ import com.intellij.idea.TestFor
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import com.maddyhome.idea.vim.newapi.IjVimEditor
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/ResetModeActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/ResetModeActionTest.kt
index c26d0537d..52ff6dbca 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/ResetModeActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/ResetModeActionTest.kt
@@ -11,7 +11,7 @@ package org.jetbrains.plugins.ideavim.action
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.MappingMode
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.key.MappingOwner
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -26,7 +26,7 @@ class ResetModeActionTest : VimTestCase() {
     val keys = "<C-\\><C-N>"
     val before = "Lorem Ipsum"
     val after = "Lorem Ipsum"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(fixture.editor.selectionModel.hasSelection())
   }
 
@@ -35,7 +35,7 @@ class ResetModeActionTest : VimTestCase() {
     val keys = listOf("i", "<C-\\><C-N>")
     val before = "Lorem Ipsum"
     val after = "Lorem Ipsum"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(fixture.editor.selectionModel.hasSelection())
   }
 
@@ -44,7 +44,7 @@ class ResetModeActionTest : VimTestCase() {
     val keys = listOf("i", "<C-\\><C-N>")
     val before = "A Disc${c}overy"
     val after = "A Dis${c}covery"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(fixture.editor.selectionModel.hasSelection())
   }
 
@@ -53,7 +53,7 @@ class ResetModeActionTest : VimTestCase() {
     val keys = listOf("i", "<C-\\><C-N>", "3l")
     val before = "${c}Lorem Ipsum"
     val after = "Lorem Ipsum"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(fixture.editor.selectionModel.hasSelection())
   }
 
@@ -62,7 +62,7 @@ class ResetModeActionTest : VimTestCase() {
     val keys = listOf("V", "<C-\\><C-N>")
     val before = "Lorem Ipsum"
     val after = "Lorem Ipsum"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(fixture.editor.selectionModel.hasSelection())
   }
 
@@ -71,7 +71,7 @@ class ResetModeActionTest : VimTestCase() {
     val keys = listOf("gH", "<C-\\><C-N>")
     val before = "Lorem Ipsum"
     val after = "Lorem Ipsum"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(fixture.editor.selectionModel.hasSelection())
   }
 
@@ -80,7 +80,7 @@ class ResetModeActionTest : VimTestCase() {
     val keys = listOf("d", "<C-\\><C-N>")
     val before = "Lorem Ipsum"
     val after = "Lorem Ipsum"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(fixture.editor.selectionModel.hasSelection())
   }
 
@@ -89,7 +89,7 @@ class ResetModeActionTest : VimTestCase() {
     val keys = "d<Esc>dw"
     val before = "Lorem Ipsum"
     val after = "Ipsum"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(fixture.editor.selectionModel.hasSelection())
   }
 
@@ -98,7 +98,7 @@ class ResetModeActionTest : VimTestCase() {
     val keys = listOf("d", "<C-\\><C-N>", "dw")
     val before = "Lorem Ipsum"
     val after = "Ipsum"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(fixture.editor.selectionModel.hasSelection())
   }
 
@@ -107,7 +107,7 @@ class ResetModeActionTest : VimTestCase() {
     val keys = listOf("d", "<Esc>", "dw")
     val before = "Lorem Ipsum"
     val after = "Ipsum"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(fixture.editor.selectionModel.hasSelection())
   }
 
@@ -117,7 +117,7 @@ class ResetModeActionTest : VimTestCase() {
     val keys = listOf("d", "<C-[>", "dw")
     val before = "Lorem Ipsum"
     val after = "Ipsum"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(fixture.editor.selectionModel.hasSelection())
   }
 
@@ -136,7 +136,7 @@ class ResetModeActionTest : VimTestCase() {
     val keys = listOf("d", "<C-D>", "dw")
     val before = "Lorem Ipsum"
     val after = "Ipsum"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(fixture.editor.selectionModel.hasSelection())
   }
 
@@ -145,7 +145,7 @@ class ResetModeActionTest : VimTestCase() {
     val keys = listOf("c", "<C-\\><C-N>", "another")
     val before = "Lorem Ipsum"
     val after = "Lnotherorem Ipsum"
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
     kotlin.test.assertFalse(fixture.editor.selectionModel.hasSelection())
   }
 
@@ -154,7 +154,7 @@ class ResetModeActionTest : VimTestCase() {
     val keys = "dt<esc>D"
     val before = "A ${c}Discovery"
     val after = "A "
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(fixture.editor.selectionModel.hasSelection())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/RepeatChangeActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/RepeatChangeActionTest.kt
index 53fc2a729..38d2d33ce 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/RepeatChangeActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/RepeatChangeActionTest.kt
@@ -9,7 +9,7 @@
 package org.jetbrains.plugins.ideavim.action.change
 
 import com.intellij.idea.TestFor
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -36,7 +36,7 @@ class RepeatChangeActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -58,7 +58,7 @@ class RepeatChangeActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -80,7 +80,7 @@ class RepeatChangeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @VimBehaviorDiffers(description = "Different caret position")
@@ -103,7 +103,7 @@ class RepeatChangeActionTest : VimTestCase() {
                 whe${c}XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
                 XXXX by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -125,7 +125,7 @@ class RepeatChangeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -147,7 +147,7 @@ class RepeatChangeActionTest : VimTestCase() {
                 where XXXXXX settled on some sodden sand
                 ${c}XXXXXXy the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -169,7 +169,7 @@ class RepeatChangeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @VimBehaviorDiffers(description = "Wrong caret position")
@@ -192,7 +192,7 @@ class RepeatChangeActionTest : VimTestCase() {
                 Sed in orci mauris.
                 ${c}XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @VimBehaviorDiffers(description = "Wrong caret position")
@@ -215,7 +215,7 @@ class RepeatChangeActionTest : VimTestCase() {
                 |Sed in orci mauris.
                 |Cras id tellus in ex imperdiet egestas.
     """.trimMargin()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @VimBehaviorDiffers(description = "Wrong caret position")
@@ -238,7 +238,7 @@ class RepeatChangeActionTest : VimTestCase() {
                 wherXXXt was settled on some sodden sand
                 hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @VimBehaviorDiffers(
@@ -273,7 +273,7 @@ class RepeatChangeActionTest : VimTestCase() {
                 XXXXX${c}Xy the torrent of a mountain pass.
 
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.UNCLEAR)
@@ -296,7 +296,7 @@ class RepeatChangeActionTest : VimTestCase() {
                 ${c}XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
                 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -318,7 +318,7 @@ class RepeatChangeActionTest : VimTestCase() {
               Sed in orci mauris.
               Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @VimBehaviorDiffers(
@@ -344,8 +344,7 @@ class RepeatChangeActionTest : VimTestCase() {
         One
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/UndoActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/UndoActionTest.kt
index 5018b863d..17fe00f8e 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/UndoActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/UndoActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -25,7 +25,7 @@ class UndoActionTest : VimTestCase() {
                 Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
     val after = before
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     val editor = fixture.editor
     kotlin.test.assertFalse(editor.caretModel.primaryCaret.hasSelection())
   }
@@ -42,7 +42,7 @@ class UndoActionTest : VimTestCase() {
                 Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
     val after = before
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(hasSelection())
   }
 
@@ -65,7 +65,7 @@ class UndoActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
     kotlin.test.assertFalse(hasSelection())
   }
 
@@ -89,7 +89,7 @@ class UndoActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent()
-      doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+      doTest(keys, before, after, Mode.NORMAL())
       kotlin.test.assertFalse(hasSelection())
     }
   }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeLineActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeLineActionTest.kt
index 7ea6ce591..9a55c6511 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeLineActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeLineActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.change
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -18,7 +18,7 @@ class ChangeLineActionTest : VimTestCase() {
     setupChecks {
       this.neoVim.ignoredRegisters = setOf('1', '"')
     }
-    doTest("cc", "", "", VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("cc", "", "", Mode.INSERT)
   }
 
   @Test
@@ -26,7 +26,7 @@ class ChangeLineActionTest : VimTestCase() {
     setupChecks {
       this.neoVim.ignoredRegisters = setOf('1', '"')
     }
-    doTest("S", "", "", VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("S", "", "", Mode.INSERT)
   }
 
   @Test
@@ -41,8 +41,7 @@ class ChangeLineActionTest : VimTestCase() {
             Lorem ipsum dolor sit amet,
             $c
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -60,8 +59,7 @@ class ChangeLineActionTest : VimTestCase() {
             $c
             
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -77,8 +75,7 @@ class ChangeLineActionTest : VimTestCase() {
             Lorem ipsum dolor sit amet,
             $c
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -96,8 +93,7 @@ class ChangeLineActionTest : VimTestCase() {
             $c
             
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -113,8 +109,7 @@ class ChangeLineActionTest : VimTestCase() {
             $c
             consectetur adipiscing elit
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -132,8 +127,7 @@ class ChangeLineActionTest : VimTestCase() {
             $c
             
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -151,8 +145,7 @@ class ChangeLineActionTest : VimTestCase() {
             consectetur adipiscing elit
             $c
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -190,8 +183,7 @@ class ChangeLineActionTest : VimTestCase() {
             that priceless mote now dimpling the convex
             and limpid teardrop on a lighted slide.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeMotionActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeMotionActionTest.kt
index 407b32366..c7ac2ca78 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeMotionActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeMotionActionTest.kt
@@ -10,7 +10,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.change
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Disabled
 import org.junit.jupiter.api.Test
@@ -19,13 +19,13 @@ class ChangeMotionActionTest : VimTestCase() {
   // VIM-515 |c| |W|
   @Test
   fun `test change big word with punctuation and alpha`() {
-    doTest("cW", "foo${c}(bar baz\n", "foo baz\n", VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("cW", "foo${c}(bar baz\n", "foo baz\n", Mode.INSERT)
   }
 
   // VIM-300 |c| |w|
   @Test
   fun testChangeWordTwoWordsWithoutWhitespace() {
-    doTest("cw", "${c}\$value\n", "value\n", VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("cw", "${c}\$value\n", "value\n", Mode.INSERT)
   }
 
   // VIM-296 |cc|
@@ -35,8 +35,7 @@ class ChangeMotionActionTest : VimTestCase() {
       "cc",
       "foo\n" + "${c}bar\n",
       "foo\n${c}" + "\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -55,8 +54,7 @@ class ChangeMotionActionTest : VimTestCase() {
         ....${c}
         }
       """.trimIndent().dotToSpace(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -67,8 +65,7 @@ class ChangeMotionActionTest : VimTestCase() {
       "ccbaz",
       "${c}foo\n" + "bar\n",
       "baz\n" + "bar\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -86,8 +83,7 @@ class ChangeMotionActionTest : VimTestCase() {
         ${c}
         
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -97,8 +93,7 @@ class ChangeMotionActionTest : VimTestCase() {
       "c_baz",
       "${c}foo\n" + "bar\n",
       "baz\n" + "bar\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -109,8 +104,7 @@ class ChangeMotionActionTest : VimTestCase() {
       "cw",
       "on${c}e two three\n",
       "on${c} two three\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -121,8 +115,7 @@ class ChangeMotionActionTest : VimTestCase() {
       "c2w",
       "on${c}e two three\n",
       "on${c} three\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -141,8 +134,7 @@ class ChangeMotionActionTest : VimTestCase() {
    }
    
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -153,8 +145,7 @@ class ChangeMotionActionTest : VimTestCase() {
       "cT(",
       "if (condition) ${c}{\n" + "}\n",
       "if ({\n" + "}\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -167,8 +158,7 @@ class ChangeMotionActionTest : VimTestCase() {
       "cFc",
       "if (condition) {${c}\n" + "}\n",
       "if (\n" + "}\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -179,8 +169,7 @@ class ChangeMotionActionTest : VimTestCase() {
       "cw",
       "ab.${c}cd\n",
       "ab.${c}\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -191,19 +180,18 @@ class ChangeMotionActionTest : VimTestCase() {
       listOf("c", "iw", "baz"),
       "foo bar bo${c}o\n",
       "foo bar baz\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
   // VIM-421 |c| |w|
   @Test
   fun testChangeLastCharInLine() {
-    doTest("cw", "fo${c}o\n", "fo${c}\n", VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("cw", "fo${c}o\n", "fo${c}\n", Mode.INSERT)
   }
 
   @Test
   fun testLastSymbolInWord() {
-    doTest("cw", "fo${c}o", "fo${c}", VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("cw", "fo${c}o", "fo${c}", Mode.INSERT)
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeVisualActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeVisualActionTest.kt
index 05ccfa4a8..6f97125bc 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeVisualActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeVisualActionTest.kt
@@ -11,7 +11,7 @@
 package org.jetbrains.plugins.ideavim.action.change.change
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
@@ -35,7 +35,7 @@ class ChangeVisualActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -56,7 +56,7 @@ class ChangeVisualActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
   }
 
   @VimBehaviorDiffers(
@@ -89,7 +89,7 @@ class ChangeVisualActionTest : VimTestCase() {
             ${c}
             
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
   }
 
   @Test
@@ -116,7 +116,7 @@ class ChangeVisualActionTest : VimTestCase() {
             
             
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
   }
 
   @VimBehaviorDiffers(description = "Wrong caret position")
@@ -139,7 +139,7 @@ class ChangeVisualActionTest : VimTestCase() {
             wh|Hello
             ha|Hello
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -147,7 +147,7 @@ class ChangeVisualActionTest : VimTestCase() {
     val keys = "VcHello<esc>"
     val before = "${c}Lorem Ipsum"
     val after = "Hello"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -156,7 +156,7 @@ class ChangeVisualActionTest : VimTestCase() {
       injector.parser.parseKeys("v2lc" + "aaa" + "<ESC>"),
       "abcd${c}ffffff${c}abcde${c}aaaa\n",
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     assertState("abcdaa${c}afffaa${c}adeaa${c}aa\n")
   }
 
@@ -178,8 +178,7 @@ class ChangeVisualActionTest : VimTestCase() {
         ba_quux_bar
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -201,8 +200,7 @@ class ChangeVisualActionTest : VimTestCase() {
         ba_quux_bar
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeVisualLinesEndActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeVisualLinesEndActionTest.kt
index c9462a7fe..138e35039 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeVisualLinesEndActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/ChangeVisualLinesEndActionTest.kt
@@ -10,7 +10,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.change
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
@@ -35,7 +35,7 @@ class ChangeVisualLinesEndActionTest : VimTestCase() {
             Sed in orci mauris.
             ${c}
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
   }
 
   @Test
@@ -59,7 +59,7 @@ class ChangeVisualLinesEndActionTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
             ${c}
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
   }
 
   @VimBehaviorDiffers(
@@ -93,6 +93,6 @@ class ChangeVisualLinesEndActionTest : VimTestCase() {
             ${c}
             
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/InsertRegisterTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/InsertRegisterTest.kt
index efb592af3..0867b74ab 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/InsertRegisterTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/InsertRegisterTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.change
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Disabled
 import org.junit.jupiter.api.Test
@@ -38,6 +38,6 @@ class InsertRegisterTest : VimTestCase() {
             all rocks and lavender and tufted grass,
             $c
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeNumberDecActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeNumberDecActionTest.kt
index 49db38ae4..9c5b7b9d5 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeNumberDecActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeNumberDecActionTest.kt
@@ -8,19 +8,19 @@
 
 package org.jetbrains.plugins.ideavim.action.change.change.number
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
 class ChangeNumberDecActionTest : VimTestCase() {
   @Test
   fun `test decrement hex to negative value`() {
-    doTest("<C-X>", "0x0000", "0xffffffffffffffff", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-X>", "0x0000", "0xffffffffffffffff", Mode.NORMAL())
   }
 
   @Test
   fun `test decrement hex to negative value by 10`() {
-    doTest("10<C-X>", "0x0005", "0xfffffffffffffffb", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("10<C-X>", "0x0005", "0xfffffffffffffffb", Mode.NORMAL())
   }
 
   @Test
@@ -29,14 +29,13 @@ class ChangeNumberDecActionTest : VimTestCase() {
       ":set nrformats+=octal<CR><C-X>",
       "00000",
       "01777777777777777777777",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
   @Test
   fun `test decrement incorrect octal`() {
-    doTest(":set nrformats+=octal<CR><C-X>", "008", "7", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(":set nrformats+=octal<CR><C-X>", "008", "7", Mode.NORMAL())
   }
 
   @Test
@@ -45,8 +44,7 @@ class ChangeNumberDecActionTest : VimTestCase() {
       ":set nrformats+=octal<CR>10<C-X>",
       "00005",
       "01777777777777777777773",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeNumberIncActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeNumberIncActionTest.kt
index da14002e1..23b59f056 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeNumberIncActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeNumberIncActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.change.number
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
@@ -17,6 +17,6 @@ class ChangeNumberIncActionTest : VimTestCase() {
   @VimBehaviorDiffers(originalVimAfter = "11X0")
   @Test
   fun `test inc fancy number`() {
-    doTest("<C-A>", "1${c}0X0", "10X1", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("<C-A>", "1${c}0X0", "10X1", Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberAvalancheDecActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberAvalancheDecActionTest.kt
index 3b7bed1c5..93d525297 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberAvalancheDecActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberAvalancheDecActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.change.number
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -30,8 +30,7 @@ class ChangeVisualNumberAvalancheDecActionTest : VimTestCase() {
                     number 1
                     number 1
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -49,8 +48,7 @@ class ChangeVisualNumberAvalancheDecActionTest : VimTestCase() {
                     number 1
                     number 1
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberAvalancheIncActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberAvalancheIncActionTest.kt
index 799e65ee1..071a9f4d0 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberAvalancheIncActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberAvalancheIncActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.change.number
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -30,8 +30,7 @@ class ChangeVisualNumberAvalancheIncActionTest : VimTestCase() {
                     number 3
                     number 4
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -49,8 +48,7 @@ class ChangeVisualNumberAvalancheIncActionTest : VimTestCase() {
                     number 5
                     number 7
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberDecActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberDecActionTest.kt
index 4197a3d0b..f4bc38db1 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberDecActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberDecActionTest.kt
@@ -9,7 +9,7 @@
 package org.jetbrains.plugins.ideavim.action.change.change.number
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -23,8 +23,7 @@ class ChangeVisualNumberDecActionTest : VimTestCase() {
       "V<C-X>",
       "${c}12345",
       "${c}12344",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -34,8 +33,7 @@ class ChangeVisualNumberDecActionTest : VimTestCase() {
       "v10w<C-X>",
       "11 <- should not be decremented |${c}11| should not be decremented -> 12",
       "11 <- should not be decremented |${c}10| should not be decremented -> 12",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -45,8 +43,7 @@ class ChangeVisualNumberDecActionTest : VimTestCase() {
       "v4l<C-X>",
       "11111${c}33333111111",
       "11111${c}33332111111",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -74,8 +71,7 @@ class ChangeVisualNumberDecActionTest : VimTestCase() {
                     no dec 1
 
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -93,8 +89,7 @@ class ChangeVisualNumberDecActionTest : VimTestCase() {
                     999
                     999
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -104,8 +99,7 @@ class ChangeVisualNumberDecActionTest : VimTestCase() {
       "V<C-X>",
       "1 should$c not be decremented -> 2",
       "${c}0 should not be decremented -> 2",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberIncActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberIncActionTest.kt
index e470f4d10..c8f565bd2 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberIncActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/change/number/ChangeVisualNumberIncActionTest.kt
@@ -9,7 +9,7 @@
 package org.jetbrains.plugins.ideavim.action.change.change.number
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -23,8 +23,7 @@ class ChangeVisualNumberIncActionTest : VimTestCase() {
       "V<C-A>",
       "${c}12345",
       "${c}12346",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -34,8 +33,7 @@ class ChangeVisualNumberIncActionTest : VimTestCase() {
       "v10w<C-A>",
       "11 <- should not be incremented |${c}11| should not be incremented -> 12",
       "11 <- should not be incremented |${c}12| should not be incremented -> 12",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -45,8 +43,7 @@ class ChangeVisualNumberIncActionTest : VimTestCase() {
       "v4l<C-A>",
       "11111${c}22222111111",
       "11111${c}22223111111",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -74,8 +71,7 @@ class ChangeVisualNumberIncActionTest : VimTestCase() {
                     no inc 1
 
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -93,8 +89,7 @@ class ChangeVisualNumberIncActionTest : VimTestCase() {
                     1000
                     1000
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -104,8 +99,7 @@ class ChangeVisualNumberIncActionTest : VimTestCase() {
       "V<C-A>",
       "1 should$c not be incremented -> 2",
       "${c}2 should not be incremented -> 2",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -138,8 +132,7 @@ class ChangeVisualNumberIncActionTest : VimTestCase() {
       "v$<C-A>",
       "1 <- should$c not be incremented 2",
       "1 <- should$c not be incremented 3",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -153,8 +146,7 @@ class ChangeVisualNumberIncActionTest : VimTestCase() {
       """1 <- should$c not be incremented 3
         |2 should not be incremented -> 2
       """.trimMargin(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -170,8 +162,7 @@ class ChangeVisualNumberIncActionTest : VimTestCase() {
         |2 should not be incremented -> 2
         |2 should not be incremented -> 2
       """.trimMargin(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -189,8 +180,7 @@ class ChangeVisualNumberIncActionTest : VimTestCase() {
         |1 <- should not be incremented -> 2
         |1 <- should not be incremented -> 2
       """.trimMargin(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -208,8 +198,7 @@ class ChangeVisualNumberIncActionTest : VimTestCase() {
         |1 <- should not be incremented 3
         |1 <- should not be incremented 3
       """.trimMargin(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteEndOfLineActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteEndOfLineActionTest.kt
index 04622aeef..4515d3851 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteEndOfLineActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteEndOfLineActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.delete
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -29,8 +29,7 @@ class DeleteEndOfLineActionTest : VimTestCase() {
                 Lorem ipsum dolor sit amet,
                 consectetur adipiscing elit
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteJoinLinesSpacesActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteJoinLinesSpacesActionTest.kt
index 723db4a18..7bafb2cf0 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteJoinLinesSpacesActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteJoinLinesSpacesActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.delete
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestIjOptionConstants
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -38,8 +38,7 @@ class DeleteJoinLinesSpacesActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -61,8 +60,7 @@ class DeleteJoinLinesSpacesActionTest : VimTestCase() {
                 Lorem ipsum dolor sit amet, consectetur adipiscing elit$c Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -87,8 +85,7 @@ class DeleteJoinLinesSpacesActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteJoinVisualLinesSpacesActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteJoinVisualLinesSpacesActionTest.kt
index d2a4f0dfd..d9ffc4636 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteJoinVisualLinesSpacesActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteJoinVisualLinesSpacesActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.delete
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestIjOptionConstants
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -39,8 +39,7 @@ class DeleteJoinVisualLinesSpacesActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteVisualActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteVisualActionTest.kt
index 24ada156c..fb500afc7 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteVisualActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteVisualActionTest.kt
@@ -9,7 +9,8 @@
 package org.jetbrains.plugins.ideavim.action.change.delete
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -40,7 +41,7 @@ class DeleteVisualActionTest : VimTestCase() {
             wh||t was settled on some sodden sand
             Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -62,7 +63,7 @@ class DeleteVisualActionTest : VimTestCase() {
             wh||t was settled on some sodden sand
             Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -84,7 +85,7 @@ class DeleteVisualActionTest : VimTestCase() {
             wh||t was settled on some sodden sand
             Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -106,7 +107,7 @@ class DeleteVisualActionTest : VimTestCase() {
             wh||t was settled on some sodden sand
             Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.DIFFERENT)
@@ -125,7 +126,7 @@ class DeleteVisualActionTest : VimTestCase() {
       """.trimIndent(),
     )
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.LINE_WISE))
     typeText(injector.parser.parseKeys("d"))
     assertState(
       """
@@ -134,7 +135,7 @@ class DeleteVisualActionTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertState(VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    assertState(Mode.NORMAL())
   }
 
   @Test
@@ -156,6 +157,6 @@ class DeleteVisualActionTest : VimTestCase() {
             wh|
             ha|
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteVisualLinesActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteVisualLinesActionTest.kt
index 560740548..6e62eeb07 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteVisualLinesActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteVisualLinesActionTest.kt
@@ -10,7 +10,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.delete
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -30,8 +30,7 @@ class DeleteVisualLinesActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -50,8 +49,7 @@ class DeleteVisualLinesActionTest : VimTestCase() {
                 consectetur adipiscing elit
                 ${c}Sed in orci mauris.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -70,8 +68,7 @@ class DeleteVisualLinesActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -90,8 +87,7 @@ class DeleteVisualLinesActionTest : VimTestCase() {
                 consectetur adipiscing elit
                 ${c}Sed in orci mauris.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -114,7 +110,7 @@ class DeleteVisualLinesActionTest : VimTestCase() {
             consectetur adipiscing elit
             ${c}
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -138,6 +134,6 @@ class DeleteVisualLinesActionTest : VimTestCase() {
             
             ${c}
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteVisualLinesEndActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteVisualLinesEndActionTest.kt
index 3b57ec148..53184b40f 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteVisualLinesEndActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/delete/DeleteVisualLinesEndActionTest.kt
@@ -10,7 +10,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.delete
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -674,7 +674,7 @@ class DeleteVisualLinesEndActionTest : VimTestCase() {
             Today it is not working
             The test is like that.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
+      Mode.INSERT,
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertAfterLineEndActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertAfterLineEndActionTest.kt
index c5d5e9d90..2e3074d84 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertAfterLineEndActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertAfterLineEndActionTest.kt
@@ -9,7 +9,7 @@
 package org.jetbrains.plugins.ideavim.action.change.insert
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -53,9 +53,8 @@ class InsertAfterLineEndActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertBeforeCursorActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertBeforeCursorActionTest.kt
index 66246ded5..d7fc1a14e 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertBeforeCursorActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertBeforeCursorActionTest.kt
@@ -8,14 +8,14 @@
 
 package org.jetbrains.plugins.ideavim.action.change.insert
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
 class InsertBeforeCursorActionTest : VimTestCase() {
   @Test
   fun `test check caret shape`() {
-    doTest("i", "123", "123", VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("i", "123", "123", Mode.INSERT)
     assertCaretsVisualAttributes()
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertBeforeFirstNonBlankActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertBeforeFirstNonBlankActionTest.kt
index cfb2a942a..616bb22bd 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertBeforeFirstNonBlankActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertBeforeFirstNonBlankActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.insert
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -33,9 +33,8 @@ class InsertBeforeFirstNonBlankActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertDeleteInsertedTextActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertDeleteInsertedTextActionTest.kt
index 63de44e0d..52e579a9b 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertDeleteInsertedTextActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertDeleteInsertedTextActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.insert
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
@@ -29,8 +29,7 @@ class InsertDeleteInsertedTextActionTest : VimTestCase() {
 
             I found iti${c}t in a legendary land
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -50,8 +49,7 @@ class InsertDeleteInsertedTextActionTest : VimTestCase() {
 
             I found ii${c}ta legendary land
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertDeletePreviousWordActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertDeletePreviousWordActionTest.kt
index 61a4ae748..fbde36ef7 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertDeletePreviousWordActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertDeletePreviousWordActionTest.kt
@@ -11,7 +11,7 @@
 package org.jetbrains.plugins.ideavim.action.change.insert
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
@@ -28,8 +28,7 @@ class InsertDeletePreviousWordActionTest : VimTestCase() {
       """
             I found it in a i${c}t land
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -43,8 +42,7 @@ class InsertDeletePreviousWordActionTest : VimTestCase() {
       """
             I ${c} it in a legendary land
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -58,8 +56,7 @@ class InsertDeletePreviousWordActionTest : VimTestCase() {
       """
             I ${c} in a legendary land
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -75,8 +72,7 @@ class InsertDeletePreviousWordActionTest : VimTestCase() {
       """
             Lorem Ipsum${c} found it in a legendary land
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -106,8 +102,7 @@ class InsertDeletePreviousWordActionTest : VimTestCase() {
                   legendary
                ${c}
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -118,8 +113,7 @@ class InsertDeletePreviousWordActionTest : VimTestCase() {
       listOf("a", "<C-W>"),
       "this is a sentence<caret>.\n",
       "this is a sentence<caret>\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -130,8 +124,7 @@ class InsertDeletePreviousWordActionTest : VimTestCase() {
       listOf("A", "<C-W>"),
       "<caret>this is a sentence\n",
       "this is a <caret>\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -142,8 +135,7 @@ class InsertDeletePreviousWordActionTest : VimTestCase() {
       listOf("A", "<C-W>"),
       "<caret>\$variable\n",
       "$<caret>\n",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertEnterActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertEnterActionTest.kt
index f4da96ea0..fa6bba65a 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertEnterActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertEnterActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.insert
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -28,7 +28,7 @@ class InsertEnterActionTest : VimTestCase() {
         |Sed in orci mauris.
         |Cras id tellus in ex imperdiet egestas.
     """.trimMargin()
-    doTest(listOf("i", "<Enter>"), before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(listOf("i", "<Enter>"), before, after, Mode.INSERT)
   }
 
   @TestWithoutNeovim(SkipNeovimReason.CTRL_CODES)
@@ -45,7 +45,7 @@ class InsertEnterActionTest : VimTestCase() {
         |Sed in orci mauris.
         |Cras id tellus in ex imperdiet egestas.
     """.trimMargin()
-    doTest(listOf("i", "<C-M>"), before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(listOf("i", "<C-M>"), before, after, Mode.INSERT)
   }
 
   @TestWithoutNeovim(SkipNeovimReason.OPTION)
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertExitModeActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertExitModeActionTest.kt
index 46fa0c6c6..c672e957b 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertExitModeActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertExitModeActionTest.kt
@@ -8,18 +8,18 @@
 
 package org.jetbrains.plugins.ideavim.action.change.insert
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
 class InsertExitModeActionTest : VimTestCase() {
   @Test
   fun `test exit visual mode`() {
-    doTest("i<Esc>", "12${c}3", "1${c}23", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("i<Esc>", "12${c}3", "1${c}23", Mode.NORMAL())
   }
 
   @Test
   fun `test exit visual mode on line start`() {
-    doTest("i<Esc>", "${c}123", "${c}123", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("i<Esc>", "${c}123", "${c}123", Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertNewLineAboveActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertNewLineAboveActionTest.kt
index 3404acdf4..e0f428584 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertNewLineAboveActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertNewLineAboveActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.insert
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -28,7 +28,7 @@ class InsertNewLineAboveActionTest : VimTestCase() {
         |Sed in orci mauris.
         |Cras id tellus in ex imperdiet egestas.
     """.trimMargin()
-    doTest("O", before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("O", before, after, Mode.INSERT)
   }
 
   @Test
@@ -44,7 +44,7 @@ class InsertNewLineAboveActionTest : VimTestCase() {
         |where it was settled on some sodden sand
         |hard by the torrent of a mountain pass.
     """.trimMargin()
-    doTest("O", before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("O", before, after, Mode.INSERT)
   }
 
   @Test
@@ -60,7 +60,7 @@ class InsertNewLineAboveActionTest : VimTestCase() {
         |    Sed in orci mauris.
         |    Cras id tellus in ex imperdiet egestas.
     """.trimMargin()
-    doTest("O", before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("O", before, after, Mode.INSERT)
   }
 
   @Test
@@ -76,7 +76,7 @@ class InsertNewLineAboveActionTest : VimTestCase() {
         |    Sed in orci mauris.
         |    Cras id tellus in ex imperdiet egestas.
     """.trimMargin()
-    doTest("O", before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("O", before, after, Mode.INSERT)
   }
 
   @TestWithoutNeovim(SkipNeovimReason.PLUGIN) // Java support would be a neovim plugin
@@ -114,7 +114,7 @@ class InsertNewLineAboveActionTest : VimTestCase() {
         |    $c
         |    hard by the torrent of a mountain pass.
     """.trimMargin()
-    doTest("O", before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("O", before, after, Mode.INSERT)
   }
 
   @TestWithoutNeovim(SkipNeovimReason.OPTION)
@@ -142,6 +142,6 @@ class InsertNewLineAboveActionTest : VimTestCase() {
         |Sed in orci mauris.
         |Cras id tellus in ex imperdiet egestas.
     """.trimMargin()
-    doTest("O", before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("O", before, after, Mode.INSERT)
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertNewLineBelowActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertNewLineBelowActionTest.kt
index 7fa689dc3..c75bcc4df 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertNewLineBelowActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertNewLineBelowActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.insert
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -28,7 +28,7 @@ class InsertNewLineBelowActionTest : VimTestCase() {
         |where it was settled on some sodden sand
         |hard by the torrent of a mountain pass.
     """.trimMargin()
-    doTest("o", before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("o", before, after, Mode.INSERT)
   }
 
   @Test
@@ -44,7 +44,7 @@ class InsertNewLineBelowActionTest : VimTestCase() {
         |where it was settled on some sodden sand
         |hard by the torrent of a mountain pass.
     """.trimMargin()
-    doTest("o", before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("o", before, after, Mode.INSERT)
   }
 
   @Test
@@ -60,7 +60,7 @@ class InsertNewLineBelowActionTest : VimTestCase() {
         |    where it was settled on some sodden sand
         |    hard by the torrent of a mountain pass.
     """.trimMargin()
-    doTest("o", before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("o", before, after, Mode.INSERT)
   }
 
   @Test
@@ -76,7 +76,7 @@ class InsertNewLineBelowActionTest : VimTestCase() {
         |    where it was settled on some sodden sand
         |    hard by the torrent of a mountain pass.
     """.trimMargin()
-    doTest("o", before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("o", before, after, Mode.INSERT)
   }
 
   @TestWithoutNeovim(SkipNeovimReason.PLUGIN) // Java support would be a neovim plugin
@@ -133,7 +133,7 @@ class InsertNewLineBelowActionTest : VimTestCase() {
         |    hard by the torrent of a mountain pass.
         |    $c
     """.trimMargin()
-    doTest("o", before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("o", before, after, Mode.INSERT)
   }
 
   @TestWithoutNeovim(SkipNeovimReason.OPTION)
@@ -160,7 +160,7 @@ class InsertNewLineBelowActionTest : VimTestCase() {
         |where it was settled on some sodden sand
         |hard by the torrent of a mountain pass.
     """.trimMargin()
-    doTest("5o", before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("5o", before, after, Mode.INSERT)
   }
 
   @Test
@@ -199,7 +199,7 @@ class InsertNewLineBelowActionTest : VimTestCase() {
 
     configureAndFold(before, "")
 
-    performTest("o", after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    performTest("o", after, Mode.INSERT)
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.FOLDING, "Neovim doesn't support arbitrary folds")
@@ -219,7 +219,7 @@ class InsertNewLineBelowActionTest : VimTestCase() {
 
     configureAndFold(before, "")
 
-    performTest("o", after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    performTest("o", after, Mode.INSERT)
   }
 
   @Test
@@ -238,6 +238,6 @@ class InsertNewLineBelowActionTest : VimTestCase() {
 
     configureAndFold(before, "")
 
-    performTest("o", after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    performTest("o", after, Mode.INSERT)
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertSingleCommandActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertSingleCommandActionTest.kt
index 3e564500d..5aa138e35 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertSingleCommandActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/InsertSingleCommandActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.change.insert
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -19,8 +19,7 @@ class InsertSingleCommandActionTest : VimTestCase() {
       listOf("i", "<C-O>", "vlll", "<Esc>"),
       "I found ${c}it in a legendary land",
       "I found it ${c}in a legendary land",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/VisualBlockAppendActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/VisualBlockAppendActionTest.kt
index 907432d24..31e4f5a33 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/VisualBlockAppendActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/VisualBlockAppendActionTest.kt
@@ -9,7 +9,7 @@
 package org.jetbrains.plugins.ideavim.action.change.insert
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -70,9 +70,8 @@ class VisualBlockAppendActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/VisualBlockInsertActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/VisualBlockInsertActionTest.kt
index 9fcb18f55..976beec54 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/VisualBlockInsertActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/change/insert/VisualBlockInsertActionTest.kt
@@ -12,7 +12,7 @@ import com.intellij.codeInsight.daemon.impl.HintRenderer
 import com.intellij.codeInsight.folding.CodeFoldingManager
 import com.intellij.codeInsight.folding.impl.FoldingUtil
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -200,8 +200,7 @@ Xbar
       listOf("<C-V>", "lll", "I"),
       before.trimIndent(),
       before.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/IdeaPutNotificationsTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/IdeaPutNotificationsTest.kt
index 74679323e..7decd220e 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/IdeaPutNotificationsTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/IdeaPutNotificationsTest.kt
@@ -12,7 +12,7 @@ import com.intellij.notification.EventLog
 import com.intellij.notification.Notification
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.group.NotificationService
 import com.maddyhome.idea.vim.newapi.vim
 import com.maddyhome.idea.vim.options.OptionConstants
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutTestAfterCursorActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutTestAfterCursorActionTest.kt
index 91a941f61..8f5ebb2c9 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutTestAfterCursorActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutTestAfterCursorActionTest.kt
@@ -18,8 +18,8 @@ import com.intellij.testFramework.ExtensionTestUtil
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.globalOptions
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import com.maddyhome.idea.vim.newapi.vim
 import com.maddyhome.idea.vim.options.OptionConstants
@@ -49,8 +49,7 @@ class PutTestAfterCursorActionTest : VimTestCase() {
       "\"4p",
       "This is my$c text",
       "This is my XXX$c text",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     kotlin.test.assertEquals(1, extension.calledExtractTransferableData)
   }
@@ -62,8 +61,7 @@ class PutTestAfterCursorActionTest : VimTestCase() {
       "\"4p",
       "This is my$c text",
       "This is my XXX$c text",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutTextBeforeCursorActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutTextBeforeCursorActionTest.kt
index 837f27078..7a24aaf75 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutTextBeforeCursorActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutTextBeforeCursorActionTest.kt
@@ -9,7 +9,7 @@
 package org.jetbrains.plugins.ideavim.action.copy
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.newapi.vim
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.jetbrains.plugins.ideavim.rangeOf
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutViaIdeaTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutViaIdeaTest.kt
index f327ea6b2..acd97263d 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutViaIdeaTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutViaIdeaTest.kt
@@ -13,7 +13,7 @@ import com.intellij.ide.CopyPasteManagerEx
 import com.intellij.openapi.ide.CopyPasteManager
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.newapi.vim
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutVisualTextActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutVisualTextActionTest.kt
index de460853c..2324907d3 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutVisualTextActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutVisualTextActionTest.kt
@@ -12,7 +12,7 @@ package org.jetbrains.plugins.ideavim.action.copy
 
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import com.maddyhome.idea.vim.newapi.vim
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutVisualTextMoveCursorActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutVisualTextMoveCursorActionTest.kt
index 3fa20f5ba..30a43a51f 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutVisualTextMoveCursorActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/PutVisualTextMoveCursorActionTest.kt
@@ -10,7 +10,7 @@ package org.jetbrains.plugins.ideavim.action.copy
 
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import com.maddyhome.idea.vim.newapi.vim
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/YankVisualActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/YankVisualActionTest.kt
index e66469812..fe80d3bfd 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/YankVisualActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/YankVisualActionTest.kt
@@ -12,7 +12,7 @@ package org.jetbrains.plugins.ideavim.action.copy
 
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import com.maddyhome.idea.vim.newapi.vim
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/YankVisualLinesActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/YankVisualLinesActionTest.kt
index 54941ce74..1b9606fc0 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/copy/YankVisualLinesActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/copy/YankVisualLinesActionTest.kt
@@ -10,7 +10,7 @@ package org.jetbrains.plugins.ideavim.action.copy
 
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -57,7 +57,7 @@ class YankVisualLinesActionTest : VimTestCase() {
             ${c}where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest("vjY", text, textAfter, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("vjY", text, textAfter, Mode.NORMAL())
     val yankedTest = """
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/GnNextTextObjectTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/GnNextTextObjectTest.kt
index e9beb3720..8d5030bdf 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/GnNextTextObjectTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/GnNextTextObjectTest.kt
@@ -12,7 +12,7 @@ package org.jetbrains.plugins.ideavim.action.motion.gn
 
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.common.Direction
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -79,8 +79,7 @@ class GnNextTextObjectTest : VimTestCase() {
       listOf("/is<CR>", ":s/test/tester/<CR>", "0", "dgn"),
       "Hello, ${c}this is a test here",
       "Hello, this is a ${c}er here",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -89,6 +88,6 @@ class GnNextTextObjectTest : VimTestCase() {
     VimPlugin.getSearch().setLastSearchState(fixture.editor, "test", "", Direction.FORWARDS)
     typeText(keys)
     assertState(after)
-    assertState(VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    assertState(Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/GnPreviousTextObjectTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/GnPreviousTextObjectTest.kt
index 5cf0884ec..68485e1ac 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/GnPreviousTextObjectTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/GnPreviousTextObjectTest.kt
@@ -12,7 +12,7 @@ package org.jetbrains.plugins.ideavim.action.motion.gn
 
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.common.Direction
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -57,8 +57,7 @@ class GnPreviousTextObjectTest : VimTestCase() {
       listOf("/is<CR>", ":s/test/tester/<CR>", "$", "dgN"),
       "Hello, ${c}this is a test here",
       "Hello, this is a ${c}er here",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -67,6 +66,6 @@ class GnPreviousTextObjectTest : VimTestCase() {
     VimPlugin.getSearch().setLastSearchState(fixture.editor, "test", "", Direction.FORWARDS)
     typeText(keys)
     assertState(after)
-    assertState(VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    assertState(Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/VisualSelectNextSearchTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/VisualSelectNextSearchTest.kt
index 1f044859c..e85f51e8d 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/VisualSelectNextSearchTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/VisualSelectNextSearchTest.kt
@@ -11,7 +11,8 @@ import com.intellij.idea.TestFor
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.Direction
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -26,7 +27,7 @@ class VisualSelectNextSearchTest : VimTestCase() {
     typeTextInFile(injector.parser.parseKeys("*" + "b" + "gn"), "h<caret>ello world\nhello world hello world")
     assertOffset(16)
     assertSelection("hello")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
@@ -37,7 +38,7 @@ class VisualSelectNextSearchTest : VimTestCase() {
       "h<caret>ello world\nh<caret>ello world hello world",
     )
     kotlin.test.assertEquals(1, fixture.editor.caretModel.caretCount)
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
@@ -49,7 +50,7 @@ class VisualSelectNextSearchTest : VimTestCase() {
     )
     assertOffset(0)
     assertSelection("h")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.UNCLEAR)
@@ -60,7 +61,7 @@ class VisualSelectNextSearchTest : VimTestCase() {
     typeText(injector.parser.parseKeys("gn"))
     assertOffset(7)
     assertSelection("test")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
@@ -69,7 +70,7 @@ class VisualSelectNextSearchTest : VimTestCase() {
     typeTextInFile(injector.parser.parseKeys("*" + "gn"), "h<caret>ello world\nhello world hello world")
     assertOffset(16)
     assertSelection("hello")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
@@ -97,7 +98,7 @@ class VisualSelectNextSearchTest : VimTestCase() {
     typeTextInFile(injector.parser.parseKeys("*" + "gn" + "gn"), "h<caret>ello world\nhello world hello, hello")
     assertOffset(28)
     assertSelection("hello world hello")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
@@ -109,7 +110,7 @@ class VisualSelectNextSearchTest : VimTestCase() {
     )
     assertOffset(28)
     assertSelection(null)
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @Test
@@ -125,7 +126,7 @@ class VisualSelectNextSearchTest : VimTestCase() {
     typeTextInFile(injector.parser.parseKeys("*0e" + "gn"), "h<caret>ello hello")
     assertOffset(4)
     assertSelection("hello")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
@@ -134,7 +135,7 @@ class VisualSelectNextSearchTest : VimTestCase() {
     typeTextInFile(injector.parser.parseKeys("*0llv" + "gn"), "h<caret>ello hello")
     assertOffset(4)
     assertSelection("llo")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
@@ -143,7 +144,7 @@ class VisualSelectNextSearchTest : VimTestCase() {
     typeTextInFile(injector.parser.parseKeys("*0ev" + "gn"), "h<caret>ello hello")
     assertOffset(10)
     assertSelection("o hello")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
@@ -155,7 +156,7 @@ class VisualSelectNextSearchTest : VimTestCase() {
     )
     assertOffset(28)
     assertSelection("hello world hello")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
@@ -167,7 +168,7 @@ class VisualSelectNextSearchTest : VimTestCase() {
     )
     assertOffset(28)
     assertSelection("hello world hello")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/VisualSelectPreviousSearchTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/VisualSelectPreviousSearchTest.kt
index 9506bd01a..b245e6f0d 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/VisualSelectPreviousSearchTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/gn/VisualSelectPreviousSearchTest.kt
@@ -11,7 +11,8 @@ import com.intellij.idea.TestFor
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.Direction
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -26,7 +27,7 @@ class VisualSelectPreviousSearchTest : VimTestCase() {
     typeTextInFile(injector.parser.parseKeys("*w" + "gN"), "h<caret>ello world\nhello world hello world")
     assertOffset(12)
     assertSelection("hello")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
@@ -37,7 +38,7 @@ class VisualSelectPreviousSearchTest : VimTestCase() {
       "h<caret>ello world\nh<caret>ello world hello world",
     )
     kotlin.test.assertEquals(1, fixture.editor.caretModel.caretCount)
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
@@ -46,7 +47,7 @@ class VisualSelectPreviousSearchTest : VimTestCase() {
     typeTextInFile(injector.parser.parseKeys("*" + "gN"), "h<caret>ello world\nhello world hello world")
     assertOffset(12)
     assertSelection("hello")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.DIFFERENT)
@@ -57,7 +58,7 @@ class VisualSelectPreviousSearchTest : VimTestCase() {
     typeText(injector.parser.parseKeys("gN"))
     assertOffset(0)
     assertSelection("test")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
@@ -74,7 +75,7 @@ class VisualSelectPreviousSearchTest : VimTestCase() {
     typeTextInFile(injector.parser.parseKeys("*" + "gN" + "gN"), "hello world\nh<caret>ello world hello")
     assertOffset(12)
     assertSelection("hello world hello")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
@@ -83,7 +84,7 @@ class VisualSelectPreviousSearchTest : VimTestCase() {
     typeTextInFile(injector.parser.parseKeys("*" + "gN" + "gN" + "<Esc>"), "hello world\nh<caret>ello world hello")
     assertOffset(12)
     assertSelection(null)
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestFor(classes = [SearchWholeWordForwardAction::class])
@@ -92,7 +93,7 @@ class VisualSelectPreviousSearchTest : VimTestCase() {
     typeTextInFile(injector.parser.parseKeys("*llv" + "gN"), "hello hello")
     assertOffset(6)
     assertSelection("hel")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @Test
@@ -100,6 +101,6 @@ class VisualSelectPreviousSearchTest : VimTestCase() {
     typeTextInFile(injector.parser.parseKeys("*" + "gN" + "gN"), "hello 1\n\thello 2\n\the<caret>llo 3\n\thello 4")
     assertOffset(18)
     assertSelection("hello 3\n\thello")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionArrowLeftActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionArrowLeftActionTest.kt
index 58d2a995a..8d482fa20 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionArrowLeftActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionArrowLeftActionTest.kt
@@ -11,7 +11,8 @@
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -247,8 +248,7 @@ class MotionArrowLeftActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -273,8 +273,7 @@ class MotionArrowLeftActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -299,8 +298,7 @@ class MotionArrowLeftActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -325,8 +323,7 @@ class MotionArrowLeftActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -351,8 +348,7 @@ class MotionArrowLeftActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionArrowRightActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionArrowRightActionTest.kt
index d014fc476..f28039f2a 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionArrowRightActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionArrowRightActionTest.kt
@@ -11,7 +11,8 @@
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -205,8 +206,7 @@ class MotionArrowRightActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -231,8 +231,7 @@ class MotionArrowRightActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -257,8 +256,7 @@ class MotionArrowRightActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -283,8 +281,7 @@ class MotionArrowRightActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -309,8 +306,7 @@ class MotionArrowRightActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionEndActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionEndActionTest.kt
index 3dd96195d..8d921f65c 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionEndActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionEndActionTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -42,7 +43,7 @@ class MotionEndActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.OPTION)
@@ -65,7 +66,7 @@ class MotionEndActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestWithoutNeovim(SkipNeovimReason.OPTION)
@@ -88,7 +89,7 @@ class MotionEndActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest(keys, before, after, Mode.SELECT(SelectionType.CHARACTER_WISE))
   }
 
   @TestWithoutNeovim(SkipNeovimReason.OPTION)
@@ -111,7 +112,7 @@ class MotionEndActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.OPTION)
@@ -134,7 +135,7 @@ class MotionEndActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.OPTION)
@@ -157,7 +158,7 @@ class MotionEndActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.NON_ASCII)
@@ -180,6 +181,6 @@ class MotionEndActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionHomeActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionHomeActionTest.kt
index d6d2bfe3a..f5a48f58e 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionHomeActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionHomeActionTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -43,7 +44,7 @@ class MotionHomeActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @OptionTest(VimOption(TestOptionConstants.keymodel, doesntAffectTest = true))
@@ -72,7 +73,7 @@ class MotionHomeActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestWithoutNeovim(SkipNeovimReason.OPTION)
@@ -95,7 +96,7 @@ class MotionHomeActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest(keys, before, after, Mode.SELECT(SelectionType.CHARACTER_WISE))
   }
 
   @TestWithoutNeovim(SkipNeovimReason.OPTION)
@@ -118,7 +119,7 @@ class MotionHomeActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.OPTION)
@@ -141,6 +142,6 @@ class MotionHomeActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLastColumnActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLastColumnActionTest.kt
index efea1a5fb..a8b8f71a8 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLastColumnActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLastColumnActionTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -37,7 +38,7 @@ class MotionLastColumnActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -59,7 +60,7 @@ class MotionLastColumnActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -81,7 +82,7 @@ class MotionLastColumnActionTest : VimTestCase() {
             wh${s}ere it was settled on some sodden sand${c}${se}
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_BLOCK)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.BLOCK_WISE))
   }
 
   @Test
@@ -136,7 +137,7 @@ class MotionLastColumnActionTest : VimTestCase() {
             wh${s}ere it was settled on some sodden san${c}d${se}
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_BLOCK)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.BLOCK_WISE))
   }
 
   @Test
@@ -158,7 +159,7 @@ class MotionLastColumnActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
   }
 
   @TestWithoutNeovim(SkipNeovimReason.CTRL_CODES)
@@ -181,6 +182,6 @@ class MotionLastColumnActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.INSERT)
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLeftInsertTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLeftInsertTest.kt
index 5d098354f..643a67102 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLeftInsertTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLeftInsertTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -26,7 +26,7 @@ class MotionLeftInsertTest : VimTestCase() {
       """
           Oh, hi M${c}ark
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
+      Mode.INSERT,
     ) {
       enterCommand("set whichwrap=[")
     }
@@ -43,7 +43,7 @@ class MotionLeftInsertTest : VimTestCase() {
       """
           ${c}Oh, hi Mark
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
+      Mode.INSERT,
     ) {
       enterCommand("set whichwrap=[")
     }
@@ -62,7 +62,7 @@ class MotionLeftInsertTest : VimTestCase() {
           Oh, hi Mark$c
           You are my favourite customer
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
+      Mode.INSERT,
     ) {
       enterCommand("set whichwrap=[")
     }
@@ -85,7 +85,7 @@ class MotionLeftInsertTest : VimTestCase() {
           
           You are my favourite customer
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
+      Mode.INSERT,
     ) {
       enterCommand("set whichwrap=[")
     }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLeftMatchCharActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLeftMatchCharActionTest.kt
index 5283d41ae..bcf0fb6a5 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLeftMatchCharActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLeftMatchCharActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -19,8 +19,7 @@ class MotionLeftMatchCharActionTest : VimTestCase() {
       "Fx;",
       "hello x hello x hello$c",
       "hello ${c}x hello x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -30,8 +29,7 @@ class MotionLeftMatchCharActionTest : VimTestCase() {
       "Fx;;",
       "hello x hello x hello x hello$c",
       "hello ${c}x hello x hello x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -41,8 +39,7 @@ class MotionLeftMatchCharActionTest : VimTestCase() {
       "Fx2;",
       "hello x hello x hello x hello$c",
       "hello ${c}x hello x hello x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -52,8 +49,7 @@ class MotionLeftMatchCharActionTest : VimTestCase() {
       "Fx3;",
       "hello x hello x hello x hello x hello$c",
       "hello ${c}x hello x hello x hello x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLeftTillMatchCharActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLeftTillMatchCharActionTest.kt
index 760d23126..4e6e2e48b 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLeftTillMatchCharActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionLeftTillMatchCharActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -19,8 +19,7 @@ class MotionLeftTillMatchCharActionTest : VimTestCase() {
       "Tx;",
       "hello x hello x hello$c",
       "hello x$c hello x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -30,8 +29,7 @@ class MotionLeftTillMatchCharActionTest : VimTestCase() {
       "Tx;;",
       "hello x hello x hello x hello$c",
       "hello x$c hello x hello x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -41,8 +39,7 @@ class MotionLeftTillMatchCharActionTest : VimTestCase() {
       "Tx2;",
       "hello x hello x hello x hello$c",
       "hello x hello x$c hello x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -52,8 +49,7 @@ class MotionLeftTillMatchCharActionTest : VimTestCase() {
       "Tx3;",
       "hello x hello x hello x hello$c",
       "hello x$c hello x hello x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightActionTest.kt
index 0b44a81db..3070a0214 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightActionTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -42,8 +43,7 @@ class MotionRightActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -67,8 +67,7 @@ class MotionRightActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -92,8 +91,7 @@ class MotionRightActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -112,8 +110,7 @@ class MotionRightActionTest : VimTestCase() {
             Today it is not working
             The test is like that.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -132,8 +129,7 @@ class MotionRightActionTest : VimTestCase() {
             Today it is not working
             The test is like that.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -158,8 +154,7 @@ class MotionRightActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -184,8 +179,7 @@ class MotionRightActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -210,8 +204,7 @@ class MotionRightActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -236,8 +229,7 @@ class MotionRightActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -261,8 +253,7 @@ class MotionRightActionTest : VimTestCase() {
         Sed in orci mauris.
         hard by the torrent of a mountain pass
       """.trimIndent().dotToTab(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -286,8 +277,7 @@ class MotionRightActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -311,8 +301,7 @@ class MotionRightActionTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.VISUAL(SelectionType.BLOCK_WISE),
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightInsertTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightInsertTest.kt
index 3ca0617cf..d791ad39a 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightInsertTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightInsertTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -26,7 +26,7 @@ class MotionRightInsertTest : VimTestCase() {
       """
           Oh, hi Ma${c}rk
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
+      Mode.INSERT,
     ) {
       enterCommand("set whichwrap=]")
     }
@@ -43,7 +43,7 @@ class MotionRightInsertTest : VimTestCase() {
       """
           Oh, hi Mark$c
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
+      Mode.INSERT,
     ) {
       enterCommand("set whichwrap=]")
     }
@@ -60,7 +60,7 @@ class MotionRightInsertTest : VimTestCase() {
       """
           Oh, hi Mark$c
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
+      Mode.INSERT,
     ) {
       enterCommand("set whichwrap=]")
     }
@@ -79,7 +79,7 @@ class MotionRightInsertTest : VimTestCase() {
           Oh, hi Mark
           ${c}You are my favourite customer
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
+      Mode.INSERT,
     ) {
       enterCommand("set whichwrap=]")
     }
@@ -102,7 +102,7 @@ class MotionRightInsertTest : VimTestCase() {
           $c
           You are my favourite customer
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
+      Mode.INSERT,
     ) {
       enterCommand("set whichwrap=]")
     }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightMatchCharActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightMatchCharActionTest.kt
index af7a265b9..84884c0bd 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightMatchCharActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightMatchCharActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -19,8 +19,7 @@ class MotionRightMatchCharActionTest : VimTestCase() {
       "fx;",
       "hello ${c}x hello x hello",
       "hello x hello ${c}x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -30,8 +29,7 @@ class MotionRightMatchCharActionTest : VimTestCase() {
       "fx;;",
       "${c}hello x hello x hello x hello",
       "hello x hello x hello ${c}x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -41,8 +39,7 @@ class MotionRightMatchCharActionTest : VimTestCase() {
       "fx2;",
       "${c}hello x hello x hello x hello",
       "hello x hello x hello ${c}x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -52,8 +49,7 @@ class MotionRightMatchCharActionTest : VimTestCase() {
       "fx3;",
       "${c}hello x hello x hello x hello x hello",
       "hello x hello x hello x hello ${c}x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightTillMatchCharActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightTillMatchCharActionTest.kt
index d4892e08e..5cf1def17 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightTillMatchCharActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionRightTillMatchCharActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -19,8 +19,7 @@ class MotionRightTillMatchCharActionTest : VimTestCase() {
       "tx;",
       "${c}hello x hello x hello",
       "hello x hello$c x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -30,8 +29,7 @@ class MotionRightTillMatchCharActionTest : VimTestCase() {
       "tx;;",
       "${c}hello x hello x hello x hello",
       "hello x hello x hello$c x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -41,8 +39,7 @@ class MotionRightTillMatchCharActionTest : VimTestCase() {
       "tx2;",
       "${c}hello x hello x hello x hello",
       "hello x hello$c x hello x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -52,8 +49,7 @@ class MotionRightTillMatchCharActionTest : VimTestCase() {
       "tx3;",
       "${c}hello x hello x hello x hello x hello",
       "hello x hello x hello$c x hello x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -63,8 +59,7 @@ class MotionRightTillMatchCharActionTest : VimTestCase() {
       "tx,",
       "hello x hello x ${c}hello x hello x hello",
       "hello x hello x$c hello x hello x hello",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftEndActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftEndActionTest.kt
index bf98c4d67..ab07f7928 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftEndActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftEndActionTest.kt
@@ -11,7 +11,8 @@
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -46,7 +47,7 @@ class MotionShiftEndActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.OPTION)
@@ -72,7 +73,7 @@ class MotionShiftEndActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestWithoutNeovim(SkipNeovimReason.OPTION)
@@ -98,7 +99,7 @@ class MotionShiftEndActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest(keys, before, after, Mode.SELECT(SelectionType.CHARACTER_WISE))
   }
 
   @OptionTest(
@@ -125,10 +126,10 @@ class MotionShiftEndActionTest : VimTestCase() {
     """.trimIndent()
     configureByText(before)
     typeText(injector.parser.parseKeys("<S-End>"))
-    assertState(VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    assertState(Mode.NORMAL())
     typeText(injector.parser.parseKeys("0v" + "<S-End>"))
     assertState(after)
-    assertState(VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    assertState(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @OptionTest(
@@ -155,9 +156,9 @@ class MotionShiftEndActionTest : VimTestCase() {
     """.trimIndent()
     configureByText(before)
     typeText(injector.parser.parseKeys("<S-End>"))
-    assertState(VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    assertState(Mode.NORMAL())
     typeText(injector.parser.parseKeys("0gh" + "<S-End>"))
     assertState(after)
-    assertState(VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    assertState(Mode.SELECT(SelectionType.CHARACTER_WISE))
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftHomeActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftHomeActionTest.kt
index 125318fa8..44491101d 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftHomeActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftHomeActionTest.kt
@@ -11,7 +11,8 @@
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -47,7 +48,7 @@ class MotionShiftHomeActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @OptionTest(
@@ -82,7 +83,7 @@ class MotionShiftHomeActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestWithoutNeovim(SkipNeovimReason.OPTION)
@@ -108,7 +109,7 @@ class MotionShiftHomeActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest(keys, before, after, Mode.SELECT(SelectionType.CHARACTER_WISE))
   }
 
   @OptionTest(
@@ -135,10 +136,10 @@ class MotionShiftHomeActionTest : VimTestCase() {
     """.trimIndent()
     configureByText(before)
     typeText(injector.parser.parseKeys("<S-Home>"))
-    assertState(VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    assertState(Mode.NORMAL())
     typeText(injector.parser.parseKeys("\$v" + "<S-Home>"))
     assertState(after)
-    assertState(VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    assertState(Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @OptionTest(
@@ -165,9 +166,9 @@ class MotionShiftHomeActionTest : VimTestCase() {
     """.trimIndent()
     configureByText(before)
     typeText(injector.parser.parseKeys("<S-Home>"))
-    assertState(VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    assertState(Mode.NORMAL())
     typeText(injector.parser.parseKeys("\$gh" + "<S-Home>"))
     assertState(after)
-    assertState(VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    assertState(Mode.SELECT(SelectionType.CHARACTER_WISE))
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftLeftActionHandlerTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftLeftActionHandlerTest.kt
index 3151d01ff..99edb321f 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftLeftActionHandlerTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftLeftActionHandlerTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -46,8 +47,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -75,8 +75,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -104,8 +103,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -133,8 +131,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -162,8 +159,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -191,8 +187,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -220,8 +215,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -249,8 +243,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -278,8 +271,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -307,8 +299,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -336,8 +327,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -365,8 +355,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -394,8 +383,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 ${se}where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -423,8 +411,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -452,8 +439,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -481,8 +467,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -510,8 +495,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -539,8 +523,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
     assertCaretsVisualAttributes()
   }
@@ -569,8 +552,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 wher$s${c}e ${se}it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
     assertCaretsVisualAttributes()
   }
@@ -599,8 +581,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -628,8 +609,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -657,8 +637,7 @@ class MotionShiftLeftActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftRightActionHandlerTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftRightActionHandlerTest.kt
index 4e39e5e13..b9fd89aaf 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftRightActionHandlerTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/leftright/MotionShiftRightActionHandlerTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.leftright
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -46,8 +47,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -75,8 +75,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -104,8 +103,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -133,8 +131,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -162,8 +159,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -191,8 +187,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -220,8 +215,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -249,8 +243,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass$s.$c$se
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -278,8 +271,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass$s.$c$se
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -307,8 +299,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -336,8 +327,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -365,8 +355,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -394,8 +383,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 ${s}hard by the torrent of a mountain pass.$c$se
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -423,8 +411,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 ${s}hard by the torrent of a mountain pass.$c$se
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -452,8 +439,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -481,8 +467,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -510,8 +495,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -539,8 +523,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass$s.$c$se
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -568,8 +551,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -597,8 +579,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -626,8 +607,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -655,8 +635,7 @@ class MotionShiftRightActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerBigWordActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerBigWordActionTest.kt
index 01ec1cbe4..a3a0b5c8e 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerBigWordActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerBigWordActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.`object`
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -29,8 +29,7 @@ class MotionInnerBigWordActionTest : VimTestCase() {
           Sed in orci mauris.
           hard by the torrent of a mountain$c 
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerBlockDoubleQuoteActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerBlockDoubleQuoteActionTest.kt
index 3306b028d..54053150b 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerBlockDoubleQuoteActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerBlockDoubleQuoteActionTest.kt
@@ -8,13 +8,13 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.`object`
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
 class MotionInnerBlockDoubleQuoteActionTest : VimTestCase() {
   @Test
   fun `test change outside quotes`() {
-    doTest("di\"", "${c}print(\"hello\")", "print(\"$c\")", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("di\"", "${c}print(\"hello\")", "print(\"$c\")", Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerBlockTagActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerBlockTagActionTest.kt
index 5ac320a59..52e4c1430 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerBlockTagActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerBlockTagActionTest.kt
@@ -9,7 +9,8 @@
 package org.jetbrains.plugins.ideavim.action.motion.`object`
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -27,7 +28,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
       "</template>\n"
 
     val after = "<template name=\"hello\"></template>\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -41,7 +42,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
       "</template>\n"
 
     val after = "<template name=\"hello\"></template>\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -54,7 +55,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
       "</template>\n"
 
     val after = "<template name=\"hello\"></template>\n"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -63,7 +64,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abc${c}de<tag>fg</tag>hi"
     val after = "abcde<tag>fg</tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -72,7 +73,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde<ta${c}g>fg</tag>hi"
     val after = "abcde<tag></tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -81,7 +82,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde<ta${c}g>fg</tag>"
     val after = "abcde<tag></tag>"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -90,7 +91,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "<ta${c}g>fg</tag>hi"
     val after = "<tag></tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -99,7 +100,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde<ta${c}g name = \"name\">fg</tag>hi"
     val after = "abcde<tag name = \"name\"></tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -108,7 +109,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde<tag>f${c}g</tag>hi"
     val after = "abcde<tag></tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -118,7 +119,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde<[abc]*>af${c}gbc</[abc]*>hi"
     val after = "abcde<[abc]*></[abc]*>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -127,7 +128,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde<tAg>f${c}g</tag>hi"
     val after = "abcde<tAg></tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -136,7 +137,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde<tag>f${c}g</TAG>hi"
     val after = "abcde<tag></TAG>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -145,7 +146,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde< tag>f${c}g</ tag>hi"
     val after = "abcde< tag>fg</ tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -154,7 +155,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde<tag >f${c}g</tag>hi"
     val after = "abcde<tag ></tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -163,7 +164,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde<tag name = \"name\">f${c}g</tag>hi"
     val after = "abcde<tag name = \"name\"></tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -172,7 +173,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde<tag>fg</ta${c}g>hi"
     val after = "abcde<tag></tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -181,7 +182,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde<tag>fg</tag>h${c}i"
     val after = "abcde<tag>fg</tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -190,7 +191,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde<ta${c}g>fghi"
     val after = "abcde<tag>fghi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -199,7 +200,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abc${c}de"
     val after = "abcde"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -208,7 +209,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abc${c}defg</tag>hi"
     val after = "abcdefg</tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -217,7 +218,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcdefg</ta${c}g>hi"
     val after = "abcdefg</tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -226,7 +227,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcdefg</tag>h${c}i"
     val after = "abcdefg</tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -235,7 +236,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abc${c}defg<tag>hi"
     val after = "abcdefg<tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -244,7 +245,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcdefg<ta${c}g>hi"
     val after = "abcdefg<tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -253,7 +254,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcdefg<tag>h${c}i"
     val after = "abcdefg<tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -262,7 +263,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abc${c}de</tag>fg<tag>hi"
     val after = "abcde</tag>fg<tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -271,7 +272,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde</ta${c}g>fg<tag>hi"
     val after = "abcde</tag>fg<tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -280,7 +281,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde</tag>f${c}g<tag>hi"
     val after = "abcde</tag>fg<tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -289,7 +290,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde</tag>fg<ta${c}g>hi"
     val after = "abcde</tag>fg<tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -299,7 +300,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "<foo><html>t${c}ext</foo></html>"
     val after = "<foo></foo></html>"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -309,7 +310,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "<foo><html>text</foo></htm${c}l>"
     val after = "<foo><html></html>"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -318,7 +319,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde</tag>fg<tag>h${c}i"
     val after = "abcde</tag>fg<tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -327,7 +328,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde<tag>f$c<>g</tag>hi"
     val after = "abcde<tag></tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -336,7 +337,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "abcde<tag>f${c}\"<>\"g</tag>hi"
     val after = "abcde<tag></tag>hi"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -345,7 +346,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "<a> <as${c}df> </A>"
     val after = "<a></A>"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |d| |v_it|
@@ -354,7 +355,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "<a href=\"https://isitchristmas.com\" class=\"button\">Bing ${c}Bing bing</a>"
     val after = "<a href=\"https://isitchristmas.com\" class=\"button\"></a>"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // VIM-1090 |d| |v_it|
@@ -364,7 +365,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val keys = listOf("dit")
     val before = "<b>as${c}d<i>as<b />df</i>asdf</b>"
     val after = "<b></b>"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   // |v_it|
@@ -383,7 +384,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
       
     """.trimIndent()
     val keys = listOf("vit")
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertPluginError(true)
   }
 
@@ -394,7 +395,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val after = "$s<a></a$c>$se"
     configureByText(before)
     val keys = listOf("vit")
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @Test
@@ -403,7 +404,7 @@ class MotionInnerBlockTagActionTest : VimTestCase() {
     val before = "<a>${c}a</a>"
     val after = "$s<a>a</a$c>$se"
     val keys = listOf("vit")
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @Test
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerWordActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerWordActionTest.kt
index 5f6bd6147..e4f4ad3c2 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerWordActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionInnerWordActionTest.kt
@@ -8,7 +8,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.`object`
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -19,8 +20,7 @@ class MotionInnerWordActionTest : VimTestCase() {
       "viw",
       "",
       "",
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionOuterBigWordActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionOuterBigWordActionTest.kt
index 414bf3b4c..03efd8ba1 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionOuterBigWordActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionOuterBigWordActionTest.kt
@@ -8,7 +8,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.`object`
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -32,8 +33,7 @@ class MotionOuterBigWordActionTest : VimTestCase() {
       where it was settled on some sodden sand
       ${c}hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -49,8 +49,7 @@ class MotionOuterBigWordActionTest : VimTestCase() {
       I found it in a ${s}legendary land
       $c}$se
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionOuterSentenceActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionOuterSentenceActionTest.kt
index fddf84465..88a562fd4 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionOuterSentenceActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/object/MotionOuterSentenceActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.`object`
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
@@ -38,8 +38,7 @@ class MotionOuterSentenceActionTest : VimTestCase() {
         Sed in orci mauris.
         
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -62,8 +61,7 @@ class MotionOuterSentenceActionTest : VimTestCase() {
         $c
       """.trimIndent(),
       "\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/search/SearchWholeWordBackwardActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/search/SearchWholeWordBackwardActionTest.kt
index 77012d323..1fa871277 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/search/SearchWholeWordBackwardActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/search/SearchWholeWordBackwardActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.search
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -18,7 +18,7 @@ import org.junit.jupiter.api.Test
 class SearchWholeWordBackwardActionTest : VimTestCase() {
   @Test
   fun `test backward search on empty string`() {
-    doTest("#", "", "", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("#", "", "", Mode.NORMAL())
     assertPluginError(false)
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/search/SearchWholeWordForwardActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/search/SearchWholeWordForwardActionTest.kt
index fc71eca6b..0c660c265 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/search/SearchWholeWordForwardActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/search/SearchWholeWordForwardActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.search
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
@@ -27,12 +27,12 @@ class SearchWholeWordForwardActionTest : VimTestCase() {
   .hello 2
   .${c}hello 3
     """.trimIndent().dotToTab()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
   fun `test backward search on empty string`() {
-    doTest("*", "", "", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("*", "", "", Mode.NORMAL())
     assertPluginError(false)
   }
 
@@ -61,8 +61,7 @@ class SearchWholeWordForwardActionTest : VimTestCase() {
           where it was settled on some sodden sand
           hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -82,8 +81,7 @@ class SearchWholeWordForwardActionTest : VimTestCase() {
           where it was settled on some sodden sand
           hard by the torrent of a mountain pass
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEnableBlockModeActionHandlerTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEnableBlockModeActionHandlerTest.kt
index c3ebef333..e99b8ea64 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEnableBlockModeActionHandlerTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEnableBlockModeActionHandlerTest.kt
@@ -8,7 +8,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.select
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -36,8 +37,7 @@ class SelectEnableBlockModeActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -62,8 +62,7 @@ class SelectEnableBlockModeActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass$s.$c$se
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -87,8 +86,7 @@ class SelectEnableBlockModeActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -113,8 +111,7 @@ class SelectEnableBlockModeActionHandlerTest : VimTestCase() {
                 where it was ${s}s$c${se}ettled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEnableCharacterModeActionHandlerTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEnableCharacterModeActionHandlerTest.kt
index 5e28d914b..1eeeaa2ee 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEnableCharacterModeActionHandlerTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEnableCharacterModeActionHandlerTest.kt
@@ -8,7 +8,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.select
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -36,8 +37,7 @@ class SelectEnableCharacterModeActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -62,8 +62,7 @@ class SelectEnableCharacterModeActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass$s.$c$se
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -87,8 +86,7 @@ class SelectEnableCharacterModeActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -113,8 +111,7 @@ class SelectEnableCharacterModeActionHandlerTest : VimTestCase() {
                 where it was ${s}s$c${se}ettled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEnableLineModeActionHandlerTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEnableLineModeActionHandlerTest.kt
index bdb43e0db..317613bfd 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEnableLineModeActionHandlerTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEnableLineModeActionHandlerTest.kt
@@ -8,7 +8,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.select
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -35,8 +36,7 @@ class SelectEnableLineModeActionHandlerTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -60,8 +60,7 @@ class SelectEnableLineModeActionHandlerTest : VimTestCase() {
                 Sed in orci mauris.
                 ${s}hard by the torrent of a mountain pass$c.$se
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -85,8 +84,7 @@ class SelectEnableLineModeActionHandlerTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -111,8 +109,7 @@ class SelectEnableLineModeActionHandlerTest : VimTestCase() {
                 ${s}where it was ${c}settled on some sodden sand$se
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEscapeActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEscapeActionTest.kt
index 8ac6b045f..8e34dfdb0 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEscapeActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectEscapeActionTest.kt
@@ -9,7 +9,7 @@
 package org.jetbrains.plugins.ideavim.action.motion.select
 
 import com.intellij.openapi.editor.Caret
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -37,10 +37,9 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@@ -64,10 +63,9 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@@ -91,10 +89,9 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@@ -118,10 +115,9 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@@ -145,10 +141,9 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@@ -172,10 +167,9 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@@ -199,10 +193,9 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@@ -226,10 +219,9 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@@ -253,10 +245,9 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@@ -280,10 +271,9 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@@ -307,10 +297,9 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@@ -334,13 +323,12 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     kotlin.test.assertFalse(fixture.editor.caretModel.allCarets.any(Caret::hasSelection))
     kotlin.test.assertEquals(1, fixture.editor.caretModel.caretCount)
     assertCaretsVisualAttributes()
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@@ -364,13 +352,12 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     kotlin.test.assertFalse(fixture.editor.caretModel.allCarets.any(Caret::hasSelection))
     kotlin.test.assertEquals(1, fixture.editor.caretModel.caretCount)
     assertCaretsVisualAttributes()
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@@ -394,13 +381,12 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     kotlin.test.assertFalse(fixture.editor.caretModel.allCarets.any(Caret::hasSelection))
     kotlin.test.assertEquals(1, fixture.editor.caretModel.caretCount)
     assertCaretsVisualAttributes()
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.SELECT_MODE)
@@ -424,12 +410,11 @@ class SelectEscapeActionTest : VimTestCase() {
                 where it was settled on some sodden sand12${c}3
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     kotlin.test.assertFalse(fixture.editor.caretModel.allCarets.any(Caret::hasSelection))
     kotlin.test.assertEquals(1, fixture.editor.caretModel.caretCount)
     assertCaretsVisualAttributes()
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectKeyHandlerTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectKeyHandlerTest.kt
index f488edf54..dc9f9a24d 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectKeyHandlerTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectKeyHandlerTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.select
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -41,8 +41,7 @@ class SelectKeyHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -68,8 +67,7 @@ class SelectKeyHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -94,8 +92,7 @@ class SelectKeyHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -120,8 +117,7 @@ class SelectKeyHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -147,8 +143,7 @@ class SelectKeyHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -174,8 +169,7 @@ class SelectKeyHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -201,8 +195,7 @@ class SelectKeyHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -228,8 +221,7 @@ class SelectKeyHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -255,8 +247,7 @@ class SelectKeyHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -292,8 +283,7 @@ class SelectKeyHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -319,8 +309,7 @@ class SelectKeyHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -346,10 +335,9 @@ class SelectKeyHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertCaretsVisualAttributes()
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectToggleVisualModeHandlerTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectToggleVisualModeHandlerTest.kt
index 9d3a022cd..68e4e5f86 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectToggleVisualModeHandlerTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/SelectToggleVisualModeHandlerTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.select
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -39,8 +40,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -65,8 +65,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -91,8 +90,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -117,8 +115,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -143,8 +140,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -169,8 +165,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was ${s}settled$c$se on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -195,8 +190,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -221,8 +215,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -247,8 +240,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -273,8 +265,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -299,8 +290,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -335,8 +325,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -371,8 +360,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -397,8 +385,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was ${s}sett${c}l${se}ed on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -423,8 +410,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -449,8 +435,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 ${se}where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -475,8 +460,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -501,8 +485,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 ${s}where it was settle${c}d on some sodden sand
                 ${se}hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -527,8 +510,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.VISUAL(SelectionType.LINE_WISE),
     )
   }
 
@@ -553,8 +535,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.VISUAL(SelectionType.LINE_WISE),
     )
   }
 
@@ -579,8 +560,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 ${s}where it was ${c}settled on some sodden sand$se
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.VISUAL(SelectionType.LINE_WISE),
     )
   }
 
@@ -605,8 +585,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 wh${s}ere i$c${se}t was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -631,8 +610,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 wh${s}${c}ere it${se} was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -657,8 +635,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 wh${s}ere ${c}i${se}t was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.VISUAL(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -683,8 +660,7 @@ class SelectToggleVisualModeHandlerTest : VimTestCase() {
                 where$s${c} it${se} was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.VISUAL(SelectionType.BLOCK_WISE),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/motion/SelectMotionLeftActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/motion/SelectMotionLeftActionTest.kt
index 0ff8213c7..7f733e490 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/motion/SelectMotionLeftActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/motion/SelectMotionLeftActionTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.select.motion
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -48,8 +49,7 @@ class SelectMotionLeftActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -74,8 +74,7 @@ class SelectMotionLeftActionTest : VimTestCase() {
                 ${c}Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -100,8 +99,7 @@ class SelectMotionLeftActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/motion/SelectMotionRightActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/motion/SelectMotionRightActionTest.kt
index 44bc95979..740364094 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/motion/SelectMotionRightActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/select/motion/SelectMotionRightActionTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.select.motion
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -43,8 +44,7 @@ class SelectMotionRightActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -69,8 +69,7 @@ class SelectMotionRightActionTest : VimTestCase() {
                 where it was settled on some sodden san${c}d
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -95,8 +94,7 @@ class SelectMotionRightActionTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionCamelEndLeftActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionCamelEndLeftActionTest.kt
index e7ead384c..6165833a2 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionCamelEndLeftActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionCamelEndLeftActionTest.kt
@@ -8,13 +8,13 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.text
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
 class MotionCamelEndLeftActionTest : VimTestCase() {
   @Test
   fun `test go with a single uppercase word`() {
-    doTest("]b", "TES${c}T", "TES${c}T", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("]b", "TES${c}T", "TES${c}T", Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionNthCharacterActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionNthCharacterActionTest.kt
index e08d83fee..a49726b38 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionNthCharacterActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionNthCharacterActionTest.kt
@@ -8,7 +8,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.text
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
@@ -34,8 +35,7 @@ class MotionNthCharacterActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -59,8 +59,7 @@ class MotionNthCharacterActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -84,8 +83,7 @@ class MotionNthCharacterActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -109,8 +107,7 @@ class MotionNthCharacterActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -134,8 +131,7 @@ class MotionNthCharacterActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -159,8 +155,7 @@ class MotionNthCharacterActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass$c.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -194,8 +189,7 @@ class MotionNthCharacterActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass$c.$se
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -232,8 +226,7 @@ class MotionNthCharacterActionTest : VimTestCase() {
             hard by the torrent of a mountain pass$c.
             
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionParagraphNextActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionParagraphNextActionTest.kt
index 0884fb596..de598df38 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionParagraphNextActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionParagraphNextActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.text
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -34,8 +34,7 @@ class MotionParagraphNextActionTest : VimTestCase() {
         void baz() {
         }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionParagraphPreviousActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionParagraphPreviousActionTest.kt
index 45d5b5732..398caab48 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionParagraphPreviousActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionParagraphPreviousActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.text
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
@@ -27,8 +27,7 @@ class MotionParagraphPreviousActionTest : VimTestCase() {
       hard by the torrent of a mountain pass$c.
       """.trimIndent(),
       ".",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSectionForwardEndActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSectionForwardEndActionTest.kt
index 4ac97c908..3f19a83fc 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSectionForwardEndActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSectionForwardEndActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.text
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
@@ -26,8 +26,7 @@ class MotionSectionForwardEndActionTest : VimTestCase() {
           Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
       "$c.",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -44,8 +43,7 @@ class MotionSectionForwardEndActionTest : VimTestCase() {
           
       """.trimIndent(),
       "$c.\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -83,8 +81,7 @@ class MotionSectionForwardEndActionTest : VimTestCase() {
         }
       }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -122,8 +119,7 @@ class MotionSectionForwardEndActionTest : VimTestCase() {
         }
       }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -161,8 +157,7 @@ class MotionSectionForwardEndActionTest : VimTestCase() {
         }
       $c}
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSectionForwardStartActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSectionForwardStartActionTest.kt
index 0954c714e..69596399e 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSectionForwardStartActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSectionForwardStartActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.text
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -47,8 +47,7 @@ class MotionSectionForwardStartActionTest : VimTestCase() {
         }
       }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -86,8 +85,7 @@ class MotionSectionForwardStartActionTest : VimTestCase() {
         }
       }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -125,8 +123,7 @@ class MotionSectionForwardStartActionTest : VimTestCase() {
         }
       $c}
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSentenceNextStartActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSentenceNextStartActionTest.kt
index 16513a6f2..bea051747 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSentenceNextStartActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSentenceNextStartActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.text
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -30,8 +30,7 @@ class MotionSentenceNextStartActionTest : VimTestCase() {
         Cras id tellus in 
         
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSentencePreviousStartActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSentencePreviousStartActionTest.kt
index c7f1d73b2..40adcf550 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSentencePreviousStartActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionSentencePreviousStartActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.text
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -31,8 +31,7 @@ class MotionSentencePreviousStartActionTest : VimTestCase() {
         where it was settled on some sodden sand
         hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionUnmatchedBraceCloseActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionUnmatchedBraceCloseActionTest.kt
index 9a375f317..16950f93a 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionUnmatchedBraceCloseActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionUnmatchedBraceCloseActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.text
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -27,8 +27,7 @@ class MotionUnmatchedBraceCloseActionTest : VimTestCase() {
         
       $c}
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -50,8 +49,7 @@ class MotionUnmatchedBraceCloseActionTest : VimTestCase() {
         $c}
       }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -75,8 +73,7 @@ class MotionUnmatchedBraceCloseActionTest : VimTestCase() {
         $c}
       }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -98,8 +95,7 @@ class MotionUnmatchedBraceCloseActionTest : VimTestCase() {
         }
       $c}
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -121,8 +117,7 @@ class MotionUnmatchedBraceCloseActionTest : VimTestCase() {
         }
       $c}
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -144,8 +139,7 @@ class MotionUnmatchedBraceCloseActionTest : VimTestCase() {
         }
       $c}
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -155,8 +149,7 @@ class MotionUnmatchedBraceCloseActionTest : VimTestCase() {
       "]}",
       """ {$c {}} """,
       """ { {}$c} """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionUnmatchedBraceOpenActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionUnmatchedBraceOpenActionTest.kt
index a79008c39..c6de2c1ff 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionUnmatchedBraceOpenActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/text/MotionUnmatchedBraceOpenActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.text
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
@@ -28,8 +28,7 @@ class MotionUnmatchedBraceOpenActionTest : VimTestCase() {
         
       }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -51,8 +50,7 @@ class MotionUnmatchedBraceOpenActionTest : VimTestCase() {
         }
       }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -74,8 +72,7 @@ class MotionUnmatchedBraceOpenActionTest : VimTestCase() {
         }
       }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -97,8 +94,7 @@ class MotionUnmatchedBraceOpenActionTest : VimTestCase() {
         }
       }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -129,8 +125,7 @@ class MotionUnmatchedBraceOpenActionTest : VimTestCase() {
         }
       }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionArrowDownActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionArrowDownActionTest.kt
index 137584800..b9ae1d84d 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionArrowDownActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionArrowDownActionTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.updown
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -46,8 +47,7 @@ class MotionArrowDownActionTest : VimTestCase() {
                 wher${c}e${se} it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -102,8 +102,7 @@ class MotionArrowDownActionTest : VimTestCase() {
                 wher${c}e${se} it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -185,8 +184,7 @@ class MotionArrowDownActionTest : VimTestCase() {
                 where${c}${se} it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionArrowUpActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionArrowUpActionTest.kt
index 2930088e6..998cd4301 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionArrowUpActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionArrowUpActionTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.updown
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -43,8 +44,7 @@ class MotionArrowUpActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -93,8 +93,7 @@ class MotionArrowUpActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -191,8 +190,7 @@ class MotionArrowUpActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionDownActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionDownActionTest.kt
index c44992734..69095ae0a 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionDownActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionDownActionTest.kt
@@ -13,9 +13,11 @@ package org.jetbrains.plugins.ideavim.action.motion.updown
 import com.intellij.codeInsight.daemon.impl.HintRenderer
 import com.maddyhome.idea.vim.api.getVisualLineCount
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.newapi.IjVimEditor
 import org.jetbrains.plugins.ideavim.VimTestCase
+import org.junit.jupiter.api.Disabled
 import org.junit.jupiter.api.Test
 import kotlin.test.assertEquals
 
@@ -42,7 +44,7 @@ class MotionDownActionTest : VimTestCase() {
             wh|${s}e${se}re i|t was settled on some sodden sand
             ha|${s}r${se}d by| the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_BLOCK)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.BLOCK_WISE))
   }
 
   @Test
@@ -64,7 +66,65 @@ class MotionDownActionTest : VimTestCase() {
             wh|${s}ere it was settled on some sodden sand[additional Chars]${c}${se}
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_BLOCK)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.BLOCK_WISE))
+  }
+
+  @Test
+  @Disabled
+  fun `test motion down in visual block mode with dollar motion2`() {
+    val keys = "i<C-O>d<ESC>"
+    val before = """
+            A Discovery
+
+            I |${c}found it in a legendary land
+            al|l rocks and lavender and tufted grass,
+            wh|ere it was settled on some sodden sand[additional Chars]
+            hard by the torrent of a mountain pass.
+    """.trimIndent()
+    val after = """
+            A Discovery
+
+            I |${s}found it in a legendary lan${c}d${se}
+            al|${s}l rocks and lavender and tufted grass${c},${se}
+            wh|${s}ere it was settled on some sodden sand[additional Chars]${c}${se}
+            hard by the torrent of a mountain pass.
+    """.trimIndent()
+    configureByTextX("aa.txt", before)
+    typeText(keys)
+//    println("Mode: " + mode())
+//    doTest(keys, before, after, MyMode.VISUAL(SelectionType.BLOCK_WISE))
+  }
+
+  @Test
+  @Disabled
+  fun `test motion down in visual block mode with dollar motion3`() {
+    val keys = "v"
+    val before = """
+            A Discovery
+
+            I |${c}found it in a legendary land
+            al|l rocks and lavender and tufted grass,
+            wh|ere it was settled on some sodden sand[additional Chars]
+            hard by the torrent of a mountain pass.
+    """.trimIndent()
+    val after = """
+            A Discovery
+
+            I |${s}found it in a legendary lan${c}d${se}
+            al|${s}l rocks and lavender and tufted grass${c},${se}
+            wh|${s}ere it was settled on some sodden sand[additional Chars]${c}${se}
+            hard by the torrent of a mountain pass.
+    """.trimIndent()
+    configureByTextX("aa.txt", before)
+    typeText(keys)
+    println("register " + this.register("'<"))
+    println("Mode: " + mode())
+    typeText("h")
+    println("register " + this.register("'<"))
+    typeText(":")
+    println("register " + this.register("'<"))
+//    println("Mode: " + mode())
+//    doTest(keys, before, after, MyMode.VISUAL(SelectionType.BLOCK_WISE))
   }
 
   @Test
@@ -79,7 +139,7 @@ class MotionDownActionTest : VimTestCase() {
             
             ${c}all rocks and lavender and tufted grass,
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -250,8 +310,7 @@ class MotionDownActionTest : VimTestCase() {
             I found it in a legendary land
             ${c}
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -267,8 +326,7 @@ class MotionDownActionTest : VimTestCase() {
             |
             |${c}
       """.trimMargin(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionGotoLineFirstActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionGotoLineFirstActionTest.kt
index d7ace66e9..31e452b03 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionGotoLineFirstActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionGotoLineFirstActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.updown
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -35,8 +35,7 @@ class MotionGotoLineFirstActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -60,8 +59,7 @@ class MotionGotoLineFirstActionTest : VimTestCase() {
                 ${c}where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -85,8 +83,7 @@ class MotionGotoLineFirstActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 ${c}hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -110,8 +107,7 @@ class MotionGotoLineFirstActionTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -135,8 +131,7 @@ class MotionGotoLineFirstActionTest : VimTestCase() {
         |       where it was settled on some sodden sand
         |       hard by the torrent of a mountain pass.
       """.trimMargin(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionPercentOrMatchActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionPercentOrMatchActionTest.kt
index 191b76493..a5dc85035 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionPercentOrMatchActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionPercentOrMatchActionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.updown
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -191,8 +191,7 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
              }
             $c}
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -216,8 +215,7 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
             ""${'"'}
             )
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -227,8 +225,7 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
       "%",
       """ "I found ${c}it in a (legendary) land" """,
       """ "I found it in a (legendary$c) land" """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -238,8 +235,7 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
       "%",
       """ "I found ${c}it in \"a (legendary) land" """,
       """ "I found it in \"a (legendary$c) land" """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -249,8 +245,7 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
       "%",
       """ $c "I found it in \"a (legendary) land" """,
       """  "I found it in \"a (legendary$c) land" """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -260,8 +255,7 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
       "%",
       """ "I found ${c}it in \\\"a (legendary) land" """,
       """ "I found it in \\\"a (legendary$c) land" """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -271,8 +265,7 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
       "%",
       """ debugPrint$c(\(var)) """,
       """ debugPrint(\(var)$c) """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -282,8 +275,7 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
       "%",
       """ debugPrint(\(var)$c) """,
       """ debugPrint$c(\(var)) """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -293,8 +285,7 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
       "%",
       """ debugPrint(\$c(var)) """,
       """ debugPrint(\(var$c)) """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -304,8 +295,7 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
       "%",
       """ debugPrint(\$c(var)) """,
       """ debugPrint(\(var$c)) """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -315,21 +305,20 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
       "%",
       """ "I found ${c}it in a \(legendary\) land" """,
       """ "I found it in a \(legendary\$c) land" """,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
   @TestWithoutNeovim(SkipNeovimReason.PLUGIN, description = "Matchit plugin affects neovim")
   @Test
   fun `test deleting with percent motion backward`() {
-    doTest("d%", "(foo bar$c)", c, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("d%", "(foo bar$c)", c, Mode.NORMAL())
   }
 
   @TestWithoutNeovim(SkipNeovimReason.PLUGIN, description = "Matchit plugin affects neovim")
   @Test
   fun `test deleting with percent motion`() {
-    doTest("d%", "$c(foo bar)", c, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("d%", "$c(foo bar)", c, Mode.NORMAL())
   }
 
   @Test
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionShiftDownActionHandlerTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionShiftDownActionHandlerTest.kt
index 118122180..621d3676a 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionShiftDownActionHandlerTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionShiftDownActionHandlerTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.updown
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -46,8 +47,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -75,8 +75,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 wh${c}e${se}re it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -104,8 +103,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.[additio${c}n${se}al chars]
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -133,8 +131,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -162,8 +159,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 wh${c}${se}ere it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -191,8 +187,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -220,8 +215,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -249,8 +243,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -278,8 +271,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard ${s}b$c${se}y the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -307,8 +299,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard ${s}b$c${se}y the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -336,8 +327,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 ${se}where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -365,8 +355,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -394,8 +383,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -423,8 +411,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 ${s}hard by the ${c}torrent of a mountain pass.$se
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -452,8 +439,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 ${se}where it was settled on some sodden sand
                 ${s}hard by the ${c}torrent of a mountain pass.$se
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -481,8 +467,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -510,8 +495,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -539,8 +523,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -568,8 +551,7 @@ class MotionShiftDownActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the ${s}t$c${se}orrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionShiftUpActionHandlerTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionShiftUpActionHandlerTest.kt
index d54a61c57..e655a4a1c 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionShiftUpActionHandlerTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/updown/MotionShiftUpActionHandlerTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.updown
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.options.OptionConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -46,8 +47,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -75,8 +75,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 whe${se}re it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -104,8 +103,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.[addition${se}al chars]
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -133,8 +131,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -162,8 +159,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 wh${se}ere it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -191,8 +187,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -220,8 +215,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -249,8 +243,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -278,8 +271,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -307,8 +299,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was ${se}settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.SELECT(SelectionType.CHARACTER_WISE)
     )
   }
 
@@ -336,8 +327,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 ${se}where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -365,8 +355,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -394,8 +383,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -423,8 +411,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -452,8 +439,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 ${se}where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.SELECT(SelectionType.LINE_WISE),
     )
   }
 
@@ -481,8 +467,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -510,8 +495,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -539,8 +523,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -568,8 +551,7 @@ class MotionShiftUpActionHandlerTest : VimTestCase() {
                 where it was settled on some sodden sand
                 hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.SELECT,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.SELECT(SelectionType.BLOCK_WISE),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualExitModeActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualExitModeActionTest.kt
index 8091bf7e3..7ea8b55ae 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualExitModeActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualExitModeActionTest.kt
@@ -8,20 +8,20 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.visual
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
 class VisualExitModeActionTest : VimTestCase() {
   @Test
   fun `test exit visual mode after line end`() {
-    doTest("vl<Esc>", "12${c}3", "12${c}3", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("vl<Esc>", "12${c}3", "12${c}3", Mode.NORMAL())
     assertCaretsVisualAttributes()
   }
 
   @Test
   fun `test double exit`() {
-    doTest("vl<Esc><Esc>", "12${c}3", "12${c}3", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("vl<Esc><Esc>", "12${c}3", "12${c}3", Mode.NORMAL())
     assertCaretsVisualAttributes()
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualSwapEndsBlockActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualSwapEndsBlockActionTest.kt
index 82cff93b2..873e16299 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualSwapEndsBlockActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualSwapEndsBlockActionTest.kt
@@ -12,7 +12,8 @@ package org.jetbrains.plugins.ideavim.action.motion.visual
 
 import com.intellij.openapi.editor.LogicalPosition
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -39,7 +40,7 @@ class VisualSwapEndsBlockActionTest : VimTestCase() {
             wh${s}${c}|ere i|${se}t was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_BLOCK)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.BLOCK_WISE))
     kotlin.test.assertEquals(LogicalPosition(4, 2), fixture.editor.caretModel.primaryCaret.logicalPosition)
   }
 
@@ -63,7 +64,7 @@ class VisualSwapEndsBlockActionTest : VimTestCase() {
             wh${s}|ere i${c}|${se}t was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_BLOCK)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.BLOCK_WISE))
     kotlin.test.assertEquals(LogicalPosition(4, 8), fixture.editor.caretModel.primaryCaret.logicalPosition)
   }
 
@@ -87,7 +88,7 @@ class VisualSwapEndsBlockActionTest : VimTestCase() {
             wh${s}${c}|ere i|${se}t was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_BLOCK)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.BLOCK_WISE))
     kotlin.test.assertEquals(LogicalPosition(2, 2), fixture.editor.caretModel.primaryCaret.logicalPosition)
   }
 
@@ -111,7 +112,7 @@ class VisualSwapEndsBlockActionTest : VimTestCase() {
             wh${s}|ere i${c}|${se}t was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_BLOCK)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.BLOCK_WISE))
     kotlin.test.assertEquals(LogicalPosition(2, 8), fixture.editor.caretModel.primaryCaret.logicalPosition)
   }
 
@@ -135,7 +136,7 @@ class VisualSwapEndsBlockActionTest : VimTestCase() {
             where it was settled on so${s}${c}me sodden sand{some${se} new symbols}
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_BLOCK)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.BLOCK_WISE))
     kotlin.test.assertEquals(LogicalPosition(4, 26), fixture.editor.caretModel.primaryCaret.logicalPosition)
   }
 
@@ -159,7 +160,7 @@ class VisualSwapEndsBlockActionTest : VimTestCase() {
             where it was settled on some sodden sand
             hard by the torrent of a mountain pass.
     """.trimIndent()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_BLOCK)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.BLOCK_WISE))
     kotlin.test.assertEquals(LogicalPosition(3, 26), fixture.editor.caretModel.primaryCaret.logicalPosition)
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualToggleBlockModeActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualToggleBlockModeActionTest.kt
index 4588f6b4d..3d2ecac28 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualToggleBlockModeActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualToggleBlockModeActionTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.visual
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -35,8 +36,7 @@ class VisualToggleBlockModeActionTest : VimTestCase() {
                     Sed in orci mauris.
                     Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.VISUAL(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -60,8 +60,7 @@ class VisualToggleBlockModeActionTest : VimTestCase() {
                     Sed in orci mauris.
                     Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.VISUAL(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -85,8 +84,7 @@ class VisualToggleBlockModeActionTest : VimTestCase() {
                     Sed in orci mauris.
                     Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.VISUAL(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -96,8 +94,7 @@ class VisualToggleBlockModeActionTest : VimTestCase() {
       "<C-V>",
       "",
       "",
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.VISUAL(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -115,6 +112,6 @@ class VisualToggleBlockModeActionTest : VimTestCase() {
     )
     enterCommand("set selectmode=cmd")
     typeText("<C-V>")
-    assertState(VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_BLOCK)
+    assertState(Mode.SELECT(SelectionType.BLOCK_WISE))
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualToggleCharacterModeActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualToggleCharacterModeActionTest.kt
index c157d616e..2e0ffec83 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualToggleCharacterModeActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualToggleCharacterModeActionTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.visual
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import com.maddyhome.idea.vim.helper.vimSelectionStart
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -124,8 +125,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -149,8 +149,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -174,8 +173,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -199,8 +197,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -224,8 +221,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -249,8 +245,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -274,8 +269,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -299,8 +293,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -324,8 +317,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -348,8 +340,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     har${c}d${se} by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -372,8 +363,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     har${c}d${se} by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -396,8 +386,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     whe${c}r${se}e it was settled on some sodden sand
                     hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -430,8 +419,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                   w${s}here it was settled on some sodden sand${c}${se}
                   hard by the torrent of a mountain pass.
               """.trimIndent(),*/
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -466,8 +454,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                    w${s}here it was settled on some sodden sand[long line]
                    hard by the torrent of a mountain pass.${c}${se}
                """.trimIndent(),*/
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -490,8 +477,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     ${se}where it was settled on some sodden sand
                     hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.VISUAL(SelectionType.LINE_WISE),
     )
   }
 
@@ -521,8 +507,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     hard by the torrent of a mountain pass.
                     ${c}${se}
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.VISUAL(SelectionType.LINE_WISE),
     )
   }
 
@@ -545,8 +530,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     ${s}${c}where it was settled on some sodden sand
                     ${se}hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.VISUAL(SelectionType.LINE_WISE),
     )
   }
 
@@ -569,8 +553,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     ${c}where it was settled on some sodden sand
                     ${se}hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.VISUAL(SelectionType.LINE_WISE),
     )
   }
 
@@ -593,8 +576,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     ${c}hard by the torrent of a mountain pass.${se}
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.VISUAL(SelectionType.LINE_WISE),
     )
   }
 
@@ -618,8 +600,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     ${se}where it was settled on some sodden sand
                     hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.VISUAL(SelectionType.LINE_WISE),
     )
   }
 
@@ -643,8 +624,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.VISUAL(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -668,8 +648,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.VISUAL(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -693,8 +672,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     wh${s}ere${c} ${se}it was settled on some sodden sand
                     ha${s}rd ${c}b${se}y the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.VISUAL(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -718,8 +696,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                     wh${s}ere it was settled on some sodden sa${c}n${se}d[long line]
                     ha${s}rd by the torrent of a mountain pass.${c}${se}
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.VISUAL(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -752,8 +729,7 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
                   w${s}here it was settled on some sodden sand[long line${c}]${se}
                   h${s}ard by the torrent of a mountain pass.${c}${se}
               """.trimIndent(),*/
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_BLOCK,
+      Mode.VISUAL(SelectionType.BLOCK_WISE),
     )
   }
 
@@ -771,6 +747,6 @@ class VisualToggleCharacterModeActionTest : VimTestCase() {
     )
     enterCommand("set selectmode=cmd")
     typeText("v")
-    assertState(VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    assertState(Mode.SELECT(SelectionType.CHARACTER_WISE))
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualToggleLineModeActionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualToggleLineModeActionTest.kt
index c2cb5efd4..a2f800b82 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualToggleLineModeActionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/action/motion/visual/VisualToggleLineModeActionTest.kt
@@ -10,7 +10,8 @@
 
 package org.jetbrains.plugins.ideavim.action.motion.visual
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
 
@@ -35,8 +36,7 @@ class VisualToggleLineModeActionTest : VimTestCase() {
                     Sed in orci mauris.
                     Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.VISUAL(SelectionType.LINE_WISE),
     )
   }
 
@@ -60,8 +60,7 @@ class VisualToggleLineModeActionTest : VimTestCase() {
                     ${s}where it ${c}was settled on some sodden sand
                     ${se}Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.VISUAL(SelectionType.LINE_WISE),
     )
   }
 
@@ -85,8 +84,7 @@ class VisualToggleLineModeActionTest : VimTestCase() {
                     wh${c}ere it was settled on some sodden sand
                     ${se}hard by the torrent of a mountain pass.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.VISUAL(SelectionType.LINE_WISE),
     )
   }
 
@@ -110,8 +108,7 @@ class VisualToggleLineModeActionTest : VimTestCase() {
                     where it was settled on some sodden sand
                     ha${c}rd by the torrent of a mountain pass.${se}
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_LINE,
+      Mode.VISUAL(SelectionType.LINE_WISE),
     )
   }
 
@@ -129,7 +126,7 @@ class VisualToggleLineModeActionTest : VimTestCase() {
     )
     enterCommand("set selectmode=cmd")
     typeText("V")
-    assertState(VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_LINE)
+    assertMode(Mode.SELECT(SelectionType.LINE_WISE))
   }
 
   @Test
@@ -152,7 +149,7 @@ class VisualToggleLineModeActionTest : VimTestCase() {
         Sed in orci mauris.
         ${se}Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_LINE
+      Mode.VISUAL(SelectionType.LINE_WISE)
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/CommandParserTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/CommandParserTest.kt
index 626076405..a86abab52 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/ex/CommandParserTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/CommandParserTest.kt
@@ -11,7 +11,7 @@ package org.jetbrains.plugins.ideavim.ex
 import com.intellij.openapi.actionSystem.DataContext
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.handler.enableOctopus
 import com.maddyhome.idea.vim.newapi.vim
 import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
@@ -39,7 +39,7 @@ class CommandParserTest : VimTestCase() {
     val keys = ">>"
     val before = "I ${c}found it in a legendary land"
     val after = "    ${c}I found it in a legendary land"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.EDITOR_MODIFICATION)
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/MultipleCaretsTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/MultipleCaretsTest.kt
index 0cdf5eb92..ad038e93f 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/ex/MultipleCaretsTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/MultipleCaretsTest.kt
@@ -10,7 +10,7 @@ package org.jetbrains.plugins.ideavim.ex
 
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
 import com.maddyhome.idea.vim.newapi.vim
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/ActionCommandTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/ActionCommandTest.kt
index a4bc326c5..cb24490a4 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/ActionCommandTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/ActionCommandTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.ex.implementation.commands
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -25,7 +25,7 @@ class ActionCommandTest : VimTestCase() {
   fun testEditorRightAction() {
     configureByText("<caret>foo\n" + "bar\n")
     enterCommand("action EditorRight")
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     assertState("f<caret>oo\n" + "bar\n")
   }
 
@@ -41,7 +41,7 @@ class ActionCommandTest : VimTestCase() {
     )
     typeText("vjl")
     enterCommand("'<,'>action CommentByBlockComment")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.NORMAL())
     assertState(
       "-----\n" +
         "1/*2345\n" +
@@ -63,7 +63,7 @@ class ActionCommandTest : VimTestCase() {
     enterCommand("set incsearch")
     typeText("vjl")
     enterCommand("'<,'>action CommentByBlockComment")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.NORMAL())
     assertState(
       "-----\n" +
         "1/*2345\n" +
@@ -79,7 +79,7 @@ class ActionCommandTest : VimTestCase() {
     configureByJavaText("1<caret>2345\n" + "abcde\n")
     typeText("vl")
     enterCommand("'<,'>action CommentByBlockComment")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.NORMAL())
     assertState("1/*23*/45\n" + "abcde\n")
   }
 
@@ -90,7 +90,7 @@ class ActionCommandTest : VimTestCase() {
     enterCommand("set incsearch")
     typeText("vl")
     enterCommand("'<,'>action CommentByBlockComment")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.NORMAL())
     assertState("1/*23*/45\n" + "abcde\n")
   }
 
@@ -106,7 +106,7 @@ class ActionCommandTest : VimTestCase() {
     )
     typeText("Vj")
     enterCommand("'<,'>action CommentByBlockComment")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.NORMAL())
     assertState(
       "-----\n" +
         "/*\n" +
@@ -129,7 +129,7 @@ class ActionCommandTest : VimTestCase() {
     enterCommand("incsearch")
     typeText("Vj")
     enterCommand("'<,'>action CommentByBlockComment")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.NORMAL())
     assertState(
       "-----\n" +
         "/*\n" +
@@ -152,7 +152,7 @@ class ActionCommandTest : VimTestCase() {
     )
     typeText("<C-V>lj")
     enterCommand("'<,'>action CommentByBlockComment")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.NORMAL())
     assertState(
       "-----\n" +
         "1/*23*/45\n" +
@@ -173,7 +173,7 @@ class ActionCommandTest : VimTestCase() {
     enterCommand("set incsearch")
     typeText("<C-V>lj")
     enterCommand("'<,'>action CommentByBlockComment")
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.NORMAL())
     assertState(
       "-----\n" +
         "1/*23*/45\n" +
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/GlobalCommandTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/GlobalCommandTest.kt
index 2d42d64fe..34a9b36c4 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/GlobalCommandTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/GlobalCommandTest.kt
@@ -9,7 +9,7 @@
 package org.jetbrains.plugins.ideavim.ex.implementation.commands
 
 import com.maddyhome.idea.vim.VimPlugin
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.history.HistoryConstants
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -240,7 +240,7 @@ class GlobalCommandTest : VimTestCase() {
   }
 
   private fun doTest(command: String, before: String, after: String) {
-    doTest(listOf(exCommand(command)), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf(exCommand(command)), before, after, Mode.NORMAL())
   }
 
   companion object {
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/JoinLinesCommandTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/JoinLinesCommandTest.kt
index 9245d002a..1017444f8 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/JoinLinesCommandTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/JoinLinesCommandTest.kt
@@ -9,7 +9,7 @@
 package org.jetbrains.plugins.ideavim.ex.implementation.commands
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.Test
@@ -35,8 +35,7 @@ class JoinLinesCommandTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -60,8 +59,7 @@ class JoinLinesCommandTest : VimTestCase() {
                 Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -84,8 +82,7 @@ class JoinLinesCommandTest : VimTestCase() {
                 Lorem ipsum dolor sit amet,
                 consectetur adipiscing elit Sed in orci mauris.$c Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/MapCommandTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/MapCommandTest.kt
index 6fb000af2..18f57e3e0 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/MapCommandTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/MapCommandTest.kt
@@ -10,7 +10,7 @@ package org.jetbrains.plugins.ideavim.ex.implementation.commands
 import com.intellij.openapi.actionSystem.DataContext
 import com.intellij.openapi.editor.textarea.TextComponentEditorImpl
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
 import com.maddyhome.idea.vim.history.HistoryConstants
 import com.maddyhome.idea.vim.newapi.vim
@@ -51,7 +51,7 @@ class MapCommandTest : VimTestCase() {
     assertPluginError(false)
     typeText(injector.parser.parseKeys("i" + "Hello, " + "jk"))
     assertState("Hello, World!\n")
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     assertOffset(6)
   }
 
@@ -287,7 +287,7 @@ n  ,f            <Plug>Foo
     injector.vimscriptExecutor.execute("inoremap # X\u0008#\n")
     typeText(injector.parser.parseKeys("i" + "#" + "<Esc>"))
     assertState("#\n")
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     typeText(commandToKeys("imap"))
     assertExOutput("i  #           * X<C-H>#\n")
   }
@@ -312,7 +312,7 @@ n  ,f            <Plug>Foo
   
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     typeText(commandToKeys("map"))
     assertExOutput("   <C-X>i        dd\n")
     typeText(injector.parser.parseKeys("<C-X>i"))
@@ -950,8 +950,7 @@ n  ,f            <Plug>Foo
               -----
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
-    assertSubMode(VimStateMachine.SubMode.NONE)
+    assertMode(Mode.NORMAL())
   }
 
   private fun checkDelayedMapping(before: String, after: String) {
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/RegistersCommandTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/RegistersCommandTest.kt
index c130b9f3c..6bb036620 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/RegistersCommandTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/RegistersCommandTest.kt
@@ -10,7 +10,7 @@ package org.jetbrains.plugins.ideavim.ex.implementation.commands
 
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.register.Register
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.jetbrains.plugins.ideavim.annotations.TestWithPrimaryClipboard
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/BuiltInFunctionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/BuiltInFunctionTest.kt
index 1fa089d54..8f4c9a80d 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/BuiltInFunctionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/functions/BuiltInFunctionTest.kt
@@ -69,7 +69,7 @@ class BuiltInFunctionTest : VimTestCase() {
     // Remove selection and check again
     typeText(injector.parser.parseKeys("<esc>"))
     typeText(commandToKeys("""echo line("v")"""))
-    assertExOutput("4\n")
+    assertExOutput("3\n")
 
     typeText(commandToKeys("""echo line("abs") line(1) line([])"""))
     assertExOutput("0 0 0\n")
@@ -113,7 +113,7 @@ class BuiltInFunctionTest : VimTestCase() {
     // Remove selection and check again
     typeText(injector.parser.parseKeys("<esc>"))
     typeText(commandToKeys("""echo col("v")"""))
-    assertExOutput("7\n")
+    assertExOutput("5\n")
 
     typeText(commandToKeys("echo col('$')"))
     assertExOutput("10\n")
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/CommonExtensionTests.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/CommonExtensionTests.kt
index 4e52401d3..18e2ef9e7 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/CommonExtensionTests.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/CommonExtensionTests.kt
@@ -17,8 +17,9 @@ import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.moveToMotion
 import com.maddyhome.idea.vim.command.MappingMode
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.extension.Alias
 import com.maddyhome.idea.vim.extension.ExtensionBeanClass
 import com.maddyhome.idea.vim.extension.ExtensionHandler
@@ -71,8 +72,7 @@ class OpMappingTest : VimTestCase() {
       "dI",
       "${c}I found it in a legendary land",
       "${c}nd it in a legendary land",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -82,8 +82,7 @@ class OpMappingTest : VimTestCase() {
       "dP",
       "I found ${c}it in a legendary land",
       "I f${c}it in a legendary land",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -93,8 +92,7 @@ class OpMappingTest : VimTestCase() {
       "dU",
       "${c}I found it in a legendary land",
       "${c}d it in a legendary land",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -116,8 +114,7 @@ class OpMappingTest : VimTestCase() {
                 ${c}Sed in orci mauris.
                 Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -378,7 +375,7 @@ private class TestExtension : VimExtension {
 
   private class MoveEmulateInclusive : ExtensionHandler {
     override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
-      VimPlugin.getVisualMotion().enterVisualMode(editor, VimStateMachine.SubMode.VISUAL_CHARACTER)
+      VimPlugin.getVisualMotion().enterVisualMode(editor, SelectionType.CHARACTER_WISE)
       val caret = editor.ij.caretModel.currentCaret
       val newOffset = VimPlugin.getMotion().getHorizontalMotion(editor, caret.vim, 5, editor.isEndAllowed)
       caret.vim.moveToMotion(newOffset)
@@ -399,7 +396,7 @@ private class TestExtension : VimExtension {
 
   private class MoveLinewise : ExtensionHandler {
     override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
-      VimPlugin.getVisualMotion().enterVisualMode(editor, VimStateMachine.SubMode.VISUAL_LINE)
+      VimPlugin.getVisualMotion().enterVisualMode(editor, SelectionType.LINE_WISE)
       val caret = editor.ij.caretModel.currentCaret
       val newOffset = VimPlugin.getMotion().getVerticalMotionOffset(editor, caret.vim, 1)
       caret.vim.moveToOffset((newOffset as Motion.AbsoluteOffset).offset)
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/argtextobj/VimArgTextObjExtensionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/argtextobj/VimArgTextObjExtensionTest.kt
index f0bbfc460..4453d98f8 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/argtextobj/VimArgTextObjExtensionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/argtextobj/VimArgTextObjExtensionTest.kt
@@ -9,7 +9,8 @@ package org.jetbrains.plugins.ideavim.extension.argtextobj
 
 import com.google.common.collect.Lists
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.BeforeEach
@@ -36,15 +37,13 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("daa"),
       "function(int arg1,    char<caret>* arg2=\"a,b,c(d,e)\")",
       "function(int arg1<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("daa"),
       "function(int arg1<caret>)",
       "function(<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -54,8 +53,7 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("cia"),
       "function(int arg1,    char<caret>* arg2=\"a,b,c(d,e)\")",
       "function(int arg1,    <caret>)",
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
   }
 
@@ -65,15 +63,13 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("dia"),
       "function(1, (20<caret>*30)+40, somefunc2(3, 4))",
       "function(1, <caret>, somefunc2(3, 4))",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("daa"),
       "function(1, (20*30)+40, somefunc2(<caret>3, 4))",
       "function(1, (20*30)+40, somefunc2(<caret>4))",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -83,29 +79,25 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("daa"),
       "function(int arg1,    char* arg2=a,b,c(<caret>arg,e))",
       "function(int arg1,    char* arg2=a,b,c(<caret>e))",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("daa"),
       "function(int arg1,    char* arg2=\"a,b,c(<caret>arg,e)\")",
       "function(int arg1<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("daa"),
       "function(int arg1,    char* arg2=\"a,b,c(arg,e\"<caret>)",
       "function(int arg1<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("daa"),
       "function(int arg1,    char* a<caret>rg2={\"a,b},c(arg,e\"})",
       "function(int arg1<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -115,43 +107,37 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("d2aa"),
       "function(int <caret>arg1,    char* arg2=\"a,b,c(d,e)\")",
       "function(<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("d2ia"),
       "function(int <caret>arg1,    char* arg2=\"a,b,c(d,e)\")",
       "function(<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("d2aa"),
       "function(int <caret>arg1,    char* arg2=\"a,b,c(d,e)\", bool arg3)",
       "function(<caret>bool arg3)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("d2ia"),
       "function(int <caret>arg1,    char* arg2=\"a,b,c(d,e)\", bool arg3)",
       "function(<caret>, bool arg3)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("d2aa"),
       "function(int arg1,    char* arg<caret>2=\"a,b,c(d,e)\", bool arg3)",
       "function(int arg1<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("d2ia"),
       "function(int arg1,    char* arg<caret>2=\"a,b,c(d,e)\", bool arg3)",
       "function(int arg1,    <caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -161,15 +147,13 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("v2aa"),
       "function(int <caret>arg1,    char* arg2=\"a,b,c(d,e)\", bool arg3)",
       "function(<selection>int arg1,    char* arg2=\"a,b,c(d,e)\", </selection>bool arg3)",
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
     doTest(
       Lists.newArrayList("v2ia"),
       "function(int <caret>arg1,    char* arg2=\"a,b,c(d,e)\", bool arg3)",
       "function(<selection>int arg1,    char* arg2=\"a,b,c(d,e)\"</selection>, bool arg3)",
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -180,8 +164,7 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("dia"),
       "std::vector<int, std::unique_p<caret>tr<bool>> v{};",
       "std::vector<int, <caret>> v{};",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -192,15 +175,13 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("dia"),
       "namespace foo { void foo(int arg1, bool arg2<caret> { body }\n}",
       "namespace foo { void foo(int arg1, <caret>}",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("dia"),
       "namespace foo { void foo(int <caret>arg1, bool arg2 { body }\n}",
       "namespace foo { <caret>, bool arg2 { body }\n}",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -210,22 +191,19 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("dia"),
       "foo(30 << 10, 20 << <caret>3) >> 17",
       "foo(30 << 10, <caret>) >> 17",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("dia"),
       "foo(30 << <caret>10, 20 * 3) >> 17",
       "foo(<caret>, 20 * 3) >> 17",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("dia"),
       "foo(<caret>30 >> 10, 20 * 3) << 17",
       "foo(<caret>, 20 * 3) << 17",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -236,16 +214,14 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("daa"),
       "<caret>",
       "<caret>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
     doTest(
       Lists.newArrayList("dia"),
       "<caret>",
       "<caret>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
   }
@@ -257,16 +233,14 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("daa"),
       "<caret>\n",
       "<caret>\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
     doTest(
       Lists.newArrayList("dia"),
       "<caret>\n",
       "<caret>\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
   }
@@ -278,16 +252,14 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("daa"),
       "foo(<caret>)",
       "foo(<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
     doTest(
       Lists.newArrayList("dia"),
       "foo(<caret>)",
       "foo(<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
   }
@@ -298,7 +270,7 @@ class VimArgTextObjExtensionTest : VimTestCase() {
 {   methodCall(arg1, "{ arg1 , 2");
    otherMeth<caret>odcall(arg, 3);
 }"""
-    doTest(Lists.newArrayList("dia"), before, before, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(Lists.newArrayList("dia"), before, before, Mode.NORMAL())
     assertPluginError(true)
   }
 
@@ -308,8 +280,7 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("dia"),
       "foo(arg1, arr<caret>ay[someexpr(Class{arg1 << 3, arg2})] + 3)\n{",
       "foo(arg1, <caret>)\n{",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -319,8 +290,7 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("dia"),
       "foo((20*<caret>30))",
       "foo(<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -330,32 +300,28 @@ class VimArgTextObjExtensionTest : VimTestCase() {
       Lists.newArrayList("dia"),
       "foo(arg1, ba<caret>r(not-an-arg{body",
       "foo(arg1, ba<caret>r(not-an-arg{body",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
     doTest(
       Lists.newArrayList("dia"),
       "foo(arg1, ba<caret>r ( x > 3 )",
       "foo(arg1, ba<caret>r ( x > 3 )",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
     doTest(
       Lists.newArrayList("dia"),
       "foo(arg1, ba<caret>r + x >",
       "foo(arg1, ba<caret>r + x >",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
     doTest(
       Lists.newArrayList("dia"),
       "<arg1, ba<caret>r + x)",
       "<arg1, ba<caret>r + x)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
   }
@@ -364,7 +330,7 @@ class VimArgTextObjExtensionTest : VimTestCase() {
   fun testArgumentBoundsSearchIsLimitedByLineCount() {
     val before = """foo(
 ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>Arg)"""
-    doTest(Lists.newArrayList("dia"), before, before, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(Lists.newArrayList("dia"), before, before, Mode.NORMAL())
     assertPluginError(true)
   }
 
@@ -374,15 +340,13 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("vllia"),
       "function(int arg1,    ch<caret>ar* arg2=\"a,b,c(d,e)\")",
       "function(int arg1,    <selection>char* arg2=\"a,b,c(d,e)\"</selection>)",
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
     doTest(
       Lists.newArrayList("vhhia"),
       "function(int arg1,    char<caret>* arg2=\"a,b,c(d,e)\")",
       "function(int arg1,    <selection>char* arg2=\"a,b,c(d,e)\"</selection>)",
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -392,8 +356,7 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("vllia"),
       "fu<caret>n(arg)",
       "fun(<selection>arg</selection>)",
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
@@ -404,15 +367,13 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("dia"),
       "function(int a, String[<caret>] b)",
       "function(int a, <caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("daa"),
       "function(int a, String[<caret>] b)",
       "function(int a)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -422,15 +383,13 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("dia"),
       "class MyClass{ public int myFun() { some<caret>Call(); } }",
       "class MyClass{ public int myFun() { some<caret>Call(); } }",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("daa"),
       "class MyClass{ public int myFun() { some<caret>Call(); } }",
       "class MyClass{ public int myFun() { some<caret>Call(); } }",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -440,15 +399,13 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("dia"),
       "function (int <caret>a)",
       "function (int <caret>a)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("daa"),
       "function (int <caret>a)",
       "function (int <caret>a)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -462,15 +419,13 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("dia"),
       "function (int <caret>a, int b)",
       "function (, int b)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("daa"),
       "function (int <caret>a, int b)",
       "function (int b)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -480,15 +435,13 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("dia"),
       "class MyClass{ public int myFun() { if (tr<caret>ue) { somFunction(); } } }",
       "class MyClass{ public int myFun() { if (tr<caret>ue) { somFunction(); } } }",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("daa"),
       "class MyClass{ public int myFun() { if (tr<caret>ue) { somFunction(); } } }",
       "class MyClass{ public int myFun() { if (tr<caret>ue) { somFunction(); } } }",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -500,8 +453,7 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("daa"),
       "f(a<caret>)",
       "f(a<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
     assertPluginErrorMessageContains("expecting ':', but got '(' instead")
@@ -510,8 +462,7 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("daa"),
       "f(a<caret>)",
       "f(a<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
     assertPluginErrorMessageContains("expecting ',', but got '(' instead")
@@ -520,8 +471,7 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("daa"),
       "f(a<caret>)",
       "f(a<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
     assertPluginErrorMessageContains("open and close brackets must be different")
@@ -530,8 +480,7 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("daa"),
       "f(a<caret>)",
       "f(a<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
     assertPluginErrorMessageContains("list of pairs is incomplete")
@@ -540,8 +489,7 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("daa"),
       "f(a<caret>)",
       "f(a<caret>)",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(true)
     assertPluginErrorMessageContains("list of pairs is incomplete")
@@ -550,8 +498,7 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("daa"),
       "f[a<caret>]",
       "f[<caret>]",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(false)
     setArgTextObjPairsVariable("::;")
@@ -559,8 +506,7 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("daa"),
       "f: a<caret> ;",
       "f:<caret>;",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     assertPluginError(false)
   }
@@ -572,22 +518,19 @@ ${java.lang.String.join("", Collections.nCopies(10, "   arg,\n"))}   last<caret>
       Lists.newArrayList("daa"),
       "[capture1, c = <caret>capture2] { return Clazz<int, bool>{ctorParam1, ctorParam2}; }",
       "[capture1] { return Clazz<int, bool>{ctorParam1, ctorParam2}; }",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("daa"),
       "[capture1, c = capture2] { return Clazz<int,<caret> bool>{ctorParam1, ctorParam2}; }",
       "[capture1, c = capture2] { return Clazz<int>{ctorParam1, ctorParam2}; }",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
     doTest(
       Lists.newArrayList("daa"),
       "[capture1, c = capture2] { return Clazz<int, bool>{ctorPar<caret>am1, ctorParam2}; }",
       "[capture1, c = capture2] { return Clazz<int, bool>{ctorParam2}; }",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/commentary/CommentaryExtensionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/commentary/CommentaryExtensionTest.kt
index 0b3cf8367..842fbc76c 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/commentary/CommentaryExtensionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/commentary/CommentaryExtensionTest.kt
@@ -10,7 +10,7 @@ package org.jetbrains.plugins.ideavim.extension.commentary
 
 import com.intellij.ide.highlighter.HtmlFileType
 import com.intellij.ide.highlighter.JavaFileType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.jetbrains.yaml.YAMLFileType
@@ -33,8 +33,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "gcll",
       "<caret>if (condition) {\n" + "}\n",
       "/<caret>*i*/f (condition) {\n" + "}\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
     assertSelection(null)
@@ -47,8 +46,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "gciw",
       "<caret>if (condition) {\n" + "}\n",
       "<caret>/*if*/ (condition) {\n" + "}\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
     assertSelection(null)
@@ -61,8 +59,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "gct{",
       "<caret>if (condition) {\n" + "}\n",
       "<caret>/*if (condition) */{\n" + "}\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -74,8 +71,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "gcab",
       "if (<caret>condition) {\n" + "}\n",
       "if <caret>/*(condition)*/ {\n" + "}\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -92,8 +88,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "<caret>if (condition) {\n" + "}\n",
       "<caret>//if (condition) {\n" +
         "//}\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -105,8 +100,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "if (<caret>condition) {\n" + "}\n",
       "//if<caret> (condition) {\n" +
         "//}\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -119,8 +113,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "<caret>if (condition) {\n" + "}\n",
       "//if (condition) {\n" +
         "//}\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -132,8 +125,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "gcip",
       "${c}if (condition) {}",
       "//if (condition) {}",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -146,8 +138,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "<caret>//if (condition) {\n" + "//}\n",
       "if (condition) {\n" +
         "}\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
     assertSelection(null)
@@ -160,8 +151,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "gcip",
       "$c//if (condition) {}",
       "if (condition) {}",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -174,8 +164,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "<caret>if (condition) {\n" + "}\n",
       "//if (condition) {\n" +
         "//}\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -188,8 +177,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "<caret>//if (condition) {\n" + "//}\n",
       "if (condition) {\n" +
         "}\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -202,8 +190,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "<caret>if (condition) {\n" + "}\n",
       "//if (condition) {\n" +
         "<caret>}\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
     assertSelection(null)
@@ -216,8 +203,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "gcc",
       "if (<caret>condition) {\n" + "}\n",
       "<caret>//if (condition) {\n" + "}\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
     assertSelection(null)
@@ -231,8 +217,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "<caret>//if (condition) {\n" + "}\n",
       "<caret>if (condition) {\n" +
         "}\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
     assertSelection(null)
@@ -245,8 +230,7 @@ class CommentaryExtensionTest : VimTestCase() {
       "gcc",
       "<div />",
       "<!--<div />-->",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
     assertSelection(null)
@@ -268,8 +252,7 @@ class CommentaryExtensionTest : VimTestCase() {
                 //if (condition) {
                 //}
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -290,8 +273,7 @@ class CommentaryExtensionTest : VimTestCase() {
                 /*if*/ (condition) {
                 }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -308,8 +290,7 @@ class CommentaryExtensionTest : VimTestCase() {
                 //if (condition) {
                 //}
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -331,8 +312,7 @@ class CommentaryExtensionTest : VimTestCase() {
          final Int value3 = 42;
         final Int value4 = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -354,8 +334,7 @@ class CommentaryExtensionTest : VimTestCase() {
          final Int value3 = 42;
         final Int value4 = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -381,8 +360,7 @@ class CommentaryExtensionTest : VimTestCase() {
         //final Int value5 = 42;
         final Int value6 = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -398,8 +376,7 @@ class CommentaryExtensionTest : VimTestCase() {
       """
         final Int value = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -419,8 +396,7 @@ class CommentaryExtensionTest : VimTestCase() {
       """
         final Int value = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -440,8 +416,7 @@ class CommentaryExtensionTest : VimTestCase() {
       """
         final Int value = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -457,8 +432,7 @@ class CommentaryExtensionTest : VimTestCase() {
       """
         final Int value = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -476,8 +450,7 @@ class CommentaryExtensionTest : VimTestCase() {
       """
         final Int value = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -497,8 +470,7 @@ class CommentaryExtensionTest : VimTestCase() {
         final Int value1 = 42;
         final Int value2 = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -517,8 +489,7 @@ class CommentaryExtensionTest : VimTestCase() {
       """
         final Int value = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -536,8 +507,7 @@ class CommentaryExtensionTest : VimTestCase() {
       """
         final Int value = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -555,8 +525,7 @@ class CommentaryExtensionTest : VimTestCase() {
       """
         final Int value = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -578,8 +547,7 @@ class CommentaryExtensionTest : VimTestCase() {
         
         final Int value = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -597,8 +565,7 @@ class CommentaryExtensionTest : VimTestCase() {
         final Int value = 42;
         final Int value2 = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -613,8 +580,7 @@ class CommentaryExtensionTest : VimTestCase() {
       """
         final Int value = 42; // Comment
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -630,8 +596,7 @@ class CommentaryExtensionTest : VimTestCase() {
       """
         final Int value = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -649,8 +614,7 @@ class CommentaryExtensionTest : VimTestCase() {
       """
         final Int value = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -671,8 +635,7 @@ class CommentaryExtensionTest : VimTestCase() {
       """
         final Int value = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -694,8 +657,7 @@ class CommentaryExtensionTest : VimTestCase() {
       """
         final Int value = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -710,8 +672,7 @@ class CommentaryExtensionTest : VimTestCase() {
       """
         final Int value /* Block comment */ = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -733,8 +694,7 @@ class CommentaryExtensionTest : VimTestCase() {
         public void something(int value, String name) {
         }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -757,8 +717,7 @@ class CommentaryExtensionTest : VimTestCase() {
         public void something(int value, String name) {
         }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -781,8 +740,7 @@ class CommentaryExtensionTest : VimTestCase() {
         public void something(int value, String name) {
         }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -808,8 +766,7 @@ class CommentaryExtensionTest : VimTestCase() {
         public void something(int value, String name) {
         }
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -828,8 +785,7 @@ class CommentaryExtensionTest : VimTestCase() {
         //final int var <caret>value2 = 42;
         final int var value3 = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -848,8 +804,7 @@ class CommentaryExtensionTest : VimTestCase() {
         //final int var value2 = 42;
         final int var value3 = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -868,8 +823,7 @@ class CommentaryExtensionTest : VimTestCase() {
         //final int var value2 = 42;
         //final int var value3 = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -899,8 +853,7 @@ class CommentaryExtensionTest : VimTestCase() {
         //final int var value2 = 42;
         //final int var <caret>value3 = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -923,8 +876,7 @@ class CommentaryExtensionTest : VimTestCase() {
         //final int var value21 = 42;
         <caret>//final int var value22 = 42;
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       JavaFileType.INSTANCE,
     )
   }
@@ -953,8 +905,7 @@ class CommentaryExtensionTest : VimTestCase() {
       - Chicago Cubs
       - Atlanta Braves
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       YAMLFileType.YML,
     )
   }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/entiretextobj/VimTextObjEntireExtensionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/entiretextobj/VimTextObjEntireExtensionTest.kt
index f110a04e4..adceab35f 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/entiretextobj/VimTextObjEntireExtensionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/entiretextobj/VimTextObjEntireExtensionTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.extension.entiretextobj
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -44,7 +44,7 @@ class VimTextObjEntireExtensionTest : VimTestCase() {
   // |c| |ae|
   @Test
   fun testChangeEntireBuffer() {
-    doTest("cae", poem, "<caret>", VimStateMachine.Mode.INSERT)
+    doTest("cae", poem, "<caret>", Mode.INSERT)
   }
 
   // |d| |ae|
@@ -86,7 +86,7 @@ class VimTextObjEntireExtensionTest : VimTestCase() {
       "cie",
       "\n  \n \n${poem}\n  \n \n",
       "\n  \n \n<caret>\n\n  \n \n",
-      VimStateMachine.Mode.INSERT,
+      Mode.INSERT,
     ) // additional \n because poem ends with a \n
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/exchange/VimExchangeExtensionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/exchange/VimExchangeExtensionTest.kt
index 81d63aee8..d38811a8a 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/exchange/VimExchangeExtensionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/exchange/VimExchangeExtensionTest.kt
@@ -10,7 +10,7 @@ package org.jetbrains.plugins.ideavim.extension.exchange
 
 import com.intellij.openapi.editor.markup.HighlighterTargetArea
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.extension.exchange.VimExchangeExtension
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -33,8 +33,7 @@ class VimExchangeExtensionTest : VimTestCase() {
       listOf("cxe", "w", "cxe"),
       "The quick ${c}brown fox catch over the lazy dog",
       "The quick fox ${c}brown catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -45,8 +44,7 @@ class VimExchangeExtensionTest : VimTestCase() {
       listOf("cxiw", "w", "."),
       "The quick ${c}brown fox catch over the lazy dog",
       "The quick fox ${c}brown catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -57,8 +55,7 @@ class VimExchangeExtensionTest : VimTestCase() {
       listOf("cxe", "b", "cxe"),
       "The quick brown ${c}fox catch over the lazy dog",
       "The quick ${c}fox brown catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -69,8 +66,7 @@ class VimExchangeExtensionTest : VimTestCase() {
       listOf("cxe", "b", "."),
       "The quick brown ${c}fox catch over the lazy dog",
       "The quick ${c}fox brown catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -81,8 +77,7 @@ class VimExchangeExtensionTest : VimTestCase() {
       listOf("veX", "w", "veX"),
       "The quick ${c}brown fox catch over the lazy dog",
       "The quick fox ${c}brown catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -97,8 +92,7 @@ class VimExchangeExtensionTest : VimTestCase() {
       listOf("veX", "b", "v3e", "X"),
       "The quick ${c}brown fox catch over the lazy dog",
       "The brow${c}n catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -113,8 +107,7 @@ class VimExchangeExtensionTest : VimTestCase() {
       listOf("v3e", "X", "w", "veX"),
       "The ${c}quick brown fox catch over the lazy dog",
       "The brow${c}n catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -142,8 +135,7 @@ class VimExchangeExtensionTest : VimTestCase() {
          brown fox
          the lazy dog
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -171,8 +163,7 @@ class VimExchangeExtensionTest : VimTestCase() {
          brown fox
          the lazy dog
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -198,8 +189,7 @@ class VimExchangeExtensionTest : VimTestCase() {
          catch over
          fox lazy dog
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -230,8 +220,7 @@ class VimExchangeExtensionTest : VimTestCase() {
          brown fox
          
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -381,8 +370,7 @@ class VimExchangeExtensionTest : VimTestCase() {
       listOf("vb", "X", "bevb", "X"),
       "The quick brow${c}n fox catch over the lazy dog",
       "The ${c}brown quick fox catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -392,8 +380,7 @@ class VimExchangeExtensionTest : VimTestCase() {
       listOf("vb", "X", "wve", "X"),
       "The quick brow${c}n fox catch over the lazy dog",
       "The quick fox ${c}brown catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -403,8 +390,7 @@ class VimExchangeExtensionTest : VimTestCase() {
       listOf("ve", "X", "wevb", "X"),
       "The quick ${c}brown fox catch over the lazy dog",
       "The quick fox ${c}brown catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/exchange/VimExchangeWithClipboardTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/exchange/VimExchangeWithClipboardTest.kt
index e370b1859..60c9656aa 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/exchange/VimExchangeWithClipboardTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/exchange/VimExchangeWithClipboardTest.kt
@@ -10,7 +10,7 @@ package org.jetbrains.plugins.ideavim.extension.exchange
 
 import com.intellij.openapi.editor.markup.HighlighterTargetArea
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.extension.exchange.VimExchangeExtension
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.TestOptionConstants
@@ -37,8 +37,7 @@ class VimExchangeWithClipboardTest : VimTestCase() {
       listOf("cxe", "w", "cxe"),
       "The quick ${c}brown fox catch over the lazy dog",
       "The quick fox ${c}brown catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -49,8 +48,7 @@ class VimExchangeWithClipboardTest : VimTestCase() {
       listOf("cxiw", "w", "."),
       "The quick ${c}brown fox catch over the lazy dog",
       "The quick fox ${c}brown catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -61,8 +59,7 @@ class VimExchangeWithClipboardTest : VimTestCase() {
       listOf("cxe", "b", "cxe"),
       "The quick brown ${c}fox catch over the lazy dog",
       "The quick ${c}fox brown catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -73,8 +70,7 @@ class VimExchangeWithClipboardTest : VimTestCase() {
       listOf("cxe", "b", "."),
       "The quick brown ${c}fox catch over the lazy dog",
       "The quick ${c}fox brown catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -85,8 +81,7 @@ class VimExchangeWithClipboardTest : VimTestCase() {
       listOf("veX", "w", "veX"),
       "The quick ${c}brown fox catch over the lazy dog",
       "The quick fox ${c}brown catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -101,8 +96,7 @@ class VimExchangeWithClipboardTest : VimTestCase() {
       listOf("veX", "b", "v3e", "X"),
       "The quick ${c}brown fox catch over the lazy dog",
       "The brow${c}n catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -117,8 +111,7 @@ class VimExchangeWithClipboardTest : VimTestCase() {
       listOf("v3e", "X", "w", "veX"),
       "The ${c}quick brown fox catch over the lazy dog",
       "The brow${c}n catch over the lazy dog",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -146,8 +139,7 @@ class VimExchangeWithClipboardTest : VimTestCase() {
          brown fox
          the lazy dog
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -175,8 +167,7 @@ class VimExchangeWithClipboardTest : VimTestCase() {
          brown fox
          the lazy dog
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -202,8 +193,7 @@ class VimExchangeWithClipboardTest : VimTestCase() {
          catch over
          fox lazy dog
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -234,8 +224,7 @@ class VimExchangeWithClipboardTest : VimTestCase() {
          brown fox
          
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/highlightedyank/VimHighlightedYankTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/highlightedyank/VimHighlightedYankTest.kt
index 492a7406d..7b522354d 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/highlightedyank/VimHighlightedYankTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/highlightedyank/VimHighlightedYankTest.kt
@@ -11,7 +11,7 @@ package org.jetbrains.plugins.ideavim.extension.highlightedyank
 import com.intellij.openapi.editor.markup.RangeHighlighter
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.extension.highlightedyank.DEFAULT_HIGHLIGHT_DURATION
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.jetbrains.plugins.ideavim.assertHappened
@@ -28,7 +28,7 @@ class VimHighlightedYankTest : VimTestCase() {
 
   @Test
   fun `test highlighting whole line when whole line is yanked`() {
-    doTest("yy", code, code, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("yy", code, code, Mode.NORMAL())
 
     assertAllHighlightersCount(1)
     assertHighlighterRange(1, 40, getFirstHighlighter())
@@ -36,7 +36,7 @@ class VimHighlightedYankTest : VimTestCase() {
 
   @Test
   fun `test highlighting single word when single word is yanked`() {
-    doTest("yiw", code, code, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("yiw", code, code, Mode.NORMAL())
 
     assertAllHighlightersCount(1)
     assertHighlighterRange(5, 8, getFirstHighlighter())
@@ -53,7 +53,7 @@ class VimHighlightedYankTest : VimTestCase() {
 
   @Test
   fun `test removing previous highlight when entering insert mode`() {
-    doTest("yyi", code, code, VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    doTest("yyi", code, code, Mode.INSERT)
 
     assertAllHighlightersCount(0)
   }
@@ -112,8 +112,7 @@ class VimHighlightedYankTest : VimTestCase() {
       "yiw",
       codeWithMultipleCurors,
       codeWithMultipleCurors,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
 
     val highlighters = fixture.editor.markupModel.allHighlighters
@@ -129,8 +128,7 @@ class VimHighlightedYankTest : VimTestCase() {
       "yiwi",
       codeWithMultipleCurors,
       codeWithMultipleCurors,
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     )
 
     assertAllHighlightersCount(0)
@@ -138,7 +136,7 @@ class VimHighlightedYankTest : VimTestCase() {
 
   @Test
   fun `test highlighting for a correct default amount of time`() {
-    doTest("yiw", code, code, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("yiw", code, code, Mode.NORMAL())
 
     assertHappened(DEFAULT_HIGHLIGHT_DURATION.toInt(), 200) {
       getAllHighlightersCount() == 0
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/matchit/MatchitGeneralTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/matchit/MatchitGeneralTest.kt
index 5a5995413..406b4ec03 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/matchit/MatchitGeneralTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/matchit/MatchitGeneralTest.kt
@@ -10,7 +10,8 @@ package org.jetbrains.plugins.ideavim.extension.matchit
 
 import com.intellij.ide.highlighter.HtmlFileType
 import com.intellij.ide.highlighter.JavaFileType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.BeforeEach
@@ -91,8 +92,7 @@ class MatchitGeneralTest : VimTestCase() {
       "v$%",
       """foo(${c}bar)""",
       """foo${s}$c(b${se}ar)""",
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
       HtmlFileType.INSTANCE,
     )
   }
@@ -103,8 +103,7 @@ class MatchitGeneralTest : VimTestCase() {
       "v$%%",
       """foo(${c}bar)""",
       """foo(${s}bar$c)$se""",
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
       HtmlFileType.INSTANCE,
     )
   }
@@ -125,8 +124,7 @@ class MatchitGeneralTest : VimTestCase() {
       "d%",
       "(x == 123$c)",
       "",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -137,8 +135,7 @@ class MatchitGeneralTest : VimTestCase() {
       "d%",
       "$c{ foo: 123 }",
       "",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -149,8 +146,7 @@ class MatchitGeneralTest : VimTestCase() {
       "d%",
       "{ foo: 123 $c}",
       "",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -161,8 +157,7 @@ class MatchitGeneralTest : VimTestCase() {
       "d%",
       "$c[1, 2, 3]",
       "",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -173,8 +168,7 @@ class MatchitGeneralTest : VimTestCase() {
       "d%",
       "[1, 2, 3$c]",
       "",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -188,8 +182,7 @@ class MatchitGeneralTest : VimTestCase() {
       "v$%",
       """</h${c}tml>""",
       """${s}$c</ht${se}ml>""",
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
       HtmlFileType.INSTANCE,
     )
   }
@@ -208,8 +201,7 @@ class MatchitGeneralTest : VimTestCase() {
           puts n
         en${se}d
       """.trimIndent(),
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
       fileName = "ruby.rb",
     )
   }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/matchit/MatchitHtmlTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/matchit/MatchitHtmlTest.kt
index 8e3b19057..09dff474c 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/matchit/MatchitHtmlTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/matchit/MatchitHtmlTest.kt
@@ -9,7 +9,7 @@
 package org.jetbrains.plugins.ideavim.extension.matchit
 
 import com.intellij.ide.highlighter.HtmlFileType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
@@ -33,8 +33,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <h1>Heading<$c/h1>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -49,8 +48,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <${c}h1>Heading</h1>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -69,8 +67,7 @@ class MatchitHtmlTest : VimTestCase() {
           <p>paragraph body</p>
         <$c/div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -89,8 +86,7 @@ class MatchitHtmlTest : VimTestCase() {
           <p>paragraph body</p>
         </div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -113,8 +109,7 @@ class MatchitHtmlTest : VimTestCase() {
           </div>
         <$c/div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -133,8 +128,7 @@ class MatchitHtmlTest : VimTestCase() {
           <div>contents<$c/div>
         </div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -157,8 +151,7 @@ class MatchitHtmlTest : VimTestCase() {
           </div>
         </div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -177,8 +170,7 @@ class MatchitHtmlTest : VimTestCase() {
           <${c}div>contents</div>
         </div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -193,8 +185,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <h1 class="headline">Post HeadLine<$c/h1>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -213,8 +204,7 @@ class MatchitHtmlTest : VimTestCase() {
           <img src=$c"my-image.png" alt="my-image">
         </div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -233,8 +223,7 @@ class MatchitHtmlTest : VimTestCase() {
         $c 
         </div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -249,8 +238,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <h1$c>Heading</h1>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -261,8 +249,7 @@ class MatchitHtmlTest : VimTestCase() {
       "%",
       "$c    <h1>Heading</h1>",
       "    <h1$c>Heading</h1>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -277,8 +264,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <h1 class="headline">Post HeadLine</h1$c>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -293,8 +279,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <h1>Heading</h1$c>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -309,8 +294,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         $c<h1>Heading</h1>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -325,8 +309,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <h1>Heading$c</h1>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -341,8 +324,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <div [ngIf$c]="someCondition()">{{displayValue}}</div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -357,8 +339,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <div [ngIf]="someCondition($c)">{{displayValue}}</div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -373,8 +354,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <div [ngIf]="someCondition()">{{displayValue}$c}</div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -389,8 +369,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <div [ngIf]="someCondition()">{{displayValue}}<$c/div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -405,8 +384,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <div [ngIf]="someCondition()">{$c{displayValue}}</div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -421,8 +399,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <img ${c}src={{imagePath}} alt={{imageDescription}}>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -453,8 +430,7 @@ class MatchitHtmlTest : VimTestCase() {
           <p>paragraph 2</p>
         <$c/div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -485,8 +461,7 @@ class MatchitHtmlTest : VimTestCase() {
           <p>paragraph 2</p>
         <$c/div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -505,8 +480,7 @@ class MatchitHtmlTest : VimTestCase() {
         <!--   This div is commented out -->
         <!-- <$c/div> -->
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -525,8 +499,7 @@ class MatchitHtmlTest : VimTestCase() {
         <!--   This div is commented out -->
         <!-- </div> -->
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -545,8 +518,7 @@ class MatchitHtmlTest : VimTestCase() {
         <!--   This div is commented out -->
         <!-- </div> -->
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -565,8 +537,7 @@ class MatchitHtmlTest : VimTestCase() {
         <!--   This div is commented out -->
         <!-- </div> -->
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -585,8 +556,7 @@ class MatchitHtmlTest : VimTestCase() {
         <!--   This div is commented out -->
         <!-- </div> -->
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -601,8 +571,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <p *ngIf="count > 0"$c>Count is greater than zero</p>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -623,8 +592,7 @@ class MatchitHtmlTest : VimTestCase() {
            Header Content
         <$c/h1>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -635,8 +603,7 @@ class MatchitHtmlTest : VimTestCase() {
       "%",
       "$c  <!-- A comment -->",
       "  <!-- A comment --$c>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -655,8 +622,7 @@ class MatchitHtmlTest : VimTestCase() {
           <BoxHeading></BoxHeading>
         <${c}/Box>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -675,8 +641,7 @@ class MatchitHtmlTest : VimTestCase() {
           <BoxHeading></BoxHeading>
         </Box>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -695,8 +660,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <h1>Heading<$c/h1>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -711,8 +675,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <${c}h1>Heading</h1>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -735,8 +698,7 @@ class MatchitHtmlTest : VimTestCase() {
           </div>
         <$c/div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -755,8 +717,7 @@ class MatchitHtmlTest : VimTestCase() {
           <div>contents<$c/div>
         </div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -779,8 +740,7 @@ class MatchitHtmlTest : VimTestCase() {
           </div>
         </div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -799,8 +759,7 @@ class MatchitHtmlTest : VimTestCase() {
           <${c}div>contents</div>
         </div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -815,8 +774,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <h1 class="headline">Post HeadLine<$c/h1>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -835,8 +793,7 @@ class MatchitHtmlTest : VimTestCase() {
           <img src=$c"my-image.png" alt="my-image">
         </div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -855,8 +812,7 @@ class MatchitHtmlTest : VimTestCase() {
         $c 
         </div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -871,8 +827,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <h1$c>Heading</h1>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -883,8 +838,7 @@ class MatchitHtmlTest : VimTestCase() {
       "g%",
       "$c    <h1>Heading</h1>",
       "    <h1$c>Heading</h1>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -899,8 +853,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <h1 class="headline">Post HeadLine</h1$c>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -915,8 +868,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         $c<h1>Heading</h1>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -931,8 +883,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <div [ngIf$c]="someCondition()">{{displayValue}}</div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -947,8 +898,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <div [ngIf]="someCondition($c)">{{displayValue}}</div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -963,8 +913,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <div [ngIf]="someCondition()">{{displayValue}$c}</div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -979,8 +928,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <div [ngIf]="someCondition()">{{displayValue}}<$c/div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -995,8 +943,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <div [ngIf]="someCondition()">{$c{displayValue}}</div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -1011,8 +958,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <img ${c}src={{imagePath}} alt={{imageDescription}}>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -1043,8 +989,7 @@ class MatchitHtmlTest : VimTestCase() {
           <p>paragraph 2</p>
         <$c/div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -1075,8 +1020,7 @@ class MatchitHtmlTest : VimTestCase() {
           <p>paragraph 2</p>
         <$c/div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -1095,8 +1039,7 @@ class MatchitHtmlTest : VimTestCase() {
         <!--   This div is commented out -->
         <!-- <$c/div> -->
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -1115,8 +1058,7 @@ class MatchitHtmlTest : VimTestCase() {
         <!--   This div is commented out -->
         <!-- </div> -->
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -1135,8 +1077,7 @@ class MatchitHtmlTest : VimTestCase() {
         <!--   This div is commented out -->
         <!-- </div> -->
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -1155,8 +1096,7 @@ class MatchitHtmlTest : VimTestCase() {
         <!--   This div is commented out -->
         <!-- </div> -->
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -1175,8 +1115,7 @@ class MatchitHtmlTest : VimTestCase() {
         <!--   This div is commented out -->
         <!-- </div> -->
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -1191,8 +1130,7 @@ class MatchitHtmlTest : VimTestCase() {
       """
         <p *ngIf="count > 0"$c>Count is greater than zero</p>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -1203,8 +1141,7 @@ class MatchitHtmlTest : VimTestCase() {
       "g%",
       "$c  <!-- A comment -->",
       "  <!-- A comment --$c>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -1223,8 +1160,7 @@ class MatchitHtmlTest : VimTestCase() {
           <BoxHeading></BoxHeading>
         <${c}/Box>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
@@ -1243,8 +1179,7 @@ class MatchitHtmlTest : VimTestCase() {
           <BoxHeading></BoxHeading>
         </Box>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       HtmlFileType.INSTANCE,
     )
   }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/matchit/MatchitPhpTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/matchit/MatchitPhpTest.kt
index 76c54dca9..289cd1022 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/matchit/MatchitPhpTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/matchit/MatchitPhpTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.extension.matchit
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
@@ -28,8 +28,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "$c<h1>Heading</h1>",
       "<h1$c>Heading</h1>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -40,8 +39,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "<${c}h1>Heading</h1>",
       "<h1>Heading<$c/h1>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -52,8 +50,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "$c<?php \$n=1 ?>",
       "<?php \$n=1 $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -64,8 +61,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "$c  <?php \$n=1 ?>",
       "  <?php \$n=1 $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -76,8 +72,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "<?php \$n=1 ?$c>",
       "$c<?php \$n=1 ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -88,8 +83,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "<$c?php \$n=1 ?>",
       "<?php \$n=1 $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -100,8 +94,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "<?php \$n=1 $c?>",
       "$c<?php \$n=1 ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -112,8 +105,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "<?ph${c}p \$n=1 ?>",
       "<?php \$n=1 $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -124,8 +116,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "$c<?= func(123) ?>",
       "<?= func(123) $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -136,8 +127,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "<$c?= func(123) ?>",
       "<?= func(123) $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -148,8 +138,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "<?$c= func(123) ?>",
       "<?= func(123) $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -160,8 +149,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "<?= ${c}func(123) ?>",
       "<?= func(123$c) ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -172,8 +160,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "<?= func(123)$c ?>",
       "$c<?= func(123) ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -184,8 +171,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "<?= func(123) ?$c>",
       "$c<?= func(123) ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -196,8 +182,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "$c<? func(123) ?>",
       "<? func(123) $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -208,8 +193,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "<$c? func(123) ?>",
       "<? func(123) $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -220,8 +204,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "<? ${c}func(123) ?>",
       "<? func(123$c) ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -232,8 +215,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "<? func(123)$c ?>",
       "$c<? func(123) ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -244,8 +226,7 @@ class MatchitPhpTest : VimTestCase() {
       "%",
       "<? func(123) ?$c>",
       "$c<? func(123) ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1394,8 +1375,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "$c<?php \$n=1 ?>",
       "<?php \$n=1 $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1406,8 +1386,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "$c  <?php \$n=1 ?>",
       "  <?php \$n=1 $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1418,8 +1397,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "<?php \$n=1 ?$c>",
       "$c<?php \$n=1 ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1430,8 +1408,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "<$c?php \$n=1 ?>",
       "<?php \$n=1 $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1442,8 +1419,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "<?php \$n=1 $c?>",
       "$c<?php \$n=1 ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1454,8 +1430,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "<?ph${c}p \$n=1 ?>",
       "<?php \$n=1 $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1466,8 +1441,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "$c<?= func(123) ?>",
       "<?= func(123) $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1478,8 +1452,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "<$c?= func(123) ?>",
       "<?= func(123) $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1490,8 +1463,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "<?$c= func(123) ?>",
       "<?= func(123) $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1502,8 +1474,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "<?= ${c}func(123) ?>",
       "<?= func(123$c) ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1514,8 +1485,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "<?= func(123)$c ?>",
       "$c<?= func(123) ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1526,8 +1496,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "<?= func(123) ?$c>",
       "$c<?= func(123) ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1538,8 +1507,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "$c<? func(123) ?>",
       "<? func(123) $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1550,8 +1518,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "<$c? func(123) ?>",
       "<? func(123) $c?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1562,8 +1529,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "<? ${c}func(123) ?>",
       "<? func(123$c) ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1574,8 +1540,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "<? func(123)$c ?>",
       "$c<? func(123) ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
@@ -1586,8 +1551,7 @@ class MatchitPhpTest : VimTestCase() {
       "g%",
       "<? func(123) ?$c>",
       "$c<? func(123) ?>",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
       fileName = "file.php",
     )
   }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/multiplecursors/VimMultipleCursorsExtensionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/multiplecursors/VimMultipleCursorsExtensionTest.kt
index fea26e5b0..200a165df 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/multiplecursors/VimMultipleCursorsExtensionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/multiplecursors/VimMultipleCursorsExtensionTest.kt
@@ -9,7 +9,8 @@
 package org.jetbrains.plugins.ideavim.extension.multiplecursors
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import com.maddyhome.idea.vim.helper.vimStateMachine
 import com.maddyhome.idea.vim.newapi.vim
@@ -269,7 +270,7 @@ class VimMultipleCursorsExtensionTest : VimTestCase() {
       |dfkjsg
     """.trimMargin()
     val editor = configureByText(before)
-    editor.vim.vimStateMachine.pushModes(VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    editor.vim.vimStateMachine.mode = Mode.VISUAL(SelectionType.CHARACTER_WISE)
 
     typeText(injector.parser.parseKeys("<A-p>"))
 
@@ -402,11 +403,11 @@ class VimMultipleCursorsExtensionTest : VimTestCase() {
       |}
     """.trimMargin()
 
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     assertState(afterEscape)
 
     typeText(injector.parser.parseKeys("I" + "@NotNull " + "<Esc>"))
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     val afterInsert = """public class Main {
       |  public static void main(String[] args) {
       |    final Integer a = 0;
@@ -420,7 +421,7 @@ class VimMultipleCursorsExtensionTest : VimTestCase() {
       |}
     """.trimMargin()
 
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     assertState(afterInsert)
   }
 
@@ -435,9 +436,9 @@ class VimMultipleCursorsExtensionTest : VimTestCase() {
     configureByText(before)
 
     typeText(injector.parser.parseKeys("<A-n>".repeat(3)))
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     typeText(injector.parser.parseKeys("<A-p>".repeat(3)))
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     typeText(injector.parser.parseKeys("<A-n>".repeat(2)))
 
     val after = """${s}qwe$se
@@ -478,10 +479,10 @@ class VimMultipleCursorsExtensionTest : VimTestCase() {
       |dfkjsg
     """.trimMargin()
     val editor = configureByText(before)
-    editor.vim.vimStateMachine.pushModes(VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    editor.vim.vimStateMachine.mode = Mode.VISUAL(SelectionType.CHARACTER_WISE)
 
     typeText(injector.parser.parseKeys("<A-x>"))
-    assertMode(VimStateMachine.Mode.VISUAL)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertState(before)
   }
 
@@ -550,7 +551,7 @@ fun getCellType(${s}pos$se: VisualPosition): CellType {
   ...${s}al${c}l$se it was settled on some sodden sand
   ...${s}al${c}l$se by the torrent of a mountain pass
     """.trimIndent().dotToTab()
-    doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest(keys, before, after, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @Test
@@ -568,7 +569,7 @@ fun getCellType(${s}pos$se: VisualPosition): CellType {
   fun `test ignores regex in search pattern`() {
     val before = "test ${s}t.*st${c}$se toast tallest t.*st"
     val editor = configureByText(before)
-    editor.vim.vimStateMachine.pushModes(VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    editor.vim.vimStateMachine.mode = Mode.VISUAL(SelectionType.CHARACTER_WISE)
 
     typeText(injector.parser.parseKeys("<A-n><A-n>"))
     val after = "test ${s}t.*st$se toast tallest ${s}t.*st$se"
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/paragraphmotion/ParagraphMotionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/paragraphmotion/ParagraphMotionTest.kt
index 82136e1d6..39a66dd75 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/paragraphmotion/ParagraphMotionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/paragraphmotion/ParagraphMotionTest.kt
@@ -8,7 +8,8 @@
 
 package org.jetbrains.plugins.ideavim.extension.paragraphmotion
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
@@ -36,7 +37,7 @@ class ParagraphMotionTest : VimTestCase() {
         |Sed in orci mauris.
         |Cras id tellus in ex imperdiet egestas.
     """.trimMargin()
-    doTest("}", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("}", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -53,7 +54,7 @@ class ParagraphMotionTest : VimTestCase() {
         |Sed in orci mauris.
         |Cras id tellus in ex imperdiet egestas.
     """.trimMargin().dotToSpace()
-    doTest("}", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("}", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -70,7 +71,7 @@ class ParagraphMotionTest : VimTestCase() {
         |Sed in orci mauris.
         |Cras id tellus in ex imperdiet egestas.
     """.trimMargin().dotToSpace()
-    doTest("v}", before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest("v}", before, after, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @Test
@@ -87,7 +88,7 @@ class ParagraphMotionTest : VimTestCase() {
         |Sed in orci mauris.
         |Cras id tellus in ex imperdiet egestas.
     """.trimMargin().dotToSpace()
-    doTest("d}", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("d}", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -104,7 +105,7 @@ class ParagraphMotionTest : VimTestCase() {
         |Sed in orci mauris.
         |Cras id tellus in ex imperdiet egestas.
     """.trimMargin()
-    doTest("{", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("{", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -121,7 +122,7 @@ class ParagraphMotionTest : VimTestCase() {
         |Sed in orci mauris.
         |Cras id tellus in ex imperdiet egestas.
     """.trimMargin().dotToSpace()
-    doTest("{", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("{", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -138,7 +139,7 @@ class ParagraphMotionTest : VimTestCase() {
         |w${se}here it was settled on some sodden sand
         |hard by the torrent of a mountain pass.
     """.trimMargin().dotToSpace()
-    doTest("v{", before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    doTest("v{", before, after, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @Test
@@ -155,7 +156,7 @@ class ParagraphMotionTest : VimTestCase() {
         |Sed in orci mauris.
         |Cras id tellus in ex imperdiet egestas.
     """.trimMargin().dotToSpace()
-    doTest("d{", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("d{", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -172,6 +173,6 @@ class ParagraphMotionTest : VimTestCase() {
         |Sed in orci mauris.
         |Cras id tellus in ex imperdiet egestas${c}.
     """.trimMargin()
-    doTest("}", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("}", before, after, Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/replacewithregister/ReplaceWithRegisterTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/replacewithregister/ReplaceWithRegisterTest.kt
index 67c4e497c..a455459be 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/replacewithregister/ReplaceWithRegisterTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/replacewithregister/ReplaceWithRegisterTest.kt
@@ -11,8 +11,8 @@ package org.jetbrains.plugins.ideavim.extension.replacewithregister
 import com.intellij.testFramework.UsefulTestCase.assertContainsElements
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import com.maddyhome.idea.vim.newapi.vim
 import com.maddyhome.idea.vim.options.OptionConstants
@@ -395,7 +395,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
       """.trimIndent(),
     )
     assertEquals("legendary", VimPlugin.getRegister().lastRegister?.text)
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @Test
@@ -417,7 +417,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @Test
@@ -440,7 +440,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @Test
@@ -465,7 +465,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @Test
@@ -487,7 +487,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   //  https://youtrack.jetbrains.com/issue/VIM-2881/ReplaceRegister-does-no-longer-worker-with-MultiCursor
@@ -517,7 +517,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
         copyMe
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   @Test
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/surround/VimSurroundExtensionTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/surround/VimSurroundExtensionTest.kt
index 810f38784..892eb8c37 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/surround/VimSurroundExtensionTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/surround/VimSurroundExtensionTest.kt
@@ -11,7 +11,7 @@
 package org.jetbrains.plugins.ideavim.extension.surround
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import org.jetbrains.plugins.ideavim.VimTestCase
 import org.junit.jupiter.api.BeforeEach
@@ -36,9 +36,9 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "if ${c}condition {\n" + "}\n"
     val after = "if ${c}(condition) {\n" + "}\n"
 
-    doTest("yseb", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("yse)", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("yse(", before, "if ( condition ) {\n" + "}\n", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("yseb", before, after, Mode.NORMAL())
+    doTest("yse)", before, after, Mode.NORMAL())
+    doTest("yse(", before, "if ( condition ) {\n" + "}\n", Mode.NORMAL())
   }
 
   @Test
@@ -46,9 +46,9 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "if (condition) ${c}return;\n"
     val after = "if (condition) {return;}\n"
 
-    doTest("ysEB", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("ysE}", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("ysE{", before, "if (condition) { return; }\n", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("ysEB", before, after, Mode.NORMAL())
+    doTest("ysE}", before, after, Mode.NORMAL())
+    doTest("ysE{", before, "if (condition) { return; }\n", Mode.NORMAL())
   }
 
   @Test
@@ -56,9 +56,9 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "int foo = bar${c}index;"
     val after = "int foo = bar[index];"
 
-    doTest("yser", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("yse]", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("yse[", before, "int foo = bar[ index ];", VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("yser", before, after, Mode.NORMAL())
+    doTest("yse]", before, after, Mode.NORMAL())
+    doTest("yse[", before, "int foo = bar[ index ];", Mode.NORMAL())
   }
 
   @Test
@@ -66,8 +66,8 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "foo = new Bar${c}Baz();"
     val after = "foo = new Bar<Baz>();"
 
-    doTest("ysea", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("yse>", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("ysea", before, after, Mode.NORMAL())
+    doTest("yse>", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -75,8 +75,8 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "foo = ${c}new Bar.Baz;"
     val after = "foo = \"new Bar.Baz\";"
 
-    doTest("yst;\"", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("ys4w\"", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("yst;\"", before, after, Mode.NORMAL())
+    doTest("ys4w\"", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -144,7 +144,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "if ${c}condition {\n}\n"
     val after = "if ((condition)) {\n}\n"
 
-    doTest(listOf("ysiw)", "l", "."), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("ysiw)", "l", "."), before, after, Mode.NORMAL())
   }
 
   @Test
@@ -152,7 +152,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "if ${c}condition {\n}\n"
     val after = "if (((condition))) {\n}\n"
 
-    doTest(listOf("ysiw)", "l", ".", "l", "."), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("ysiw)", "l", ".", "l", "."), before, after, Mode.NORMAL())
   }
 
   @Test
@@ -166,7 +166,7 @@ class VimSurroundExtensionTest : VimTestCase() {
                   if 'condition' { }
                     """
 
-    doTest(listOf("ysiw)", "cs\"'", "j", "."), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("ysiw)", "cs\"'", "j", "."), before, after, Mode.NORMAL())
   }
 
   @Test
@@ -184,8 +184,7 @@ class VimSurroundExtensionTest : VimTestCase() {
       listOf("ysiwf", "myFunction<CR>", "j", "."),
       before,
       after,
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -200,7 +199,7 @@ class VimSurroundExtensionTest : VimTestCase() {
                   <myTag>abc</myTag>
                     """
 
-    doTest(listOf("ysiwt", "myTag>", "j", "."), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("ysiwt", "myTag>", "j", "."), before, after, Mode.NORMAL())
   }
 
   /* visual surround */
@@ -210,18 +209,17 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "if ${c}condition {\n" + "}\n"
     val after = "if ${c}(condition) {\n" + "}\n"
 
-    doTest("veSb", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    assertMode(VimStateMachine.Mode.COMMAND)
-    doTest("veS)", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    assertMode(VimStateMachine.Mode.COMMAND)
+    doTest("veSb", before, after, Mode.NORMAL())
+    assertMode(Mode.NORMAL())
+    doTest("veS)", before, after, Mode.NORMAL())
+    assertMode(Mode.NORMAL())
     doTest(
       "veS(",
       before,
       "if ( condition ) {\n" + "}\n",
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
   }
 
   /* Delete surroundings */
@@ -231,9 +229,9 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "if (${c}condition) {\n" + "}\n"
     val after = "if condition {\n" + "}\n"
 
-    doTest("dsb", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("ds(", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("ds)", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("dsb", before, after, Mode.NORMAL())
+    doTest("ds(", before, after, Mode.NORMAL())
+    doTest("ds)", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -241,7 +239,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "if (\"${c}foo\".equals(foo)) {\n" + "}\n"
     val after = "if (${c}foo.equals(foo)) {\n" + "}\n"
 
-    doTest("ds\"", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("ds\"", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -249,9 +247,9 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "if (condition) {${c}return;}\n"
     val after = "if (condition) return;\n"
 
-    doTest(listOf("dsB"), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("ds}", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("ds{", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("dsB"), before, after, Mode.NORMAL())
+    doTest("ds}", before, after, Mode.NORMAL())
+    doTest("ds{", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -259,9 +257,9 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "int foo = bar[${c}index];"
     val after = "int foo = barindex;"
 
-    doTest("dsr", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("ds]", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("ds[", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("dsr", before, after, Mode.NORMAL())
+    doTest("ds]", before, after, Mode.NORMAL())
+    doTest("ds[", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -269,9 +267,9 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "foo = new Bar<${c}Baz>();"
     val after = "foo = new BarBaz();"
 
-    doTest("dsa", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("ds>", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("ds<", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("dsa", before, after, Mode.NORMAL())
+    doTest("ds>", before, after, Mode.NORMAL())
+    doTest("ds<", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -279,7 +277,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "<div><p>${c}Foo</p></div>"
     val after = "<div>${c}Foo</div>"
 
-    doTest(listOf("dst"), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("dst"), before, after, Mode.NORMAL())
   }
 
   // VIM-1085
@@ -288,7 +286,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "Foo\n" + "Seq(\"-${c}Yrangepos\")\n"
     val after = "Foo\n" + "Seq\"-Yrangepos\"\n"
 
-    doTest(listOf("dsb"), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("dsb"), before, after, Mode.NORMAL())
   }
 
   // VIM-1085
@@ -305,7 +303,7 @@ class VimSurroundExtensionTest : VimTestCase() {
       "    other\n" +
       "Baz\n"
 
-    doTest(listOf("dsb"), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("dsb"), before, after, Mode.NORMAL())
   }
 
   // VIM-2227
@@ -313,10 +311,10 @@ class VimSurroundExtensionTest : VimTestCase() {
   fun testDeleteInvalidSurroundingCharacter() {
     val text = "if (${c}condition) {"
 
-    doTest("yibds]", text, text, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("yibds[", text, text, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("yibds}", text, text, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("yibds{", text, text, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("yibds]", text, text, Mode.NORMAL())
+    doTest("yibds[", text, text, Mode.NORMAL())
+    doTest("yibds}", text, text, Mode.NORMAL())
+    doTest("yibds{", text, text, Mode.NORMAL())
   }
 
   @Test
@@ -324,7 +322,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "if ((${c}condition)) {\n}\n"
     val after = "if condition {\n}\n"
 
-    doTest(listOf("dsb."), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("dsb."), before, after, Mode.NORMAL())
   }
 
   @Test
@@ -332,7 +330,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "if (\"${c}condition\") {\n}\n"
     val after = "if (condition) {\n}\n"
 
-    doTest(listOf("ds\"."), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("ds\"."), before, after, Mode.NORMAL())
   }
 
   /* Change surroundings */
@@ -342,7 +340,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "if (${c}condition) {\n" + "}\n"
     val after = "if [condition] {\n" + "}\n"
 
-    doTest(listOf("csbr"), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("csbr"), before, after, Mode.NORMAL())
   }
 
   @Test
@@ -350,7 +348,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "if (condition) {${c}return;}"
     val after = "if (condition) (return;)"
 
-    doTest(listOf("csBb"), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("csBb"), before, after, Mode.NORMAL())
   }
 
   @Test
@@ -358,7 +356,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "<div><p>${c}Foo</p></div>"
     val after = "<div>${c}(Foo)</div>"
 
-    doTest(listOf("cstb"), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("cstb"), before, after, Mode.NORMAL())
   }
 
   @Test
@@ -366,7 +364,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "<div><p>${c}Foo</p></div>"
     val after = "<div>${c}<b>Foo</b></div>"
 
-    doTest(listOf("cst<b>"), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("cst<b>"), before, after, Mode.NORMAL())
   }
 
   @Test
@@ -374,7 +372,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "foo(${c}index)(index2) = bar;"
     val after = "foo[index][index2] = bar;"
 
-    doTest(listOf("csbrE."), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("csbrE."), before, after, Mode.NORMAL())
   }
 
   // VIM-2227
@@ -382,10 +380,10 @@ class VimSurroundExtensionTest : VimTestCase() {
   fun testChangeInvalidSurroundingCharacter() {
     val text = "if (${c}condition) {"
 
-    doTest("yibcs]}", text, text, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("yibcs[}", text, text, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("yibcs}]", text, text, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
-    doTest("yibcs{]", text, text, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("yibcs]}", text, text, Mode.NORMAL())
+    doTest("yibcs[}", text, text, Mode.NORMAL())
+    doTest("yibcs}]", text, text, Mode.NORMAL())
+    doTest("yibcs{]", text, text, Mode.NORMAL())
   }
 
   @VimBehaviorDiffers(
@@ -419,8 +417,7 @@ class VimSurroundExtensionTest : VimTestCase() {
       
       <p>Some text</p>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -455,8 +452,7 @@ class VimSurroundExtensionTest : VimTestCase() {
           <p>Some other paragraph</p>
       </div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -478,8 +474,7 @@ class VimSurroundExtensionTest : VimTestCase() {
           <p>Some other paragraph</p>
       </div>
       """.trimIndent(),
-      VimStateMachine.Mode.COMMAND,
-      VimStateMachine.SubMode.NONE,
+      Mode.NORMAL(),
     )
   }
 
@@ -512,7 +507,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     val before = "if (condition) ${c}return;\n"
     val after = "if (condition) \"return\";\n"
 
-    doTest(":map gw ysiw\"<CR>gw", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(":map gw ysiw\"<CR>gw", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -526,7 +521,7 @@ class VimSurroundExtensionTest : VimTestCase() {
       if ${c}condition return;
     """.trimIndent()
 
-    doTest("qqds)qj@q", before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest("qqds)qj@q", before, after, Mode.NORMAL())
   }
 
   @Test
@@ -541,7 +536,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     """.trimIndent()
 
     val keys = ":map gw ds)<CR>" + "qqgwqj@q"
-    doTest(keys, before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(keys, before, after, Mode.NORMAL())
   }
 
   @Test
@@ -555,7 +550,7 @@ class VimSurroundExtensionTest : VimTestCase() {
                   ${c}[xyz]
                     """
 
-    doTest(listOf("cs(]"), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("cs(]"), before, after, Mode.NORMAL())
   }
 
   @Test
@@ -569,7 +564,7 @@ class VimSurroundExtensionTest : VimTestCase() {
                   ${c}xyz
                     """
 
-    doTest(listOf("ds("), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("ds("), before, after, Mode.NORMAL())
   }
 
   @Test
@@ -583,14 +578,14 @@ class VimSurroundExtensionTest : VimTestCase() {
                   ${c}"xyz"  
                     """
 
-    doTest(listOf("yss\""), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("yss\""), before, after, Mode.NORMAL())
   }
 
   @Test
   fun `test csw`() {
     val before = "var1, va${c}r2, var3"
     val after = "var1, ${c}\"var2\", var3"
-    doTest(listOf("csw\""), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("csw\""), before, after, Mode.NORMAL())
   }
 
   @Test
@@ -601,7 +596,7 @@ class VimSurroundExtensionTest : VimTestCase() {
     val after = """
       <a id="languageChanger" class="shababMallFont" href="?lang={{ App::getLocale() == "en" ? ${c}'ar' : "en" }}">
     """
-    doTest(listOf("cs\"'"), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("cs\"'"), before, after, Mode.NORMAL())
   }
 
   @Test
@@ -612,6 +607,6 @@ class VimSurroundExtensionTest : VimTestCase() {
     val after = """
       "Hello (HI test test) extra information"
     """
-    doTest(listOf("cs\")"), before, after, VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    doTest(listOf("cs\")"), before, after, Mode.NORMAL())
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/textobjindent/VimIndentObjectTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/textobjindent/VimIndentObjectTest.kt
index 5f8622aa1..c763be150 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/extension/textobjindent/VimIndentObjectTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/textobjindent/VimIndentObjectTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.extension.textobjindent
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -231,7 +231,7 @@ class VimIndentObjectTest : VimTestCase() {
         five
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     assertSelection(null)
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/group/visual/IdeaVisualControlTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/group/visual/IdeaVisualControlTest.kt
index 2c162e851..e99b979a7 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/group/visual/IdeaVisualControlTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/group/visual/IdeaVisualControlTest.kt
@@ -14,11 +14,13 @@ import com.intellij.codeInsight.template.TemplateManager
 import com.intellij.codeInsight.template.impl.ConstantNode
 import com.intellij.codeInsight.template.impl.TemplateManagerImpl
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.selectionType
 import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
 import com.maddyhome.idea.vim.group.visual.VimVisualTimer
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
-import com.maddyhome.idea.vim.helper.subMode
+import com.maddyhome.idea.vim.state.mode.mode
 import com.maddyhome.idea.vim.listener.VimListenerManager
 import com.maddyhome.idea.vim.newapi.vim
 import com.maddyhome.idea.vim.options.OptionConstants
@@ -47,10 +49,9 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    assertMode(VimStateMachine.Mode.COMMAND)
-    assertSubMode(VimStateMachine.SubMode.NONE)
+    assertMode(Mode.NORMAL())
     assertCaretsVisualAttributes()
   }
 
@@ -67,11 +68,10 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("l"))
@@ -85,8 +85,7 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -113,11 +112,10 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("l"))
@@ -131,8 +129,7 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -149,11 +146,10 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("l"))
@@ -167,8 +163,7 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -185,11 +180,10 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("l"))
@@ -203,8 +197,7 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -221,11 +214,10 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("l"))
@@ -239,8 +231,7 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -257,11 +248,10 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("l"))
@@ -275,8 +265,7 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -293,11 +282,10 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("l"))
@@ -311,8 +299,7 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -329,11 +316,10 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain ${s}pass.$c$se
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("l"))
@@ -347,8 +333,7 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain ${s}pass.$c$se
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -365,11 +350,10 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.LINE_WISE))
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("l"))
@@ -383,8 +367,7 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("j"))
@@ -398,8 +381,7 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -416,11 +398,10 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.LINE_WISE))
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("j"))
@@ -434,8 +415,7 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -452,11 +432,10 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.LINE_WISE))
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("j"))
@@ -470,8 +449,7 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent o${c}f a mountain pass.$se
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -488,11 +466,10 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.LINE_WISE))
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("j"))
@@ -506,8 +483,7 @@ class IdeaVisualControlTest : VimTestCase() {
             ${se}hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -524,11 +500,10 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain pass.$se
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.LINE_WISE))
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("j"))
@@ -542,8 +517,7 @@ class IdeaVisualControlTest : VimTestCase() {
             ${s}hard by the torrent o${c}f a mountain pass.$se
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -560,11 +534,10 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.LINE_WISE))
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("k"))
@@ -578,8 +551,7 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -596,11 +568,10 @@ class IdeaVisualControlTest : VimTestCase() {
             ha${s}rd by $c${se}the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -617,11 +588,10 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_BLOCK)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.BLOCK_WISE))
+    assertMode(Mode.VISUAL(SelectionType.BLOCK_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("l"))
@@ -635,8 +605,7 @@ class IdeaVisualControlTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_BLOCK)
+    assertMode(Mode.VISUAL(SelectionType.BLOCK_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -653,11 +622,10 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_BLOCK)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.BLOCK_WISE))
+    assertMode(Mode.VISUAL(SelectionType.BLOCK_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("j"))
@@ -671,8 +639,7 @@ class IdeaVisualControlTest : VimTestCase() {
             ha${s}rd by the torrent of a mountain pass.${c}$se
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_BLOCK)
+    assertMode(Mode.VISUAL(SelectionType.BLOCK_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -689,11 +656,10 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_BLOCK)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.BLOCK_WISE))
+    assertMode(Mode.VISUAL(SelectionType.BLOCK_WISE))
     assertCaretsVisualAttributes()
 
     typeText(injector.parser.parseKeys("l"))
@@ -707,8 +673,7 @@ class IdeaVisualControlTest : VimTestCase() {
             hard by the torrent of a mountain pass.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_BLOCK)
+    assertMode(Mode.VISUAL(SelectionType.BLOCK_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -724,11 +689,11 @@ class IdeaVisualControlTest : VimTestCase() {
       """.trimIndent(),
     )
     VimListenerManager.EditorListeners.addAll()
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
 
     fixture.editor.selectionModel.setSelection(5, 10)
 
-    waitAndAssertMode(fixture, VimStateMachine.Mode.SELECT)
+    waitAndAssertMode(fixture, Mode.SELECT(SelectionType.CHARACTER_WISE))
   }
 
   @OptionTest(VimOption(TestOptionConstants.selectmode, limitedValues = [""]))
@@ -743,11 +708,11 @@ class IdeaVisualControlTest : VimTestCase() {
       """.trimIndent(),
     )
     VimListenerManager.EditorListeners.addAll()
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
 
     fixture.editor.selectionModel.setSelection(5, 10)
 
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @OptionTest(VimOption(TestOptionConstants.selectmode, limitedValues = [""]))
@@ -762,15 +727,13 @@ class IdeaVisualControlTest : VimTestCase() {
       """.trimIndent(),
     )
     typeText(injector.parser.parseKeys("V"))
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
 
     fixture.editor.selectionModel.setSelection(2, 5)
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
 
-    waitAndAssert { fixture.editor.vim.subMode == VimStateMachine.SubMode.VISUAL_CHARACTER }
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssert { fixture.editor.vim.mode.selectionType == SelectionType.CHARACTER_WISE }
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
   }
 
@@ -791,15 +754,13 @@ class IdeaVisualControlTest : VimTestCase() {
     VimVisualTimer.doNow()
 
     typeText(injector.parser.parseKeys("<esc>V"))
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_LINE)
+    assertMode(Mode.VISUAL(SelectionType.LINE_WISE))
 
     fixture.editor.selectionModel.setSelection(2, 5)
     IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
 
-    waitAndAssert { fixture.editor.vim.subMode == VimStateMachine.SubMode.VISUAL_CHARACTER }
-    assertMode(VimStateMachine.Mode.VISUAL)
-    assertSubMode(VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssert { fixture.editor.vim.mode.selectionType == SelectionType.CHARACTER_WISE }
+    assertMode(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     assertCaretsVisualAttributes()
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/group/visual/NonVimVisualChangeTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/group/visual/NonVimVisualChangeTest.kt
index 09efc2215..178bd39fc 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/group/visual/NonVimVisualChangeTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/group/visual/NonVimVisualChangeTest.kt
@@ -13,9 +13,10 @@ import com.intellij.openapi.application.ApplicationManager
 import com.intellij.openapi.command.CommandProcessor
 import com.intellij.openapi.editor.LogicalPosition
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
-import com.maddyhome.idea.vim.helper.mode
-import com.maddyhome.idea.vim.helper.subMode
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.selectionType
+import com.maddyhome.idea.vim.state.mode.mode
 import com.maddyhome.idea.vim.listener.VimListenerManager
 import com.maddyhome.idea.vim.newapi.vim
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
@@ -48,7 +49,7 @@ class NonVimVisualChangeTest : VimTestCase() {
     )
     VimListenerManager.EditorListeners.add(fixture.editor)
     typeText(injector.parser.parseKeys("i"))
-    assertMode(VimStateMachine.Mode.INSERT)
+    assertMode(Mode.INSERT)
     ApplicationManager.getApplication().runWriteAction {
       CommandProcessor.getInstance().runUndoTransparentAction {
         BackspaceHandler.deleteToTargetPosition(fixture.editor, LogicalPosition(2, 0))
@@ -64,7 +65,7 @@ class NonVimVisualChangeTest : VimTestCase() {
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
     )
-    assertMode(VimStateMachine.Mode.INSERT)
+    assertMode(Mode.INSERT)
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
@@ -82,13 +83,13 @@ class NonVimVisualChangeTest : VimTestCase() {
     )
     VimListenerManager.EditorListeners.add(fixture.editor)
     typeText(injector.parser.parseKeys("i"))
-    assertMode(VimStateMachine.Mode.INSERT)
+    assertMode(Mode.INSERT)
 
     // Fast add and remove selection
     fixture.editor.selectionModel.setSelection(0, 10)
     fixture.editor.selectionModel.removeSelection()
 
-    assertDoesntChange { fixture.editor.vim.mode == VimStateMachine.Mode.INSERT }
+    assertDoesntChange { fixture.editor.vim.mode == Mode.INSERT }
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
@@ -106,14 +107,14 @@ class NonVimVisualChangeTest : VimTestCase() {
     )
     VimListenerManager.EditorListeners.add(fixture.editor)
     typeText(injector.parser.parseKeys("i"))
-    assertMode(VimStateMachine.Mode.INSERT)
+    assertMode(Mode.INSERT)
 
     // Fast add and remove selection
     fixture.editor.selectionModel.setSelection(0, 10)
     fixture.editor.selectionModel.removeSelection()
     fixture.editor.selectionModel.setSelection(0, 10)
 
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
@@ -130,15 +131,15 @@ class NonVimVisualChangeTest : VimTestCase() {
     configureByText(text)
     VimListenerManager.EditorListeners.add(fixture.editor)
     typeText(injector.parser.parseKeys("i"))
-    assertMode(VimStateMachine.Mode.INSERT)
+    assertMode(Mode.INSERT)
 
     val range = text.rangeOf("Discovery")
     fixture.editor.selectionModel.setSelection(range.startOffset, range.endOffset)
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    kotlin.test.assertEquals(VimStateMachine.SubMode.VISUAL_CHARACTER, fixture.editor.vim.subMode)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
+    kotlin.test.assertEquals(SelectionType.CHARACTER_WISE, fixture.editor.vim.mode.selectionType)
 
     val rangeLine = text.rangeOf("A Discovery\n")
     fixture.editor.selectionModel.setSelection(rangeLine.startOffset, rangeLine.endOffset)
-    waitAndAssert { fixture.editor.vim.subMode == VimStateMachine.SubMode.VISUAL_LINE }
+    waitAndAssert { fixture.editor.vim.mode.selectionType == SelectionType.LINE_WISE }
   }
 }
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/group/visual/TemplateTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/group/visual/TemplateTest.kt
index a0c5ca06e..936a4a92f 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/group/visual/TemplateTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/group/visual/TemplateTest.kt
@@ -21,14 +21,15 @@ import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
 import com.intellij.testFramework.PlatformTestUtil
 import com.intellij.testFramework.fixtures.CodeInsightTestUtil.doInlineRename
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.group.IjOptionConstants
 import com.maddyhome.idea.vim.helper.inInsertMode
-import com.maddyhome.idea.vim.helper.inNormalMode
-import com.maddyhome.idea.vim.helper.inSelectMode
 import com.maddyhome.idea.vim.helper.inVisualMode
 import com.maddyhome.idea.vim.listener.VimListenerManager
 import com.maddyhome.idea.vim.newapi.vim
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.inNormalMode
+import com.maddyhome.idea.vim.state.mode.inSelectMode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestIjOptionConstants
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@@ -90,12 +91,12 @@ class TemplateTest : VimTestCase() {
       """.trimIndent(),
     )
     startRenaming(VariableInplaceRenameHandler())
-    waitAndAssertMode(fixture, VimStateMachine.Mode.SELECT)
-    assertState(VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.SELECT(SelectionType.CHARACTER_WISE))
+    assertState(Mode.SELECT(SelectionType.CHARACTER_WISE))
 
     typeText(injector.parser.parseKeys("myNewVar" + "<CR>"))
 
-    assertState(VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    assertState(Mode.INSERT)
     assertState(
       """
             class Hello {
@@ -121,8 +122,8 @@ class TemplateTest : VimTestCase() {
     )
     enterCommand("set idearefactormode=visual")
     startRenaming(VariableInplaceRenameHandler())
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
-    assertState(VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
+    assertState(Mode.VISUAL(SelectionType.CHARACTER_WISE))
     // Disable template
     typeText(injector.parser.parseKeys("<CR>"))
   }
@@ -140,15 +141,15 @@ class TemplateTest : VimTestCase() {
       """.trimIndent(),
     )
     startRenaming(VariableInplaceRenameHandler())
-    waitAndAssertMode(fixture, VimStateMachine.Mode.SELECT)
-    assertState(VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.SELECT(SelectionType.CHARACTER_WISE))
+    assertState(Mode.SELECT(SelectionType.CHARACTER_WISE))
 
     LookupManager.hideActiveLookup(fixture.project)
     typeText(injector.parser.parseKeys("<Left>"))
-    assertState(VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    assertState(Mode.INSERT)
     typeText(injector.parser.parseKeys("pre" + "<CR>"))
 
-    assertState(VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    assertState(Mode.INSERT)
     assertState(
       """
             class Hello {
@@ -173,12 +174,12 @@ class TemplateTest : VimTestCase() {
       """.trimIndent(),
     )
     startRenaming(VariableInplaceRenameHandler())
-    waitAndAssertMode(fixture, VimStateMachine.Mode.SELECT)
-    assertState(VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.SELECT(SelectionType.CHARACTER_WISE))
+    assertState(Mode.SELECT(SelectionType.CHARACTER_WISE))
 
     LookupManager.hideActiveLookup(fixture.project)
     typeText(injector.parser.parseKeys("<Right>"))
-    assertState(VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    assertState(Mode.INSERT)
     assertState(
       """
             class Hello {
@@ -203,12 +204,12 @@ class TemplateTest : VimTestCase() {
       """.trimIndent(),
     )
     startRenaming(VariableInplaceRenameHandler())
-    waitAndAssertMode(fixture, VimStateMachine.Mode.SELECT)
-    assertState(VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.SELECT(SelectionType.CHARACTER_WISE))
+    assertState(Mode.SELECT(SelectionType.CHARACTER_WISE))
 
     LookupManager.hideActiveLookup(fixture.project)
     typeText(injector.parser.parseKeys("<Left>"))
-    assertState(VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    assertState(Mode.INSERT)
     assertState(
       """
             class Hello {
@@ -233,12 +234,12 @@ class TemplateTest : VimTestCase() {
       """.trimIndent(),
     )
     startRenaming(VariableInplaceRenameHandler())
-    waitAndAssertMode(fixture, VimStateMachine.Mode.SELECT)
-    assertState(VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.SELECT(SelectionType.CHARACTER_WISE))
+    assertState(Mode.SELECT(SelectionType.CHARACTER_WISE))
 
     LookupManager.hideActiveLookup(fixture.project)
     typeText(injector.parser.parseKeys("<Right>"))
-    assertState(VimStateMachine.Mode.INSERT, VimStateMachine.SubMode.NONE)
+    assertState(Mode.INSERT)
     assertState(
       """
             class Hello {
@@ -263,12 +264,12 @@ class TemplateTest : VimTestCase() {
       """.trimIndent(),
     )
     startRenaming(VariableInplaceRenameHandler())
-    waitAndAssertMode(fixture, VimStateMachine.Mode.SELECT)
-    assertState(VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.SELECT(SelectionType.CHARACTER_WISE))
+    assertState(Mode.SELECT(SelectionType.CHARACTER_WISE))
 
     typeText(injector.parser.parseKeys("<ESC>"))
 
-    assertState(VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    assertState(Mode.NORMAL())
     assertState(
       """
             class Hello {
@@ -293,12 +294,12 @@ class TemplateTest : VimTestCase() {
       """.trimIndent(),
     )
     startRenaming(VariableInplaceRenameHandler())
-    waitAndAssertMode(fixture, VimStateMachine.Mode.SELECT)
-    assertState(VimStateMachine.Mode.SELECT, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    waitAndAssertMode(fixture, Mode.SELECT(SelectionType.CHARACTER_WISE))
+    assertState(Mode.SELECT(SelectionType.CHARACTER_WISE))
 
     typeText(injector.parser.parseKeys("Hello" + "<ESC>"))
 
-    assertState(VimStateMachine.Mode.COMMAND, VimStateMachine.SubMode.NONE)
+    assertState(Mode.NORMAL())
     assertState(
       """
             class Hello {
@@ -381,7 +382,7 @@ class TemplateTest : VimTestCase() {
       """.trimIndent(),
     )
     startRenaming(VariableInplaceRenameHandler())
-    waitAndAssertMode(fixture, VimStateMachine.Mode.SELECT)
+    waitAndAssertMode(fixture, Mode.SELECT(SelectionType.CHARACTER_WISE))
   }
 
   @OptionTest(
@@ -400,7 +401,7 @@ class TemplateTest : VimTestCase() {
     )
     typeText(injector.parser.parseKeys("i"))
     startRenaming(VariableInplaceRenameHandler())
-    waitAndAssertMode(fixture, VimStateMachine.Mode.SELECT)
+    waitAndAssertMode(fixture, Mode.SELECT(SelectionType.CHARACTER_WISE))
   }
 
   @OptionTest(
@@ -456,7 +457,7 @@ class TemplateTest : VimTestCase() {
       """.trimIndent(),
     )
     startRenaming(VariableInplaceRenameHandler())
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @OptionTest(
@@ -475,7 +476,7 @@ class TemplateTest : VimTestCase() {
     )
     typeText(injector.parser.parseKeys("i"))
     startRenaming(VariableInplaceRenameHandler())
-    waitAndAssertMode(fixture, VimStateMachine.Mode.VISUAL)
+    waitAndAssertMode(fixture, Mode.VISUAL(SelectionType.CHARACTER_WISE))
   }
 
   @OptionTest(
@@ -530,10 +531,10 @@ class TemplateTest : VimTestCase() {
     manager.startTemplate(fixture.editor, template)
     PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue()
 
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     assertOffset(2)
     typeText(injector.parser.parseKeys("<CR>"))
-    assertMode(VimStateMachine.Mode.COMMAND)
+    assertMode(Mode.NORMAL())
     assertOffset(12)
     typeText(injector.parser.parseKeys("<CR>"))
     kotlin.test.assertNull(TemplateManagerImpl.getTemplateState(fixture.editor))
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/helper/CaretVisualAttributesHelperTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/helper/CaretVisualAttributesHelperTest.kt
index 48b6bf7ec..308682234 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/helper/CaretVisualAttributesHelperTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/helper/CaretVisualAttributesHelperTest.kt
@@ -14,6 +14,7 @@ import com.intellij.openapi.editor.VisualPosition
 import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
 import com.maddyhome.idea.vim.VimPlugin
 import com.maddyhome.idea.vim.api.injector
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
 import com.maddyhome.idea.vim.newapi.vim
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
@@ -188,6 +189,7 @@ class CaretVisualAttributesHelperTest : VimTestCase() {
     configureByText("I ${c}found it in a legendary land")
     enterCommand("set keymodel=startsel,stopsel")
     typeText("i", "<S-Right><S-Right><S-Right>", "<Esc>")
+    assertMode(Mode.INSERT)
     assertCaretVisualAttributes(CaretVisualAttributes.Shape.BAR, 0.25F)
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/helper/SearchHelperTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/helper/SearchHelperTest.kt
index 36e6453da..d76f6a3fc 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/helper/SearchHelperTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/helper/SearchHelperTest.kt
@@ -8,7 +8,8 @@
 package org.jetbrains.plugins.ideavim.helper
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.helper.checkInString
 import com.maddyhome.idea.vim.newapi.vim
@@ -108,8 +109,7 @@ class SearchHelperTest : VimTestCase() {
       "va(",
       "((int) nu<caret>m)",
       "<selection>((int) num)</selection>",
-      VimStateMachine.Mode.VISUAL,
-      VimStateMachine.SubMode.VISUAL_CHARACTER,
+      Mode.VISUAL(SelectionType.CHARACTER_WISE),
     )
   }
 
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/option/DigraphTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/option/DigraphTest.kt
index d3e8408b1..0e86c5efd 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/option/DigraphTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/option/DigraphTest.kt
@@ -8,7 +8,7 @@
 
 package org.jetbrains.plugins.ideavim.option
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import org.jetbrains.plugins.ideavim.SkipNeovimReason
 import org.jetbrains.plugins.ideavim.TestWithoutNeovim
 import org.jetbrains.plugins.ideavim.VimTestCase
@@ -40,8 +40,7 @@ class DigraphTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     ) {
       enterCommand("set digraph")
     }
@@ -68,8 +67,7 @@ class DigraphTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     ) {
       enterCommand("set digraph")
     }
@@ -96,8 +94,7 @@ class DigraphTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     ) {
       enterCommand("set digraph")
     }
@@ -124,8 +121,7 @@ class DigraphTest : VimTestCase() {
             Sed in orci mauris.
             Cras id tellus in ex imperdiet egestas.
       """.trimIndent(),
-      VimStateMachine.Mode.INSERT,
-      VimStateMachine.SubMode.NONE,
+Mode.INSERT,
     ) {
       enterCommand("set digraph")
     }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/KeyHandler.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/KeyHandler.kt
index fba815d2f..a0848c36e 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/KeyHandler.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/KeyHandler.kt
@@ -19,8 +19,6 @@ import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.MappingMode
 import com.maddyhome.idea.vim.command.MappingProcessor
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
-import com.maddyhome.idea.vim.command.VimStateMachine.Companion.getInstance
 import com.maddyhome.idea.vim.common.CurrentCommandState
 import com.maddyhome.idea.vim.common.DigraphResult
 import com.maddyhome.idea.vim.common.argumentCaptured
@@ -29,15 +27,16 @@ import com.maddyhome.idea.vim.diagnostic.debug
 import com.maddyhome.idea.vim.diagnostic.trace
 import com.maddyhome.idea.vim.diagnostic.vimLogger
 import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
-import com.maddyhome.idea.vim.helper.inNormalMode
-import com.maddyhome.idea.vim.helper.inSingleNormalMode
-import com.maddyhome.idea.vim.helper.inVisualMode
 import com.maddyhome.idea.vim.helper.isCloseKeyStroke
 import com.maddyhome.idea.vim.helper.vimStateMachine
 import com.maddyhome.idea.vim.key.CommandNode
 import com.maddyhome.idea.vim.key.CommandPartNode
 import com.maddyhome.idea.vim.key.KeyStack
 import com.maddyhome.idea.vim.key.Node
+import com.maddyhome.idea.vim.state.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.ReturnTo
+import com.maddyhome.idea.vim.state.mode.returnTo
 import java.awt.event.InputEvent
 import java.awt.event.KeyEvent
 import javax.swing.KeyStroke
@@ -146,10 +145,10 @@ public class KeyHandler {
             LOG.trace("We are not able to find a node for this key")
 
             // If we are in insert/replace mode send this key in for processing
-            if (editorState.mode == VimStateMachine.Mode.INSERT || editorState.mode == VimStateMachine.Mode.REPLACE) {
+            if (editorState.mode == Mode.INSERT || editorState.mode == Mode.REPLACE) {
               LOG.trace("Process insert or replace")
               shouldRecord = injector.changeGroup.processKey(editor, context, key) && shouldRecord
-            } else if (editorState.mode == VimStateMachine.Mode.SELECT) {
+            } else if (editorState.mode is Mode.SELECT) {
               LOG.trace("Process select")
               shouldRecord = injector.changeGroup.processKeyInSelectMode(editor, context, key) && shouldRecord
             } else if (editorState.mappingState.mappingMode == MappingMode.CMD_LINE) {
@@ -252,9 +251,9 @@ public class KeyHandler {
   private fun isCommandCountKey(chKey: Char, editorState: VimStateMachine): Boolean {
     // Make sure to avoid handling '0' as the start of a count.
     val commandBuilder = editorState.commandBuilder
-    val notRegisterPendingCommand = editorState.mode.inNormalMode && !editorState.isRegisterPending
-    val visualMode = editorState.mode.inVisualMode && !editorState.isRegisterPending
-    val opPendingMode = editorState.mode === VimStateMachine.Mode.OP_PENDING
+    val notRegisterPendingCommand = editorState.mode is Mode.NORMAL && !editorState.isRegisterPending
+    val visualMode = editorState.mode is Mode.VISUAL && !editorState.isRegisterPending
+    val opPendingMode = editorState.mode is Mode.OP_PENDING
 
     if (notRegisterPendingCommand || visualMode || opPendingMode) {
       if (commandBuilder.isExpectingCount && Character.isDigit(chKey) && (commandBuilder.count > 0 || chKey != '0')) {
@@ -270,7 +269,7 @@ public class KeyHandler {
     // See `:help N<Del>`
     val commandBuilder = editorState.commandBuilder
     val isDeleteCommandKeyCount =
-      (editorState.mode === VimStateMachine.Mode.COMMAND || editorState.mode === VimStateMachine.Mode.VISUAL || editorState.mode === VimStateMachine.Mode.OP_PENDING) &&
+      (editorState.mode is Mode.NORMAL || editorState.mode is Mode.VISUAL || editorState.mode is Mode.OP_PENDING) &&
         commandBuilder.isExpectingCount && commandBuilder.count > 0 && key.keyCode == KeyEvent.VK_DELETE
 
     LOG.debug { "This is a delete command key count: $isDeleteCommandKeyCount" }
@@ -278,13 +277,13 @@ public class KeyHandler {
   }
 
   private fun isEditorReset(key: KeyStroke, editorState: VimStateMachine): Boolean {
-    val editorReset = editorState.mode == VimStateMachine.Mode.COMMAND && key.isCloseKeyStroke()
+    val editorReset = editorState.mode is Mode.NORMAL && key.isCloseKeyStroke()
     LOG.debug { "This is editor reset: $editorReset" }
     return editorReset
   }
 
   private fun isSelectRegister(key: KeyStroke, editorState: VimStateMachine): Boolean {
-    if (editorState.mode != VimStateMachine.Mode.COMMAND && editorState.mode != VimStateMachine.Mode.VISUAL) {
+    if (editorState.mode !is Mode.NORMAL && editorState.mode !is Mode.VISUAL) {
       return false
     }
     return if (editorState.isRegisterPending) {
@@ -418,14 +417,13 @@ public class KeyHandler {
       editorState.mappingState.mappingMode == MappingMode.OP_PENDING,
       command.rawCount,
       editorState.mode,
-      editorState.subMode,
     )
 
     // If we were in "operator pending" mode, reset back to normal mode.
     editorState.resetOpPending()
 
     // Save off the command we are about to execute
-    editorState.setExecutingCommand(command)
+    editorState.executingCommand = command
     val type = command.type
     if (type.isWrite) {
       if (!editor.isWritable()) {
@@ -500,7 +498,7 @@ public class KeyHandler {
       val text = injector.processGroup.endSearchCommand()
       commandBuilder.popCommandPart() // Pop ProcessExEntryAction
       commandBuilder.completeCommandPart(Argument(text)) // Set search text on SearchEntry(Fwd|Rev)Action
-      editorState.popModes() // Pop CMD_LINE
+      editorState.mode = Mode.NORMAL()
     }
   }
 
@@ -524,8 +522,9 @@ public class KeyHandler {
         if (editorState.isDotRepeatInProgress && argumentCaptured != null) {
           commandBuilder.completeCommandPart(argumentCaptured!!)
         }
-        editorState.pushModes(VimStateMachine.Mode.OP_PENDING, VimStateMachine.SubMode.NONE)
+        editorState.mode = Mode.OP_PENDING(editorState.mode.returnTo)
       }
+
       Argument.Type.DIGRAPH -> // Command actions represent the completion of a command. Showcmd relies on this - if the action represents a
         // part of a command, the showcmd output is reset part way through. This means we need to special case entering
         // digraph/literal input mode. We have an action that takes a digraph as an argument, and pushes it back through
@@ -540,13 +539,15 @@ public class KeyHandler {
           editorState.startLiteralSequence()
           setPromptCharacterEx('^')
         }
+
       Argument.Type.EX_STRING -> {
         // The current Command expects an EX_STRING argument. E.g. SearchEntry(Fwd|Rev)Action. This won't execute until
         // state hits READY. Start the ex input field, push CMD_LINE mode and wait for the argument.
         injector.processGroup.startSearchCommand(editor, context, commandBuilder.count, key)
         commandBuilder.commandState = CurrentCommandState.NEW_COMMAND
-        editorState.pushModes(VimStateMachine.Mode.CMD_LINE, VimStateMachine.SubMode.NONE)
+        editorState.mode = Mode.CMD_LINE
       }
+
       else -> Unit
     }
 
@@ -572,7 +573,7 @@ public class KeyHandler {
    * @param editor The editor to reset.
    */
   public fun partialReset(editor: VimEditor) {
-    val editorState = getInstance(editor)
+    val editorState = VimStateMachine.getInstance(editor)
     editorState.mappingState.resetMappingSequence()
     editorState.commandBuilder.resetInProgressCommandPart(getKeyRoot(editorState.mappingState.mappingMode))
   }
@@ -584,7 +585,7 @@ public class KeyHandler {
    */
   public fun reset(editor: VimEditor) {
     partialReset(editor)
-    val editorState = getInstance(editor)
+    val editorState = VimStateMachine.getInstance(editor)
     editorState.commandBuilder.resetAll(getKeyRoot(editorState.mappingState.mappingMode))
   }
 
@@ -600,7 +601,7 @@ public class KeyHandler {
    */
   public fun fullReset(editor: VimEditor) {
     injector.messages.clearError()
-    getInstance(editor).reset()
+    VimStateMachine.getInstance(editor).reset()
     reset(editor)
     injector.registerGroupIfCreated?.resetRegister()
     editor.removeSelection()
@@ -623,14 +624,14 @@ public class KeyHandler {
     val operatorArguments: OperatorArguments,
   ) : Runnable {
     override fun run() {
-      val editorState = getInstance(editor)
+      val editorState = VimStateMachine.getInstance(editor)
       editorState.commandBuilder.commandState = CurrentCommandState.NEW_COMMAND
       val register = cmd.register
       if (register != null) {
         injector.registerGroup.selectRegister(register)
       }
       injector.actionExecutor.executeVimAction(editor, cmd.action, context, operatorArguments)
-      if (editorState.mode === VimStateMachine.Mode.INSERT || editorState.mode === VimStateMachine.Mode.REPLACE) {
+      if (editorState.mode is Mode.INSERT || editorState.mode is Mode.REPLACE) {
         injector.changeGroup.processCommand(editor, cmd)
       }
 
@@ -644,10 +645,18 @@ public class KeyHandler {
       // mode we were in. This handles commands in those modes that temporarily allow us to execute normal
       // mode commands. An exception is if this command should leave us in the temporary mode such as
       // "select register"
-      if (editorState.mode.inSingleNormalMode &&
-        !cmd.flags.contains(CommandFlags.FLAG_EXPECT_MORE)
-      ) {
-        editorState.popModes()
+      val myMode = editorState.mode
+      val returnTo = myMode.returnTo
+      if (myMode is Mode.NORMAL && returnTo != null && !cmd.flags.contains(CommandFlags.FLAG_EXPECT_MORE)) {
+        when (returnTo) {
+          ReturnTo.INSERT -> {
+            editorState.mode = Mode.INSERT
+          }
+
+          ReturnTo.REPLACE -> {
+            editorState.mode = Mode.REPLACE
+          }
+        }
       }
       if (editorState.commandBuilder.isDone()) {
         getInstance().reset(editor)
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/ResetModeAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/ResetModeAction.kt
index 5a444793f..82b11f993 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/ResetModeAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/ResetModeAction.kt
@@ -14,13 +14,13 @@ import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.moveToMotion
 import com.maddyhome.idea.vim.command.Command
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.handler.VimActionHandler
-import com.maddyhome.idea.vim.helper.mode
+import com.maddyhome.idea.vim.state.mode.mode
 
 public class ResetModeAction : VimActionHandler.ConditionalMulticaret() {
-  private lateinit var modeBeforeReset: VimStateMachine.Mode
+  private lateinit var modeBeforeReset: Mode
   override val type: Command.Type = Command.Type.OTHER_WRITABLE
   override fun runAsMulticaret(
     editor: VimEditor,
@@ -40,7 +40,7 @@ public class ResetModeAction : VimActionHandler.ConditionalMulticaret() {
     cmd: Command,
     operatorArguments: OperatorArguments,
   ): Boolean {
-    if (modeBeforeReset == VimStateMachine.Mode.INSERT) {
+    if (modeBeforeReset == Mode.INSERT) {
       val position = injector.motion.getHorizontalMotion(editor, caret, -1, false)
       caret.moveToMotion(position)
     }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeReplaceAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeReplaceAction.kt
index e7c66ae77..697754b4e 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeReplaceAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeReplaceAction.kt
@@ -13,8 +13,8 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
 import com.maddyhome.idea.vim.helper.enumSetOf
 import java.util.*
@@ -41,5 +41,5 @@ public class ChangeReplaceAction : ChangeEditorActionHandler.SingleExecution() {
  * @param context The data context
  */
 private fun changeReplace(editor: VimEditor, context: ExecutionContext) {
-  injector.changeGroup.initInsert(editor, context, VimStateMachine.Mode.REPLACE)
+  injector.changeGroup.initInsert(editor, context, Mode.REPLACE)
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeVisualLinesAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeVisualLinesAction.kt
index a2412ad23..1a3a99df1 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeVisualLinesAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeVisualLinesAction.kt
@@ -19,7 +19,7 @@ import com.maddyhome.idea.vim.command.CommandFlags.FLAG_EXIT_VISUAL
 import com.maddyhome.idea.vim.command.CommandFlags.FLAG_MOT_LINEWISE
 import com.maddyhome.idea.vim.command.CommandFlags.FLAG_MULTIKEY_UNDO
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.group.visual.VimSelection
 import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeVisualLinesEndAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeVisualLinesEndAction.kt
index f711bdba0..2ac72be76 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeVisualLinesEndAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/change/ChangeVisualLinesEndAction.kt
@@ -19,7 +19,7 @@ import com.maddyhome.idea.vim.command.CommandFlags.FLAG_EXIT_VISUAL
 import com.maddyhome.idea.vim.command.CommandFlags.FLAG_MOT_LINEWISE
 import com.maddyhome.idea.vim.command.CommandFlags.FLAG_MULTIKEY_UNDO
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.group.visual.VimSelection
 import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/delete/DeleteVisualLinesAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/delete/DeleteVisualLinesAction.kt
index 55fa07eb1..8baf46926 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/delete/DeleteVisualLinesAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/delete/DeleteVisualLinesAction.kt
@@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.group.visual.VimSelection
 import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/delete/DeleteVisualLinesEndAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/delete/DeleteVisualLinesEndAction.kt
index d349d3625..52070216f 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/delete/DeleteVisualLinesEndAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/delete/DeleteVisualLinesEndAction.kt
@@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.group.visual.VimSelection
 import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertDeleteInsertedTextAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertDeleteInsertedTextAction.kt
index f6a278a6c..24e932169 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertDeleteInsertedTextAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertDeleteInsertedTextAction.kt
@@ -15,7 +15,7 @@ import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.Offset
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertDeletePreviousWordAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertDeletePreviousWordAction.kt
index db092d236..eaf423a75 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertDeletePreviousWordAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertDeletePreviousWordAction.kt
@@ -15,7 +15,7 @@ import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
 import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertNewLineBelowAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertNewLineBelowAction.kt
index 8c56c74e7..300ce7f6f 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertNewLineBelowAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertNewLineBelowAction.kt
@@ -14,8 +14,8 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.common.Offset
 import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
 import com.maddyhome.idea.vim.helper.enumSetOf
@@ -93,7 +93,7 @@ private fun insertNewLineAbove(editor: VimEditor, context: ExecutionContext) {
     for ((first, second) in moves) {
       first.moveToOffsetNative(second)
     }
-    injector.changeGroup.initInsert(editor, context, VimStateMachine.Mode.INSERT)
+    injector.changeGroup.initInsert(editor, context, Mode.INSERT)
     injector.changeGroup.runEnterAction(editor, context)
     for (caret in editor.nativeCarets()) {
       if (firstLiners.contains(caret)) {
@@ -102,7 +102,7 @@ private fun insertNewLineAbove(editor: VimEditor, context: ExecutionContext) {
       }
     }
   } else {
-    injector.changeGroup.initInsert(editor, context, VimStateMachine.Mode.INSERT)
+    injector.changeGroup.initInsert(editor, context, Mode.INSERT)
     injector.changeGroup.runEnterAboveAction(editor, context)
   }
   injector.scroll.scrollCaretIntoView(editor)
@@ -120,7 +120,7 @@ private fun insertNewLineBelow(editor: VimEditor, context: ExecutionContext) {
     caret.moveToOffset(injector.motion.moveCaretToCurrentLineEnd(editor, caret))
   }
 
-  injector.changeGroup.initInsert(editor, context, VimStateMachine.Mode.INSERT)
+  injector.changeGroup.initInsert(editor, context, Mode.INSERT)
   injector.changeGroup.runEnterAction(editor, context)
   injector.scroll.scrollCaretIntoView(editor)
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertRegisterAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertRegisterAction.kt
index b746e2f8f..ca0966be7 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertRegisterAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/InsertRegisterAction.kt
@@ -13,7 +13,7 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.ex.ExException
 import com.maddyhome.idea.vim.handler.VimActionHandler
 import com.maddyhome.idea.vim.helper.RWLockLabel
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/VisualBlockAppendAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/VisualBlockAppendAction.kt
index fac3b060e..e8168a5e1 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/VisualBlockAppendAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/VisualBlockAppendAction.kt
@@ -14,7 +14,7 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.group.visual.VimSelection
 import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
 import com.maddyhome.idea.vim.helper.enumSetOf
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/VisualBlockInsertAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/VisualBlockInsertAction.kt
index bec6ad9fb..b968a3414 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/VisualBlockInsertAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/change/insert/VisualBlockInsertAction.kt
@@ -14,7 +14,7 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.group.visual.VimSelection
 import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
 import com.maddyhome.idea.vim.helper.enumSetOf
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/copy/YankVisualLinesAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/copy/YankVisualLinesAction.kt
index 653e49f7e..ffd719655 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/copy/YankVisualLinesAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/copy/YankVisualLinesAction.kt
@@ -14,7 +14,7 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.group.visual.VimSelection
 import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/gn/VisualSelectSearch.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/gn/VisualSelectSearch.kt
index 61c192692..904721d95 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/gn/VisualSelectSearch.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/gn/VisualSelectSearch.kt
@@ -14,11 +14,11 @@ import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.MotionType
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.handler.Motion
 import com.maddyhome.idea.vim.handler.MotionActionHandler
 import com.maddyhome.idea.vim.handler.toMotionOrError
-import com.maddyhome.idea.vim.helper.inVisualMode
+import com.maddyhome.idea.vim.state.mode.inVisualMode
 import com.maddyhome.idea.vim.helper.noneOfEnum
 import java.util.*
 import kotlin.math.max
@@ -60,7 +60,7 @@ private fun selectNextSearch(editor: VimEditor, count: Int, forwards: Boolean):
   if (!editor.inVisualMode) {
     val startOffset = if (forwards) range.startOffset else max(range.endOffset - adj, 0)
     caret.moveToOffset(startOffset)
-    injector.visualMotionGroup.enterVisualMode(editor, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    injector.visualMotionGroup.enterVisualMode(editor, SelectionType.CHARACTER_WISE)
   }
   return if (forwards) max(range.endOffset - adj, 0) else range.startOffset
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionEndAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionEndAction.kt
index e2a7d8334..6a8ff87c6 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionEndAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionEndAction.kt
@@ -19,9 +19,9 @@ import com.maddyhome.idea.vim.command.MotionType
 import com.maddyhome.idea.vim.command.OperatorArguments
 import com.maddyhome.idea.vim.handler.Motion
 import com.maddyhome.idea.vim.handler.NonShiftedSpecialKeyHandler
-import com.maddyhome.idea.vim.helper.inInsertMode
-import com.maddyhome.idea.vim.helper.inSelectMode
-import com.maddyhome.idea.vim.helper.inVisualMode
+import com.maddyhome.idea.vim.state.mode.isInsertionAllowed
+import com.maddyhome.idea.vim.state.mode.inSelectMode
+import com.maddyhome.idea.vim.state.mode.inVisualMode
 
 public class MotionEndAction : NonShiftedSpecialKeyHandler() {
   override val motionType: MotionType = MotionType.INCLUSIVE
@@ -34,7 +34,7 @@ public class MotionEndAction : NonShiftedSpecialKeyHandler() {
     operatorArguments: OperatorArguments,
   ): Motion {
     var allow = false
-    if (editor.inInsertMode) {
+    if (editor.isInsertionAllowed) {
       allow = true
     } else if (editor.inVisualMode || editor.inSelectMode) {
       allow = injector.options(editor).selection != "old"
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionLastColumnAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionLastColumnAction.kt
index 3caaac53e..cc8fc1e17 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionLastColumnAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionLastColumnAction.kt
@@ -21,7 +21,7 @@ import com.maddyhome.idea.vim.command.OperatorArguments
 import com.maddyhome.idea.vim.handler.Motion
 import com.maddyhome.idea.vim.handler.MotionActionHandler
 import com.maddyhome.idea.vim.helper.enumSetOf
-import com.maddyhome.idea.vim.helper.inVisualMode
+import com.maddyhome.idea.vim.state.mode.inVisualMode
 import com.maddyhome.idea.vim.helper.isEndAllowed
 import java.util.*
 
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionLastScreenColumnAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionLastScreenColumnAction.kt
index 9102ba4c7..896ff17da 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionLastScreenColumnAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionLastScreenColumnAction.kt
@@ -18,8 +18,8 @@ import com.maddyhome.idea.vim.command.MotionType
 import com.maddyhome.idea.vim.command.OperatorArguments
 import com.maddyhome.idea.vim.handler.Motion
 import com.maddyhome.idea.vim.handler.MotionActionHandler
-import com.maddyhome.idea.vim.helper.inInsertMode
-import com.maddyhome.idea.vim.helper.inVisualMode
+import com.maddyhome.idea.vim.state.mode.isInsertionAllowed
+import com.maddyhome.idea.vim.state.mode.inVisualMode
 
 public class MotionLastScreenColumnAction : MotionActionHandler.ForEachCaret() {
   override fun getOffset(
@@ -30,7 +30,7 @@ public class MotionLastScreenColumnAction : MotionActionHandler.ForEachCaret() {
     operatorArguments: OperatorArguments,
   ): Motion {
     var allow = false
-    if (editor.inInsertMode) {
+    if (editor.isInsertionAllowed) {
       allow = true
     } else if (editor.inVisualMode) {
       allow = injector.options(editor).selection != "old"
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionShiftEndAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionShiftEndAction.kt
index 23928e79b..4797b464c 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionShiftEndAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/leftright/MotionShiftEndAction.kt
@@ -16,9 +16,9 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.options
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.handler.ShiftedSpecialKeyHandler
-import com.maddyhome.idea.vim.helper.inInsertMode
-import com.maddyhome.idea.vim.helper.inSelectMode
-import com.maddyhome.idea.vim.helper.inVisualMode
+import com.maddyhome.idea.vim.state.mode.isInsertionAllowed
+import com.maddyhome.idea.vim.state.mode.inSelectMode
+import com.maddyhome.idea.vim.state.mode.inVisualMode
 
 public class MotionShiftEndAction : ShiftedSpecialKeyHandler() {
 
@@ -26,7 +26,7 @@ public class MotionShiftEndAction : ShiftedSpecialKeyHandler() {
 
   override fun motion(editor: VimEditor, context: ExecutionContext, cmd: Command, caret: VimCaret) {
     var allow = false
-    if (editor.inInsertMode) {
+    if (editor.isInsertionAllowed) {
       allow = true
     } else if (editor.inVisualMode || editor.inSelectMode) {
       allow = injector.options(editor).selection != "old"
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/object/MotionWordAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/object/MotionWordAction.kt
index 3d2257155..8c2a133c5 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/object/MotionWordAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/object/MotionWordAction.kt
@@ -12,11 +12,11 @@ import com.maddyhome.idea.vim.api.ExecutionContext
 import com.maddyhome.idea.vim.api.ImmutableVimCaret
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.injector
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.TextObjectVisualType
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.handler.TextObjectActionHandler
-import com.maddyhome.idea.vim.helper.mode
+import com.maddyhome.idea.vim.state.mode.mode
 
 public class MotionInnerBigWordAction : TextObjectActionHandler() {
 
@@ -87,7 +87,7 @@ private fun getWordRange(
 ): TextRange {
   var dir = 1
   var selection = false
-  if (editor.mode == VimStateMachine.Mode.VISUAL) {
+  if (editor.mode is Mode.VISUAL) {
     if (caret.vimSelectionStart > caret.offset.point) {
       dir = -1
     }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEnableBlockModeAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEnableBlockModeAction.kt
index 7870ae1c0..7435f8c2c 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEnableBlockModeAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEnableBlockModeAction.kt
@@ -14,7 +14,7 @@ import com.maddyhome.idea.vim.api.getLineEndForOffset
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
 import com.maddyhome.idea.vim.handler.VimActionHandler
 
@@ -38,6 +38,6 @@ public class SelectEnableBlockModeAction : VimActionHandler.SingleExecution() {
       vimSetSystemSelectionSilently(offset.point, (offset.point + 1).coerceAtMost(lineEnd))
       moveToInlayAwareOffset((offset.point + 1).coerceAtMost(lineEnd))
     }
-    return injector.visualMotionGroup.enterSelectMode(editor, VimStateMachine.SubMode.VISUAL_BLOCK)
+    return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.BLOCK_WISE)
   }
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEnableCharacterModeAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEnableCharacterModeAction.kt
index 9e5e328da..2ef4ab393 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEnableCharacterModeAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEnableCharacterModeAction.kt
@@ -14,7 +14,7 @@ import com.maddyhome.idea.vim.api.getLineEndForOffset
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
 import com.maddyhome.idea.vim.handler.VimActionHandler
 
@@ -39,6 +39,6 @@ public class SelectEnableCharacterModeAction : VimActionHandler.SingleExecution(
         moveToInlayAwareOffset((offset.point + 1).coerceAtMost(lineEnd))
       }
     }
-    return injector.visualMotionGroup.enterSelectMode(editor, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.CHARACTER_WISE)
   }
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEnableLineModeAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEnableLineModeAction.kt
index 25c6e8481..158266589 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEnableLineModeAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEnableLineModeAction.kt
@@ -15,7 +15,7 @@ import com.maddyhome.idea.vim.api.getLineStartForOffset
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
 import com.maddyhome.idea.vim.handler.VimActionHandler
 
@@ -38,6 +38,6 @@ public class SelectEnableLineModeAction : VimActionHandler.SingleExecution() {
       val lineStart = editor.getLineStartForOffset(caret.offset.point)
       caret.vimSetSystemSelectionSilently(lineStart, lineEnd)
     }
-    return injector.visualMotionGroup.enterSelectMode(editor, VimStateMachine.SubMode.VISUAL_LINE)
+    return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.LINE_WISE)
   }
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEscapeAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEscapeAction.kt
index 592df09af..fee612013 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEscapeAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectEscapeAction.kt
@@ -13,7 +13,7 @@ import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.OperatorArguments
 import com.maddyhome.idea.vim.handler.VimActionHandler
-import com.maddyhome.idea.vim.helper.inBlockSubMode
+import com.maddyhome.idea.vim.state.mode.inBlockSelection
 
 public class SelectEscapeAction : VimActionHandler.SingleExecution() {
 
@@ -25,7 +25,7 @@ public class SelectEscapeAction : VimActionHandler.SingleExecution() {
     cmd: Command,
     operatorArguments: OperatorArguments,
   ): Boolean {
-    val blockMode = editor.inBlockSubMode
+    val blockMode = editor.inBlockSelection
     editor.exitSelectModeNative(true)
     if (blockMode) editor.removeSecondaryCarets()
     return true
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectToggleVisualMode.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectToggleVisualMode.kt
index 95d65a362..317cf9b24 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectToggleVisualMode.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/select/SelectToggleVisualMode.kt
@@ -13,12 +13,12 @@ import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.handler.VimActionHandler
-import com.maddyhome.idea.vim.helper.inVisualMode
-import com.maddyhome.idea.vim.helper.pushSelectMode
 import com.maddyhome.idea.vim.helper.pushVisualMode
+import com.maddyhome.idea.vim.helper.setSelectMode
 import com.maddyhome.idea.vim.helper.vimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
 
 /**
  * @author Alex Plate
@@ -41,21 +41,19 @@ public class SelectToggleVisualMode : VimActionHandler.SingleExecution() {
   public companion object {
     public fun toggleMode(editor: VimEditor) {
       val commandState = editor.vimStateMachine
-      val subMode = commandState.subMode
-      val mode = commandState.mode
-      commandState.popModes()
-      if (mode.inVisualMode) {
-        commandState.pushSelectMode(subMode, mode)
-        if (subMode != VimStateMachine.SubMode.VISUAL_LINE) {
+      val myMode = commandState.mode
+      if (myMode is Mode.VISUAL) {
+        commandState.setSelectMode(myMode.selectionType)
+        if (myMode.selectionType != SelectionType.LINE_WISE) {
           editor.nativeCarets().forEach {
             if (it.offset.point + injector.visualMotionGroup.selectionAdj == it.selectionEnd) {
               it.moveToInlayAwareOffset(it.offset.point + injector.visualMotionGroup.selectionAdj)
             }
           }
         }
-      } else {
-        commandState.pushVisualMode(subMode, mode)
-        if (subMode != VimStateMachine.SubMode.VISUAL_LINE) {
+      } else if (myMode is Mode.SELECT) {
+        commandState.pushVisualMode(myMode.selectionType)
+        if (myMode.selectionType != SelectionType.LINE_WISE) {
           editor.nativeCarets().forEach {
             if (it.offset.point == it.selectionEnd && it.visualLineStart <= it.offset.point - injector.visualMotionGroup.selectionAdj) {
               it.moveToInlayAwareOffset(it.offset.point - injector.visualMotionGroup.selectionAdj)
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/updown/MotionGotoLineLastEndAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/updown/MotionGotoLineLastEndAction.kt
index 8dd5a4980..2f81a2725 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/updown/MotionGotoLineLastEndAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/updown/MotionGotoLineLastEndAction.kt
@@ -22,8 +22,8 @@ import com.maddyhome.idea.vim.handler.Motion
 import com.maddyhome.idea.vim.handler.MotionActionHandler
 import com.maddyhome.idea.vim.handler.toMotion
 import com.maddyhome.idea.vim.helper.enumSetOf
-import com.maddyhome.idea.vim.helper.inInsertMode
-import com.maddyhome.idea.vim.helper.inVisualMode
+import com.maddyhome.idea.vim.state.mode.isInsertionAllowed
+import com.maddyhome.idea.vim.state.mode.inVisualMode
 import java.util.*
 
 public class MotionGotoLineLastEndAction : MotionActionHandler.ForEachCaret() {
@@ -39,7 +39,7 @@ public class MotionGotoLineLastEndAction : MotionActionHandler.ForEachCaret() {
     operatorArguments: OperatorArguments,
   ): Motion {
     var allow = false
-    if (editor.inInsertMode) {
+    if (editor.isInsertionAllowed) {
       allow = true
     } else if (editor.inVisualMode) {
       allow = injector.options(editor).selection != "old"
@@ -62,7 +62,7 @@ public class MotionGotoLineLastEndInsertAction : MotionActionHandler.ForEachCare
     operatorArguments: OperatorArguments,
   ): Motion {
     var allow = false
-    if (editor.inInsertMode) {
+    if (editor.isInsertionAllowed) {
       allow = true
     } else if (editor.inVisualMode) {
       allow = injector.options(editor).selection != "old"
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualSelectPreviousAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualSelectPreviousAction.kt
index f1d097e7a..1d9fda3a1 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualSelectPreviousAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualSelectPreviousAction.kt
@@ -11,8 +11,8 @@ import com.maddyhome.idea.vim.api.ExecutionContext
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Command
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.group.visual.vimSetSelection
 import com.maddyhome.idea.vim.handler.VimActionHandler
 import com.maddyhome.idea.vim.helper.vimStateMachine
@@ -35,7 +35,7 @@ public class VisualSelectPreviousAction : VimActionHandler.SingleExecution() {
 
     if (caretToSelectionInfo.any { it.second.start == null || it.second.end == null }) return false
 
-    editor.vimStateMachine.pushModes(VimStateMachine.Mode.VISUAL, selectionType.toSubMode())
+    editor.vimStateMachine.mode = Mode.VISUAL(selectionType)
 
     for ((caret, selectionInfo) in caretToSelectionInfo) {
       val startOffset = editor.bufferPositionToOffset(selectionInfo.start!!)
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualSwapEndsAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualSwapEndsAction.kt
index 5c9cef224..773cc4754 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualSwapEndsAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualSwapEndsAction.kt
@@ -15,7 +15,7 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.OperatorArguments
 import com.maddyhome.idea.vim.handler.VimActionHandler
-import com.maddyhome.idea.vim.helper.inBlockSubMode
+import com.maddyhome.idea.vim.state.mode.inBlockSelection
 
 /**
  * @author vlan
@@ -46,7 +46,7 @@ public class VisualSwapEndsBlockAction : VimActionHandler.SingleExecution() {
     cmd: Command,
     operatorArguments: OperatorArguments,
   ): Boolean {
-    if (editor.inBlockSubMode) {
+    if (editor.inBlockSelection) {
       return swapVisualEndsBigO(editor)
     }
 
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualSwapSelectionsAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualSwapSelectionsAction.kt
index 5eb485342..173a48e04 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualSwapSelectionsAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualSwapSelectionsAction.kt
@@ -13,11 +13,12 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.setVisualSelectionMarks
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.group.visual.vimSetSelection
 import com.maddyhome.idea.vim.handler.VimActionHandler
-import com.maddyhome.idea.vim.helper.subMode
+import com.maddyhome.idea.vim.helper.vimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.mode
 
 /**
  * @author vlan
@@ -37,6 +38,9 @@ public class VisualSwapSelectionsAction : VimActionHandler.SingleExecution() {
 }
 
 private fun swapVisualSelections(editor: VimEditor): Boolean {
+  val mode = editor.mode
+  check(mode is Mode.VISUAL)
+
   val lastSelectionType = editor.vimLastSelectionType ?: return false
 
   val primaryCaret = editor.primaryCaret()
@@ -44,10 +48,10 @@ private fun swapVisualSelections(editor: VimEditor): Boolean {
   editor.removeSecondaryCarets()
   val vimSelectionStart = primaryCaret.vimSelectionStart
 
-  editor.vimLastSelectionType = SelectionType.fromSubMode(editor.subMode)
+  editor.vimLastSelectionType = mode.selectionType
   injector.markService.setVisualSelectionMarks(primaryCaret, TextRange(vimSelectionStart, primaryCaret.offset.point))
 
-  editor.subMode = lastSelectionType.toSubMode()
+  editor.vimStateMachine.mode = mode.copy(selectionType = lastSelectionType)
   primaryCaret.vimSetSelection(lastVisualRange.startOffset, lastVisualRange.endOffset, true)
 
   injector.scroll.scrollCaretIntoView(editor)
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualToggleBlockModeAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualToggleBlockModeAction.kt
index 6f01f980d..526d14a6c 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualToggleBlockModeAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualToggleBlockModeAction.kt
@@ -13,7 +13,7 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.options
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.handler.VimActionHandler
 import com.maddyhome.idea.vim.options.OptionConstants
 
@@ -27,10 +27,10 @@ public class VisualToggleBlockModeAction : VimActionHandler.SingleExecution() {
     operatorArguments: OperatorArguments,
   ): Boolean {
     return if (injector.options(editor).selectmode.contains(OptionConstants.selectmode_cmd)) {
-      injector.visualMotionGroup.enterSelectMode(editor, VimStateMachine.SubMode.VISUAL_BLOCK)
+      injector.visualMotionGroup.enterSelectMode(editor, SelectionType.BLOCK_WISE)
     } else {
       injector.visualMotionGroup
-        .toggleVisual(editor, cmd.count, cmd.rawCount, VimStateMachine.SubMode.VISUAL_BLOCK)
+        .toggleVisual(editor, cmd.count, cmd.rawCount, SelectionType.BLOCK_WISE)
     }
   }
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualToggleCharacterModeAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualToggleCharacterModeAction.kt
index 1fbb0d564..a748ba7c5 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualToggleCharacterModeAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualToggleCharacterModeAction.kt
@@ -13,7 +13,7 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.options
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.handler.VimActionHandler
 import com.maddyhome.idea.vim.options.OptionConstants
 
@@ -27,10 +27,10 @@ public class VisualToggleCharacterModeAction : VimActionHandler.SingleExecution(
     operatorArguments: OperatorArguments,
   ): Boolean {
     return if (injector.options(editor).selectmode.contains(OptionConstants.selectmode_cmd)) {
-      injector.visualMotionGroup.enterSelectMode(editor, VimStateMachine.SubMode.VISUAL_CHARACTER)
+      injector.visualMotionGroup.enterSelectMode(editor, SelectionType.CHARACTER_WISE)
     } else {
       injector.visualMotionGroup
-        .toggleVisual(editor, cmd.count, cmd.rawCount, VimStateMachine.SubMode.VISUAL_CHARACTER)
+        .toggleVisual(editor, cmd.count, cmd.rawCount, SelectionType.CHARACTER_WISE)
     }
   }
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualToggleLineModeAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualToggleLineModeAction.kt
index b46c312a3..e69045a62 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualToggleLineModeAction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/visual/VisualToggleLineModeAction.kt
@@ -15,7 +15,7 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.options
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.group.visual.vimSetSelection
 import com.maddyhome.idea.vim.handler.VimActionHandler
 import com.maddyhome.idea.vim.options.OptionConstants
@@ -30,7 +30,7 @@ public class VisualToggleLineModeAction : VimActionHandler.ConditionalMulticaret
     operatorArguments: OperatorArguments,
   ): Boolean {
     return if (injector.options(editor).selectmode.contains(OptionConstants.selectmode_cmd)) {
-      injector.visualMotionGroup.enterSelectMode(editor, VimStateMachine.SubMode.VISUAL_LINE)
+      injector.visualMotionGroup.enterSelectMode(editor, SelectionType.LINE_WISE)
       true
     } else {
       false
@@ -55,6 +55,6 @@ public class VisualToggleLineModeAction : VimActionHandler.ConditionalMulticaret
     operatorArguments: OperatorArguments,
   ): Boolean {
     return injector.visualMotionGroup
-      .toggleVisual(editor, cmd.count, cmd.rawCount, VimStateMachine.SubMode.VISUAL_LINE)
+      .toggleVisual(editor, cmd.count, cmd.rawCount, SelectionType.LINE_WISE)
   }
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimCaret.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimCaret.kt
index e3670a800..2c728aeb1 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimCaret.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimCaret.kt
@@ -8,7 +8,7 @@
 
 package com.maddyhome.idea.vim.api
 
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.EditorLine
 import com.maddyhome.idea.vim.common.LiveRange
 import com.maddyhome.idea.vim.common.Offset
@@ -19,9 +19,9 @@ import com.maddyhome.idea.vim.group.visual.vimMoveSelectionToCaret
 import com.maddyhome.idea.vim.handler.Motion
 import com.maddyhome.idea.vim.helper.StrictMode
 import com.maddyhome.idea.vim.helper.exitVisualMode
-import com.maddyhome.idea.vim.helper.inBlockSubMode
-import com.maddyhome.idea.vim.helper.inSelectMode
-import com.maddyhome.idea.vim.helper.inVisualMode
+import com.maddyhome.idea.vim.state.mode.inBlockSelection
+import com.maddyhome.idea.vim.state.mode.inSelectMode
+import com.maddyhome.idea.vim.state.mode.inVisualMode
 import com.maddyhome.idea.vim.register.Register
 import javax.swing.KeyStroke
 
@@ -83,7 +83,7 @@ per-caret marks.
 
   public fun moveToOffset(offset: Int): VimCaret {
     if (offset < 0 || offset > editor.text().length || !isValid) return this
-    if (editor.inBlockSubMode) {
+    if (editor.inBlockSelection) {
       StrictMode.assert(this == editor.primaryCaret(), "Block selection can only be moved with primary caret!")
 
       // Note that this call replaces ALL carets, so any local caret instances will be invalid!
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimCaretBase.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimCaretBase.kt
index 690631a3b..5d4c7352f 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimCaretBase.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimCaretBase.kt
@@ -8,7 +8,7 @@
 
 package com.maddyhome.idea.vim.api
 
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.register.Register
 import com.maddyhome.idea.vim.register.RegisterConstants
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimChangeGroup.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimChangeGroup.kt
index de4cfa79f..296bd3d0b 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimChangeGroup.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimChangeGroup.kt
@@ -9,9 +9,9 @@ package com.maddyhome.idea.vim.api
 
 import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.Command
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.ex.ranges.LineRange
 import com.maddyhome.idea.vim.group.visual.VimSelection
@@ -34,13 +34,13 @@ public interface VimChangeGroup {
 
   public fun insertPreviousInsert(editor: VimEditor, context: ExecutionContext, exit: Boolean, operatorArguments: OperatorArguments)
 
-  public fun initInsert(editor: VimEditor, context: ExecutionContext, mode: VimStateMachine.Mode)
+  public fun initInsert(editor: VimEditor, context: ExecutionContext, mode: Mode)
 
   public fun processEscape(editor: VimEditor, context: ExecutionContext?, operatorArguments: OperatorArguments)
 
   public fun processEnter(editor: VimEditor, context: ExecutionContext)
 
-  public fun processPostChangeModeSwitch(editor: VimEditor, context: ExecutionContext, toSwitch: VimStateMachine.Mode)
+  public fun processPostChangeModeSwitch(editor: VimEditor, context: ExecutionContext, toSwitch: Mode)
 
   public fun processCommand(editor: VimEditor, cmd: Command)
 
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimChangeGroupBase.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimChangeGroupBase.kt
index d2fedd714..5df4c5e3d 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimChangeGroupBase.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimChangeGroupBase.kt
@@ -13,9 +13,7 @@ import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
-import com.maddyhome.idea.vim.command.VimStateMachine.Companion.getInstance
+import com.maddyhome.idea.vim.state.VimStateMachine.Companion.getInstance
 import com.maddyhome.idea.vim.common.ChangesListener
 import com.maddyhome.idea.vim.common.Offset
 import com.maddyhome.idea.vim.common.OperatedRange
@@ -28,8 +26,6 @@ import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
 import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
 import com.maddyhome.idea.vim.helper.CharacterHelper
 import com.maddyhome.idea.vim.helper.CharacterHelper.charType
-import com.maddyhome.idea.vim.helper.inInsertMode
-import com.maddyhome.idea.vim.helper.inSingleCommandMode
 import com.maddyhome.idea.vim.helper.usesVirtualSpace
 import com.maddyhome.idea.vim.helper.vimStateMachine
 import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
@@ -37,6 +33,9 @@ import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_END
 import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_POS
 import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_START
 import com.maddyhome.idea.vim.register.RegisterConstants.LAST_INSERTED_TEXT_REGISTER
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.ReturnTo
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import org.jetbrains.annotations.NonNls
 import java.awt.event.KeyEvent
 import java.util.*
@@ -169,15 +168,20 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
         return false
       }
     }
+    val mode = operatorArguments.mode
     if (type == null ||
-      operatorArguments.mode.inInsertMode ||
+      (mode == Mode.INSERT || mode == Mode.REPLACE) ||
       !saveToRegister ||
       caret.registerStorage.storeText(editor, updatedRange, type, true)
     ) {
       val startOffsets = updatedRange.startOffsets
       val endOffsets = updatedRange.endOffsets
       for (i in updatedRange.size() - 1 downTo 0) {
-        val (newRange, _) = editor.search(startOffsets[i].offset to endOffsets[i].offset, editor, LineDeleteShift.NL_ON_END) ?: continue
+        val (newRange, _) = editor.search(
+          startOffsets[i].offset to endOffsets[i].offset,
+          editor,
+          LineDeleteShift.NL_ON_END
+        ) ?: continue
         editor.deleteString(TextRange(newRange.first.point, newRange.second.point))
       }
       if (type != null) {
@@ -369,14 +373,14 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
    * @param context The data context
    */
   override fun insertBeforeCursor(editor: VimEditor, context: ExecutionContext) {
-    initInsert(editor, context, VimStateMachine.Mode.INSERT)
+    initInsert(editor, context, Mode.INSERT)
   }
 
   override fun insertAfterLineEnd(editor: VimEditor, context: ExecutionContext) {
     for (caret in editor.nativeCarets()) {
       caret.moveToOffset(injector.motion.moveCaretToCurrentLineEnd(editor, caret))
     }
-    initInsert(editor, context, VimStateMachine.Mode.INSERT)
+    initInsert(editor, context, Mode.INSERT)
   }
 
   /**
@@ -388,7 +392,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
     for (caret in editor.nativeCarets()) {
       caret.moveToMotion(injector.motion.getHorizontalMotion(editor, caret, 1, true))
     }
-    initInsert(editor, context, VimStateMachine.Mode.INSERT)
+    initInsert(editor, context, Mode.INSERT)
   }
 
   /**
@@ -400,7 +404,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
     for (caret in editor.nativeCarets()) {
       caret.moveToOffset(injector.motion.moveCaretToCurrentLineStart(editor, caret))
     }
-    initInsert(editor, context, VimStateMachine.Mode.INSERT)
+    initInsert(editor, context, Mode.INSERT)
   }
 
   /**
@@ -412,7 +416,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
     for (caret in editor.nativeCarets()) {
       caret.moveToOffset(injector.motion.moveCaretToCurrentLineStartSkipLeading(editor, caret))
     }
-    initInsert(editor, context, VimStateMachine.Mode.INSERT)
+    initInsert(editor, context, Mode.INSERT)
   }
 
   /**
@@ -421,7 +425,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
    * @param context The data context
    * @param mode    The mode - indicate insert or replace
    */
-  override fun initInsert(editor: VimEditor, context: ExecutionContext, mode: VimStateMachine.Mode) {
+  override fun initInsert(editor: VimEditor, context: ExecutionContext, mode: Mode) {
     val state = getInstance(editor)
     for (caret in editor.nativeCarets()) {
       caret.vimInsertStart = editor.createLiveMarker(caret.offset, caret.offset)
@@ -429,8 +433,8 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
     }
     val cmd = state.executingCommand
     if (cmd != null && state.isDotRepeatInProgress) {
-      state.pushModes(mode, VimStateMachine.SubMode.NONE)
-      if (mode === VimStateMachine.Mode.REPLACE) {
+      state.mode = mode
+      if (mode == Mode.REPLACE) {
         editor.insertMode = false
       }
       if (cmd.flags.contains(CommandFlags.FLAG_NO_REPEAT_INSERT)) {
@@ -440,7 +444,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
           context,
           1,
           false,
-          OperatorArguments(false, 1, commandState.mode, commandState.subMode),
+          OperatorArguments(false, 1, commandState.mode),
         )
       } else {
         val commandState = getInstance(editor)
@@ -449,13 +453,13 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
           context,
           cmd.count,
           false,
-          OperatorArguments(false, cmd.count, commandState.mode, commandState.subMode),
+          OperatorArguments(false, cmd.count, commandState.mode),
         )
       }
-      if (mode === VimStateMachine.Mode.REPLACE) {
+      if (mode == Mode.REPLACE) {
         editor.insertMode = true
       }
-      state.popModes()
+      state.mode = Mode.NORMAL()
     } else {
       lastInsert = cmd
       strokes.clear()
@@ -469,8 +473,8 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
       vimDocumentListener = myChangeListener
       vimDocument!!.addChangeListener(myChangeListener)
       oldOffset = editor.currentCaret().offset.point
-      editor.insertMode = mode === VimStateMachine.Mode.INSERT
-      state.pushModes(mode, VimStateMachine.SubMode.NONE)
+      editor.insertMode = mode == Mode.INSERT
+      state.mode = mode
     }
     notifyListeners(editor)
   }
@@ -529,7 +533,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
     val markGroup = injector.markService
     markGroup.setMark(editor, VimMarkService.INSERT_EXIT_MARK)
     markGroup.setMark(editor, MARK_CHANGE_END)
-    if (getInstance(editor).mode === VimStateMachine.Mode.REPLACE) {
+    if (getInstance(editor).mode is Mode.REPLACE) {
       editor.insertMode = true
     }
     var cnt = if (lastInsert != null) lastInsert!!.count else 0
@@ -544,14 +548,14 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
     if (context != null) {
       injector.changeGroup.repeatInsert(editor, context, if (cnt == 0) 0 else cnt - 1, true, operatorArguments)
     }
-    if (getInstance(editor).mode === VimStateMachine.Mode.INSERT) {
+    if (getInstance(editor).mode is Mode.INSERT) {
       updateLastInsertedTextRegister()
     }
 
     // The change pos '.' mark is the offset AFTER processing escape, and after switching to overtype
     markGroup.setMark(editor, MARK_CHANGE_POS)
-    getInstance(editor).popModes()
-    exitAllSingleCommandInsertModes(editor)
+    getInstance(editor).mode = Mode.NORMAL()
+    editor.vimStateMachine.mode = Mode.NORMAL()
   }
 
   private fun updateLastInsertedTextRegister() {
@@ -566,15 +570,6 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
     injector.registerGroup.storeTextSpecial(LAST_INSERTED_TEXT_REGISTER, textToPutRegister.toString())
   }
 
-  private fun exitAllSingleCommandInsertModes(editor: VimEditor) {
-    while (editor.inSingleCommandMode) {
-      editor.vimStateMachine.popModes()
-      if (editor.inInsertMode) {
-        editor.vimStateMachine.popModes()
-      }
-    }
-  }
-
   /**
    * Processes the Enter key by running the first successful action registered for "ENTER" keystroke.
    *
@@ -586,7 +581,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
    * @param context The data context
    */
   override fun processEnter(editor: VimEditor, context: ExecutionContext) {
-    if (editor.vimStateMachine.mode === VimStateMachine.Mode.REPLACE) {
+    if (editor.vimStateMachine.mode is Mode.REPLACE) {
       editor.insertMode = true
     }
     val enterKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)
@@ -596,7 +591,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
         break
       }
     }
-    if (editor.vimStateMachine.mode === VimStateMachine.Mode.REPLACE) {
+    if (editor.vimStateMachine.mode is Mode.REPLACE) {
       editor.insertMode = false
     }
   }
@@ -610,10 +605,10 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
   override fun processPostChangeModeSwitch(
     editor: VimEditor,
     context: ExecutionContext,
-    toSwitch: VimStateMachine.Mode,
+    toSwitch: Mode,
   ) {
-    if (toSwitch === VimStateMachine.Mode.INSERT) {
-      initInsert(editor, context, VimStateMachine.Mode.INSERT)
+    if (toSwitch == Mode.INSERT) {
+      initInsert(editor, context, Mode.INSERT)
     }
   }
 
@@ -640,7 +635,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
    * @param editor The editor to put into NORMAL mode for one command
    */
   override fun processSingleCommand(editor: VimEditor) {
-    getInstance(editor).pushModes(VimStateMachine.Mode.INSERT_NORMAL, VimStateMachine.SubMode.NONE)
+    getInstance(editor).mode = Mode.NORMAL(returnTo = ReturnTo.INSERT)
     clearStrokes(editor)
   }
 
@@ -972,7 +967,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
     val res = deleteEndOfLine(editor, caret, count, operatorArguments)
     if (res) {
       caret.moveToOffset(injector.motion.moveCaretToCurrentLineEnd(editor, caret))
-      editor.vimChangeActionSwitchMode = VimStateMachine.Mode.INSERT
+      editor.vimChangeActionSwitchMode = Mode.INSERT
     }
     return res
   }
@@ -994,7 +989,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
     }
     val res = deleteCharacter(editor, caret, count, true, operatorArguments)
     if (res) {
-      editor.vimChangeActionSwitchMode = VimStateMachine.Mode.INSERT
+      editor.vimChangeActionSwitchMode = Mode.INSERT
     }
     return res
   }
@@ -1113,7 +1108,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
       val updated = caret.moveToOffset((motion as AbsoluteOffset).offset)
       updated.moveToOffset(injector.motion.moveCaretToCurrentLineEnd(editor, updated))
     }
-    editor.vimChangeActionSwitchMode = VimStateMachine.Mode.INSERT
+    editor.vimChangeActionSwitchMode = Mode.INSERT
     newCaret = insertText(
       editor,
       newCaret,
@@ -1150,7 +1145,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
         if (wordMotions.contains(id) && lastWordChar && motion.count == 1) {
           val res = deleteCharacter(editor, caret, 1, true, operatorArguments)
           if (res) {
-            editor.vimChangeActionSwitchMode = VimStateMachine.Mode.INSERT
+            editor.vimChangeActionSwitchMode = Mode.INSERT
           }
           return res
         }
@@ -1221,7 +1216,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
   private fun insertNewLineBelow(editor: VimEditor, caret: VimCaret, col: Int) {
     if (editor.isOneLineMode()) return
     val newCaret = caret.moveToOffset(injector.motion.moveCaretToCurrentLineEnd(editor, caret))
-    editor.vimChangeActionSwitchMode = VimStateMachine.Mode.INSERT
+    editor.vimChangeActionSwitchMode = Mode.INSERT
     insertText(editor, newCaret, "\n${editor.createIndentBySize(col)}")
   }
 
@@ -1270,7 +1265,7 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
         if (type === SelectionType.BLOCK_WISE) {
           setInsertRepeat(lines, col, false)
         }
-        editor.vimChangeActionSwitchMode = VimStateMachine.Mode.INSERT
+        editor.vimChangeActionSwitchMode = Mode.INSERT
       }
     } else {
       insertBeforeCursor(editor, context)
@@ -1308,7 +1303,8 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
     public const val VIM_MOTION_BIG_WORD_END_RIGHT: String = "VimMotionBigWordEndRightAction"
     public const val VIM_MOTION_CAMEL_END_RIGHT: String = "VimMotionCamelEndRightAction"
     public const val MAX_HEX_INTEGER: @NonNls String = "ffffffffffffffff"
-    public val wordMotions: Set<String> = setOf(VIM_MOTION_WORD_RIGHT, VIM_MOTION_BIG_WORD_RIGHT, VIM_MOTION_CAMEL_RIGHT)
+    public val wordMotions: Set<String> =
+      setOf(VIM_MOTION_WORD_RIGHT, VIM_MOTION_BIG_WORD_RIGHT, VIM_MOTION_CAMEL_RIGHT)
   }
 }
 
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimEditor.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimEditor.kt
index 0a4721a53..5bab26160 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimEditor.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimEditor.kt
@@ -8,9 +8,9 @@
 
 package com.maddyhome.idea.vim.api
 
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.EditorLine
 import com.maddyhome.idea.vim.common.LiveRange
 import com.maddyhome.idea.vim.common.Offset
@@ -126,7 +126,7 @@ import com.maddyhome.idea.vim.common.TextRange
 public interface VimEditor {
 
   public val lfMakesNewLine: Boolean
-  public var vimChangeActionSwitchMode: VimStateMachine.Mode?
+  public var vimChangeActionSwitchMode: Mode?
   public var vimKeepingVisualOperatorAction: Boolean
 
   public fun fileSize(): Long
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimInjector.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimInjector.kt
index dfc732e92..8f38ba280 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimInjector.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimInjector.kt
@@ -8,7 +8,7 @@
 
 package com.maddyhome.idea.vim.api
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.VimStateMachine
 import com.maddyhome.idea.vim.diagnostic.VimLogger
 import com.maddyhome.idea.vim.group.TabService
 import com.maddyhome.idea.vim.group.VimWindowGroup
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimMarkServiceBase.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimMarkServiceBase.kt
index d9b619518..c4f3c78c5 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimMarkServiceBase.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimMarkServiceBase.kt
@@ -24,12 +24,14 @@ import com.maddyhome.idea.vim.api.VimMarkService.Companion.SENTENCE_END_MARK
 import com.maddyhome.idea.vim.api.VimMarkService.Companion.SENTENCE_START_MARK
 import com.maddyhome.idea.vim.api.VimMarkService.Companion.UPPERCASE_MARKS
 import com.maddyhome.idea.vim.command.Command
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
+import com.maddyhome.idea.vim.state.mode.selectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.diagnostic.debug
 import com.maddyhome.idea.vim.diagnostic.vimLogger
-import com.maddyhome.idea.vim.helper.inVisualMode
-import com.maddyhome.idea.vim.helper.subMode
+import com.maddyhome.idea.vim.state.mode.inVisualMode
+import com.maddyhome.idea.vim.state.mode.mode
 import com.maddyhome.idea.vim.helper.vimStateMachine
 import com.maddyhome.idea.vim.mark.Jump
 import com.maddyhome.idea.vim.mark.Mark
@@ -233,7 +235,7 @@ public abstract class VimMarkServiceBase : VimMarkService {
 
   override fun setVisualSelectionMarks(editor: VimEditor) {
     if (!editor.inVisualMode) return
-    val selectionType = SelectionType.fromSubMode(editor.subMode)
+    val selectionType = editor.mode.selectionType ?: CHARACTER_WISE
     editor.carets()
       .forEach {
         val start = editor.offsetToBufferPosition(it.vimSelectionStart)
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimMotionGroupBase.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimMotionGroupBase.kt
index a57c5f16e..356272062 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimMotionGroupBase.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimMotionGroupBase.kt
@@ -21,12 +21,11 @@ import com.maddyhome.idea.vim.handler.TextObjectActionHandler
 import com.maddyhome.idea.vim.handler.toAdjustedMotionOrError
 import com.maddyhome.idea.vim.handler.toMotionOrError
 import com.maddyhome.idea.vim.helper.isEndAllowed
-import com.maddyhome.idea.vim.helper.isEndAllowedIgnoringOnemore
-import com.maddyhome.idea.vim.helper.mode
+import com.maddyhome.idea.vim.state.mode.isEndAllowedIgnoringOnemore
+import com.maddyhome.idea.vim.state.mode.mode
 import kotlin.math.abs
 import kotlin.math.absoluteValue
 import kotlin.math.min
-import kotlin.math.sign
 
 public abstract class VimMotionGroupBase : VimMotionGroup {
   override var lastFTCmd: TillCharacterMotionType = TillCharacterMotionType.LAST_SMALL_T
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimVisualMotionGroup.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimVisualMotionGroup.kt
index f0856336c..32bbdbf1a 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimVisualMotionGroup.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimVisualMotionGroup.kt
@@ -8,7 +8,8 @@
 
 package com.maddyhome.idea.vim.api
 
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.ReturnTo
+import com.maddyhome.idea.vim.state.mode.SelectionType
 
 public interface VimVisualMotionGroup {
   public val exclusiveSelection: Boolean
@@ -18,11 +19,11 @@ public interface VimVisualMotionGroup {
    * This function toggles visual mode.
    *
    * If visual mode is disabled, enable it
-   * If visual mode is enabled, but [subMode] differs, update visual according to new [subMode]
-   * If visual mode is enabled with the same [subMode], disable it
+   * If visual mode is enabled, but [selectionType] differs, update visual according to new [selectionType]
+   * If visual mode is enabled with the same [selectionType], disable it
    */
-  public fun toggleVisual(editor: VimEditor, count: Int, rawCount: Int, subMode: VimStateMachine.SubMode): Boolean
-  public fun enterSelectMode(editor: VimEditor, subMode: VimStateMachine.SubMode): Boolean
+  public fun toggleVisual(editor: VimEditor, count: Int, rawCount: Int, selectionType: SelectionType, returnTo: ReturnTo? = null): Boolean
+  public fun enterSelectMode(editor: VimEditor, subMode: SelectionType): Boolean
 
   /**
    * Enters visual mode based on current editor state.
@@ -38,6 +39,6 @@ public interface VimVisualMotionGroup {
    * - DOES NOT move caret
    * - DOES NOT check if carets actually have any selection
    */
-  public fun enterVisualMode(editor: VimEditor, subMode: VimStateMachine.SubMode? = null): Boolean
-  public fun autodetectVisualSubmode(editor: VimEditor): VimStateMachine.SubMode
+  public fun enterVisualMode(editor: VimEditor, subMode: SelectionType? = null): Boolean
+  public fun autodetectVisualSubmode(editor: VimEditor): SelectionType
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimVisualMotionGroupBase.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimVisualMotionGroupBase.kt
index 4e3c5caf7..0b2598caa 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimVisualMotionGroupBase.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/VimVisualMotionGroupBase.kt
@@ -8,18 +8,22 @@
 
 package com.maddyhome.idea.vim.api
 
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.group.visual.VisualChange
 import com.maddyhome.idea.vim.group.visual.VisualOperation
 import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset
 import com.maddyhome.idea.vim.group.visual.vimSetSelection
 import com.maddyhome.idea.vim.group.visual.vimUpdateEditorSelection
 import com.maddyhome.idea.vim.helper.exitVisualMode
-import com.maddyhome.idea.vim.helper.inVisualMode
-import com.maddyhome.idea.vim.helper.pushSelectMode
 import com.maddyhome.idea.vim.helper.pushVisualMode
-import com.maddyhome.idea.vim.helper.subMode
+import com.maddyhome.idea.vim.helper.setSelectMode
 import com.maddyhome.idea.vim.helper.vimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.ReturnTo
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.inVisualMode
+import com.maddyhome.idea.vim.state.mode.mode
+import com.maddyhome.idea.vim.state.mode.returnTo
+import com.maddyhome.idea.vim.state.mode.selectionType
 
 public abstract class VimVisualMotionGroupBase : VimVisualMotionGroup {
   override val exclusiveSelection: Boolean
@@ -27,8 +31,8 @@ public abstract class VimVisualMotionGroupBase : VimVisualMotionGroup {
   override val selectionAdj: Int
     get() = if (exclusiveSelection) 0 else 1
 
-  override fun enterSelectMode(editor: VimEditor, subMode: VimStateMachine.SubMode): Boolean {
-    editor.vimStateMachine.pushSelectMode(subMode)
+  override fun enterSelectMode(editor: VimEditor, subMode: SelectionType): Boolean {
+    editor.vimStateMachine.setSelectMode(subMode)
     editor.forEachCaret { it.vimSelectionStart = it.vimLeadSelectionOffset }
     return true
   }
@@ -37,18 +41,24 @@ public abstract class VimVisualMotionGroupBase : VimVisualMotionGroup {
    * This function toggles visual mode.
    *
    * If visual mode is disabled, enable it
-   * If visual mode is enabled, but [subMode] differs, update visual according to new [subMode]
-   * If visual mode is enabled with the same [subMode], disable it
+   * If visual mode is enabled, but [selectionType] differs, update visual according to new [selectionType]
+   * If visual mode is enabled with the same [selectionType], disable it
    */
-  override fun toggleVisual(editor: VimEditor, count: Int, rawCount: Int, subMode: VimStateMachine.SubMode): Boolean {
+  override fun toggleVisual(
+    editor: VimEditor,
+    count: Int,
+    rawCount: Int,
+    selectionType: SelectionType,
+    returnTo: ReturnTo?
+  ): Boolean {
     if (!editor.inVisualMode) {
       // Enable visual subMode
       if (rawCount > 0) {
-        val primarySubMode = editor.primaryCaret().vimLastVisualOperatorRange?.type?.toSubMode() ?: subMode
+        val primarySubMode = editor.primaryCaret().vimLastVisualOperatorRange?.type ?: selectionType
         editor.vimStateMachine.pushVisualMode(primarySubMode)
 
         editor.forEachCaret {
-          val range = it.vimLastVisualOperatorRange ?: VisualChange.default(subMode)
+          val range = it.vimLastVisualOperatorRange ?: VisualChange.default(selectionType)
           val end = VisualOperation.calculateRange(editor, range, count, it)
           val intendedColumn = if (range.columns == VimMotionGroupBase.LAST_COLUMN) {
             VimMotionGroupBase.LAST_COLUMN
@@ -61,20 +71,26 @@ public abstract class VimVisualMotionGroupBase : VimVisualMotionGroup {
           it.vimLastColumn = intendedColumn
         }
       } else {
-        editor.vimStateMachine.pushVisualMode(subMode)
+        editor.vimStateMachine.mode = Mode.VISUAL(
+          selectionType,
+          returnTo ?: editor.vimStateMachine.mode.returnTo
+        )
         editor.forEachCaret { it.vimSetSelection(it.offset.point) }
       }
       return true
     }
 
-    if (subMode == editor.subMode) {
+    if (selectionType == editor.mode.selectionType) {
       // Disable visual subMode
       editor.exitVisualMode()
       return true
     }
 
+    val mode = editor.mode
+    check(mode is Mode.VISUAL)
+
     // Update visual subMode with new sub subMode
-    editor.subMode = subMode
+    editor.vimStateMachine.mode = mode.copy(selectionType = selectionType)
     for (caret in editor.carets()) {
       if (!caret.isValid) continue
       caret.vimUpdateEditorSelection()
@@ -111,9 +127,9 @@ public abstract class VimVisualMotionGroupBase : VimVisualMotionGroup {
     return true
   }
 
-  override fun autodetectVisualSubmode(editor: VimEditor): VimStateMachine.SubMode {
+  override fun autodetectVisualSubmode(editor: VimEditor): SelectionType {
     if (editor.carets().size > 1 && seemsLikeBlockMode(editor)) {
-      return VimStateMachine.SubMode.VISUAL_BLOCK
+      return SelectionType.BLOCK_WISE
     }
     val all = editor.nativeCarets().all { caret ->
       // Detect if visual mode is character wise or line wise
@@ -126,8 +142,8 @@ public abstract class VimVisualMotionGroupBase : VimVisualMotionGroup {
       val lineEndOfSelectionEnd = editor.getLineEndOffset(endLine, true)
       lineStartOfSelectionStart == selectionStart && (lineEndOfSelectionEnd + 1 == selectionEnd || lineEndOfSelectionEnd == selectionEnd)
     }
-    if (all) return VimStateMachine.SubMode.VISUAL_LINE
-    return VimStateMachine.SubMode.VISUAL_CHARACTER
+    if (all) return SelectionType.LINE_WISE
+    return SelectionType.CHARACTER_WISE
   }
 
   /**
@@ -144,10 +160,11 @@ public abstract class VimVisualMotionGroupBase : VimVisualMotionGroup {
    * - DOES NOT move caret
    * - DOES NOT check if carets actually have any selection
    */
-  override fun enterVisualMode(editor: VimEditor, subMode: VimStateMachine.SubMode?): Boolean {
+  override fun enterVisualMode(editor: VimEditor, subMode: SelectionType?): Boolean {
     val autodetectedSubMode = subMode ?: autodetectVisualSubmode(editor)
-    editor.vimStateMachine.pushModes(VimStateMachine.Mode.VISUAL, autodetectedSubMode)
-    if (autodetectedSubMode == VimStateMachine.SubMode.VISUAL_BLOCK) {
+    editor.vimStateMachine.mode = Mode.VISUAL(autodetectedSubMode)
+    //    editor.vimStateMachine.setMode(VimStateMachine.Mode.VISUAL, autodetectedSubMode)
+    if (autodetectedSubMode == SelectionType.BLOCK_WISE) {
       editor.primaryCaret().run { vimSelectionStart = vimLeadSelectionOffset }
     } else {
       editor.nativeCarets().forEach { it.vimSelectionStart = it.vimLeadSelectionOffset }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/MappingProcessor.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/MappingProcessor.kt
index f8ab4d24d..38df466d9 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/MappingProcessor.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/MappingProcessor.kt
@@ -18,6 +18,7 @@ import com.maddyhome.idea.vim.diagnostic.trace
 import com.maddyhome.idea.vim.diagnostic.vimLogger
 import com.maddyhome.idea.vim.helper.vimStateMachine
 import com.maddyhome.idea.vim.key.KeyMappingLayer
+import com.maddyhome.idea.vim.state.VimStateMachine
 import javax.swing.KeyStroke
 
 public object MappingProcessor {
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/MappingState.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/MappingState.kt
index fc72903a7..e7caab7fc 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/MappingState.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/MappingState.kt
@@ -36,6 +36,9 @@ public class MappingState {
     get() = keyList
 
   public var mappingMode: MappingMode = MappingMode.NORMAL
+    set(value) {
+      field = value
+    }
 
   private val timer = Timer(injector.globalOptions().timeoutlen, null)
   private var keyList = mutableListOf<KeyStroke>()
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/OperatorArguments.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/OperatorArguments.kt
index b67cc140e..a75c09de2 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/OperatorArguments.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/OperatorArguments.kt
@@ -8,6 +8,8 @@
 
 package com.maddyhome.idea.vim.command
 
+import com.maddyhome.idea.vim.state.mode.Mode
+
 /**
  * [count0] is a raw count entered by user. May be zero.
  * [count1] is the same count, but 1-based. If [count0] is zero, [count1] is one.
@@ -18,8 +20,7 @@ public data class OperatorArguments(
   val isOperatorPending: Boolean,
   val count0: Int,
 
-  val mode: VimStateMachine.Mode,
-  val subMode: VimStateMachine.SubMode,
+  val mode: Mode,
 ) {
   val count1: Int = count0.coerceAtLeast(1)
 
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/SelectionType.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/SelectionType.kt
deleted file mode 100644
index 490fbe7c7..000000000
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/SelectionType.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2003-2023 The IdeaVim authors
- *
- * Use of this source code is governed by an MIT-style
- * license that can be found in the LICENSE.txt file or at
- * https://opensource.org/licenses/MIT.
- */
-package com.maddyhome.idea.vim.command
-
-import com.maddyhome.idea.vim.command.VimStateMachine.SubMode
-
-/**
- * @author vlan
- */
-public enum class SelectionType(public val value: Int) {
-  // Integer values for registers serialization in RegisterGroup.readData()
-  LINE_WISE(1 shl 1),
-  CHARACTER_WISE(1 shl 2),
-  BLOCK_WISE(1 shl 3),
-  ;
-
-  public fun toSubMode(): SubMode = when (this) {
-    LINE_WISE -> SubMode.VISUAL_LINE
-    CHARACTER_WISE -> SubMode.VISUAL_CHARACTER
-    BLOCK_WISE -> SubMode.VISUAL_BLOCK
-  }
-
-  public companion object {
-    @JvmStatic
-    public fun fromValue(value: Int): SelectionType {
-      for (type in values()) {
-        if (type.value == value) {
-          return type
-        }
-      }
-      return CHARACTER_WISE
-    }
-
-    @JvmStatic
-    public fun fromSubMode(subMode: SubMode): SelectionType = when (subMode) {
-      SubMode.VISUAL_LINE -> LINE_WISE
-      SubMode.VISUAL_BLOCK -> BLOCK_WISE
-      else -> CHARACTER_WISE
-    }
-  }
-}
-
-public val SelectionType.isLine: Boolean get() = this == SelectionType.LINE_WISE
-public val SelectionType.isChar: Boolean get() = this == SelectionType.CHARACTER_WISE
-public val SelectionType.isBlock: Boolean get() = this == SelectionType.BLOCK_WISE
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/VimStateMachine.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/VimStateMachine.kt
deleted file mode 100644
index c1768e699..000000000
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/command/VimStateMachine.kt
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright 2003-2023 The IdeaVim authors
- *
- * Use of this source code is governed by an MIT-style
- * license that can be found in the LICENSE.txt file or at
- * https://opensource.org/licenses/MIT.
- */
-package com.maddyhome.idea.vim.command
-
-import com.maddyhome.idea.vim.api.VimActionsInitiator
-import com.maddyhome.idea.vim.api.VimEditor
-import com.maddyhome.idea.vim.api.globalOptions
-import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.common.DigraphResult
-import com.maddyhome.idea.vim.common.DigraphSequence
-import com.maddyhome.idea.vim.diagnostic.debug
-import com.maddyhome.idea.vim.diagnostic.vimLogger
-import com.maddyhome.idea.vim.helper.noneOfEnum
-import com.maddyhome.idea.vim.key.CommandPartNode
-import org.jetbrains.annotations.Contract
-import java.util.*
-import javax.swing.KeyStroke
-
-/**
- * Used to maintain state before and while entering a Vim command (operator, motion, text object, etc.)
- *
- * // TODO: 21.02.2022 This constructor should be empty
- */
-public class VimStateMachine(private val editor: VimEditor?) {
-  public val commandBuilder: CommandBuilder = CommandBuilder(getKeyRootNode(MappingMode.NORMAL))
-  private val modeStates = Stack<ModeState>()
-  public val mappingState: MappingState = MappingState()
-  public val digraphSequence: DigraphSequence = DigraphSequence()
-  public var isRecording: Boolean = false
-    set(value) {
-      field = value
-      doShowMode()
-    }
-  public var isDotRepeatInProgress: Boolean = false
-  public var isRegisterPending: Boolean = false
-  public var isReplaceCharacter: Boolean = false
-    set(value) {
-      field = value
-      onModeChanged()
-    }
-
-  /**
-   * The currently executing command
-   *
-   * This is a complete command, e.g. operator + motion. Some actions/helpers require additional context from flags in
-   * the command/argument. Ideally, we would pass the command through KeyHandler#executeVimAction and
-   * EditorActionHandlerBase#execute, but we also need to know the command type in MarkGroup#updateMarkFromDelete,
-   * which is called via a document change event.
-   *
-   * This field is reset after the command has been executed.
-   */
-  public var executingCommand: Command? = null
-    private set
-
-  public val isOperatorPending: Boolean
-    get() = mappingState.mappingMode == MappingMode.OP_PENDING && !commandBuilder.isEmpty
-
-  init {
-    pushModes(defaultModeState.mode, defaultModeState.subMode)
-  }
-
-  public fun isDuplicateOperatorKeyStroke(key: KeyStroke?): Boolean {
-    return isOperatorPending && commandBuilder.isDuplicateOperatorKeyStroke(key!!)
-  }
-
-  public fun setExecutingCommand(cmd: Command) {
-    executingCommand = cmd
-  }
-
-  public val executingCommandFlags: EnumSet<CommandFlags>
-    get() = executingCommand?.flags ?: noneOfEnum()
-
-  public fun pushModes(mode: Mode, submode: SubMode) {
-    val newModeState = ModeState(mode, submode)
-
-    logger.debug("Push new mode state: ${newModeState.toSimpleString()}")
-    logger.debug { "Stack of mode states before push: ${toSimpleString()}" }
-
-    val previousMode = currentModeState()
-    modeStates.push(newModeState)
-    setMappingMode()
-
-    if (previousMode != newModeState) {
-      onModeChanged()
-    }
-  }
-
-  public fun popModes() {
-    val popped = modeStates.pop()
-    setMappingMode()
-    if (popped != currentModeState()) {
-      onModeChanged()
-    }
-
-    logger.debug("Popped mode state: ${popped.toSimpleString()}")
-    logger.debug { "Stack of mode states after pop: ${toSimpleString()}" }
-  }
-
-  public fun resetOpPending() {
-    if (mode == Mode.OP_PENDING) {
-      popModes()
-    }
-  }
-
-  public fun resetReplaceCharacter() {
-    if (isReplaceCharacter) {
-      isReplaceCharacter = false
-    }
-  }
-
-  public fun resetRegisterPending() {
-    if (isRegisterPending) {
-      isRegisterPending = false
-    }
-  }
-
-  private fun resetModes() {
-    modeStates.clear()
-    pushModes(defaultModeState.mode, defaultModeState.subMode)
-    onModeChanged()
-    setMappingMode()
-  }
-
-  private fun onModeChanged() {
-    if (editor != null) {
-      editor.updateCaretsVisualAttributes()
-      editor.updateCaretsVisualPosition()
-    } else {
-      injector.application.localEditors().forEach { editor ->
-        editor.updateCaretsVisualAttributes()
-        editor.updateCaretsVisualPosition()
-      }
-    }
-    doShowMode()
-  }
-
-  private fun setMappingMode() {
-    mappingState.mappingMode = modeToMappingMode(mode)
-  }
-
-  public val mode: Mode
-    get() = currentModeState().mode
-
-  public var subMode: SubMode
-    get() = currentModeState().subMode
-    set(submode) {
-      val modeState = currentModeState()
-      popModes()
-      pushModes(modeState.mode, submode)
-    }
-
-  public fun startDigraphSequence() {
-    digraphSequence.startDigraphSequence()
-  }
-
-  public fun startLiteralSequence() {
-    digraphSequence.startLiteralSequence()
-  }
-
-  public fun processDigraphKey(key: KeyStroke, editor: VimEditor): DigraphResult {
-    return digraphSequence.processKey(key, editor)
-  }
-
-  public fun resetDigraph() {
-    digraphSequence.reset()
-  }
-
-  /**
-   * Toggles the insert/overwrite state. If currently insert, goto replace mode. If currently replace, goto insert
-   * mode.
-   */
-  public fun toggleInsertOverwrite() {
-    val oldMode = mode
-    var newMode = oldMode
-    if (oldMode == Mode.INSERT) {
-      newMode = Mode.REPLACE
-    } else if (oldMode == Mode.REPLACE) {
-      newMode = Mode.INSERT
-    }
-    if (oldMode != newMode) {
-      val modeState = currentModeState()
-      popModes()
-      pushModes(newMode, modeState.subMode)
-    }
-  }
-
-  /**
-   * Resets the command, mode, visual mode, and mapping mode to initial values.
-   */
-  public fun reset() {
-    executingCommand = null
-    resetModes()
-    commandBuilder.resetInProgressCommandPart(getKeyRootNode(mappingState.mappingMode))
-    digraphSequence.reset()
-  }
-
-  public fun toSimpleString(): String = modeStates.joinToString { it.toSimpleString() }
-
-  /**
-   * It's a bit more complicated
-   *
-   *  Neovim
-   * :h mode()
-   *
-   * - mode(expr)          Return a string that indicates the current mode.
-   *
-   *   If "expr" is supplied and it evaluates to a non-zero Number or
-   *   a non-empty String (|non-zero-arg|), then the full mode is
-   *   returned, otherwise only the first letter is returned.
-   *
-   *   n          Normal
-   *   no         Operator-pending
-   *   nov        Operator-pending (forced characterwise |o_v|)
-   *   noV        Operator-pending (forced linewise |o_V|)
-   *   noCTRL-V   Operator-pending (forced blockwise |o_CTRL-V|)
-   *   niI        Normal using |i_CTRL-O| in |Insert-mode|
-   *   niR        Normal using |i_CTRL-O| in |Replace-mode|
-   *   niV        Normal using |i_CTRL-O| in |Virtual-Replace-mode|
-   *   v          Visual by character
-   *   V          Visual by line
-   *   CTRL-V     Visual blockwise
-   *   s          Select by character
-   *   S          Select by line
-   *   CTRL-S     Select blockwise
-   *   i          Insert
-   *   ic         Insert mode completion |compl-generic|
-   *   ix         Insert mode |i_CTRL-X| completion
-   *   R          Replace |R|
-   *   Rc         Replace mode completion |compl-generic|
-   *   Rv         Virtual Replace |gR|
-   *   Rx         Replace mode |i_CTRL-X| completion
-   *   c          Command-line editing
-   *   cv         Vim Ex mode |gQ|
-   *   ce         Normal Ex mode |Q|
-   *   r          Hit-enter prompt
-   *   rm         The -- more -- prompt
-   *   r?         |:confirm| query of some sort
-   *   !          Shell or external command is executing
-   *   t          Terminal mode: keys go to the job
-   *   This is useful in the 'statusline' option or when used
-   *   with |remote_expr()| In most other places it always returns
-   *   "c" or "n".
-   *   Note that in the future more modes and more specific modes may
-   *   be added. It's better not to compare the whole string but only
-   *   the leading character(s).
-   */
-  public fun toVimNotation(): String {
-    return when (mode) {
-      Mode.COMMAND -> "n"
-      Mode.VISUAL -> when (subMode) {
-        SubMode.VISUAL_CHARACTER -> "v"
-        SubMode.VISUAL_LINE -> "V"
-        SubMode.VISUAL_BLOCK -> "\u0016"
-        else -> error("Unexpected state")
-      }
-      Mode.INSERT -> "i"
-      Mode.SELECT -> when (subMode) {
-        SubMode.VISUAL_CHARACTER -> "s"
-        SubMode.VISUAL_LINE -> "S"
-        SubMode.VISUAL_BLOCK -> "\u0013"
-        else -> error("Unexpected state")
-      }
-
-      Mode.REPLACE -> "R"
-      Mode.INSERT_VISUAL -> when (subMode) {
-        SubMode.VISUAL_CHARACTER -> "v"
-        SubMode.VISUAL_LINE -> "V"
-        SubMode.VISUAL_BLOCK -> "\u0016"
-        else -> error("Unexpected state")
-      }
-      else -> error("Unexpected state")
-    }
-  }
-
-  private fun currentModeState(): ModeState {
-    return if (modeStates.size > 0) modeStates.peek() else defaultModeState
-  }
-
-  private fun doShowMode() {
-    val msg = StringBuilder()
-    if (injector.globalOptions().showmode) {
-      msg.append(getStatusString())
-    }
-    if (isRecording) {
-      if (msg.isNotEmpty()) {
-        msg.append(" - ")
-      }
-      msg.append(injector.messages.message("show.mode.recording"))
-    }
-    injector.messages.showMode(editor, msg.toString())
-  }
-
-  public fun getStatusString(): String {
-    val pos = modeStates.size - 1
-    val modeState = if (pos >= 0) {
-      modeStates[pos]
-    } else {
-      defaultModeState
-    }
-    return buildString {
-      when (modeState.mode) {
-        Mode.INSERT_NORMAL -> append("-- (insert) --")
-        Mode.INSERT -> append("-- INSERT --")
-        Mode.REPLACE -> append("-- REPLACE --")
-        Mode.VISUAL -> {
-          append("-- VISUAL")
-          when (modeState.subMode) {
-            SubMode.VISUAL_LINE -> append(" LINE")
-            SubMode.VISUAL_BLOCK -> append(" BLOCK")
-            else -> Unit
-          }
-          append(" --")
-        }
-        Mode.SELECT -> {
-          append("-- SELECT")
-          when (modeState.subMode) {
-            SubMode.VISUAL_LINE -> append(" LINE")
-            SubMode.VISUAL_BLOCK -> append(" BLOCK")
-            else -> Unit
-          }
-          append(" --")
-        }
-        Mode.INSERT_VISUAL -> {
-          append("-- (insert) VISUAL")
-          when (modeState.subMode) {
-            SubMode.VISUAL_LINE -> append(" LINE")
-            SubMode.VISUAL_BLOCK -> append(" BLOCK")
-            else -> Unit
-          }
-          append(" --")
-        }
-        Mode.INSERT_SELECT -> {
-          append("-- (insert) SELECT")
-          when (modeState.subMode) {
-            SubMode.VISUAL_LINE -> append(" LINE")
-            SubMode.VISUAL_BLOCK -> append(" BLOCK")
-            else -> Unit
-          }
-          append(" --")
-        }
-        else -> Unit
-      }
-    }
-  }
-
-  public enum class Mode {
-    // Basic modes
-    COMMAND, VISUAL, SELECT, INSERT, CMD_LINE, /*EX*/
-
-    // Additional modes
-    OP_PENDING, REPLACE /*, VISUAL_REPLACE*/, INSERT_NORMAL, INSERT_VISUAL, INSERT_SELECT
-  }
-
-  public enum class SubMode {
-    NONE, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK
-  }
-
-  private data class ModeState(val mode: Mode, val subMode: SubMode) {
-    fun toSimpleString(): String = "$mode:$subMode"
-  }
-
-  public companion object {
-    private val logger = vimLogger<VimStateMachine>()
-    private val defaultModeState = ModeState(Mode.COMMAND, SubMode.NONE)
-    private val globalState = VimStateMachine(null)
-
-    /**
-     * COMPATIBILITY-LAYER: Method switched to Any (was VimEditor)
-     * Please see: https://jb.gg/zo8n0r
-     */
-    @JvmStatic
-    public fun getInstance(editor: Any?): VimStateMachine {
-      return if (editor == null || injector.globalOptions().ideaglobalmode) {
-        globalState
-      } else {
-        injector.commandStateFor(editor)
-      }
-    }
-
-    private fun getKeyRootNode(mappingMode: MappingMode): CommandPartNode<VimActionsInitiator> {
-      return injector.keyGroup.getKeyRoot(mappingMode)
-    }
-
-    @Contract(pure = true)
-    public fun modeToMappingMode(mode: Mode): MappingMode {
-      return when (mode) {
-        Mode.COMMAND -> MappingMode.NORMAL
-        Mode.INSERT, Mode.REPLACE -> MappingMode.INSERT
-        Mode.VISUAL -> MappingMode.VISUAL
-        Mode.SELECT -> MappingMode.SELECT
-        Mode.CMD_LINE -> MappingMode.CMD_LINE
-        Mode.OP_PENDING -> MappingMode.OP_PENDING
-        Mode.INSERT_NORMAL -> MappingMode.NORMAL
-        Mode.INSERT_VISUAL -> MappingMode.VISUAL
-        Mode.INSERT_SELECT -> MappingMode.SELECT
-      }
-    }
-  }
-}
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/common/VimVisualGroup.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/common/VimVisualGroup.kt
index 2867be648..14cd827a8 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/common/VimVisualGroup.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/common/VimVisualGroup.kt
@@ -15,13 +15,13 @@ import com.maddyhome.idea.vim.api.getLineStartForOffset
 import com.maddyhome.idea.vim.api.globalOptions
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.lineLength
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 
-public fun charToNativeSelection(editor: VimEditor, start: Int, end: Int, mode: VimStateMachine.Mode): Pair<Int, Int> {
+public fun charToNativeSelection(editor: VimEditor, start: Int, end: Int, mode: Mode): Pair<Int, Int> {
   val (nativeStart, nativeEnd) = sort(start, end)
   val lineEnd = editor.getLineEndForOffset(nativeEnd)
   val adj =
-    if (isExclusiveSelection() || nativeEnd == lineEnd || mode == VimStateMachine.Mode.SELECT) 0 else 1
+    if (isExclusiveSelection() || nativeEnd == lineEnd || mode is Mode.SELECT) 0 else 1
   val adjEnd = (nativeEnd + adj).coerceAtMost(editor.fileSize().toInt())
   return nativeStart to adjEnd
 }
@@ -47,11 +47,11 @@ public fun blockToNativeSelection(
   editor: VimEditor,
   start: Int,
   end: Int,
-  mode: VimStateMachine.Mode,
+  mode: Mode,
 ): Pair<BufferPosition, BufferPosition> {
   var blockStart = editor.offsetToBufferPosition(start)
   var blockEnd = editor.offsetToBufferPosition(end)
-  if (!isExclusiveSelection() && mode != VimStateMachine.Mode.SELECT) {
+  if (!isExclusiveSelection() && mode !is Mode.SELECT) {
     if (blockStart.column > blockEnd.column) {
       if (blockStart.column < editor.lineLength(blockStart.line)) {
         blockStart = BufferPosition(blockStart.line, blockStart.column + 1)
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/EngineVisualGroup.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/EngineVisualGroup.kt
index 9db399533..c10a1ffc0 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/EngineVisualGroup.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/EngineVisualGroup.kt
@@ -9,24 +9,33 @@
 package com.maddyhome.idea.vim.group.visual
 
 import com.maddyhome.idea.vim.api.*
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
+import com.maddyhome.idea.vim.state.mode.selectionType
 import com.maddyhome.idea.vim.helper.*
+import com.maddyhome.idea.vim.state.mode.inBlockSelection
+import com.maddyhome.idea.vim.state.mode.inSelectMode
+import com.maddyhome.idea.vim.state.mode.inVisualMode
+import com.maddyhome.idea.vim.state.mode.mode
 
 public fun setVisualSelection(selectionStart: Int, selectionEnd: Int, caret: VimCaret) {
   val (start, end) = if (selectionStart > selectionEnd) selectionEnd to selectionStart else selectionStart to selectionEnd
   val editor = caret.editor
-  val subMode = editor.subMode
+  val subMode = editor.mode.selectionType ?: CHARACTER_WISE
   val mode = editor.mode
   when (subMode) {
-    VimStateMachine.SubMode.VISUAL_CHARACTER -> {
+    SelectionType.CHARACTER_WISE -> {
       val (nativeStart, nativeEnd) = charToNativeSelection(editor, start, end, mode)
       caret.vimSetSystemSelectionSilently(nativeStart, nativeEnd)
     }
-    VimStateMachine.SubMode.VISUAL_LINE -> {
+
+    SelectionType.LINE_WISE -> {
       val (nativeStart, nativeEnd) = lineToNativeSelection(editor, start, end)
       caret.vimSetSystemSelectionSilently(nativeStart, nativeEnd)
     }
-    VimStateMachine.SubMode.VISUAL_BLOCK -> {
+
+    SelectionType.BLOCK_WISE -> {
       // This will invalidate any secondary carets, but we shouldn't have any of these cached in local variables, etc.
       editor.removeSecondaryCarets()
 
@@ -61,7 +70,7 @@ public fun setVisualSelection(selectionStart: Int, selectionEnd: Int, caret: Vim
           // Put right caret position for tab character
           aCaret.moveToVisualPosition(visualPosition)
         }
-        if (mode != VimStateMachine.Mode.SELECT &&
+        if (mode !is Mode.SELECT &&
           !editor.isLineEmpty(line, false) &&
           aCaret.offset.point == aCaret.selectionEnd &&
           aCaret.selectionEnd - 1 >= lineStartOffset &&
@@ -74,7 +83,6 @@ public fun setVisualSelection(selectionStart: Int, selectionEnd: Int, caret: Vim
 
       editor.primaryCaret().moveToInlayAwareOffset(selectionEnd)
     }
-    else -> Unit
   }
 }
 
@@ -86,7 +94,7 @@ public fun setVisualSelection(selectionStart: Int, selectionEnd: Int, caret: Vim
 public fun VimCaret.vimSetSelection(start: Int, end: Int = start, moveCaretToSelectionEnd: Boolean = false) {
   vimSelectionStart = start
   setVisualSelection(start, end, this)
-  if (moveCaretToSelectionEnd && !editor.inBlockSubMode) moveToInlayAwareOffset(end)
+  if (moveCaretToSelectionEnd && !editor.inBlockSelection) moveToInlayAwareOffset(end)
 }
 
 /**
@@ -110,7 +118,7 @@ public fun vimMoveBlockSelectionToOffset(editor: VimEditor, offset: Int) {
  */
 public fun VimCaret.vimMoveSelectionToCaret(vimSelectionStart: Int = this.vimSelectionStart) {
   if (!editor.inVisualMode && !editor.inSelectMode) error("Attempt to extent selection in non-visual mode")
-  if (editor.inBlockSubMode) error("Move caret with [vimMoveBlockSelectionToOffset]")
+  if (editor.inBlockSelection) error("Move caret with [vimMoveBlockSelectionToOffset]")
 
   val startOffsetMark = vimSelectionStart
 
@@ -146,7 +154,7 @@ public val ImmutableVimCaret.vimLeadSelectionOffset: Int
         }
       }
 
-      return if (editor.subMode == VimStateMachine.SubMode.VISUAL_LINE) {
+      return if (editor.mode.selectionType == SelectionType.LINE_WISE) {
         val selectionStartLine = editor.offsetToBufferPosition(selectionStart).line
         val caretLine = editor.offsetToBufferPosition(this.offset.point).line
         if (caretLine == selectionStartLine) {
@@ -155,7 +163,7 @@ public val ImmutableVimCaret.vimLeadSelectionOffset: Int
         } else {
           selectionStart
         }
-      } else if (editor.inBlockSubMode) {
+      } else if (editor.inBlockSelection) {
         val selections = editor.nativeCarets().map { it.selectionStart to it.selectionEnd }.sortedBy { it.first }
         val pCaret = editor.primaryCaret()
         when (pCaret.offset.point) {
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/VimSelection.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/VimSelection.kt
index e2ef63aec..1c221e9c4 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/VimSelection.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/VimSelection.kt
@@ -11,11 +11,11 @@ package com.maddyhome.idea.vim.group.visual
 import com.maddyhome.idea.vim.api.BufferPosition
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.getLineEndOffset
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.SelectionType.BLOCK_WISE
-import com.maddyhome.idea.vim.command.SelectionType.CHARACTER_WISE
-import com.maddyhome.idea.vim.command.SelectionType.LINE_WISE
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType.BLOCK_WISE
+import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
+import com.maddyhome.idea.vim.state.mode.SelectionType.LINE_WISE
 import com.maddyhome.idea.vim.common.Pointer
 import com.maddyhome.idea.vim.common.TextRange
 import org.jetbrains.annotations.NonNls
@@ -49,7 +49,7 @@ public sealed class VimSelection {
   public companion object {
     public fun create(vimStart: Int, vimEnd: Int, type: SelectionType, editor: VimEditor): VimSelection = when (type) {
       CHARACTER_WISE -> {
-        val nativeSelection = charToNativeSelection(editor, vimStart, vimEnd, VimStateMachine.Mode.VISUAL)
+        val nativeSelection = charToNativeSelection(editor, vimStart, vimEnd, Mode.VISUAL(SelectionType.CHARACTER_WISE))
         VimCharacterSelection(vimStart, vimEnd, nativeSelection.first, nativeSelection.second, editor)
       }
       LINE_WISE -> {
@@ -153,7 +153,8 @@ public class VimBlockSelection(
   override val editor: VimEditor,
   private val toLineEnd: Boolean,
 ) : VimSelection() {
-  override fun getNativeStartAndEnd(): Pair<Int, Int> = blockToNativeSelection(editor, vimStart, vimEnd, VimStateMachine.Mode.VISUAL).let {
+  override fun getNativeStartAndEnd(): Pair<Int, Int> = blockToNativeSelection(editor, vimStart, vimEnd, Mode.VISUAL(
+    SelectionType.CHARACTER_WISE)).let {
     editor.bufferPositionToOffset(it.first) to editor.bufferPositionToOffset(it.second)
   }
 
@@ -170,7 +171,7 @@ public class VimBlockSelection(
   }
 
   private fun forEachLine(action: (start: Int, end: Int) -> Unit) {
-    val (startPosition, endPosition) = blockToNativeSelection(editor, vimStart, vimEnd, VimStateMachine.Mode.VISUAL)
+    val (startPosition, endPosition) = blockToNativeSelection(editor, vimStart, vimEnd, Mode.VISUAL(SelectionType.CHARACTER_WISE))
     val lineRange =
       if (startPosition.line > endPosition.line) endPosition.line..startPosition.line else startPosition.line..endPosition.line
     lineRange.map { line ->
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/VisualChange.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/VisualChange.kt
index e7351a7e0..499e7a14b 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/VisualChange.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/VisualChange.kt
@@ -8,15 +8,14 @@
 
 package com.maddyhome.idea.vim.group.visual
 
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 
 public data class VisualChange(val lines: Int, val columns: Int, val type: SelectionType) {
   public companion object {
-    public fun default(subMode: VimStateMachine.SubMode): VisualChange =
-      when (val type = SelectionType.fromSubMode(subMode)) {
-        SelectionType.LINE_WISE, SelectionType.CHARACTER_WISE -> VisualChange(1, 1, type)
-        SelectionType.BLOCK_WISE -> VisualChange(0, 1, type)
+    public fun default(subMode: SelectionType): VisualChange =
+      when (subMode) {
+        SelectionType.LINE_WISE, SelectionType.CHARACTER_WISE -> VisualChange(1, 1, subMode)
+        SelectionType.BLOCK_WISE -> VisualChange(0, 1, subMode)
       }
   }
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/VisualOperation.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/VisualOperation.kt
index f613ff576..99ebfdb2d 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/VisualOperation.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/VisualOperation.kt
@@ -16,9 +16,11 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.lineLength
 import com.maddyhome.idea.vim.api.normalizeOffset
 import com.maddyhome.idea.vim.command.CommandFlags
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.helper.inBlockSubMode
-import com.maddyhome.idea.vim.helper.subMode
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
+import com.maddyhome.idea.vim.state.mode.selectionType
+import com.maddyhome.idea.vim.state.mode.inBlockSelection
+import com.maddyhome.idea.vim.state.mode.mode
 import java.util.*
 import kotlin.math.min
 
@@ -28,9 +30,9 @@ public object VisualOperation {
    */
   public fun getRange(editor: VimEditor, caret: ImmutableVimCaret, cmdFlags: EnumSet<CommandFlags>): VisualChange {
     var (start, end) = caret.run {
-      if (editor.inBlockSubMode) sort(vimSelectionStart, offset.point) else sort(selectionStart, selectionEnd)
+      if (editor.inBlockSelection) sort(vimSelectionStart, offset.point) else sort(selectionStart, selectionEnd)
     }
-    val type = SelectionType.fromSubMode(editor.subMode)
+    val type = editor.mode.selectionType ?: CHARACTER_WISE
 
     start = editor.normalizeOffset(start, false)
     end = editor.normalizeOffset(end, false)
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/EditorActionHandlerBase.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/EditorActionHandlerBase.kt
index 70583a186..b25015ae0 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/EditorActionHandlerBase.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/EditorActionHandlerBase.kt
@@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.VimStateMachine
 import com.maddyhome.idea.vim.diagnostic.vimLogger
 import com.maddyhome.idea.vim.helper.noneOfEnum
 import org.jetbrains.annotations.NonNls
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/MotionActionHandler.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/MotionActionHandler.kt
index 7b95857c4..1b03e6032 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/MotionActionHandler.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/MotionActionHandler.kt
@@ -24,8 +24,8 @@ import com.maddyhome.idea.vim.command.OperatorArguments
 import com.maddyhome.idea.vim.diagnostic.VimLogger
 import com.maddyhome.idea.vim.diagnostic.vimLogger
 import com.maddyhome.idea.vim.helper.StrictMode
-import com.maddyhome.idea.vim.helper.inBlockSubMode
-import com.maddyhome.idea.vim.helper.inVisualMode
+import com.maddyhome.idea.vim.state.mode.inBlockSelection
+import com.maddyhome.idea.vim.state.mode.inVisualMode
 import com.maddyhome.idea.vim.helper.isEndAllowed
 
 /**
@@ -107,7 +107,7 @@ public sealed class MotionActionHandler : EditorActionHandlerBase(false) {
     cmd: Command,
     operatorArguments: OperatorArguments,
   ): Boolean {
-    val blockSubmodeActive = editor.inBlockSubMode
+    val blockSubmodeActive = editor.inBlockSelection
 
     when (this) {
       is SingleExecution -> run {
@@ -175,7 +175,7 @@ public sealed class MotionActionHandler : EditorActionHandlerBase(false) {
     // Block selection mode is emulated with multiple carets. We should only be operating on the primary caret. Note
     // that moving the primary caret to modify the selection can cause IntelliJ to invalidate, replace or add a new
     // primary caret
-    if (editor.inBlockSubMode) {
+    if (editor.inBlockSelection) {
       StrictMode.assert(caret.isPrimary, "Block selection mode must only operate on primary caret")
     }
 
@@ -192,7 +192,7 @@ public sealed class MotionActionHandler : EditorActionHandlerBase(false) {
 
     // We've moved the caret, so reset the intended column. Visual block movement can replace the primary caret when
     // moving the selection up, so make sure we've got a valid caret
-    val validCaret = if (editor.inBlockSubMode) editor.primaryCaret() else caretAfterMove
+    val validCaret = if (editor.inBlockSelection) editor.primaryCaret() else caretAfterMove
     validCaret.vimLastColumn = offset.intendedColumn
   }
 
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/SpecialKeyHandlers.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/SpecialKeyHandlers.kt
index effd1407f..0761c95a4 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/SpecialKeyHandlers.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/SpecialKeyHandlers.kt
@@ -17,11 +17,13 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.helper.exitVisualMode
-import com.maddyhome.idea.vim.helper.inSelectMode
-import com.maddyhome.idea.vim.helper.inVisualMode
 import com.maddyhome.idea.vim.options.OptionConstants
+import com.maddyhome.idea.vim.state.mode.ReturnTo
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.isInsertionAllowed
+import com.maddyhome.idea.vim.state.mode.inSelectMode
+import com.maddyhome.idea.vim.state.mode.inVisualMode
 
 /**
  * @author Alex Plate
@@ -58,10 +60,10 @@ public abstract class ShiftedSpecialKeyHandler : VimActionHandler.ConditionalMul
     val startSel = injector.globalOptions().keymodel.contains(OptionConstants.keymodel_startsel)
     if (startSel && !editor.inVisualMode && !editor.inSelectMode) {
       if (injector.globalOptions().selectmode.contains(OptionConstants.selectmode_key)) {
-        injector.visualMotionGroup.enterSelectMode(editor, VimStateMachine.SubMode.VISUAL_CHARACTER)
+        injector.visualMotionGroup.enterSelectMode(editor, SelectionType.CHARACTER_WISE)
       } else {
         injector.visualMotionGroup
-          .toggleVisual(editor, 1, 0, VimStateMachine.SubMode.VISUAL_CHARACTER)
+          .toggleVisual(editor, 1, 0, SelectionType.CHARACTER_WISE)
       }
     }
     return true
@@ -93,10 +95,15 @@ public abstract class ShiftedArrowKeyHandler(private val runBothCommandsAsMultic
     if (withKey) {
       if (!inVisualMode && !inSelectMode) {
         if (injector.globalOptions().selectmode.contains(OptionConstants.selectmode_key)) {
-          injector.visualMotionGroup.enterSelectMode(editor, VimStateMachine.SubMode.VISUAL_CHARACTER)
+          injector.visualMotionGroup.enterSelectMode(editor, SelectionType.CHARACTER_WISE)
         } else {
-          injector.visualMotionGroup
-            .toggleVisual(editor, 1, 0, VimStateMachine.SubMode.VISUAL_CHARACTER)
+          if (editor.isInsertionAllowed) {
+            injector.visualMotionGroup
+              .toggleVisual(editor, 1, 0, SelectionType.CHARACTER_WISE, ReturnTo.INSERT)
+          } else {
+            injector.visualMotionGroup
+              .toggleVisual(editor, 1, 0, SelectionType.CHARACTER_WISE)
+          }
         }
       }
       return true
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/TextObjectActionHandler.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/TextObjectActionHandler.kt
index 6010e8040..856d81482 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/TextObjectActionHandler.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/TextObjectActionHandler.kt
@@ -16,13 +16,14 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.OperatorArguments
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.command.TextObjectVisualType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.selectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.group.visual.vimSetSelection
 import com.maddyhome.idea.vim.helper.endOffsetInclusive
-import com.maddyhome.idea.vim.helper.inVisualMode
-import com.maddyhome.idea.vim.helper.subMode
+import com.maddyhome.idea.vim.state.mode.inVisualMode
+import com.maddyhome.idea.vim.state.mode.mode
 
 /**
  * @author Alex Plate
@@ -75,10 +76,10 @@ public abstract class TextObjectActionHandler : EditorActionHandlerBase(true) {
       caret.vimSetSelection(newstart, newstart, false)
     }
 
-    if (visualType == TextObjectVisualType.LINE_WISE && editor.subMode != VimStateMachine.SubMode.VISUAL_LINE) {
-      injector.visualMotionGroup.toggleVisual(editor, 1, 0, VimStateMachine.SubMode.VISUAL_LINE)
-    } else if (visualType != TextObjectVisualType.LINE_WISE && editor.subMode == VimStateMachine.SubMode.VISUAL_LINE) {
-      injector.visualMotionGroup.toggleVisual(editor, 1, 0, VimStateMachine.SubMode.VISUAL_CHARACTER)
+    if (visualType == TextObjectVisualType.LINE_WISE && editor.mode.selectionType != SelectionType.LINE_WISE) {
+      injector.visualMotionGroup.toggleVisual(editor, 1, 0, SelectionType.LINE_WISE)
+    } else if (visualType != TextObjectVisualType.LINE_WISE && editor.mode.selectionType == SelectionType.LINE_WISE) {
+      injector.visualMotionGroup.toggleVisual(editor, 1, 0, SelectionType.CHARACTER_WISE)
     }
 
     caret.moveToOffset(newend)
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/VisualOperatorActionHandler.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/VisualOperatorActionHandler.kt
index 32dba0bd9..621818f78 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/VisualOperatorActionHandler.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/VisualOperatorActionHandler.kt
@@ -17,7 +17,6 @@ import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Command
 import com.maddyhome.idea.vim.command.CommandFlags
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
 import com.maddyhome.idea.vim.diagnostic.debug
 import com.maddyhome.idea.vim.diagnostic.vimLogger
 import com.maddyhome.idea.vim.group.visual.VimBlockSelection
@@ -26,10 +25,13 @@ import com.maddyhome.idea.vim.group.visual.VimSimpleSelection
 import com.maddyhome.idea.vim.group.visual.VisualChange
 import com.maddyhome.idea.vim.group.visual.VisualOperation
 import com.maddyhome.idea.vim.helper.exitVisualMode
-import com.maddyhome.idea.vim.helper.inBlockSubMode
 import com.maddyhome.idea.vim.helper.inRepeatMode
-import com.maddyhome.idea.vim.helper.inVisualMode
 import com.maddyhome.idea.vim.helper.vimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
+import com.maddyhome.idea.vim.state.mode.inBlockSelection
+import com.maddyhome.idea.vim.state.mode.inVisualMode
+import com.maddyhome.idea.vim.state.mode.selectionType
 
 /**
  * @author Alex Plate
@@ -198,7 +200,7 @@ public sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false)
           carets.toMap()
         }
       }
-      this.inBlockSubMode -> {
+      this.inBlockSelection -> {
         val primaryCaret = primaryCaret()
         mapOf(
           primaryCaret to VimBlockSelection(
@@ -210,13 +212,13 @@ public sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false)
         )
       }
       else -> this.nativeCarets().associateWith { caret ->
-        val subMode = this.vimStateMachine.subMode
+        val mode = this.vimStateMachine.mode
         VimSimpleSelection.createWithNative(
           caret.vimSelectionStart,
           caret.offset.point,
           caret.selectionStart,
           caret.selectionEnd,
-          SelectionType.fromSubMode(subMode),
+          mode.selectionType ?: CHARACTER_WISE,
           this,
         )
       }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/helper/EngineHelper.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/helper/EngineHelper.kt
index e20c58ab1..d135f5da9 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/helper/EngineHelper.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/helper/EngineHelper.kt
@@ -11,9 +11,14 @@ package com.maddyhome.idea.vim.helper
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.api.options
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.VimStateMachine
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.options.OptionConstants
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.isSingleModeActive
+import com.maddyhome.idea.vim.state.mode.mode
+import com.maddyhome.idea.vim.state.mode.returnTo
 import java.util.*
 
 public inline fun <reified T : Enum<T>> noneOfEnum(): EnumSet<T> = EnumSet.noneOf(T::class.java)
@@ -21,109 +26,38 @@ public inline fun <reified T : Enum<T>> noneOfEnum(): EnumSet<T> = EnumSet.noneO
 public val TextRange.endOffsetInclusive: Int
   get() = if (this.endOffset > 0 && this.endOffset > this.startOffset) this.endOffset - 1 else this.endOffset
 
-public val VimEditor.mode: VimStateMachine.Mode
-  get() = this.vimStateMachine.mode
-
-public val VimEditor.inVisualMode: Boolean
-  get() = this.mode.inVisualMode
-
 public val VimEditor.inRepeatMode: Boolean
   get() = this.vimStateMachine.isDotRepeatInProgress
 
-public var VimEditor.subMode: VimStateMachine.SubMode
-  get() = this.vimStateMachine.subMode
-  set(value) {
-    this.vimStateMachine.subMode = value
-  }
-
 public val VimEditor.vimStateMachine: VimStateMachine
   get() = VimStateMachine.getInstance(this)
 
-public val VimStateMachine.Mode.inVisualMode: Boolean
-  get() = this == VimStateMachine.Mode.VISUAL || this == VimStateMachine.Mode.INSERT_VISUAL
-
-public val VimEditor.inBlockSubMode: Boolean
-  get() = this.subMode == VimStateMachine.SubMode.VISUAL_BLOCK
-
 public val VimEditor.usesVirtualSpace: Boolean
   get() = injector.options(this).virtualedit.contains(OptionConstants.virtualedit_onemore)
 
 public val VimEditor.isEndAllowed: Boolean
   get() = this.isEndAllowed(this.mode)
 
-public fun VimEditor.isEndAllowed(mode: VimStateMachine.Mode): Boolean {
+public fun VimEditor.isEndAllowed(mode: Mode): Boolean {
   return when (mode) {
-    VimStateMachine.Mode.INSERT, VimStateMachine.Mode.VISUAL, VimStateMachine.Mode.SELECT, VimStateMachine.Mode.INSERT_VISUAL, VimStateMachine.Mode.INSERT_SELECT -> true
-    VimStateMachine.Mode.COMMAND, VimStateMachine.Mode.CMD_LINE, VimStateMachine.Mode.REPLACE, VimStateMachine.Mode.OP_PENDING, VimStateMachine.Mode.INSERT_NORMAL -> {
+    is Mode.INSERT, is Mode.VISUAL, is Mode.SELECT -> true
+    is Mode.NORMAL, Mode.CMD_LINE, Mode.REPLACE, is Mode.OP_PENDING -> {
       // One day we'll use a proper insert_normal mode
-      if (mode.inSingleMode) true else usesVirtualSpace
+      if (mode.isSingleModeActive) true else usesVirtualSpace
     }
   }
 }
 
-public val VimStateMachine.Mode.inSingleMode: Boolean
-  get() = when (this) {
-    VimStateMachine.Mode.INSERT_NORMAL, VimStateMachine.Mode.INSERT_SELECT, VimStateMachine.Mode.INSERT_VISUAL -> true
-    else -> false
-  }
-
-public val VimStateMachine.Mode.inInsertMode: Boolean
-  get() = this == VimStateMachine.Mode.INSERT || this == VimStateMachine.Mode.REPLACE
-
-public val VimStateMachine.Mode.inSingleNormalMode: Boolean
-  get() = when (this) {
-    VimStateMachine.Mode.INSERT_NORMAL -> true
-    else -> false
-  }
-
-public val VimEditor.inNormalMode: Boolean
-  get() = this.mode.inNormalMode
-
-public val VimStateMachine.Mode.inNormalMode: Boolean
-  get() = this == VimStateMachine.Mode.COMMAND || this == VimStateMachine.Mode.INSERT_NORMAL
-
-public val VimStateMachine.Mode.isEndAllowedIgnoringOnemore: Boolean
-  get() = when (this) {
-    VimStateMachine.Mode.INSERT, VimStateMachine.Mode.VISUAL, VimStateMachine.Mode.SELECT -> true
-    VimStateMachine.Mode.COMMAND, VimStateMachine.Mode.CMD_LINE, VimStateMachine.Mode.REPLACE, VimStateMachine.Mode.OP_PENDING -> false
-    VimStateMachine.Mode.INSERT_NORMAL -> false
-    VimStateMachine.Mode.INSERT_VISUAL -> true
-    VimStateMachine.Mode.INSERT_SELECT -> true
-  }
-
-public val VimEditor.inInsertMode: Boolean
-  get() = this.mode.inInsertMode
-
-public val VimEditor.inSelectMode: Boolean
-  get() = this.mode == VimStateMachine.Mode.SELECT || this.mode == VimStateMachine.Mode.INSERT_SELECT
-
-public val VimEditor.inSingleCommandMode: Boolean
-  get() = this.mode.inSingleMode
-
 public inline fun <reified T : Enum<T>> enumSetOf(vararg value: T): EnumSet<T> = when (value.size) {
   0 -> noneOfEnum()
   1 -> EnumSet.of(value[0])
   else -> EnumSet.of(value[0], *value.slice(1..value.lastIndex).toTypedArray())
 }
 
-public fun VimStateMachine.pushSelectMode(subMode: VimStateMachine.SubMode, prevMode: VimStateMachine.Mode = this.mode) {
-  if (prevMode.inSingleMode) {
-    popModes()
-    pushModes(VimStateMachine.Mode.INSERT_SELECT, subMode)
-  } else {
-    pushModes(VimStateMachine.Mode.SELECT, subMode)
-  }
+public fun VimStateMachine.setSelectMode(submode: SelectionType) {
+  mode = Mode.SELECT(submode, this.mode.returnTo)
 }
 
-public fun VimStateMachine.pushVisualMode(subMode: VimStateMachine.SubMode, prevMode: VimStateMachine.Mode = this.mode) {
-  if (prevMode.inSingleMode) {
-    popModes()
-    pushModes(VimStateMachine.Mode.INSERT_VISUAL, subMode)
-  } else {
-    pushModes(VimStateMachine.Mode.VISUAL, subMode)
-  }
-}
-
-public fun <K, V> Map<K, V>.firstOrNull(): Map.Entry<K, V>? {
-  return this.entries.firstOrNull()
+public fun VimStateMachine.pushVisualMode(submode: SelectionType) {
+  mode = Mode.VISUAL(submode, this.mode.returnTo)
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/helper/EngineModeExtensions.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/helper/EngineModeExtensions.kt
index 3b932b36d..425dde8be 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/helper/EngineModeExtensions.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/helper/EngineModeExtensions.kt
@@ -11,13 +11,20 @@ package com.maddyhome.idea.vim.helper
 import com.maddyhome.idea.vim.api.VimCaret
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.SelectionType
 import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.ReturnTo
+import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
+import com.maddyhome.idea.vim.state.mode.inBlockSelection
+import com.maddyhome.idea.vim.state.mode.inVisualMode
+import com.maddyhome.idea.vim.state.mode.mode
+import com.maddyhome.idea.vim.state.mode.returnTo
+import com.maddyhome.idea.vim.state.mode.selectionType
 
 public fun VimEditor.exitVisualMode() {
-  val selectionType = SelectionType.fromSubMode(this.subMode)
+  val selectionType = this.mode.selectionType ?: CHARACTER_WISE
   SelectionVimListenerSuppressor.lock().use {
-    if (inBlockSubMode) {
+    if (inBlockSelection) {
       this.removeSecondaryCarets()
     }
     if (!this.vimKeepingVisualOperatorAction) {
@@ -29,6 +36,19 @@ public fun VimEditor.exitVisualMode() {
     injector.markService.setVisualSelectionMarks(this)
     this.nativeCarets().forEach { it.vimSelectionStartClear() }
 
-    this.vimStateMachine.popModes()
+    val returnTo = this.vimStateMachine.mode.returnTo
+    when (returnTo) {
+      ReturnTo.INSERT -> {
+        this.vimStateMachine.mode = Mode.INSERT
+      }
+
+      ReturnTo.REPLACE -> {
+        this.vimStateMachine.mode = Mode.REPLACE
+      }
+
+      null -> {
+        this.vimStateMachine.mode = Mode.NORMAL()
+      }
+    }
   }
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/impl/state/VimStateMachineImpl.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/impl/state/VimStateMachineImpl.kt
new file mode 100644
index 000000000..e121d0ee4
--- /dev/null
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/impl/state/VimStateMachineImpl.kt
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2003-2023 The IdeaVim authors
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE.txt file or at
+ * https://opensource.org/licenses/MIT.
+ */
+package com.maddyhome.idea.vim.impl.state
+
+import com.maddyhome.idea.vim.api.VimActionsInitiator
+import com.maddyhome.idea.vim.api.VimEditor
+import com.maddyhome.idea.vim.api.globalOptions
+import com.maddyhome.idea.vim.api.injector
+import com.maddyhome.idea.vim.command.Command
+import com.maddyhome.idea.vim.command.CommandBuilder
+import com.maddyhome.idea.vim.command.CommandFlags
+import com.maddyhome.idea.vim.command.MappingMode
+import com.maddyhome.idea.vim.command.MappingState
+import com.maddyhome.idea.vim.common.DigraphResult
+import com.maddyhome.idea.vim.common.DigraphSequence
+import com.maddyhome.idea.vim.diagnostic.vimLogger
+import com.maddyhome.idea.vim.helper.noneOfEnum
+import com.maddyhome.idea.vim.key.CommandPartNode
+import com.maddyhome.idea.vim.state.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.ReturnTo
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.returnTo
+import org.jetbrains.annotations.Contract
+import java.util.*
+import javax.swing.KeyStroke
+
+/**
+ * Used to maintain state before and while entering a Vim command (operator, motion, text object, etc.)
+ *
+ * // TODO: 21.02.2022 This constructor should be empty
+ */
+public class VimStateMachineImpl(private val editor: VimEditor?) : VimStateMachine {
+  override val commandBuilder: CommandBuilder = CommandBuilder(getKeyRootNode(MappingMode.NORMAL))
+  override var mode: Mode = Mode.NORMAL()
+    set(value) {
+      if (field == value) return
+
+      field = value
+      setMappingMode()
+      onModeChanged()
+    }
+  override val mappingState: MappingState = MappingState()
+  override val digraphSequence: DigraphSequence = DigraphSequence()
+  override var isRecording: Boolean = false
+    set(value) {
+      field = value
+      doShowMode()
+    }
+  override var isDotRepeatInProgress: Boolean = false
+  override var isRegisterPending: Boolean = false
+  override var isReplaceCharacter: Boolean = false
+    set(value) {
+      field = value
+      onModeChanged()
+    }
+
+  /**
+   * The currently executing command
+   *
+   * This is a complete command, e.g. operator + motion. Some actions/helpers require additional context from flags in
+   * the command/argument. Ideally, we would pass the command through KeyHandler#executeVimAction and
+   * EditorActionHandlerBase#execute, but we also need to know the command type in MarkGroup#updateMarkFromDelete,
+   * which is called via a document change event.
+   *
+   * This field is reset after the command has been executed.
+   */
+  override var executingCommand: Command? = null
+
+  override val isOperatorPending: Boolean
+    get() = mappingState.mappingMode == MappingMode.OP_PENDING && !commandBuilder.isEmpty
+
+  override fun isDuplicateOperatorKeyStroke(key: KeyStroke?): Boolean {
+    return isOperatorPending && commandBuilder.isDuplicateOperatorKeyStroke(key!!)
+  }
+
+  override val executingCommandFlags: EnumSet<CommandFlags>
+    get() = executingCommand?.flags ?: noneOfEnum()
+
+  override fun resetOpPending() {
+    if (this.mode is Mode.OP_PENDING) {
+      val returnTo = this.mode.returnTo
+      mode = when (returnTo) {
+        ReturnTo.INSERT -> Mode.INSERT
+        ReturnTo.REPLACE -> Mode.INSERT
+        null -> Mode.NORMAL()
+      }
+    }
+  }
+
+  override fun resetReplaceCharacter() {
+    if (isReplaceCharacter) {
+      isReplaceCharacter = false
+    }
+  }
+
+  override fun resetRegisterPending() {
+    if (isRegisterPending) {
+      isRegisterPending = false
+    }
+  }
+
+  private fun resetModes() {
+//    modeStates.clear()
+    mode = Mode.NORMAL()
+    onModeChanged()
+    setMappingMode()
+  }
+
+  private fun onModeChanged() {
+    if (editor != null) {
+      editor.updateCaretsVisualAttributes()
+      editor.updateCaretsVisualPosition()
+    } else {
+      injector.application.localEditors().forEach { editor ->
+        editor.updateCaretsVisualAttributes()
+        editor.updateCaretsVisualPosition()
+      }
+    }
+    doShowMode()
+  }
+
+  private fun setMappingMode() {
+    mappingState.mappingMode = modeToMappingMode(this.mode)
+  }
+
+  override fun startDigraphSequence() {
+    digraphSequence.startDigraphSequence()
+  }
+
+  override fun startLiteralSequence() {
+    digraphSequence.startLiteralSequence()
+  }
+
+  override fun processDigraphKey(key: KeyStroke, editor: VimEditor): DigraphResult {
+    return digraphSequence.processKey(key, editor)
+  }
+
+  override fun resetDigraph() {
+    digraphSequence.reset()
+  }
+
+  /**
+   * Toggles the insert/overwrite state. If currently insert, goto replace mode. If currently replace, goto insert
+   * mode.
+   */
+  override fun toggleInsertOverwrite() {
+    val oldMode = this.mode
+    var newMode = oldMode
+    if (oldMode == Mode.INSERT) {
+      newMode = Mode.REPLACE
+    } else if (oldMode == Mode.REPLACE) {
+      newMode = Mode.INSERT
+    }
+    if (oldMode != newMode) {
+      mode = newMode
+    }
+  }
+
+  /**
+   * Resets the command, mode, visual mode, and mapping mode to initial values.
+   */
+  override fun reset() {
+    executingCommand = null
+    resetModes()
+    commandBuilder.resetInProgressCommandPart(getKeyRootNode(mappingState.mappingMode))
+    digraphSequence.reset()
+  }
+
+  private fun doShowMode() {
+    val msg = StringBuilder()
+    if (injector.globalOptions().showmode) {
+      msg.append(getStatusString())
+    }
+    if (isRecording) {
+      if (msg.isNotEmpty()) {
+        msg.append(" - ")
+      }
+      msg.append(injector.messages.message("show.mode.recording"))
+    }
+    injector.messages.showMode(editor, msg.toString())
+  }
+
+  override fun getStatusString(): String {
+    val modeState = this.mode
+    return buildString {
+      when (modeState) {
+        is Mode.NORMAL -> {
+          if (modeState.returnTo != null) append("-- (insert) --")
+        }
+
+        Mode.INSERT -> append("-- INSERT --")
+        Mode.REPLACE -> append("-- REPLACE --")
+        is Mode.VISUAL -> {
+          val inInsert = if (modeState.returnTo != null) "(insert) " else ""
+          append("-- ${inInsert}VISUAL")
+          when (modeState.selectionType) {
+            SelectionType.LINE_WISE -> append(" LINE")
+            SelectionType.BLOCK_WISE -> append(" BLOCK")
+            else -> Unit
+          }
+          append(" --")
+        }
+
+        is Mode.SELECT -> {
+          val inInsert = if (modeState.returnTo != null) "(insert) " else ""
+          append("-- ${inInsert}SELECT")
+          when (modeState.selectionType) {
+            SelectionType.LINE_WISE -> append(" LINE")
+            SelectionType.BLOCK_WISE -> append(" BLOCK")
+            else -> Unit
+          }
+          append(" --")
+        }
+
+        else -> Unit
+      }
+    }
+  }
+
+  public companion object {
+    private val logger = vimLogger<VimStateMachine>()
+
+    private fun getKeyRootNode(mappingMode: MappingMode): CommandPartNode<VimActionsInitiator> {
+      return injector.keyGroup.getKeyRoot(mappingMode)
+    }
+
+    @Contract(pure = true)
+    public fun modeToMappingMode(mode: Mode): MappingMode {
+      return when (mode) {
+        is Mode.NORMAL -> MappingMode.NORMAL
+        Mode.INSERT, Mode.REPLACE -> MappingMode.INSERT
+        is Mode.VISUAL -> MappingMode.VISUAL
+        is Mode.SELECT -> MappingMode.SELECT
+        Mode.CMD_LINE -> MappingMode.CMD_LINE
+        is Mode.OP_PENDING -> MappingMode.OP_PENDING
+      }
+    }
+  }
+}
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/key/MappingInfo.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/key/MappingInfo.kt
index f1f4e4ecd..671f23556 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/key/MappingInfo.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/key/MappingInfo.kt
@@ -16,10 +16,12 @@ import com.maddyhome.idea.vim.api.ImmutableVimCaret
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Argument
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.SelectionType.Companion.fromSubMode
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
+import com.maddyhome.idea.vim.state.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.selectionType
 import com.maddyhome.idea.vim.common.Offset
 import com.maddyhome.idea.vim.common.argumentCaptured
 import com.maddyhome.idea.vim.common.offset
@@ -28,7 +30,7 @@ import com.maddyhome.idea.vim.extension.ExtensionHandler
 import com.maddyhome.idea.vim.group.visual.VimSelection
 import com.maddyhome.idea.vim.group.visual.VimSelection.Companion.create
 import com.maddyhome.idea.vim.helper.VimNlsSafe
-import com.maddyhome.idea.vim.helper.subMode
+import com.maddyhome.idea.vim.state.mode.mode
 import com.maddyhome.idea.vim.helper.vimStateMachine
 import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
 import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
@@ -177,7 +179,7 @@ public class ToHandlerMappingInfo(
       }
     }
 
-    val operatorArguments = OperatorArguments(vimStateMachine.isOperatorPending, vimStateMachine.commandBuilder.count, vimStateMachine.mode, vimStateMachine.subMode)
+    val operatorArguments = OperatorArguments(vimStateMachine.isOperatorPending, vimStateMachine.commandBuilder.count, vimStateMachine.mode)
     injector.actionExecutor.executeCommand(
       editor,
       { extensionHandler.execute(editor, context, operatorArguments) },
@@ -210,9 +212,10 @@ public class ToHandlerMappingInfo(
         for (caret in editor.carets()) {
           var startOffset = startOffsets[caret]
           if (caret.hasSelection()) {
-            val vimSelection = create(caret.vimSelectionStart, caret.offset.point, fromSubMode(editor.subMode), editor)
+            val vimSelection =
+              create(caret.vimSelectionStart, caret.offset.point, editor.mode.selectionType ?: CHARACTER_WISE, editor)
             offsets[caret] = vimSelection
-            commandState.popModes()
+            commandState.mode = Mode.NORMAL()
           } else if (startOffset != null && startOffset.point != caret.offset.point) {
             // Command line motions are always characterwise exclusive
             var endOffset = caret.offset
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/key/OperatorFunction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/key/OperatorFunction.kt
index 900112915..2f7fd0f64 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/key/OperatorFunction.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/key/OperatorFunction.kt
@@ -9,7 +9,7 @@ package com.maddyhome.idea.vim.key
 
 import com.maddyhome.idea.vim.api.ExecutionContext
 import com.maddyhome.idea.vim.api.VimEditor
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 
 /**
  * @author vlan
@@ -21,7 +21,7 @@ public interface OperatorFunction {
    *
    * Make sure to synchronize your function properly using read/write actions.
    */
-  public fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean
+  public fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean
 
   public fun postProcessSelection(): Boolean = true
 }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/key/ShortcutOwner.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/key/ShortcutOwner.kt
index f1f73f06b..c0f4e7758 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/key/ShortcutOwner.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/key/ShortcutOwner.kt
@@ -8,8 +8,8 @@
 package com.maddyhome.idea.vim.key
 
 import com.maddyhome.idea.vim.api.VimEditor
-import com.maddyhome.idea.vim.command.VimStateMachine
-import com.maddyhome.idea.vim.helper.mode
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.mode
 import org.jetbrains.annotations.NonNls
 
 public sealed class ShortcutOwnerInfo {
@@ -73,16 +73,13 @@ public sealed class ShortcutOwnerInfo {
     return when (this) {
       is AllModes -> this.owner
       is PerMode -> when (editor.mode) {
-        VimStateMachine.Mode.COMMAND -> this.normal
-        VimStateMachine.Mode.VISUAL -> this.visual
-        VimStateMachine.Mode.SELECT -> this.visual
-        VimStateMachine.Mode.INSERT -> this.insert
-        VimStateMachine.Mode.CMD_LINE -> this.normal
-        VimStateMachine.Mode.OP_PENDING -> this.normal
-        VimStateMachine.Mode.REPLACE -> this.insert
-        VimStateMachine.Mode.INSERT_NORMAL -> this.normal
-        VimStateMachine.Mode.INSERT_VISUAL -> this.visual
-        VimStateMachine.Mode.INSERT_SELECT -> this.select
+        is Mode.NORMAL -> this.normal
+        is Mode.VISUAL -> this.visual
+        is Mode.SELECT -> this.visual
+        Mode.INSERT -> this.insert
+        Mode.CMD_LINE -> this.normal
+        is Mode.OP_PENDING -> this.normal
+        Mode.REPLACE -> this.insert
       }
     }
   }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/options/helpers/GuiCursorOptionHelper.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/options/helpers/GuiCursorOptionHelper.kt
index a7d9ca4c7..4a4c78d9a 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/options/helpers/GuiCursorOptionHelper.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/options/helpers/GuiCursorOptionHelper.kt
@@ -10,7 +10,7 @@ package com.maddyhome.idea.vim.options.helpers
 
 import com.maddyhome.idea.vim.api.Options
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.ex.exExceptionMessage
 import com.maddyhome.idea.vim.helper.enumSetOf
 import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
@@ -156,26 +156,26 @@ public enum class GuiCursorMode(public val token: String) {
   public companion object {
     public fun fromString(s: String): GuiCursorMode? = values().firstOrNull { it.token == s }
 
-    // Used in FleetVim
-    @Suppress("unused")
-    public fun fromMode(mode: VimStateMachine.Mode, isReplaceCharacter: Boolean): GuiCursorMode {
+    // Also used in FleetVim as direct call
+    public fun fromMode(mode: Mode, isReplaceCharacter: Boolean): GuiCursorMode {
       if (isReplaceCharacter) {
         // Can be true for NORMAL and VISUAL
-        return REPLACE
+        return GuiCursorMode.REPLACE
       }
 
+      // Note that Vim does not change the caret for SELECT mode and continues to use VISUAL or VISUAL_EXCLUSIVE. IdeaVim
+      // makes much more use of SELECT than Vim does (e.g. it's the default for idearefactormode) so it makes sense for us
+      // to more visually distinguish VISUAL and SELECT. So we use INSERT; a selection and the insert caret is intuitively
+      // the same as SELECT
       return when (mode) {
-        VimStateMachine.Mode.COMMAND -> NORMAL
-        VimStateMachine.Mode.VISUAL -> VISUAL // TODO: VISUAL_EXCLUSIVE
-        VimStateMachine.Mode.SELECT -> VISUAL
-        VimStateMachine.Mode.INSERT -> INSERT
-        VimStateMachine.Mode.OP_PENDING -> OP_PENDING
-        VimStateMachine.Mode.REPLACE -> REPLACE
-        // TODO: ci and cr
-        VimStateMachine.Mode.CMD_LINE -> CMD_LINE
-        VimStateMachine.Mode.INSERT_NORMAL -> NORMAL
-        VimStateMachine.Mode.INSERT_VISUAL -> VISUAL
-        VimStateMachine.Mode.INSERT_SELECT -> INSERT
+        is Mode.NORMAL -> GuiCursorMode.NORMAL
+        is Mode.OP_PENDING -> GuiCursorMode.OP_PENDING
+        Mode.INSERT -> GuiCursorMode.INSERT
+        Mode.REPLACE -> GuiCursorMode.REPLACE
+        is Mode.SELECT -> GuiCursorMode.INSERT
+        is Mode.VISUAL -> GuiCursorMode.VISUAL // TODO: VISUAL_EXCLUSIVE
+        // This doesn't handle ci and cr, but we don't care - our CMD_LINE will never call this
+        Mode.CMD_LINE -> GuiCursorMode.CMD_LINE
       }
     }
   }
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/ProcessedTextData.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/ProcessedTextData.kt
index c323f1481..765a92c4f 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/ProcessedTextData.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/ProcessedTextData.kt
@@ -8,7 +8,7 @@
 
 package com.maddyhome.idea.vim.put
 
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 
 public data class ProcessedTextData(
   val text: String,
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/PutData.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/PutData.kt
index 4999d2a3e..23d8ff735 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/PutData.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/PutData.kt
@@ -9,7 +9,7 @@
 package com.maddyhome.idea.vim.put
 
 import com.maddyhome.idea.vim.api.VimCaret
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.group.visual.VimSelection
 
 /**
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/VimPut.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/VimPut.kt
index ada14a9c9..510371b02 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/VimPut.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/VimPut.kt
@@ -12,8 +12,7 @@ import com.maddyhome.idea.vim.api.ExecutionContext
 import com.maddyhome.idea.vim.api.VimCaret
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.helper.RWLockLabel
 
 public interface VimPut {
@@ -49,7 +48,7 @@ public interface VimPut {
     vimEditor: VimEditor,
     vimContext: ExecutionContext,
     text: ProcessedTextData,
-    subMode: VimStateMachine.SubMode,
+    subMode: SelectionType,
     data: PutData,
     additionalData: Map<String, Any>,
   )
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/VimPutBase.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/VimPutBase.kt
index 2c30b3755..b9fb6bf28 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/VimPutBase.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/put/VimPutBase.kt
@@ -25,22 +25,20 @@ import com.maddyhome.idea.vim.api.moveToMotion
 import com.maddyhome.idea.vim.api.setChangeMarks
 import com.maddyhome.idea.vim.api.setVisualSelectionMarks
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
-import com.maddyhome.idea.vim.command.isBlock
-import com.maddyhome.idea.vim.command.isChar
-import com.maddyhome.idea.vim.command.isLine
 import com.maddyhome.idea.vim.common.Offset
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.common.offset
 import com.maddyhome.idea.vim.diagnostic.VimLogger
 import com.maddyhome.idea.vim.diagnostic.vimLogger
 import com.maddyhome.idea.vim.helper.RWLockLabel
-import com.maddyhome.idea.vim.helper.firstOrNull
-import com.maddyhome.idea.vim.helper.mode
-import com.maddyhome.idea.vim.helper.subMode
 import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_POS
 import com.maddyhome.idea.vim.options.OptionConstants
+import com.maddyhome.idea.vim.state.mode.Mode
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.mode.isBlock
+import com.maddyhome.idea.vim.state.mode.isChar
+import com.maddyhome.idea.vim.state.mode.isLine
+import com.maddyhome.idea.vim.state.mode.mode
 import java.util.*
 import kotlin.math.abs
 import kotlin.math.max
@@ -94,7 +92,7 @@ public abstract class VimPutBase : VimPut {
   private fun wrapInsertedTextWithVisualMarks(caret: VimCaret, data: PutData, text: ProcessedTextData) {
     val textLength: Int = data.textData?.rawText?.length ?: return
     val caretsAndSelections = data.visualSelection?.caretsAndSelections ?: return
-    val selection = caretsAndSelections[caret] ?: caretsAndSelections.firstOrNull()?.value ?: return
+    val selection = caretsAndSelections[caret] ?: caretsAndSelections.entries.firstOrNull()?.value ?: return
 
     val leftIndex = min(selection.vimStart, selection.vimEnd)
     val rightIndex = leftIndex + textLength - 1
@@ -153,17 +151,17 @@ public abstract class VimPutBase : VimPut {
     startOffset: Int,
     endOffset: Int,
     typeInRegister: SelectionType,
-    modeInEditor: VimStateMachine.SubMode,
+    modeInEditor: SelectionType,
     caretAfterInsertedText: Boolean,
   ): VimCaret {
     val cursorMode = when (typeInRegister) {
       SelectionType.BLOCK_WISE -> when (modeInEditor) {
-        VimStateMachine.SubMode.VISUAL_LINE -> if (caretAfterInsertedText) "postEndOffset" else "startOffset"
+        SelectionType.LINE_WISE -> if (caretAfterInsertedText) "postEndOffset" else "startOffset"
         else -> if (caretAfterInsertedText) "preLineEndOfEndOffset" else "startOffset"
       }
       SelectionType.LINE_WISE -> if (caretAfterInsertedText) "postEndOffset" else "startOffsetSkipLeading"
       SelectionType.CHARACTER_WISE -> when (modeInEditor) {
-        VimStateMachine.SubMode.VISUAL_LINE -> if (caretAfterInsertedText) "postEndOffset" else "startOffset"
+        SelectionType.LINE_WISE -> if (caretAfterInsertedText) "postEndOffset" else "startOffset"
         else -> if (caretAfterInsertedText) "preLineEndOfEndOffset" else "preEndOffset"
       }
     }
@@ -178,7 +176,7 @@ public abstract class VimPutBase : VimPut {
       "postEndOffset" -> caret.moveToOffset(endOffset + 1)
       "preLineEndOfEndOffset" -> {
         var rightestPosition = editor.getLineEndForOffset(endOffset - 1)
-        if (editor.mode != VimStateMachine.Mode.INSERT) --rightestPosition // it's not possible to place a caret at the end of the line in any mode except insert
+        if (editor.mode !is Mode.INSERT) --rightestPosition // it's not possible to place a caret at the end of the line in any mode except insert
         val pos = min(endOffset, rightestPosition)
         caret.moveToOffset(pos)
       }
@@ -202,7 +200,7 @@ public abstract class VimPutBase : VimPut {
     context: ExecutionContext,
     text: String,
     type: SelectionType,
-    mode: VimStateMachine.SubMode,
+    mode: SelectionType,
     startOffset: Int,
     count: Int,
     indent: Boolean,
@@ -228,7 +226,7 @@ public abstract class VimPutBase : VimPut {
     context: ExecutionContext,
     text: String,
     type: SelectionType,
-    mode: VimStateMachine.SubMode,
+    mode: SelectionType,
     startOffset: Int,
     count: Int,
     indent: Boolean,
@@ -274,14 +272,14 @@ public abstract class VimPutBase : VimPut {
     context: ExecutionContext,
     text: String,
     type: SelectionType,
-    mode: VimStateMachine.SubMode,
+    mode: SelectionType,
     startOffset: Int,
     count: Int,
     indent: Boolean,
     cursorAfter: Boolean,
   ): Pair<Int, VimCaret> {
     val startPosition = editor.offsetToBufferPosition(startOffset)
-    val currentColumn = if (mode == VimStateMachine.SubMode.VISUAL_LINE) 0 else startPosition.column
+    val currentColumn = if (mode == SelectionType.LINE_WISE) 0 else startPosition.column
     var currentLine = startPosition.line
 
     val lineCount = text.getLineBreakCount() + 1
@@ -317,7 +315,7 @@ public abstract class VimPutBase : VimPut {
       updated = injector.changeGroup.insertText(editor, updated, insertedText)
       endOffset += insertedText.length
 
-      if (mode == VimStateMachine.SubMode.VISUAL_LINE) {
+      if (mode == SelectionType.LINE_WISE) {
         updated = updated.moveToOffset(endOffset)
         updated = injector.changeGroup.insertText(editor, updated, "\n")
         ++endOffset
@@ -344,7 +342,7 @@ public abstract class VimPutBase : VimPut {
     context: ExecutionContext,
     text: String,
     type: SelectionType,
-    mode: VimStateMachine.SubMode,
+    mode: SelectionType,
     startOffset: Int,
     count: Int,
     indent: Boolean,
@@ -488,7 +486,7 @@ public abstract class VimPutBase : VimPut {
     val startOffsets = prepareDocumentAndGetStartOffsets(editor, updated, text.typeInRegister, data, additionalData)
 
     startOffsets.forEach { startOffset ->
-      val subMode = data.visualSelection?.typeInEditor?.toSubMode() ?: VimStateMachine.SubMode.NONE
+      val subMode = data.visualSelection?.typeInEditor ?: SelectionType.CHARACTER_WISE
       val (endOffset, updatedCaret) = putTextInternal(
         editor, updated, context, text.text, text.typeInRegister, subMode,
         startOffset, data.count, data.indent, data.caretAfterInsertedText,
@@ -515,7 +513,7 @@ public abstract class VimPutBase : VimPut {
       deleteSelectedText(
         editor,
         data,
-        OperatorArguments(false, 0, editor.mode, editor.subMode),
+        OperatorArguments(false, 0, editor.mode),
         modifyRegister,
       )
     }
@@ -536,7 +534,7 @@ public abstract class VimPutBase : VimPut {
     additionalData: Map<String, Any>,
   ) {
     val visualSelection = data.visualSelection
-    val subMode = visualSelection?.typeInEditor?.toSubMode() ?: VimStateMachine.SubMode.NONE
+    val subMode = visualSelection?.typeInEditor ?: SelectionType.CHARACTER_WISE
     if (injector.globalOptions().clipboard.contains(OptionConstants.clipboard_ideaput)) {
       val idePasteProvider = getProviderForPasteViaIde(editor, text.typeInRegister, data)
       if (idePasteProvider != null) {
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/register/Register.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/register/Register.kt
index c12077b06..5981c57d0 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/register/Register.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/register/Register.kt
@@ -8,7 +8,7 @@
 package com.maddyhome.idea.vim.register
 
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import org.jetbrains.annotations.NonNls
 import java.awt.event.KeyEvent
 import javax.swing.KeyStroke
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/register/VimRegisterGroup.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/register/VimRegisterGroup.kt
index 52af8d169..dabbc7d47 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/register/VimRegisterGroup.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/register/VimRegisterGroup.kt
@@ -9,7 +9,7 @@ package com.maddyhome.idea.vim.register
 
 import com.maddyhome.idea.vim.api.ImmutableVimCaret
 import com.maddyhome.idea.vim.api.VimEditor
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import org.jetbrains.annotations.TestOnly
 import javax.swing.KeyStroke
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/register/VimRegisterGroupBase.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/register/VimRegisterGroupBase.kt
index 4c2a08e93..da3b896bd 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/register/VimRegisterGroupBase.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/register/VimRegisterGroupBase.kt
@@ -14,8 +14,8 @@ import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.getText
 import com.maddyhome.idea.vim.api.globalOptions
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.SelectionType
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.mode.SelectionType
+import com.maddyhome.idea.vim.state.VimStateMachine
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.diagnostic.VimLogger
 import com.maddyhome.idea.vim.diagnostic.debug
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/state/VimStateMachine.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/state/VimStateMachine.kt
new file mode 100644
index 000000000..aa6a6b39a
--- /dev/null
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/state/VimStateMachine.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2003-2023 The IdeaVim authors
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE.txt file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+package com.maddyhome.idea.vim.state
+
+import com.maddyhome.idea.vim.api.VimEditor
+import com.maddyhome.idea.vim.api.globalOptions
+import com.maddyhome.idea.vim.api.injector
+import com.maddyhome.idea.vim.command.Command
+import com.maddyhome.idea.vim.command.CommandBuilder
+import com.maddyhome.idea.vim.command.CommandFlags
+import com.maddyhome.idea.vim.command.MappingState
+import com.maddyhome.idea.vim.common.DigraphResult
+import com.maddyhome.idea.vim.common.DigraphSequence
+import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
+import com.maddyhome.idea.vim.state.mode.Mode
+import java.util.*
+import javax.swing.KeyStroke
+
+/**
+ * Used to maintain state before and while entering a Vim command (operator, motion, text object, etc.)
+ */
+public interface VimStateMachine {
+  public val commandBuilder: CommandBuilder
+  public var mode: Mode
+  public val mappingState: MappingState
+  public val digraphSequence: DigraphSequence
+  public var isRecording: Boolean
+  public var isDotRepeatInProgress: Boolean
+  public var isRegisterPending: Boolean
+  public var isReplaceCharacter: Boolean
+
+  /**
+   * The currently executing command
+   *
+   * This is a complete command, e.g. operator + motion. Some actions/helpers require additional context from flags in
+   * the command/argument. Ideally, we would pass the command through KeyHandler#executeVimAction and
+   * EditorActionHandlerBase#execute, but we also need to know the command type in MarkGroup#updateMarkFromDelete,
+   * which is called via a document change event.
+   *
+   * This field is reset after the command has been executed.
+   */
+  public var executingCommand: Command?
+  public val isOperatorPending: Boolean
+  public val executingCommandFlags: EnumSet<CommandFlags>
+
+  public fun isDuplicateOperatorKeyStroke(key: KeyStroke?): Boolean
+
+  public fun resetOpPending()
+  public fun resetReplaceCharacter()
+  public fun resetRegisterPending()
+  public fun startLiteralSequence()
+  public fun processDigraphKey(key: KeyStroke, editor: VimEditor): DigraphResult
+  public fun resetDigraph()
+
+  /**
+   * Toggles the insert/overwrite state. If currently insert, goto replace mode. If currently replace, goto insert
+   * mode.
+   */
+  public fun toggleInsertOverwrite()
+
+  /**
+   * Resets the command, mode, visual mode, and mapping mode to initial values.
+   */
+  public fun reset()
+  public fun getStatusString(): String
+  public fun startDigraphSequence()
+
+  public companion object {
+    private val globalState = VimStateMachineImpl(null)
+
+    public fun getInstance(editor: Any?): VimStateMachine {
+      return if (editor == null || injector.globalOptions().ideaglobalmode) {
+        globalState
+      } else {
+        injector.commandStateFor(editor)
+      }
+    }
+  }
+}
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/state/mode/Mode.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/state/mode/Mode.kt
new file mode 100644
index 000000000..f4cbacfe1
--- /dev/null
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/state/mode/Mode.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2003-2023 The IdeaVim authors
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE.txt file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+@file:Suppress("ClassName")
+
+package com.maddyhome.idea.vim.state.mode
+
+import com.maddyhome.idea.vim.state.VimStateMachine
+
+/**
+ * Represents a mode in IdeaVim.
+ *
+ * If mode has [returnTo] variable, it can be active during the one-command-mode (':h i_Ctrl-o'). If this value
+ *   is not null, the one-command-mode is active and we should get back to [returnTo] mode.
+ *
+ * Modes with selection have [selectionType] variable representing if the selection is character-, line-, or block-wise.
+ *
+ * To update the current mode, use [VimStateMachine.setMode]. To get the current mode use [VimStateMachine.mode].
+ *
+ * [Mode] also has a bunch of extension functions like [Mode.isSingleModeActive].
+ */
+public sealed interface Mode {
+  public data class NORMAL(public val returnTo: ReturnTo? = null) : Mode
+  public data class OP_PENDING(public val returnTo: ReturnTo? = null, public val forcedVisual: SelectionType? = null) :
+    Mode
+  public data class VISUAL(public val selectionType: SelectionType, public val returnTo: ReturnTo? = null) : Mode
+  public data class SELECT(public val selectionType: SelectionType, public val returnTo: ReturnTo? = null) : Mode
+  public object INSERT : Mode
+  public object REPLACE : Mode
+  public object CMD_LINE : Mode
+}
+
+public sealed interface ReturnTo {
+  public object INSERT : ReturnTo
+  public object REPLACE : ReturnTo
+}
+
+public enum class SelectionType {
+  LINE_WISE,
+  CHARACTER_WISE,
+  BLOCK_WISE,
+}
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/state/mode/editorExtensions.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/state/mode/editorExtensions.kt
new file mode 100644
index 000000000..7711a1a5e
--- /dev/null
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/state/mode/editorExtensions.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2003-2023 The IdeaVim authors
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE.txt file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+package com.maddyhome.idea.vim.state.mode
+
+import com.maddyhome.idea.vim.api.VimEditor
+import com.maddyhome.idea.vim.helper.vimStateMachine
+
+public val VimEditor.mode: Mode
+  get() = this.vimStateMachine.mode
+
+public val VimEditor.inVisualMode: Boolean
+  get() = this.vimStateMachine.mode is Mode.VISUAL
+
+public val VimEditor.inBlockSelection: Boolean
+  get() = this.mode.selectionType == SelectionType.BLOCK_WISE
+
+public val VimEditor.inNormalMode: Boolean
+  get() = this.mode is Mode.NORMAL
+
+public val VimEditor.inSelectMode: Boolean
+  get() = this.mode is Mode.SELECT
+
+public val VimEditor.singleModeActive: Boolean
+  get() = this.mode.isSingleModeActive
+
+/**
+ * Check if text insertion is allowed. It's true in [Mode.INSERT] or [Mode.REPLACE].
+ */
+public val VimEditor.isInsertionAllowed: Boolean
+  get() = this.mode == Mode.INSERT || this.mode == Mode.REPLACE
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/state/mode/modeExtensions.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/state/mode/modeExtensions.kt
new file mode 100644
index 000000000..203c88857
--- /dev/null
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/state/mode/modeExtensions.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2003-2023 The IdeaVim authors
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE.txt file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+package com.maddyhome.idea.vim.state.mode
+
+/**
+ * Get the selection type if the mode is [Mode.VISUAL] or [Mode.SELECT]. Otherwise, returns null.
+ */
+public val Mode.selectionType: SelectionType?
+  get() = when (this) {
+    is Mode.VISUAL -> this.selectionType
+    is Mode.SELECT -> this.selectionType
+    else -> null
+  }
+
+/**
+ * Get the mode that we need to return to if the one-command-mode (':h i_Ctrl-o') is active.
+ * Otherwise, returns null.
+ */
+public val Mode.returnTo: ReturnTo?
+  get() = when (this) {
+    is Mode.NORMAL -> this.returnTo
+    is Mode.SELECT -> this.returnTo
+    is Mode.VISUAL -> this.returnTo
+    is Mode.OP_PENDING -> this.returnTo
+    else -> null
+  }
+
+/**
+ * Check if one-command-mode (':h i_Ctrl-o') is active.
+ */
+public val Mode.isSingleModeActive: Boolean
+  get() = returnTo != null
+
+/**
+ * Check if the caret can be placed after the end of line.
+ *
+ * `onemore` option is ignored.
+ */
+public val Mode.isEndAllowedIgnoringOnemore: Boolean
+  get() = when (this) {
+    is Mode.INSERT, is Mode.VISUAL, is Mode.SELECT -> true
+    else -> false
+  }
+
+public val SelectionType.isLine: Boolean get() = this == SelectionType.LINE_WISE
+public val SelectionType.isChar: Boolean get() = this == SelectionType.CHARACTER_WISE
+public val SelectionType.isBlock: Boolean get() = this == SelectionType.BLOCK_WISE
+
+/**
+ * Convert the IdeaVim [Mode] into a string according to the rules of `mode()` function in Vim.
+ *
+ *  Neovim
+ * :h mode()
+ *
+ * - mode(expr)          Return a string that indicates the current mode.
+ *
+ *   If "expr" is supplied and it evaluates to a non-zero Number or
+ *   a non-empty String (|non-zero-arg|), then the full mode is
+ *   returned, otherwise only the first letter is returned.
+ *
+ *   n          Normal
+ *   no         Operator-pending
+ *   nov        Operator-pending (forced characterwise |o_v|)
+ *   noV        Operator-pending (forced linewise |o_V|)
+ *   noCTRL-V   Operator-pending (forced blockwise |o_CTRL-V|)
+ *   niI        Normal using |i_CTRL-O| in |Insert-mode|
+ *   niR        Normal using |i_CTRL-O| in |Replace-mode|
+ *   niV        Normal using |i_CTRL-O| in |Virtual-Replace-mode|
+ *   v          Visual by character
+ *   V          Visual by line
+ *   CTRL-V     Visual blockwise
+ *   s          Select by character
+ *   S          Select by line
+ *   CTRL-S     Select blockwise
+ *   i          Insert
+ *   ic         Insert mode completion |compl-generic|
+ *   ix         Insert mode |i_CTRL-X| completion
+ *   R          Replace |R|
+ *   Rc         Replace mode completion |compl-generic|
+ *   Rv         Virtual Replace |gR|
+ *   Rx         Replace mode |i_CTRL-X| completion
+ *   c          Command-line editing
+ *   cv         Vim Ex mode |gQ|
+ *   ce         Normal Ex mode |Q|
+ *   r          Hit-enter prompt
+ *   rm         The -- more -- prompt
+ *   r?         |:confirm| query of some sort
+ *   !          Shell or external command is executing
+ *   t          Terminal mode: keys go to the job
+ *   This is useful in the 'statusline' option or when used
+ *   with |remote_expr()| In most other places it always returns
+ *   "c" or "n".
+ *   Note that in the future more modes and more specific modes may
+ *   be added. It's better not to compare the whole string but only
+ *   the leading character(s).
+ */
+public fun Mode.toVimNotation(): String {
+  return when (this) {
+    is Mode.NORMAL -> "n"
+    is Mode.VISUAL -> when (selectionType) {
+      SelectionType.CHARACTER_WISE -> "v"
+      SelectionType.LINE_WISE -> "V"
+      SelectionType.BLOCK_WISE -> "\u0016"
+    }
+
+    Mode.INSERT -> "i"
+    is Mode.SELECT -> when (selectionType) {
+      SelectionType.CHARACTER_WISE -> "s"
+      SelectionType.LINE_WISE -> "S"
+      SelectionType.BLOCK_WISE -> "\u0013"
+    }
+
+    Mode.REPLACE -> "R"
+    Mode.CMD_LINE -> "c"
+    is Mode.OP_PENDING -> "no"
+  }
+}
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/Command.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/Command.kt
index ef5dc4391..e690ffac3 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/Command.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/Command.kt
@@ -24,11 +24,8 @@ import com.maddyhome.idea.vim.ex.NoRangeAllowedException
 import com.maddyhome.idea.vim.ex.ranges.LineRange
 import com.maddyhome.idea.vim.ex.ranges.Ranges
 import com.maddyhome.idea.vim.helper.Msg
-import com.maddyhome.idea.vim.helper.exitVisualMode
-import com.maddyhome.idea.vim.helper.inVisualMode
-import com.maddyhome.idea.vim.helper.mode
+import com.maddyhome.idea.vim.state.mode.mode
 import com.maddyhome.idea.vim.helper.noneOfEnum
-import com.maddyhome.idea.vim.helper.subMode
 import com.maddyhome.idea.vim.helper.vimStateMachine
 import com.maddyhome.idea.vim.vimscript.model.Executable
 import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
@@ -64,8 +61,9 @@ public sealed class Command(public var commandRanges: Ranges, public val command
   override fun execute(editor: VimEditor, context: ExecutionContext): ExecutionResult {
     checkRanges(editor)
     checkArgument(editor)
-    if (editor.inVisualMode && Flag.SAVE_VISUAL !in argFlags.flags) {
-      editor.exitVisualMode()
+    if (editor.nativeCarets().any { it.hasSelection() } && Flag.SAVE_VISUAL !in argFlags.flags) {
+      editor.removeSelection()
+      editor.removeSecondaryCarets()
     }
     if (argFlags.access == Access.WRITABLE && !editor.isDocumentWritable()) {
       logger.info("Trying to modify readonly document")
@@ -76,7 +74,6 @@ public sealed class Command(public var commandRanges: Ranges, public val command
       editor.vimStateMachine.isOperatorPending,
       0,
       editor.mode,
-      editor.subMode,
     )
 
     val runCommand = { runCommand(editor, context, operatorArguments) }
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 2ac8dd68e..dc47cc127 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,7 +14,7 @@ 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.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.ex.ExException
 import com.maddyhome.idea.vim.ex.ranges.Ranges
 import com.maddyhome.idea.vim.put.PutData
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/DeleteLinesCommand.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/DeleteLinesCommand.kt
index 333c09011..338ac2e0d 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/DeleteLinesCommand.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/DeleteLinesCommand.kt
@@ -14,7 +14,7 @@ import com.maddyhome.idea.vim.api.VimCaret
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.ex.ranges.Ranges
 import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
 
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/MoveTextCommand.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/MoveTextCommand.kt
index 6bed54be6..36649b5dc 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/MoveTextCommand.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/MoveTextCommand.kt
@@ -19,7 +19,7 @@ import com.maddyhome.idea.vim.api.VimMarkService
 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.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.ex.ExException
 import com.maddyhome.idea.vim.ex.InvalidRangeException
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/NormalCommand.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/NormalCommand.kt
index 1af495e32..499a493c3 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/NormalCommand.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/NormalCommand.kt
@@ -15,11 +15,11 @@ import com.maddyhome.idea.vim.api.ExecutionContext
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.VimMarkService
 import com.maddyhome.idea.vim.api.injector
+import com.maddyhome.idea.vim.state.mode.Mode
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.VimStateMachine
 import com.maddyhome.idea.vim.ex.ranges.Ranges
 import com.maddyhome.idea.vim.helper.exitVisualMode
-import com.maddyhome.idea.vim.helper.mode
+import com.maddyhome.idea.vim.state.mode.mode
 import com.maddyhome.idea.vim.helper.vimStateMachine
 import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
 
@@ -44,20 +44,17 @@ public data class NormalCommand(val ranges: Ranges, val argument: String) : Comm
     val commandState = editor.vimStateMachine
     val rangeUsed = ranges.size() != 0
     when (editor.mode) {
-      VimStateMachine.Mode.VISUAL -> {
+      is Mode.VISUAL -> {
         editor.exitVisualMode()
         if (!rangeUsed) {
           val selectionStart = injector.markService.getMark(editor.primaryCaret(), VimMarkService.SELECTION_START_MARK)!!
           editor.currentCaret().moveToBufferPosition(BufferPosition(selectionStart.line, selectionStart.col))
         }
       }
-      VimStateMachine.Mode.CMD_LINE -> injector.processGroup.cancelExEntry(editor, false)
-      VimStateMachine.Mode.INSERT, VimStateMachine.Mode.REPLACE -> editor.exitInsertMode(context, OperatorArguments(false, 1, commandState.mode, commandState.subMode))
-      VimStateMachine.Mode.SELECT -> editor.exitSelectModeNative(false)
-      VimStateMachine.Mode.OP_PENDING, VimStateMachine.Mode.COMMAND -> Unit
-      VimStateMachine.Mode.INSERT_NORMAL -> Unit
-      VimStateMachine.Mode.INSERT_VISUAL -> Unit
-      VimStateMachine.Mode.INSERT_SELECT -> Unit
+      is Mode.CMD_LINE -> injector.processGroup.cancelExEntry(editor, false)
+      Mode.INSERT, Mode.REPLACE -> editor.exitInsertMode(context, OperatorArguments(false, 1, commandState.mode))
+      is Mode.SELECT -> editor.exitSelectModeNative(false)
+      is Mode.OP_PENDING, is Mode.NORMAL -> Unit
     }
     val range = getLineRange(editor, editor.primaryCaret())
 
@@ -81,11 +78,11 @@ public data class NormalCommand(val ranges: Ranges, val argument: String) : Comm
 
       // Exit if state leaves as insert or cmd_line
       val mode = commandState.mode
-      if (mode == VimStateMachine.Mode.CMD_LINE) {
+      if (mode is Mode.CMD_LINE) {
         injector.processGroup.cancelExEntry(editor, false)
       }
-      if (mode == VimStateMachine.Mode.INSERT || mode == VimStateMachine.Mode.REPLACE) {
-        editor.exitInsertMode(context, OperatorArguments(false, 1, commandState.mode, commandState.subMode))
+      if (mode is Mode.INSERT || mode is Mode.REPLACE) {
+        editor.exitInsertMode(context, OperatorArguments(false, 1, commandState.mode))
       }
     }
 
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/PutLinesCommand.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/PutLinesCommand.kt
index b48e11c81..d74a0f156 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/PutLinesCommand.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/PutLinesCommand.kt
@@ -13,7 +13,7 @@ import com.maddyhome.idea.vim.api.ExecutionContext
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.ex.ranges.Ranges
 import com.maddyhome.idea.vim.put.PutData
 import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/RegistersCommand.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/RegistersCommand.kt
index b96eb182e..c42d98ee9 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/RegistersCommand.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/RegistersCommand.kt
@@ -13,7 +13,7 @@ import com.maddyhome.idea.vim.api.ExecutionContext
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.ex.ranges.Ranges
 import com.maddyhome.idea.vim.helper.EngineStringHelper
 import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/SortCommand.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/SortCommand.kt
index c88dbea92..de06b9848 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/SortCommand.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/SortCommand.kt
@@ -17,7 +17,7 @@ import com.maddyhome.idea.vim.command.OperatorArguments
 import com.maddyhome.idea.vim.ex.ExException
 import com.maddyhome.idea.vim.ex.ranges.LineRange
 import com.maddyhome.idea.vim.ex.ranges.Ranges
-import com.maddyhome.idea.vim.helper.inBlockSubMode
+import com.maddyhome.idea.vim.state.mode.inBlockSelection
 import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
 import java.util.*
 
@@ -41,7 +41,7 @@ public data class SortCommand(val ranges: Ranges, val argument: String) : Comman
       unique = nonEmptyArg && "u" in arg,
     )
     val lineComparator = LineComparator(sortOption.ignoreCase, sortOption.numeric, sortOption.reverse)
-    if (editor.inBlockSubMode) {
+    if (editor.inBlockSelection) {
       val primaryCaret = editor.primaryCaret()
       val range = getSortLineRange(editor, primaryCaret)
       val worked = injector.changeGroup.sortRange(editor, primaryCaret, range, lineComparator, sortOption)
@@ -52,7 +52,7 @@ public data class SortCommand(val ranges: Ranges, val argument: String) : Comman
     }
 
     var worked = true
-    for (caret in editor.nativeCarets()) {
+    for (caret in editor.carets()) {
       val range = getSortLineRange(editor, caret)
       if (!injector.changeGroup.sortRange(editor, caret, range, lineComparator, sortOption)) {
         worked = false
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/YankLinesCommand.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/YankLinesCommand.kt
index 949367a84..9c58b7ccf 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/YankLinesCommand.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/YankLinesCommand.kt
@@ -13,7 +13,7 @@ import com.maddyhome.idea.vim.api.ExecutionContext
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.ex.ExException
 import com.maddyhome.idea.vim.ex.ranges.Ranges
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/services/VimVariableServiceBase.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/services/VimVariableServiceBase.kt
index f343b1c8c..9b9a80e73 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/services/VimVariableServiceBase.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/services/VimVariableServiceBase.kt
@@ -15,7 +15,7 @@ import com.maddyhome.idea.vim.api.getOrPutBufferData
 import com.maddyhome.idea.vim.api.getOrPutTabData
 import com.maddyhome.idea.vim.api.getOrPutWindowData
 import com.maddyhome.idea.vim.api.injector
-import com.maddyhome.idea.vim.command.VimStateMachine
+import com.maddyhome.idea.vim.state.VimStateMachine
 import com.maddyhome.idea.vim.common.Direction
 import com.maddyhome.idea.vim.ex.ExException
 import com.maddyhome.idea.vim.vimscript.model.ExecutableContext
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/yank/VimYankGroup.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/yank/VimYankGroup.kt
index db04f2a09..687881755 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/yank/VimYankGroup.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/yank/VimYankGroup.kt
@@ -12,7 +12,7 @@ import com.maddyhome.idea.vim.api.ExecutionContext
 import com.maddyhome.idea.vim.api.VimEditor
 import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 
 public interface VimYankGroup {
diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/yank/YankGroupBase.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/yank/YankGroupBase.kt
index 41a62c9c6..10089b665 100644
--- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/yank/YankGroupBase.kt
+++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/yank/YankGroupBase.kt
@@ -18,7 +18,7 @@ import com.maddyhome.idea.vim.api.getLineStartForOffset
 import com.maddyhome.idea.vim.api.injector
 import com.maddyhome.idea.vim.command.Argument
 import com.maddyhome.idea.vim.command.OperatorArguments
-import com.maddyhome.idea.vim.command.SelectionType
+import com.maddyhome.idea.vim.state.mode.SelectionType
 import com.maddyhome.idea.vim.common.TextRange
 import com.maddyhome.idea.vim.listener.VimYankListener
 import org.jetbrains.annotations.Contract