reline 0.0.2 → 0.0.7

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: f47cc0525de297744dff41dc87b9a4ac5c9ff1cad21da7f60a41aa4b17bd1b0e
4
- data.tar.gz: '04681f2a2dc431f65fa9f2df68c8b7aa1955a7a4032b8d6d5483984e236d4c9b'
3
+ metadata.gz: 985d3ce68260542cce79b3d3291c29ca30ee82250e50089c2de7bb16f60d755d
4
+ data.tar.gz: d3444d9e894a66fd2a0e5be4fddd5d36b9b254f0aaff7ed1d590fec26c13b3f6
5
5
  SHA512:
6
- metadata.gz: ad38bf76659bc7553ebf084fd4aeedf6aba7cba1a2860fc78af0683222109216d59bdb3b4bf66962ce9a640b9033d21f707442151834e9ccc21c6fc8d115347b
7
- data.tar.gz: 95d9d943870e64897076cdc97750f6110817bc4ad14e958755164aa862785d7e48399465f7997f900547f797e6304cf48d6761f78612651ebe7aa6dca505fab3
6
+ metadata.gz: 9a5651134aeb6d0eba7e33b4fa6e85c2ac43677432a7b94e8aa24d1c9f05326126c81669aa297f9cb03bea7f92c2ee8600364205c8a8eafa7c6185441472f9d1
7
+ data.tar.gz: 63cc8b0b0b936e8ac1a490c9a1c849631b2c4c33c520eb2dd666b9b93b641417f3640542067de4f64ff2e28fd4dd883ed3821c7704bb82a64485ee52d5273c3b
@@ -16,12 +16,6 @@ module Reline
16
16
  CursorPos = Struct.new(:x, :y)
17
17
 
18
18
  class Core
