thegreatape / evil-matchit

Vim matchit ported into Emacs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

evil-matchit (v1.5.2)

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

Why use evil-matchit

  • 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

Screen cast for python: screencast.gif

Install

It’s already uploaded to http://melpa.org/.

Set up

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.

Usage

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.

Advanced tips

Support more major modes

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.

Mixed languages in one html template file is supported

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

Re-define keybinding

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)

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.

Developer guide

Write Emacs Lisp to support new language

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

Use SDK

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

Share your code to the world

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

Contact me

Report bugs at https://github.com/redguardtoo/evil-matchit.

About

Vim matchit ported into Emacs


Languages

Language:Emacs Lisp 99.7%Language:Shell 0.3%