16

I'm very fond of ivy for completion and love that a simple (ivy-mode 1) is enough to get ivy almost everywhere that Emacs uses some sort of completion (that is acomplished by overriding completeing-read, I believe). But this doesn't setup eshell to use ivy for tab completion. I do like eshell's list of completion candidates, but would prefer the ivy UI to pick among them. Is there some way to reuse whatever eshell does to produce completion candidates and hand that off to `ivy'?

Omar
  • 4,732
  • 1
  • 17
  • 32

2 Answers2

12

This should work fine:

(define-key eshell-mode-map (kbd "<tab>") 'completion-at-point)

I don't know why the above isn't the default. But I use only shell-mode and term-mode anyway.

abo-abo
  • 13,943
  • 1
  • 29
  • 43
  • 3
    You should probably add that `(add-hook 'eshell-mode-hook '(lambda ()` needs to be wrapped around it. – Timm Oct 20 '16 at 18:04
  • Thanks, that's a little neater than what I had. The reason it's not the default is probably just that someone at some point in the past prefered `pcomplete` over the standard completion UI. – Omar Oct 20 '16 at 22:07
6

I found something that seems to work OK upon initial testing: essentially rebind <tab> to pcomplete-std-complete, but since that function isn't interactive for some reason, you need to wrap it:

(define-key eshell-mode-map (kbd "<tab>")
  (lambda () (interactive) (pcomplete-std-complete)))

The pcompete-std-complete tries to use the completions written for pcomplete with the standard completion UI, which ivy by default will override (with the very recent update, they appear in a nice overlay right in the eshell buffer near point!).

The comments in the source code of pcomplete-completions-at-point (which pcomplete-std-complete depends on) mention a couple of potential problems:

;; FIXME: it only completes the text before point, whereas the
;; standard UI may also consider text after point.
;; FIXME: the `pcomplete' UI may be used internally during
;; pcomplete-completions and then throw to `pcompleted', thus
;; imposing the pcomplete UI over the standard UI.

I think the first one won't affect me, since I don't think I ever press <tab> except at the end of the input line. I haven't seen the second problem surface either, so far.

EDIT: For those unfamiliar with eshell's quirks (this is a euphemism), maybe I should add that to rebind eshell's key as suggested above you should put this in your init file:

(add-hook 'eshell-mode-hook
  (lambda () 
    (define-key eshell-mode-map (kbd "<tab>")
      (lambda () (interactive) (pcomplete-std-complete)))))

Before you yell at me for the wasteful keybindings-in-a-hook antipattern, let me explain that eshell declares its keymap with (defvar eshell-mode-map nil) and then does (setq-local eshell-mode-map (sparse-keymap)) inside eshell-mode! (The code has a helpful ;; FIXME: What the hell!?.) That means that you can't rebind keys until after eshell-mode runs! Hence the hook.

Omar
  • 4,732
  • 1
  • 17
  • 32