19
- if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
20
- IS_WINDOWS = true
21
- else
22
- IS_WINDOWS = false
23
- end
24
-
25
19
  ATTR_READER_NAMES = %i(
26
20
  completion_append_character
27
21
  basic_word_break_characters
@@ -90,22 +84,22 @@ module Reline
90
84
  end
91
85
 
92
86
  def completion_proc=(p)
93
- raise ArgumentError unless p.is_a?(Proc)
87
+ raise ArgumentError unless p.respond_to?(:call)
94
88
  @completion_proc = p
95
89
  end
96
90
 
97
91
  def output_modifier_proc=(p)
98
- raise ArgumentError unless p.is_a?(Proc)
92
+ raise ArgumentError unless p.respond_to?(:call)
99
93
  @output_modifier_proc = p
100
94
  end
101
95
 
102
96
  def prompt_proc=(p)
103
- raise ArgumentError unless p.is_a?(Proc)
97
+ raise ArgumentError unless p.respond_to?(:call)
104
98
  @prompt_proc = p
105
99
  end
106
100
 
107
101
  def auto_indent_proc=(p)
108
- raise ArgumentError unless p.is_a?(Proc)
102
+ raise ArgumentError unless p.respond_to?(:call)
109
103
  @auto_indent_proc = p
110
104
  end
111
105
 
@@ -114,7 +108,7 @@ module Reline
114
108
  end
115
109
 
116
110
  def dig_perfect_match_proc=(p)
117
- raise ArgumentError unless p.is_a?(Proc)
111
+ raise ArgumentError unless p.respond_to?(:call)
118
112
  @dig_perfect_match_proc = p
119
113
  end
120
114
 
@@ -166,7 +160,7 @@ module Reline
166
160
  inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
167
161
 
168
162
  whole_buffer = line_editor.whole_buffer.dup
169
- whole_buffer.taint
163
+ whole_buffer.taint if RUBY_VERSION < '2.7'
170
164
  if add_hist and whole_buffer and whole_buffer.chomp.size > 0
171
165
  Reline::HISTORY << whole_buffer
172
166
  end
@@ -179,7 +173,7 @@ module Reline
179
173
  inner_readline(prompt, add_hist, false)
180
174
 
181
175
  line = line_editor.line.dup
182
- line.taint
176
+ line.taint if RUBY_VERSION < '2.7'
183
177
  if add_hist and line and line.chomp.size > 0
184
178
  Reline::HISTORY << line.chomp
185
179
  end
@@ -260,7 +254,10 @@ module Reline
260
254
  result = key_stroke.match_status(buffer)
261
255
  case result
262
256
  when :matched
263
- block.(key_stroke.expand(buffer).map{ |c| Reline::Key.new(c, c, false) })
257
+ expanded = key_stroke.expand(buffer).map{ |expanded_c|
258
+ Reline::Key.new(expanded_c, expanded_c, false)
259
+ }
260
+ block.(expanded)
264
261
  break
265
262
  when :matching
266
263
  if buffer.size == 1
@@ -287,16 +284,19 @@ module Reline
287
284
  end
288
285
  when :unmatched
289
286
  if buffer.size == 1 and c == "\e".ord
290
- read_escaped_key(keyseq_timeout, buffer, block)
287
+ read_escaped_key(keyseq_timeout, c, block)
291
288
  else
292
- block.(buffer.map{ |c| Reline::Key.new(c, c, false) })
289
+ expanded = buffer.map{ |expanded_c|
290
+ Reline::Key.new(expanded_c, expanded_c, false)
291
+ }
292
+ block.(expanded)
293
293
  end
294
294
  break
295
295
  end
296
296
  end
297
297
  end
298
298
 
299
- private def read_escaped_key(keyseq_timeout, buffer, block)
299
+ private def read_escaped_key(keyseq_timeout, c, block)
300
300
  begin
301
301
  escaped_c = nil
302
302
  Timeout.timeout(keyseq_timeout / 1000.0) {
@@ -319,7 +319,7 @@ module Reline
319
319
 
320
320
  private def may_req_ambiguous_char_width
321
321
  @ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
322
- return if @ambiguous_width
322
+ return if ambiguous_width
323
323
  Reline::IOGate.move_cursor_column(0)
324
324
  print "\u{25bd}"
325
325
  @ambiguous_width = Reline::IOGate.cursor_pos.x
@@ -342,6 +342,7 @@ module Reline
342
342
  def_single_delegators :core, :vi_editing_mode, :emacs_editing_mode
343
343
  def_single_delegators :core, :readline
344
344
  def_instance_delegators self, :readline
345
+ private :readline
345
346
 
346
347
 
347
348
  #--------------------------------------------------------
@@ -369,6 +370,7 @@ module Reline
369
370
 
370
371
  def_single_delegators :core, :readmultiline
371
372
  def_instance_delegators self, :readmultiline
373
+ private :readmultiline
372
374
 
373
375
  def self.core
374
376
  @core ||= Core.new { |core|
@@ -392,9 +394,15 @@ module Reline
392
394
  HISTORY = History.new(core.config)
393
395
  end
394
396
 
395
- if Reline::Core::IS_WINDOWS
397
+ if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
396
398
  require 'reline/windows'
397
- Reline::IOGate = Reline::Windows
399
+ if Reline::Windows.get_screen_size == [0, 0]
400
+ # Maybe Mintty on Cygwin
401
+ require 'reline/ansi'
402
+ Reline::IOGate = Reline::ANSI
403
+ else
404
+ Reline::IOGate = Reline::Windows
405
+ end
398
406
  else
399
407
  require 'reline/ansi'
400
408
  Reline::IOGate = Reline::ANSI
@@ -7,7 +7,11 @@ class Reline::ANSI
7
7
  [27, 91, 51, 126] => :key_delete, # Del
8
8
  [27, 91, 49, 126] => :ed_move_to_beg, # Home
9
9
  [27, 91, 52, 126] => :ed_move_to_end, # End
10
- }.each_key(&:freeze).freeze
10
+ [27, 91, 72] => :ed_move_to_beg, # Home
11
+ [27, 91, 70] => :ed_move_to_end, # End
12
+ [27, 32] => :em_set_mark, # M-<space>
13
+ [24, 24] => :em_exchange_mark, # C-x C-x TODO also add Windows
14
+ }
11
15
 
12
16
  @@input = STDIN
13
17
  def self.input=(val)
@@ -24,20 +28,22 @@ class Reline::ANSI
24
28
  unless @@buf.empty?
25
29
  return @@buf.shift
26
30
  end
27
- c = nil
28
- loop do
29
- result = select([@@input], [], [], 0.1)
30
- next if result.nil?
31
- c = @@input.read(1)
32
- break
33
- end
34
- c&.ord
31
+ @@input.getbyte
35
32
  end
36
33
 
37
34
  def self.ungetc(c)
38
35
  @@buf.unshift(c)
39
36
  end
40
37
 
38
+ def self.retrieve_keybuffer
39
+ result = select([@@input], [], [], 0.001)
40
+ return if result.nil?
41
+ str = @@input.read_nonblock(1024)
42
+ str.bytes.each do |c|
43
+ @@buf.push(c)
44
+ end
45
+ end
46
+
41
47
  def self.get_screen_size
42
48
  @@input.winsize
43
49
  rescue Errno::ENOTTY
@@ -112,12 +118,17 @@ class Reline::ANSI
112
118
  end
113
119
 
114
120
  def self.prep
121
+ retrieve_keybuffer
115
122
  int_handle = Signal.trap('INT', 'IGNORE')
116
123
  otio = `stty -g`.chomp
117
124
  setting = ' -echo -icrnl cbreak'
118
- if /-parenb\b/ =~ `stty -a`
125
+ stty = `stty -a`
126
+ if /-parenb\b/ =~ stty
119
127
  setting << ' pass8'
120
128
  end
129
+ if /\bdsusp *=/ =~ stty
130
+ setting << ' dsusp undef'
131
+ end
121
132
  setting << ' -ixoff'
122
133
  `stty #{setting}`
123
134
  Signal.trap('INT', int_handle)
@@ -126,7 +137,7 @@ class Reline::ANSI
126
137
 
127
138
  def self.deprep(otio)
128
139
  int_handle = Signal.trap('INT', 'IGNORE')
129
- `stty #{otio}`
140
+ system("stty #{otio}", err: File::NULL)
130
141
  Signal.trap('INT', int_handle)
131
142
  Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
132
143
  end
@@ -1,7 +1,7 @@
1
1
  require 'timeout'
2
2
 
3
3
  class Reline::GeneralIO
4
- RAW_KEYSTROKE_CONFIG = {}.freeze
4
+ RAW_KEYSTROKE_CONFIG = {}
5
5
 
6
6
  @@buf = []
7
7
 
@@ -55,7 +55,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
55
55
  # 26 ^Z
56
56
  :ed_unassigned,
57
57
  # 27 ^[
58
- :em_meta_next,
58
+ :ed_unassigned,
59
59
  # 28 ^\
60
60
  :ed_ignore,
61
61
  # 29 ^]
@@ -37,7 +37,7 @@ class Reline::KeyActor::ViInsert < Reline::KeyActor::Base
37
37
  # 17 ^Q
38
38
  :ed_ignore,
39
39
  # 18 ^R
40
- :ed_insert,
40
+ :ed_search_prev_history,
41
41
  # 19 ^S
42
42
  :ed_ignore,
43
43
  # 20 ^T
@@ -32,7 +32,7 @@ class Reline::KeyStroke
32
32
  end
33
33
 
34
34
  def expand(input)
35
- lhs = key_mapping.keys.select { |lhs| input.start_with? lhs }.sort_by(&:size).reverse.first
35
+ lhs = key_mapping.keys.select { |item| input.start_with? item }.sort_by(&:size).reverse.first
36
36
  return input unless lhs
37
37
  rhs = key_mapping[lhs]
38
38
 
@@ -60,14 +60,35 @@ class Reline::LineEditor
60
60
  reset_variables
61
61
  end
62
62
 
63
+ private def check_multiline_prompt(buffer, prompt)
64
+ if @vi_arg
65
+ prompt = "(arg: #{@vi_arg}) "
66
+ @rerender_all = true
67
+ elsif @searching_prompt
68
+ prompt = @searching_prompt
69
+ @rerender_all = true
70
+ else
71
+ prompt = @prompt
72
+ end
73
+ if @prompt_proc
74
+ prompt_list = @prompt_proc.(buffer)
75
+ prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
76
+ prompt = prompt_list[@line_index]
77
+ prompt_width = calculate_width(prompt, true)
78
+ [prompt, prompt_width, prompt_list]
79
+ else
80
+ prompt_width = calculate_width(prompt, true)
81
+ [prompt, prompt_width, nil]
82
+ end
83
+ end
84
+
63
85
  def reset(prompt = '', encoding = Encoding.default_external)
64
86
  @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
65
87
  @screen_size = Reline::IOGate.get_screen_size
66
88
  reset_variables(prompt, encoding)
67
89
  @old_trap = Signal.trap('SIGINT') {
68
- scroll_down(@highest_in_all - @first_line_started_from)
69
- Reline::IOGate.move_cursor_column(0)
70
90
  @old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
91
+ raise Interrupt
71
92
  }
72
93
  Reline::IOGate.set_winch_handler do
73
94
  @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
@@ -77,28 +98,9 @@ class Reline::LineEditor
77
98
  @rerender_all = true
78
99
  rerender
79
100
  else
80
- special_prompt = nil
81
- if @vi_arg
82
- prompt = "(arg: #{@vi_arg}) "
83
- prompt_width = calculate_width(prompt)
84
- special_prompt = prompt
85
- elsif @searching_prompt
86
- prompt = @searching_prompt
87
- prompt_width = calculate_width(prompt)
88
- special_prompt = prompt
89
- else
90
- prompt = @prompt
91
- prompt_width = calculate_width(prompt, true)
92
- end
93
101
  back = 0
94
102
  new_buffer = whole_lines
95
- prompt_list = nil
96
- if @prompt_proc
97
- prompt_list = @prompt_proc.(new_buffer)
98
- prompt_list[@line_index] = special_prompt if special_prompt
99
- prompt = prompt_list[@line_index]
100
- prompt_width = calculate_width(prompt, true)
101
- end
103
+ prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
102
104
  new_buffer.each_with_index do |line, index|
103
105
  prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
104
106
  width = prompt_width + calculate_width(line)
@@ -111,9 +113,7 @@ class Reline::LineEditor
111
113
  if @line_index.zero?
112
114
  0
113
115
  else
114
- @buffer_of_lines[0..(@line_index - 1)].inject(0) { |result, line|
115
- result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
116
- }
116
+ calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
117
117
  end
118
118
  if @prompt_proc
119
119
  prompt = prompt_list[@line_index]
@@ -138,6 +138,7 @@ class Reline::LineEditor
138
138
 
139
139
  def reset_variables(prompt = '', encoding = Encoding.default_external)
140
140
  @prompt = prompt
141
+ @mark_pointer = nil
141
142
  @encoding = encoding
142
143
  @is_multiline = false
143
144
  @finished = false
@@ -186,6 +187,16 @@ class Reline::LineEditor
186
187
  @is_multiline = false
187
188
  end
188
189
 
190
+ private def calculate_height_by_lines(lines, prompt_list)
191
+ result = 0
192
+ lines.each_with_index { |line, i|
193
+ prompt = ''
194
+ prompt = prompt_list[i] if prompt_list and prompt_list[i]
195
+ result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
196
+ }
197
+ result
198
+ end
199
+
189
200
  private def insert_new_line(cursor_line, next_line)
190
201
  @line = cursor_line
191
202
  @buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding))
