hlissner / evil-snipe

2-char searching ala vim-sneak & vim-seek, for evil-mode
MIT License
336 stars 25 forks source link
emacs emacs-packages evil vim-sneak

Made with Doom Emacs Release tag MELPA Build status

evil-snipe

Sniper!

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.

Install

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)

Usage

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:

Customization

Search scope

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

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.

Examples

Faces

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)
                        (evil-snipe-enable-highlight)
                        (evil-snipe-enable-incremental-highlight))))

(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)

Appendix

Other settings

Functions

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))