reline 0.5.10 → 0.6.0

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: 402ac8415b342c58b741ab1ae942818c44f092d43665d9546fde986572ce2bcf
4
- data.tar.gz: 0a54115c2e2b02720e67a7870edc77430493b3d2fa89c033046d817b5b984b13
3
+ metadata.gz: 8fb968cf5a8c86917a35ba517a627df5a5dc51cb9b75cef5fdd3183042cd3224
4
+ data.tar.gz: b02916eae200fc26a65aafde67e450e97d26450d7a1b694db37d1dce8c43c7a4
5
5
  SHA512:
6
- metadata.gz: 0525a60f8fe7512bf23575935d15c7173c825ce6b15a062505d73e9a34836d8b751182280c4b67288af7b949b0417a57a4f384281123ef7b6e90c3072b65f20c
7
- data.tar.gz: 299034a835b97c80a119614394a650c60104d571ec95ffbff8356531dc1de96cd78b8aa3fa8830520a40d43e4b7feb1958285af29633877a772be7ed995d7d33
6
+ metadata.gz: 7c35aff3260b914d6137b87ba0d7031ce80ddf10896191dc79302b6e79749d55590b997b7705eac4ec03046fa95f19d2ff3be4d6f566491c9f40e99813e221ff
7
+ data.tar.gz: cd15de1099bcb91eb69881c20f969e6ece3e4d8548565d97d743e01d082d63aa4554381f86fbb7d29068ca84e8699ed1a91cc30a77e2b7aadb58d1d2338998c8
data/lib/reline/config.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  class Reline::Config
2
2
  attr_reader :test_mode
3
3
 
4
- 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}|./
4
+ 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}|./
5
5
 
6
6
  class InvalidInputrc < RuntimeError
7
7
  attr_accessor :file, :lineno
@@ -194,13 +194,14 @@ class Reline::Config
194
194
  # value ignores everything after a space, raw_value does not.
195
195
  var, value, raw_value = $1.downcase, $2.partition(' ').first, $2
196
196
  bind_variable(var, value, raw_value)
197
- next
198
- when /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
199
- key, func_name = $1, $2
200
- func_name = func_name.split.first
201
- keystroke, func = bind_key(key, func_name)
202
- next unless keystroke
203
- @additional_key_bindings[@keymap_label].add(@keymap_prefix + keystroke, func)
197
+ when /^\s*(?:M|Meta)-([a-zA-Z_])\s*:\s*(.*)\s*$/o
198
+ bind_key("\"\\M-#$1\"", $2)
199
+ when /^\s*(?:C|Control)-([a-zA-Z_])\s*:\s*(.*)\s*$/o
200
+ bind_key("\"\\C-#$1\"", $2)
201
+ when /^\s*(?:(?:C|Control)-(?:M|Meta)|(?:M|Meta)-(?:C|Control))-([a-zA-Z_])\s*:\s*(.*)\s*$/o
202
+ bind_key("\"\\M-\\C-#$1\"", $2)
203
+ when /^\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
204
+ bind_key($1, $2)
204
205
  end
205
206
  end
206
207
  unless if_stack.empty?
@@ -310,7 +311,12 @@ class Reline::Config
310
311
  parse_keyseq(str).map { |c| c.chr(Reline.encoding_system_needs) }.join
311
312
  end
312
313
 
313
- def bind_key(key, func_name)
314
+ def bind_key(key, value)
315
+ keystroke, func = parse_key_binding(key, value)
316
+ @additional_key_bindings[@keymap_label].add(@keymap_prefix + keystroke, func) if keystroke
317
+ end
318
+
319
+ def parse_key_binding(key, func_name)
314
320
  if key =~ /\A"(.*)"\z/
315
321
  keyseq = parse_keyseq($1)
316
322
  else
@@ -319,27 +325,19 @@ class Reline::Config
319
325
  if func_name =~ /"(.*)"/
320
326
  func = parse_keyseq($1)
321
327
  else
322
- func = func_name.tr(?-, ?_).to_sym # It must be macro.
328
+ func = func_name.split.first.tr(?-, ?_).to_sym # It must be macro.
323
329
  end
324
330
  [keyseq, func]
325
331
  end
