Vim matchit.vim by Benji Fisher ported into Emacs.
Press “%” to jump between matched tags in Emacs. For example, in HTML “<div>” and “</div>” are a pair of tags.
Many modern languages are supported:
- HTML
- Python
- Java
- C++/C
- Javascript
- JSON
- Perl
- Latex
- CMake
- Org-mode (match tag of org-mode and tags of other languages embedded in org file)
- Ruby
- Bash
- Lua
- PHP
- Fortran
- SQL
- Laravel Blade Templating
- Vim script
- Emacs email (mesage-mode)
This package uses Evil as its vi layer!
Tested on Emacs 23.4, 24.3, 24.4
- No learning curve. Press “%” to jump. That’s all!
- Stable! The ONLY dependency is evil-mode. It works in any major modes (web-mode, html-mode …).
- 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.
- Long term support
It’s already uploaded to http://melpa.org/.
Insert below code into your ~/.emacs:
(require 'evil-matchit)
(global-evil-matchit-mode 1)
Alternatively, you can enable evil-matchit-mode along a major mode by adding `turn-on-evil-matchit-mode’ to the mode hook.
Press “%” to jump inside between tags in evil-normal-mode. Please note evil-matchit is smart enough to detect the beginning of tag automatically.
Inner/outer text object “%” is also created. It roughly equal 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%”
Please note “3%” will jump 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.
Let’s use html tag matching as an example.
html tags are automatically supported in sgml-mode, nxml-mode, web-mode, html-mode and nxhtml-mode,.
Let’s say you want a new major-mode “my-mode” to do the html tag matching. Easy. Please add below line into your ~/.emacs:
(plist-put evilmi-plugins my-mode '((evilmi-html-get-tag evilmi-html-jump)))
Further explanation: “evilmi-html-get-tag” and “evilmi-html-jump” are existing APIs which are defined in evil-matchit-html.el. The above line means that in my-mode, use “evilmi-html-get-tag” to find open/closed tag in current line and use “evilmi-html-jump” to jump to matched closed/open tag.
If you embed python language in a html file. You can match both html tags and python statement by inserting below code into your ~/.emacs:
(plist-put evilmi-plugins web-mode
'((evilmi-python-get-tag evilmi-python-jump)
(evilmi-html-get-tag evilmi-html-jump)
))
You can define your own key bindings instead using evil-matchit default key binding.
All you need to do is to define function evilmi-customize-keybinding before turning on evil-mathcit-mode:
(defun evilmi-customize-keybinding ()
(evil-define-key 'normal evil-matchit-mode-map
"%" 'evilmi-jump-items))
(global-evil-matchit-mode 1)
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.
Simple. You only need define two functions and tell evil-matchit in which major-mode they should be used.
Here is a complete sample:
;; 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 ()
(let (rlt )
(setq rlt '(position-of-open-end "anything-you-like" "anything-you-like")
rlt
)
)
;; @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 (rlt NUM)
(message "rlt=%s" rlt)
;; if we need select region between tags (including tags itself)
;; we get the beginning of region by reading the first element of
;; rlt
(push-mark (nth 0 rlt) 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
(plist-put evilmi-plugins mylang-mode '((evilmi-mylang-get-tag evilmi-mylang-jump)))
Place above code into your ~/.emacs, after the line “(global-evil-matchit-mode 1)”
Please note SDK is OPTIONAL! You don’t need SDK to write a plugin for evil-matchit.
You can check the evil-matchit-script.el for the sample on how to use SDK.
I attached the full content of evil-matchit-script.el here:
(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 FN_EXIT or 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)
Simple, eh?
Basically you just need:
- copy the content of evil-matchit-script.el to your ~/.emacs
- Search and replace the string “_script” with “_mylang” to respect the name space
- Update the value of evilmi–mylang-match-tags
- Notify the evil-matchit about support for new commands. As I mentioned before, it’s just one line code in ~/.emacs
(plist-put evilmi-plugins mylang-mode '((evilmi-mylang-get-tag evilmi-mylang-jump)))
Tweak your code a little bit to make it a plugin and ask me to merge it into upstream.
Please check “evil-matchit-latex.el” for technical details about plugin.
Key points about code quality of plugin:
- minimum dependency. For example, if your plugin for html template files is only some web-mode API wrapper, it will break when user don’t have web-mode
- support emacs 23
- performance is the first priority
Report bugs at https://github.com/redguardtoo/evil-matchit.