Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time
June 6, 2021 22:58
June 6, 2021 23:09
June 6, 2021 22:58

Made with Doom Emacs Release tag MELPA Build status



Evil-snipe emulates vim-seek and/or vim-sneak in evil-mode.

It provides 2-character motions for quickly (and more accurately) jumping around text, compared to evil's built-in f/F/t/T motions, incrementally highlighting candidate targets as you type.


Evil-snipe is available on MELPA.

M-x package-install evil-snipe

(require 'evil-snipe)

evil-snipe comes with two global modes: evil-snipe-mode and evil-snipe-override-mode, and two local modes: evil-snipe-local-mode and evil-snipe-override-local-mode.

You can either a) enable one or both globally:

(evil-snipe-mode +1)
(evil-snipe-override-mode +1)

;; and disable in specific modes
(push 'python-mode evil-snipe-disabled-modes)

;; or disable it manually
(add-hook 'python-mode-hook #'turn-off-evil-snipe-mode)
(add-hook 'python-mode-hook #'turn-off-evil-snipe-override-mode)

Or b) enable one or both locally, where you need it:

(add-hook 'python-mode-hook 'turn-on-evil-snipe-mode)
(add-hook 'python-mode-hook 'turn-on-evil-snipe-override-local-mode)


By default, snipe only binds s (forward)/S (backward) to evil-snipe-s and evil-snipe-S, respectively. In operator mode, snipe is bound to z/Z and x/X (exclusive).

The last snipe can be repeated with s/S after a successful snipe (or with s+RET).

Evil-snipe can override evil-mode's native motions with 1-char sniping:

;; Globally
(evil-snipe-override-mode 1)

;; Or locally
(add-hook 'ruby-mode-hook 'evil-snipe-override-local-mode)

The benefit of this is:

  • Incremental highlighting
  • You can repeat searches with f, F, t and T (ala Clever-F)
  • ; and , are available for repeating searches (and won't interfere with the original maps; they take effect only after a successful snipe)
  • A more streamlined experience in general


Search scope

These three variables determine the scope of snipes (and the incremental highlighter):

  • evil-snipe-scope (default: line)
  • evil-snipe-repeat-scope (default: whole-line) Scope while repeating searches with evil-snipe-repeat or evil-snipe-repeat-reverse.
  • evil-snipe-spillover-scope (default: nil) Scope to expand to when a snipe fails. Only useful if set to a broader scope than evil-snipe-scope.

These are the possible settings:

Value Description
'line rest of the current line after cursor (vim-seek behavior)
'buffer rest of the buffer after cursor (vim-sneak behavior)
'visible the rest of the visible buffer after cursor
'whole-line same as 'line, but highlights on either side of cursor
'whole-buffer same as 'buffer, but highlights all matches in buffer
'whole-visible same as 'visible, but highlights all visible matches in buffer

Character aliases

Specific characters can be aliased to regex patterns by modifying evil-snipe-aliases.


  • To map [ to any opening parentheses or bracket in all modes:

    (push '(?\[ "[[{(]") evil-snipe-aliases)

    Therefore, sa[ will match a[, a{ or a(

  • To map : to a python function (but only in python-mode):

    (add-hook 'python-mode-hook
      (lambda ()
        (make-variable-buffer-local 'evil-snipe-aliases)
        (push '(?: "def .+:") evil-snipe-aliases)))


  • evil-snipe-first-match-face: The first highlighted match.
  • evil-snipe-matches-face: The rest of the highlighted matches.

Sniping in visual mode

To avoid binding conflicts, evil-snipe has no visual mode bindings. You can add them with:

(evil-define-key 'visual evil-snipe-local-mode-map "z" 'evil-snipe-s)
(evil-define-key 'visual evil-snipe-local-mode-map "Z" 'evil-snipe-S)

Integration into avy/evil-easymotion

This will allow you to quickly hop into avy/evil-easymotion right after a snipe.

(define-key evil-snipe-parent-transient-map (kbd "C-;")
  (evilem-create 'evil-snipe-repeat
                 :bind ((evil-snipe-scope 'buffer)

(Thanks to PythonNut for this. More info here)

Conflicts with other plugins

It seems evil-snipe-override-mode causes problems in Magit buffers, to fix this:

(add-hook 'magit-mode-hook 'turn-off-evil-snipe-override-mode)


Other settings

  • evil-snipe-enable-highlight (default: t) Highlight first match.
  • evil-snipe-enable-incremental-highlight (default: t) Incrementally highlight all matches in scope.
  • evil-snipe-override-evil-repeat-keys (default: t) Whether or not evil-snipe will override evil's default ; and , mappings with snipe's (when evil-snipe-override-mode is on).
  • evil-snipe-repeat-keys (default t) If non-nil, pressing s/S after a search will repeat it. If evil-snipe-override-evil is non-nil, this applies to f/F/t/T as well.
  • evil-snipe-show-prompt (default t) Whether or not to show the "N>" prompt.
  • evil-snipe-smart-case (default nil) If non-nil, searches will be case-insenstive unless your search contains a capital letter.
  • evil-snipe-auto-scroll (default nil) If non-nil, the window will scroll to follow the cursor.
  • evil-snipe-auto-disable-substitute (default: t) Whether or not evil's default substitute mappings (s/S) are unset. They can sometimes interfere with snipe. Must be set before evil-snipe is loaded.
  • evil-snipe-skip-leading-whitespace (default t) If non-nil, sniping will skip over leading whitespace when you search for whitespace.
  • evil-snipe-tab-increment (default nil) If non-nil, pressing TAB in the snipe prompt will increase the size of the snipe buffer.
  • evil-snipe-use-vim-sneak-bindings (default nil) If non-nil, evil-snipe binds z/Z to exclusive sniping in operator state, but leaves the x/X bindings free. This mirrors the default bindings of vim-sneak, and frees up cx/cX to be used by evil-exchange.


  • evil-snipe-mode / evil-snipe-local-mode
  • evil-snipe-override-mode / evil-snipe-override-local-mode
  • evil-snipe-repeat / evil-snipe-repeat-reverse
  • evil-snipe-s / evil-snipe-S: inclusive 2-char sniping
  • evil-snipe-x / evil-snipe-X: exclusive 2-char sniping
  • evil-snipe-f / evil-snipe-F: inclusive 1-char sniping
  • evil-snipe-t / evil-snipe-T: exclusive 1-char sniping

Default keybindings

(evil-define-key '(normal motion) evil-snipe-local-mode-map
  "s" 'evil-snipe-s
  "S" 'evil-snipe-S)

(evil-define-key 'operator evil-snipe-local-mode-map
  "z" 'evil-snipe-s
  "Z" 'evil-snipe-S
  "x" 'evil-snipe-x
  "X" 'evil-snipe-X)

(evil-define-key 'motion evil-snipe-override-local-mode-map
  "f" 'evil-snipe-f
  "F" 'evil-snipe-F
  "t" 'evil-snipe-t
  "T" 'evil-snipe-T)

(when evil-snipe-override-evil-repeat-keys
  (evil-define-key 'motion map
    ";" 'evil-snipe-repeat
    "," 'evil-snipe-repeat-reverse))