326
332
 
327
333
  def key_notation_to_code(notation)
328
334
  case notation
335
+ when /(?:\\(?:C|Control)-\\(?:M|Meta)|\\(?:M|Meta)-\\(?:C|Control))-([A-Za-z_])/
336
+ [?\e.ord, $1.ord % 32]
329
337
  when /\\(?:C|Control)-([A-Za-z_])/
330
- (1 + $1.downcase.ord - ?a.ord)
338
+ ($1.upcase.ord % 32)
331
339
  when /\\(?:M|Meta)-([0-9A-Za-z_])/
332
- modified_key = $1
333
- case $1
334
- when /[0-9]/
335
- ?\M-0.bytes.first + (modified_key.ord - ?0.ord)
336
- when /[A-Z]/
337
- ?\M-A.bytes.first + (modified_key.ord - ?A.ord)
338
- when /[a-z]/
339
- ?\M-a.bytes.first + (modified_key.ord - ?a.ord)
340
- end
341
- when /\\(?:C|Control)-(?:M|Meta)-[A-Za-z_]/, /\\(?:M|Meta)-(?:C|Control)-[A-Za-z_]/
342
- # 129 M-^A
340
+ [?\e.ord, $1.ord]
343
341
  when /\\(\d{1,3})/ then $1.to_i(8) # octal
344
342
  when /\\x(\h{1,2})/ then $1.to_i(16) # hexadecimal
345
343
  when "\\e" then ?\e.ord
@@ -359,11 +357,9 @@ class Reline::Config
359
357
  end
360
358
 
361
359
  def parse_keyseq(str)
362
- ret = []
363
- str.scan(KEYSEQ_PATTERN) do
364
- ret << key_notation_to_code($&)
360
+ str.scan(KEYSEQ_PATTERN).flat_map do |notation|
361
+ key_notation_to_code(notation)
365
362
  end
366
- ret
367
363
  end
368
364
 
369
365
  def reload
@@ -19,7 +19,7 @@ class Reline::History < Array
19
19
 
20
20
  def []=(index, val)
21
21
  index = check_index(index)
22
- super(index, String.new(val, encoding: Reline.encoding_system_needs))
22
+ super(index, Reline::Unicode.safe_encode(val, Reline.encoding_system_needs))
23
23
  end
24
24
 
25
25
  def concat(*val)
@@ -45,7 +45,7 @@ class Reline::History < Array
45
45
  end
46
46
  end
47
47
  super(*(val.map{ |v|
48
- String.new(v, encoding: Reline.encoding_system_needs)
48
+ Reline::Unicode.safe_encode(v, Reline.encoding_system_needs)
49
49
  }))
50
50
  end
51
51
 
@@ -56,7 +56,7 @@ class Reline::History < Array
56
56
  if @config.history_size.positive?
57
57
  shift if size + 1 > @config.history_size
58
58
  end
59
- super(String.new(val, encoding: Reline.encoding_system_needs))
59
+ super(Reline::Unicode.safe_encode(val, Reline.encoding_system_needs))
60
60
  end
61
61
 
62
62
  private def check_index(index)
@@ -29,29 +29,27 @@ class Reline::ANSI < Reline::IO
29
29
  'H' => [:ed_move_to_beg, {}],
30
30
  }
31
31
 
32
- if Reline::Terminfo.enabled?
33
- Reline::Terminfo.setupterm(0, 2)
34
- end
32
+ attr_writer :input, :output
35
33
 
36
34
  def initialize
37
35
  @input = STDIN
38
36
  @output = STDOUT
39
37
  @buf = []
38
+ @output_buffer = nil
40
39
  @old_winch_handler = nil
41
40
  end
42
41
 
43
42
  def encoding
43
+ @input.external_encoding || Encoding.default_external
44
+ rescue IOError
45
+ # STDIN.external_encoding raises IOError in Ruby <= 3.0 when STDIN is closed
44
46
  Encoding.default_external
45
47
  end
46
48
 
47
- def set_default_key_bindings(config, allow_terminfo: true)
49
+ def set_default_key_bindings(config)
48
50
  set_bracketed_paste_key_bindings(config)
49
51
  set_default_key_bindings_ansi_cursor(config)