@@ -306,36 +317,18 @@ class Reline::LineEditor
306
317
  @menu_info.list.each do |item|
307
318
  Reline::IOGate.move_cursor_column(0)
308
319
  @output.print item
320
+ @output.flush
309
321
  scroll_down(1)
310
322
  end
311
323
  scroll_down(@highest_in_all - 1)
312
324
  move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
313
325
  @menu_info = nil
314
326
  end
315
- special_prompt = nil
316
- if @vi_arg
317
- prompt = "(arg: #{@vi_arg}) "
318
- prompt_width = calculate_width(prompt)
319
- special_prompt = prompt
320
- elsif @searching_prompt
321
- prompt = @searching_prompt
322
- prompt_width = calculate_width(prompt)
323
- special_prompt = prompt
324
- else
325
- prompt = @prompt
326
- prompt_width = calculate_width(prompt, true)
327
- end
327
+ prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
328
328
  if @cleared
329
329
  Reline::IOGate.clear_screen
330
330
  @cleared = false
331
331
  back = 0
332
- prompt_list = nil
333
- if @prompt_proc
334
- prompt_list = @prompt_proc.(whole_lines)
335
- prompt_list[@line_index] = special_prompt if special_prompt
336
- prompt = prompt_list[@line_index]
337
- prompt_width = calculate_width(prompt, true)
338
- end
339
332
  modify_lines(whole_lines).each_with_index do |line, index|
