irb: In macOS, home and end keys no longer work

Description

In macOS (I am using Monterey 12.1), the home and end keys no longer move the cursor to the start of the line and the end of the line respectively. Instead, these keys output the escape sequences you see in the screenshot below.

Screen Shot 2022-01-12 at 6 28 57 AM

This behavior is new in irb that ships with Ruby 3.1. In the previous version, these keys worked as expected.

Result of irb_info

Ruby version: 3.1.0
IRB version: irb 1.4.1 (2021-12-25)
InputMethod: ReidlineInputMethod with Reline 0.3.1
RUBY_PLATFORM: x86_64-darwin21
LANG env: en_US.UTF-8
East Asian Ambiguous Width: 1

Terminal Emulator

I am using iTerm2 Build 3.4.14 which is configured to use xterm-256color.

Setting Files

I am not using a settings file.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 7
  • Comments: 22 (2 by maintainers)

Commits related to this issue

Most upvoted comments

For those who would prefer to do the above patches at runtime, for macOS/iTerm2, add this code to “~/.irbrc”:

require "reline/ansi"

if defined?(Reline::ANSI::CAPNAME_KEY_BINDINGS) # make sure you're using an affected version
  # Fix insert, delete, pgup, and pgdown.
  Reline::ANSI::CAPNAME_KEY_BINDINGS.merge!({
    "kich1" => :ed_ignore,
    "kdch1" => :key_delete,
    "kpp" => :ed_ignore,
    "knp" => :ed_ignore
  })

  Reline::ANSI.singleton_class.prepend(
    Module.new do
      def set_default_key_bindings(config)
        # Fix home and end.
        set_default_key_bindings_comprehensive_list(config)
        # Fix iTerm2 insert.
        key = [239, 157, 134]
        func = :ed_ignore
        config.add_default_key_binding_by_keymap(:emacs, key, func)
        config.add_default_key_binding_by_keymap(:vi_insert, key, func)
        config.add_default_key_binding_by_keymap(:vi_command, key, func)
        # The rest of the behavior.
        super
      end
    end
  )
end

No switch to application mode needed, and no iTerm2 rebinding.

I discovered a new workaround that is relatively simple and fixes all keys; just modify ~/.inputrc to contain the following:

$if Ruby
    "\e[H": beginning-of-line
    "\e[F": end-of-line
    "\e[3~": delete-char
    "\e[2~": ""
    "\e[5~": ""
    "\e[6~": ""
    "\xef\x9d\x86": ""
$endif

I did a bunch of testing and I am convinced that iTerm2 is sending the wrong escape sequence for Home and End I believe iTerm2 is sending the correct sequences for everything except Insert.

Update: The main problem with Home and End is that the latest version of Reline only maps the application mode codes (SS3 == escape + O) not normal mode codes (CSI == escape + [), but it doesn’t put the terminal into application mode.

The main problem with Delete, PgUp, and PgDown is that Reline is not mapping them to anything.

So here are all the issues and workarounds / solutions I’ve come up with:

Home key (khome)

  • Problem: Reline only recognizes \eOH not \e[H.
  • Workaround 1: Use Key Mappings to force iTerm2 to always map Home to \EOH
    • (Probably not a good idea)
  • Workaround 2: Force terminal into application mode when running IRB (and return to normal mode after)
    • See ~/.irbrc code block below
  • Solution 1: Fix Reline to put terminal into application mode (and return to normal mode after)
  • Solution 2: Fix Reline to recognize both sequences like it used to

End key (kend)

  • Problem: Reline only recognizes \eOF not \E[F.
  • Workaround 1: Use Key Mappings to force iTerm2 to always map End to \EOF
    • (Probably not a good idea)
  • Workaround 2: Force terminal into application mode when running IRB (and return to normal mode after)
    • See ~/.irbrc code block below
  • Solution 1: Fix Reline to put terminal into application mode (and return to normal mode after)
  • Solution 2: Fix Reline to recognize both sequences like it used to

Delete key (kdch1 )

  • Problem: Reline doesn’t map it to anything (although somehow it works on Linux)
    • Update: I believe the reason I wasn’t seeing the problem on Linux is because reline was failing to load ncurses so it wasn’t even using Reline::ANSI; see my comment here; anyway, now that I have ncurses loading I am also able to reproduce all these problems on Linux
  • Solution: Update Reline code to map 'kdch1' to :key_delete

Insert key (kich1)

  • Problem 1: iTerm2 maps it to codes [239, 157, 134], which is incorrect for xterm
  • Problem 2: Reline doesn’t map it to anything
  • Solution 1: Change iTerm2 Key Mappings to map it to \E[2~
  • Solution 2: Update Reline code to map 'kich1' to :ed_ignore

PgUp key (kpp)

PgDn key (knp)

Here’s a screenshot showing Home, End, and Insert key mappings changed in iTerm2 (not recommended, except for Insert key):

iterm2 fixes

Here’s what I added to ~/.irbrc to make it switch the terminal into application mode:

::IRB.conf[:USE_AUTOCOMPLETE] = false
::IRB.conf[:USE_COLORIZE] = true

# Enter application cursor mode:
print "\e[?1h"

# Leave application cursor mode:
::IRB.conf[:AT_EXIT] << proc { print "\e[?1l" }

Here’s a screenshot showing how to fix the Delete key in Reline, and also how to prevent Insert, PgUp, and PgDn from printing out nasties:

reline fixes

I am seeing a similar issue with the Delete key. Output inside irb: Screen Shot 2022-01-24 at 6 43 30 AM

ruby -r reline -e 'while (c = Reline::IOGate.getc); pp c; end' shows the following for the Delete key.

❯ ruby -r reline -e 'while (c = Reline::IOGate.getc); pp c; end'
27
91
51
126

I think this has been addressed by https://github.com/ruby/reline/pull/569? cc @tompng

I discovered the following workaround for my setup (using archlinux, kitty and fish): IRB.conf[:USE_MULTILINE] = false in ~/.irbrc

@neonn I think adding your experience is helpful and a good reminder to the maintainers of irb and reline that we still have people needing this to be fixed.

I’ve been waiting patiently for these fixes to be merged in (the last one is still DRAFT, but the first two are tiny and have been ready to go for 9 months):

The workarounds work, but any permanent solution?

@rgaufman the delete key is fixed now, but we need to get one of these (preferably both) across the finish line to fix Home and End:

I think this may be a problem in reline, introduced in reline v0.2.7.

I switched to Ruby 3.0 with reline 0.2.0 and the problem went away. Then I tried various versions of reline and the problem first shows up in reline 0.2.7.

Digging further, it appears that the problem surfaced when default key bindings started using set_default_key_bindings_terminfo instead of set_default_key_bindings_comprehensive_list.

I managed to fix this by mapping Home and End keys in my iTerm2 profile key mappings to the hex codes 0x01 and 0x05 respectively. These hex codes map to Ctrl+A and Ctrl+E which in an iTerm2 session, function as Home (go to beginning of line) and End (go to end of line) and work as expected in irb. From my profile key mapping settings:

Screen Shot 2022-01-12 at 10 09 16 AM