summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/reline/config.rb48
-rw-r--r--test/reline/test_config.rb47
2 files changed, 55 insertions, 40 deletions
diff --git a/lib/reline/config.rb b/lib/reline/config.rb
index 6aa6ba8d94..e0fc37fc68 100644
--- a/lib/reline/config.rb
+++ b/lib/reline/config.rb
@@ -1,7 +1,7 @@
class Reline::Config
attr_reader :test_mode
- KEYSEQ_PATTERN = /\\(?:C|Control)-[A-Za-z_]|\\(?:M|Meta)-[0-9A-Za-z_]|\\(?:C|Control)-(?:M|Meta)-[A-Za-z_]|\\(?:M|Meta)-(?:C|Control)-[A-Za-z_]|\\e|\\[\\\"\'abdfnrtv]|\\\d{1,3}|\\x\h{1,2}|./
+ KEYSEQ_PATTERN = /\\(?:C|Control)-[A-Za-z_]|\\(?:M|Meta)-[0-9A-Za-z_]|\\(?:C|Control)-\\(?:M|Meta)-[A-Za-z_]|\\(?:M|Meta)-\\(?:C|Control)-[A-Za-z_]|\\e|\\[\\\"\'abdfnrtv]|\\\d{1,3}|\\x\h{1,2}|./
class InvalidInputrc < RuntimeError
attr_accessor :file, :lineno
@@ -194,13 +194,14 @@ class Reline::Config
# value ignores everything after a space, raw_value does not.
var, value, raw_value = $1.downcase, $2.partition(' ').first, $2
bind_variable(var, value, raw_value)
- next
- when /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
- key, func_name = $1, $2
- func_name = func_name.split.first
- keystroke, func = bind_key(key, func_name)
- next unless keystroke
- @additional_key_bindings[@keymap_label].add(@keymap_prefix + keystroke, func)
+ when /^\s*(?:M|Meta)-([a-zA-Z_])\s*:\s*(.*)\s*$/o
+ bind_key("\"\\M-#$1\"", $2)
+ when /^\s*(?:C|Control)-([a-zA-Z_])\s*:\s*(.*)\s*$/o
+ bind_key("\"\\C-#$1\"", $2)
+ when /^\s*(?:(?:C|Control)-(?:M|Meta)|(?:M|Meta)-(?:C|Control))-([a-zA-Z_])\s*:\s*(.*)\s*$/o
+ bind_key("\"\\M-\\C-#$1\"", $2)
+ when /^\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
+ bind_key($1, $2)
end
end
unless if_stack.empty?
@@ -310,7 +311,12 @@ class Reline::Config
parse_keyseq(str).map { |c| c.chr(Reline.encoding_system_needs) }.join
end
- def bind_key(key, func_name)
+ def bind_key(key, value)
+ keystroke, func = parse_key_binding(key, value)
+ @additional_key_bindings[@keymap_label].add(@keymap_prefix + keystroke, func) if keystroke
+ end
+
+ def parse_key_binding(key, func_name)
if key =~ /\A"(.*)"\z/
keyseq = parse_keyseq($1)
else
@@ -319,27 +325,19 @@ class Reline::Config
if func_name =~ /"(.*)"/
func = parse_keyseq($1)
else
- func = func_name.tr(?-, ?_).to_sym # It must be macro.
+ func = func_name.split.first.tr(?-, ?_).to_sym # It must be macro.
end
[keyseq, func]
end
def key_notation_to_code(notation)
case notation
+ when /(?:\\(?:C|Control)-\\(?:M|Meta)|\\(?:M|Meta)-\\(?:C|Control))-([A-Za-z_])/
+ [?\e.ord, $1.ord % 32]
when /\\(?:C|Control)-([A-Za-z_])/
- (1 + $1.downcase.ord - ?a.ord)
+ ($1.upcase.ord % 32)
when /\\(?:M|Meta)-([0-9A-Za-z_])/
- modified_key = $1
- case $1
- when /[0-9]/
- ?\M-0.bytes.first + (modified_key.ord - ?0.ord)
- when /[A-Z]/
- ?\M-A.bytes.first + (modified_key.ord - ?A.ord)
- when /[a-z]/
- ?\M-a.bytes.first + (modified_key.ord - ?a.ord)
- end
- when /\\(?:C|Control)-(?:M|Meta)-[A-Za-z_]/, /\\(?:M|Meta)-(?:C|Control)-[A-Za-z_]/
- # 129 M-^A
+ [?\e.ord, $1.ord]
when /\\(\d{1,3})/ then $1.to_i(8) # octal
when /\\x(\h{1,2})/ then $1.to_i(16) # hexadecimal
when "\\e" then ?\e.ord
@@ -359,11 +357,9 @@ class Reline::Config
end
def parse_keyseq(str)
- ret = []
- str.scan(KEYSEQ_PATTERN) do
- ret << key_notation_to_code($&)
+ str.scan(KEYSEQ_PATTERN).flat_map do |notation|
+ key_notation_to_code(notation)
end
- ret
end
def reload
diff --git a/test/reline/test_config.rb b/test/reline/test_config.rb
index 68a102a599..878477fe5e 100644
--- a/test/reline/test_config.rb
+++ b/test/reline/test_config.rb
@@ -126,41 +126,46 @@ class Reline::Config::Test < Reline::TestCase
end
def test_bind_key
- assert_equal ['input'.bytes, 'abcde'.bytes], @config.bind_key('"input"', '"abcde"')
+ assert_equal ['input'.bytes, 'abcde'.bytes], @config.parse_key_binding('"input"', '"abcde"')
end
def test_bind_key_with_macro
- assert_equal ['input'.bytes, :abcde], @config.bind_key('"input"', 'abcde')
+ assert_equal ['input'.bytes, :abcde], @config.parse_key_binding('"input"', 'abcde')
end
def test_bind_key_with_escaped_chars
- 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"')
+ assert_equal ['input'.bytes, "\e \\ \" ' \a \b \d \f \n \r \t \v".bytes], @config.parse_key_binding('"input"', '"\\e \\\\ \\" \\\' \\a \\b \\d \\f \\n \\r \\t \\v"')
end
def test_bind_key_with_ctrl_chars
- assert_equal ['input'.bytes, "\C-h\C-h".bytes], @config.bind_key('"input"', '"\C-h\C-H"')
- assert_equal ['input'.bytes, "\C-h\C-h".bytes], @config.bind_key('"input"', '"\Control-h\Control-H"')
+ assert_equal ['input'.bytes, "\C-h\C-h\C-_".bytes], @config.parse_key_binding('"input"', '"\C-h\C-H\C-_"')
+ assert_equal ['input'.bytes, "\C-h\C-h\C-_".bytes], @config.parse_key_binding('"input"', '"\Control-h\Control-H\Control-_"')
end
def test_bind_key_with_meta_chars
- assert_equal ['input'.bytes, "\M-h\M-H".bytes], @config.bind_key('"input"', '"\M-h\M-H"')
- assert_equal ['input'.bytes, "\M-h\M-H".bytes], @config.bind_key('"input"', '"\Meta-h\Meta-H"')
+ assert_equal ['input'.bytes, "\eh\eH\e_".bytes], @config.parse_key_binding('"input"', '"\M-h\M-H\M-_"')
+ assert_equal ['input'.bytes, "\eh\eH\e_".bytes], @config.parse_key_binding('"input"', '"\Meta-h\Meta-H\M-_"')
+ end
+
+ def test_bind_key_with_ctrl_meta_chars
+ assert_equal ['input'.bytes, "\e\C-h\e\C-h\e\C-_".bytes], @config.parse_key_binding('"input"', '"\M-\C-h\C-\M-H\M-\C-_"')
+ assert_equal ['input'.bytes, "\e\C-h\e\C-_".bytes], @config.parse_key_binding('"input"', '"\Meta-\Control-h\Control-\Meta-_"')
end
def test_bind_key_with_octal_number
input = %w{i n p u t}.map(&:ord)
- assert_equal [input, "\1".bytes], @config.bind_key('"input"', '"\1"')
- assert_equal [input, "\12".bytes], @config.bind_key('"input"', '"\12"')
- assert_equal [input, "\123".bytes], @config.bind_key('"input"', '"\123"')
- assert_equal [input, "\123".bytes + '4'.bytes], @config.bind_key('"input"', '"\1234"')
+ assert_equal [input, "\1".bytes], @config.parse_key_binding('"input"', '"\1"')
+ assert_equal [input, "\12".bytes], @config.parse_key_binding('"input"', '"\12"')
+ assert_equal [input, "\123".bytes], @config.parse_key_binding('"input"', '"\123"')
+ assert_equal [input, "\123".bytes + '4'.bytes], @config.parse_key_binding('"input"', '"\1234"')
end
def test_bind_key_with_hexadecimal_number
input = %w{i n p u t}.map(&:ord)
- assert_equal [input, "\x4".bytes], @config.bind_key('"input"', '"\x4"')
- assert_equal [input, "\x45".bytes], @config.bind_key('"input"', '"\x45"')
- assert_equal [input, "\x45".bytes + '6'.bytes], @config.bind_key('"input"', '"\x456"')
+ assert_equal [input, "\x4".bytes], @config.parse_key_binding('"input"', '"\x4"')
+ assert_equal [input, "\x45".bytes], @config.parse_key_binding('"input"', '"\x45"')
+ assert_equal [input, "\x45".bytes + '6'.bytes], @config.parse_key_binding('"input"', '"\x456"')
end
def test_include
@@ -384,6 +389,20 @@ class Reline::Config::Test < Reline::TestCase
assert_equal expected, registered_key_bindings(expected.keys)
end
+ def test_unquoted_additional_key_bindings
+ @config.read_lines(<<~'LINES'.lines)
+ Meta-a: "Ma"
+ Control-b: "Cb"
+ Meta-Control-c: "MCc"
+ Control-Meta-d: "CMd"
+ M-C-e: "MCe"
+ C-M-f: "CMf"
+ LINES
+
+ expected = { "\ea".bytes => 'Ma'.bytes, "\C-b".bytes => 'Cb'.bytes, "\e\C-c".bytes => 'MCc'.bytes, "\e\C-d".bytes => 'CMd'.bytes, "\e\C-e".bytes => 'MCe'.bytes, "\e\C-f".bytes => 'CMf'.bytes }
+ assert_equal expected, registered_key_bindings(expected.keys)
+ end
+
def test_additional_key_bindings_with_nesting_and_comment_out
@config.read_lines(<<~'LINES'.lines)
#"ab": "AB"