dieggsy / esh-autosuggest

Fish-like autosuggestions in eshell.
GNU General Public License v3.0
102 stars 10 forks source link

How can I use this together with (company-capf/pcomplete)? #1

Closed terlar closed 6 years ago

terlar commented 6 years ago

I am trying to use this together with the pcomplete suggestions from emacs-fish-completion. However specifying the backend: company-eshell-autosuggest seems to only suggest the autosuggest completions. I assume you have this working looking at your setup and your blog post. If I don't have the company-eshell-autosuggest the other suggestions work very well.

I would like to have the pcomplete first and if nothing else matches fallback to this one.

dieggsy commented 6 years ago

I do use this with pcomplete, but not with company-capf. I should clarify this in the README, but my aim was for this to not interfere with completion at all because it's not really a completion package, since it only suggests a single candidiate - the most recent matching history item. This is to try and follow fish shell's behavior. To much more closely follow fish shell autosuggestion behavior, you can use some tricky bindings:

If you use general.el:

(general-def :keymaps 'company-active-map
  [tab] (general-predicate-dispatch nil
          (not (eq major-mode 'eshell-mode)) 'company-complete-common)

  [return] (general-predicate-dispatch nil
             (not (eq major-mode 'eshell-mode)) 'company-complete-selection)

  "RET" (general-predicate-dispatch nil
          (not (eq major-mode 'eshell-mode)) 'company-complete-selection)

  "<right>" (general-predicate-dispatch nil
              (eq major-mode 'eshell-mode) 'company-complete-selection))

without general.el:

(define-key company-active-map
  [tab] '(menu-item "" nil :filter
                    (lambda
                      (&optional _)
                      (cond
                       ((not
                         (eq major-mode 'eshell-mode))
                        'company-complete-common)
                       (t nil)))))

(define-key company-active-map
  [return] '(menu-item "" nil :filter
                       (lambda
                         (&optional _)
                         (cond
                          ((not
                            (eq major-mode 'eshell-mode))
                           'company-complete-selection)
                          (t nil)))))

(define-key company-active-map
  (kbd "RET") '(menu-item "" nil :filter
                          (lambda
                            (&optional _)
                            (cond
                             ((not
                               (eq major-mode 'eshell-mode))
                              'company-complete-selection)
                             (t nil)))))

(define-key company-active-map
  (kbd "<right>") '(menu-item "" nil :filter
                              (lambda
                                (&optional _)
                                (cond
                                 ((eq major-mode 'eshell-mode)
                                  'company-complete-selection)
                                 (t nil)))))

What this does is essentially escape some company-active-map bindings in eshell, allowing eshell-company-autosuggest to simply be a "passive" history suggestion mechanism, while relying on pcomplete for completion on pressing tab.

Notes:

I have not looked into relying on company-capf first, I can investigate that option and get back to you.

I could be possible to look automate some of this behavior in a minor mode, though it might be a bit tricky to take into account user/externally customized bindings in company-active-map.

dieggsy commented 6 years ago

@terlar I think I have some ideas, but could you describe in more detail the behavior you want? You want to use company-mode normally in eshell, with the capf backend, and if that doesn't find anything you'd like to get history suggestions (presumably multiple) ?

Or would it be fine to get just the single most recent history suggestion if company-capf has no completions? I think either case should be doable, give me some time :)

terlar commented 6 years ago

@dieggsy Thank you for your help, that behavior achieved from that one is pretty much exactly what I want, only that instead of using the default eshell-pcomplete with the buffer popping up which I find annoying, I would have company showing those completions instead. I might have misunderstood from some company-mode issue that it was using company-capf to get those completions, but that doesn't seem to be the case.

But somehow company receives the completions when the company-eshell-autosuggest is disabled, but I am not sure from which backend. When I enable company-eshell-autosuggest I lose those other completions. I figured it should just work if the company-eshell-autosuggest was just added to that list and I would complete the common parts first. But perhaps when actually experiencing that behavior it wouldn't be as nice as I expect.

I would want:

terlar commented 6 years ago

Okay, seems I have pretty much what I want and I was correct about the company-capf. I will try it out for a bit. I am currently using it with '(company-capf company-eshell-autosuggest) so it gets both completions in the list. I am not sure why it didn't work out earlier, I guess it we somewhere between the tab key being bound to eshell-pcomplete and me not being sure which backend provided those pcomplete completions for company.

dieggsy commented 6 years ago

@terlar I'm not sure you can have the autosuggested line always visible if you're using company-capf on top of company-eshell-autosuggest - is that working for you?

Regarding the annoying buffer - I agree. As an alternative, I'd recommend not using company-capf and trying the following:

If you use ivy, I'd recommend trying out binding tab to completion-at-point in eshell-mode-map like so:

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

(add-hook 'eshell-mode-hook 'setup-eshell)

You need to make sure ivy-do-completion-in-region is t, and if you find the in-buffer completion annoying and would like to use the minibuffer you can set ivy-display-functions-alist to nil.

If you use helm, you could bind tab to helm-esh-pcomplete:

(defun setup-eshell ()
  (define-key eshell-mode-map (kbd "<tab>") 'helm-esh-pcomplete))

(add-hook 'eshell-mode-hook 'setup-eshell)

This is the most truly "fish"-like behavior I could get - you get history autosuggestions automatically, but tab will use the minibuffer for pcomplete completion (without the annoying popup window/buffer).

dieggsy commented 6 years ago

In any case, I think I'm gonna try to roll out some minor modes to make all of this painless, just (add-hook 'eshell-mode #'company-eshell-autosuggest-mode) or (add-hook 'eshell-mode #'company-eshell-autosuggest-with-capf-mode).

terlar commented 6 years ago

Yes this is the conclusion I came to too, I tried out the ivy completion and it worked well, just I found it annoying having to accept the completion and not being able to continue with space, but that is fixable. Actually you can use this quite well with company mode if you enable the front-end: company-preview-frontend, in that sense it works very good with the setup I mentioned earlier.

I am using:

(setq company-frontends
        '(company-tng-frontend
          company-pseudo-tooltip-unless-just-one-frontend
          company-echo-metadata-frontend
          company-preview-frontend
          company-quickhelp-frontend))

I guess we can close this one since I figured it out.

dieggsy commented 6 years ago

@terlar If you don't mind, I'll reopen so I can remind myself that i'd like to enable these in a minor mode for convenience.

A question - shouldn't it work with the default company-frontends? (In which case you wouldn't have to actually set that variable).

terlar commented 6 years ago

Of course I don't mind.

I think the caveat with having the line visible when using these two together is using the package company-statistics and if the autosuggested one is the top alternative, then it will show as preview with company-preview-frontend.

That would be neat, but I am not sure what is possible with company, I guess it should be technically possible if this mode combines these two in some way always showing the autosuggestion at top. I have seen other modes that wrap several modes at the package level.

dieggsy commented 6 years ago

@terlar oh, sorry - I meant that for your current use case (not your ideal one) you might be able to leave company-frontends to its default value.

dieggsy commented 6 years ago

@terlar can you describe the setup you say is working now?

I'm testing setting company-backends to '(company-capf company-eshell-autosuggest), but it seems i never actually get the history autosuggestions.

dieggsy commented 6 years ago

In fact, https://github.com/company-mode/company-mode/issues/663 and https://github.com/company-mode/company-mode/issues/142 seem to suggest this isn't possible...

dieggsy commented 6 years ago

Closing in light of https://github.com/company-mode/company-mode/issues/744, which confirms my previous comment. Note that there's now a minor mode for company-eshell-autosuggest if you don't want to use company as your main completion system, which gets around some of the funky bindings decribed here. Minor mode is described in the updated Readme.

terlar commented 6 years ago

Sorry for not getting back to you, but there were some issues with the loading of use-package during that time that made it hard for me to reproduce my setup, seems things are stable now and I can reproduce now.

Since I use company-statistics my top choices will be presented first, so for example if I recently used docker ps -a it will show up when I write docker and I have bound C-e to complete the current selection so that is what I use to accept the completion, however if I continue write docker p and tab, it will use the completions from company-capf.

Here is a short gif with demonstration:

esh-autosuggest

As noted there in your comments it only works as long as the prefix is the same, so it only works for the first word, but after space when you try to complete next word it doesn't work.

The relevant setup for this would be:

(use-package company
  :demand t
  :hook
  (after-init . global-company-mode)
  :general
  (:keymaps 'company-active-map
            ;; No interference with return key
            [return]  'nil
            "RET"     'nil
            ;; Abort company instead of insert mode
            [escape]  'company-abort
            ;; Complete the common part before cycling
            [tab]     'company-complete-common-or-cycle
            "TAB"     'company-complete-common-or-cycle
            [backtab] 'company-select-previous
            "S-TAB"   'company-select-previous
            ;; Complete the current selection
            "C-e"     'company-complete-selection
            "C-f"     'company-complete-selection)
  :preface
  (defun company-preview-if-not-tng-frontend (command)
    "`company-preview-frontend', but not when tng is active."
    (unless (and (eq command 'post-command)
                 company-selection-changed
                 (memq 'company-tng-frontend company-frontends))
      (company-preview-frontend command)))
  :init
  (autoload 'company-tng-frontend "company-tng" nil t)

  (setq company-require-match nil
        company-frontends
        '(company-tng-frontend
          company-pseudo-tooltip-unless-just-one-frontend
          company-preview-if-not-tng-frontend
          company-echo-metadata-frontend)
        company-backends
        company-transformers '(company-sort-by-occurrence)))

(use-package company-statistics
  :hook (company-mode . company-statistics-mode))

(use-package esh-autosuggest
  :commands esh-autosuggest
  :init
  (add-hook 'eshell-mode-hook
            (lambda () (setq-local 'company-backends (push '(company-capf esh-autosuggest) company-backends)))))
dieggsy commented 6 years ago

Ah, cool. Thanks for the detailed description - since I don't actually use this method, do you think the readme could be clearer regarding this alternative, or is it sufficient? You could also feel free to add your setup to the wiki if it's not covered by the readme, or open a PR for the readme.

terlar commented 6 years ago

My setup is a bit bigger than minimally needed, even I stripped out most irrelevant stuff. I think it is fine with the mention you already have about being able to combine company-capf and esh-autosuggest. I just wanted to update you since I never got back to you and realized things was working as I wanted now.

dieggsy commented 6 years ago

Great. Thanks, I appreciated it.