reline 0.6.1 → 0.6.3

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: 2fb74f487afccdfb3b0530a043cfb7090f2a4860f2fdd168bc94112818b5f57d
4
- data.tar.gz: 96b74e0f611f24022f204e9551d8692ef017c52a197ae169fbfdc1074a130857
3
+ metadata.gz: cac4dc95dc3a9106a6f332d3503a62502f09205161a21157222e2dcd985ab706
4
+ data.tar.gz: 586f28ad1d6e68eefe7e5b4a15a4ee69f9d55d5469c144b686b72084b0abebe8
5
5
  SHA512:
6
- metadata.gz: ffdcec13818f2445bd9f639e75b36405c76a439378335fa7c05fad90a26f06965e54cd9dce1b8e1411e16df9ffc80c3c0ac356320654af2ebd40a33fd28cb247
7
- data.tar.gz: 24be10266aebce2d07f1519010f6ce0e6b92b816b17e526ede204943880b54388aa5ebf5da086bde0212b5e1f837936e4972c09a57a2d6c94f7c9a41412966d9
6
+ metadata.gz: 6f2e8fc8a404e1e8a0491c083e5ce524c5a5e465fceebcfa6eb7c0a5df20b98e07d337bb2c8703806f54e055efbeeba9c5d1f3171bd8e0faf6a611e30bbbf5da
7
+ data.tar.gz: a73fe51d441f70a4bb84ecfac2172251e6db0abf9f3007cbadf4f0374e5772b3ee62f71c753f10e88aa460278d1c0611b32ebca2e6f59fc6e42894377b56eb34
data/lib/reline/config.rb CHANGED
@@ -177,6 +177,11 @@ class Reline::Config
177
177
  if_stack = []
178
178
 
179
179
  lines.each_with_index do |line, no|
