TabCompletion

This page is about some ways to complete text in a buffer (other than the minibuffer).

TabKey2

tabkey2.el (available from nXhtml repository at Launchpad) is a little library that tries to generalize the ideas from Smart Tab. However, it works a bit differently. In most major modes, the first Tab press will indent and following Tab presses will complete. Since there are many ways to complete you can also choose which function you want to use (major mode specific completion, dynamic abbreviation completion etc). By default you do that by pressing F8 (this key binding will probably be changed).

If you are using tabkey2 with yasnippets, and see this behavior:

I write `(req’ in a Lisp buffer, press ‘Tab’, and enter the tab completion state. The popup (after 2 seconds) shows that YASnippet is the active function even though `(yas/expandable-at-point)’ returns ‘false’ (checked by eval-expression). Pressing ‘Tab’ second time gives the message
No more active completion functions in this buffer
Pressing ‘Tab’ more times has no effect.

you should download the latest version from the link above.

There was a bug report here (saying there were problems using Yasnippet or pabbrev with tabkey2-mode), but I get no answer when trying to get more details and I can’t reproduce the problem. If someone has problem with tabkey2-mode please download the latest version and file a bug report here: https://bugs.launchpad.net/nxhtml

However there indeed are problems with MuMaMo (part of nXhtml) and YASnippet so I have just filed a bug report for YASnippets about this: http://code.google.com/p/yasnippet/issues/detail?id=143LennartBorgman

Smart Tab

Here’s what I use every day:

It feels very natural (to me at least), <tab> always do what I mean.

Note the use of “\\_>” instead of “\\>”: this allows to tab-expand symbols, according to the major mode. Like “(goto-…”, or “BOOST_…” (useful when coding in Lisp or C).

Note: to enable tab completion in the `eval-expression` minibuffer, I just bind the tab key to `hippie-expand` in the `read-expression-map`.

(define-key read-expression-map [(tab)] 'hippie-expand)

Note: and since I like to undo the expansion before the end of an expansion cycle, I like to define unexpand as well:

(defun hippie-unexpand ()
  (interactive)
  (hippie-expand 0))

(define-key read-expression-map [(shift tab)] 'hippie-unexpand)

I hope you like it, cheers!

SebastienRoccaSerra

Update: DanielHackney has created a nice minor mode out of it and submited it to ELPA (see his git repository).

Then, JohnAnderson forked his code and added SebastianB’s great mode-specific behavior.

Please use their version of smart-tab, I’m using John’s fork of Daniel’s code myself.

Cheers!

SebastienRoccaSerra

I love this function and have created a git repository for it. This doesn’t seem to be a function that is in too much danger of growing large and unwieldy, but there were enough other suggestions by people that a repository might be nice.

DanielHackney

This function is excellent! I have one small improvement, though: replace (minibuffer-complete) with (unless (minibuffer-complete) (dabbrev-expand nil)).
    (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)))))
What this does is that if regular minibuffer completion is unsuccessful, then upon hitting Tab again the other buffers will be searched for completion words. (Also, I’ve replaced hippie-expand with dabbrev-expand. hippie-expand, while very powerful, is for “advanced” users and needs to be set up to work satisfactorily. dabbrev-expand is Emacs’ standard completion and works out of the box.)
To be able to use dabbrev-expand (or hippie-expand) in the minibuffer regardless of whether minibuffer-complete is successful, one should make a key binding for it. The standard binding is the somewhat cumbersome M-/. I suggest binding it to Shift-Tab: (global-set-key '[S-tab] 'dabbrev-expand).
My take on this problem: add exceptions, so if it’s the minibuffer, but we’re in eval-expression then do dynamic-abbrev instead of minibuffer-complete:
    (if (and (minibufferp)
             (not (string-match "^Eval:" (minibuffer-prompt))))
        (minibuffer-complete)
    ...
--TamasPatrovics
smart-tab is excellent. If for some strange reason you are stuck with emacs 21, here’s a slight modification to smart-tab (the second version above) that will work for you:
   (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 (string-match "Minibuf" (buffer-name))
         (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)))))
--BryanMurdock
Smart tab seems really nice. I have minor problem thought. It seems that buffer completion with InteractivelyDoThings don’t work how it suppose. I does not show all completion candidates after two tabs. Any tips on how the get it working.
--Petteri
I added a simple mode specific completion feature to smart-tab, that completes according to the current major-mode. It’s not as advanced as TabKey2, but IMHO it’s simple enough to be useful nevertheless.
(defvar smart-tab-completion-functions
  '((emacs-lisp-mode lisp-complete-symbol)
    (lisp-mode slime-complete-symbol)
    (python-mode py-complete)
    (text-mode dabbrev-completion))
  "List of major modes in which to use a mode specific completion
  function.")

(defun get-completion-function()
  "Get a completion function according to current major mode."
  (let ((completion-function
         (second (assq major-mode smart-tab-completion-functions))))
    (if (null completion-function)
        'dabbrev-completion
        completion-function)))

(defun smart-tab (prefix)
  "Needs `transient-mark-mode' to be on. This smart tab is
minibuffer compliant: it acts as usual in the minibuffer.

In all other buffers: if PREFIX is \\[universal-argument], calls
`smart-indent'. Else if point is at the end of a symbol,
expands it. Else calls `smart-indent'."
  (interactive "P")
  (if (minibufferp)
      (minibuffer-complete)
    (if (smart-tab-must-expand prefix)
        (let ((dabbrev-case-fold-search t)
              (dabbrev-case-replace nil))
          (funcall (get-completion-function))))
    (smart-indent)))

