reline 0.2.8.pre.8 → 0.3.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.
@@ -1,5 +1,13 @@
1
- require 'fiddle'
2
- require 'fiddle/import'
1
+ begin
2
+ require 'fiddle'
3
+ require 'fiddle/import'
4
+ rescue LoadError
5
+ module Reline::Terminfo
6
+ def self.curses_dl
7
+ false
8
+ end
9
+ end
10
+ end
3
11
 
4
12
  module Reline::Terminfo
5
13
  extend Fiddle::Importer
@@ -50,7 +58,7 @@ module Reline::Terminfo
50
58
  @curses_dl = nil if @curses_dl == false
51
59
  @curses_dl
52
60
  end
53
- end
61
+ end if not Reline.const_defined?(:Terminfo) or not Reline::Terminfo.respond_to?(:curses_dl)
54
62
 
55
63
  module Reline::Terminfo
56
64
  dlload curses_dl
@@ -71,7 +79,7 @@ module Reline::Terminfo
71
79
  def self.setupterm(term, fildes)
72
80
  errret_int = String.new("\x00" * 8, encoding: 'ASCII-8BIT')
73
81
  ret = @setupterm.(term, fildes, errret_int)
74
- errret = errret_int.unpack('i')[0]
82
+ errret = errret_int.unpack1('i')
75
83
  case ret
76
84
  when 0 # OK
77
85
  0
@@ -79,6 +79,8 @@ class Reline::Unicode
79
79
 
80
80
  require 'reline/unicode/east_asian_width'
81
81
 
82
+ HalfwidthDakutenHandakuten = /[\u{FF9E}\u{FF9F}]/
83
+
82
84
  MBCharWidthRE = /