340
333
  if @prompt_proc
341
334
  pr = prompt_list[index]
@@ -361,16 +354,8 @@ class Reline::LineEditor
361
354
  else
362
355
  new_lines = whole_lines
363
356
  end
364
- prompt_list = nil
365
- if @prompt_proc
366
- prompt_list = @prompt_proc.(new_lines)
367
- prompt_list[@line_index] = special_prompt if special_prompt
368
- prompt = prompt_list[@line_index]
369
- prompt_width = calculate_width(prompt, true)
370
- end
371
- all_height = new_lines.inject(0) { |result, line|
372
- result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
373
- }
357
+ prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
358
+ all_height = calculate_height_by_lines(new_lines, prompt_list)
374
359
  diff = all_height - @highest_in_all
375
360
  move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
376
361
  if diff > 0
@@ -410,9 +395,7 @@ class Reline::LineEditor
410
395
  if @line_index.zero?
411
396
  0
412
397
  else
413
- @buffer_of_lines[0..(@line_index - 1)].inject(0) { |result, line|
414
- result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
415
- }
398
+ calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
416
399
  end
417
400
  if @prompt_proc
418
401
  prompt = prompt_list[@line_index]
@@ -431,13 +414,7 @@ class Reline::LineEditor
431
414
  Reline::IOGate.move_cursor_column(0)
432
415
  back = 0
433
416
  new_buffer = whole_lines
434
- prompt_list = nil
435
- if @prompt_proc
436
- prompt_list = @prompt_proc.(new_buffer)
437
- prompt_list[@line_index] = special_prompt if special_prompt
438
- prompt = prompt_list[@line_index]
439
- prompt_width = calculate_width(prompt, true)
440
- end
417
+ prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
441
418
  new_buffer.each_with_index do |line, index|
442
419
  prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
443
420
  width = prompt_width + calculate_width(line)
@@ -477,9 +454,7 @@ class Reline::LineEditor
477
454
  if @line_index.zero?
478
455
  0
479
456
  else
480
- new_buffer[0..(@line_index - 1)].inject(0) { |result, line|
481
- result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
482
- }
457
+ calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list)
483
458
  end
484
459
  @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
485
460
  move_cursor_down(@first_line_started_from + @started_from)
@@ -489,13 +464,7 @@ class Reline::LineEditor
489
464
  end
490
465
  line = modify_lines(whole_lines)[@line_index]
491
466
  if @is_multiline
492
- prompt_list = nil
493
- if @prompt_proc
494
- prompt_list = @prompt_proc.(whole_lines)
495
- prompt_list[@line_index] = special_prompt if special_prompt
496
- prompt = prompt_list[@line_index]
497
- prompt_width = calculate_width(prompt, true)
498
- end
467
+ prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
499
468
  if finished?
