Skip to content

Commit 353ec23

Browse files
tompngst0012
andauthored
Improve key binding match/matching check (#709)
* Improve key binding match/matching check * Rename key_actors to default_key_bindings * Make key_stroke.expand always return a value * Update add_default_key_binding to use a add_default_key_binding_by_keymap internally Co-authored-by: Stan Lo <stan001212@gmail.com> --------- Co-authored-by: Stan Lo <stan001212@gmail.com>
1 parent dc1518e commit 353ec23

17 files changed

+201
-277
lines changed

lib/reline.rb

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,10 @@ module Reline
1919
class ConfigEncodingConversionError < StandardError; end
2020

2121
Key = Struct.new(:char, :combined_char, :with_meta) do
22-
def match?(other)
23-
case other
24-
when Reline::Key
25-
(other.char.nil? or char.nil? or char == other.char) and
26-
(other.combined_char.nil? or combined_char.nil? or combined_char == other.combined_char) and
27-
(other.with_meta.nil? or with_meta.nil? or with_meta == other.with_meta)
28-
when Integer, Symbol
29-
(combined_char and combined_char == other) or
30-
(combined_char.nil? and char and char == other)
31-
else
32-
false
33-
end
22+
# For dialog_proc `key.match?(dialog.name)`
23+
def match?(sym)
24+
combined_char.is_a?(Symbol) && combined_char == sym
3425
end
35-
alias_method :==, :match?
3626
end
3727
CursorPos = Struct.new(:x, :y)
3828
DialogRenderInfo = Struct.new(
@@ -400,9 +390,8 @@ def readline(prompt = '', add_hist = false)
400390
end
401391
case result
402392
when :matched
403-
expanded = key_stroke.expand(buffer).map{ |expanded_c|
404-
Reline::Key.new(expanded_c, expanded_c, false)
405-
}
393+
expanded, rest_bytes = key_stroke.expand(buffer)
394+
rest_bytes.reverse_each { |c| io_gate.ungetc(c) }
406395
block.(expanded)
407396
break
408397
when :matching
@@ -416,9 +405,8 @@ def readline(prompt = '', add_hist = false)
416405
if buffer.size == 1 and c == "\e".ord
417406
read_escaped_key(keyseq_timeout, c, block)
418407
else
419-
expanded = buffer.map{ |expanded_c|
420-
Reline::Key.new(expanded_c, expanded_c, false)
421-
}
408+
expanded, rest_bytes = key_stroke.expand(buffer)
409+
rest_bytes.reverse_each { |c| io_gate.ungetc(c) }
422410
block.(expanded)
423411
end
424412
break
@@ -442,9 +430,8 @@ def readline(prompt = '', add_hist = false)
442430
return :next
443431
when :matched
444432
buffer << succ_c
445-
expanded = key_stroke.expand(buffer).map{ |expanded_c|
446-
Reline::Key.new(expanded_c, expanded_c, false)
447-
}
433+
expanded, rest_bytes = key_stroke.expand(buffer)
434+
rest_bytes.reverse_each { |c| io_gate.ungetc(c) }
448435
block.(expanded)
449436
return :break
450437
end

lib/reline/config.rb

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,20 @@ class InvalidInputrc < RuntimeError
2929
attr_accessor :autocompletion
3030

3131
def initialize
32-
@additional_key_bindings = {} # from inputrc
33-
@additional_key_bindings[:emacs] = {}
34-
@additional_key_bindings[:vi_insert] = {}
35-
@additional_key_bindings[:vi_command] = {}
36-
@oneshot_key_bindings = {}
32+
@additional_key_bindings = { # from inputrc
33+
emacs: Reline::KeyActor::Base.new,
34+
vi_insert: Reline::KeyActor::Base.new,
35+
vi_command: Reline::KeyActor::Base.new
36+
}
37+
@oneshot_key_bindings = Reline::KeyActor::Base.new
3738
@editing_mode_label = :emacs
3839
@keymap_label = :emacs
3940
@keymap_prefix = []
40-
@key_actors = {}
41-
@key_actors[:emacs] = Reline::KeyActor::Emacs.new
42-
@key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new
43-
@key_actors[:vi_command] = Reline::KeyActor::ViCommand.new
41+
@default_key_bindings = {
42+
emacs: Reline::KeyActor::Base.new(Reline::KeyActor::EMACS_MAPPING),
43+
vi_insert: Reline::KeyActor::Base.new(Reline::KeyActor::VI_INSERT_MAPPING),
44+
vi_command: Reline::KeyActor::Base.new(Reline::KeyActor::VI_COMMAND_MAPPING)
45+
}
4446
@vi_cmd_mode_string = '(cmd)'
4547
@vi_ins_mode_string = '(ins)'
4648
@emacs_mode_string = '@'
@@ -62,7 +64,7 @@ def reset
6264
end
6365

6466
def editing_mode
65-
@key_actors[@editing_mode_label]
67+
@default_key_bindings[@editing_mode_label]
6668
end
6769

6870
def editing_mode=(val)
@@ -74,7 +76,7 @@ def editing_mode_is?(*val)
7476
end
7577

7678
def keymap
77-
@key_actors[@keymap_label]
79+
@default_key_bindings[@keymap_label]
7880
end
7981

8082
def loaded?
@@ -133,26 +135,26 @@ def read(file = nil)
133135

134136
def key_bindings
135137
# The key bindings for each editing mode will be overwritten by the user-defined ones.
136-
kb = @key_actors[@editing_mode_label].default_key_bindings.dup
137-
kb.merge!(@additional_key_bindings[@editing_mode_label])
138-
kb.merge!(@oneshot_key_bindings)
139-
kb
138+
Reline::KeyActor::Composite.new([@oneshot_key_bindings, @additional_key_bindings[@editing_mode_label], @default_key_bindings[@editing_mode_label]])
140139
end
141140

142141
def add_oneshot_key_binding(keystroke, target)
143-
@oneshot_key_bindings[keystroke] = target
142+
# IRB sets invalid keystroke [Reline::Key]. We should ignore it.
143+
return unless keystroke.all? { |c| c.is_a?(Integer) }
144+
145+
@oneshot_key_bindings.add(keystroke, target)
144146
end
145147

146148
def reset_oneshot_key_bindings
147149
@oneshot_key_bindings.clear
148150
end
149151

150152
def add_default_key_binding_by_keymap(keymap, keystroke, target)
151-
@key_actors[keymap].default_key_bindings[keystroke] = target
153+
@default_key_bindings[keymap].add(keystroke, target)
152154
end
153155

154156
def add_default_key_binding(keystroke, target)
155-
@key_actors[@keymap_label].default_key_bindings[keystroke] = target
157+
add_default_key_binding_by_keymap(@keymap_label, keystroke, target)
156158
end
157159

158160
def read_lines(lines, file = nil)
@@ -192,7 +194,7 @@ def read_lines(lines, file = nil)
192194
func_name = func_name.split.first
193195
keystroke, func = bind_key(key, func_name)
194196
next unless keystroke
195-
@additional_key_bindings[@keymap_label][@keymap_prefix + keystroke] = func
197+
@additional_key_bindings[@keymap_label].add(@keymap_prefix + keystroke, func)
196198
end
197199
end
198200
unless if_stack.empty?

lib/reline/key_actor.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module Reline::KeyActor
22
end
33

44
require 'reline/key_actor/base'
5+
require 'reline/key_actor/composite'
56
require 'reline/key_actor/emacs'
67
require 'reline/key_actor/vi_command'
78
require 'reline/key_actor/vi_insert'

lib/reline/key_actor/base.rb

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,31 @@
11
class Reline::KeyActor::Base
2-
MAPPING = Array.new(256)
2+
def initialize(mapping = [])
3+
@mapping = mapping
4+
@matching_bytes = {}
5+
@key_bindings = {}
6+
end
37

48
def get_method(key)
5-
self.class::MAPPING[key]
9+
@mapping[key]
10+
end
11+
12+
def add(key, func)
13+
(1...key.size).each do |size|
14+
@matching_bytes[key.take(size)] = true
15+
end
16+
@key_bindings[key] = func
17+
end
18+
19+
def matching?(key)
20+
@matching_bytes[key]
621
end
722

8-
def initialize
9-
@default_key_bindings = {}
23+
def get(key)
24+
@key_bindings[key]
1025
end
1126

12-
def default_key_bindings
13-
@default_key_bindings
27+
def clear
28+
@matching_bytes.clear
29+
@key_bindings.clear
1430
end
1531
end

lib/reline/key_actor/composite.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class Reline::KeyActor::Composite
2+
def initialize(key_actors)
3+
@key_actors = key_actors
4+
end
5+
6+
def matching?(key)
7+
@key_actors.any? { |key_actor| key_actor.matching?(key) }
8+
end
9+
10+
def get(key)
11+
@key_actors.each do |key_actor|
12+
func = key_actor.get(key)
13+
return func if func
14+
end
15+
nil
16+
end
17+
end

lib/reline/key_actor/emacs.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
class Reline::KeyActor::Emacs < Reline::KeyActor::Base
2-
MAPPING = [
1+
module Reline::KeyActor
2+
EMACS_MAPPING = [
33
# 0 ^@
44
:em_set_mark,
55
# 1 ^A

lib/reline/key_actor/vi_command.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
2-
MAPPING = [
1+
module Reline::KeyActor
2+
VI_COMMAND_MAPPING = [
33
# 0 ^@
44
:ed_unassigned,
55
# 1 ^A

lib/reline/key_actor/vi_insert.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
class Reline::KeyActor::ViInsert < Reline::KeyActor::Base
2-
MAPPING = [
1+
module Reline::KeyActor
2+
VI_INSERT_MAPPING = [
33
# 0 ^@
44
:ed_unassigned,
55
# 1 ^A

0 commit comments

Comments
 (0)