83
85
  (?<width_2_1>
84
86
  [#{ EscapedChars.map {|c| "\\x%02x" % c.ord }.join }] (?# ^ + char, such as ^M, ^H, ^[, ...)
@@ -93,6 +95,12 @@ class Reline::Unicode
93
95
  #{ EastAsianWidth::TYPE_H }
94
96
  | #{ EastAsianWidth::TYPE_NA }
95
97
  | #{ EastAsianWidth::TYPE_N }
98
+ )(?!#{ HalfwidthDakutenHandakuten })
99
+ | (?<width_2_3>
100
+ (?: #{ EastAsianWidth::TYPE_H }
101
+ | #{ EastAsianWidth::TYPE_NA }
102
+ | #{ EastAsianWidth::TYPE_N })
103
+ #{ HalfwidthDakutenHandakuten }
96
104
  )
97
105
  | (?<ambiguous_width>
98
106
  #{EastAsianWidth::TYPE_A}
@@ -109,7 +117,7 @@ class Reline::Unicode
109
117
  m = mbchar.encode(Encoding::UTF_8).match(MBCharWidthRE)
110
118
  case
111
119
  when m.nil? then 1 # TODO should be U+FFFD � REPLACEMENT CHARACTER
112
- when m[:width_2_1], m[:width_2_2] then 2
120
+ when m[:width_2_1], m[:width_2_2], m[:width_2_3] then 2
113
121
  when m[:width_3] then 3
114
122
  when m[:width_0] then 0
115
123
  when m[:width_1] then 1
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.2.8.pre.8'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -168,7 +168,9 @@ class Reline::Windows
168
168
  @@input_buf = []
169
169
  @@output_buf = []
170
170
 
171
- def self.msys_tty?(io=@@hConsoleInputHandle)
171
+ @@output = STDOUT
172
+
173
+ def self.msys_tty?(io = @@hConsoleInputHandle)
172
174
  # check if fd is a pipe
173
175
  if @@GetFileType.call(io) != FILE_TYPE_PIPE
174
176
  return false
@@ -184,7 +186,7 @@ class Reline::Windows
184
186
  # DWORD FileNameLength;
185
187
  # WCHAR FileName[1];
186
188
  # } FILE_NAME_INFO
187
- len = p_buffer[0, 4].unpack("L")[0]
189
+ len = p_buffer[0, 4].unpack1("L")
188
190
  name = p_buffer[4, len].encode(Encoding::UTF_8, Encoding::UTF_16LE, invalid: :replace)
189
191
 
190
192
  # Check if this could be a MSYS2 pty pipe ('\msys-XXXX-ptyN-XX')
@@ -213,8 +215,29 @@ class Reline::Windows
213
215
  [ { control_keys: :SHIFT, virtual_key_code: VK_TAB }, [27, 91, 90] ],
214
216
  ]
215
217
 
218
+ @@hsg = nil
219
+
216
220
  def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
217
221
 
222
+ # high-surrogate
223
+ if 0xD800 <= char_code and char_code <= 0xDBFF
224
+ @@hsg = char_code
225
+ return
226
+ end
227
+ # low-surrogate
228
+ if 0xDC00 <= char_code and char_code <= 0xDFFF
229
+ if @@hsg
230
+ char_code = 0x10000 + (@@hsg - 0xD800) * 0x400 + char_code - 0xDC00
231
+ @@hsg = nil
232
+ else
233
+ # no high-surrogate. ignored.
234
+ return
235
+ end
236
+ else
237
+ # ignore high-surrogate without low-surrogate if there
238
+ @@hsg = nil
239
+ end
240
+
218
241
  key = KeyEventRecord.new(virtual_key_code, char_code, control_key_state)
219
242
 
220
243
  match = KEY_MAP.find { |args,| key.matches?(**args) }
@@ -233,26 +256,35 @@ class Reline::Windows
233
256
 
234
257
  def self.check_input_event
235
258
  num_of_events = 0.chr * 8
236
- while @@output_buf.empty? #or true
237
- next if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
238
- next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack('L').first == 0
239
- input_record = 0.chr * 18
259
+ while @@output_buf.empty?
260
+ Reline.core.line_editor.resize
261
+ if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
262
+ # prevent for background consolemode change
263
+ @@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
264
+ next
265
+ end
266
+ next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack1('L') == 0
267
+ input_records = 0.chr * 20 * 80
240
268
  read_event = 0.chr * 4
241
- if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
242
- event = input_record[0, 2].unpack('s*').first
243
- case event
244
- when WINDOW_BUFFER_SIZE_EVENT
245
- @@winch_handler.()
246
- when KEY_EVENT
247
- key_down = input_record[4, 4].unpack('l*').first
248
- repeat_count = input_record[8, 2].unpack('s*').first
249
- virtual_key_code = input_record[10, 2].unpack('s*').first
250
- virtual_scan_code = input_record[12, 2].unpack('s*').first
251
- char_code = input_record[14, 2].unpack('S*').first
252
- control_key_state = input_record[16, 2].unpack('S*').first
253
- is_key_down = key_down.zero? ? false : true
254
- if is_key_down
255
- process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
269
+ if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_records, 80, read_event) != 0
270
+ read_events = read_event.unpack1('L')
271
+ 0.upto(read_events) do |idx|
272
+ input_record = input_records[idx * 20, 20]
273
+ event = input_record[0, 2].unpack1('s*')
274
+ case event
275
+ when WINDOW_BUFFER_SIZE_EVENT
276
+ @@winch_handler.()
277
+ when KEY_EVENT
278
+ key_down = input_record[4, 4].unpack1('l*')
279
+ repeat_count = input_record[8, 2].unpack1('s*')
280
+ virtual_key_code = input_record[10, 2].unpack1('s*')
281
+ virtual_scan_code = input_record[12, 2].unpack1('s*')
282
+ char_code = input_record[14, 2].unpack1('S*')
283
+ control_key_state = input_record[16, 2].unpack1('S*')
284
+ is_key_down = key_down.zero? ? false : true
285
+ if is_key_down
286
+ process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
287
+ end
256
288
  end
257
289
  end
258
290
  end
@@ -273,7 +305,7 @@ class Reline::Windows
273
305
  end
274
306
 
275
307
  def self.empty_buffer?
276
- if not @@input_buf.empty?
308
+ if not @@output_buf.empty?
277
309
  false
278
310
  elsif @@kbhit.call == 0
279
311
  true
@@ -282,17 +314,37 @@ class Reline::Windows
282
314
  end
283
315
  end
284
316
 
285
- def self.get_screen_size
317
+ def self.get_console_screen_buffer_info
318
+ # CONSOLE_SCREEN_BUFFER_INFO
319
+ # [ 0,2] dwSize.X
320
+ # [ 2,2] dwSize.Y
321
+ # [ 4,2] dwCursorPositions.X
322
+ # [ 6,2] dwCursorPositions.Y
323
+ # [ 8,2] wAttributes
324
+ # [10,2] srWindow.Left
325
+ # [12,2] srWindow.Top
326
+ # [14,2] srWindow.Right
327
+ # [16,2] srWindow.Bottom
328
+ # [18,2] dwMaximumWindowSize.X
329
+ # [20,2] dwMaximumWindowSize.Y
286
330
  csbi = 0.chr * 22
287
- @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)
331
+ return if @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi) == 0
332
+ csbi
333
+ end
334
+
335
+ def self.get_screen_size
336
+ unless csbi = get_console_screen_buffer_info
337
+ return [1, 1]
338
+ end
288
339
  csbi[0, 4].unpack('SS').reverse