50
- if allow_terminfo && Reline::Terminfo.enabled?
51
- set_default_key_bindings_terminfo(config)
52
- else
53
- set_default_key_bindings_comprehensive_list(config)
54
- end
52
+ set_default_key_bindings_comprehensive_list(config)
55
53
  {
56
54
  [27, 91, 90] => :completion_journey_up, # S-Tab
57
55
  }.each_pair do |key, func|
@@ -75,7 +73,10 @@ class Reline::ANSI < Reline::IO
75
73
 
76
74
  def set_default_key_bindings_ansi_cursor(config)
77
75
  ANSI_CURSOR_KEY_BINDINGS.each do |char, (default_func, modifiers)|
78
- bindings = [["\e[#{char}", default_func]] # CSI + char
76
+ bindings = [
77
+ ["\e[#{char}", default_func], # CSI + char
78
+ ["\eO#{char}", default_func] # SS3 + char, application cursor key mode
79
+ ]
79
80
  if modifiers[:ctrl]
80
81
  # CSI + ctrl_key_modifier + char
81
82
  bindings << ["\e[1;5#{char}", modifiers[:ctrl]]
@@ -95,23 +96,6 @@ class Reline::ANSI < Reline::IO
95
96
  end
96
97
  end
97
98
 
98
- def set_default_key_bindings_terminfo(config)
99
- key_bindings = CAPNAME_KEY_BINDINGS.map do |capname, key_binding|
100
- begin
101
- key_code = Reline::Terminfo.tigetstr(capname)
102
- [ key_code.bytes, key_binding ]
103
- rescue Reline::Terminfo::TerminfoError
104
- # capname is undefined
105
- end
106
- end.compact.to_h
107
-
108
- key_bindings.each_pair do |key, func|
109
- config.add_default_key_binding_by_keymap(:emacs, key, func)
110
- config.add_default_key_binding_by_keymap(:vi_insert, key, func)
111
- config.add_default_key_binding_by_keymap(:vi_command, key, func)
112
- end
113
- end
114
-
115
99
  def set_default_key_bindings_comprehensive_list(config)
116
100
  {
117
101
  # xterm
@@ -123,27 +107,9 @@ class Reline::ANSI < Reline::IO
123
107
  [27, 91, 49, 126] => :ed_move_to_beg, # Home
124
108
  [27, 91, 52, 126] => :ed_move_to_end, # End
125
109
 
126
- # KDE
127
- # Del is 0x08
128
- [27, 71, 65] => :ed_prev_history, # ↑
129
- [27, 71, 66] => :ed_next_history, # ↓
130
- [27, 71, 67] => :ed_next_char, # →
131
- [27, 71, 68] => :ed_prev_char, # ←
132
-
133
110
  # urxvt / exoterm
134
111
  [27, 91, 55, 126] => :ed_move_to_beg, # Home
135
112
  [27, 91, 56, 126] => :ed_move_to_end, # End
136
-
137
- # GNOME
138
- [27, 79, 72] => :ed_move_to_beg, # Home
139
- [27, 79, 70] => :ed_move_to_end, # End
140
- # Del is 0x08
141
- # Arrow keys are the same of KDE
142
-
143
- [27, 79, 65] => :ed_prev_history, # ↑
144
- [27, 79, 66] => :ed_next_history, # ↓
145
- [27, 79, 67] => :ed_next_char, # →
146
- [27, 79, 68] => :ed_prev_char, # ←
147
113
  }.each_pair do |key, func|
148
114
  config.add_default_key_binding_by_keymap(:emacs, key, func)
149
115
  config.add_default_key_binding_by_keymap(:vi_insert, key, func)
@@ -151,14 +117,6 @@ class Reline::ANSI < Reline::IO
151
117
  end
152
118
  end
153
119
 
154
- def input=(val)
155
- @input = val
156
- end
157
-
158
- def output=(val)
159
- @output = val
160
- end
161
-
162
120
  def with_raw_input
163
121
  if @input.tty?
164
122
  @input.raw(intr: true) { yield }
@@ -245,52 +203,59 @@ class Reline::ANSI < Reline::IO
245
203
  self
246
204
  end
247
205
 
