reline 0.2.5 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1da0f8ec80f16c720589413fbc14c02ac199dcfc0b0344270a61edbad4aec966
4
- data.tar.gz: d214e02ad3500323e98d97d57677b7bbc94ea4c09df0b529d5b21ea66374a6a8
3
+ metadata.gz: 1009c9156a8c15c1f3c64a3cb89a0d6a1acef037185bfb76c24ed9df7e49cb2c
4
+ data.tar.gz: 983ad1d570617a47d732402485a79eba8bd1acc6f3555f117ca6822cee52030d
5
5
  SHA512:
6
- metadata.gz: 0d218a92032ad8b02218e44bda7fe7d7404845f6cc9cceb9aade01d3c49bc33a0396b02857de417ecee59b5d65c0cb787eecec8dea60180ae3b5457df6c89ebd
7
- data.tar.gz: 12c4b8d00608e1f9f16a1c5e530ae86c7530f39ddb1c0f6100b1d4ae87a6dbd53def15d9f387941062aafdb57360fd499dc5850a5bd60d07776daf60e057acfe
6
+ metadata.gz: 424c026623c896ccd59fed73b5f61e7bb067dfdc8fe34a13bdb317895fb512eb666b25c76439e940915307e658e85dbbfcb8339ecfae5c5b72fa9239d1599778
7
+ data.tar.gz: 93077ced9c8009d5c2f7f59eb013f8134394347c57660e7958244a2adfe60b2a09463ac2062d9ce31e5d75974b19665f7e800f2078bf9b461dfbada76c3e572e
data/README.md CHANGED
@@ -8,6 +8,52 @@ This is a screen capture of *IRB improved by Reline*.
8
8
 
9
9
  Reline is compatible with the API of Ruby's stdlib 'readline', GNU Readline and Editline by pure Ruby implementation.
10
10
 
