3

I have used answer of zsh kill Ctrl + Backspace, Ctrl + Delete to configure following key binding:

  • Ctrl+Backspace: delete until the beginning of current word,
  • Ctrl+Delete: delete until the end of current word,
  • Ctrl+Shift+Delete: delete until the end of the line.

This have been done using these commands:

$ bindkey -M emacs '^[[3;5~' kill-word
$ bindkey -M emacs '^H' backward-kill-word 
$ bindkey -M emacs '^[[3;6~' kill-line  

To know how to encode the keys (i.e., the ^[[3;5~ part), I used the "trick" detailed in the answer: "type Ctrl+C Ctrl+Delete to see what the value is on your system".

Problem

I would like to bind Ctrl+Shift+Backspace to the backward-kill-line command (i.e. delete everything between the cursor and the beginning of the line).

However, when I type Ctrl+C Ctrl+Shift+Backspace, my prompt only shows ^H — i.e. the same key combination as Ctrl+Backspace.

3
  • 2
    You need to tell your terminal emulator to send some character or character sequence upon that key combination, and then bind that in zsh to the action you want. How to do that depends on the terminal emulator. Which one are you using? Apr 4, 2019 at 17:26
  • @StéphaneChazelas Thanks for the explanation! I'm using guake 3.0.5 (on Ubuntu 18.04).
    – ebosi
    Apr 4, 2019 at 17:29
  • FYI: I have raised an issue on guake repo.
    – ebosi
    Apr 4, 2019 at 18:03

2 Answers 2

3

Your terminal sends the same escape sequence for Ctrl+Shift+Backspace as for Ctrl+Backspace, so there's no way for zsh to distinguish between the two. The only solution is to configure your terminal to send different escape sequences. Not all terminals permit this.

Some terminals, such as xterm, rxvt, iTerm2 and Emacs term, allow you to configure escape sequences for each keychord manually. Consult your terminal's documentation.

For example, for xterm, you can put the snippet below in your .Xresources. Load it with xrdb -merge ~/.Xresources. Many environments load this when you log in; if yours doesn't, add this command to your X11 startup file.

XTerm.VT100.translations: #override \
    Ctrl Shift <Key>BackSpace: string("\033[27;6;8~") \n

Then you can use this escape sequence¹:

bindkey -M emacs '^[[27;6;8~' backward-kill-word 

With terminals based on vte, including Gnome-terminal, Guake and Terminator, you're out of luck. They don't have any way to configure key bindings. They may be willing to add ad hoc support for a specific key though.

¹ I chose this sequence to be compatible with xterm's modifyOtherKeys mode. I'd normally recommend to enable modifyOtherKeys, which is mostly backwards compatible, but the particular key chord you want is only enabled at level 2, which is a pain to cope with (e.g. Ctrl+letter doesn't send the corresponding control character).

0
1

With X11 terminal emulators where Ctrl+Shift+Backspace sends the same thing as Backspace and that don't provide with any way to change it (and if you're desperate), as a dirty hack you could hijack the communication between the terminal emulator and the X server, and for instance replace Backspace (keycode 22) with F12 (keybode 96) in the X11 event message sent to the terminal emulator when Backspace is pressed while Shift and Ctrl are being held.

Incidentally, zsh makes it relatively easy as it has builtin Unix domain and TCP socket APIs. Run the script below as:

that-script guake

And add to your ~/.zshrc

if [ -n "$WRAPPED_DISPLAY" ]; then
  export DISPLAY="$WRAPPED_DISPLAY"
  unset DISPLAY
fi

(to avoid other applications started within that terminal to go through that wrapper).

And bind:

bindkey -M emacs '^[[24;6~' backward-kill-word

where \e[24;6~ is the sequence send upon pressing Ctrl+Shift+F12 in VTE and xterm at least.

#! /bin/zsh -

die() {
  (($# == 0)) || print -ru2 -- "$@"
  exit 1
}

case $DISPLAY in
  (:<->(.<->|))
    mode=unix;;
  ((localhost|127.0.0.1|"[::1]"):<->(.<->|))
    mode=tcp
    zmodload zsh/net/tcp || die;;
  (*)
    die "Unsupported display: $DISPLAY";;
esac

conn=${DISPLAY##*:}
port=${conn%%.*}
screennumber=${conn#$port}
(($# > 0)) || argv=(gnome-terminal --wait)

unset -v listen_fd
typeset -A clients

tcp_connect() ztcp -v localhost $((port + 6000))
unix_connect() zsocket /tmp/.X11-unix/X$port

zmodload zsh/net/socket || die
zmodload zsh/system || die
zmodload zsh/zselect || die

new_port=20
until
  new_socket_path=/tmp/.X11-unix/X$new_port
  zsocket -l $new_socket_path 2> /dev/null
do
  ((new_port++))
done
listen_fd=$REPLY

unset -v pid
trap '
  kill "$pid" 2> /dev/null
  wait "$pid"; ret=$?
  rm -f $new_socket_path
  exit "$ret"' EXIT INT TERM HUP
{
  coproc {
    export WRAPPED_DISPLAY=$DISPLAY DISPLAY=:$new_port$screennumber
    xauth list "$WRAPPED_DISPLAY" |
      awk '{$1 = "add " ENVIRON["DISPLAY"];print}' |
      xauth -q -
    "$@" <&3 3>&1 >&4 4>&- {listen_fd}<&-
  }
} 3<&0 4>&1
pid=$!
exec {child_monitor}<&p
coproc :

LC_ALL=C
set -o extendedglob

tear() {
  exec {1}>&- {2}>&-
  unset "clients[$1]"
}

typeset -A ready
while zselect -A ready -r $listen_fd $child_monitor ${(kv)clients}; do
  [[ $ready[$child_monitor] ]] && exit
  if [[ $ready[$listen_fd] ]]; then
    zsocket -a $listen_fd || die
    fd=$REPLY
    ${mode}_connect || die
    clients[$fd]=$REPLY
  fi
  for client server (${(kv)clients}) {
    for from fdin fdout (
      client $client $server
      server $server $client
    ) if [[ $ready[$fdin] ]]; then
        if sysread -s 65536 -i $fdin buf; then
          if [[ $from = server ]]; then
            if [[ $buf[1,2] = $'\x23\x83' ]]; then
              offsets=(9 17 73) # Generic XInputExtension Event 
            else
              offsets=(1 2 29)  # Normal Event
            fi
            if
              [[ $buf[offsets[1]] = ($'\x2'|$'\x3') ]] && # KeyPress or KeyRelease
                [[ $buf[offsets[2]] = $'\x16' ]] && # keycode 22, Backspace
                printf -v modifiers %d "'$buf[offsets[3]]" &&
                ((modifiers & 5 == 5)) # Shift+Ctrl
            then
              buf[offsets[2]]=$'\x60' # keycode 96, F12
            fi
          fi
          syswrite -o $fdout -- $buf || tear $client $server
        else
          tear $client $server
        fi
      fi
  }
done

Note that we're not doing a full X11 protocol interpretation there and assume that the keypress events turn up as full messages. If you press that key combination whilst the terminal is already busy talking to the X server, it may miss it. It also has a performance impact as all the X11 traffic has to go through that wrapper written in zsh (a shell, not a language geared for performance).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .