27

In my emacs, let's say, I use a "elisp" yasnippet to extend a lisp block in org-mode. But before I extend it, company is triggered first, which gives me a menu like "1. elisp1, 2. elisp2" without an option "elisp". Now if I use tab to extend yasnippet, it is always annoying that "elisp1" always first goes on the screen. So I need to delete "1"firstly, and do the extension of yasnippet snippet.

So as a solution, I always use left arrow key to turn company completion menu off first, but the cursor now will go to "elis|p", so again I use right arrow key to move the cursor to end of "elisp|", and extend the snippet.

Here comes my question: how can I bind tab key firstly trigger yasnippet but not company to save me life?

4
  • 1
    I'm using tab for company and C-o for yasnippet. I can describe further if you're interested. Commented Feb 3, 2015 at 10:07
  • @abo-abo, thanks reply. I know I can do it as you do, but I bind C-o to other command, and badly I have trained my hand muscle to adapt tab. So I would not like to change the binding. Commented Feb 3, 2015 at 10:10
  • That's why I asked:) No point in going on a rant of how C-o could expand abbrevs and snippets and open lines etc. if you're not interested. Commented Feb 3, 2015 at 10:14
  • seems interesting, can you describe more?:) Commented Feb 3, 2015 at 10:17

2 Answers 2

25

This is what I created for myself, facing the same issue. It is from company-mode's Emacs Wiki page, but heavily extended:

(defun check-expansion () (save-excursion (if (looking-at "\\_>") t (backward-char 1) (if (looking-at "\\.") t (backward-char 1) (if (looking-at "->") t nil))))) (defun do-yas-expand () (let ((yas/fallback-behavior 'return-nil)) (yas/expand))) (defun tab-indent-or-complete () (interactive) (cond ((minibufferp) (minibuffer-complete)) (t (indent-for-tab-command) (if (or (not yas/minor-mode) (null (do-yas-expand))) (if (check-expansion) (progn (company-manual-begin) (if (null company-candidates) (progn (company-abort) (indent-for-tab-command))))))))) (defun tab-complete-or-next-field () (interactive) (if (or (not yas/minor-mode) (null (do-yas-expand))) (if company-candidates (company-complete-selection) (if (check-expansion) (progn (company-manual-begin) (if (null company-candidates) (progn (company-abort) (yas-next-field)))) (yas-next-field))))) (defun expand-snippet-or-complete-selection () (interactive) (if (or (not yas/minor-mode) (null (do-yas-expand)) (company-abort)) (company-complete-selection))) (defun abort-company-or-yas () (interactive) (if (null company-candidates) (yas-abort-snippet) (company-abort))) (global-set-key [tab] 'tab-indent-or-complete) (global-set-key (kbd "TAB") 'tab-indent-or-complete) (global-set-key [(control return)] 'company-complete-common) (define-key company-active-map [tab] 'expand-snippet-or-complete-selection) (define-key company-active-map (kbd "TAB") 'expand-snippet-or-complete-selection) (define-key yas-minor-mode-map [tab] nil) (define-key yas-minor-mode-map (kbd "TAB") nil) (define-key yas-keymap [tab] 'tab-complete-or-next-field) (define-key yas-keymap (kbd "TAB") 'tab-complete-or-next-field) (define-key yas-keymap [(control tab)] 'yas-next-field) (define-key yas-keymap (kbd "C-g") 'abort-company-or-yas) 

Basically, this makes <tab> do the right thing most of the time. Pressing tab will

  • Indent the current line,
  • If there is a yasnippet to expand, expand it, even if this means aborting a company completion (I don't use abbreviations much, so no abbreviation support yet),
  • If a company completion is ongoing, complete with the selected item,
  • Otherwise try to use company to start autocomplete,
  • If there is nothing to autocomplete and we're in a yasnippet placeholder, skip to the next placeholder.

Note that if there is an opportunity to autocomplete and you are currently editing in a snippet placeholder, the situation is ambigous. As a compromise, I bound C-<tab> to skip to the next placeholder directly.

The fact that the snippet's name does not appear in the company menu and the existence of a snippet silently modifies the behaviour of the tab key is not particularly nice, unfortunately... Although at least it is possible to type <return> instead to get the completion instead of the snippet.

7
  • This seems to interfere with magit. Causes tab in magit to raise Buffer is read-only: #<buffer *magit: ~/.emacs.d/*>. Any idea how I can fix that? Commented Sep 5, 2015 at 10:59
  • @zsquare I don't use magit (I know, I'm nuts) so I can't test this to be sure, but it sounds like magit's keymap for TAB, which binds it to magit-section-toggle, is conflicting with the line (global-set-key [tab] 'tab-indent-or-complete) above. A quick and dirty fix would be to add a check at the beginning of the function tab-indent-or-complete above to see whether we're in magit mode, e.g. for a global variable that gets set on magit-mode-hook. Commented Sep 8, 2015 at 21:53
  • this is awesome, thanks! :) small style point, when is pretty much an if + progn Commented Jan 14, 2016 at 6:19
  • @zsquare To support tab in magit mode add this to the tab-indent-or-complete cond ` ((derived-mode-p 'magit-mode) (magit-section-toggle (magit-current-section)))` Commented Aug 19, 2017 at 8:35
  • To support ido instead of default minibuffer completion, replace the cond with ` ((minibufferp) (ido-complete))` Commented Aug 19, 2017 at 8:36
13

Here's the code that I'm using:

(global-set-key "\C-o" 'aya-open-line) (defun aya-open-line () "Call `open-line', unless there are abbrevs or snippets at point. In that case expand them. If there's a snippet expansion in progress, move to the next field. Call `open-line' if nothing else applies." (interactive) (cond ((expand-abbrev)) ((yas--snippets-at-point) (yas-next-field-or-maybe-expand)) ((ignore-errors (yas-expand))) (t (open-line 1)))) 

aya-open-line from auto-yasnippet does more than a plain open-line:

  • it tries to expand abbrevs
  • it tries to move to the next field of yasnippet
  • it tries to expand yasnippet
  • finally, it calls open-line if all else fails
2
  • thanks for your snippet. Pretty good. But the problem still exists. When I first use C-o, it just close company menu, so I need twice press to extend yasnippet. Commented Feb 3, 2015 at 10:40
  • I have no such issue: C-o with company menu active will close the menu and expand snippet. Commented Feb 3, 2015 at 10:52

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.