reline 0.2.5 → 0.3.1
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 +4 -4
- data/README.md +46 -0
- data/lib/reline/ansi.rb +153 -62
- data/lib/reline/config.rb +62 -14
- data/lib/reline/general_io.rb +14 -4
- data/lib/reline/key_actor/base.rb +12 -0
- data/lib/reline/key_actor/emacs.rb +1 -1
- data/lib/reline/key_stroke.rb +64 -14
- data/lib/reline/line_editor.rb +638 -74
- data/lib/reline/terminfo.rb +171 -0
- data/lib/reline/unicode.rb +42 -3
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +281 -112
- data/lib/reline.rb +150 -35
- metadata +4 -59
data/lib/reline/line_editor.rb
CHANGED
@@ -5,6 +5,7 @@ require 'tempfile'
|
|
5
5
|
|
6
6
|
class Reline::LineEditor
|
7
7
|
# TODO: undo
|
8
|
+
# TODO: Use "private alias_method" idiom after drop Ruby 2.5.
|
8
9
|
attr_reader :line
|
9
10
|
attr_reader :byte_pointer
|
10
11
|
attr_accessor :confirm_multiline_termination_proc
|
@@ -92,7 +93,7 @@ class Reline::LineEditor
|
|
92
93
|
mode_string
|
93
94
|
end
|
94
95
|
|
95
|
-
private def check_multiline_prompt(buffer
|
96
|
+
private def check_multiline_prompt(buffer)
|
96
97
|
if @vi_arg
|
97
98
|
prompt = "(arg: #{@vi_arg}) "
|
98
99
|
@rerender_all = true
|
@@ -120,7 +121,7 @@ class Reline::LineEditor
|
|
120
121
|
if use_cached_prompt_list
|
121
122
|
prompt_list = @cached_prompt_list
|
122
123
|
else
|
123
|
-
prompt_list = @cached_prompt_list = @prompt_proc.(buffer)
|
124
|
+
prompt_list = @cached_prompt_list = @prompt_proc.(buffer).map { |pr| pr.gsub("\n", "\\n") }
|
124
125
|
@prompt_cache_time = Time.now.to_f
|
125
126
|
end
|
126
127
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
@@ -150,7 +151,75 @@ class Reline::LineEditor
|
|
150
151
|
@screen_size = Reline::IOGate.get_screen_size
|
151
152
|
@screen_height = @screen_size.first
|
152
153
|
reset_variables(prompt, encoding: encoding)
|
153
|
-
|
154
|
+
Reline::IOGate.set_winch_handler do
|
155
|
+
@resized = true
|
156
|
+
end
|
157
|
+
if ENV.key?('RELINE_ALT_SCROLLBAR')
|
158
|
+
@full_block = '::'
|
159
|
+
@upper_half_block = "''"
|
160
|
+
@lower_half_block = '..'
|
161
|
+
@block_elem_width = 2
|
162
|
+
elsif Reline::IOGate.win?
|
163
|
+
@full_block = '█'
|
164
|
+
@upper_half_block = '▀'
|
165
|
+
@lower_half_block = '▄'
|
166
|
+
@block_elem_width = 1
|
167
|
+
elsif @encoding == Encoding::UTF_8
|
168
|
+
@full_block = '█'
|
169
|
+
@upper_half_block = '▀'
|
170
|
+
@lower_half_block = '▄'
|
171
|
+
@block_elem_width = Reline::Unicode.calculate_width('█')
|
172
|
+
else
|
173
|
+
@full_block = '::'
|
174
|
+
@upper_half_block = "''"
|
175
|
+
@lower_half_block = '..'
|
176
|
+
@block_elem_width = 2
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def resize
|
181
|
+
return unless @resized
|
182
|
+
@resized = false
|
183
|
+
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
184
|
+
old_screen_size = @screen_size
|
185
|
+
@screen_size = Reline::IOGate.get_screen_size
|
186
|
+
@screen_height = @screen_size.first
|
187
|
+
if old_screen_size.last < @screen_size.last # columns increase
|
188
|
+
@rerender_all = true
|
189
|
+
rerender
|
190
|
+
else
|
191
|
+
back = 0
|
192
|
+
new_buffer = whole_lines
|
193
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer)
|
194
|
+
new_buffer.each_with_index do |line, index|
|
195
|
+
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
196
|
+
width = prompt_width + calculate_width(line)
|
197
|
+
height = calculate_height_by_width(width)
|
198
|
+
back += height
|
199
|
+
end
|
200
|
+
@highest_in_all = back
|
201
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
202
|
+
@first_line_started_from =
|
203
|
+
if @line_index.zero?
|
204
|
+
0
|
205
|
+
else
|
206
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
207
|
+
end
|
208
|
+
if @prompt_proc
|
209
|
+
prompt = prompt_list[@line_index]
|
210
|
+
prompt_width = calculate_width(prompt, true)
|
211
|
+
end
|
212
|
+
calculate_nearest_cursor
|
213
|
+
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
214
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
215
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
216
|
+
@rerender_all = true
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def set_signal_handlers
|
221
|
+
@old_trap = Signal.trap('INT') {
|
222
|
+
clear_dialog
|
154
223
|
if @scroll_partial_screen
|
155
224
|
move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1)
|
156
225
|
else
|
@@ -158,50 +227,32 @@ class Reline::LineEditor
|
|
158
227
|
end
|
159
228
|
Reline::IOGate.move_cursor_column(0)
|
160
229
|
scroll_down(1)
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
@screen_height = @screen_size.first
|
169
|
-
if old_screen_size.last < @screen_size.last # columns increase
|
170
|
-
@rerender_all = true
|
171
|
-
rerender
|
230
|
+
case @old_trap
|
231
|
+
when 'DEFAULT', 'SYSTEM_DEFAULT'
|
232
|
+
raise Interrupt
|
233
|
+
when 'IGNORE'
|
234
|
+
# Do nothing
|
235
|
+
when 'EXIT'
|
236
|
+
exit
|
172
237
|
else
|
173
|
-
|
174
|
-
new_buffer = whole_lines
|
175
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
176
|
-
new_buffer.each_with_index do |line, index|
|
177
|
-
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
178
|
-
width = prompt_width + calculate_width(line)
|
179
|
-
height = calculate_height_by_width(width)
|
180
|
-
back += height
|
181
|
-
end
|
182
|
-
@highest_in_all = back
|
183
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
184
|
-
@first_line_started_from =
|
185
|
-
if @line_index.zero?
|
186
|
-
0
|
187
|
-
else
|
188
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
189
|
-
end
|
190
|
-
if @prompt_proc
|
191
|
-
prompt = prompt_list[@line_index]
|
192
|
-
prompt_width = calculate_width(prompt, true)
|
193
|
-
end
|
194
|
-
calculate_nearest_cursor
|
195
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
196
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
197
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
198
|
-
@rerender_all = true
|
238
|
+
@old_trap.call if @old_trap.respond_to?(:call)
|
199
239
|
end
|
240
|
+
}
|
241
|
+
begin
|
242
|
+
@old_tstp_trap = Signal.trap('TSTP') {
|
243
|
+
Reline::IOGate.ungetc("\C-z".ord)
|
244
|
+
@old_tstp_trap.call if @old_tstp_trap.respond_to?(:call)
|
245
|
+
}
|
246
|
+
rescue ArgumentError
|
200
247
|
end
|
201
248
|
end
|
202
249
|
|
203
250
|
def finalize
|
204
|
-
Signal.trap('
|
251
|
+
Signal.trap('INT', @old_trap)
|
252
|
+
begin
|
253
|
+
Signal.trap('TSTP', @old_tstp_trap)
|
254
|
+
rescue ArgumentError
|
255
|
+
end
|
205
256
|
end
|
206
257
|
|
207
258
|
def eof?
|
@@ -209,7 +260,7 @@ class Reline::LineEditor
|
|
209
260
|
end
|
210
261
|
|
211
262
|
def reset_variables(prompt = '', encoding:)
|
212
|
-
@prompt = prompt
|
263
|
+
@prompt = prompt.gsub("\n", "\\n")
|
213
264
|
@mark_pointer = nil
|
214
265
|
@encoding = encoding
|
215
266
|
@is_multiline = false
|
@@ -241,6 +292,9 @@ class Reline::LineEditor
|
|
241
292
|
@drop_terminate_spaces = false
|
242
293
|
@in_pasting = false
|
243
294
|
@auto_indent_proc = nil
|
295
|
+
@dialogs = []
|
296
|
+
@last_key = nil
|
297
|
+
@resized = false
|
244
298
|
reset_line
|
245
299
|
end
|
246
300
|
|
@@ -384,7 +438,7 @@ class Reline::LineEditor
|
|
384
438
|
show_menu
|
385
439
|
@menu_info = nil
|
386
440
|
end
|
387
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines
|
441
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
|
388
442
|
if @cleared
|
389
443
|
clear_screen_buffer(prompt, prompt_list, prompt_width)
|
390
444
|
@cleared = false
|
@@ -395,7 +449,7 @@ class Reline::LineEditor
|
|
395
449
|
Reline::IOGate.move_cursor_up(@first_line_started_from + @started_from - @scroll_partial_screen)
|
396
450
|
Reline::IOGate.move_cursor_column(0)
|
397
451
|
@scroll_partial_screen = nil
|
398
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines
|
452
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
|
399
453
|
if @previous_line_index
|
400
454
|
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
401
455
|
else
|
@@ -406,10 +460,10 @@ class Reline::LineEditor
|
|
406
460
|
Reline::IOGate.erase_after_cursor
|
407
461
|
end
|
408
462
|
@output.flush
|
463
|
+
clear_dialog
|
409
464
|
return
|
410
465
|
end
|
411
466
|
new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
|
412
|
-
# FIXME: end of logical line sometimes breaks
|
413
467
|
rendered = false
|
414
468
|
if @add_newline_to_end_of_buffer
|
415
469
|
rerender_added_newline(prompt, prompt_width)
|
@@ -417,6 +471,7 @@ class Reline::LineEditor
|
|
417
471
|
else
|
418
472
|
if @just_cursor_moving and not @rerender_all
|
419
473
|
rendered = just_move_cursor
|
474
|
+
render_dialog((prompt_width + @cursor) % @screen_size.last)
|
420
475
|
@just_cursor_moving = false
|
421
476
|
return
|
422
477
|
elsif @previous_line_index or new_highest_in_this != @highest_in_this
|
@@ -439,18 +494,20 @@ class Reline::LineEditor
|
|
439
494
|
new_lines = whole_lines
|
440
495
|
end
|
441
496
|
line = modify_lines(new_lines)[@line_index]
|
442
|
-
|
497
|
+
clear_dialog
|
498
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
|
443
499
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
444
500
|
move_cursor_down(@highest_in_all - (@first_line_started_from + @highest_in_this - 1) - 1)
|
445
501
|
scroll_down(1)
|
446
502
|
Reline::IOGate.move_cursor_column(0)
|
447
503
|
Reline::IOGate.erase_after_cursor
|
448
|
-
|
449
|
-
|
504
|
+
else
|
505
|
+
if not rendered and not @in_pasting
|
450
506
|
line = modify_lines(whole_lines)[@line_index]
|
451
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines
|
507
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
|
452
508
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
453
509
|
end
|
510
|
+
render_dialog((prompt_width + @cursor) % @screen_size.last)
|
454
511
|
end
|
455
512
|
@buffer_of_lines[@line_index] = @line
|
456
513
|
@rest_height = 0 if @scroll_partial_screen
|
@@ -465,6 +522,405 @@ class Reline::LineEditor
|
|
465
522
|
end
|
466
523
|
end
|
467
524
|
|
525
|
+
class DialogProcScope
|
526
|
+
def initialize(line_editor, config, proc_to_exec, context)
|
527
|
+
@line_editor = line_editor
|
528
|
+
@config = config
|
529
|
+
@proc_to_exec = proc_to_exec
|
530
|
+
@context = context
|
531
|
+
@cursor_pos = Reline::CursorPos.new
|
532
|
+
end
|
533
|
+
|
534
|
+
def context
|
535
|
+
@context
|
536
|
+
end
|
537
|
+
|
538
|
+
def retrieve_completion_block(set_completion_quote_character = false)
|
539
|
+
@line_editor.retrieve_completion_block(set_completion_quote_character)
|
540
|
+
end
|
541
|
+
|
542
|
+
def call_completion_proc_with_checking_args(pre, target, post)
|
543
|
+
@line_editor.call_completion_proc_with_checking_args(pre, target, post)
|
544
|
+
end
|
545
|
+
|
546
|
+
def set_dialog(dialog)
|
547
|
+
@dialog = dialog
|
548
|
+
end
|
549
|
+
|
550
|
+
def dialog
|
551
|
+
@dialog
|
552
|
+
end
|
553
|
+
|
554
|
+
def set_cursor_pos(col, row)
|
555
|
+
@cursor_pos.x = col
|
556
|
+
@cursor_pos.y = row
|
557
|
+
end
|
558
|
+
|
559
|
+
def set_key(key)
|
560
|
+
@key = key
|
561
|
+
end
|
562
|
+
|
563
|
+
def key
|
564
|
+
@key
|
565
|
+
end
|
566
|
+
|
567
|
+
def cursor_pos
|
568
|
+
@cursor_pos
|
569
|
+
end
|
570
|
+
|
571
|
+
def just_cursor_moving
|
572
|
+
@line_editor.instance_variable_get(:@just_cursor_moving)
|
573
|
+
end
|
574
|
+
|
575
|
+
def screen_width
|
576
|
+
@line_editor.instance_variable_get(:@screen_size).last
|
577
|
+
end
|
578
|
+
|
579
|
+
def completion_journey_data
|
580
|
+
@line_editor.instance_variable_get(:@completion_journey_data)
|
581
|
+
end
|
582
|
+
|
583
|
+
def config
|
584
|
+
@config
|
585
|
+
end
|
586
|
+
|
587
|
+
def call
|
588
|
+
instance_exec(&@proc_to_exec)
|
589
|
+
end
|
590
|
+
end
|
591
|
+
|
592
|
+
class Dialog
|
593
|
+
attr_reader :name, :contents, :width
|
594
|
+
attr_accessor :scroll_top, :scrollbar_pos, :pointer, :column, :vertical_offset, :lines_backup, :trap_key
|
595
|
+
|
596
|
+
def initialize(name, config, proc_scope)
|
597
|
+
@name = name
|
598
|
+
@config = config
|
599
|
+
@proc_scope = proc_scope
|
600
|
+
@width = nil
|
601
|
+
@scroll_top = 0
|
602
|
+
@trap_key = nil
|
603
|
+
end
|
604
|
+
|
605
|
+
def set_cursor_pos(col, row)
|
606
|
+
@proc_scope.set_cursor_pos(col, row)
|
607
|
+
end
|
608
|
+
|
609
|
+
def width=(v)
|
610
|
+
@width = v
|
611
|
+
end
|
612
|
+
|
613
|
+
def contents=(contents)
|
614
|
+
@contents = contents
|
615
|
+
if contents and @width.nil?
|
616
|
+
@width = contents.map{ |line| Reline::Unicode.calculate_width(line, true) }.max
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
def call(key)
|
621
|
+
@proc_scope.set_dialog(self)
|
622
|
+
@proc_scope.set_key(key)
|
623
|
+
dialog_render_info = @proc_scope.call
|
624
|
+
if @trap_key
|
625
|
+
if @trap_key.any?{ |i| i.is_a?(Array) } # multiple trap
|
626
|
+
@trap_key.each do |t|
|
627
|
+
@config.add_oneshot_key_binding(t, @name)
|
628
|
+
end
|
629
|
+
elsif @trap_key.is_a?(Array)
|
630
|
+
@config.add_oneshot_key_binding(@trap_key, @name)
|
631
|
+
elsif @trap_key.is_a?(Integer) or @trap_key.is_a?(Reline::Key)
|
632
|
+
@config.add_oneshot_key_binding([@trap_key], @name)
|
633
|
+
end
|
634
|
+
end
|
635
|
+
dialog_render_info
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
def add_dialog_proc(name, p, context = nil)
|
640
|
+
dialog = Dialog.new(name, @config, DialogProcScope.new(self, @config, p, context))
|
641
|
+
if index = @dialogs.find_index { |d| d.name == name }
|
642
|
+
@dialogs[index] = dialog
|
643
|
+
else
|
644
|
+
@dialogs << dialog
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
DIALOG_DEFAULT_HEIGHT = 20
|
649
|
+
private def render_dialog(cursor_column)
|
650
|
+
@dialogs.each do |dialog|
|
651
|
+
render_each_dialog(dialog, cursor_column)
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
private def padding_space_with_escape_sequences(str, width)
|
656
|
+
str + (' ' * (width - calculate_width(str, true)))
|
657
|
+
end
|
658
|
+
|
659
|
+
private def render_each_dialog(dialog, cursor_column)
|
660
|
+
if @in_pasting
|
661
|
+
clear_each_dialog(dialog)
|
662
|
+
dialog.contents = nil
|
663
|
+
dialog.trap_key = nil
|
664
|
+
return
|
665
|
+
end
|
666
|
+
dialog.set_cursor_pos(cursor_column, @first_line_started_from + @started_from)
|
667
|
+
dialog_render_info = dialog.call(@last_key)
|
668
|
+
if dialog_render_info.nil? or dialog_render_info.contents.nil? or dialog_render_info.contents.empty?
|
669
|
+
dialog.lines_backup = {
|
670
|
+
lines: modify_lines(whole_lines),
|
671
|
+
line_index: @line_index,
|
672
|
+
first_line_started_from: @first_line_started_from,
|
673
|
+
started_from: @started_from,
|
674
|
+
byte_pointer: @byte_pointer
|
675
|
+
}
|
676
|
+
clear_each_dialog(dialog)
|
677
|
+
dialog.contents = nil
|
678
|
+
dialog.trap_key = nil
|
679
|
+
return
|
680
|
+
end
|
681
|
+
old_dialog = dialog.clone
|
682
|
+
dialog.contents = dialog_render_info.contents
|
683
|
+
pointer = dialog.pointer
|
684
|
+
if dialog_render_info.width
|
685
|
+
dialog.width = dialog_render_info.width
|
686
|
+
else
|
687
|
+
dialog.width = dialog.contents.map { |l| calculate_width(l, true) }.max
|
688
|
+
end
|
689
|
+
height = dialog_render_info.height || DIALOG_DEFAULT_HEIGHT
|
690
|
+
height = dialog.contents.size if dialog.contents.size < height
|
691
|
+
if dialog.contents.size > height
|
692
|
+
if dialog.pointer
|
693
|
+
if dialog.pointer < 0
|
694
|
+
dialog.scroll_top = 0
|
695
|
+
elsif (dialog.pointer - dialog.scroll_top) >= (height - 1)
|
696
|
+
dialog.scroll_top = dialog.pointer - (height - 1)
|
697
|
+
elsif (dialog.pointer - dialog.scroll_top) < 0
|
698
|
+
dialog.scroll_top = dialog.pointer
|
699
|
+
end
|
700
|
+
pointer = dialog.pointer - dialog.scroll_top
|
701
|
+
end
|
702
|
+
dialog.contents = dialog.contents[dialog.scroll_top, height]
|
703
|
+
end
|
704
|
+
if dialog.contents and dialog.scroll_top >= dialog.contents.size
|
705
|
+
dialog.scroll_top = dialog.contents.size - height
|
706
|
+
end
|
707
|
+
if dialog_render_info.scrollbar and dialog_render_info.contents.size > height
|
708
|
+
bar_max_height = height * 2
|
709
|
+
moving_distance = (dialog_render_info.contents.size - height) * 2
|
710
|
+
position_ratio = dialog.scroll_top.zero? ? 0.0 : ((dialog.scroll_top * 2).to_f / moving_distance)
|
711
|
+
bar_height = (bar_max_height * ((dialog.contents.size * 2).to_f / (dialog_render_info.contents.size * 2))).floor.to_i
|
712
|
+
dialog.scrollbar_pos = ((bar_max_height - bar_height) * position_ratio).floor.to_i
|
713
|
+
else
|
714
|
+
dialog.scrollbar_pos = nil
|
715
|
+
end
|
716
|
+
upper_space = @first_line_started_from - @started_from
|
717
|
+
dialog.column = dialog_render_info.pos.x
|
718
|
+
dialog.width += @block_elem_width if dialog.scrollbar_pos
|
719
|
+
diff = (dialog.column + dialog.width) - (@screen_size.last)
|
720
|
+
if diff > 0
|
721
|
+
dialog.column -= diff
|
722
|
+
end
|
723
|
+
if (@rest_height - dialog_render_info.pos.y) >= height
|
724
|
+
dialog.vertical_offset = dialog_render_info.pos.y + 1
|
725
|
+
elsif upper_space >= height
|
726
|
+
dialog.vertical_offset = dialog_render_info.pos.y - height
|
727
|
+
else
|
728
|
+
if (@rest_height - dialog_render_info.pos.y) < height
|
729
|
+
scroll_down(height + dialog_render_info.pos.y)
|
730
|
+
move_cursor_up(height + dialog_render_info.pos.y)
|
731
|
+
end
|
732
|
+
dialog.vertical_offset = dialog_render_info.pos.y + 1
|
733
|
+
end
|
734
|
+
Reline::IOGate.hide_cursor
|
735
|
+
if dialog.column < 0
|
736
|
+
dialog.column = 0
|
737
|
+
dialog.width = @screen_size.last
|
738
|
+
end
|
739
|
+
reset_dialog(dialog, old_dialog)
|
740
|
+
move_cursor_down(dialog.vertical_offset)
|
741
|
+
Reline::IOGate.move_cursor_column(dialog.column)
|
742
|
+
dialog.contents.each_with_index do |item, i|
|
743
|
+
if i == pointer
|
744
|
+
bg_color = '45'
|
745
|
+
else
|
746
|
+
if dialog_render_info.bg_color
|
747
|
+
bg_color = dialog_render_info.bg_color
|
748
|
+
else
|
749
|
+
bg_color = '46'
|
750
|
+
end
|
751
|
+
end
|
752
|
+
str_width = dialog.width - (dialog.scrollbar_pos.nil? ? 0 : @block_elem_width)
|
753
|
+
str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, str_width), str_width)
|
754
|
+
@output.write "\e[#{bg_color}m#{str}"
|
755
|
+
if dialog.scrollbar_pos and (dialog.scrollbar_pos != old_dialog.scrollbar_pos or dialog.column != old_dialog.column)
|
756
|
+
@output.write "\e[37m"
|
757
|
+
if dialog.scrollbar_pos <= (i * 2) and (i * 2 + 1) < (dialog.scrollbar_pos + bar_height)
|
758
|
+
@output.write @full_block
|
759
|
+
elsif dialog.scrollbar_pos <= (i * 2) and (i * 2) < (dialog.scrollbar_pos + bar_height)
|
760
|
+
@output.write @upper_half_block
|
761
|
+
str += ''
|
762
|
+
elsif dialog.scrollbar_pos <= (i * 2 + 1) and (i * 2) < (dialog.scrollbar_pos + bar_height)
|
763
|
+
@output.write @lower_half_block
|
764
|
+
else
|
765
|
+
@output.write ' ' * @block_elem_width
|
766
|
+
end
|
767
|
+
end
|
768
|
+
@output.write "\e[0m"
|
769
|
+
Reline::IOGate.move_cursor_column(dialog.column)
|
770
|
+
move_cursor_down(1) if i < (dialog.contents.size - 1)
|
771
|
+
end
|
772
|
+
Reline::IOGate.move_cursor_column(cursor_column)
|
773
|
+
move_cursor_up(dialog.vertical_offset + dialog.contents.size - 1)
|
774
|
+
Reline::IOGate.show_cursor
|
775
|
+
dialog.lines_backup = {
|
776
|
+
lines: modify_lines(whole_lines),
|
777
|
+
line_index: @line_index,
|
778
|
+
first_line_started_from: @first_line_started_from,
|
779
|
+
started_from: @started_from,
|
780
|
+
byte_pointer: @byte_pointer
|
781
|
+
}
|
782
|
+
end
|
783
|
+
|
784
|
+
private def reset_dialog(dialog, old_dialog)
|
785
|
+
return if dialog.lines_backup.nil? or old_dialog.contents.nil?
|
786
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines])
|
787
|
+
visual_lines = []
|
788
|
+
visual_start = nil
|
789
|
+
dialog.lines_backup[:lines].each_with_index { |l, i|
|
790
|
+
pr = prompt_list ? prompt_list[i] : prompt
|
791
|
+
vl, _ = split_by_width(pr + l, @screen_size.last)
|
792
|
+
vl.compact!
|
793
|
+
if i == dialog.lines_backup[:line_index]
|
794
|
+
visual_start = visual_lines.size + dialog.lines_backup[:started_from]
|
795
|
+
end
|
796
|
+
visual_lines.concat(vl)
|
797
|
+
}
|
798
|
+
old_y = dialog.lines_backup[:first_line_started_from] + dialog.lines_backup[:started_from]
|
799
|
+
y = @first_line_started_from + @started_from
|
800
|
+
y_diff = y - old_y
|
801
|
+
if (old_y + old_dialog.vertical_offset) < (y + dialog.vertical_offset)
|
802
|
+
# rerender top
|
803
|
+
move_cursor_down(old_dialog.vertical_offset - y_diff)
|
804
|
+
start = visual_start + old_dialog.vertical_offset
|
805
|
+
line_num = dialog.vertical_offset - old_dialog.vertical_offset
|
806
|
+
line_num.times do |i|
|
807
|
+
Reline::IOGate.move_cursor_column(old_dialog.column)
|
808
|
+
if visual_lines[start + i].nil?
|
809
|
+
s = ' ' * old_dialog.width
|
810
|
+
else
|
811
|
+
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, old_dialog.width)
|
812
|
+
s = padding_space_with_escape_sequences(s, old_dialog.width)
|
813
|
+
end
|
814
|
+
@output.write "\e[0m#{s}\e[0m"
|
815
|
+
move_cursor_down(1) if i < (line_num - 1)
|
816
|
+
end
|
817
|
+
move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
|
818
|
+
end
|
819
|
+
if (old_y + old_dialog.vertical_offset + old_dialog.contents.size) > (y + dialog.vertical_offset + dialog.contents.size)
|
820
|
+
# rerender bottom
|
821
|
+
move_cursor_down(dialog.vertical_offset + dialog.contents.size - y_diff)
|
822
|
+
start = visual_start + dialog.vertical_offset + dialog.contents.size
|
823
|
+
line_num = (old_dialog.vertical_offset + old_dialog.contents.size) - (dialog.vertical_offset + dialog.contents.size)
|
824
|
+
line_num.times do |i|
|
825
|
+
Reline::IOGate.move_cursor_column(old_dialog.column)
|
826
|
+
if visual_lines[start + i].nil?
|
827
|
+
s = ' ' * old_dialog.width
|
828
|
+
else
|
829
|
+
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, old_dialog.width)
|
830
|
+
s = padding_space_with_escape_sequences(s, old_dialog.width)
|
831
|
+
end
|
832
|
+
@output.write "\e[0m#{s}\e[0m"
|
833
|
+
move_cursor_down(1) if i < (line_num - 1)
|
834
|
+
end
|
835
|
+
move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff)
|
836
|
+
end
|
837
|
+
if old_dialog.column < dialog.column
|
838
|
+
# rerender left
|
839
|
+
move_cursor_down(old_dialog.vertical_offset - y_diff)
|
840
|
+
width = dialog.column - old_dialog.column
|
841
|
+
start = visual_start + old_dialog.vertical_offset
|
842
|
+
line_num = old_dialog.contents.size
|
843
|
+
line_num.times do |i|
|
844
|
+
Reline::IOGate.move_cursor_column(old_dialog.column)
|
845
|
+
if visual_lines[start + i].nil?
|
846
|
+
s = ' ' * width
|
847
|
+
else
|
848
|
+
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, width)
|
849
|
+
s = padding_space_with_escape_sequences(s, dialog.width)
|
850
|
+
end
|
851
|
+
@output.write "\e[0m#{s}\e[0m"
|
852
|
+
move_cursor_down(1) if i < (line_num - 1)
|
853
|
+
end
|
854
|
+
move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
|
855
|
+
end
|
856
|
+
if (old_dialog.column + old_dialog.width) > (dialog.column + dialog.width)
|
857
|
+
# rerender right
|
858
|
+
move_cursor_down(old_dialog.vertical_offset + y_diff)
|
859
|
+
width = (old_dialog.column + old_dialog.width) - (dialog.column + dialog.width)
|
860
|
+
start = visual_start + old_dialog.vertical_offset
|
861
|
+
line_num = old_dialog.contents.size
|
862
|
+
line_num.times do |i|
|
863
|
+
Reline::IOGate.move_cursor_column(old_dialog.column + dialog.width)
|
864
|
+
if visual_lines[start + i].nil?
|
865
|
+
s = ' ' * width
|
866
|
+
else
|
867
|
+
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column + dialog.width, width)
|
868
|
+
rerender_width = old_dialog.width - dialog.width
|
869
|
+
s = padding_space_with_escape_sequences(s, rerender_width)
|
870
|
+
end
|
871
|
+
Reline::IOGate.move_cursor_column(dialog.column + dialog.width)
|
872
|
+
@output.write "\e[0m#{s}\e[0m"
|
873
|
+
move_cursor_down(1) if i < (line_num - 1)
|
874
|
+
end
|
875
|
+
move_cursor_up(old_dialog.vertical_offset + line_num - 1 + y_diff)
|
876
|
+
end
|
877
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
878
|
+
end
|
879
|
+
|
880
|
+
private def clear_dialog
|
881
|
+
@dialogs.each do |dialog|
|
882
|
+
clear_each_dialog(dialog)
|
883
|
+
end
|
884
|
+
end
|
885
|
+
|
886
|
+
private def clear_each_dialog(dialog)
|
887
|
+
dialog.trap_key = nil
|
888
|
+
return unless dialog.contents
|
889
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines])
|
890
|
+
visual_lines = []
|
891
|
+
visual_lines_under_dialog = []
|
892
|
+
visual_start = nil
|
893
|
+
dialog.lines_backup[:lines].each_with_index { |l, i|
|
894
|
+
pr = prompt_list ? prompt_list[i] : prompt
|
895
|
+
vl, _ = split_by_width(pr + l, @screen_size.last)
|
896
|
+
vl.compact!
|
897
|
+
if i == dialog.lines_backup[:line_index]
|
898
|
+
visual_start = visual_lines.size + dialog.lines_backup[:started_from] + dialog.vertical_offset
|
899
|
+
end
|
900
|
+
visual_lines.concat(vl)
|
901
|
+
}
|
902
|
+
visual_lines_under_dialog = visual_lines[visual_start, dialog.contents.size]
|
903
|
+
visual_lines_under_dialog = [] if visual_lines_under_dialog.nil?
|
904
|
+
Reline::IOGate.hide_cursor
|
905
|
+
move_cursor_down(dialog.vertical_offset)
|
906
|
+
dialog_vertical_size = dialog.contents.size
|
907
|
+
dialog_vertical_size.times do |i|
|
908
|
+
if i < visual_lines_under_dialog.size
|
909
|
+
Reline::IOGate.move_cursor_column(dialog.column)
|
910
|
+
str = Reline::Unicode.take_range(visual_lines_under_dialog[i], dialog.column, dialog.width)
|
911
|
+
str = padding_space_with_escape_sequences(str, dialog.width)
|
912
|
+
@output.write "\e[0m#{str}\e[0m"
|
913
|
+
else
|
914
|
+
Reline::IOGate.move_cursor_column(dialog.column)
|
915
|
+
@output.write "\e[0m#{' ' * dialog.width}\e[0m"
|
916
|
+
end
|
917
|
+
move_cursor_down(1) if i < (dialog_vertical_size - 1)
|
918
|
+
end
|
919
|
+
move_cursor_up(dialog_vertical_size - 1 + dialog.vertical_offset)
|
920
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
921
|
+
Reline::IOGate.show_cursor
|
922
|
+
end
|
923
|
+
|
468
924
|
private def calculate_scroll_partial_screen(highest_in_all, cursor_y)
|
469
925
|
if @screen_height < highest_in_all
|
470
926
|
old_scroll_partial_screen = @scroll_partial_screen
|
@@ -515,7 +971,7 @@ class Reline::LineEditor
|
|
515
971
|
end
|
516
972
|
|
517
973
|
def just_move_cursor
|
518
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines
|
974
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines)
|
519
975
|
move_cursor_up(@started_from)
|
520
976
|
new_first_line_started_from =
|
521
977
|
if @line_index.zero?
|
@@ -552,7 +1008,7 @@ class Reline::LineEditor
|
|
552
1008
|
else
|
553
1009
|
new_lines = whole_lines
|
554
1010
|
end
|
555
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines
|
1011
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
|
556
1012
|
all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
|
557
1013
|
diff = all_height - @highest_in_all
|
558
1014
|
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
@@ -599,7 +1055,7 @@ class Reline::LineEditor
|
|
599
1055
|
Reline::IOGate.move_cursor_column(0)
|
600
1056
|
back = 0
|
601
1057
|
new_buffer = whole_lines
|
602
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer
|
1058
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer)
|
603
1059
|
new_buffer.each_with_index do |line, index|
|
604
1060
|
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
605
1061
|
width = prompt_width + calculate_width(line)
|
@@ -678,7 +1134,6 @@ class Reline::LineEditor
|
|
678
1134
|
private def render_partial(prompt, prompt_width, line_to_render, this_started_from, with_control: true)
|
679
1135
|
visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
|
680
1136
|
cursor_up_from_last_line = 0
|
681
|
-
# TODO: This logic would be sometimes buggy if this logical line isn't the current @line_index.
|
682
1137
|
if @scroll_partial_screen
|
683
1138
|
last_visual_line = this_started_from + (height - 1)
|
684
1139
|
last_screen_line = @scroll_partial_screen + (@screen_height - 1)
|
@@ -807,7 +1262,7 @@ class Reline::LineEditor
|
|
807
1262
|
height = render_partial(prompt, prompt_width, line, back, with_control: false)
|
808
1263
|
end
|
809
1264
|
if index < (@buffer_of_lines.size - 1)
|
810
|
-
move_cursor_down(
|
1265
|
+
move_cursor_down(1)
|
811
1266
|
back += height
|
812
1267
|
end
|
813
1268
|
end
|
@@ -926,6 +1381,16 @@ class Reline::LineEditor
|
|
926
1381
|
@completion_journey_data = CompletionJourneyData.new(
|
927
1382
|
preposing, postposing,
|
928
1383
|
[target] + list.select{ |item| item.start_with?(target) }, 0)
|
1384
|
+
if @completion_journey_data.list.size == 1
|
1385
|
+
@completion_journey_data.pointer = 0
|
1386
|
+
else
|
1387
|
+
case direction
|
1388
|
+
when :up
|
1389
|
+
@completion_journey_data.pointer = @completion_journey_data.list.size - 1
|
1390
|
+
when :down
|
1391
|
+
@completion_journey_data.pointer = 1
|
1392
|
+
end
|
1393
|
+
end
|
929
1394
|
@completion_state = CompletionState::JOURNEY
|
930
1395
|
else
|
931
1396
|
case direction
|
@@ -940,13 +1405,15 @@ class Reline::LineEditor
|
|
940
1405
|
@completion_journey_data.pointer = 0
|
941
1406
|
end
|
942
1407
|
end
|
943
|
-
completed = @completion_journey_data.list[@completion_journey_data.pointer]
|
944
|
-
@line = @completion_journey_data.preposing + completed + @completion_journey_data.postposing
|
945
|
-
line_to_pointer = @completion_journey_data.preposing + completed
|
946
|
-
@cursor_max = calculate_width(@line)
|
947
|
-
@cursor = calculate_width(line_to_pointer)
|
948
|
-
@byte_pointer = line_to_pointer.bytesize
|
949
1408
|
end
|
1409
|
+
completed = @completion_journey_data.list[@completion_journey_data.pointer]
|
1410
|
+
new_line = (@completion_journey_data.preposing + completed + @completion_journey_data.postposing).split("\n")[@line_index]
|
1411
|
+
@line = new_line.nil? ? String.new(encoding: @encoding) : new_line
|
1412
|
+
line_to_pointer = (@completion_journey_data.preposing + completed).split("\n").last
|
1413
|
+
line_to_pointer = String.new(encoding: @encoding) if line_to_pointer.nil?
|
1414
|
+
@cursor_max = calculate_width(@line)
|
1415
|
+
@cursor = calculate_width(line_to_pointer)
|
1416
|
+
@byte_pointer = line_to_pointer.bytesize
|
950
1417
|
end
|
951
1418
|
|
952
1419
|
private def run_for_operators(key, method_symbol, &block)
|
@@ -978,7 +1445,10 @@ class Reline::LineEditor
|
|
978
1445
|
end
|
979
1446
|
@waiting_operator_proc = nil
|
980
1447
|
@waiting_operator_vi_arg = nil
|
981
|
-
@vi_arg
|
1448
|
+
if @vi_arg
|
1449
|
+
@rerender_all = true
|
1450
|
+
@vi_arg = nil
|
1451
|
+
end
|
982
1452
|
else
|
983
1453
|
block.(false)
|
984
1454
|
end
|
@@ -1029,7 +1499,10 @@ class Reline::LineEditor
|
|
1029
1499
|
wrap_method_call(method_symbol, method_obj, key) if method_obj
|
1030
1500
|
end
|
1031
1501
|
@kill_ring.process
|
1032
|
-
@vi_arg
|
1502
|
+
if @vi_arg
|
1503
|
+
@rerender_al = true
|
1504
|
+
@vi_arg = nil
|
1505
|
+
end
|
1033
1506
|
elsif @vi_arg
|
1034
1507
|
if key.chr =~ /[0-9]/
|
1035
1508
|
ed_argument_digit(key)
|
@@ -1046,7 +1519,10 @@ class Reline::LineEditor
|
|
1046
1519
|
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
1047
1520
|
end
|
1048
1521
|
@kill_ring.process
|
1049
|
-
@vi_arg
|
1522
|
+
if @vi_arg
|
1523
|
+
@rerender_all = true
|
1524
|
+
@vi_arg = nil
|
1525
|
+
end
|
1050
1526
|
end
|
1051
1527
|
elsif @waiting_proc
|
1052
1528
|
@waiting_proc.(key)
|
@@ -1104,6 +1580,13 @@ class Reline::LineEditor
|
|
1104
1580
|
end
|
1105
1581
|
|
1106
1582
|
def input_key(key)
|
1583
|
+
@last_key = key
|
1584
|
+
@config.reset_oneshot_key_bindings
|
1585
|
+
@dialogs.each do |dialog|
|
1586
|
+
if key.char.instance_of?(Symbol) and key.char == dialog.name
|
1587
|
+
return
|
1588
|
+
end
|
1589
|
+
end
|
1107
1590
|
@just_cursor_moving = nil
|
1108
1591
|
if key.char.nil?
|
1109
1592
|
if @first_char
|
@@ -1121,7 +1604,20 @@ class Reline::LineEditor
|
|
1121
1604
|
if result.is_a?(Array)
|
1122
1605
|
completion_occurs = true
|
1123
1606
|
process_insert
|
1124
|
-
|
1607
|
+
if @config.autocompletion
|
1608
|
+
move_completed_list(result, :down)
|
1609
|
+
else
|
1610
|
+
complete(result)
|
1611
|
+
end
|
1612
|
+
end
|
1613
|
+
end
|
1614
|
+
elsif @config.editing_mode_is?(:emacs, :vi_insert) and key.char == :completion_journey_up
|
1615
|
+
if not @config.disable_completion and @config.autocompletion
|
1616
|
+
result = call_completion_proc
|
1617
|
+
if result.is_a?(Array)
|
1618
|
+
completion_occurs = true
|
1619
|
+
process_insert
|
1620
|
+
move_completed_list(result, :up)
|
1125
1621
|
end
|
1126
1622
|
end
|
1127
1623
|
elsif not @config.disable_completion and @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
|
@@ -1140,6 +1636,7 @@ class Reline::LineEditor
|
|
1140
1636
|
end
|
1141
1637
|
unless completion_occurs
|
1142
1638
|
@completion_state = CompletionState::NORMAL
|
1639
|
+
@completion_journey_data = nil
|
1143
1640
|
end
|
1144
1641
|
if not @in_pasting and @just_cursor_moving.nil?
|
1145
1642
|
if @previous_line_index and @buffer_of_lines[@previous_line_index] == @line
|
@@ -1159,7 +1656,13 @@ class Reline::LineEditor
|
|
1159
1656
|
|
1160
1657
|
def call_completion_proc
|
1161
1658
|
result = retrieve_completion_block(true)
|
1162
|
-
|
1659
|
+
pre, target, post = result
|
1660
|
+
result = call_completion_proc_with_checking_args(pre, target, post)
|
1661
|
+
Reline.core.instance_variable_set(:@completion_quote_character, nil)
|
1662
|
+
result
|
1663
|
+
end
|
1664
|
+
|
1665
|
+
def call_completion_proc_with_checking_args(pre, target, post)
|
1163
1666
|
if @completion_proc and target
|
1164
1667
|
argnum = @completion_proc.parameters.inject(0) { |result, item|
|
1165
1668
|
case item.first
|
@@ -1173,12 +1676,11 @@ class Reline::LineEditor
|
|
1173
1676
|
when 1
|
1174
1677
|
result = @completion_proc.(target)
|
1175
1678
|
when 2
|
1176
|
-
result = @completion_proc.(target,
|
1679
|
+
result = @completion_proc.(target, pre)
|
1177
1680
|
when 3..Float::INFINITY
|
1178
|
-
result = @completion_proc.(target,
|
1681
|
+
result = @completion_proc.(target, pre, post)
|
1179
1682
|
end
|
1180
1683
|
end
|
1181
|
-
Reline.core.instance_variable_set(:@completion_quote_character, nil)
|
1182
1684
|
result
|
1183
1685
|
end
|
1184
1686
|
|
@@ -1459,6 +1961,8 @@ class Reline::LineEditor
|
|
1459
1961
|
end
|
1460
1962
|
end
|
1461
1963
|
|
1964
|
+
# Editline:: +ed-unassigned+ This editor command always results in an error.
|
1965
|
+
# GNU Readline:: There is no corresponding macro.
|
1462
1966
|
private def ed_unassigned(key) end # do nothing
|
1463
1967
|
|
1464
1968
|
private def process_insert(force: false)
|
@@ -1476,6 +1980,19 @@ class Reline::LineEditor
|
|
1476
1980
|
@continuous_insertion_buffer.clear
|
1477
1981
|
end
|
1478
1982
|
|
1983
|
+
# Editline:: +ed-insert+ (vi input: almost all; emacs: printable characters)
|
1984
|
+
# In insert mode, insert the input character left of the cursor
|
1985
|
+
# position. In replace mode, overwrite the character at the
|
1986
|
+
# cursor and move the cursor to the right by one character
|
1987
|
+
# position. Accept an argument to do this repeatedly. It is an
|
1988
|
+
# error if the input character is the NUL character (+Ctrl-@+).
|
1989
|
+
# Failure to enlarge the edit buffer also results in an error.
|
1990
|
+
# Editline:: +ed-digit+ (emacs: 0 to 9) If in argument input mode, append
|
1991
|
+
# the input digit to the argument being read. Otherwise, call
|
1992
|
+
# +ed-insert+. It is an error if the input character is not a
|
1993
|
+
# digit or if the existing argument is already greater than a
|
1994
|
+
# million.
|
1995
|
+
# GNU Readline:: +self-insert+ (a, b, A, 1, !, …) Insert yourself.
|
1479
1996
|
private def ed_insert(key)
|
1480
1997
|
str = nil
|
1481
1998
|
width = nil
|
@@ -1512,8 +2029,16 @@ class Reline::LineEditor
|
|
1512
2029
|
last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
1513
2030
|
@byte_pointer += bytesize
|
1514
2031
|
last_mbchar = @line.byteslice((@byte_pointer - bytesize - last_byte_size), last_byte_size)
|
1515
|
-
|
1516
|
-
|
2032
|
+
combined_char = last_mbchar + str
|
2033
|
+
if last_byte_size != 0 and combined_char.grapheme_clusters.size == 1
|
2034
|
+
# combined char
|
2035
|
+
last_mbchar_width = Reline::Unicode.get_mbchar_width(last_mbchar)
|
2036
|
+
combined_char_width = Reline::Unicode.get_mbchar_width(combined_char)
|
2037
|
+
if combined_char_width > last_mbchar_width
|
2038
|
+
width = combined_char_width - last_mbchar_width
|
2039
|
+
else
|
2040
|
+
width = 0
|
2041
|
+
end
|
1517
2042
|
end
|
1518
2043
|
@cursor += width
|
1519
2044
|
@cursor_max += width
|
@@ -1526,6 +2051,8 @@ class Reline::LineEditor
|
|
1526
2051
|
arg.times do
|
1527
2052
|
if key == "\C-j".ord or key == "\C-m".ord
|
1528
2053
|
key_newline(key)
|
2054
|
+
elsif key == 0
|
2055
|
+
# Ignore NUL.
|
1529
2056
|
else
|
1530
2057
|
ed_insert(key)
|
1531
2058
|
end
|
@@ -1960,6 +2487,7 @@ class Reline::LineEditor
|
|
1960
2487
|
arg -= 1
|
1961
2488
|
ed_prev_history(key, arg: arg) if arg > 0
|
1962
2489
|
end
|
2490
|
+
alias_method :previous_history, :ed_prev_history
|
1963
2491
|
|
1964
2492
|
private def ed_next_history(key, arg: 1)
|
1965
2493
|
if @is_multiline and @line_index < (@buffer_of_lines.size - 1)
|
@@ -2007,6 +2535,7 @@ class Reline::LineEditor
|
|
2007
2535
|
arg -= 1
|
2008
2536
|
ed_next_history(key, arg: arg) if arg > 0
|
2009
2537
|
end
|
2538
|
+
alias_method :next_history, :ed_next_history
|
2010
2539
|
|
2011
2540
|
private def ed_newline(key)
|
2012
2541
|
process_insert(force: true)
|
@@ -2041,7 +2570,7 @@ class Reline::LineEditor
|
|
2041
2570
|
end
|
2042
2571
|
end
|
2043
2572
|
|
2044
|
-
private def em_delete_prev_char(key)
|
2573
|
+
private def em_delete_prev_char(key, arg: 1)
|
2045
2574
|
if @is_multiline and @cursor == 0 and @line_index > 0
|
2046
2575
|
@buffer_of_lines[@line_index] = @line
|
2047
2576
|
@cursor = calculate_width(@buffer_of_lines[@line_index - 1])
|
@@ -2059,9 +2588,16 @@ class Reline::LineEditor
|
|
2059
2588
|
@cursor -= width
|
2060
2589
|
@cursor_max -= width
|
2061
2590
|
end
|
2591
|
+
arg -= 1
|
2592
|
+
em_delete_prev_char(key, arg: arg) if arg > 0
|
2062
2593
|
end
|
2063
2594
|
alias_method :backward_delete_char, :em_delete_prev_char
|
2064
2595
|
|
2596
|
+
# Editline:: +ed-kill-line+ (vi command: +D+, +Ctrl-K+; emacs: +Ctrl-K+,
|
2597
|
+
# +Ctrl-U+) + Kill from the cursor to the end of the line.
|
2598
|
+
# GNU Readline:: +kill-line+ (+C-k+) Kill the text from point to the end of
|
2599
|
+
# the line. With a negative numeric argument, kill backward
|
2600
|
+
# from the cursor to the beginning of the current line.
|
2065
2601
|
private def ed_kill_line(key)
|
2066
2602
|
if @line.bytesize > @byte_pointer
|
2067
2603
|
@line, deleted = byteslice!(@line, @byte_pointer, @line.bytesize - @byte_pointer)
|
@@ -2078,8 +2614,14 @@ class Reline::LineEditor
|
|
2078
2614
|
@rest_height += 1
|
2079
2615
|
end
|
2080
2616
|
end
|
2617
|
+
alias_method :kill_line, :ed_kill_line
|
2081
2618
|
|
2082
|
-
|
2619
|
+
# Editline:: +vi-kill-line-prev+ (vi: +Ctrl-U+) Delete the string from the
|
2620
|
+
# beginning of the edit buffer to the cursor and save it to the
|
2621
|
+
# cut buffer.
|
2622
|
+
# GNU Readline:: +unix-line-discard+ (+C-u+) Kill backward from the cursor
|
2623
|
+
# to the beginning of the current line.
|
2624
|
+
private def vi_kill_line_prev(key)
|
2083
2625
|
if @byte_pointer > 0
|
2084
2626
|
@line, deleted = byteslice!(@line, 0, @byte_pointer)
|
2085
2627
|
@byte_pointer = 0
|
@@ -2088,7 +2630,22 @@ class Reline::LineEditor
|
|
2088
2630
|
@cursor = 0
|
2089
2631
|
end
|
2090
2632
|
end
|
2091
|
-
alias_method :
|
2633
|
+
alias_method :unix_line_discard, :vi_kill_line_prev
|
2634
|
+
|
2635
|
+
# Editline:: +em-kill-line+ (not bound) Delete the entire contents of the
|
2636
|
+
# edit buffer and save it to the cut buffer. +vi-kill-line-prev+
|
2637
|
+
# GNU Readline:: +kill-whole-line+ (not bound) Kill all characters on the
|
2638
|
+
# current line, no matter where point is.
|
2639
|
+
private def em_kill_line(key)
|
2640
|
+
if @line.size > 0
|
2641
|
+
@kill_ring.append(@line.dup, true)
|
2642
|
+
@line.clear
|
2643
|
+
@byte_pointer = 0
|
2644
|
+
@cursor_max = 0
|
2645
|
+
@cursor = 0
|
2646
|
+
end
|
2647
|
+
end
|
2648
|
+
alias_method :kill_whole_line, :em_kill_line
|
2092
2649
|
|
2093
2650
|
private def em_delete(key)
|
2094
2651
|
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
|
@@ -2593,7 +3150,14 @@ class Reline::LineEditor
|
|
2593
3150
|
|
2594
3151
|
private def ed_argument_digit(key)
|
2595
3152
|
if @vi_arg.nil?
|
2596
|
-
|
3153
|
+
if key.chr.to_i.zero?
|
3154
|
+
if key.anybits?(0b10000000)
|
3155
|
+
unescaped_key = key ^ 0b10000000
|
3156
|
+
unless unescaped_key.chr.to_i.zero?
|
3157
|
+
@vi_arg = unescaped_key.chr.to_i
|
3158
|
+
end
|
3159
|
+
end
|
3160
|
+
else
|
2597
3161
|
@vi_arg = key.chr.to_i
|
2598
3162
|
end
|
2599
3163
|
else
|