32

I use Emacs + evil mode.

When I position the cursor on e (in normal state) in a chunk of text such as abc_def_ghi, ciw (change inner word) changes only the def part of the string, while Vim would change the entire abc_def_ghi.

How can I force evil to treat the underscore as Vim does (ie, as part of the word)?

25

The simplest workaround: use ciW to select a whitespace-delimited word.

The bigger issue has to do with the value of the _ character in the syntax table. The issue is that _ is, by default, a symbol constituent in the syntax table, and you want to treat it as a word constituent.

If you're using emacs 24.4, you could try enabling superword-mode. I haven't tried it myself, so your mileage may vary.

An alternative is simply to modify the syntax table yourself and tell Emacs you want it to treat the _ character as a word constituent, like so:

(modify-syntax-entry ?_ "w")

After you do that, ciw works as you want it to, such that it will select all of abc_def_ghi rather than just def.

Doing it this way, however, may be overkill, especially if you only want the _ to count as part of the word for the text object. Instead, you can advise evil-inner-word as follows:

(defadvice evil-inner-word (around underscore-as-word activate)
  (let ((table (copy-syntax-table (syntax-table))))
    (modify-syntax-entry ?_ "w" table)
    (with-syntax-table table
      ad-do-it)))

Now, _ is still a symbol constituent for everything except for the inner-word text object, so ciw will do precisely what you want without touching the functionality of anything else.

You can read more about modifying syntax tables on the EmacsWiki node.

  • This works like a charm. To alter all the movements (w, aw, etc.), you can do the same with the following functions too: evil-forward-word-begin,evil-forward-word-end,evil-backward-word-begin,evil-backward-word-end,evil-a-word,evil-inner-word. Note: you need to change underscore-as-word into different, unique (arbitrary) values. – sitaktif Oct 18 '16 at 6:33
  • (modify-syntax-entry ?_ "w") didn't work for me for some reason. Doing it interactively didn't work either. Any idea why or how I can debug this? I'm on Spacemacs in case that's relevant. – hsribei Feb 17 '18 at 13:23
  • 1
    This explains my problem. Apparently you have to refer to the target syntax table explictly. In my case I used (modify-syntax-entry ?_ "w" rust-mode-syntax-table), then it worked. – hsribei Feb 17 '18 at 13:28
  • What are the unintended effects of modifying the syntax entry? – luntain Feb 28 '18 at 21:40
  • FTR: superword-mode doesn't affect movement in evil-mode. So it's not a constituent to modifying syntax tables. – Hi-Angel Dec 24 '18 at 13:10
26

The following snippet will make Evil treat an Emacs symbol as a word.

(with-eval-after-load 'evil
    (defalias #'forward-evil-word #'forward-evil-symbol))

This has the advantage that it changes depending on the language:

foo-bar

is one symbol in lisp-mode but two symbols (separated by -) in c-mode.

  • There is unfortunately one issue with this solution: if you try to search a word like :this (a lisp keyword, or elixir atom) with search forward (*) evil will use the regex \<:this\> which will not generate any match. – pera Sep 9 at 21:48
  • That sounds very much like a bug in Evil. – Erik yesterday
13

The spacemacs FAQ offers the following language-specific solution:

;; For python
(add-hook 'python-mode-hook #'(lambda () (modify-syntax-entry ?_ "w")))
;; For ruby
(add-hook 'ruby-mode-hook #'(lambda () (modify-syntax-entry ?_ "w")))
;; For Javascript
(add-hook 'js2-mode-hook #'(lambda () (modify-syntax-entry ?_ "w")))

It also works in regular emacs. With evil on, it works with all word-related commands (w, e, *, #, etc)

More generally, this will do it for all major modes:

(add-hook 'after-change-major-mode-hook
          (lambda ()
            (modify-syntax-entry ?_ "w")))
  • 2
    Is using 'prog-mode-hook an option to cover every language? – BallpointBen May 11 '18 at 7:43
2

Emacs 24 introduced superword-mode, a minor mode that treats underscores as part of words. evil-mode respects this setting.

Here is the documentation:

(superword-mode &optional ARG)

Toggle superword movement and editing (Superword mode). With a prefix argument ARG, enable Superword mode if ARG is positive, and disable it otherwise. If called from Lisp, enable the mode if ARG is omitted or nil.

Superword mode is a buffer-local minor mode. Enabling it changes the definition of words such that symbols characters are treated as parts of words: e.g., in superword-mode, "this_is_a_symbol" counts as one word.

  • If you are using evil-mode, unfortunately, there is a bug when superword-mode is enable. It causes some erratic behavior when evil-forward-word-begin is called: github.com/emacs-evil/evil/issues/721 – modulitos Aug 2 at 21:53
1

I actually quite like this behavior since I might want to just edit a part of the word and keep the other parts intact. If I want to go over the whole word I can always use E, W or B, the uppercase variants. This gives me more fine-grained control.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.