Evil-mode and camelcase movement

INA Lintaro ina at kuis.kyoto-u.ac.jp
Mon Jun 4 13:02:25 CEST 2012


From: Simon Carter <bbbscarter at gmail.com>
Subject: Evil-mode and camelcase movement
Date: Sat, 2 Jun 2012 20:20:40 +0100

> I was wondering if there was a canonical method for allowing camelcase and underscore word movement in evil-mode.

If you are looking for Evil implementation of camelcasemotion.vim
(http://www.vim.org/scripts/script.php?script_id=1905), then you need
to redefine evil-{forward,backward}-word-{begin,end} and
evil-{a,inner}-word with somehow dealing with word boundaries of
CamelCase/snake_case words.

I think using search-forward-regexp is not a good idea for two reasons:

a. There are many other uppercase letters other than [A-Z] and it's
   too hard to write them all in a regular expression. You can see the
   list of uppercase letters:
   http://www.fileformat.info/info/unicode/category/Lu/list.htm

b. I had a hard experience dealing with word boundaries separated by
   non-whitespace characters, when I was making a Evil patch to
   support Japanese words (a CJK patch 7adc3b1).

For these reasons, I recommend a way using Emacs' native support of
character categories and word boundaries just like in the CJK patch.

First, we need to define new character categories for
uppercase/lowercase letters and underscore.

  (define-category ?U "Uppercase")
  (define-category ?L "Lowercase")
  (define-category ?_ "Underscore")
  (modify-category-entry (cons ?A ?Z) ?U)
  (modify-category-entry (cons ?a ?z) ?L)
  (modify-category-entry ?_ ?_)

(We have (cons ?A ?Z)/(cons ?a ?z) here but I will fix this later to
cover all uppercase/lowercase letters.)

Then, define word boundaries by indicating which categories of
successive characters are separating CamelCase/snake_case words.

  (setq evil-little-word-separating-categories
        '((?L . ?U) (?_ . ?L) (?_ . ?U)))
  (setq evil-little-word-combining-categories
        '())

Finally, redefine movement commands by using these definitions. Since
Evil already have mechanism to deal with Emacs' word boundaries, all
we have to do is to activate the above definitions before calling the
original movement commands.  This can be done by the following macro.

  (defmacro evil-with-little-word (&rest body)
    (declare (indent defun)
             (debug t))
    `(let ((evil-cjk-emacs-word-boundary t) ; turn off CJK word boundary
           (word-separating-categories evil-little-word-separating-categories)
           (word-combining-categories evil-little-word-combining-categories))
       , at body))

With this macro, we are able to have redefinitions as follows:

  (evil-define-motion evil-forward-little-word-begin (count)
    "Move the cursor to the beginning of the COUNT-th next little word."
    :type exclusive
    (evil-with-little-word (evil-forward-word-begin count)))

  (evil-define-motion evil-backward-little-word-begin (count)
    "Move the cursor to the beginning of the COUNT-th previous little word."
    :type exclusive
    (evil-with-little-word (evil-backward-word-begin count)))

  ...

I know this is a way-too-long solution, so I put them together with
covering all uppercase/lowercase letters into an Evil plugin:
https://github.com/tarao/evil-plugins/blob/master/evil-little-word.el

I hope this helps you.

-- 
Lintaro INA
mail: ina at kuis.kyoto-u.ac.jp



More information about the implementations-list mailing list