[ruby/reline] Always set ANSI CSI keybindings for Home, End, and

Arrow.
(https://github.com/ruby/reline/pull/569)

https://github.com/ruby/reline/commit/f363a43a45
This commit is contained in:
tomoya ishida 2023-07-19 22:25:54 +09:00 committed by git
parent ea2fc58d9a
commit ca561480ea
3 changed files with 57 additions and 35 deletions

View File

@ -14,10 +14,21 @@ class Reline::ANSI
'kcud1' => :ed_next_history,
'kcuf1' => :ed_next_char,
'kcub1' => :ed_prev_char,
'cuu' => :ed_prev_history,
'cud' => :ed_next_history,
'cuf' => :ed_next_char,
'cub' => :ed_prev_char,
}
ANSI_CURSOR_KEY_BINDINGS = {
# Up
'A' => [:ed_prev_history, {}],
# Down
'B' => [:ed_next_history, {}],
# Right
'C' => [:ed_next_char, { ctrl: :em_next_word, meta: :em_next_word }],
# Left
'D' => [:ed_prev_char, { ctrl: :ed_prev_word, meta: :ed_prev_word }],
# End
'F' => [:ed_move_to_end, {}],
# Home
'H' => [:ed_move_to_beg, {}],
}
if Reline::Terminfo.enabled?
@ -33,22 +44,12 @@ class Reline::ANSI
end
def self.set_default_key_bindings(config, allow_terminfo: true)
set_default_key_bindings_ansi_cursor(config)
if allow_terminfo && Reline::Terminfo.enabled?
set_default_key_bindings_terminfo(config)
else
set_default_key_bindings_comprehensive_list(config)
end
{
# extended entries of terminfo
[27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→, extended entry
[27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+←, extended entry
[27, 91, 49, 59, 51, 67] => :em_next_word, # Meta+→, extended entry
[27, 91, 49, 59, 51, 68] => :ed_prev_word, # Meta+←, extended entry
}.each_pair do |key, func|
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)
end
{
[27, 91, 90] => :completion_journey_up, # S-Tab
}.each_pair do |key, func|
@ -64,18 +65,33 @@ class Reline::ANSI
end
end
def self.set_default_key_bindings_ansi_cursor(config)
ANSI_CURSOR_KEY_BINDINGS.each do |char, (default_func, modifiers)|
bindings = [["\e[#{char}", default_func]] # CSI + char
if modifiers[:ctrl]
# CSI + ctrl_key_modifier + char
bindings << ["\e[1;5#{char}", modifiers[:ctrl]]
end
if modifiers[:meta]
# CSI + meta_key_modifier + char
bindings << ["\e[1;3#{char}", modifiers[:meta]]
# Meta(ESC) + CSI + char
bindings << ["\e\e[#{char}", modifiers[:meta]]
end
bindings.each do |sequence, func|
key = sequence.bytes
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)
end
end
end
def self.set_default_key_bindings_terminfo(config)
key_bindings = CAPNAME_KEY_BINDINGS.map do |capname, key_binding|
begin
key_code = Reline::Terminfo.tigetstr(capname)
case capname
# Escape sequences that omit the move distance and are set to defaults
# value 1 may be sometimes sent by pressing the arrow-key.
when 'cuu', 'cud', 'cuf', 'cub'
[ key_code.sub(/%p1%d/, '').bytes, key_binding ]
else
[ key_code.bytes, key_binding ]
end
[ key_code.bytes, key_binding ]
rescue Reline::Terminfo::TerminfoError
# capname is undefined
end
@ -94,14 +110,8 @@ class Reline::ANSI
[27, 91, 49, 126] => :ed_move_to_beg, # Home
[27, 91, 52, 126] => :ed_move_to_end, # End
[27, 91, 51, 126] => :key_delete, # Del
[27, 91, 65] => :ed_prev_history, # ↑
[27, 91, 66] => :ed_next_history, # ↓
[27, 91, 67] => :ed_next_char, # →
[27, 91, 68] => :ed_prev_char, # ←
# KDE
[27, 91, 72] => :ed_move_to_beg, # Home
[27, 91, 70] => :ed_move_to_end, # End
# Del is 0x08
[27, 71, 65] => :ed_prev_history, # ↑
[27, 71, 66] => :ed_next_history, # ↓
@ -118,12 +128,6 @@ class Reline::ANSI
# Del is 0x08
# Arrow keys are the same of KDE
# iTerm2
[27, 27, 91, 67] => :em_next_word, # Option+→, extended entry
[27, 27, 91, 68] => :ed_prev_word, # Option+←, extended entry
[195, 166] => :em_next_word, # Option+f
[195, 162] => :ed_prev_word, # Option+b
[27, 79, 65] => :ed_prev_history, # ↑
[27, 79, 66] => :ed_next_history, # ↓
[27, 79, 67] => :ed_next_char, # →

View File

@ -75,12 +75,28 @@ class Reline::ANSI::TestWithTerminfo < Reline::TestCase
omit e.message
end
# Home and End; always mapped regardless of terminfo enabled or not
def test_home_end
assert_key_binding("\e[H", :ed_move_to_beg)
assert_key_binding("\e[F", :ed_move_to_end)
end
# Arrow; always mapped regardless of terminfo enabled or not
def test_arrow
assert_key_binding("\e[A", :ed_prev_history)
assert_key_binding("\e[B", :ed_next_history)
assert_key_binding("\e[C", :ed_next_char)
assert_key_binding("\e[D", :ed_prev_char)
end
# Ctrl+arrow and Meta+arrow; always mapped regardless of terminfo enabled or not
def test_extended
assert_key_binding("\e[1;5C", :em_next_word) # Ctrl+→
assert_key_binding("\e[1;5D", :ed_prev_word) # Ctrl+←
assert_key_binding("\e[1;3C", :em_next_word) # Meta+→
assert_key_binding("\e[1;3D", :ed_prev_word) # Meta+←
assert_key_binding("\e\e[C", :em_next_word) # Meta+→
assert_key_binding("\e\e[D", :ed_prev_word) # Meta+←
end
# Shift-Tab; always mapped regardless of terminfo enabled or not

View File

@ -60,6 +60,8 @@ class Reline::ANSI::TestWithoutTerminfo < Reline::TestCase
assert_key_binding("\e[1;5D", :ed_prev_word) # Ctrl+←
assert_key_binding("\e[1;3C", :em_next_word) # Meta+→
assert_key_binding("\e[1;3D", :ed_prev_word) # Meta+←
assert_key_binding("\e\e[C", :em_next_word) # Meta+→
assert_key_binding("\e\e[D", :ed_prev_word) # Meta+←
end
# Shift-Tab; always mapped regardless of terminfo enabled or not