Seems to be working well enough for my purposes. I hope I didn’t screw anything up, if anybody has some style advice, I’d be glad to hear it, I haven’t got too much experience with elisp so far.

SebastianB

I wanted the mode-specific functionality of Sebastian’s code, so I forked the github project and added it in – http://github.com/genehack/smart-tab/tree/master

JohnAnderson

When using ido-mode, you do not wan’t to use minibuffer-complete but ido-complete instead. Ido is active when ido-cur-item is bound. I’m using the original smart-tab.el, but as far as I can see, this is needed for John Anderson’s edition too. Inserting this after the cond seems to do the trick

((boundp ‘ido-cur-item) (ido-complete))

Actually, not needed in my fork because it just disables smart-tab-mode when in the minibuffer (or in any mode listed in the smart-tab-disabled-major-modes list). Thanks for the suggestion though! – JohnAnderson

Fancy Tab

This works for me better than SuperTab. In Python, multiple presses to tab will re-indent at different levels and I like this functionality. This function fixes it. Likely it could be merged into SuperTab but I don’t have the desire yet.

(defun fancy-tab (arg)
  (interactive "P")
  (setq this-command last-command)
  (if (or (eq this-command 'hippie-expand) (looking-at "\\_>"))
      (progn
	(setq this-command 'hippie-expand)
	(hippie-expand arg))
    (setq this-command 'indent-for-tab-command)
    (indent-for-tab-command arg)))


(define-key read-expression-map [(tab)] 'hippie-expand)
(global-set-key (kbd "TAB") 'fancy-tab)

Other Solutions

    (defun indent-or-complete ()
      "Complete if point is at end of a word, otherwise indent line."
      (interactive)
      (if (looking-at "\\>")
          (dabbrev-expand nil)
        (indent-for-tab-command)
        ))
and then add something like this for whichever language modes you want it to work for (A global-set-key (kbd “<tab>”) screws up the minibuffer):
    (add-hook 'c-mode-common-hook
              (function (lambda ()
                          (local-set-key (kbd "<tab>") 'indent-or-complete)
                          )))
The following will work as expected – mina86
 (add-hook 'find-file-hooks (function (lambda ()
  (local-set-key (kbd "<tab>") 'indent-or-complete))))
An alternative to this is the following, which 1) ONLY does it when the key is at the end of the line (yay!) and 2) still does the indentation:
    (defun indent-or-complete ()
      "Complete if point is at end of line, and indent line."
      (interactive)
      (if (looking-at "$")
          (hippie-expand nil))
      (indent-for-tab-command))
Or, slight modification of the above to allow you to still use tab (or whichever key you’re using) to indent when there’s nothing (except maybe whitespace) on the line:
     (defun indent-or-complete ()
       "Complete if point is at end of line, and indent line."
       (interactive)
       (if (and (looking-at "$") (not (looking-back "^\\s-*")))
           (hippie-expand nil))
       (indent-for-tab-command)
       )
indent-or-complete with snippet support (used for rails.el)
      (defun indent-or-complete ()
       "Complete if point is at end of a word, otherwise indent line."
       (interactive)
       (if snippet
           (snippet-next-field)
        (if (looking-at "\\>")
        (dabbrev-expand nil)
        (indent-for-tab-command))))

Other Discussion

Is it possible to make it search for completions saved in a local file (eg. ~/.emacs-completions) so that when no word/symbol/whatever is found matching the prefix it would search for match there. For isntance I want to make string "my-email" expand to "mn86/at/o2.pl”. It would be even better if one could define several expansions, eg. “my-email” expands to “mn86/at/o2.pl" then to "mina86/at/staszicw.aw.pl” when TAB is pressed 2nd time. – mina86
If anyone is interested I’ve posted code which does what I wanted in RotateText. It’s called indent-or-rotate-or-complete. – mina86
I would think that rotate is overkill for something like “my-email”. I’d suggest ExpandMode or AbbrevMode instead. – RyanDavis
What would be the best way to let Emacs use a mode-specific expand function with smart-tab? I’d like to use py-complete when in python-mode, ruby-complete when in ruby-mode and dabbrev-expand as a default. I’m guessing that this should be possible by defining a variable that contains the indenting function and is used by smart-tab then binding this variable to the correct indenting function in the mode hooks, but since I’m a (e)Lisp newbie, I would really appreciate if one of you guys could help me out. Thanks!
In TabKey2 the option tabkey2-completion-functions is used for that. If you have other suggestions for mode-specific expand functions then those used by TabKey2 please tell me.
Thanks, that helped me a lot. I think TabKey2 is a little too much for me and it lacks the “indent-region by tab key (if mark is active)” feature that I like so much in smart-tab, otherwise it seems to be great. However, a friend of mine wrote some simple elisp functions that solve the problem for me. He’s coming over later and I’ll ask him to post them here.
I am glad it helped, but what do you mean with that Tabkey2 is too much? Yes, it is big, but that should mostly be a (small burden) to Emacs ;-). The indent-region thing is something that comes by default with Emacs 23 (not yet released) and I have therefor not included it in Takey2. (You can get code for it in ourcomments-util.el that are part of nXhtml.)
Don’t get me wrong, I think TabKey2 is great, I’d just rather not deal with manually switching completion functions if Emacs can “guess” the correct completion function by the means of a simple alist. Also, apart from not providing mode specific completion (so far) smart-tab.el provides everything I really need and it’s pretty readable and hackable for elisp simpletons like me. ☺ (Unfortunately I’ve just switched to Emacs 22 less than a year ago, so I don’t think I will be upgrading to GNU Emacs 23 too soon. However, I will definitely keep an eye on TabKey2, it does look very promising.)

See Also:


CategoryCompletion