180
+ # Even after encoding conversion, we need to verify the encoding is valid
181
+ # as some invalid byte sequences might pass through the conversion.
182
+ unless line.valid_encoding?
183
+ raise InvalidInputrc, "#{file}:#{no + 1}: can't be converted to the locale #{Reline.encoding_system_needs.name}"
184
+ end
180
185
  next if line.match(/\A\s*#/)
181
186
 
182
187
  no += 1
@@ -124,6 +124,10 @@ class Reline::ANSI < Reline::IO
124
124
  Reline.core.line_editor.handle_signal
125
125
  end
126
126
  c = @input.getbyte
127
+
128
+ # When "Escape non-ASCII Input with Control-V" is enabled in macOS Terminal.app,
129
+ # all non-ascii bytes are automatically escaped with `C-v`.
130
+ # "\xE3\x81\x82" (U+3042) becomes "\x16\xE3\x16\x81\x16\x82".
127
131
  (c == 0x16 && @input.tty? && @input.raw(min: 0, time: 0, &:getbyte)) || c
128
132
  rescue Errno::EIO
129
133
  # Maybe the I/O has been closed.
@@ -163,31 +167,20 @@ class Reline::ANSI < Reline::IO
163
167
  @buf.unshift(c)
164
168
  end
165
169
 
166
- def retrieve_keybuffer
167
- begin
168
- return unless @input.wait_readable(0.001)
169
- str = @input.read_nonblock(1024)
170
- str.bytes.each do |c|
171
- @buf.push(c)
172
- end
173
- rescue EOFError
174
- end
175
- end
176
-
177
170
  def get_screen_size
178
171
  s = @input.winsize
179
172
  return s if s[0] > 0 && s[1] > 0
180
173
  s = [ENV["LINES"].to_i, ENV["COLUMNS"].to_i]
181
174
  return s if s[0] > 0 && s[1] > 0
182
175
  [24, 80]
183
- rescue Errno::ENOTTY, Errno::ENODEV
176
+ rescue SystemCallError
184
177
  [24, 80]
185
178
  end
186
179
 
187
180
  def set_screen_size(rows, columns)
188
181
  @input.winsize = [rows, columns]
189
182
  self
190
- rescue Errno::ENOTTY, Errno::ENODEV
183
+ rescue SystemCallError
191
184
  self
192
185
  end
193
186
 
@@ -295,7 +288,7 @@ class Reline::ANSI < Reline::IO
295
288
  # Signal.trap may raise an ArgumentError if the platform doesn't support the signal.
296
289
  end
297
290
 
298
- def read_single_char(keyseq_timeout)
291
+ def read_single_char(timeout_second)
299
292
  # Disable intr to read `C-c` `C-z` `C-\` for quoted insert
300
293
  @input.raw(intr: false) do
301
294
  super
@@ -305,7 +298,6 @@ class Reline::ANSI < Reline::IO
305
298
  def prep
306
299
  # Enable bracketed paste
307
300
  write "\e[?2004h" if Reline.core.config.enable_bracketed_paste && both_tty?
308
- retrieve_keybuffer
309
301
  nil
310
302
  end
311
303
 
data/lib/reline/io.rb CHANGED
@@ -37,10 +37,10 @@ module Reline
37
37
  end
38
38
 
39
39
  # Read a single encoding valid character from the input.
40
- def read_single_char(keyseq_timeout)
40
+ def read_single_char(timeout_second)
41
41
  buffer = String.new(encoding: Encoding::ASCII_8BIT)
42
42
  loop do
43
- timeout = buffer.empty? ? Float::INFINITY : keyseq_timeout
43
+ timeout = buffer.empty? ? Float::INFINITY : timeout_second
44
44
  c = getc(timeout)
45
45
  return unless c
46
46
 
@@ -377,11 +377,11 @@ module Reline::KeyActor
377
377
  # 187 M-;
378
378
  nil,
379
379
  # 188 M-<
380
- nil,
380
+ :beginning_of_history,
381
381
  # 189 M-=
382
382
  nil,
383
383
  # 190 M->
384
- nil,
384
+ :end_of_history,
385
385
  # 191 M-?
386
386
  nil,
387
387
  # 192 M-@
@@ -470,14 +470,6 @@ class Reline::LineEditor
470
470
  end
471
471
  end
472
472
 
473
- def print_nomultiline_prompt
474
- Reline::IOGate.disable_auto_linewrap(true) if Reline::IOGate.win?
475
- # Readline's test `TestRelineAsReadline#test_readline` requires first output to be prompt, not cursor reset escape sequence.
476
- Reline::IOGate.write Reline::Unicode.strip_non_printing_start_end(@prompt) if @prompt && !@is_multiline
477
- ensure
478
- Reline::IOGate.disable_auto_linewrap(false) if Reline::IOGate.win?
479
- end
480
-
481
473
  def render
482
474
  wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
483
475
  new_lines = wrapped_prompt_and_input_lines.flatten(1)[screen_scroll_top, screen_height].map do |prompt, line|
@@ -956,7 +948,7 @@ class Reline::LineEditor
956
948
 
957
949
  def wrap_method_call(method_symbol, key, with_operator)
958
950
  if @waiting_proc
959
- @waiting_proc.call(key)
951
+ @waiting_proc.call(key, method_symbol)
960
952
  return
961
953
  end
962
954
 
@@ -1442,21 +1434,24 @@ class Reline::LineEditor
1442
1434
  end
1443
1435
  alias_method :end_of_line, :ed_move_to_end
1444
1436
 
1445
- private def generate_searcher(search_key)
1437
+ private def generate_searcher(direction)
1446
1438
  search_word = String.new(encoding: encoding)
1447
1439
  hit_pointer = nil
1448
- lambda do |key|
1440
+ lambda do |key, key_symbol|
1449
1441
  search_again = false
1450
- case key
1451
- when "\C-h", "\C-?"
1442
+ case key_symbol
1443
+ when :em_delete_prev_char, :backward_delete_char
1452
1444
  grapheme_clusters = search_word.grapheme_clusters
1453
1445
  if grapheme_clusters.size > 0
1454
1446
  grapheme_clusters.pop
1455
1447
  search_word = grapheme_clusters.join
1456
1448
  end
1457
- when "\C-r", "\C-s"
1458
- search_again = true if search_key == key
1459
- search_key = key
1449
+ when :reverse_search_history, :vi_search_prev
1450
+ search_again = direction == :reverse
1451
+ direction = :reverse
1452
+ when :forward_search_history, :vi_search_next
1453
+ search_again = direction == :forward
1454
+ direction = :forward
1460
1455
  else
1461
1456
  search_word << key
1462
1457
  end
@@ -1470,11 +1465,11 @@ class Reline::LineEditor
1470
1465
  search_word = Reline.last_incremental_search
1471
1466
  end
1472
1467
  if @history_pointer
1473
- case search_key
1474
- when "\C-r"
1468
+ case direction
1469
+ when :reverse
1475
1470
  history_pointer_base = 0
1476
1471
  history = Reline::HISTORY[0..(@history_pointer - 1)]
1477
- when "\C-s"
1472
+ when :forward
1478
1473
  history_pointer_base = @history_pointer + 1
1479
1474
  history = Reline::HISTORY[(@history_pointer + 1)..-1]
1480
1475
  end
@@ -1483,11 +1478,11 @@ class Reline::LineEditor
1483
1478
  history = Reline::HISTORY
1484
1479
  end
1485
1480
  elsif @history_pointer
1486
- case search_key
1487
- when "\C-r"
1481
+ case direction
1482
+ when :reverse
1488
1483
  history_pointer_base = 0
1489
1484
  history = Reline::HISTORY[0..@history_pointer]
1490
- when "\C-s"
1485
+ when :forward
1491
1486
  history_pointer_base = @history_pointer
1492
1487
  history = Reline::HISTORY[@history_pointer..-1]
1493
1488
  end
@@ -1495,12 +1490,12 @@ class Reline::LineEditor
1495
1490
  history_pointer_base = 0
1496
1491
  history = Reline::HISTORY
1497
1492
  end
1498
- case search_key
1499
- when "\C-r"
1493
+ case direction
1494
+ when :reverse
1500
1495
  hit_index = history.rindex { |item|
1501
1496
  item.include?(search_word)
1502
1497
  }
1503
- when "\C-s"
1498
+ when :forward
1504
1499
  hit_index = history.index { |item|
1505
1500
  item.include?(search_word)
1506
1501
  }
@@ -1510,31 +1505,28 @@ class Reline::LineEditor
1510
1505
  hit = Reline::HISTORY[hit_pointer]
1511
1506
  end
1512
1507
  end
1513
- case search_key
1514
- when "\C-r"
1515
- prompt_name = 'reverse-i-search'
1516
- when "\C-s"
1517
- prompt_name = 'i-search'
1518
- end
1508
+ prompt_name = direction == :forward ? 'i-search' : 'reverse-i-search'
1519
1509
  prompt_name = "failed #{prompt_name}" unless hit
1520
1510
  [search_word, prompt_name, hit_pointer]
1521
1511
  end
1522
1512
  end
1523
1513
 
1524
- private def incremental_search_history(key)
1514
+ private def incremental_search_history(direction)
1525
1515
  backup = @buffer_of_lines.dup, @line_index, @byte_pointer, @history_pointer, @line_backup_in_history
1526
- searcher = generate_searcher(key)
1527
- @searching_prompt = "(reverse-i-search)`': "
1516
+ searcher = generate_searcher(direction)
1517
+ prompt_name = direction == :forward ? 'i-search' : 'reverse-i-search'
1518
+ @searching_prompt = "(#{prompt_name})`': "
1528
1519
  termination_keys = ["\C-j"]
1529
1520
  termination_keys.concat(@config.isearch_terminators.chars) if @config.isearch_terminators
1530
- @waiting_proc = ->(k) {
1521
+ accept_key_syms = [:em_delete_prev_char, :backward_delete_char, :vi_search_prev, :vi_search_next, :reverse_search_history, :forward_search_history]
1522
+ @waiting_proc = ->(k, key_symbol) {
1531
1523
  if k == "\C-g"
1532
1524
  # cancel search and restore buffer
1533
1525
  @buffer_of_lines, @line_index, @byte_pointer, @history_pointer, @line_backup_in_history = backup
1534
1526
  @searching_prompt = nil
1535
1527
  @waiting_proc = nil
1536
- elsif !termination_keys.include?(k) && (k.match?(/[[:print:]]/) || k == "\C-h" || k == "\C-?" || k == "\C-r" || k == "\C-s")
1537
- search_word, prompt_name, hit_pointer = searcher.call(k)
1528
+ elsif !termination_keys.include?(k) && (k.match?(/[[:print:]]/) || accept_key_syms.include?(key_symbol))
1529
+ search_word, prompt_name, hit_pointer = searcher.call(k, key_symbol)
1538
1530
  Reline.last_incremental_search = search_word
1539
1531
  @searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
1540
1532
  @searching_prompt += ': ' unless @is_multiline
@@ -1549,12 +1541,12 @@ class Reline::LineEditor
1549
1541
  end
1550
1542
 
1551
1543
  private def vi_search_prev(key)
1552
- incremental_search_history(key)
1544
+ incremental_search_history(:reverse)
1553
1545
  end
1554
1546
  alias_method :reverse_search_history, :vi_search_prev
1555
1547
 
1556
1548
  private def vi_search_next(key)
1557
- incremental_search_history(key)
1549
+ incremental_search_history(:forward)
1558
1550
  end
1559
1551
  alias_method :forward_search_history, :vi_search_next
1560
1552
 
@@ -1654,6 +1646,16 @@ class Reline::LineEditor
1654
1646
  end
1655
1647
  alias_method :next_history, :ed_next_history
1656
1648
 
1649
+ private def ed_beginning_of_history(key)
1650
+ move_history(0, line: :end, cursor: :end)
1651
+ end
1652
+ alias_method :beginning_of_history, :ed_beginning_of_history
1653
+
1654
+ private def ed_end_of_history(key)
1655
+ move_history(Reline::HISTORY.size, line: :end, cursor: :end)
1656
+ end
1657
+ alias_method :end_of_history, :ed_end_of_history
1658
+
1657
1659
  private def ed_newline(key)
1658
1660
  process_insert(force: true)
1659
1661
  if @is_multiline
@@ -2180,7 +2182,7 @@ class Reline::LineEditor
2180
2182
  end
2181
2183
 
2182
2184
  private def vi_replace_char(key, arg: 1)
2183
- @waiting_proc = ->(k) {
2185
+ @waiting_proc = ->(k, _sym) {
2184
2186
  if arg == 1
2185
2187
  byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
2186
2188
  before = current_line.byteslice(0, @byte_pointer)
@@ -2204,11 +2206,11 @@ class Reline::LineEditor
2204
2206
  end
2205
2207
 
2206
2208
  private def vi_next_char(key, arg: 1, inclusive: false)
2207
- @waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, inclusive: inclusive) }
2209
+ @waiting_proc = ->(key_for_proc, _sym) { search_next_char(key_for_proc, arg, inclusive: inclusive) }
2208
2210
  end
2209
2211
 
2210
2212
  private def vi_to_next_char(key, arg: 1, inclusive: false)
2211
- @waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, need_prev_char: true, inclusive: inclusive) }
2213
+ @waiting_proc = ->(key_for_proc, _sym) { search_next_char(key_for_proc, arg, need_prev_char: true, inclusive: inclusive) }
2212
2214
  end
2213
2215
 
2214
2216
  private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
@@ -2251,11 +2253,11 @@ class Reline::LineEditor
2251
2253
  end
2252
2254
 
2253
2255
  private def vi_prev_char(key, arg: 1)
2254
- @waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg) }
2256
+ @waiting_proc = ->(key_for_proc, _sym) { search_prev_char(key_for_proc, arg) }
2255
2257
  end
