Software! Math! Data! The blog of R. Sean Bowman
The blog of R. Sean Bowman
November 26 2016

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