zsh-autosuggestions
zsh-autosuggestions copied to clipboard
Autosuggest eats buffer by ctrl+w
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)
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`
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
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-wordhas flagZLE_KILLwhich flags it as a "kill" type command. - There's functionality here that checks if the last widget executed had the
ZLE_KILLflag 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. lastcmdflags 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:
backward-kill-wordbuilt in finishes: from now onlastcmdwas a kill commandmy-backward-kill-wordwidget finishes: from now onlastcmdwas not a kill commandbackward-kill-wordbuilt in finishes: from now onlastcmdwas a kill commandmy-backward-kill-wordwidget finishes: from now onlastcmdwas 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:
backward-kill-wordbuilt in finishes: from now onlastcmdwas a kill commandbackward-kill-wordbuilt in finishes: from now onlastcmdwas a kill command
Not sure how this could be fixed. Next step is probably an email to the mailing list.
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)
Also found this related issue in zsh-syntax-highlighting: https://github.com/zsh-users/zsh-syntax-highlighting/issues/150
Looks like it hasn't been merged yet, but there's a commit https://github.com/danielshahaf/zsh-syntax-highlighting/commit/bfa71c983fa6c3b43cc657276223410123d5c145 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.
Here is a patch based on @ericfreese comment and the solution he has linked
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!
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.
@vincentbernat I can do this, but next week the earliest (no I am almost fully offline with no access to any computer).
I have created a pull request. In my system yanking does not leave the suggestion, so it also seems to solve #526.
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.
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! 🤷
@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