2256
2258
 
2257
2259
  private def vi_to_prev_char(key, arg: 1)
2258
- @waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg, true) }
2260
+ @waiting_proc = ->(key_for_proc, _sym) { search_prev_char(key_for_proc, arg, true) }
2259
2261
  end
2260
2262
 
2261
2263
  private def search_prev_char(key, arg, need_next_char = false)
@@ -129,7 +129,7 @@ class Reline::Unicode::EastAsianWidth
129
129
  [0x450, 1],
130
130
  [0x451, -1],
131
131
  [0x482, 1],
132
- [0x487, 0],
132
+ [0x489, 0],
133
133
  [0x590, 1],
134
134
  [0x5bd, 0],
135
135
  [0x5be, 1],
@@ -356,6 +356,7 @@ class Reline::Unicode::EastAsianWidth
356
356
  [0x109d, 0],
357
357
  [0x10ff, 1],
358
358
  [0x115f, 2],
359
+ [0x11ff, 0],
359
360
  [0x135c, 1],
360
361
  [0x135f, 0],
361
362
  [0x1711, 1],
@@ -411,8 +412,6 @@ class Reline::Unicode::EastAsianWidth
411
412
  [0x1a7e, 1],
412
413
  [0x1a7f, 0],
413
414
  [0x1aaf, 1],
