M-x all-things-emacs

Tab Completion Everywhere

March 12th, 2007 by Ryan McGeary · 13 Comments

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 dabrev-expand; otherwise, indent-according-to-mode. To avoid errors, the bobp and 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

We used 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.

Tags: elisp · tips

13 responses so far ↓

  • 1 sean // Mar 13, 2007 at 4:29 pm

    Nice tip! A similar trick I’ve used is to first run the indent function, then try completion only if it didn’t change anything. i.e. something like this:

    (let ((p (point)))
      (indent-according-to-mode)
      (when (and (= p (point))
                 (not (bolp))
                 (looking-at "\\_>"))
        (dabbrev-expand)))
  • 2 piyo // Mar 16, 2007 at 2:48 am

    Thanks for this topic, I look forward to a future dabbrev-expand or hippie-expand topic.

    M-: (eval-expression) has keyword completion, but at M-TAB. Since I am Windows user that prefers using Alt for Meta, I cannot use it (Alt-Tab is grabbed by the OS). So since I use M-: frequently, I use Tab for completion. I found that the following keymapping works for me:

    (define-key read-expression-map (kbd "") 'lisp-complete-symbol)
  • 3 Jeff Squires // Mar 22, 2007 at 5:54 pm

    One thing that drives me crazy:

    You know how in bash, you can copy a path, then go somewhere in the middle of that string to change an ancestor. When you hit tab, it completes.

    In emacs (under windows at least), when I go to find a file, does anyone know of a way to get the tab completion working when you are not at the end of the string?

  • 4 Ryan McGeary // Mar 22, 2007 at 7:36 pm

    Jeff, What version of Emacs are you running? Granted, I’m on OS X, but under Emacs 22.0.95, my find file minibuffer seems to already work just like bash. In other words, I can tab complete in the minibuffer even if my point is in the middle of a file/directory name.

  • 5 Maxime Biais // Apr 6, 2007 at 1:48 pm

    Thanks ! I never imagined this, but I really like it. Bye M-/ ;)

  • 6 Giving ido-mode a Second Chance // May 19, 2008 at 3:59 pm

    [...] me off. Today I realized why, and I was finally motivated enough to find a fix. I am a big fan of tab-completion everywhere, so when tab-completion doesn’t work like I expect, I get upset. As it turns out, this is why [...]

  • 7 Marius Andersen // Jun 22, 2008 at 7:35 pm

    I strongly recommend the smart-tab function from http://www.emacswiki.org/cgi-bin/wiki/TabCompletion:

    (global-set-key [(tab)] 'smart-tab)
    (defun smart-tab ()
      "This smart tab is minibuffer compliant: it acts as usual in
        the minibuffer. Else, if mark is active, indents region. Else if
        point is at the end of a symbol, expands it. Else indents the
        current line."
      (interactive)
      (if (minibufferp)
          (unless (minibuffer-complete)
            (dabbrev-expand nil))
        (if mark-active
            (indent-region (region-beginning)
                           (region-end))
          (if (looking-at "\\_>")
              (dabbrev-expand nil)
            (indent-for-tab-command)))))

    It calls indent-region if the mark is active. Otherwise, it dabbrev-expands if the point is at the end of a symbol. Otherwise, it indents the current line. So, I can indent whole blocks of code by selecting it and pressing TAB (as well as single lines).

    The function is also minibuffer compliant.

  • 8 Lukas // Oct 3, 2008 at 11:28 am

    Nice feature.!
    I preferred the version proposed by Marius Andersen, though I removed the dabbrev-expansion for the minibuffer (keeping only minibuffer-complete), since it seems to mostly expand to something useless.

  • 9 I want it my way … « Schemy things, lispy thoughts // May 5, 2009 at 3:54 pm

    [...] want it my way … By joelbf The M-x all-things-emacs blog had a post on tab-completion a while ago. It is basically what I need, but I’d like the oportunity to [...]

  • 10 pookleblinky // May 18, 2009 at 11:47 pm

    Hm. This interferes with yasnippet.

    No reason why they shouldn’t be able to work together so long as tab-completion is invoked on non-yas-keywords….

    I’ll start hacking on it.

  • 11 Emacs autocomplete con tab « Gastón Ramos – Ruby, Rails… // May 22, 2012 at 6:14 pm

    [...] disfrutar del autocomplete. La función la saqué de uno de los comentarios de este artículo: http://emacsblog.org/2007/03/12/tab-completion-everywhere/ Like this:LikeBe the first to like this post. Posted by Gastón Ramos Filed in emacs Leave a [...]

  • 12 Sam // Sep 4, 2013 at 10:05 pm

    I found a straight-forward way to bind it to all modes:

    ```
    (defun my-auto-mode ()
    (whitespace-mode)
    (column-number-mode)
    (rainbow-delimiters-mode)
    (my-tab-fix))

    (add-hook ‘find-file-hook ‘my-auto-mode)
    ```

  • 13 prada bag // Nov 18, 2013 at 11:39 am

    Thank you!!!!
    prada bag http://www.earlypurse.me/