reline 0.2.8.pre.8 → 0.3.0

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