4

In bash behavior is like this:

  • Ctrl+W deletes the word behind the cursor up to the next space
  • Ctrl+Alt+H deletes the word behind the cursor up to the next seperation charcater like ., ,, -, / etc.

In zsh both Ctrl+W and Ctrl+Alt+H behave like the latter one in bash.

I would like the same behavior as in bash.

3 Answers 3

3

This does what you want, regardless of what's in $WORDCHARS, and makes the killed words available to yank:

# Create a new widget.
zle -N backward-kill-space-word
backward-kill-space-word() {
  # Inform the line editor that this widget will kill text.
  zle -f kill

  # Set $WORDCHARS for this command only. 
  WORDCHARS='*?_-.[]~=/&;!#$%^(){}<>' zle .backward-kill-word
}

# See comments above.
zle -N backward-kill-bash-word
backward-kill-bash-word() {
  zle -f kill
  WORDCHARS='' zle .backward-kill-word
}

# Bind the widgets to keys.
bindkey   '^W' backward-kill-space-word
bindkey '^[^H' backward-kill-bash-word

Alternatively, I've released a plugin called zsh-edit, which comes with more sophisticated versions of these keybindings.

3
  • 1
    Hey nice, thanks so much! I still don't completely understand how the zle and zstyle functions work, the documentation is quite abstract and technical, but maybe I'll read up on it some day! Anyway looks like only the first bindkey is necessary because Ctrl + Alt + H already works as desired, so no need to rebind that.
    – gitaarik
    Jun 22, 2020 at 17:27
  • I added more explanation for you to my answer. Hopefully, this helps you understand better what zle and zstyle do. 🙂 Jun 24, 2020 at 8:24
  • 1
    Oh wow, thank you so much, I understand it much better now ^^
    – gitaarik
    Jun 26, 2020 at 1:16
1

You can set the special characters that are considered part of a word with the WORDCHARS variable. This has influence on how words are deleted by Ctrl + W:

WORDCHARS='~!#$%^&*(){}[]<>?.+;-_/\|=@`'

However it also has influence on Ctrl + Alt + H. We want this behavior to only apply to Ctrl + W. But there's a trick that we can do. Let me explain:

You can rebind the key combinations to different functions (see man zshzle). And there are 2 functions that actually have the same behavior:

  • backward-delete-word
  • backward-kill-word

You can also redefine these functions with zle -N <func>. I'm not completely sure how it works but you get an idea if you read the code, anyway, it does the trick.

By default both Ctrl + W and Ctrl + Alt + H are mapped to backward-kill-word. So we can redefine backward-delete-word and then bind that to Ctrl + W:

# Make `Ctrl + W` behave like it does in Bash, deleting words separated by
# spaces. We do this by redefining the `backward-delete-word` function and bind
# that to `Ctrl + W`.
SPACE_WORDCHARS='~!#$%^&*(){}[]<>?.+;-_/\|=@`'
backward-delete-word() WORDCHARS=$SPACE_WORDCHARS zle .$WIDGET
zle -N backward-delete-word
bindkey "^W" backward-delete-word

Yeah, now Ctrl + W deletes bigger words than Ctrl + Alt + H!

Edit:

Very sadly I'm now discovering that this approach lacks some functionality, when you delete a word, it doesn't get yanked to the paste buffer (Ctrl + Y). Haven't found a solution for that yet..

2
  • You need to use backward-kill-word instead of backward-delete-word word, if you want the word to end up in the kill ring. Jun 18, 2020 at 12:44
  • Yes, but how do I preserve the Ctrl + Alt + H behavior to delete only part of a word?
    – gitaarik
    Jun 19, 2020 at 0:39
-2

I ultimately found my desired behavior with these bindings:

# Configures bindings for jumping/deleting full and sub-words, similar to
# the keybindings in bash.

# Jumping:
# Alt + B                Backward sub-word
# Ctrl + Alt + B         Backward full-word
# Alt + F                Forward sub-word
# Ctrl + Alt + F         Forward full-word

# Deleting:
# Ctrl + W               Backward delete full-word
# Ctrl + Alt + H         Backward delete sub-word
# Alt + D                Forward delete sub-word
# Ctrl + Alt + D         Forward delete full-word

# Which characters, besides letters and numbers, that are jumped over by a
# full-word jump:
FULLWORDCHARS="*?_-.,[]~=/&:;!#$%^(){}<>'\""

backward-full-word() { WORDCHARS=$FULLWORDCHARS zle .backward-word ; }
backward-sub-word() { WORDCHARS="" zle .backward-word ; }
forward-full-word() { WORDCHARS=$FULLWORDCHARS zle .forward-word ; }
backward-kill-full-word() { WORDCHARS=$FULLWORDCHARS zle .backward-kill-word ; }
backward-kill-sub-word() { WORDCHARS="" zle .backward-kill-word ; }
forward-kill-full-word() { WORDCHARS=$FULLWORDCHARS zle .kill-word ; }
forward-kill-sub-word() { WORDCHARS="" zle .kill-word ; }

zle -N backward-full-word
zle -N backward-sub-word
zle -N forward-full-word
zle -N backward-kill-full-word
zle -N backward-kill-sub-word
zle -N forward-kill-full-word
zle -N forward-kill-sub-word

# For `forward-sub-word` we use the built-in `emacs-forward-word` widget,
# because that simulates bash behavior.
zle -A emacs-forward-word forward-sub-word

bindkey "^[b" backward-sub-word
bindkey "^[^b" backward-full-word
bindkey "^[f" forward-sub-word
bindkey "^[^f" forward-full-word
bindkey "^[^h" backward-kill-sub-word
bindkey "^w" backward-kill-full-word
bindkey "^[d" forward-kill-sub-word
bindkey "^[^d" forward-kill-full-word
2
  • 2 years ago, I answered your question as written and you accepted it. But now, you submit your own answer that basically copies mine, but adds details that are not relevant to your original question, and accept that instead, thus losing me reputation points? That’s not very nice of you. Apr 24 at 8:14
  • Hey Marlon, I'm very happy with your answer, you pointed me in the right direction. My script is not completely the same as yours. I removed the zle -f kill, because it seemed to remove the functionality of putting the deleted text on the copy/paste buffer. Also I added other keybindings that make it function more like bash. Also, the zsh-edit plugin you referenced in your answer doesn't seem to work for me. I created an issue about that on the GitHub page. For these reasons I think my own answer is better. But that doesn't mean your answer wasn't helpful, and therefore I still upvoted it.
    – gitaarik
    Apr 26 at 15:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.