248
- def cursor_pos
249
- if both_tty?
250
- res = +''
251
- m = nil
252
- @input.raw do |stdin|
253
- @output << "\e[6n"
254
- @output.flush
255
- loop do
256
- c = stdin.getc
257
- next if c.nil?
258
- res << c
259
- m = res.match(/\e\[(?<row>\d+);(?<column>\d+)R/)
260
- break if m
261
- end
262
- (m.pre_match + m.post_match).chars.reverse_each do |ch|
263
- stdin.ungetc ch
206
+ private def cursor_pos_internal(timeout:)
207
+ match = nil
208
+ @input.raw do |stdin|
209
+ @output << "\e[6n"
210
+ @output.flush
211
+ timeout_at = Time.now + timeout
212
+ buf = +''
213
+ while (wait = timeout_at - Time.now) > 0 && stdin.wait_readable(wait)
214
+ buf << stdin.readpartial(1024)
215
+ if (match = buf.match(/\e\[(?<row>\d+);(?<column>\d+)R/))
216
+ buf = match.pre_match + match.post_match
217
+ break
264
218
  end
265
219
  end
266
- column = m[:column].to_i - 1
267
- row = m[:row].to_i - 1
268
- else
269
- begin
270
- buf = @output.pread(@output.pos, 0)
271
- row = buf.count("\n")
272
- column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
273
- rescue Errno::ESPIPE, IOError
274
- # Just returns column 1 for ambiguous width because this I/O is not
275
- # tty and can't seek.
276
- row = 0
277
- column = 1
220
+ buf.chars.reverse_each do |ch|
221
+ stdin.ungetc ch
278
222
  end
279
223
  end
280
- Reline::CursorPos.new(column, row)
224
+ [match[:column].to_i - 1, match[:row].to_i - 1] if match
225
+ end
226
+
227
+ def cursor_pos
228
+ col, row = cursor_pos_internal(timeout: 0.5) if both_tty?
229
+ Reline::CursorPos.new(col || 0, row || 0)
281
230
  end
282
231
 
283
232
  def both_tty?
284
233
  @input.tty? && @output.tty?
285
234
  end
286
235
 
236
+ def write(string)
237
+ if @output_buffer
238
+ @output_buffer << string
239
+ else
240
+ @output.write(string)
241
+ end
242
+ end
243
+
244
+ def buffered_output
245
+ @output_buffer = +''
246
+ yield
247
+ @output.write(@output_buffer)
248
+ ensure
249
+ @output_buffer = nil
250
+ end
251
+
287
252
  def move_cursor_column(x)
288
- @output.write "\e[#{x + 1}G"
253
+ write "\e[#{x + 1}G"
289
254
  end
290
255
 
291
256
  def move_cursor_up(x)
292
257
  if x > 0
293
- @output.write "\e[#{x}A"
258
+ write "\e[#{x}A"
294
259
  elsif x < 0
295
260
  move_cursor_down(-x)
296
261
  end
@@ -298,38 +263,22 @@ class Reline::ANSI < Reline::IO
298
263
 
299
264
  def move_cursor_down(x)
300
265
  if x > 0
301
- @output.write "\e[#{x}B"
266
+ write "\e[#{x}B"
302
267
  elsif x < 0
303
268
  move_cursor_up(-x)
304
269
  end
305
270
  end
306
271
 
307
272
  def hide_cursor
308
- seq = "\e[?25l"
309
- if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
310
- begin
311
- seq = Reline::Terminfo.tigetstr('civis')
312
- rescue Reline::Terminfo::TerminfoError
313
- # civis is undefined
314
- end
315
- end
316
- @output.write seq
273
+ write "\e[?25l"
317
274
  end
318
275
 
319
276
  def show_cursor
320
- seq = "\e[?25h"
321
- if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
322
- begin
323
- seq = Reline::Terminfo.tigetstr('cnorm')
324
- rescue Reline::Terminfo::TerminfoError
325
- # cnorm is undefined
326
- end
327
- end
328
- @output.write seq
277
+ write "\e[?25h"
329
278
  end
330
279
 
331
280
  def erase_after_cursor
332
- @output.write "\e[K"
281
+ write "\e[K"
333
282
  end
334
283
 
335
284
  # This only works when the cursor is at the bottom of the scroll range
@@ -337,20 +286,24 @@ class Reline::ANSI < Reline::IO
337
286
  def scroll_down(x)
338
287
  return if x.zero?
339
288
  # We use `\n` instead of CSI + S because CSI + S would cause https://github.com/ruby/reline/issues/576
340
- @output.write "\n" * x
289
+ write "\n" * x
341
290
  end
342
291
 
343
292
  def clear_screen
344
- @output.write "\e[2J"
345
- @output.write "\e[1;1H"
293
+ write "\e[2J"
294
+ write "\e[1;1H"
346
295
  end
347
296
 
348
297
  def set_winch_handler(&handler)
349
- @old_winch_handler = Signal.trap('WINCH', &handler)
350
- @old_cont_handler = Signal.trap('CONT') do
298
+ @old_winch_handler = Signal.trap('WINCH') do |arg|
299
+ handler.call
300
+ @old_winch_handler.call(arg) if @old_winch_handler.respond_to?(:call)
301
+ end
302
+ @old_cont_handler = Signal.trap('CONT') do |arg|
351
303
  @input.raw!(intr: true) if @input.tty?
352
304
  # Rerender the screen. Note that screen size might be changed while suspended.
353
305
  handler.call
306
+ @old_cont_handler.call(arg) if @old_cont_handler.respond_to?(:call)
354
307
  end
355
308
  rescue ArgumentError
356
309
  # Signal.trap may raise an ArgumentError if the platform doesn't support the signal.
@@ -358,14 +311,14 @@ class Reline::ANSI < Reline::IO
358
311
 
359
312
  def prep
360
313
  # Enable bracketed paste
361
- @output.write "\e[?2004h" if Reline.core.config.enable_bracketed_paste && both_tty?
314
+ write "\e[?2004h" if Reline.core.config.enable_bracketed_paste && both_tty?
362
315
  retrieve_keybuffer
363
316
  nil
364
317
  end
365
318
 
366
319
  def deprep(otio)
367
320
  # Disable bracketed paste
368
- @output.write "\e[?2004l" if Reline.core.config.enable_bracketed_paste && both_tty?
321
+ write "\e[?2004l" if Reline.core.config.enable_bracketed_paste && both_tty?
369
322
  Signal.trap('WINCH', @old_winch_handler) if @old_winch_handler
370
323
  Signal.trap('CONT', @old_cont_handler) if @old_cont_handler
371
324
  end
@@ -3,8 +3,11 @@ require 'io/wait'
3
3
  class Reline::Dumb < Reline::IO
4
4
  RESET_COLOR = '' # Do not send color reset sequence
5
5
 
6
+ attr_writer :output
7
+
6
8
  def initialize(encoding: nil)
7
9
  @input = STDIN
10
+ @output = STDOUT
8
11
  @buf = []
9
12
  @pasting = false
10
13
  @encoding = encoding
@@ -21,8 +24,11 @@ class Reline::Dumb < Reline::IO
21
24
  elsif RUBY_PLATFORM =~ /mswin|mingw/
22
25
  Encoding::UTF_8
23
26
  else
24
- Encoding::default_external
27
+ @input.external_encoding || Encoding.default_external
25
28
  end
29
+ rescue IOError
30
+ # STDIN.external_encoding raises IOError in Ruby <= 3.0 when STDIN is closed
31
+ Encoding.default_external
26
32
  end
27
33
 
28
34
  def set_default_key_bindings(_)
@@ -36,6 +42,14 @@ class Reline::Dumb < Reline::IO
36
42
  yield
37
43
  end
38
44
 
45
+ def write(string)
46
+ @output.write(string)
47
+ end
48
+
49
+ def buffered_output
50
+ yield
51
+ end
52
+
39
53
  def getc(_timeout_second)
40
54
  unless @buf.empty?
41
55
  return @buf.shift
@@ -60,7 +74,7 @@ class Reline::Dumb < Reline::IO
60
74
  end
61
75
 
62
76
  def cursor_pos
63
- Reline::CursorPos.new(1, 1)
77
+ Reline::CursorPos.new(0, 0)
64
78
  end
65
79
 
66
80
  def hide_cursor