reline 0.2.5 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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