414
- [0x1abd, 0],
415
- [0x1abe, 1],
416
415
  [0x1ace, 0],
417
416
  [0x1aff, 1],
418
417
  [0x1b03, 0],
@@ -491,10 +490,6 @@ class Reline::Unicode::EastAsianWidth
491
490
  [0x20ab, 1],
492
491
  [0x20ac, -1],
493
492
  [0x20cf, 1],
494
- [0x20dc, 0],
495
- [0x20e0, 1],
496
- [0x20e1, 0],
497
- [0x20e4, 1],
498
493
  [0x20f0, 0],
499
494
  [0x2102, 1],
500
495
  [0x2103, -1],
@@ -767,7 +762,7 @@ class Reline::Unicode::EastAsianWidth
767
762
  [0xa48f, 1],
768
763
  [0xa4c6, 2],
769
764
  [0xa66e, 1],
770
- [0xa66f, 0],
765
+ [0xa672, 0],
771
766
  [0xa673, 1],
772
767
  [0xa67d, 0],
773
768
  [0xa69d, 1],
@@ -840,6 +835,10 @@ class Reline::Unicode::EastAsianWidth
840
835
  [0xabed, 0],
841
836
  [0xabff, 1],
842
837
  [0xd7a3, 2],
838
+ [0xd7af, 1],
839
+ [0xd7c6, 0],
840
+ [0xd7ca, 1],
841
+ [0xd7fb, 0],
843
842
  [0xdfff, 1],
