Discussion:
Evil-mode and camelcase movement
Simon Carter
2012-06-02 19:20:40 UTC
Permalink
Hi!

I was wondering if there was a canonical method for allowing camelcase and underscore word movement in evil-mode. I've looked at the suggestion in http://slashusr.wordpress.com/2011/09/15/heretical-confessions-of-an-emacs-addict-joy-of-the-vim-text-editor/, but that solution was somewhat limited (no backward movement, and it only worked with other operators, rather than as an actual method of movement).

Many thanks, and apologies in advance if I've missed something obvious!

Simon
Michael Markert
2012-06-02 20:14:17 UTC
Permalink
Post by Simon Carter
I was wondering if there was a canonical method for allowing camelcase
and underscore word movement in evil-mode.
Not to my knowledge.
Post by Simon Carter
I've looked at the suggestion in
http://slashusr.wordpress.com/2011/09/15/heretical-confessions-of-an-emacs-addict-joy-of-the-vim-text-editor/,
but that solution was somewhat limited (no backward movement, and it
only worked with other operators, rather than as an actual method of
movement).
Ok this is the solution posted:

(evil-define-motion evil-little-word (count)
:type exclusive
(let ((case-fold-search nil))
(forward-char)
(search-forward-regexp "[_A-Z]\\|\\W" nil t)
(backward-char)))

it only works with other operators, because it was defined in
`evil-operator-state-map' define in in `evil-motion-state-map' and it
will work on its own (but you maybe want to chose another key sequence
to not shadow `l').

Here is code for the backword movement:

(evil-define-motion evil-little-word-backward (count)
:type inclusive
(let ((case-fold-search nil))
(backward-char)
(search-backward-regexp "[_A-Z]\\|\\W" nil t)))

You have to define it as well as keys, of course.

Another change you want to make is the inclusion of `count':

(evil-define-motion evil-little-word (count)
:type exclusive
(let ((case-fold-search nil))
(forward-char)
(search-forward-regexp "[_A-Z]\\|\\W" nil t count)
(backward-char)))

respective

(evil-define-motion evil-little-word-backward (count)
:type inclusive
(let ((case-fold-search nil))
(backward-char)
(search-backward-regexp "[_A-Z]\\|\\W" nil t count)))

As there's `subword-mode' for vanilla Emacs maybe it would be nice to
change the word movements (b, e, w and their ilk) to subword movements
if subword-move is active for a tighter emacs integration?

Michael
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 835 bytes
Desc: not available
Url : https://lists.ourproject.org/pipermail/implementations-list/attachments/20120602/21416fc5/attachment.pgp
INA Lintaro
2012-06-04 11:02:25 UTC
Permalink
From: Simon Carter <bbbscarter at gmail.com>
Subject: Evil-mode and camelcase movement
Date: Sat, 2 Jun 2012 20:20:40 +0100
Post by Simon Carter
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
Loading...