11
+ ## Usage
12
+
13
+ ### Single line editing mode
14
+
15
+ It's compatible with the readline standard library.
16
+
17
+ See [the document of readline stdlib](https://ruby-doc.org/stdlib/libdoc/readline/rdoc/Readline.html) or [bin/example](https://github.com/ruby/reline/blob/master/bin/example).
18
+
19
+ ### Multi-line editing mode
20
+
21
+ ```ruby
22
+ require "reline"
23
+
24
+ prompt = 'prompt> '
25
+ use_history = true
26
+
27
+ begin
28
+ while true
29
+ text = Reline.readmultiline(prompt, use_history) do |multiline_input|
30
+ # Accept the input until `end` is entered
31
+ multiline_input.split.last == "end"
32
+ end
33
+
34
+ puts 'You entered:'
35
+ puts text
36
+ end
37
+ # If you want to exit, type Ctrl-C
38
+ rescue Interrupt
39
+ puts '^C'
40
+ exit 0
41
+ end
42
+ ```
43
+
44
+ ```bash
45
+ $ ruby example.rb
46
+ prompt> aaa
47
+ prompt> bbb
48
+ prompt> end
49
+ You entered:
50
+ aaa
51
+ bbb
52
+ end
53
+ ```
54
+
55
+ See also: [test/reline/yamatanooroti/multiline_repl](https://github.com/ruby/reline/blob/master/test/reline/yamatanooroti/multiline_repl)
56
+
11
57
  ## License
12
58
 
13
59
  The gem is available as open source under the terms of the [Ruby License](https://www.ruby-lang.org/en/about/license.txt).
data/lib/reline/ansi.rb CHANGED
@@ -1,7 +1,26 @@
1
1
  require 'io/console'
2
+ require 'io/wait'
2
3
  require 'timeout'
4
+ require_relative 'terminfo'
3
5
 
4
6
  class Reline::ANSI
7
+ CAPNAME_KEY_BINDINGS = {
8
+ 'khome' => :ed_move_to_beg,
9
+ 'kend' => :ed_move_to_end,
10
+ 'kcuu1' => :ed_prev_history,
11
+ 'kcud1' => :ed_next_history,
12
+ 'kcuf1' => :ed_next_char,
13
+ 'kcub1' => :ed_prev_char,
14
+ 'cuu' => :ed_prev_history,
15
+ 'cud' => :ed_next_history,
16
+ 'cuf' => :ed_next_char,
17
+ 'cub' => :ed_prev_char,
18
+ }
19
+
20
+ if Reline::Terminfo.enabled?
21
+ Reline::Terminfo.setupterm(0, 2)
22
+ end
23
+
5
24
  def self.encoding
6
25
  Encoding.default_external
7
26
  end
@@ -10,52 +29,108 @@ class Reline::ANSI
10
29
  false
11
30
  end
12
31
 
13
- RAW_KEYSTROKE_CONFIG = {
14
- # Console (80x25)
15
- [27, 91, 49, 126] => :ed_move_to_beg, # Home
16
- [27, 91, 52, 126] => :ed_move_to_end, # End
17
- [27, 91, 51, 126] => :key_delete, # Del
18
- [27, 91, 65] => :ed_prev_history, # ↑
19
- [27, 91, 66] => :ed_next_history, # ↓
20
- [27, 91, 67] => :ed_next_char, # →
21
- [27, 91, 68] => :ed_prev_char, #
22
-
23
- # KDE
24
- [27, 91, 72] => :ed_move_to_beg, # Home
25
- [27, 91, 70] => :ed_move_to_end, # End
26
- # Del is 0x08
27
- [27, 71, 65] => :ed_prev_history, # ↑
28
- [27, 71, 66] => :ed_next_history, # ↓
29
- [27, 71, 67] => :ed_next_char, # →
30
- [27, 71, 68] => :ed_prev_char, # ←
31
-
32
- # urxvt / exoterm
33
- [27, 91, 55, 126] => :ed_move_to_beg, # Home
34
- [27, 91, 56, 126] => :ed_move_to_end, # End
35
-
36
- # GNOME
37
- [27, 79, 72] => :ed_move_to_beg, # Home
38
- [27, 79, 70] => :ed_move_to_end, # End
39
- # Del is 0x08
40
- # Arrow keys are the same of KDE
41
-
42
- # iTerm2
43
- [27, 27, 91, 67] => :em_next_word, # Option+→
44
- [27, 27, 91, 68] => :ed_prev_word, # Option+←
45
- [195, 166] => :em_next_word, # Option+f
46
- [195, 162] => :ed_prev_word, # Option+b
47
-
48
- # others
49
- [27, 32] => :em_set_mark, # M-<space>
50
- [24, 24] => :em_exchange_mark, # C-x C-x TODO also add Windows
51
- [27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→
52
- [27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+←
53
-
54
- [27, 79, 65] => :ed_prev_history, # ↑
55
- [27, 79, 66] => :ed_next_history, # ↓
56
- [27, 79, 67] => :ed_next_char, # →
57
- [27, 79, 68] => :ed_prev_char, # ←
58
- }
32
+ def self.set_default_key_bindings(config)
33
+ if Reline::Terminfo.enabled?
34
+ set_default_key_bindings_terminfo(config)
35
+ else
36
+ set_default_key_bindings_comprehensive_list(config)
37
+ end
38
+ {
39
+ # extended entries of terminfo
40
+ [27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→, extended entry
41
+ [27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+←, extended entry
42
+ [27, 91, 49, 59, 51, 67] => :em_next_word, # Meta+→, extended entry
43
+ [27, 91, 49, 59, 51, 68] => :ed_prev_word, # Meta+←, extended entry
44
+ }.each_pair do |key, func|
45
+ config.add_default_key_binding_by_keymap(:emacs, key, func)
46
+ config.add_default_key_binding_by_keymap(:vi_insert, key, func)
47
+ config.add_default_key_binding_by_keymap(:vi_command, key, func)
48
+ end
49
+ {
50
+ [27, 91, 90] => :completion_journey_up, # S-Tab
51
+ }.each_pair do |key, func|
52
+ config.add_default_key_binding_by_keymap(:emacs, key, func)
53
+ config.add_default_key_binding_by_keymap(:vi_insert, key, func)
54
+ end
55
+ {
56
+ # default bindings
57
+ [27, 32] => :em_set_mark, # M-<space>
58
+ [24, 24] => :em_exchange_mark, # C-x C-x
59
+ }.each_pair do |key, func|
60
+ config.add_default_key_binding_by_keymap(:emacs, key, func)
61
+ end
62
+ end
63
+
64
+ def self.set_default_key_bindings_terminfo(config)
65
+ key_bindings = CAPNAME_KEY_BINDINGS.map do |capname, key_binding|
66
+ begin
67
+ key_code = Reline::Terminfo.tigetstr(capname)
68
+ case capname
69
+ # Escape sequences that omit the move distance and are set to defaults
70
+ # value 1 may be sometimes sent by pressing the arrow-key.
71
+ when 'cuu', 'cud', 'cuf', 'cub'
72
+ [ key_code.sub(/%p1%d/, '').bytes, key_binding ]
73
+ else
74
+ [ key_code.bytes, key_binding ]
75
+ end
76
+ rescue Reline::Terminfo::TerminfoError
77
+ # capname is undefined
78
+ end
79
+ end.compact.to_h
80
+
81
+ key_bindings.each_pair do |key, func|
82
+ config.add_default_key_binding_by_keymap(:emacs, key, func)
83
+ config.add_default_key_binding_by_keymap(:vi_insert, key, func)
84
+ config.add_default_key_binding_by_keymap(:vi_command, key, func)
85
+ end
86
+ end
87
+
88
+ def self.set_default_key_bindings_comprehensive_list(config)
89
+ {
90
+ # Console (80x25)
91
+ [27, 91, 49, 126] => :ed_move_to_beg, # Home
92
+ [27, 91, 52, 126] => :ed_move_to_end, # End
93
+ [27, 91, 51, 126] => :key_delete, # Del
94
+ [27, 91, 65] => :ed_prev_history, # ↑
95
+ [27, 91, 66] => :ed_next_history, # ↓
96
+ [27, 91, 67] => :ed_next_char, # →
97
+ [27, 91, 68] => :ed_prev_char, # ←
98
+
99
+ # KDE
100
+ [27, 91, 72] => :ed_move_to_beg, # Home
101
+ [27, 91, 70] => :ed_move_to_end, # End
102
+ # Del is 0x08
103
+ [27, 71, 65] => :ed_prev_history, # ↑
104
+ [27, 71, 66] => :ed_next_history, # ↓
105
+ [27, 71, 67] => :ed_next_char, # →
106
+ [27, 71, 68] => :ed_prev_char, # ←
107
+
108
+ # urxvt / exoterm
109
+ [27, 91, 55, 126] => :ed_move_to_beg, # Home
110
+ [27, 91, 56, 126] => :ed_move_to_end, # End
111
+
112
+ # GNOME
113
+ [27, 79, 72] => :ed_move_to_beg, # Home
114
+ [27, 79, 70] => :ed_move_to_end, # End
115
+ # Del is 0x08
116
+ # Arrow keys are the same of KDE
117
+
118
+ # iTerm2
119
+ [27, 27, 91, 67] => :em_next_word, # Option+→, extended entry
120
+ [27, 27, 91, 68] => :ed_prev_word, # Option+←, extended entry
121
+ [195, 166] => :em_next_word, # Option+f
122
+ [195, 162] => :ed_prev_word, # Option+b
123
+
124
+ [27, 79, 65] => :ed_prev_history, # ↑
125
+ [27, 79, 66] => :ed_next_history, # ↓
126
+ [27, 79, 67] => :ed_next_char, # →
127
+ [27, 79, 68] => :ed_prev_char, # ←
128
+ }.each_pair do |key, func|
129
+ config.add_default_key_binding_by_keymap(:emacs, key, func)
130
+ config.add_default_key_binding_by_keymap(:vi_insert, key, func)
131
+ config.add_default_key_binding_by_keymap(:vi_command, key, func)
132
+ end
133
+ end
59
134
 
60
135
  @@input = STDIN
61
136
  def self.input=(val)
@@ -72,13 +147,15 @@ class Reline::ANSI
72
147
  unless @@buf.empty?
73
148
  return @@buf.shift
74
149
  end
75
- until c = @@input.raw(intr: true, &:getbyte)
76
- sleep 0.1
150
+ until c = @@input.raw(intr: true) { @@input.wait_readable(0.1) && @@input.getbyte }
151
+ Reline.core.line_editor.resize
77
152
  end
78
153
  (c == 0x16 && @@input.raw(min: 0, tim: 0, &:getbyte)) || c
79
154
  rescue Errno::EIO
80
155
  # Maybe the I/O has been closed.
81
156
  nil
157
+ rescue Errno::ENOTTY
158
+ nil
82
159
  end
83
160
 
84
161
  @@in_bracketed_paste_mode = false
@@ -129,12 +206,7 @@ class Reline::ANSI
129
206
  unless @@buf.empty?
130
207
  return false
131
208
  end
132
- rs, = IO.select([@@input], [], [], 0.00001)
133
- if rs and rs[0]
134
- false
135
- else
136
- true
137
- end
209
+ !@@input.wait_readable(0)
138
210
  end
139
211
 
140
212
  def self.ungetc(c)
@@ -143,8 +215,7 @@ class Reline::ANSI
143
215
 
144
216
  def self.retrieve_keybuffer
145
217
  begin
146
- result = select([@@input], [], [], 0.001)
147
- return if result.nil?
218
+ return unless @@input.wait_readable(0.001)
148
219
  str = @@input.read_nonblock(1024)
149
220
  str.bytes.each do |c|
150
221
  @@buf.push(c)
@@ -211,7 +282,7 @@ class Reline::ANSI
211
282
 
212
283
  def self.move_cursor_up(x)
213
284
  if x > 0
214
- @@output.write "\e[#{x}A" if x > 0
285
+ @@output.write "\e[#{x}A"
215
286
  elsif x < 0
216
287
  move_cursor_down(-x)
217
288
  end
@@ -219,12 +290,36 @@ class Reline::ANSI
219
290
 
220
291
  def self.move_cursor_down(x)
221
292
  if x > 0
222
- @@output.write "\e[#{x}B" if x > 0
293
+ @@output.write "\e[#{x}B"
223
294
  elsif x < 0
224
295
  move_cursor_up(-x)
225
296
  end
226
297
  end
227
298
 
299
+ def self.hide_cursor
300
+ if Reline::Terminfo.enabled?
301
+ begin
302
+ @@output.write Reline::Terminfo.tigetstr('civis')
303
+ rescue Reline::Terminfo::TerminfoError
304
+ # civis is undefined
305
+ end
306
+ else
307
+ # ignored
308
+ end
309
+ end
310
+
311
+ def self.show_cursor
312
+ if Reline::Terminfo.enabled?
313
+ begin
314
+ @@output.write Reline::Terminfo.tigetstr('cnorm')
315
+ rescue Reline::Terminfo::TerminfoError
316
+ # cnorm is undefined
317
+ end
318
+ else
319
+ # ignored
320
+ end
321
+ end
322
+
228
323
  def self.erase_after_cursor
229
324
  @@output.write "\e[K"
230
325
  end
@@ -246,14 +341,10 @@ class Reline::ANSI
246
341
 
247
342
  def self.prep
248
343
  retrieve_keybuffer
249
- int_handle = Signal.trap('INT', 'IGNORE')
250
- Signal.trap('INT', int_handle)
251
344
  nil
252
345
  end
253
346
 
254
347
  def self.deprep(otio)
255
- int_handle = Signal.trap('INT', 'IGNORE')
256
- Signal.trap('INT', int_handle)
257
348
  Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
258
349
  end
259
350
  end
data/lib/reline/config.rb CHANGED
@@ -47,7 +47,10 @@ class Reline::Config
47
47
 
48
48
  def initialize
49
49
  @additional_key_bindings = {} # from inputrc
50
- @default_key_bindings = {} # environment-dependent
50
+ @additional_key_bindings[:emacs] = {}
51
+ @additional_key_bindings[:vi_insert] = {}
52
+ @additional_key_bindings[:vi_command] = {}
53
+ @oneshot_key_bindings = {}
51
54
  @skip_section = nil
52
55
  @if_stack = nil
53
56
  @editing_mode_label = :emacs
@@ -63,14 +66,19 @@ class Reline::Config
63
66
  @history_size = -1 # unlimited
64
67
  @keyseq_timeout = 500
65
68
  @test_mode = false
69
+ @autocompletion = false
70
+ @convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding)
66
71
  end
67
72
 
68
73
  def reset
69
74
  if editing_mode_is?(:vi_command)
70
75
  @editing_mode_label = :vi_insert
71
76
  end
72
- @additional_key_bindings = {}
73
- @default_key_bindings = {}
77
+ @additional_key_bindings.keys.each do |key|
78
+ @additional_key_bindings[key].clear
79
+ end
80
+ @oneshot_key_bindings.clear
81
+ reset_default_key_bindings
74
82
  end
75
83
 
76
84
  def editing_mode
@@ -85,6 +93,14 @@ class Reline::Config
85
93
  (val.respond_to?(:any?) ? val : [val]).any?(@editing_mode_label)
86
94
  end
87
95
 
96
+ def autocompletion=(val)
97
+ @autocompletion = val
98
+ end
99
+
100
+ def autocompletion
101
+ @autocompletion
102
+ end
103
+
88
104
  def keymap
89
105
  @key_actors[@keymap_label]
90
106
  end
@@ -115,8 +131,12 @@ class Reline::Config
115
131
  return home_rc_path
116
132
  end
117
133
 
134
+ private def default_inputrc_path
135
+ @default_inputrc_path ||= inputrc_path
136
+ end
137
+
118
138
  def read(file = nil)
119
- file ||= inputrc_path
139
+ file ||= default_inputrc_path
120
140
  begin
121
141
  if file.respond_to?(:readlines)
122
142
  lines = file.readlines
@@ -135,19 +155,46 @@ class Reline::Config
135
155
  end
136
156
 
137
157
  def key_bindings
138
- # override @default_key_bindings with @additional_key_bindings
139
- @default_key_bindings.merge(@additional_key_bindings)
158
+ # The key bindings for each editing mode will be overwritten by the user-defined ones.
159
+ kb = @key_actors[@editing_mode_label].default_key_bindings.dup
160
+ kb.merge!(@additional_key_bindings[@editing_mode_label])
161
+ kb.merge!(@oneshot_key_bindings)
162
+ kb
163
+ end
164
+
165
+ def add_oneshot_key_binding(keystroke, target)
166
+ @oneshot_key_bindings[keystroke] = target
167
+ end
168
+
169
+ def reset_oneshot_key_bindings
170
+ @oneshot_key_bindings.clear
171
+ end
172
+
173
+ def add_default_key_binding_by_keymap(keymap, keystroke, target)
174
+ @key_actors[keymap].default_key_bindings[keystroke] = target
140
175
  end
141
176
 
142
177
  def add_default_key_binding(keystroke, target)
143
- @default_key_bindings[keystroke] = target
178
+ @key_actors[@keymap_label].default_key_bindings[keystroke] = target
144
179
  end
145
180
 
146
181
  def reset_default_key_bindings
147
- @default_key_bindings = {}
182
+ @key_actors.values.each do |ka|
183
+ ka.reset_default_key_bindings
184
+ end
148
185
  end
149
186
 
150
187
  def read_lines(lines, file = nil)
188
+ if not lines.empty? and lines.first.encoding != Reline.encoding_system_needs
189
+ begin
190
+ lines = lines.map do |l|
191
+ l.encode(Reline.encoding_system_needs)
192
+ rescue Encoding::UndefinedConversionError
193
+ mes = "The inputrc encoded in #{lines.first.encoding.name} can't be converted to the locale #{Reline.encoding_system_needs.name}."
194
+ raise Reline::ConfigEncodingConversionError.new(mes)
195
+ end
196
+ end
197
+ end
151
198
  conditions = [@skip_section, @if_stack]
152
199
  @skip_section = nil
153
200
  @if_stack = []
@@ -174,7 +221,7 @@ class Reline::Config
174
221
  key, func_name = $1, $2
175
222
  keystroke, func = bind_key(key, func_name)
176
223
  next unless keystroke
177
- @additional_key_bindings[keystroke] = func
224
+ @additional_key_bindings[@keymap_label][keystroke] = func
178
225
  end
179
226
  end
180
227
  unless @if_stack.empty?
@@ -282,11 +329,8 @@ class Reline::Config
282
329
  end
283
330
 
284
331
  def retrieve_string(str)
285
- if str =~ /\A"(.*)"\z/
286
- parse_keyseq($1).map(&:chr).join
287
- else
288
- parse_keyseq(str).map(&:chr).join
289
- end
332
+ str = $1 if str =~ /\A"(.*)"\z/
333
+ parse_keyseq(str).map { |c| c.chr(Reline.encoding_system_needs) }.join
290
334
  end
291
335
 
292
336
  def bind_key(key, func_name)
@@ -344,4 +388,8 @@ class Reline::Config
344
388
  end
345
389
  ret
346
390
  end
391
+
392
+ private def seven_bit_encoding?(encoding)
393
+ encoding == Encoding::US_ASCII
394
+ end
347
395
  end
@@ -1,21 +1,31 @@
1
1
  require 'timeout'
2
+ require 'io/wait'
2
3
 
3
4
  class Reline::GeneralIO
4
- def self.reset
5
+ def self.reset(encoding: nil)
5
6
  @@pasting = false
7
+ @@encoding = encoding
6
8
  end
7
9
 
8
10
  def self.encoding
9
- RUBY_PLATFORM =~ /mswin|mingw/ ? Encoding::UTF_8 : Encoding::default_external
11
+ if defined?(@@encoding)
12
+ @@encoding
13
+ elsif RUBY_PLATFORM =~ /mswin|mingw/
14
+ Encoding::UTF_8
15
+ else
16
+ Encoding::default_external
17
+ end
10
18
  end
11
19
 
12
20
  def self.win?
13
21
  false
14
22
  end
15
23
 
16
- RAW_KEYSTROKE_CONFIG = {}
24
+ def self.set_default_key_bindings(_)
25
+ end
17
26
 
18
27
  @@buf = []
28
+ @@input = STDIN
19
29
 
20
30
  def self.input=(val)
21
31
  @@input = val
@@ -27,7 +37,7 @@ class Reline::GeneralIO
27
37
  end
28
38
  c = nil
29
39
  loop do
30
- result = select([@@input], [], [], 0.1)
40
+ result = @@input.wait_readable(0.1)
31
41
  next if result.nil?
32
42
  c = @@input.read(1)
33
43
  break
@@ -4,4 +4,16 @@ class Reline::KeyActor::Base
4
4
  def get_method(key)
5
5
  self.class::MAPPING[key]
6
6
  end
7
+
8
+ def initialize
9
+ @default_key_bindings = {}
10
+ end
11
+
12
+ def default_key_bindings
13
+ @default_key_bindings
14
+ end
15
+
16
+ def reset_default_key_bindings
17
+ @default_key_bindings.clear
18
+ end
7
19
  end
@@ -43,7 +43,7 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
43
43
  # 20 ^T
44
44
  :ed_transpose_chars,
45
45
  # 21 ^U
46
- :em_kill_line,
46
+ :unix_line_discard,
47
47
  # 22 ^V
48
48
  :ed_quoted_insert,
49
49
  # 23 ^W
@@ -1,38 +1,88 @@
1
1
  class Reline::KeyStroke
2
- using Module.new {
3
- refine Array do
4
- def start_with?(other)
5
- other.size <= size && other == self.take(other.size)
2
+ def initialize(config)
3
+ @config = config
4
+ end
5
+
6
+ def compress_meta_key(ary)
7
+ return ary unless @config.convert_meta
8
+ ary.inject([]) { |result, key|
9
+ if result.size > 0 and result.last == "\e".ord
10
+ result[result.size - 1] = Reline::Key.new(key, key | 0b10000000, true)
11
+ else
12
+ result << key
6
13
  end
14
+ result
15
+ }
16
+ end
7
17
 
8
- def bytes
9
- self
18
+ def start_with?(me, other)
19
+ compressed_me = compress_meta_key(me)
20
+ compressed_other = compress_meta_key(other)
21
+ i = 0
22
+ loop do
23
+ my_c = compressed_me[i]
24
+ other_c = compressed_other[i]
25
+ other_is_last = (i + 1) == compressed_other.size
26
+ me_is_last = (i + 1) == compressed_me.size
27
+ if my_c != other_c
28
+ if other_c == "\e".ord and other_is_last and my_c.is_a?(Reline::Key) and my_c.with_meta
29
+ return true
30
+ else
31
+ return false
32
+ end
33
+ elsif other_is_last
34
+ return true
35
+ elsif me_is_last
36
+ return false
10
37
  end
38
+ i += 1
11
39
  end
12
- }
40
+ end
13
41
 
14
- def initialize(config)
15
- @config = config
42
+ def equal?(me, other)
43
+ case me
44
+ when Array
45
+ compressed_me = compress_meta_key(me)
46
+ compressed_other = compress_meta_key(other)
47
+ compressed_me.size == compressed_other.size and [compressed_me, compressed_other].transpose.all?{ |i| equal?(i[0], i[1]) }
48
+ when Integer
49
+ if other.is_a?(Reline::Key)
50
+ if other.combined_char == "\e".ord
51
+ false
52
+ else
53
+ other.combined_char == me
54
+ end
55
+ else
56
+ me == other
57
+ end
58
+ when Reline::Key
59
+ if other.is_a?(Integer)
60
+ me.combined_char == other
61
+ else
62
+ me == other
63
+ end
64
+ end
16
65
  end
17
66
 
18
67
  def match_status(input)
19
68
  key_mapping.keys.select { |lhs|
20
- lhs.start_with? input
69
+ start_with?(lhs, input)
21
70
  }.tap { |it|
22
- return :matched if it.size == 1 && (it.max_by(&:size)&.size&.== input.size)
23
- return :matching if it.size == 1 && (it.max_by(&:size)&.size&.!= input.size)
71
+ return :matched if it.size == 1 && equal?(it[0], input)
72
+ return :matching if it.size == 1 && !equal?(it[0], input)
24
73
  return :matched if it.max_by(&:size)&.size&.< input.size
25
74
  return :matching if it.size > 1
26
75
  }
27
76
  key_mapping.keys.select { |lhs|
28
- input.start_with? lhs
77
+ start_with?(input, lhs)
29
78
  }.tap { |it|
30
79
  return it.size > 0 ? :matched : :unmatched
31
80
  }
32
81
  end
33
82
 
34
83
  def expand(input)
35
- lhs = key_mapping.keys.select { |item| input.start_with? item }.sort_by(&:size).reverse.first
84
+ input = compress_meta_key(input)
85
+ lhs = key_mapping.keys.select { |item| start_with?(input, item) }.sort_by(&:size).last
36
86
  return input unless lhs
37
87
  rhs = key_mapping[lhs]
38
88