500
469
  # Always rerender on finish because output_modifier_proc may return a different output.
501
470
  render_partial(prompt, prompt_width, line)
@@ -541,6 +510,7 @@ class Reline::LineEditor
541
510
  next
542
511
  end
543
512
  @output.print line
513
+ @output.flush
544
514
  if @first_prompt
545
515
  @first_prompt = false
546
516
  @pre_input_hook&.call
@@ -683,9 +653,9 @@ class Reline::LineEditor
683
653
  else
684
654
  old_waiting_proc = @waiting_proc
685
655
  old_waiting_operator_proc = @waiting_operator_proc
686
- @waiting_proc = proc { |key|
656
+ @waiting_proc = proc { |k|
687
657
  old_cursor, old_byte_pointer = @cursor, @byte_pointer
688
- old_waiting_proc.(key)
658
+ old_waiting_proc.(k)
689
659
  cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
690
660
  @cursor, @byte_pointer = old_cursor, old_byte_pointer
691
661
  @waiting_operator_proc.(cursor_diff, byte_pointer_diff)
@@ -796,7 +766,7 @@ class Reline::LineEditor
796
766
  end
797
767
 
798
768
  def input_key(key)
799
- if key.nil? or key.char.nil?
769
+ if key.char.nil?
800
770
  if @first_char
801
771
  @line = nil
802
772
  end
@@ -836,6 +806,26 @@ class Reline::LineEditor
836
806
 
837
807
  private def process_auto_indent
838
808
  return if not @check_new_auto_indent and @previous_line_index # move cursor up or down
809
+ if @check_new_auto_indent and @previous_line_index and @previous_line_index > 0 and @line_index > @previous_line_index
810
+ # Fix indent of a line when a newline is inserted to the next
811
+ new_lines = whole_lines(index: @previous_line_index, line: @line)
812
+ new_indent = @auto_indent_proc.(new_lines[0..-3].push(''), @line_index - 1, 0, true)
813
+ md = @line.match(/\A */)
814
+ prev_indent = md[0].count(' ')
815
+ @line = ' ' * new_indent + @line.lstrip
816
+
817
+ new_indent = nil
818
+ (new_lines[-2].size + 1).times do |n|
819
+ result = @auto_indent_proc.(new_lines[0..-2], @line_index - 1, n, false)
820
+ if result
821
+ new_indent = result
822
+ break
823
+ end
824
+ end
825
+ if new_indent&.>= 0
826
+ @line = ' ' * new_indent + @line.lstrip
827
+ end
828
+ end
839
829
  if @previous_line_index
840
830
  new_lines = whole_lines(index: @previous_line_index, line: @line)
841
831
  else
@@ -1027,8 +1017,8 @@ class Reline::LineEditor
1027
1017
  end
1028
1018
  width
1029
1019
  else
1030
- str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |width, gc|
1031
- width + Reline::Unicode.get_mbchar_width(gc)
1020
+ str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |w, gc|
1021
+ w + Reline::Unicode.get_mbchar_width(gc)
1032
1022
  }
1033
1023
  end
1034
1024
  end
@@ -1049,6 +1039,8 @@ class Reline::LineEditor
1049
1039
  end
1050
1040
  end
1051
1041
 
1042
+ private def ed_unassigned(key) end # do nothing
1043
+
1052
1044
  private def ed_insert(key)
1053
1045
  if key.instance_of?(String)
1054
1046
  width = Reline::Unicode.get_mbchar_width(key)
@@ -1153,7 +1145,11 @@ class Reline::LineEditor
1153
1145
  alias_method :end_of_line, :ed_move_to_end
1154
1146
 
1155
1147
  private def ed_search_prev_history(key)
1156
- @line_backup_in_history = @line
1148
+ if @is_multiline
1149
+ @line_backup_in_history = whole_buffer
1150
+ else
1151
+ @line_backup_in_history = @line
1152
+ end
1157
1153
  searcher = Fiber.new do
1158
1154
  search_word = String.new(encoding: @encoding)
1159
1155
  multibyte_buf = String.new(encoding: 'ASCII-8BIT')
@@ -1188,18 +1184,32 @@ class Reline::LineEditor
1188
1184
  end
1189
1185
  end
1190
1186
  if hit
1191
- @searching_prompt = "(reverse-i-search)`%s': %s" % [search_word, hit]
1192
- @line = hit
1187
+ if @is_multiline
1188
+ @buffer_of_lines = hit.split("\n")
1189
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1190
+ @line_index = @buffer_of_lines.size - 1
1191
+ @line = @buffer_of_lines.last
1192
+ @rerender_all = true
1193
+ @searching_prompt = "(reverse-i-search)`%s'" % [search_word]
1194
+ else
1195
+ @line = hit
1196
+ @searching_prompt = "(reverse-i-search)`%s': %s" % [search_word, hit]
1197
+ end
1193
1198
  last_hit = hit
1194
1199
  else
1195
- @searching_prompt = "(failed reverse-i-search)`%s': %s" % [search_word, last_hit]
1200
+ if @is_multiline
1201
+ @rerender_all = true
1202
+ @searching_prompt = "(failed reverse-i-search)`%s'" % [search_word]
1203
+ else
1204
+ @searching_prompt = "(failed reverse-i-search)`%s': %s" % [search_word, last_hit]
1205
+ end
1196
1206
  end
1197
1207
  end
1198
1208
  end
1199
1209
  searcher.resume
1200
1210
  @searching_prompt = "(reverse-i-search)`': "
1201
- @waiting_proc = ->(key) {
1202
- case key
1211
+ @waiting_proc = ->(k) {
1212
+ case k
1203
1213
  when "\C-j".ord, "\C-?".ord
1204
1214
  if @history_pointer
1205
1215
  @line = Reline::HISTORY[@history_pointer]
@@ -1219,14 +1229,25 @@ class Reline::LineEditor
1219
1229
  @cursor_max = calculate_width(@line)
1220
1230
  @cursor = @byte_pointer = 0
1221
1231
  else
1222
- chr = key.is_a?(String) ? key : key.chr(Encoding::ASCII_8BIT)
1223
- if chr.match?(/[[:print:]]/)
1224
- searcher.resume(key)
1232
+ chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
1233
+ if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == 127
1234
+ searcher.resume(k)
1225
1235
  else
1226
1236
  if @history_pointer
1227
- @line = Reline::HISTORY[@history_pointer]
1237
+ line = Reline::HISTORY[@history_pointer]
1228
1238
  else
1229
- @line = @line_backup_in_history
1239
+ line = @line_backup_in_history
1240
+ end
1241
+ if @is_multiline
1242
+ @line_backup_in_history = whole_buffer
1243
+ @buffer_of_lines = line.split("\n")
1244
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1245
+ @line_index = @buffer_of_lines.size - 1
1246
+ @line = @buffer_of_lines.last
1247
+ @rerender_all = true
1248
+ else
1249
+ @line_backup_in_history = @line
1250
+ @line = line
1230
1251
  end
1231
1252
  @searching_prompt = nil
1232
1253
  @waiting_proc = nil
@@ -1279,7 +1300,7 @@ class Reline::LineEditor
1279
1300
  @line = Reline::HISTORY[@history_pointer]
1280
1301
  end
1281
1302
  end
1282
- if @config.editing_mode_is?(:emacs)
1303
+ if @config.editing_mode_is?(:emacs, :vi_insert)
1283
1304
  @cursor_max = @cursor = calculate_width(@line)
1284
1305
  @byte_pointer = @line.bytesize
1285
1306
  elsif @config.editing_mode_is?(:vi_command)
@@ -1326,7 +1347,7 @@ class Reline::LineEditor
1326
1347
  end
1327
1348
  end
1328
1349
  @line = '' unless @line
1329
- if @config.editing_mode_is?(:emacs)
1350
+ if @config.editing_mode_is?(:emacs, :vi_insert)
1330
1351
  @cursor_max = @cursor = calculate_width(@line)
1331
1352
  @byte_pointer = @line.bytesize
1332
1353
  elsif @config.editing_mode_is?(:vi_command)
@@ -1863,13 +1884,13 @@ class Reline::LineEditor
1863
1884
  end
1864
1885
 
1865
1886
  private def vi_replace_char(key, arg: 1)
1866
- @waiting_proc = ->(key) {
1887
+ @waiting_proc = ->(k) {
1867
1888
  if arg == 1
1868
1889
  byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
1869
1890
  before = @line.byteslice(0, @byte_pointer)
1870
1891
  remaining_point = @byte_pointer + byte_size
1871
1892
  after = @line.byteslice(remaining_point, @line.size - remaining_point)
1872
- @line = before + key.chr + after
1893
+ @line = before + k.chr + after
1873
1894
  @cursor_max = calculate_width(@line)
1874
1895
  @waiting_proc = nil
1875
1896
  elsif arg > 1
@@ -1880,7 +1901,7 @@ class Reline::LineEditor
1880
1901
  before = @line.byteslice(0, @byte_pointer)
1881
1902
  remaining_point = @byte_pointer + byte_size
1882
1903
  after = @line.byteslice(remaining_point, @line.size - remaining_point)
1883
- replaced = key.chr * arg
1904
+ replaced = k.chr * arg
1884
1905
  @line = before + replaced + after
1885
1906
  @byte_pointer += replaced.bytesize
1886
1907
  @cursor += calculate_width(replaced)
@@ -1941,4 +1962,20 @@ class Reline::LineEditor
1941
1962
  arg -= 1
1942
1963
  vi_join_lines(key, arg: arg) if arg > 0
1943
1964
  end
1965
+
1966
+ private def em_set_mark(key)
1967
+ @mark_pointer = [@byte_pointer, @line_index]
1968
+ end
1969
+ alias_method :set_mark, :em_set_mark
1970
+
1971
+ private def em_exchange_mark(key)
1972
+ new_pointer = [@byte_pointer, @line_index]
1973
+ @previous_line_index = @line_index
1974
+ @byte_pointer, @line_index = @mark_pointer
1975
+ @byte_pointer, @line_index = @mark_pointer
1976
+ @cursor = calculate_width(@line.byteslice(0, @byte_pointer))
1977
+ @cursor_max = calculate_width(@line)
1978
+ @mark_pointer = new_pointer
1979
+ end
1980
+ alias_method :exchange_point_and_mark, :em_exchange_mark
1944
1981
  end
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.7'
3
3
  end
@@ -9,47 +9,61 @@ class Reline::Windows
9
9
  [224, 83] => :key_delete, # Del
10
10
  [224, 71] => :ed_move_to_beg, # Home
11
11
  [224, 79] => :ed_move_to_end, # End
12
- }.each_key(&:freeze).freeze
13
-
14
- class Win32API
15
- DLL = {}
16
- TYPEMAP = {"0" => Fiddle::TYPE_VOID, "S" => Fiddle::TYPE_VOIDP, "I" => Fiddle::TYPE_LONG}
17
- POINTER_TYPE = Fiddle::SIZEOF_VOIDP == Fiddle::SIZEOF_LONG_LONG ? 'q*' : 'l!*'
18
-
19
- WIN32_TYPES = "VPpNnLlIi"
20
- DL_TYPES = "0SSI"
21
-
22
- def initialize(dllname, func, import, export = "0", calltype = :stdcall)
23
- @proto = [import].join.tr(WIN32_TYPES, DL_TYPES).sub(/^(.)0*$/, '\1')
24
- import = @proto.chars.map {|win_type| TYPEMAP[win_type.tr(WIN32_TYPES, DL_TYPES)]}
25
- export = TYPEMAP[export.tr(WIN32_TYPES, DL_TYPES)]
26
- calltype = Fiddle::Importer.const_get(:CALL_TYPE_TO_ABI)[calltype]
27
-
28
- handle = DLL[dllname] ||=
29
- begin
30
- Fiddle.dlopen(dllname)
31
- rescue Fiddle::DLError
32
- raise unless File.extname(dllname).empty?
33
- Fiddle.dlopen(dllname + ".dll")
34
- end
35
-
36
- @func = Fiddle::Function.new(handle[func], import, export, calltype)
37
- rescue Fiddle::DLError => e
38
- raise LoadError, e.message, e.backtrace
39
- end
12
+ [ 0, 41] => :ed_unassigned, # input method on/off
13
+ [ 0, 72] => :ed_prev_history, # ↑
14
+ [ 0, 80] => :ed_next_history, # ↓
15
+ [ 0, 77] => :ed_next_char, # →
16
+ [ 0, 75] => :ed_prev_char, #
17
+ [ 0, 83] => :key_delete, # Del
18
+ [ 0, 71] => :ed_move_to_beg, # Home
19
+ [ 0, 79] => :ed_move_to_end # End
20
+ }
21
+
22
+ if defined? JRUBY_VERSION
23
+ require 'win32api'
24
+ else
25
+ class Win32API
26
+ DLL = {}
27
+ TYPEMAP = {"0" => Fiddle::TYPE_VOID, "S" => Fiddle::TYPE_VOIDP, "I" => Fiddle::TYPE_LONG}
28
+ POINTER_TYPE = Fiddle::SIZEOF_VOIDP == Fiddle::SIZEOF_LONG_LONG ? 'q*' : 'l!*'
29
+
30
+ WIN32_TYPES = "VPpNnLlIi"
31
+ DL_TYPES = "0SSI"
32
+
33
+ def initialize(dllname, func, import, export = "0", calltype = :stdcall)
34
+ @proto = [import].join.tr(WIN32_TYPES, DL_TYPES).sub(/^(.)0*$/, '\1')
35
+ import = @proto.chars.map {|win_type| TYPEMAP[win_type.tr(WIN32_TYPES, DL_TYPES)]}
36
+ export = TYPEMAP[export.tr(WIN32_TYPES, DL_TYPES)]
37
+ calltype = Fiddle::Importer.const_get(:CALL_TYPE_TO_ABI)[calltype]
38
+
39
+ handle = DLL[dllname] ||=
40
+ begin
41
+ Fiddle.dlopen(dllname)
42
+ rescue Fiddle::DLError
43
+ raise unless File.extname(dllname).empty?
44
+ Fiddle.dlopen(dllname + ".dll")
45
+ end
46
+
47
+ @func = Fiddle::Function.new(handle[func], import, export, calltype)
48
+ rescue Fiddle::DLError => e
49
+ raise LoadError, e.message, e.backtrace
50
+ end
40
51
 
41
- def call(*args)
42
- import = @proto.split("")
43
- args.each_with_index do |x, i|
44
- args[i], = [x == 0 ? nil : x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
45
- args[i], = [x].pack("I").unpack("i") if import[i] == "I"
52
+ def call(*args)
53
+ import = @proto.split("")
54
+ args.each_with_index do |x, i|
55
+ args[i], = [x == 0 ? nil : x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
56
+ args[i], = [x].pack("I").unpack("i") if import[i] == "I"
57
+ end
58
+ ret, = @func.call(*args)
59
+ return ret || 0
46
60
  end
47
- ret, = @func.call(*args)
48
- return ret || 0
49
61
  end
50
62
  end
51
63
 
52
64
  VK_MENU = 0x12
65
+ VK_LMENU = 0xA4
66
+ VK_CONTROL = 0x11
53
67
  VK_SHIFT = 0x10
54
68
  STD_INPUT_HANDLE = -10
55
69
  STD_OUTPUT_HANDLE = -11
@@ -66,23 +80,33 @@ class Reline::Windows
66
80
  @@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE)
67
81
  @@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L')
68
82
  @@ReadConsoleInput = Win32API.new('kernel32', 'ReadConsoleInput', ['L', 'P', 'L', 'P'], 'L')
69
- @@buf = []
83
+ @@input_buf = []
84
+ @@output_buf = []
70
85
 
71
86
  def self.getwch
87
+ unless @@input_buf.empty?
88
+ return @@input_buf.shift
89
+ end
72
90
  while @@kbhit.call == 0
73
91
  sleep(0.001)
74
92
  end
75
- result = []
76
93
  until @@kbhit.call == 0
77
94
  ret = @@getwch.call
95
+ if ret == 0 or ret == 0xE0
96
+ @@input_buf << ret
97
+ ret = @@getwch.call
98
+ @@input_buf << ret
99
+ return @@input_buf.shift
100
+ end
78
101
  begin
79
- result.concat(ret.chr(Encoding::UTF_8).encode(Encoding.default_external).bytes)
102
+ bytes = ret.chr(Encoding::UTF_8).encode(Encoding.default_external).bytes
103
+ @@input_buf.push(*bytes)
80
104
  rescue Encoding::UndefinedConversionError
81
- result << ret
82
- result << @@getwch.call if ret == 224
105
+ @@input_buf << ret
106
+ @@input_buf << @@getwch.call if ret == 224
83
107
  end
84
108
  end
85
- result
109
+ @@input_buf.shift
86
110
  end
87
111
 
88
112
  def self.getc
@@ -97,44 +121,44 @@ class Reline::Windows
97
121
  end
98
122
  end
99
123
  end
100
- unless @@buf.empty?
101
- return @@buf.shift
124
+ unless @@output_buf.empty?
125
+ return @@output_buf.shift
102
126
  end
103
127
  input = getwch
104
- alt = (@@GetKeyState.call(VK_MENU) & 0x80) != 0
105
- shift_enter = (@@GetKeyState.call(VK_SHIFT) & 0x80) != 0 && input.first == 0x0D
106
- if shift_enter
128
+ meta = (@@GetKeyState.call(VK_LMENU) & 0x80) != 0
129
+ control = (@@GetKeyState.call(VK_CONTROL) & 0x80) != 0
130
+ shift = (@@GetKeyState.call(VK_SHIFT) & 0x80) != 0
131
+ force_enter = !input.instance_of?(Array) && (control or shift) && input == 0x0D
132
+ if force_enter
107
133
  # It's treated as Meta+Enter on Windows
108
- @@buf.concat(["\e".ord])
109
- @@buf.concat(input)
110
- elsif input.size > 1
111
- @@buf.concat(input)
112
- else # single byte
113
- case input[0]
134
+ @@output_buf.push("\e".ord)
135
+ @@output_buf.push(input)
136
+ else
137
+ case input
114
138
  when 0x00
115
- getwch
116
- alt = false
139
+ meta = false
140
+ @@output_buf.push(input)
117
141
  input = getwch
118
- @@buf.concat(input)
142
+ @@output_buf.push(*input)
119
143
  when 0xE0
120
- @@buf.concat(input)
144
+ @@output_buf.push(input)
121
145
  input = getwch
122
- @@buf.concat(input)
146
+ @@output_buf.push(*input)
123
147
  when 0x03
124
- @@buf.concat(input)
148
+ @@output_buf.push(input)
125
149
  else
126
- @@buf.concat(input)
150
+ @@output_buf.push(input)
127
151
  end
128
152
  end
129
- if alt
153
+ if meta
130
154
  "\e".ord
131
155
  else
132
- @@buf.shift
156
+ @@output_buf.shift
133
157
  end
134
158
  end
135
159
 
136
160
  def self.ungetc(c)
137
- @@buf.unshift(c)
161
+ @@output_buf.unshift(c)
138
162
  end
139
163
 
140
164
  def self.get_screen_size
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.0.2
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-01 00:00:00.000000000 Z
11
+ date: 2019-11-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler