Make Backspace Work in emacs-eat

This post details my troubleshooting process for a specific issue I encountered while setting emacs-eat. For those unfamiliar, emacs-eat is a(nother) terminal emulator designed to run directly within Emacs. Even though it was functional for the most part after following the installation instructions, there was one major hurdle: the backspace key wasn't working.

Now, I'm genuinely confused about terminal emulators and the machinery behind it, so take the following as just a hint of where to look. I have no explanations why the setup wasn't correct in the first place and why my seemingly incomplete solution works.

Web search didn't lead to a specific solution. Some people had the problem and fixed it by changing TERM or eat-term-name or re-compiling the .terminfo files. None of that worked for me.

TERMINFO?

So, starting from scratch, I went to the documentation. It goes through some common problems, one of which applied to my configuration: $TERMINFO differed from eat-term-terminfo-directory. Now, I don't want to break my native terminal so to fix this, but I found that emacs sets INSIDE_EMACS and I can test for that:

if string match --quiet "*,eat*" "$INSIDE_EMACS"
    set -gx TERMINFO "[...]/straight/build-30.1/eat/terminfo"
end

Note: Far from perfect since this path is version dependent, so it will break as I update emacs

But that didn't work; backspace still did nothing.

stty -a?

A bit more search tells me I should check the output of stty -a.

stty -a
speed 9600 baud; 74 rows; 108 columns;
lflags: icanon isig iexten echo echoe -echok echoke -echonl echoctl
        -echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo
        -extproc
iflags: -istrip icrnl -inlcr -igncr -ixon -ixoff ixany imaxbel -iutf8
        -ignbrk brkint -inpck -ignpar -parmrk
oflags: opost onlcr -oxtabs -onocr -onlret
cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow
        -dtrflow -mdmbuf
cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;
        eol2 = <undef>; erase = <undef>; intr = ^C; kill = <undef>;
        lnext = ^V; min = 1; quit = ^\; reprint = ^R; start = ^Q;
        status = ^T; stop = ^S; susp = ^Z; time = 0; werase = ^W;

For reference, in iterm2 + fish:

speed 38400 baud; 74 rows; 318 columns;
lflags: icanon isig iexten echo echoe echok echoke -echonl echoctl
	-echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo
	-extproc
iflags: -istrip icrnl -inlcr -igncr -ixon -ixoff ixany imaxbel iutf8
	-ignbrk brkint -inpck -ignpar -parmrk
oflags: opost onlcr -oxtabs -onocr -onlret
cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow
	-dtrflow -mdmbuf
cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;
	eol2 = <undef>; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;
	min = 1; quit = ^\; reprint = ^R; start = ^Q; status = ^T;
	stop = ^S; susp = ^Z; time = 0; werase = ^W;

so erase is <undef> in eat. Why? No clue but OK let's define it to ^H:

stty erase '^H'

Nope, doesn't work. ^? neither. But let's leave it as ^? since it's the same as in iterm2 for the time being and look for another possible solution.

fish_key_reader enters the room

A bit more searching, this time focusing on fish. I find a clue that there's a fish_key_reader function:

╰─>$ fish_key_reader
Press a key:
bind delete 'do something'

It doesn't show any binding (not sure if it should, it doesn't show). Let's see in iterm2:

╰─>$ fish_key_reader
Press a key:
bind backspace 'do something'

No binding either, but the key it receives is different. OK, anyway let's try to bind it then with bind delete backward-delete-char.

Oh it works 🎉

Hmm, let's remove the stty erase, setting it back to <undef>. It still works….🧐🙄😮‍💨🤷‍♂️ I give up and settle on:

  • I keep the TERMINFO setting since it's the documentation's recommendation.
  • I don't keep the stty setting since it seems unnecessary.
  • I keep the bind, obviously.

Unresolved questions:

  • I don't know why the stty setting isn't set or isn't needed.
  • I don't know why through emacs, fish receive the delete event and not backspace though that's probably eat doing its think but then I'd expect it to be more documented, unless it's a specific eat * fish thing.