I am addicted to tab completion in the minibuffer and in shells like bash — so much so that I want it everywhere. By default, emacs comes with
dabbrev-expand (bound to
M-/). When invoked after typing the first few letters of a word,
dabbrev-expand first searches the current buffer and then other open buffers for possible completions. Completions are expanded inline on each subsequent call. This is pretty close to what we want, but it’s not the TAB key. We could rebind
dabbrev-expand to TAB, but then how would we indent a line? Let’s apply a little bit of elisp smarts.
1 2 3 4 5 6 7 8 9
(defun indent-or-expand (arg) "Either indent according to mode, or expand the word preceding point." (interactive "*P") (if (and (or (bobp) (= ?w (char-syntax (char-before)))) (or (eobp) (not (= ?w (char-syntax (char-after)))))) (dabbrev-expand arg) (indent-according-to-mode)))
If we’re at the end of a word boundary, this function invokes
indent-according-to-mode. To avoid errors, the
eobp calls are there to make sure we’re not looking for characters before or after the buffer’s boundary. An interactive prefix argument is optional and is passed directly to dabbrev (
C-h f dabbrev-expand for more info).
A positive prefix argument, N, says to take the Nth backward distinct possibility. A negative argument says search forward.
I find that this makes for some wicked-cool behavior. For me, it seems intuitive and natural. But wait, we haven’t bound this to TAB yet. Let’s do that now.
1 2 3 4 5 6 7
(defun my-tab-fix () (local-set-key [tab] 'indent-or-expand)) (add-hook 'c-mode-hook 'my-tab-fix) (add-hook 'sh-mode-hook 'my-tab-fix) (add-hook 'emacs-lisp-mode-hook 'my-tab-fix) ;; more mode hooks, yada yada, etc ...
To avoid strange behavior in the minibuffer and other special buffers, it is necessary to rebind TAB only in local modes. I’m sure there is a smarter/cleaner way to do this by instead excluding only the problematic buffers or modes, but I’ll leave that as an exercise for the reader (please post a comment if you have a solution though).
Credits: While I’ve had this nugget in my
.emacs for a very long time, I cannot take credit for it. I’ve seen similar solutions in several places, and while I tweaked it a bit myself, I originally stole it from someone else. Emacs-Rails takes this approach one step further by also including snippet support.
Hippie Expand Alternative
dabbrev-expand above, but I am considering changing that to
hippie-expand instead. Hippie Expand is more powerful, but might require some customization to suit to your liking. I’m not ready for groovy vans or tie-dye, so we’ll have to talk about the details of Hippie Expand sometime later.