blog/source/_posts/ctrl-h-backspace.md

5.9 KiB

title excerpt date tags
Mapping Ctrl+H to Backspace in terminal emulator Also fix Ctrl+Backspace in PowerShell 2023-07-17
linux
zsh
powershell

A few months ago, there was an article which encouraged Linux users to use more readline keyboard shortcuts. readline keyboard shortcuts are based on Emacs keybindings, while also support switching to vi keybindings. At that time, I was only familiar with Ctrl+a (line start) and Ctrl+e (line end). Interested to learn more tricks, I went on search for a cheatsheet and found this. I then added two missing shortcuts (Ctrl+h & Ctrl+d), printed it out and stick it to my desk.

readline keyboard shortcuts

However there were two shortcuts which did not work as intended: Ctrl+h and Ctrl+Backspace. The first one is supposed to be equivalent to backspace, but it was deleting previous word just like Ctrl+Backspace or Ctrl+w. The second one did not work on PowerShell's Emacs mode.

While looking for a workaround for other terminal and shell, I find it helpful to remember these two facts so that you can stay on the right track.

  • $TERM does not refer to the terminal emulator
  • Shell does not recognise Ctrl+Backspace

$TERM is not the terminal emulator

In Kitty, $TERM is "xterm-kitty"; most other Linux terminals output it as "xterm-256color". The value actually refers to the "terminfo" being used and not the terminal emulator.

Shell does not recognise Ctrl+Backspace

When Ctrl+Backspace is pressed, a terminal emulator either sends "^?" or "^H" control character to the shell, which then initiate an action (e.g. "backward-kill-word").

"^[character]" is first and foremost a caret notation of a control character, a friendlier representation of hexadecimal, much like hexadecimal is a nicer representation of binary. "^H" actually means control-code-8 (H is the eighth letter), instead of representing Ctrl+h. "^H" can be entered using Ctrl+h simply because it is more practical than having a dedicated key for each control character on a keyboard.

Remap Ctrl+h to ^?

Most terminal emulators map Backspace to "^?" and Ctrl+Backspace to "^H". Since Ctrl+h is also mapped to "^H", thus sharing a similar action ("backward-kill-word") with Ctrl+Backspace. The easiest fix is to remap Ctrl+h to "^?". This approach only needs to configure the terminal emulator.

To check which control character is mapped to:

$ showkey -a

# backspace
^? 	127 0177 0x7f

# ctrl+ backspace
^H 	 8 0010 0x08

kitty

map ctrl+h send_text normal \x7f

Add the above line to the end of "$HOME/.config/kitty/kitty.conf". "7f" is the hex of "^?".

Press Ctrl+Shirt+F5 to reload the config and run showkey -a to verify Ctrl+h has been remapped.

$ showkey -a

# ctrl+h
^? 	127 0177 0x7f

Windows Terminal

Go Settings -> Open JSON file which will open "$home\AppData\Local\Packages\Microsoft.WindowsTerminal_xxx\LocalState\settings.json". Under "actions" list, append the following object.

{
  "command": {
    "action": "sendInput",
    "input": "\u007F"
  },
  "keys": "ctrl+h"
}

Map Ctrl+Backspace to backward-kill-word

Ctrl+Backspace does not work as expected when I switch the PowerShell's edit mode to Emacs Set-PSReadLineOption -EditMode Emacs, even though it works in the default Cmd mode. This is because PowerShell binds it to BackwardDeleteChar in Emacs mode. Somehow I could not remap it to "^H" (\b).

Some xterm users also have this issue and a workaround is by mapping it to an unused escape sequence, then bind it to backward-kill-word in the shell. While Windows Terminal supports sending an escape sequence, the corresponding binding is not supported in PowerShell. Instead of using escape sequence, let's use a unicode character, specifically a character within the range of private use area (U+E888-U+F8FF) to avoid conflict with existing characters. I choose U+E888 for this example.

Anyhow, it is only a tiny issue for me since I can always use Ctrl+w.

Windows Terminal

Go Settings -> Open JSON file which will open "$home\AppData\Local\Packages\Microsoft.WindowsTerminal_xxx\LocalState\settings.json". Under "actions" list, append the following object.

{
  "command": {
    "action": "sendInput",
    "input": "\uE888"
  },
  "keys": "ctrl+backspace"
}

PowerShell

Set-PSReadLineKeyHandler -Chord "`u{E888}" -Function BackwardKillWord

The following Windows Terminal + PowerShell configs did not work for me. Windows Terminal did yield the correct control character, but somehow PowerShell could not recognise it.

{
  "command": {
    "action": "sendInput",
    "input": "\u007F"
  },
  "keys": "backspace"
},
{
  "command": {
    "action": "sendInput",
    "input": "\b"
  },
  "keys": "ctrl+backspace"
}
Set-PSReadLineKeyHandler -Chord "`u{007F}" -Function BackwardDeleteChar
Set-PSReadLineKeyHandler -Chord "`b" -Function BackwardKillWord

zsh

bindkey '\uE888' backward-kill-word

bash

bind '"\uE888":backward-kill-word'