Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autosuggest eats buffer by ctrl+w #363

Open
jsirex opened this issue Jul 30, 2018 · 13 comments
Open

Autosuggest eats buffer by ctrl+w #363

jsirex opened this issue Jul 30, 2018 · 13 comments

Comments

@jsirex
Copy link

jsirex commented Jul 30, 2018

Given command: echo first second
I want to cut two words first second by typing CTRL+WW .

But then when I try to paste it with CTRL+Y I'm getting only one word: first.

I'm using oh-my-zsh:

plugins=(aws backup git extract knife knife_ssh mercurial mvn vagrant bundler gem rake rvm thor debian sudo kitchen docker-compose emacs berkshelf power-save terraform systemd zsh-autosuggestions)
@ericfreese
Copy link
Member

I pushed a failing spec to fixes/kill-multiple-words

multiple words killed with `backward-kill-word`
  can be yanked back with `yank` (FAILED - 1)

Failures:

  1) multiple words killed with `backward-kill-word` can be yanked back with `yank`
     Failure/Error: wait_for { session.content }.to eq('echo first second')
     
       expected: "echo first second"
            got: "echo first"
     
       (compared using ==)
     # /usr/local/bundle/gems/rspec-wait-0.0.9/lib/rspec/wait/handler.rb:13:in `block (2 levels) in handle_matcher'
     # /usr/local/bundle/gems/rspec-wait-0.0.9/lib/rspec/wait/handler.rb:10:in `loop'
     # /usr/local/bundle/gems/rspec-wait-0.0.9/lib/rspec/wait/handler.rb:10:in `block in handle_matcher'
     # /usr/local/bundle/gems/rspec-wait-0.0.9/lib/rspec/wait/handler.rb:9:in `handle_matcher'
     # /usr/local/bundle/gems/rspec-wait-0.0.9/lib/rspec/wait/target.rb:30:in `block in to'
     # /usr/local/bundle/gems/rspec-wait-0.0.9/lib/rspec/wait/target.rb:44:in `block in with_wait'
     # /usr/local/bundle/gems/rspec-wait-0.0.9/lib/rspec/wait.rb:28:in `with_wait'
     # /usr/local/bundle/gems/rspec-wait-0.0.9/lib/rspec/wait/target.rb:44:in `with_wait'
     # /usr/local/bundle/gems/rspec-wait-0.0.9/lib/rspec/wait/target.rb:30:in `to'
     # ./spec/integrations/kill_word_spec.rb:12:in `block (2 levels) in <top (required)>'
     # ./spec/spec_helper.rb:19:in `block (2 levels) in <top (required)>'
     # /usr/local/bundle/gems/rspec-wait-0.0.9/lib/rspec/wait.rb:46:in `block (2 levels) in <top (required)>'
     # ------------------
     # --- Caused by: ---
     # Timeout::Error:
     #   execution expired
     #   /usr/local/bundle/gems/rspec-wait-0.0.9/lib/rspec/wait/handler.rb:16:in `sleep'