289
340
  end
290
341
 
291
342
  def self.cursor_pos
292
- csbi = 0.chr * 22
293
- @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)
294
- x = csbi[4, 2].unpack('s*').first
295
- y = csbi[6, 2].unpack('s*').first
343
+ unless csbi = get_console_screen_buffer_info
344
+ return Reline::CursorPos.new(0, 0)
345
+ end
346
+ x = csbi[4, 2].unpack1('s')
347
+ y = csbi[6, 2].unpack1('s')
296
348
  Reline::CursorPos.new(x, y)
297
349
  end
298
350
 
@@ -312,6 +364,7 @@ class Reline::Windows
312
364
 
313
365
  def self.move_cursor_down(val)
314
366
  if val > 0
367
+ return unless csbi = get_console_screen_buffer_info
315
368
  screen_height = get_screen_size.first
316
369
  y = cursor_pos.y + val
317
370
  y = screen_height - 1 if y > (screen_height - 1)
@@ -322,36 +375,54 @@ class Reline::Windows
322
375
  end
323
376
 
324
377
  def self.erase_after_cursor
325
- csbi = 0.chr * 24
326
- @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)
327
- cursor = csbi[4, 4].unpack('L').first
378
+ return unless csbi = get_console_screen_buffer_info
379
+ attributes = csbi[8, 2].unpack1('S')
380
+ cursor = csbi[4, 4].unpack1('L')
328
381
  written = 0.chr * 4
329
382
  @@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, get_screen_size.last - cursor_pos.x, cursor, written)
330
- @@FillConsoleOutputAttribute.call(@@hConsoleHandle, 0, get_screen_size.last - cursor_pos.x, cursor, written)
383
+ @@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, get_screen_size.last - cursor_pos.x, cursor, written)
331
384
  end
332
385
 
333
386
  def self.scroll_down(val)
334
- return if val.zero?
335
- screen_height = get_screen_size.first
336
- val = screen_height - 1 if val > (screen_height - 1)
337
- scroll_rectangle = [0, val, get_screen_size.last, get_screen_size.first].pack('s4')
338
- destination_origin = 0 # y * 65536 + x
339
- fill = [' '.ord, 0].pack('SS')
340
- @@ScrollConsoleScreenBuffer.call(@@hConsoleHandle, scroll_rectangle, nil, destination_origin, fill)
387
+ return if val < 0
388
+ return unless csbi = get_console_screen_buffer_info
389
+ buffer_width, x, y, buffer_lines, attributes, window_left, window_top, window_bottom = csbi.unpack('ssssSssx2s')
390
+ screen_height = window_bottom - window_top + 1
391
+ val = screen_height if val > screen_height
392
+
393
+ if @@legacy_console || window_left != 0
394
+ # unless ENABLE_VIRTUAL_TERMINAL,
395
+ # if srWindow.Left != 0 then it's conhost.exe hosted console
396
+ # and puts "\n" causes horizontal scroll. its glitch.
397
+ # FYI irb write from culumn 1, so this gives no gain.
398
+ scroll_rectangle = [0, val, buffer_width, buffer_lines - val].pack('s4')
399
+ destination_origin = 0 # y * 65536 + x
400
+ fill = [' '.ord, attributes].pack('SS')
401
+ @@ScrollConsoleScreenBuffer.call(@@hConsoleHandle, scroll_rectangle, nil, destination_origin, fill)
402
+ else
403
+ origin_x = x + 1
404
+ origin_y = y - window_top + 1
405
+ @@output.write [
406
+ (origin_y != screen_height) ? "\e[#{screen_height};H" : nil,
407
+ "\n" * val,
408
+ (origin_y != screen_height or !x.zero?) ? "\e[#{origin_y};#{origin_x}H" : nil
409
+ ].join
410
+ end
341
411
  end
342
412
 
343
413
  def self.clear_screen
