evil-matchit

by redguardtoo

redguardtoo / evil-matchit

Vim matchit ported into Emacs

222 Stars 25 Forks Last release: Not found 215 Commits 43 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

  • evil-matchit [[https://travis-ci.org/redguardtoo/evil-matchit][https://travis-ci.org/redguardtoo/evil-matchit.svg?branch=master]] [[http://melpa.org/#/evil-matchit][file:http://melpa.org/packages/evil-matchit-badge.svg]] [[http://stable.melpa.org/#/evil-matchit][file:http://stable.melpa.org/packages/evil-matchit-badge.svg]]

Vim [[http://www.vim.org/scripts/script.php?script_id=39][matchit.vim]] by Benji Fisher is ported into Emacs.

Press "%" to jump between matched tags in Emacs. For example, in HTML "

" and "
" are a pair of tags.

Many modern languages are supported: - HTML - Python - Java - C++/C - Javascript - Typescript - React JSX (rjsx-mode, react-mode) - JSON - OCaml - Markdown - Perl - Latex - CMake - Org-mode (match tag of org-mode and tags of other languages embedded in org file) - Ruby - Elixir - Bash - Lua - PHP - Fortran - SQL - Laravel Blade Templating - Vim script - Verilog - Diff/Patch - Shell/Terminal bundled in Emacs - Emacs email (message-mode)

This package uses Evil as its vi layer!

Tested on Emacs 24.4, 24.5, 25.1, 26, 27, 28

  • Why use evil-matchit
  • No learning curve. Press "%" to jump. That's all!
  • Stable. Usage of Evil API is minimized
  • Perfect integration with Evil
  • Support any modern languages (html/java/c/c++/python/latex/javascript ...)
  • Powerful. If you mix jsp, freemarker, html, jquery template or any weird syntax into one file, it still works!
  • Extendable. Write a plugin for it takes only 5 minutes

Screen cast for python: [[file:screencast.gif]]

  • Install It's already uploaded to [[http://melpa.org/]].

  • Set up Insert below code into =~/.emacs=:

    +BEGIN_SRC lisp

    (require 'evil-matchit) (global-evil-matchit-mode 1)

    +END_SRC

Alternatively, you can enable =evil-matchit-mode= along a major mode by adding =turn-on-evil-matchit-mode= to the mode hook.

  • Usage Press "%" to jump inside between tag pair in normal mode or visual mode (you press "v" to switch to visual mode). Please note evil-matchit is smart enough to detect the tag automatically.

Tag pair could be open/closed html tag, or character pair like "{}" "[]" "()", or the single/double quote(s) at the two ends of the string.

Inner/outer text object "%" is also created. It roughly equals the region when you press "%" from evil-matchit.

Press "va%" to select line(s) wrapped by tags including tags themselves. =M-x evilmi-select-items= does the same thing.

Press "da%" to delete line(s) wrapped by tags including tags themselves. =M-x evilmi-delete-items= does the same thing.

All commands support numeric argument like "3%", "5va%" or "9da%"

Pressing "3%" jumps to a line 3 percentage down the file. It's the default behavior in original =evil-mode=. You can =(setq evilmi-may-jump-by-percentage nil)= to turn off this feature. Then "3%" will jump 3 times.

Please note only =evil-visual-state= and =evil-normal-state= are supported.

If you need visually select lines, I strongly recommend using =evilmi-select-items= instead.

This is actually an advantage of Emacs, you can tweak the select region without go into visual state at all.

  • Advanced tips ** Support new major modes In order to apply three matching rules =evilmi-template=, =evilmi-simple=, and =evilmi-html= on =mhtml-mode=, please insert below code after your evil-nerd-commenter setup: #+beginsrc elisp (evilmi-load-plugin-rules '(mhtml-mode) '(template simple html)) #+endsrc ** Use evilmi-select-items instead press "%" in evil-visual-state =evilmi-select-items= is more robust and provides more functionality. It works even when =evil-mode= is not loaded.

So you'd better stick to =evilmi-select-item= if possible. ** Add new tags into existing languages Use ruby as an example.

If you want to add more tags into ruby, you can do two things: - You need define the regular expression to extract keyword - You need define the open/middle/closed tags

Open evil-matchit-ruby.el whole structure is like,

+begin_src elisp

(defvar evilmi-ruby-extract-keyword-howtos '()) (defvar evilmi-ruby-match-tags '()) ;; more code here ... (provide 'evil-matchit-ruby)

+end_src

So you configuration in =~/.emacs= is as below:

+begin_src elisp

(with-eval-after-load "evil-matchit-ruby" (push '("^[ \t]\([a-z]+\)\( .\| *\)$" 1) evilmi-ruby-extract-keyword-howtos) (push '(("unless" "if") ("elsif" "else") "end")) evilmi-ruby-match-tags)

+end_src

** Re-define keybinding All you need to do is to define function =evilmi-customize-keybinding= before turning on =evil-matchit-mode=:

The shortcut =%= is defined in =evilmi-shortcut=. It's the name of text object and shortcut of =evilmi-jump-items=. Some people prefer set it to "m".

Change keybinding of =evilmi-jump-items= and name of the text object,

+begin_src elisp

(setq evilmi-shortcut "m") (global-evil-matchit-mode 1)

+end_src

Change keybinding only,

+BEGIN_SRC elisp

(defun evilmi-customize-keybinding () (evil-define-key 'normal evil-matchit-mode-map "%" 'evilmi-jump-items)) (global-evil-matchit-mode 1)

+END_SRC

** Jump between the two end of the "string" Please note the definition of "string" could be customized by user.

For example, we could treat C comment as string wrapper by "/".

Here is the setup to jump between the two ends of the C comment:

+begin_src elisp

(setq evilmi-quote-chars (string-to-list "'\"/"))

+end_src

** Match case-sensitive tags? It's decided by the Emacs global variable "case-fold-search". You need not care about it because the major mode will set this flag automatically. ** Python You can turn on =evilmi-always-simple-jump= to match brackets at first.

Thus, you disable our advanced algorithm which I highly recommend.

Some people may prefer simpler algorithm in =python-mode=. * Developer guide ** Create plugin to support new language Simple. You only need define two functions and tell evil-matchit in which major-mode they should be used.

A complete setup:

+BEGIN_SRC elisp

;; detect tag in current line and return the result in variable rlt ;; the rlt will be used by evilmi-mylang-jump as the first parameter. ;; if NO tag found, the rlt SHOULD be nil ;; ;; @return the data to be used by evilmi-mylang-jump which should be a list ;; the first element of the list is the position of cursor before jump ;; we use it to select/delete tag. The other elements of the list could ;; be any data type (defun evilmi-mylang-find-tag () (list position-of-open-end "anything-you-like" "anything-you-like"))

;; @parama rlt result from evilmi-mylang-find-tag ;; @param NUM numeric argument when user press "%" to match tag ;; @return the matching tag position in theory, useful only for ;; selecting or deleting text between matching tags and tags (defun evilmi-mylang-jump (info num) (message "info=%s" info) ;; if we need select region between tags (including tags itself) ;; we get the beginning of region by reading the first element of ;; info (push-mark (nth 0 info) t t) ;; say 999 is the where we jump to (goto-char 999) ;; If you need know where is the end of the region for region operation, ;; you need return the end of region at the end of function ;; region operation means selection/deletion of region. 888)

;; Notify evil-matchit how to use above functions (evilmi-load-plugin-rules '(mylang-mode) '(mylang))

+END_SRC

Place above code into your =~/.emacs=, after the line "(global-evil-matchit-mode 1)" ** Use SDK For example, it only takes 3 steps to create a new rule =script= to match tags in script like Ruby/Lua/Bash/VimScript,

Step 1, create =evil-matchit-script.el=,

+BEGIN_SRC elisp

(require 'evil-matchit-sdk)

;; ruby/bash/lua/vimrc (defvar evilmi-script-match-tags '((("unless" "if") ("elif" "elsif" "elseif" "else") ( "end" "fi" "endif")) ("begin" ("rescue" "ensure") "end") ("case" ("when" "else") ("esac" "end")) (("fun!" "function!" "class" "def" "while" "function" "do") () ("end" "endfun" "endfunction")) ("repeat" () "until")) "The table we look up match tags. This is a three column table. The first column contains the open tag(s). The second column contains the middle tag(s). The third column contains the closed tags(s). The forth optional column defines the relationship between open and close tags. It could be MONOGAMY ")

;;;###autoload (defun evilmi-script-get-tag () (evilmi-sdk-get-tag evilmi-script-match-tags evilmi-sdk-extract-keyword-howtos))

;;;###autoload (defun evilmi-script-jump (rlt num) (evilmi-sdk-jump rlt num evilmi-script-match-tags evilmi-sdk-extract-keyword-howtos))

(provide 'evil-matchit-script)

+END_SRC

Step 2, make sure the directory of =evil-matchit-script.el= is added into =load-path=.

Step 3, add below code to =~/.emacs.=,

+BEGIN_SRC lisp

(evilmi-load-plugin-rules '(ruby-mode lua-mode) '(script))

+END_SRC

** APIs - evilmi-load-plugin-rules * Contact me Report bugs at [[https://github.com/redguardtoo/evil-matchit]].

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.