844
843
  [0xf8ff, -1],
845
844
  [0xfaff, 2],
@@ -37,7 +37,7 @@ class Reline::Unicode
37
37
 
38
38
  NON_PRINTING_START = "\1"
39
39
  NON_PRINTING_END = "\2"
40
- CSI_REGEXP = /\e\[[\d;]*[ABCDEFGHJKSTfminsuhl]/
40
+ CSI_REGEXP = /\e\[[\x30-\x3f]*[\x20-\x2f]*[a-zA-Z]/
41
41
  OSC_REGEXP = /\e\]\d+(?:;[^;\a\e]+)*(?:\a|\e\\)/
42
42
  WIDTH_SCANNER = /\G(?:(#{NON_PRINTING_START})|(#{NON_PRINTING_END})|(#{CSI_REGEXP})|(#{OSC_REGEXP})|(\X))/o
43
43
 
@@ -72,26 +72,32 @@ class Reline::Unicode
72
72
 
73
73
  require 'reline/unicode/east_asian_width'
74
74
 
75
+ def self.east_asian_width(ord)
76
+ chunk_index = EastAsianWidth::CHUNK_LAST.bsearch_index { |o| ord <= o }
77
+ size = EastAsianWidth::CHUNK_WIDTH[chunk_index]
78
+ size == -1 ? Reline.ambiguous_width : size
79
+ end
80
+
75
81
  def self.get_mbchar_width(mbchar)
76
82
  ord = mbchar.ord
77
83
  if ord <= 0x1F # in EscapedPairs
78
84
  return 2
79
- elsif ord <= 0x7E # printable ASCII chars
85
+ elsif mbchar.length == 1 && ord <= 0x7E # printable ASCII chars
80
86
  return 1
81
87
  end
88
+
82
89
  utf8_mbchar = mbchar.encode(Encoding::UTF_8)
83
- ord = utf8_mbchar.ord
84
- chunk_index = EastAsianWidth::CHUNK_LAST.bsearch_index { |o| ord <= o }
85
- size = EastAsianWidth::CHUNK_WIDTH[chunk_index]
86
- if size == -1
87
- Reline.ambiguous_width
88
- elsif size == 1 && utf8_mbchar.size >= 2
89
- second_char_ord = utf8_mbchar[1].ord
90
- # Halfwidth Dakuten Handakuten
91
- # Only these two character has Letter Modifier category and can be combined in a single grapheme cluster
92
- (second_char_ord == 0xFF9E || second_char_ord == 0xFF9F) ? 2 : 1
93
- else
94
- size
90
+ zwj = false
91
+ utf8_mbchar.chars.sum do |c|
92
+ if zwj
93
+ zwj = false
94
+ 0
95
+ elsif c.ord == 0x200D # Zero Width Joiner
96
+ zwj = true
97
+ 0
98
+ else
99
+ east_asian_width(c.ord)
100
+ end
95
101
  end
96
102
  end
97
103
 
@@ -214,7 +220,7 @@ class Reline::Unicode
214
220
  next
215
221
  elsif padding && !cover_begin && prev_width < start_col && start_col < total_width
216
222
  # Add preceding padding. This padding might have background color.
217
- chunk << ' '
223
+ chunk << ' ' * (total_width - start_col)
218
224
  chunk_start_col ||= start_col
219
225
  chunk_end_col = total_width
220
226
  next
@@ -228,7 +234,7 @@ class Reline::Unicode
228
234
  # Current character exceeds end_col
229
235
  if padding && end_col < total_width
230
236
  # Add succeeding padding. This padding might have background color.
231
- chunk << ' '
237
+ chunk << ' ' * (end_col - prev_width)
232
238
  chunk_start_col ||= prev_width
233
239
  chunk_end_col = end_col
234
240
  end
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.6.1'
2
+ VERSION = '0.6.3'
3
3
  end
data/lib/reline.rb CHANGED
@@ -324,8 +324,6 @@ module Reline
324
324
  line_editor.auto_indent_proc = auto_indent_proc
325
325
  line_editor.dig_perfect_match_proc = dig_perfect_match_proc
326
326
 
327
- # Readline calls pre_input_hook just after printing the first prompt.
328
- line_editor.print_nomultiline_prompt
329
327
  pre_input_hook&.call
330
328
 
331
329
  unless Reline::IOGate.dumb?
@@ -347,7 +345,8 @@ module Reline
347
345
  # io_gate is Reline::ANSI because the key :bracketed_paste_start is only assigned in Reline::ANSI
348
346
  key = Reline::Key.new(io_gate.read_bracketed_paste, :insert_multiline_text)
349
347
  when :quoted_insert, :ed_quoted_insert
350
- key = Reline::Key.new(io_gate.read_single_char(config.keyseq_timeout), :insert_raw_char)
348
+ char = io_gate.read_single_char(config.keyseq_timeout.fdiv(1000))
349
+ key = Reline::Key.new(char || '', :insert_raw_char)
351
350
  end
352
351
  line_editor.set_pasting_state(io_gate.in_pasting?)
353
352
  line_editor.update(key)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-04 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: io-console
@@ -75,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
75
  - !ruby/object:Gem::Version
76
76
  version: '0'
77
77
  requirements: []
78
- rubygems_version: 3.6.3
78
+ rubygems_version: 3.6.7
79
79
  specification_version: 4
80
80
  summary: Alternative GNU Readline or Editline implementation by pure Ruby.
81
81
  test_files: []