One thing I love about Emacs is that it is so extensible. I was an avid Vim user for a long time and it took me several tries to switch to Emacs, but I will never look back. With the Vimpulse addon to the built-in viper-mode, Emacs becomes a powerful Vim impersonator, capable of being extended with Emacs-Lisp.

A lot of my time is spent doing web development, and so strong HTML editing ability is essential in any editor I use. Vim has this amazing concept of text objects which allows you to be very efficient when transforming text; one of the most useful when doing web development is therefore the XML/HTML tag text object. I would use this all the time by invoking cit to change text between a pair of tags. To demonstrate, here's a piece of text you might have sitting in a file:

<title>Hello |World</title>

Now with the cursor somewhere in between the tags (visualized above by the pipe, '|') we can go into command mode and type cit and the above will be replaced with:


Ready to be edited!

Well sadly, Vimpulse doesn't have this feature by default, probably one of the very few parts of Vim that it doesn't emulate in fact! (NB. Evil contains emulation for this already) But not to worry, we can just add it in! This is Emacs after all.

So how do we instruct Vimpulse to recognise another text object? Turns out it's REALLY easy:

(vimpulse-define-text-object vimpulse-tag (arg)
  "Select a tag"
  :keys '("at" "it") ; The keys that will trigger this text object
  (vimpulse-inner-object-range ; We're basically defining a range on which to act upon
   (lambda (arg) (search-backward ">" nil nil arg) (forward-char)) ; Look to the left for a ">" character
   (lambda (arg) (search-forward "<" nil nil arg) (backward-char)) ; Look to the right for a "<" character
   )) ; and return the distance between them!

Note that this isn't a perfect implementation, but for the purposes of this demonstration it will suffice! We can now cit and dit to our hearts content! Hail Emacs!

For a more robust implementation it may be worth looking at using sgml-skip-tag-backward and sgml-skip-tag-forward for movement instead.

Happy Hacking!

(Maybe) Related posts: