diff --git a/ThirdPartyLicenses.md b/ThirdPartyLicenses.md
index 112cd06cf..bf1784094 100644
--- a/ThirdPartyLicenses.md
+++ b/ThirdPartyLicenses.md
@@ -84,3 +84,8 @@ IV)  It is not allowed to remove this license from the distribution of the Vim
      license for previous Vim releases instead of the license that they came
      with, at your option.
 ```
+
+---
+
+File [sneakIcon.png](doc/images/sneakIcon.svg), which is originally an icon of the ideavim-sneak plugin,
+is merged icons of IdeaVim plugin and a random sneaker by FreePic from flaticon.com.
diff --git a/doc/IdeaVim Plugins.md b/doc/IdeaVim Plugins.md
index f2478e027..8e1d6b546 100644
--- a/doc/IdeaVim Plugins.md	
+++ b/doc/IdeaVim Plugins.md	
@@ -44,16 +44,21 @@ All commands with the mappings are supported. See the [full list of supported co
 
 <details>
 <summary><h2>sneak</h2></summary>
-   
+
+<img src="images/sneakIcon.svg" width="80" height="80" alt="icon"/>  
+
+By [Mikhail Levchenko](https://github.com/Mishkun)  
+Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak  
 Original plugin: [vim-sneak](https://github.com/justinmk/vim-sneak).
    
 ### Setup:
-- Install [IdeaVim-sneak](https://plugins.jetbrains.com/plugin/15348-ideavim-sneak) plugin.
-- Add the following command to `~/.ideavimrc`: `set sneak`
+- Add the following command to `~/.ideavimrc`: `Plug 'justinmk/vim-sneak'`
    
 ### Instructions
-   
-See the [docs](https://github.com/Mishkun/ideavim-sneak#usage)
+
+* Type `s` and two chars to start sneaking in forward direction
+* Type `S` and two chars to start sneaking in backward direction
+* Type `;` or `,` to proceed with sneaking just as if you were using `f` or `t` commands
 
 </details>
 
diff --git a/doc/images/sneakIcon.svg b/doc/images/sneakIcon.svg
new file mode 100644
index 000000000..79dc30b65
--- /dev/null
+++ b/doc/images/sneakIcon.svg
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg viewBox="386.498 234 32 32" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="ideavim_plugin-a" x1="-6.748%" x2="47.286%" y1="33.61%" y2="85.907%">
+      <stop offset="0" stop-color="#3BEA62"/>
+      <stop offset="1" stop-color="#087CFA"/>
+    </linearGradient>
+  </defs>
+  <g transform="matrix(1.238978, 0.90017, -0.90017, 1.238978, 131.776901, -422.953003)" style="">
+    <path d="M 399.962 247.648 C 399.207 246.894 399.147 246.318 399.692 245.453 C 400.237 244.588 401.955 245.886 401.955 245.886 L 401.955 250.737" style="fill: rgb(248, 245, 231);"/>
+    <path d="M 413.846 253.602 C 413.846 255.077 411.827 256.134 409.392 256.134 L 396.381 256.134 C 395.211 256.134 394.232 256.003 393.433 255.817 C 391.496 255.367 390.613 254.596 390.613 254.596 L 391.478 252.513 L 393.607 252.617 Z M 413.846 253.602" fill="#cce6f6" style=""/>
+    <path d="M 413.846 253.602 C 413.846 253.602 411.475 254.468 408.27 254.468 C 405.94 254.468 398.116 253.433 394.023 252.869 C 392.488 252.658 391.478 252.512 391.478 252.512 C 390.864 251.662 392.078 248.741 392.758 247.263 C 393.118 246.484 393.85 245.929 394.703 245.83 C 394.782 245.82 394.861 245.815 394.939 245.815 C 395.369 245.815 395.645 246.532 396.059 247.225 C 396.446 247.877 396.955 248.507 397.823 248.507 C 399.617 248.507 401.955 245.886 401.955 245.886 C 406.544 249.03 410.097 250.43 410.097 250.43 C 410.097 250.43 413.061 250.446 413.782 251.167 C 414.503 251.888 414.422 253.218 413.846 253.602 Z M 413.846 253.602" style="fill: rgb(8, 124, 250);"/>
+    <path d="M 394.023 252.869 L 393.433 255.817 C 391.496 255.367 390.613 254.596 390.613 254.596 L 391.478 252.513 L 393.607 252.617 Z M 394.023 252.869" fill="#9dcae0" style=""/>
+    <path d="M 396.059 247.225 C 395.073 245.986 393.193 250.255 394.023 252.869 C 392.488 252.658 391.478 252.513 391.478 252.513 C 390.864 251.662 392.078 248.741 392.758 247.263 C 393.118 246.484 393.85 245.929 394.703 245.83 C 394.782 245.82 394.861 245.815 394.939 245.815 C 395.369 245.815 395.645 246.532 396.059 247.225 Z M 396.059 247.225" style="fill: rgb(14, 112, 142);"/>
+    <path d="M 403.527 246.924 L 399.174 251.768 C 397.7 250.892 395.578 250.174 394.016 249.717 C 392.843 249.372 391.985 249.174 391.959 249.168 C 392.108 248.769 392.268 248.379 392.421 248.022 L 394.341 248.65 L 394.884 248.827 C 395.509 249.031 396.192 248.95 396.753 248.608 L 397.153 248.363 C 397.35 248.454 397.572 248.507 397.823 248.507 C 399.617 248.507 401.955 245.886 401.955 245.886 C 402.494 246.256 403.02 246.601 403.527 246.924 Z M 403.527 246.924" style="fill: rgb(59, 234, 98);"/>
+    <path d="M 413.847 253.602 C 413.847 253.602 411.475 254.468 408.27 254.468 C 407.586 254.468 406.426 254.378 405.025 254.238 L 405.025 254.237 C 405.025 253.495 406.924 251.743 408.366 251.616 C 408.623 252.865 410.097 252.512 411.219 252.128 C 412.341 251.743 413.783 251.167 413.783 251.167 C 414.503 251.888 414.422 253.218 413.847 253.602 Z M 413.847 253.602" style="fill: rgb(8, 124, 250);"/>
+    <path d="M 394.341 248.65 C 394.214 248.978 394.103 249.339 394.016 249.717 C 392.843 249.372 391.985 249.174 391.959 249.168 C 392.108 248.769 392.268 248.379 392.421 248.022 Z M 394.341 248.65" style="fill: rgb(37, 187, 163);"/>
+    <path d="M 408.366 251.616 C 406.924 251.743 405.025 253.495 405.025 254.237 L 405.025 254.238 C 403.784 254.113 402.355 253.948 400.899 253.77 C 400.899 253.051 400.191 252.372 399.174 251.768 L 399.174 251.768 L 403.528 246.924 C 407.331 249.34 410.097 250.43 410.097 250.43 C 410.77 251.102 409.809 251.487 408.366 251.616 Z M 408.366 251.616" style="fill: rgb(248, 245, 231);"/>
+    <polygon fill="url(#ideavim_plugin-a)" fill-rule="evenodd" points="406.356 248.463 403.261 252.616 402.183 248.212 400.984 249.899 401.999 254.236 403.927 254.176 407.639 249.062" style="" transform="matrix(0.994522, 0.104529, -0.104529, 0.994522, 28.475005, -40.88594)"/>
+    <g fill="#fb6572" transform="matrix(0.046265, 0, 0, 0.046265, 390.612823, 245.155533)" style="">
+      <path d="m288.839844 72.65625c-2.007813 0-4.011719-.761719-5.542969-2.292969-3.058594-3.0625-3.058594-8.023437 0-11.082031l20.089844-20.089844c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.058594 3.0625 3.0625 8.023438 0 11.082032l-20.089844 20.089843c-1.527344 1.53125-3.535156 2.292969-5.539062 2.292969zm0 0" style="fill: rgb(56, 228, 105);"/>
+      <path d="m314.589844 87.082031c-2.007813 0-4.011719-.765625-5.542969-2.296875-3.0625-3.058594-3.0625-8.019531 0-11.082031l20.089844-20.085937c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.058594 3.0625 3.058594 8.023437 0 11.082031l-20.089844 20.085937c-1.527344 1.53125-3.535156 2.296875-5.539062 2.296875zm0 0" style="fill: rgb(59, 233, 100);"/>
+      <path d="m340.339844 101.507812c-2.007813 0-4.011719-.765624-5.542969-2.296874-3.058594-3.058594-3.058594-8.023438 0-11.082032l20.089844-20.085937c3.0625-3.0625 8.023437-3.0625 11.082031 0 3.058594 3.058593 3.0625 8.023437 0 11.082031l-20.089844 20.085938c-1.527344 1.53125-3.535156 2.296874-5.539062 2.296874zm0 0" style="fill: rgb(59, 233, 100);"/>
+      <path d="m366.089844 115.929688c-2.003906 0-4.011719-.761719-5.539063-2.292969-3.0625-3.0625-3.0625-8.023438 0-11.082031l20.085938-20.089844c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.0625 3.0625 3.0625 8.023437 0 11.082031l-20.085938 20.089844c-1.53125 1.53125-3.535156 2.292969-5.542968 2.292969zm0 0" style="fill: rgb(59, 233, 100);"/>
+    </g>
+    <path d="M 401.925 247.748 C 401.761 247.748 401.611 247.629 401.573 247.469 C 401.536 247.312 401.611 247.144 401.753 247.067 C 402 246.933 402.318 247.147 402.286 247.426 C 402.265 247.606 402.107 247.748 401.925 247.748 Z M 401.925 247.748" fill="#1e2628" style=""/>
+  </g>
+</svg>
\ No newline at end of file
diff --git a/src/main/java/com/maddyhome/idea/vim/extension/VimExtensionRegistrar.kt b/src/main/java/com/maddyhome/idea/vim/extension/VimExtensionRegistrar.kt
index 3b42bdc0d..445c6a36a 100644
--- a/src/main/java/com/maddyhome/idea/vim/extension/VimExtensionRegistrar.kt
+++ b/src/main/java/com/maddyhome/idea/vim/extension/VimExtensionRegistrar.kt
@@ -53,6 +53,11 @@ internal object VimExtensionRegistrar : VimExtensionRegistrator {
   @Synchronized
   private fun registerExtension(extensionBean: ExtensionBeanClass) {
     val name = extensionBean.name ?: extensionBean.instance.name
+    if (name == "sneak" && extensionBean.name == null) {
+      // Filter out the old ideavim-sneak extension that used to be a separate plugin
+      // https://github.com/Mishkun/ideavim-sneak
+      return
+    }
     if (name in registeredExtensions) return
 
     registeredExtensions.add(name)
diff --git a/src/main/java/com/maddyhome/idea/vim/extension/sneak/IdeaVimSneakExtension.kt b/src/main/java/com/maddyhome/idea/vim/extension/sneak/IdeaVimSneakExtension.kt
new file mode 100644
index 000000000..2bde8d17b
--- /dev/null
+++ b/src/main/java/com/maddyhome/idea/vim/extension/sneak/IdeaVimSneakExtension.kt
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2003-2024 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.extension.sneak
+
+import com.intellij.openapi.actionSystem.DataContext
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.ScrollType
+import com.intellij.openapi.editor.colors.EditorColors
+import com.intellij.openapi.editor.markup.EffectType
+import com.intellij.openapi.editor.markup.HighlighterLayer
+import com.intellij.openapi.editor.markup.HighlighterTargetArea
+import com.intellij.openapi.editor.markup.RangeHighlighter
+import com.intellij.openapi.editor.markup.TextAttributes
+import com.intellij.openapi.util.Disposer
+import com.maddyhome.idea.vim.VimProjectService
+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.api.options
+import com.maddyhome.idea.vim.command.MappingMode
+import com.maddyhome.idea.vim.command.OperatorArguments
+import com.maddyhome.idea.vim.common.TextRange
+import com.maddyhome.idea.vim.extension.ExtensionHandler
+import com.maddyhome.idea.vim.extension.VimExtension
+import com.maddyhome.idea.vim.extension.VimExtensionFacade
+import com.maddyhome.idea.vim.extension.VimExtensionHandler
+import com.maddyhome.idea.vim.helper.StrictMode
+import com.maddyhome.idea.vim.newapi.ij
+import java.awt.Font
+import java.awt.event.KeyEvent
+import java.util.concurrent.Executors
+import java.util.concurrent.TimeUnit
+
+
+private const val DEFAULT_HIGHLIGHT_DURATION_SNEAK: Long = 300
+
+// By [Mikhail Levchenko](https://github.com/Mishkun)
+// Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak
+internal class IdeaVimSneakExtension : VimExtension {
+  override fun getName(): String = "sneak"
+
+  override fun init() {
+    val highlightHandler = HighlightHandler()
+    mapToFunctionAndProvideKeys("s", SneakHandler(highlightHandler, Direction.FORWARD))
+    mapToFunctionAndProvideKeys("S", SneakHandler(highlightHandler, Direction.BACKWARD))
+
+    // workaround to support ; and , commands
+    mapToFunctionAndProvideKeys("f", SneakMemoryHandler("f"))
+    mapToFunctionAndProvideKeys("F", SneakMemoryHandler("F"))
+    mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"))
+    mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"))
+
+    mapToFunctionAndProvideKeys(";", SneakRepeatHandler(highlightHandler, RepeatDirection.IDENTICAL))
+    mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE))
+  }
+
+  private class SneakHandler(
+    private val highlightHandler: HighlightHandler,
+    private val direction: Direction,
+  ) : ExtensionHandler {
+    override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
+      val charone = getChar(editor) ?: return
+      val chartwo = getChar(editor) ?: return
+      val range = Util.jumpTo(editor, charone, chartwo, direction)
+      range?.let { highlightHandler.highlightSneakRange(editor.ij, range) }
+      Util.lastSymbols = "${charone}${chartwo}"
+      Util.lastSDirection = direction
+    }
+
+    private fun getChar(editor: VimEditor): Char? {
+      val key = VimExtensionFacade.inputKeyStroke(editor.ij)
+      return when {
+        key.keyChar == KeyEvent.CHAR_UNDEFINED || key.keyCode == KeyEvent.VK_ESCAPE -> null
+        else -> key.keyChar
+      }
+    }
+  }
+
+  /**
+   * This class acts as proxy for normal find commands because we need to update [Util.lastSDirection]
+   */
+  private class SneakMemoryHandler(private val char: String) : VimExtensionHandler {
+    override fun execute(editor: Editor, context: DataContext) {
+      Util.lastSDirection = null
+      VimExtensionFacade.executeNormalWithoutMapping(injector.parser.parseKeys(char), editor)
+    }
+  }
+
+  private class SneakRepeatHandler(
+    private val highlightHandler: HighlightHandler,
+    private val direction: RepeatDirection,
+  ) : ExtensionHandler {
+    override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
+      val lastSDirection = Util.lastSDirection
+      if (lastSDirection != null) {
+        val (charone, chartwo) = Util.lastSymbols.toList()
+        val jumpRange = Util.jumpTo(editor, charone, chartwo, direction.map(lastSDirection))
+        jumpRange?.let { highlightHandler.highlightSneakRange(editor.ij, jumpRange) }
+      } else {
+        VimExtensionFacade.executeNormalWithoutMapping(injector.parser.parseKeys(direction.symb), editor.ij)
+      }
+    }
+  }
+
+  private object Util {
+    var lastSDirection: Direction? = null
+    var lastSymbols: String = ""
+    fun jumpTo(editor: VimEditor, charone: Char, chartwo: Char, sneakDirection: Direction): TextRange? {
+      val caret = editor.primaryCaret()
+      val position = caret.offset.point
+      val chars = editor.text()
+      val foundPosition = sneakDirection.findBiChar(editor, chars, position, charone, chartwo)
+      if (foundPosition != null) {
+        editor.primaryCaret().moveToOffset(foundPosition)
+      }
+      editor.ij.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE)
+      return foundPosition?.let { TextRange(foundPosition, foundPosition + 2) }
+    }
+  }
+
+  private enum class Direction(val offset: Int) {
+    FORWARD(1) {
+      override fun findBiChar(
+        editor: VimEditor,
+        charSequence: CharSequence,
+        position: Int,
+        charone: Char,
+        chartwo: Char
+      ): Int? {
+        for (i in (position + offset) until charSequence.length - 1) {
+          if (matches(editor, charSequence, i, charone, chartwo)) {
+            return i
+          }
+        }
+        return null
+      }
+    },
+    BACKWARD(-1) {
+      override fun findBiChar(
+        editor: VimEditor,
+        charSequence: CharSequence,
+        position: Int,
+        charone: Char,
+        chartwo: Char
+      ): Int? {
+        for (i in (position + offset) downTo 0) {
+          if (matches(editor, charSequence, i, charone, chartwo)) {
+            return i
+          }
+        }
+        return null
+      }
+
+    };
+
+    abstract fun findBiChar(
+      editor: VimEditor,
+      charSequence: CharSequence,
+      position: Int,
+      charone: Char,
+      chartwo: Char,
+    ): Int?
+
+    fun matches(
+      editor: VimEditor,
+      charSequence: CharSequence,
+      charPosition: Int,
+      charOne: Char,
+      charTwo: Char,
+    ): Boolean {
+      var match = charSequence[charPosition].equals(charOne, ignoreCase = injector.options(editor).ignorecase) &&
+        charSequence[charPosition + 1].equals(charTwo, ignoreCase = injector.options(editor).ignorecase)
+
+      if (injector.options(editor).ignorecase && injector.options(editor).smartcase) {
+        if (charOne.isUpperCase() || charTwo.isUpperCase()) {
+          match = charSequence[charPosition].equals(charOne, ignoreCase = false) &&
+            charSequence[charPosition + 1].equals(charTwo, ignoreCase = false)
+        }
+      }
+      return match
+    }
+  }
+
+  private enum class RepeatDirection(val symb: String) {
+    IDENTICAL(";") {
+      override fun map(direction: Direction): Direction = direction
+    },
+    REVERSE(",") {
+      override fun map(direction: Direction): Direction = when (direction) {
+        Direction.FORWARD -> Direction.BACKWARD
+        Direction.BACKWARD -> Direction.FORWARD
+      }
+    };
+
+    abstract fun map(direction: Direction): Direction
+  }
+
+  private class HighlightHandler {
+    private var editor: Editor? = null
+    private val sneakHighlighters: MutableSet<RangeHighlighter> = mutableSetOf()
+
+    fun highlightSneakRange(editor: Editor, range: TextRange) {
+      clearAllSneakHighlighters()
+
+      this.editor = editor
+      val project = editor.project
+      if (project != null) {
+        Disposer.register(VimProjectService.getInstance(project)) {
+          this.editor = null
+          sneakHighlighters.clear()
+        }
+      }
+
+      if (range.isMultiple) {
+        for (i in 0 until range.size()) {
+          highlightSingleRange(editor, range.startOffsets[i]..range.endOffsets[i])
+        }
+      } else {
+        highlightSingleRange(editor, range.startOffset..range.endOffset)
+      }
+    }
+
+    fun clearAllSneakHighlighters() {
+      sneakHighlighters.forEach { highlighter ->
+        editor?.markupModel?.removeHighlighter(highlighter) ?: StrictMode.fail("Highlighters without an editor")
+      }
+
+      sneakHighlighters.clear()
+    }
+
+    private fun highlightSingleRange(editor: Editor, range: ClosedRange<Int>) {
+      val highlighter = editor.markupModel.addRangeHighlighter(
+        range.start,
+        range.endInclusive,
+        HighlighterLayer.SELECTION,
+        getHighlightTextAttributes(),
+        HighlighterTargetArea.EXACT_RANGE
+      )
+
+      sneakHighlighters.add(highlighter)
+
+      setClearHighlightRangeTimer(highlighter)
+    }
+
+    private fun setClearHighlightRangeTimer(highlighter: RangeHighlighter) {
+      Executors.newSingleThreadScheduledExecutor().schedule({
+        ApplicationManager.getApplication().invokeLater {
+          editor?.markupModel?.removeHighlighter(highlighter) ?: StrictMode.fail("Highlighters without an editor")
+        }
+      }, DEFAULT_HIGHLIGHT_DURATION_SNEAK, TimeUnit.MILLISECONDS)
+    }
+
+    private fun getHighlightTextAttributes() = TextAttributes(
+      null,
+      EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES.defaultAttributes.backgroundColor,
+      editor?.colorsScheme?.getColor(EditorColors.CARET_COLOR),
+      EffectType.SEARCH_MATCH,
+      Font.PLAIN
+    )
+  }
+}
+
+/**
+ * Map some <Plug>(keys) command to given handler
+ *  and create mapping to <Plug>(prefix)[keys]
+ */
+private fun VimExtension.mapToFunctionAndProvideKeys(keys: String, handler: ExtensionHandler) {
+  VimExtensionFacade.putExtensionHandlerMapping(
+    MappingMode.NXO,
+    injector.parser.parseKeys(command(keys)),
+    owner,
+    handler,
+    false
+  )
+  VimExtensionFacade.putKeyMapping(
+    MappingMode.NXO,
+    injector.parser.parseKeys(keys),
+    owner,
+    injector.parser.parseKeys(command(keys)),
+    true
+  )
+}
+
+private fun command(keys: String) = "<Plug>(sneak-$keys)"
diff --git a/src/main/resources/META-INF/includes/VimExtensions.xml b/src/main/resources/META-INF/includes/VimExtensions.xml
index 63d1e30d1..e2006e5a2 100644
--- a/src/main/resources/META-INF/includes/VimExtensions.xml
+++ b/src/main/resources/META-INF/includes/VimExtensions.xml
@@ -124,6 +124,14 @@
         <alias name="chrisbra/matchit"/>
       </aliases>
     </vimExtension>
+
+    <vimExtension implementation="com.maddyhome.idea.vim.extension.sneak.IdeaVimSneakExtension" name="sneak">
+      <aliases>
+        <alias name="https://github.com/justinmk/vim-sneak"/>
+        <alias name="justinmk/vim-sneak"/>
+        <alias name="vim-sneak"/>
+      </aliases>
+    </vimExtension>
   </extensions>
 
   <!--  IdeaVim extensions-->
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetCommandTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetCommandTest.kt
index a3d8381d2..3b81e5aa7 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetCommandTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetCommandTest.kt
@@ -164,19 +164,20 @@ class SetCommandTest : VimTestCase() {
     assertCommandOutput("set all",
       """
         |--- Options ---
-        |noargtextobj        noideatracetime       scroll=0          nosurround
-        |  closenotebooks      ideawrite=all       scrolljump=1      notextobj-entire
-        |nocommentary        noignorecase          scrolloff=0       notextobj-indent
-        |nodigraph           noincsearch           selectmode=         timeout
-        |noexchange          nomatchit             shellcmdflag=-x     timeoutlen=1000
-        |nogdefault            maxmapdepth=20      shellxescape=@    notrackactionids
-        |nohighlightedyank     more                shellxquote={       undolevels=1000
-        |  history=50        nomultiple-cursors    showcmd             unifyjumps
-        |nohlsearch          noNERDTree            showmode            virtualedit=
-        |noideaglobalmode      nrformats=hex       sidescroll=0      novisualbell
-        |noideajoin          nonumber              sidescrolloff=0     visualdelay=100
-        |  ideamarks           octopushandler    nosmartcase           whichwrap=b,s
-        |  ideastrictmode    norelativenumber      startofline         wrapscan
+        |noargtextobj          ideawrite=all       scrolloff=0       notextobj-indent
+        |  closenotebooks    noignorecase          selectmode=         timeout
+        |nocommentary        noincsearch           shellcmdflag=-x     timeoutlen=1000
+        |nodigraph           nomatchit             shellxescape=@    notrackactionids
+        |noexchange            maxmapdepth=20      shellxquote={       undolevels=1000
+        |nogdefault            more                showcmd             unifyjumps
+        |nohighlightedyank   nomultiple-cursors    showmode            virtualedit=
+        |  history=50        noNERDTree            sidescroll=0      novisualbell
+        |nohlsearch            nrformats=hex       sidescrolloff=0     visualdelay=100
+        |noideaglobalmode    nonumber            nosmartcase           whichwrap=b,s
+        |noideajoin            octopushandler    nosneak               wrapscan
+        |  ideamarks         norelativenumber      startofline
+        |  ideastrictmode      scroll=0          nosurround
+        |noideatracetime       scrolljump=1      notextobj-entire
         |  clipboard=ideaput,autoselect,exclude:cons\|linux
         |  excommandannotation
         |  guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175
@@ -277,6 +278,7 @@ class SetCommandTest : VimTestCase() {
       |  sidescroll=0
       |  sidescrolloff=0
       |nosmartcase
+      |nosneak
       |  startofline
       |nosurround
       |notextobj-entire
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetglobalCommandTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetglobalCommandTest.kt
index 4bd1a3108..cf18809a5 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetglobalCommandTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetglobalCommandTest.kt
@@ -350,19 +350,20 @@ class SetglobalCommandTest : VimTestCase() {
     setOsSpecificOptionsToSafeValues()
     assertCommandOutput("setglobal all", """
       |--- Global option values ---
-      |noargtextobj        noideatracetime       scroll=0          nosurround
-      |  closenotebooks      ideawrite=all       scrolljump=1      notextobj-entire
-      |nocommentary        noignorecase          scrolloff=0       notextobj-indent
-      |nodigraph           noincsearch           selectmode=         timeout
-      |noexchange          nomatchit             shellcmdflag=-x     timeoutlen=1000
-      |nogdefault            maxmapdepth=20      shellxescape=@    notrackactionids
-      |nohighlightedyank     more                shellxquote={       undolevels=1000
-      |  history=50        nomultiple-cursors    showcmd             unifyjumps
-      |nohlsearch          noNERDTree            showmode            virtualedit=
-      |noideaglobalmode      nrformats=hex       sidescroll=0      novisualbell
-      |noideajoin          nonumber              sidescrolloff=0     visualdelay=100
-      |  ideamarks           octopushandler    nosmartcase           whichwrap=b,s
-      |  ideastrictmode    norelativenumber      startofline         wrapscan
+      |noargtextobj          ideawrite=all       scrolloff=0       notextobj-indent
+      |  closenotebooks    noignorecase          selectmode=         timeout
+      |nocommentary        noincsearch           shellcmdflag=-x     timeoutlen=1000
+      |nodigraph           nomatchit             shellxescape=@    notrackactionids
+      |noexchange            maxmapdepth=20      shellxquote={       undolevels=1000
+      |nogdefault            more                showcmd             unifyjumps
+      |nohighlightedyank   nomultiple-cursors    showmode            virtualedit=
+      |  history=50        noNERDTree            sidescroll=0      novisualbell
+      |nohlsearch            nrformats=hex       sidescrolloff=0     visualdelay=100
+      |noideaglobalmode    nonumber            nosmartcase           whichwrap=b,s
+      |noideajoin            octopushandler    nosneak               wrapscan
+      |  ideamarks         norelativenumber      startofline
+      |  ideastrictmode      scroll=0          nosurround
+      |noideatracetime       scrolljump=1      notextobj-entire
       |  clipboard=ideaput,autoselect,exclude:cons\|linux
       |  excommandannotation
       |  guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175
@@ -459,6 +460,7 @@ class SetglobalCommandTest : VimTestCase() {
       |  sidescroll=0
       |  sidescrolloff=0
       |nosmartcase
+      |nosneak
       |  startofline
       |nosurround
       |notextobj-entire
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetlocalCommandTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetlocalCommandTest.kt
index 8beecd4d4..9a24456c2 100644
--- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetlocalCommandTest.kt
+++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetlocalCommandTest.kt
@@ -382,19 +382,20 @@ class SetlocalCommandTest : VimTestCase() {
     setOsSpecificOptionsToSafeValues()
     assertCommandOutput("setlocal all", """
       |--- Local option values ---
-      |noargtextobj          ideastrictmode    norelativenumber      startofline
-      |  closenotebooks    noideatracetime       scroll=0          nosurround
-      |nocommentary          ideawrite=all       scrolljump=1      notextobj-entire
-      |nodigraph           noignorecase          scrolloff=-1      notextobj-indent
-      |noexchange          noincsearch           selectmode=         timeout
-      |nogdefault          nomatchit             shellcmdflag=-x     timeoutlen=1000
-      |nohighlightedyank     maxmapdepth=20      shellxescape=@    notrackactionids
-      |  history=50          more                shellxquote={       unifyjumps
-      |nohlsearch          nomultiple-cursors    showcmd             virtualedit=
-      |noideaglobalmode    noNERDTree            showmode          novisualbell
-      |--ideajoin            nrformats=hex       sidescroll=0        visualdelay=100
-      |  ideamarks         nonumber              sidescrolloff=-1    whichwrap=b,s
-      |  idearefactormode=   octopushandler    nosmartcase           wrapscan
+      |noargtextobj        noideatracetime       scrolljump=1      notextobj-entire
+      |  closenotebooks      ideawrite=all       scrolloff=-1      notextobj-indent
+      |nocommentary        noignorecase          selectmode=         timeout
+      |nodigraph           noincsearch           shellcmdflag=-x     timeoutlen=1000
+      |noexchange          nomatchit             shellxescape=@    notrackactionids
+      |nogdefault            maxmapdepth=20      shellxquote={       unifyjumps
+      |nohighlightedyank     more                showcmd             virtualedit=
+      |  history=50        nomultiple-cursors    showmode          novisualbell
+      |nohlsearch          noNERDTree            sidescroll=0        visualdelay=100
+      |noideaglobalmode      nrformats=hex       sidescrolloff=-1    whichwrap=b,s
+      |--ideajoin          nonumber            nosmartcase           wrapscan
+      |  ideamarks           octopushandler    nosneak
+      |  idearefactormode= norelativenumber      startofline
+      |  ideastrictmode      scroll=0          nosurround
       |  clipboard=ideaput,autoselect,exclude:cons\|linux
       |  excommandannotation
       |  guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175
@@ -497,6 +498,7 @@ class SetlocalCommandTest : VimTestCase() {
       |  sidescroll=0
       |  sidescrolloff=-1
       |nosmartcase
+      |nosneak
       |  startofline
       |nosurround
       |notextobj-entire
diff --git a/src/test/java/org/jetbrains/plugins/ideavim/extension/sneak/IdeaVimSneakTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/extension/sneak/IdeaVimSneakTest.kt
new file mode 100644
index 000000000..947f9e66b
--- /dev/null
+++ b/src/test/java/org/jetbrains/plugins/ideavim/extension/sneak/IdeaVimSneakTest.kt
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2003-2024 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 org.jetbrains.plugins.ideavim.extension.sneak
+
+import com.maddyhome.idea.vim.state.mode.Mode
+import org.jetbrains.plugins.ideavim.VimTestCase
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.TestInfo
+
+class IdeaVimSneakTest : VimTestCase() {
+  @Throws(Exception::class)
+  override fun setUp(testInfo: TestInfo) {
+    super.setUp(testInfo)
+    enableExtensions("sneak")
+  }
+
+  @Test
+  fun testSneakForward() {
+    val before = "som${c}e text"
+    val after = "some te${c}xt"
+
+    doTest("sxt", before, after, Mode.NORMAL())
+  }
+
+  @Test
+  fun testSneakForwardVertical() {
+    val before = """som${c}e text
+        another line
+        third line"""
+    val after = """some text
+        another line
+        thi${c}rd line"""
+
+    doTest("srd", before, after, Mode.NORMAL())
+  }
+
+  @Test
+  fun testSneakForwardDoNotIgnoreCase() {
+    val before = "som${c}e teXt"
+    val after = "som${c}e teXt"
+
+    doTest("sxt", before, after, Mode.NORMAL())
+  }
+
+  @Test
+  fun testSneakForwardIgnoreCase() {
+    val before = "som${c}e teXt"
+    val after = "some te${c}Xt"
+
+    enableExtensions("ignorecase")
+
+    doTest("sxt", before, after, Mode.NORMAL())
+    doTest("sXt", before, after, Mode.NORMAL())
+    doTest("sXT", before, after, Mode.NORMAL())
+    doTest("sxT", before, after, Mode.NORMAL())
+  }
+
+  @Test
+  fun testSneakForwardSmartIgnoreCase() {
+    val before = "som${c}e teXt"
+    val after = "some te${c}Xt"
+
+    enableExtensions("ignorecase", "smartcase")
+
+    doTest("sxt", before, after, Mode.NORMAL())
+    doTest("sXt", before, after, Mode.NORMAL())
+    doTest("sXT", before, before, Mode.NORMAL())
+    doTest("sxT", before, before, Mode.NORMAL())
+  }
+
+  @Test
+  fun testSneakForwardAndFindAgain() {
+    val before = "som${c}e text text"
+    val after = "some text te${c}xt"
+
+    doTest("sxt;", before, after, Mode.NORMAL())
+  }
+
+  @Test
+  fun testSneakForwardAndFindReverseAgain() {
+    val before = "some tex${c}t text"
+    val after = "some ${c}text text"
+
+    doTest("ste,", before, after, Mode.NORMAL())
+  }
+
+  @Test
+  fun testSneakBackward() {
+    val before = "some tex${c}t"
+    val after = "so${c}me text"
+
+    doTest("Sme", before, after, Mode.NORMAL())
+  }
+
+  @Test
+  fun testSneakBackwardVertical() {
+    val before = """some text
+        another line
+        thi${c}rd line"""
+    val after = """so${c}me text
+        another line
+        third line"""
+
+    doTest("Sme", before, after, Mode.NORMAL())
+  }
+
+  @Test
+  fun testSneakBackwardAndFindAgain() {
+    // caret has to be before another character (space here)
+    val before = "some text text${c} "
+    val after = "some ${c}text text "
+
+    doTest("Ste;", before, after, Mode.NORMAL())
+  }
+
+  @Test
+  fun testSneakBackwardAndFindReverseAgain() {
+    val before = "some tex${c}t text"
+    val after = "some text ${c}text"
+
+    doTest("Ste,", before, after, Mode.NORMAL())
+  }
+
+  @Test
+  fun testEndOfFile() {
+    val before = """first line
+        some te${c}xt
+        another line
+        last line."""
+    val after = """first line
+        some text
+        another line
+        last lin${c}e."""
+
+    doTest("se.", before, after, Mode.NORMAL())
+  }
+
+  @Test
+  fun testStartOfFile() {
+    val before = """first line
+        some text
+        another${c} line
+        last line."""
+    val after = """${c}first line
+        some text
+        another line
+        last line."""
+
+    doTest("Sfi", before, after, Mode.NORMAL())
+  }
+
+  @Test
+  fun testEscapeFirstChar() {
+    val before = "so${c}me dwarf"
+    val after = "some ${c}dwarf"
+
+    doTest("sa<ESC>sdw", before, after, Mode.NORMAL())
+  }
+}