344
- csbi = 0.chr * 22
345
- return if @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi) == 0
346
- buffer_width = csbi[0, 2].unpack('S').first
347
- attributes = csbi[8, 2].unpack('S').first
348
- _window_left, window_top, _window_right, window_bottom = *csbi[10,8].unpack('S*')
349
- fill_length = buffer_width * (window_bottom - window_top + 1)
350
- screen_topleft = window_top * 65536
351
- written = 0.chr * 4
352
- @@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, fill_length, screen_topleft, written)
353
- @@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, fill_length, screen_topleft, written)
354
- @@SetConsoleCursorPosition.call(@@hConsoleHandle, screen_topleft)
414
+ if @@legacy_console
415
+ return unless csbi = get_console_screen_buffer_info
416
+ buffer_width, _buffer_lines, attributes, window_top, window_bottom = csbi.unpack('ss@8S@12sx2s')
417
+ fill_length = buffer_width * (window_bottom - window_top + 1)
418
+ screen_topleft = window_top * 65536
419
+ written = 0.chr * 4
420
+ @@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, fill_length, screen_topleft, written)
421
+ @@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, fill_length, screen_topleft, written)
422
+ @@SetConsoleCursorPosition.call(@@hConsoleHandle, screen_topleft)
423
+ else
424
+ @@output.write "\e[2J" "\e[H"
425
+ end
355
426
  end
356
427
 
357
428
  def self.set_screen_size(rows, columns)
data/lib/reline.rb CHANGED
@@ -17,19 +17,15 @@ module Reline
17
17
  class ConfigEncodingConversionError < StandardError; end
18
18
 
19
19
  Key = Struct.new('Key', :char, :combined_char, :with_meta) do
20
- def match?(key)
21
- if key.instance_of?(Reline::Key)
22
- (key.char.nil? or char.nil? or char == key.char) and
23
- (key.combined_char.nil? or combined_char.nil? or combined_char == key.combined_char) and
24
- (key.with_meta.nil? or with_meta.nil? or with_meta == key.with_meta)
25
- elsif key.is_a?(Integer) or key.is_a?(Symbol)
26
- if not combined_char.nil? and combined_char == key
27
- true
28
- elsif combined_char.nil? and not char.nil? and char == key
29
- true
30
- else
31
- false
32
- end
20
+ def match?(other)
21
+ case other
22
+ when Reline::Key
23
+ (other.char.nil? or char.nil? or char == other.char) and
24
+ (other.combined_char.nil? or combined_char.nil? or combined_char == other.combined_char) and
25
+ (other.with_meta.nil? or with_meta.nil? or with_meta == other.with_meta)
26
+ when Integer, Symbol
27
+ (combined_char and combined_char == other) or
28
+ (combined_char.nil? and char and char == other)
33
29
  else
34
30
  false
35
31
  end
@@ -37,7 +33,7 @@ module Reline
37
33
  alias_method :==, :match?
38
34
  end
39
35
  CursorPos = Struct.new(:x, :y)
40
- DialogRenderInfo = Struct.new(:pos, :contents, :pointer, :bg_color, :width, :height, :scrollbar, keyword_init: true)
36
+ DialogRenderInfo = Struct.new(:pos, :contents, :bg_color, :width, :height, :scrollbar, keyword_init: true)
41
37
 
42
38
  class Core
43
39
  ATTR_READER_NAMES = %i(
@@ -64,7 +60,7 @@ module Reline
64
60
 
65
61
  def initialize
66
62
  self.output = STDOUT
67
- @dialog_proc_list = []
63
+ @dialog_proc_list = {}
68
64
  yield self
69
65
  @completion_quote_character = nil
70
66
  @bracketed_paste_finished = false
@@ -159,10 +155,15 @@ module Reline
159
155
  @dig_perfect_match_proc = p
160
156
  end
161
157
 
158
+ DialogProc = Struct.new(:dialog_proc, :context)
162
159
  def add_dialog_proc(name_sym, p, context = nil)
163
160
  raise ArgumentError unless p.respond_to?(:call) or p.nil?
164
161
  raise ArgumentError unless name_sym.instance_of?(Symbol)
165
- @dialog_proc_list << [name_sym, p, context]
162
+ @dialog_proc_list[name_sym] = DialogProc.new(p, context)
163
+ end
164
+
165
+ def dialog_proc(name_sym)
166
+ @dialog_proc_list[name_sym]
166
167
  end
167
168
 
168
169
  def input=(val)
@@ -214,7 +215,7 @@ module Reline
214
215
  return nil
215
216
  end
216
217
  pre, target, post = retrieve_completion_block(true)
217
- if target.nil? or target.empty? or target.size <= 3
218
+ if target.nil? or target.empty? or (completion_journey_data&.pointer == -1 and target.size <= 3)
218
219
  return nil
219
220
  end
220
221
  if completion_journey_data and completion_journey_data.list
@@ -241,7 +242,8 @@ module Reline
241
242
  context.clear
242
243
  context.push(cursor_pos_to_render, result, pointer, dialog)
243
244
  end
244
- DialogRenderInfo.new(pos: cursor_pos_to_render, contents: result, pointer: pointer, scrollbar: true, height: 15)
245
+ dialog.pointer = pointer
246
+ DialogRenderInfo.new(pos: cursor_pos_to_render, contents: result, scrollbar: true, height: 15)
245
247
  }
