Daemian Mack home

Making emacs' word navigation respect underscores everywhere

April 28, 2009

Update, 2019-11-03

I just stumbled upon this blog entry while digging around in the archives; everything below is the broken fever-dream of an Emacs newb who lacked the terminology for an effective Google search. The correct way to achieve the below is to just install the (venerable) Subword-mode.

I’ve been playing with emacs lately, using technomancy’s emacs starter kit. It’s awesome so far, but some piece of the (rather large) config fileset is clobbering what I’d consider normal navigation behavior.

I work with a ton of stuff that uses underscores, especially SQL, and it’s been driving me crazy that, on its own, my emacs config doesn’t consider the underscore punctuation in all modes, so hitting M-DEL (Alt+Backspace) to delete back one word in sqlplus mode deletes the whole string – underscores and all.

That is, if | is our cursor, and we’re inside emacs at a sqlplus prompt like so…

desc prod.some_long_table_name|

hitting Alt+Backspace once gets you…

desc prod.|

which is annoying, especially because emacs’ autocomplete is so awesomely useful in sqlplus mode. I’d guess most database schemas have lots of tablenames that share prefixes (e.g., user_history, user_bookmarks, user_buddies, etc), which renders that autocomplete somewhat less than effective.

I’d much rather have it so that the cursor stopped at underscores. On successive keypresses, you’d zap back another segment, stopping at the underscore, like so…

desc prod.some_long_table_name|

desc prod.some_long_table_|

desc prod.some_long_|

desc prod.some_|

desc prod.|

This is already the long-established behavior of interfaces that inherit from emacs, like BASH’s emacs mode, and irssi, but is evidently a legacy design decision within Emacs without an obvious config handle, nearly undiscussed as far as I could google…

From a comment in python-mode.el:

  ;; For historical reasons, underscore is word class instead of 
  ;; symbol class.  GNU conventions say it should be symbol class, but 
  ;; there's a natural conflict between what major mode authors want 
  ;; and what users expect from `forward-word' and `backward-word'. 
  ;; Guido and I have hashed this out and have decided to keep 
  ;; underscore in word class.  If you're tempted to change it, try 
  ;; binding M-f and M-b to py-forward-into-nomenclature and 
  ;; py-backward-into-nomenclature instead.  This doesn't help in all 
  ;; situations where you'd want the different behavior 
  ;; (e.g. backward-kill-word).

Luckily, TFM is rather nice.

The short answer is: one needs to specify the underscore character as a member of emacs’ punctuation class…

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

That will only work for text-mode, and gets overwritten on a major-mode switch by any new syntax table specified, so the following hook reference is necessary for this change to be global for all major modes…

(defun change-major-mode-hook () (modify-syntax-entry ?_ "_"))

Folks who aren’t emacs newbs might be aware of further subtleties, but this seems to produce exactly what I want – autocomplete and word navigation in every mode that’s identical to the other command-line interfaces I use.