Use inputrc data for keystroke setting

This commit is contained in:
aycabta 2019-06-01 09:05:58 +09:00
parent c1e5299787
commit 7df65ef676
8 changed files with 109 additions and 82 deletions

View File

@ -326,9 +326,12 @@ module Reline
@@line_editor.pre_input_hook = @@pre_input_hook @@line_editor.pre_input_hook = @@pre_input_hook
@@line_editor.rerender @@line_editor.rerender
config = Reline::IOGate::RAW_KEYSTROKE_CONFIG @@config.reset_default_key_bindings
Reline::IOGate::RAW_KEYSTROKE_CONFIG.each_pair do |key, func|
@@config.add_default_key_binding(key, func)
end
key_stroke = Reline::KeyStroke.new(config) key_stroke = Reline::KeyStroke.new(@@config)
begin begin
loop do loop do
key_stroke.read_io(@@config.keyseq_timeout) { |inputs| key_stroke.read_io(@@config.keyseq_timeout) { |inputs|

View File

@ -1,15 +1,13 @@
class Reline::ANSI class Reline::ANSI
RAW_KEYSTROKE_CONFIG = { RAW_KEYSTROKE_CONFIG = {
key_mapping: { [27, 91, 65] => :ed_prev_history, # ↑
[27, 91, 65] => :ed_prev_history, # ↑ [27, 91, 66] => :ed_next_history, # ↓
[27, 91, 66] => :ed_next_history, # ↓ [27, 91, 67] => :ed_next_char, # →
[27, 91, 67] => :ed_next_char, # → [27, 91, 68] => :ed_prev_char, # ←
[27, 91, 68] => :ed_prev_char, # ← [27, 91, 51, 126] => :key_delete, # Del
[27, 91, 51, 126] => :key_delete, # Del [27, 91, 49, 126] => :ed_move_to_beg, # Home
[27, 91, 49, 126] => :ed_move_to_beg, # Home [27, 91, 52, 126] => :ed_move_to_end, # End
[27, 91, 52, 126] => :ed_move_to_end, # End }.each_key(&:freeze).freeze
}.each_key(&:freeze).freeze,
}.freeze
@@input = STDIN @@input = STDIN
def self.input=(val) def self.input=(val)

View File

@ -36,6 +36,8 @@ class Reline::Config
end end
def initialize def initialize
@additional_key_bindings = {}
@default_key_bindings = {}
@skip_section = nil @skip_section = nil
@if_stack = [] @if_stack = []
@editing_mode_label = :emacs @editing_mode_label = :emacs
@ -52,6 +54,8 @@ class Reline::Config
if editing_mode_is?(:vi_command) if editing_mode_is?(:vi_command)
@editing_mode_label = :vi_insert @editing_mode_label = :vi_insert
end end
@additional_key_bindings = {}
@default_key_bindings = {}
end end
def editing_mode def editing_mode
@ -88,8 +92,23 @@ class Reline::Config
self self
end end
def key_bindings
# override @default_key_bindings with @additional_key_bindings
@default_key_bindings.merge(@additional_key_bindings)
end
def add_default_key_binding(keystroke, target)
@default_key_bindings[keystroke] = target
end
def reset_default_key_bindings
@default_key_bindings = {}
end
def read_lines(lines) def read_lines(lines)
lines.each do |line| lines.each do |line|
next if line.start_with?('#')
line = line.chomp.gsub(/^\s*/, '') line = line.chomp.gsub(/^\s*/, '')
if line[0, 1] == '$' if line[0, 1] == '$'
handle_directive(line[1..-1]) handle_directive(line[1..-1])
@ -106,7 +125,8 @@ class Reline::Config
if line =~ /\s*(.*)\s*:\s*(.*)\s*$/ if line =~ /\s*(.*)\s*:\s*(.*)\s*$/
key, func_name = $1, $2 key, func_name = $1, $2
bind_key(key, func_name) keystroke, func = bind_key(key, func_name)
@additional_key_bindings[keystroke] = func
end end
end end
end end
@ -187,59 +207,57 @@ class Reline::Config
def bind_key(key, func_name) def bind_key(key, func_name)
if key =~ /"(.*)"/ if key =~ /"(.*)"/
keyseq = parse_keyseq($1).force_encoding('ASCII-8BIT') keyseq = parse_keyseq($1)
else else
keyseq = nil keyseq = nil
end end
if func_name =~ /"(.*)"/ if func_name =~ /"(.*)"/
func = parse_keyseq($1).force_encoding('ASCII-8BIT') func = parse_keyseq($1)
else else
func = func_name.to_sym # It must be macro. func = func_name.tr(?-, ?_).to_sym # It must be macro.
end end
[keyseq, func] [keyseq, func]
end end
def key_notation_to_char(notation) def key_notation_to_code(notation)
case notation case notation
when /\\C-([A-Za-z_])/ when /\\C-([A-Za-z_])/
(1 + $1.downcase.ord - ?a.ord).chr('ASCII-8BIT') (1 + $1.downcase.ord - ?a.ord)
when /\\M-([0-9A-Za-z_])/ when /\\M-([0-9A-Za-z_])/
modified_key = $1 modified_key = $1
code = case $1
case $1 when /[0-9]/
when /[0-9]/ ?\M-0.bytes.first + (modified_key.ord - ?0.ord)
?\M-0.bytes.first + (modified_key.ord - ?0.ord) when /[A-Z]/
when /[A-Z]/ ?\M-A.bytes.first + (modified_key.ord - ?A.ord)
?\M-A.bytes.first + (modified_key.ord - ?A.ord) when /[a-z]/
when /[a-z]/ ?\M-a.bytes.first + (modified_key.ord - ?a.ord)
?\M-a.bytes.first + (modified_key.ord - ?a.ord) end
end
code.chr('ASCII-8BIT')
when /\\C-M-[A-Za-z_]/, /\\M-C-[A-Za-z_]/ when /\\C-M-[A-Za-z_]/, /\\M-C-[A-Za-z_]/
# 129 M-^A # 129 M-^A
when /\\(\d{1,3})/ then $1.to_i(8).chr # octal when /\\(\d{1,3})/ then $1.to_i(8) # octal
when /\\x(\h{1,2})/ then $1.to_i(16).chr # hexadecimal when /\\x(\h{1,2})/ then $1.to_i(16) # hexadecimal
when "\\e" then ?\e when "\\e" then ?\e.ord
when "\\\\" then ?\\ when "\\\\" then ?\\.ord
when "\\\"" then ?" when "\\\"" then ?".ord
when "\\'" then ?' when "\\'" then ?'.ord
when "\\a" then ?\a when "\\a" then ?\a.ord
when "\\b" then ?\b when "\\b" then ?\b.ord
when "\\d" then ?\d when "\\d" then ?\d.ord
when "\\f" then ?\f when "\\f" then ?\f.ord
when "\\n" then ?\n when "\\n" then ?\n.ord
when "\\r" then ?\r when "\\r" then ?\r.ord
when "\\t" then ?\t when "\\t" then ?\t.ord
when "\\v" then ?\v when "\\v" then ?\v.ord
else notation else notation.ord
end end
end end
def parse_keyseq(str) def parse_keyseq(str)
# TODO: Control- and Meta- # TODO: Control- and Meta-
ret = String.new(encoding: 'ASCII-8BIT') ret = []
while str =~ /(\\C-[A-Za-z_]|\\M-[0-9A-Za-z_]|\\C-M-[A-Za-z_]|\\M-C-[A-Za-z_]|\\e|\\\\|\\"|\\'|\\a|\\b|\\d|\\f|\\n|\\r|\\t|\\v|\\\d{1,3}|\\x\h{1,2}|.)/ while str =~ /(\\C-[A-Za-z_]|\\M-[0-9A-Za-z_]|\\C-M-[A-Za-z_]|\\M-C-[A-Za-z_]|\\e|\\\\|\\"|\\'|\\a|\\b|\\d|\\f|\\n|\\r|\\t|\\v|\\\d{1,3}|\\x\h{1,2}|.)/
ret << key_notation_to_char($&) ret << key_notation_to_code($&)
str = $' str = $'
end end
ret ret

View File

@ -1,7 +1,7 @@
require 'timeout' require 'timeout'
class Reline::GeneralIO class Reline::GeneralIO
RAW_KEYSTROKE_CONFIG = {key_mapping: {}.freeze}.freeze RAW_KEYSTROKE_CONFIG = {}.freeze
@@buf = [] @@buf = []

View File

@ -121,6 +121,6 @@ class Reline::KeyStroke
end end
def key_mapping def key_mapping
@config[:key_mapping].transform_keys(&:bytes) @config.key_bindings
end end
end end

View File

@ -2,16 +2,14 @@ require 'fiddle/import'
class Reline::Windows class Reline::Windows
RAW_KEYSTROKE_CONFIG = { RAW_KEYSTROKE_CONFIG = {
key_mapping: { [224, 72] => :ed_prev_history, # ↑
[224, 72] => :ed_prev_history, # ↑ [224, 80] => :ed_next_history, # ↓
[224, 80] => :ed_next_history, # ↓ [224, 77] => :ed_next_char, # →
[224, 77] => :ed_next_char, # → [224, 75] => :ed_prev_char, # ←
[224, 75] => :ed_prev_char, # ← [224, 83] => :key_delete, # Del
[224, 83] => :key_delete, # Del [224, 71] => :ed_move_to_beg, # Home
[224, 71] => :ed_move_to_beg, # Home [224, 79] => :ed_move_to_end, # End
[224, 79] => :ed_move_to_end, # End }.each_key(&:freeze).freeze
}.each_key(&:freeze).freeze,
}.freeze
class Win32API class Win32API
DLL = {} DLL = {}

View File

@ -28,42 +28,40 @@ class Reline::Config::Test < Reline::TestCase
end end
def test_bind_key def test_bind_key
key, func = @config.bind_key('"input"', '"abcde"') assert_equal ['input'.bytes, 'abcde'.bytes], @config.bind_key('"input"', '"abcde"')
assert_equal 'input', key
assert_equal 'abcde', func
end end
def test_bind_key_with_macro def test_bind_key_with_macro
key, func = @config.bind_key('"input"', 'abcde')
assert_equal 'input', key assert_equal ['input'.bytes, :abcde], @config.bind_key('"input"', 'abcde')
assert_equal :abcde, func
end end
def test_bind_key_with_escaped_chars def test_bind_key_with_escaped_chars
assert_equal ['input', "\e \\ \" ' \a \b \d \f \n \r \t \v"], @config.bind_key('"input"', '"\\e \\\\ \\" \\\' \\a \\b \\d \\f \\n \\r \\t \\v"') key, func =
assert_equal ['input'.bytes, "\e \\ \" ' \a \b \d \f \n \r \t \v".bytes], @config.bind_key('"input"', '"\\e \\\\ \\" \\\' \\a \\b \\d \\f \\n \\r \\t \\v"')
end end
def test_bind_key_with_ctrl_chars def test_bind_key_with_ctrl_chars
assert_equal ['input', "\C-h\C-h"], @config.bind_key('"input"', '"\C-h\C-H"') assert_equal ['input'.bytes, "\C-h\C-h".bytes], @config.bind_key('"input"', '"\C-h\C-H"')
end end
def test_bind_key_with_meta_chars def test_bind_key_with_meta_chars
assert_equal ['input', "\M-h\M-H".force_encoding('ASCII-8BIT')], @config.bind_key('"input"', '"\M-h\M-H"') assert_equal ['input'.bytes, "\M-h\M-H".bytes], @config.bind_key('"input"', '"\M-h\M-H"')
end end
def test_bind_key_with_octal_number def test_bind_key_with_octal_number
assert_equal ['input', "\1"], @config.bind_key('"input"', '"\1"') input = %w{i n p u t}.map(&:ord)
assert_equal ['input', "\12"], @config.bind_key('"input"', '"\12"') assert_equal [input, "\1".bytes], @config.bind_key('"input"', '"\1"')
assert_equal ['input', "\123"], @config.bind_key('"input"', '"\123"') assert_equal [input, "\12".bytes], @config.bind_key('"input"', '"\12"')
assert_equal ['input', ["\123", '4'].join], @config.bind_key('"input"', '"\1234"') assert_equal [input, "\123".bytes], @config.bind_key('"input"', '"\123"')
assert_equal [input, "\123".bytes + '4'.bytes], @config.bind_key('"input"', '"\1234"')
end end
def test_bind_key_with_hexadecimal_number def test_bind_key_with_hexadecimal_number
assert_equal ['input', "\x4"], @config.bind_key('"input"', '"\x4"') input = %w{i n p u t}.map(&:ord)
assert_equal ['input', "\x45"], @config.bind_key('"input"', '"\x45"') assert_equal [input, "\x4".bytes], @config.bind_key('"input"', '"\x4"')
assert_equal ['input', ["\x45", '6'].join], @config.bind_key('"input"', '"\x456"') assert_equal [input, "\x45".bytes], @config.bind_key('"input"', '"\x45"')
assert_equal [input, "\x45".bytes + '6'.bytes], @config.bind_key('"input"', '"\x456"')
end end
def test_include def test_include
@ -115,4 +113,15 @@ class Reline::Config::Test < Reline::TestCase
assert_equal :audible, @config.instance_variable_get(:@bell_style) assert_equal :audible, @config.instance_variable_get(:@bell_style)
end end
def test_default_key_bindings
@config.add_default_key_binding('abcd'.bytes, 'EFGH'.bytes)
@config.read_lines(<<~'LINES'.split(/(?<=\n)/))
"abcd": "ABCD"
"ijkl": "IJKL"
LINES
expected = { 'abcd'.bytes => 'ABCD'.bytes, 'ijkl'.bytes => 'IJKL'.bytes }
assert_equal expected, @config.key_bindings
end
end end

View File

@ -14,14 +14,15 @@ class Reline::KeyStroke::Test < Reline::TestCase
} }
def test_match_status def test_match_status
config = { config = Reline::Config.new
key_mapping: { {
"a" => "xx", "a" => "xx",
"ab" => "y", "ab" => "y",
"abc" => "z", "abc" => "z",
"x" => "rr" "x" => "rr"
} }.each_pair do |key, func|
} config.add_default_key_binding(key.bytes, func.bytes)
end
stroke = Reline::KeyStroke.new(config) stroke = Reline::KeyStroke.new(config)
assert_equal(:matching, stroke.match_status("a".bytes)) assert_equal(:matching, stroke.match_status("a".bytes))
assert_equal(:matching, stroke.match_status("ab".bytes)) assert_equal(:matching, stroke.match_status("ab".bytes))