246
248
  Reline::DEFAULT_DIALOG_CONTEXT = Array.new
247
249
 
@@ -304,9 +306,8 @@ module Reline
304
306
  line_editor.auto_indent_proc = auto_indent_proc
305
307
  line_editor.dig_perfect_match_proc = dig_perfect_match_proc
306
308
  line_editor.pre_input_hook = pre_input_hook
307
- @dialog_proc_list.each do |d|
308
- name_sym, dialog_proc, context = d
309
- line_editor.add_dialog_proc(name_sym, dialog_proc, context)
309
+ @dialog_proc_list.each_pair do |name_sym, d|
310
+ line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
310
311
  end
311
312
 
312
313
  unless config.test_mode
@@ -318,6 +319,7 @@ module Reline
318
319
  line_editor.rerender
319
320
 
320
321
  begin
322
+ line_editor.set_signal_handlers
321
323
  prev_pasting_state = false
322
324
  loop do
323
325
  prev_pasting_state = Reline::IOGate.in_pasting?
@@ -346,6 +348,11 @@ module Reline
346
348
  line_editor.finalize
347
349
  Reline::IOGate.deprep(otio)
348
350
  raise e
351
+ rescue Exception
352
+ # Including Interrupt
353
+ line_editor.finalize
354
+ Reline::IOGate.deprep(otio)
355
+ raise
349
356
  end
350
357
 
351
358
  line_editor.finalize
@@ -460,7 +467,7 @@ module Reline
460
467
 
461
468
  private def may_req_ambiguous_char_width
462
469
  @ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
463
- return if @ambiguous_width
470
+ return if defined? @ambiguous_width
464
471
  Reline::IOGate.move_cursor_column(0)
465
472
  begin
466
473
  output.write "\u{25bd}"
@@ -483,7 +490,7 @@ module Reline
483
490
  #--------------------------------------------------------
484
491
 
485
492
  (Core::ATTR_READER_NAMES).each { |name|
486
- def_single_delegators :core, "#{name}", "#{name}="
493
+ def_single_delegators :core, :"#{name}", :"#{name}="
487
494
  }
488
495
  def_single_delegators :core, :input=, :output=
489
496
  def_single_delegators :core, :vi_editing_mode, :emacs_editing_mode
@@ -519,6 +526,7 @@ module Reline
519
526
  def_single_delegators :core, :last_incremental_search
520
527
  def_single_delegators :core, :last_incremental_search=
521
528
  def_single_delegators :core, :add_dialog_proc
529
+ def_single_delegators :core, :dialog_proc
522
530
  def_single_delegators :core, :autocompletion, :autocompletion=
523
531
 
524
532
  def_single_delegators :core, :readmultiline
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8.pre.8
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-06 00:00:00.000000000 Z
11
+ date: 2021-12-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -47,7 +47,6 @@ files:
47
47
  - lib/reline/key_stroke.rb
48
48
  - lib/reline/kill_ring.rb
49
49
  - lib/reline/line_editor.rb
50
- - lib/reline/line_editor.rb.orig
51
50
  - lib/reline/sibori.rb
52
51
  - lib/reline/terminfo.rb
53
52
  - lib/reline/unicode.rb
@@ -70,9 +69,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
70
69
  version: '2.5'
71
70
  required_rubygems_version: !ruby/object:Gem::Requirement
72
71
  requirements:
73
- - - ">"
72
+ - - ">="
74
73
  - !ruby/object:Gem::Version
75
- version: 1.3.1
74
+ version: '0'
76
75
  requirements: []
77
76
  rubygems_version: 3.2.22
78
77
  signing_key: