diff --git a/src/main/kotlin/org/acejump/action/AceEditorAction.kt b/src/main/kotlin/org/acejump/action/AceEditorAction.kt
index ac0f544..b322972 100644
--- a/src/main/kotlin/org/acejump/action/AceEditorAction.kt
+++ b/src/main/kotlin/org/acejump/action/AceEditorAction.kt
@@ -5,10 +5,10 @@ import com.intellij.openapi.editor.Caret
 import com.intellij.openapi.editor.Editor
 import com.intellij.openapi.editor.actionSystem.EditorActionHandler
 import org.acejump.boundaries.StandardBoundaries
+import org.acejump.interact.VisitDirection
 import org.acejump.search.Pattern
 import org.acejump.session.Session
 import org.acejump.session.SessionManager
-import org.acejump.session.mode.VisitDirection
 
 /**
  * Base class for keyboard-activated overrides of existing editor actions, that have a different meaning during an AceJump [Session].
diff --git a/src/main/kotlin/org/acejump/action/AceKeyboardAction.kt b/src/main/kotlin/org/acejump/action/AceKeyboardAction.kt
index ee35f14..bb9ff7a 100644
--- a/src/main/kotlin/org/acejump/action/AceKeyboardAction.kt
+++ b/src/main/kotlin/org/acejump/action/AceKeyboardAction.kt
@@ -5,11 +5,11 @@ import com.intellij.openapi.actionSystem.CommonDataKeys.EDITOR
 import com.intellij.openapi.project.DumbAwareAction
 import org.acejump.boundaries.Boundaries
 import org.acejump.boundaries.StandardBoundaries
+import org.acejump.interact.mode.DefaultMode
+import org.acejump.interact.mode.MultiCaretMode
 import org.acejump.search.Pattern
 import org.acejump.session.Session
 import org.acejump.session.SessionManager
-import org.acejump.session.mode.MultiCaretSessionMode
-import org.acejump.session.mode.SingleCaretSessionMode
 
 /**
  * Base class for keyboard-activated actions that create or update an AceJump [Session].
@@ -36,14 +36,14 @@ sealed class AceKeyboardAction : DumbAwareAction() {
    * Starts or ends an AceJump session.
    */
   object ActivateAceJump : AceKeyboardAction() {
-    override fun invoke(session: Session) = session.setMode(SingleCaretSessionMode())
+    override fun invoke(session: Session) = session.toggleMode(DefaultMode)
   }
   
   /**
    * Starts or ends an AceJump session in multicaret mode.
    */
   object ActivateAceJumpMultiCaret : AceKeyboardAction() {
-    override fun invoke(session: Session) = session.setMode(MultiCaretSessionMode())
+    override fun invoke(session: Session) = session.toggleMode(MultiCaretMode())
   }
   
   // @formatter:off
diff --git a/src/main/kotlin/org/acejump/action/AceTagAction.kt b/src/main/kotlin/org/acejump/action/AceTagAction.kt
index a120948..c398acd 100644
--- a/src/main/kotlin/org/acejump/action/AceTagAction.kt
+++ b/src/main/kotlin/org/acejump/action/AceTagAction.kt
@@ -7,8 +7,10 @@ import com.intellij.find.actions.FindUsagesAction
 import com.intellij.find.actions.ShowUsagesAction
 import com.intellij.openapi.actionSystem.ActionManager
 import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.actionSystem.IdeActions
 import com.intellij.openapi.command.CommandProcessor
 import com.intellij.openapi.command.UndoConfirmationPolicy
+import com.intellij.openapi.command.WriteCommandAction
 import com.intellij.openapi.editor.Document
 import com.intellij.openapi.editor.Editor
 import com.intellij.openapi.editor.actionSystem.DocCommandGroupId
@@ -196,6 +198,89 @@ internal sealed class AceTagAction {
     }
   }
   
+  object SelectQuery : AceTagAction() {
+    override fun invoke(editor: Editor, searchProcessor: SearchProcessor, offset: Int, shiftMode: Boolean) {
+      recordCaretPosition(editor)
+      
+      val startOffset = JumpToSearchStart.getCaretOffset(editor, searchProcessor, offset)
+      val endOffset = JumpPastSearchEnd.getCaretOffset(editor, searchProcessor, offset)
+      
+      selectRange(editor, startOffset, endOffset)
+    }
+  }
+  
+  object SelectWord : AceTagAction() {
+    override fun invoke(editor: Editor, searchProcessor: SearchProcessor, offset: Int, shiftMode: Boolean) {
+      val chars = editor.immutableText
+      val queryEndOffset = JumpToSearchEnd.getCaretOffset(editor, searchProcessor, offset)
+  
+      if (chars[queryEndOffset].isWordPart) {
+        recordCaretPosition(editor)
+        
+        val startOffset = JumpToWordStartTag.getCaretOffset(editor, offset, queryEndOffset, isInsideWord = true)
+        val endOffset = JumpToWordEndTag.getCaretOffset(editor, offset, queryEndOffset, isInsideWord = true)
+        
+        selectRange(editor, startOffset, endOffset)
+      }
+      else {
+        SelectQuery(editor, searchProcessor, offset, shiftMode)
+      }
+    }
+  }
+  
+  object SelectHump : AceTagAction() {
+    override fun invoke(editor: Editor, searchProcessor: SearchProcessor, offset: Int, shiftMode: Boolean) {
+      val chars = editor.immutableText
+      val queryEndOffset = JumpToSearchEnd.getCaretOffset(editor, searchProcessor, offset)
+  
+      if (chars[queryEndOffset].isWordPart) {
+        recordCaretPosition(editor)
+    
+        val startOffset = chars.humpStart(queryEndOffset)
+        val endOffset = chars.humpEnd(queryEndOffset) + 1
+    
+        selectRange(editor, startOffset, endOffset)
+      }
+      else {
+        SelectQuery(editor, searchProcessor, offset, shiftMode)
+      }
+    }
+  }
+  
+  object SelectLine : AceTagAction() {
+    override fun invoke(editor: Editor, searchProcessor: SearchProcessor, offset: Int, shiftMode: Boolean) {
+      JumpToSearchEnd(editor, searchProcessor, offset, shiftMode = false)
+      editor.selectionModel.selectLineAtCaret()
+    }
+  }
+  
+  class SelectExtended(private val extendCount: Int) : AceTagAction() {
+    override fun invoke(editor: Editor, searchProcessor: SearchProcessor, offset: Int, shiftMode: Boolean) {
+      JumpToSearchEnd(editor, searchProcessor, offset, shiftMode = false)
+      
+      val action = ActionManager.getInstance().getAction(IdeActions.ACTION_EDITOR_SELECT_WORD_AT_CARET)
+      
+      repeat(extendCount) {
+        performAction(action)
+      }
+    }
+  }
+  
+  class Delete(private val selector: AceTagAction) : AceTagAction() {
+    override fun invoke(editor: Editor, searchProcessor: SearchProcessor, offset: Int, shiftMode: Boolean) {
+      val oldCarets = editor.caretModel.caretsAndSelections
+      
+      selector(editor, searchProcessor, offset, shiftMode)
+      WriteCommandAction.writeCommandAction(editor.project).withName("AceJump Delete").run<Throwable> {
+        editor.selectionModel.let { editor.document.deleteString(it.selectionStart, it.selectionEnd) }
+      }
+  
+      if (shiftMode) {
+        editor.caretModel.caretsAndSelections = oldCarets
+      }
+    }
+  }
+  
   /**
    * On default action, performs the Go To Declaration action, available via `Navigate | Declaration or Usages`.
    * On shift action, performs the Go To Type Declaration action, available via `Navigate | Type Declaration`.
diff --git a/src/main/kotlin/org/acejump/session/mode/TypeResult.kt b/src/main/kotlin/org/acejump/interact/TypeResult.kt
similarity index 66%
rename from src/main/kotlin/org/acejump/session/mode/TypeResult.kt
rename to src/main/kotlin/org/acejump/interact/TypeResult.kt
index 920d1d1..48b2bae 100644
--- a/src/main/kotlin/org/acejump/session/mode/TypeResult.kt
+++ b/src/main/kotlin/org/acejump/interact/TypeResult.kt
@@ -1,10 +1,12 @@
-package org.acejump.session.mode
+package org.acejump.interact
 
+import org.acejump.session.SessionMode
 import org.acejump.session.SessionSnapshot
 
 sealed class TypeResult {
   object Nothing : TypeResult()
   object UpdateResults : TypeResult()
   class LoadSnapshot(val snapshot: SessionSnapshot) : TypeResult()
+  class ChangeMode(val mode: SessionMode) : TypeResult()
   object EndSession : TypeResult()
 }
diff --git a/src/main/kotlin/org/acejump/session/mode/VisitDirection.kt b/src/main/kotlin/org/acejump/interact/VisitDirection.kt
similarity index 61%
rename from src/main/kotlin/org/acejump/session/mode/VisitDirection.kt
rename to src/main/kotlin/org/acejump/interact/VisitDirection.kt
index b900eb3..29e1b75 100644
--- a/src/main/kotlin/org/acejump/session/mode/VisitDirection.kt
+++ b/src/main/kotlin/org/acejump/interact/VisitDirection.kt
@@ -1,4 +1,4 @@
-package org.acejump.session.mode
+package org.acejump.interact
 
 enum class VisitDirection {
   BACKWARD,
diff --git a/src/main/kotlin/org/acejump/session/mode/VisitResult.kt b/src/main/kotlin/org/acejump/interact/VisitResult.kt
similarity index 82%
rename from src/main/kotlin/org/acejump/session/mode/VisitResult.kt
rename to src/main/kotlin/org/acejump/interact/VisitResult.kt
index eaad3c1..5e0619d 100644
--- a/src/main/kotlin/org/acejump/session/mode/VisitResult.kt
+++ b/src/main/kotlin/org/acejump/interact/VisitResult.kt
@@ -1,4 +1,4 @@
-package org.acejump.session.mode
+package org.acejump.interact
 
 sealed class VisitResult {
   object Nothing : VisitResult()
diff --git a/src/main/kotlin/org/acejump/interact/mode/AbstractNavigableMode.kt b/src/main/kotlin/org/acejump/interact/mode/AbstractNavigableMode.kt
new file mode 100644
index 0000000..e50930c
--- /dev/null
+++ b/src/main/kotlin/org/acejump/interact/mode/AbstractNavigableMode.kt
@@ -0,0 +1,53 @@
+package org.acejump.interact.mode
+
+import com.intellij.openapi.editor.Editor
+import org.acejump.action.AceTagAction
+import org.acejump.action.TagVisitor
+import org.acejump.interact.TypeResult
+import org.acejump.interact.VisitDirection
+import org.acejump.interact.VisitResult
+import org.acejump.search.SearchProcessor
+import org.acejump.search.Tagger
+import org.acejump.session.SessionMode
+
+internal abstract class AbstractNavigableMode : SessionMode {
+  abstract val actionMap: Map<Char, AceTagAction>
+  abstract val modeMap: Map<Char, () -> SessionMode>
+  
+  override fun type(editor: Editor, processor: SearchProcessor, tagger: Tagger, charTyped: Char, acceptedTag: Int?): TypeResult {
+    if (acceptedTag != null) {
+      val action = actionMap[charTyped.toUpperCase()]
+      if (action != null) {
+        action(editor, processor, acceptedTag, charTyped.isUpperCase())
+        return TypeResult.EndSession
+      }
+      
+      val mode = modeMap[charTyped.toUpperCase()]
+      if (mode != null) {
+        return TypeResult.ChangeMode(mode())
+      }
+      
+      return TypeResult.Nothing
+    }
+    
+    if (processor.type(charTyped, tagger)) {
+      return TypeResult.UpdateResults
+    }
+    
+    return TypeResult.Nothing
+  }
+  
+  override fun visit(editor: Editor, processor: SearchProcessor, direction: VisitDirection, acceptedTag: Int?): VisitResult {
+    if (acceptedTag != null) {
+      AceTagAction.JumpToSearchStart(editor, processor, acceptedTag, shiftMode = false)
+      return VisitResult.EndSession
+    }
+    
+    val onlyTagOffset = when (direction) {
+      VisitDirection.BACKWARD -> TagVisitor(editor, processor).visitPrevious()
+      VisitDirection.FORWARD  -> TagVisitor(editor, processor).visitNext()
+    }
+    
+    return onlyTagOffset?.let(VisitResult::SetAcceptedTag) ?: VisitResult.Nothing
+  }
+}
diff --git a/src/main/kotlin/org/acejump/interact/mode/DefaultMode.kt b/src/main/kotlin/org/acejump/interact/mode/DefaultMode.kt
new file mode 100644
index 0000000..227b25a
--- /dev/null
+++ b/src/main/kotlin/org/acejump/interact/mode/DefaultMode.kt
@@ -0,0 +1,33 @@
+package org.acejump.interact.mode
+
+import org.acejump.action.AceTagAction
+import org.acejump.config.AceConfig
+
+internal object DefaultMode : AbstractNavigableMode() {
+  override val actionMap = mapOf(
+    'J' to AceTagAction.JumpToSearchStart,
+    'K' to AceTagAction.JumpToSearchEnd,
+    'L' to AceTagAction.JumpPastSearchEnd,
+    'W' to AceTagAction.JumpToWordStartTag,
+    'E' to AceTagAction.JumpToWordEndTag,
+    'B' to AceTagAction.GoToDeclaration,
+    'U' to AceTagAction.ShowUsages,
+    'I' to AceTagAction.ShowIntentions,
+    'R' to AceTagAction.Refactor
+  )
+  
+  override val modeMap = mapOf(
+    'S' to { SelectMode },
+    'D' to { DeleteMode }
+  )
+  
+  override val caretColor
+    get() = AceConfig.singleCaretModeColor
+  
+  override val actionHint = arrayOf(
+    "<f>[J]</f>ump to Tag / <f>[K]</f> to Query / <f>[L]</f> past Query",
+    "<f>[W]</f>ord Start / Word <f>[E]</f>nd",
+    "<f>[S]</f>elect... / <f>[D]</f>elete...",
+    "<f>[B]</f> Declaration / <f>[U]</f>sages / <f>[I]</f>ntentions / <f>[R]</f>efactor"
+  )
+}
diff --git a/src/main/kotlin/org/acejump/interact/mode/DeleteMode.kt b/src/main/kotlin/org/acejump/interact/mode/DeleteMode.kt
new file mode 100644
index 0000000..e13afec
--- /dev/null
+++ b/src/main/kotlin/org/acejump/interact/mode/DeleteMode.kt
@@ -0,0 +1,17 @@
+package org.acejump.interact.mode
+
+import org.acejump.action.AceTagAction
+import org.acejump.config.AceConfig
+
+internal object DeleteMode : AbstractNavigableMode() {
+  override val actionMap = SelectMode.actionMap.mapValues { AceTagAction.Delete(it.value) }
+  
+  override val modeMap
+    get() = SelectMode.modeMap
+  
+  override val caretColor
+    get() = AceConfig.singleCaretModeColor
+  
+  override val actionHint
+    get() = SelectMode.actionHint
+}
diff --git a/src/main/kotlin/org/acejump/session/mode/MultiCaretSessionMode.kt b/src/main/kotlin/org/acejump/interact/mode/MultiCaretMode.kt
similarity index 91%
rename from src/main/kotlin/org/acejump/session/mode/MultiCaretSessionMode.kt
rename to src/main/kotlin/org/acejump/interact/mode/MultiCaretMode.kt
index d6bee68..38879ab 100644
--- a/src/main/kotlin/org/acejump/session/mode/MultiCaretSessionMode.kt
+++ b/src/main/kotlin/org/acejump/interact/mode/MultiCaretMode.kt
@@ -1,13 +1,16 @@
-package org.acejump.session.mode
+package org.acejump.interact.mode
 import com.intellij.openapi.editor.Editor
 import org.acejump.action.AceTagAction
 import org.acejump.config.AceConfig
+import org.acejump.interact.TypeResult
+import org.acejump.interact.VisitDirection
+import org.acejump.interact.VisitResult
 import org.acejump.search.SearchProcessor
 import org.acejump.search.Tagger
 import org.acejump.session.SessionMode
 import org.acejump.session.SessionSnapshot
 
-internal class MultiCaretSessionMode : SessionMode {
+internal class MultiCaretMode : SessionMode {
   private companion object {
     private val actionMap = mapOf(
       'J' to AceTagAction.JumpToSearchStart,
diff --git a/src/main/kotlin/org/acejump/interact/mode/SelectMode.kt b/src/main/kotlin/org/acejump/interact/mode/SelectMode.kt
new file mode 100644
index 0000000..f90bc73
--- /dev/null
+++ b/src/main/kotlin/org/acejump/interact/mode/SelectMode.kt
@@ -0,0 +1,27 @@
+package org.acejump.interact.mode
+
+import org.acejump.action.AceTagAction
+import org.acejump.config.AceConfig
+import org.acejump.session.SessionMode
+
+internal object SelectMode : AbstractNavigableMode() {
+  override val actionMap = mapOf(
+    'Q' to AceTagAction.SelectQuery,
+    'W' to AceTagAction.SelectWord,
+    'H' to AceTagAction.SelectHump,
+    'L' to AceTagAction.SelectLine,
+    *('1'..'9').mapIndexed { index, char -> char to AceTagAction.SelectExtended(index + 1) }.toTypedArray()
+  )
+  
+  override val modeMap
+    get() = emptyMap<Char, () -> SessionMode>()
+  
+  override val caretColor
+    get() = AceConfig.singleCaretModeColor
+  
+  override val actionHint = arrayOf(
+    "<f>[Q]</f>uery / <f>[L]</f>ine",
+    "<f>[W]</f>ord / <f>[H]</f>ump",
+    "<f>[1-9] Extend Selection</f>"
+  )
+}
diff --git a/src/main/kotlin/org/acejump/session/EditorSettings.kt b/src/main/kotlin/org/acejump/session/EditorSettings.kt
index 592a54c..0d8cadd 100644
--- a/src/main/kotlin/org/acejump/session/EditorSettings.kt
+++ b/src/main/kotlin/org/acejump/session/EditorSettings.kt
@@ -26,6 +26,14 @@ internal data class EditorSettings(private val isBlockCursor: Boolean, private v
     }
   }
   
+  fun startEditing(editor: Editor) {
+    editor.document.setReadOnly(isReadOnly)
+  }
+  
+  fun stopEditing(editor: Editor) {
+    editor.document.setReadOnly(true)
+  }
+  
   fun onTagAccepted(editor: Editor) = editor.let {
     it.settings.isBlockCursor = isBlockCursor
   }
diff --git a/src/main/kotlin/org/acejump/session/Session.kt b/src/main/kotlin/org/acejump/session/Session.kt
index c6f9b05..6400147 100644
--- a/src/main/kotlin/org/acejump/session/Session.kt
+++ b/src/main/kotlin/org/acejump/session/Session.kt
@@ -18,11 +18,11 @@ import org.acejump.boundaries.StandardBoundaries
 import org.acejump.config.AceConfig
 import org.acejump.input.EditorKeyListener
 import org.acejump.input.KeyLayoutCache
+import org.acejump.interact.TypeResult
+import org.acejump.interact.VisitDirection
+import org.acejump.interact.VisitResult
+import org.acejump.interact.mode.DefaultMode
 import org.acejump.search.*
-import org.acejump.session.mode.SingleCaretSessionMode
-import org.acejump.session.mode.TypeResult
-import org.acejump.session.mode.VisitDirection
-import org.acejump.session.mode.VisitResult
 import org.acejump.view.TagCanvas
 import org.acejump.view.TextHighlighter
 
@@ -53,17 +53,7 @@ class Session(private val editor: Editor) {
         
         if (processor != null) {
           textHighlighter.renderFinal(value, processor.query)
-          
-          val hintText = mode.actionHint
-            .joinToString("\n")
-            .replace("<f>", "<span style=\"font-family:'${editor.colorsScheme.editorFontName}';font-weight:bold\">")
-            .replace("</f>", "</span>")
-          
-          val hint = LightweightHint(HintUtil.createInformationLabel(hintText))
-          val pos = HintManagerImpl.getHintPosition(hint, editor, editor.offsetToLogicalPosition(value), HintManager.ABOVE)
-          val info = HintManagerImpl.createHintHint(editor, pos, hint, HintManager.ABOVE).setShowImmediately(true)
-          val flags = HintManager.UPDATE_BY_SCROLLING or HintManager.HIDE_BY_ESCAPE
-          HintManagerImpl.getInstanceImpl().showEditorHint(hint, editor, pos, flags, 0, true, info)
+          setMode(DefaultMode)
         }
       }
     }
@@ -88,11 +78,18 @@ class Session(private val editor: Editor) {
         if (processor == null) {
           processor = SearchProcessor.fromChar(editor, charTyped, defaultBoundaries).also { searchProcessor = it }
           updateSearch(processor)
+          return
         }
-        else when (val result = mode.type(editor, processor, tagger, charTyped, acceptedTag)) {
+        
+        editorSettings.startEditing(editor)
+        val result = mode.type(editor, processor, tagger, charTyped, acceptedTag)
+        editorSettings.stopEditing(editor)
+        
+        when (result) {
           TypeResult.UpdateResults   -> updateSearch(processor)
           TypeResult.EndSession      -> end()
           is TypeResult.LoadSnapshot -> loadSnapshot(result.snapshot)
+          is TypeResult.ChangeMode   -> setMode(result.mode)
           else                       -> return
         }
       }
@@ -136,13 +133,31 @@ class Session(private val editor: Editor) {
     searchProcessor = snapshot.savedProcessor?.also(::updateSearch)
   }
   
+  private fun setMode(mode: SessionMode) {
+    this.mode = mode
+    editor.colorsScheme.setColor(EditorColors.CARET_COLOR, mode.caretColor)
+    
+    val acceptedTag = acceptedTag
+    if (acceptedTag != null) {
+      val hintText = mode.actionHint
+        .joinToString("\n")
+        .replace("<f>", "<span style=\"font-family:'${editor.colorsScheme.editorFontName}';font-weight:bold\">")
+        .replace("</f>", "</span>")
+  
+      val hint = LightweightHint(HintUtil.createInformationLabel(hintText))
+      val pos = HintManagerImpl.getHintPosition(hint, editor, editor.offsetToLogicalPosition(acceptedTag), HintManager.ABOVE)
+      val info = HintManagerImpl.createHintHint(editor, pos, hint, HintManager.ABOVE).setShowImmediately(true)
+      val flags = HintManager.UPDATE_BY_SCROLLING or HintManager.HIDE_BY_ESCAPE
+      HintManagerImpl.getInstanceImpl().showEditorHint(hint, editor, pos, flags, 0, true, info)
+    }
+  }
+  
   /**
    * Sets AceJump mode or ends the session if the mode is the same.
    */
-  fun setMode(mode: SessionMode) {
+  fun toggleMode(mode: SessionMode) {
     if (!this::mode.isInitialized) {
-      this.mode = mode
-      editor.colorsScheme.setColor(EditorColors.CARET_COLOR, mode.caretColor)
+      setMode(mode)
     }
     else if (!this.mode.isSame(mode)) {
       this.mode = mode
@@ -157,7 +172,7 @@ class Session(private val editor: Editor) {
    * Starts a regular expression search. If a search was already active, it will be reset alongside its tags and highlights.
    */
   fun startRegexSearch(pattern: String, boundaries: Boundaries) {
-    setMode(SingleCaretSessionMode())
+    toggleMode(DefaultMode)
     tagger = Tagger(editor)
     tagCanvas.setMarkers(emptyList(), isRegex = true)
     updateSearch(SearchProcessor.fromRegex(editor, pattern, boundaries.intersection(defaultBoundaries)).also { searchProcessor = it })
diff --git a/src/main/kotlin/org/acejump/session/SessionMode.kt b/src/main/kotlin/org/acejump/session/SessionMode.kt
index b158fcf..ba29fad 100644
--- a/src/main/kotlin/org/acejump/session/SessionMode.kt
+++ b/src/main/kotlin/org/acejump/session/SessionMode.kt
@@ -1,11 +1,11 @@
 package org.acejump.session
 
 import com.intellij.openapi.editor.Editor
+import org.acejump.interact.TypeResult
+import org.acejump.interact.VisitDirection
+import org.acejump.interact.VisitResult
 import org.acejump.search.SearchProcessor
 import org.acejump.search.Tagger
-import org.acejump.session.mode.TypeResult
-import org.acejump.session.mode.VisitDirection
-import org.acejump.session.mode.VisitResult
 import java.awt.Color
 
 interface SessionMode {
diff --git a/src/main/kotlin/org/acejump/session/mode/SingleCaretSessionMode.kt b/src/main/kotlin/org/acejump/session/mode/SingleCaretSessionMode.kt
deleted file mode 100644
index cd4ee64..0000000
--- a/src/main/kotlin/org/acejump/session/mode/SingleCaretSessionMode.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.acejump.session.mode
-
-import com.intellij.openapi.editor.Editor
-import org.acejump.action.AceTagAction
-import org.acejump.action.TagVisitor
-import org.acejump.config.AceConfig
-import org.acejump.search.SearchProcessor
-import org.acejump.search.Tagger
-import org.acejump.session.SessionMode
-
-internal class SingleCaretSessionMode : SessionMode {
-  private companion object {
-    private val actionMap = mapOf(
-      'J' to AceTagAction.JumpToSearchStart,
-      'K' to AceTagAction.JumpToSearchEnd,
-      'L' to AceTagAction.JumpPastSearchEnd,
-      'W' to AceTagAction.JumpToWordStartTag,
-      'E' to AceTagAction.JumpToWordEndTag,
-      'S' to AceTagAction.SelectWordOrHump,
-      'D' to AceTagAction.GoToDeclaration,
-      'U' to AceTagAction.ShowUsages,
-      'I' to AceTagAction.ShowIntentions,
-      'R' to AceTagAction.Refactor
-    )
-  }
-  
-  override val caretColor
-    get() = AceConfig.singleCaretModeColor
-  
-  override val actionHint = arrayOf(
-    "<f>[J]</f>ump to Tag / <f>[K]</f> to Query / <f>[L]</f> past Query",
-    "<f>[W]</f>ord Start / Word <f>[E]</f>nd / <f>[S]</f>elect Word",
-    "<f>[D]</f>eclaration / <f>[U]</f>sages / <f>[I]</f>ntentions / <f>[R]</f>efactor"
-  )
-  
-  override fun type(editor: Editor, processor: SearchProcessor, tagger: Tagger, charTyped: Char, acceptedTag: Int?): TypeResult {
-    if (acceptedTag != null) {
-      val action = actionMap[charTyped.toUpperCase()] ?: return TypeResult.Nothing
-      action(editor, processor, acceptedTag, charTyped.isUpperCase())
-      return TypeResult.EndSession
-    }
-    else if (processor.type(charTyped, tagger)) {
-      return TypeResult.UpdateResults
-    }
-    
-    return TypeResult.Nothing
-  }
-  
-  override fun visit(editor: Editor, processor: SearchProcessor, direction: VisitDirection, acceptedTag: Int?): VisitResult {
-    if (acceptedTag != null) {
-      AceTagAction.JumpToSearchStart(editor, processor, acceptedTag, shiftMode = false)
-      return VisitResult.EndSession
-    }
-    
-    val onlyTagOffset = when (direction) {
-      VisitDirection.BACKWARD -> TagVisitor(editor, processor).visitPrevious()
-      VisitDirection.FORWARD  -> TagVisitor(editor, processor).visitNext()
-    }
-    
-    return onlyTagOffset?.let(VisitResult::SetAcceptedTag) ?: VisitResult.Nothing
-  }
-}