Finished in 2.15 seconds (files took 0.24763 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/integrations/kill_word_spec.rb:10 # multiple words killed with `backward-kill-word` can be yanked back with `yank`

@ericfreese
Copy link
Member

Looks like this behavior can be reproduced by simply wrapping backward-kill-word in a user-defined widget.

% my-backward-kill-word() { zle backward-kill-word }
% zle -N my-backward-kill-word
% bindkey ^W my-backward-kill-word

@ericfreese
Copy link
Member

ericfreese commented Dec 22, 2018

The functionality where cut text is concatenated to be yanked in combined form later seems to be dependent on the two built-in cutting widgets running one directly after the other.

Some interesting places in the upstream code:

  • The backward-kill-word has flag ZLE_KILL which flags it as a "kill" type command.
  • There's functionality here that checks if the last widget executed had the ZLE_KILL flag set. If it didn't, it will start a new buffer and thus not concatenate the text cut this time with the text cut last time.
  • lastcmd flags are reset to zero here when the user-defined widget finishes running.

So the flow when using my-backward-kill-word twice is something like:

  1. backward-kill-word built in finishes: from now on lastcmd was a kill command
  2. my-backward-kill-word widget finishes: from now on lastcmd was not a kill command
  3. backward-kill-word built in finishes: from now on lastcmd was a kill command
  4. my-backward-kill-word widget finishes: from now on lastcmd was not a kill command

Because the kill commands aren't coming one after another, the text is not concatenated for yanking.

Compare with the flow when using built-in backward-kill-word:

  1. backward-kill-word built in finishes: from now on lastcmd was a kill command
  2. backward-kill-word built in finishes: from now on lastcmd was a kill command

Not sure how this could be fixed. Next step is probably an email to the mailing list.

@ericfreese
Copy link
Member

Actually... there's a pretty easy fix if you're ok with not fetching suggestions after backward-kill-word is executed.

Add backward-kill-word to the list of ignore widgets in your zshrc:

# After sourcing zsh-autosuggestions.zsh
ZSH_AUTOSUGGEST_IGNORE_WIDGETS+=(backward-kill-word)

@ericfreese
Copy link
Member

Also found this related issue in zsh-syntax-highlighting: zsh-users/zsh-syntax-highlighting#150

Looks like it hasn't been merged yet, but there's a commit danielshahaf/zsh-syntax-highlighting@bfa71c9 that uses zle -f (only available in zsh >=5.2) to set the "kill" flag on the user-defined widgets wrapping the builtin kill widgets.

@macdems
Copy link

macdems commented Feb 5, 2020

Here is a patch based on @ericfreese comment and the solution he has linked

zsh-autosuggestions.patch.txt

@iloveitaly
Copy link

I ran into this issue on and @ericfreese's hack worked for me. Would be great to get a long term fix for this merged in. Thanks for all of the great work on this plugin!

@vincentbernat
Copy link

The issue with the proposed workaround is that it leaves the suggestion while this is not valid.

@macdems Maybe you could start a pull request with your suggestion? I think you need to somehow put the list of widgets into a specific variable, like this is done for ZSH_AUTOSUGGEST_IGNORE_WIDGETS. If you don't have time, I can do that for you.

@macdems
Copy link

macdems commented Jun 28, 2020

@vincentbernat I can do this, but next week the earliest (no I am almost fully offline with no access to any computer).

@macdems
Copy link

macdems commented Jul 8, 2020

I have created a pull request. In my system yanking does not leave the suggestion, so it also seems to solve #526.

@artem-nefedov
Copy link

I have created a pull request. In my system yanking does not leave the suggestion, so it also seems to solve #526.

I tried applying this patch on top of upstream.
Ctrl-w Ctrl-y is still broken for me after that.

@trinitronx
Copy link

Has anyone gotten the normal behavior of select-word-style bash to actually work while zsh-autosuggestions is enabled?

I've been seeing broken behavior where Ctrl+w will gobble up the entire line always into the cut buffer, no matter that WORDCHARS had been set to (e.g. so as to not include spaces). Then Ctrl+y shows what is there by "yanking" (pasting) that entire line out, not just the last word. When I use the ZSH_AUTOSUGGEST_IGNORE_WIDGETS workaround, and disable select-word-style bash, then normal Zsh behavior for backward-kill-word is restored where one word at a time can be deleted backwards via Ctrl+w, then pasted back with Ctrl+y. Yet, then I can't set it up like bash word behavior because Zsh defaults to not include / and ' separators as "words".

I've tried out the change in PR #551, but behavior is still the same unless I add backward-kill-word, and backward-kill-word-match to the ZSH_AUTOSUGGEST_IGNORE_WIDGETS array, and disable select-word-style bash entirely. So my choices seem to be: use zsh-autosuggestions but be forced to have Zsh default Ctrl+w word behavior, OR disable zsh-autosuggestions and then get select-word-style bash behavior working.

The only way I've been able to get normal select-word-style bash behavior is to not load zsh-autosuggestions plugin at all! 🤷

@aculich
Copy link

aculich commented Jul 14, 2024

@trinitronx I just tried out PR #551 with zsh 5.9 and it works for me, though it also need a little fix for yank-pop as noted by @vincentbernat which I've added in another PR #795

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants