I use Emacs together with Evil mode, a combo I settled on after many years in part because of its extensibility. Not long after I started using Evil mode, I wanted Vim text objects representing C++ functions. This turned out to be a good exercise in learning about Evil mode. It also shows off the things Emacs can help you do with just a little study.
Evil text objects
With Vim, you can edit by character or word, but also by sentence, paragraph, and even specially created semantic units appropriate for a particular programming language. These are called text objects. They’re great because you can perform common operations in just a few keystrokes, things like copying, deleting, moving, and otherwise changing.
I edit C++ code a lot, and it’s useful to be able to refer to functions while
editing. Function text objects are convenient but not built in to Evil mode (that I
know of). But Evil mode let’s us define our own text objects using its
evil-define-text-object
function. How cool is that?
The Emacs lisp pillar
That’s the power of Evil mode, but the real power is piggybacking off Emacs
itself. Emacs includes a mark-defun
function that works in tons of major
modes, including C/C++ mode. We get, essentially for free, function, class, and
struct text objects. The outer text object below works in lots of modes other than
C++ mode. I use it for Python quite a bit, and Emacs lisp as well.
Here’s the version I use currently. Please note that I’m an amateur Emacs lisper; I’m sure this code could be improved in lots of ways.
(evil-define-text-object rsb/textobj-inner-c-defun (count &optional beg end type)
(save-excursion
(mark-defun)
(re-search-forward "{")
(exchange-point-and-mark)
(re-search-backward "}")
(evil-range (region-beginning) (region-end) type :expanded t)))
(evil-define-text-object rsb/textobj-outer-c-defun (count &optional beg end type)
:type line
(save-excursion
(mark-defun)
(if (looking-at "[:space:]*$")
(forward-line))
(exchange-point-and-mark)
(unless (save-excursion
(forward-line)
(looking-at "[:space:]*$"))
(forward-line))
(evil-range (region-beginning) (region-end) type :expanded t)))
Bind these to Evil’s inner
and outer
keymaps, respectively, and go to town!
Approx. 331 words, plus code