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.
- checksums.yaml +4 -4
- data/lib/reline/ansi.rb +50 -23
- data/lib/reline/config.rb +10 -2
- data/lib/reline/general_io.rb +2 -1
- data/lib/reline/key_actor/emacs.rb +1 -1
- data/lib/reline/key_stroke.rb +60 -62
- data/lib/reline/line_editor.rb +239 -110
- data/lib/reline/terminfo.rb +12 -4
- data/lib/reline/unicode.rb +9 -1
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +121 -50
- data/lib/reline.rb +31 -23
- metadata +4 -5
- data/lib/reline/line_editor.rb.orig +0 -3199
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,74 @@ 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') {
|
154
222
|
clear_dialog
|
155
223
|
if @scroll_partial_screen
|
156
224
|
move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1)
|
@@ -167,50 +235,24 @@ class Reline::LineEditor
|
|
167
235
|
when 'EXIT'
|
168
236
|
exit
|
169
237
|
else
|
170
|
-
@old_trap.call
|
238
|
+
@old_trap.call if @old_trap.respond_to?(:call)
|
171
239
|
end
|
172
240
|
}
|
173
|
-
|
174
|
-
@
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
@rerender_all = true
|
180
|
-
rerender
|
181
|
-
else
|
182
|
-
back = 0
|
183
|
-
new_buffer = whole_lines
|
184
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
185
|
-
new_buffer.each_with_index do |line, index|
|
186
|
-
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
187
|
-
width = prompt_width + calculate_width(line)
|
188
|
-
height = calculate_height_by_width(width)
|
189
|
-
back += height
|
190
|
-
end
|
191
|
-
@highest_in_all = back
|
192
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
193
|
-
@first_line_started_from =
|
194
|
-
if @line_index.zero?
|
195
|
-
0
|
196
|
-
else
|
197
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
198
|
-
end
|
199
|
-
if @prompt_proc
|
200
|
-
prompt = prompt_list[@line_index]
|
201
|
-
prompt_width = calculate_width(prompt, true)
|
202
|
-
end
|
203
|
-
calculate_nearest_cursor
|
204
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
205
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
206
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
207
|
-
@rerender_all = true
|
208
|
-
end
|
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
|
209
247
|
end
|
210
248
|
end
|
211
249
|
|
212
250
|
def finalize
|
213
|
-
Signal.trap('
|
251
|
+
Signal.trap('INT', @old_trap)
|
252
|
+
begin
|
253
|
+
Signal.trap('TSTP', @old_tstp_trap)
|
254
|
+
rescue ArgumentError
|
255
|
+
end
|
214
256
|
end
|
215
257
|
|
216
258
|
def eof?
|
@@ -218,7 +260,7 @@ class Reline::LineEditor
|
|
218
260
|
end
|
219
261
|
|
220
262
|
def reset_variables(prompt = '', encoding:)
|
221
|
-
@prompt = prompt
|
263
|
+
@prompt = prompt.gsub("\n", "\\n")
|
222
264
|
@mark_pointer = nil
|
223
265
|
@encoding = encoding
|
224
266
|
@is_multiline = false
|
@@ -252,6 +294,7 @@ class Reline::LineEditor
|
|
252
294
|
@auto_indent_proc = nil
|
253
295
|
@dialogs = []
|
254
296
|
@last_key = nil
|
297
|
+
@resized = false
|
255
298
|
reset_line
|
256
299
|
end
|
257
300
|
|
@@ -395,7 +438,7 @@ class Reline::LineEditor
|
|
395
438
|
show_menu
|
396
439
|
@menu_info = nil
|
397
440
|
end
|
398
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines
|
441
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
|
399
442
|
if @cleared
|
400
443
|
clear_screen_buffer(prompt, prompt_list, prompt_width)
|
401
444
|
@cleared = false
|
@@ -406,7 +449,7 @@ class Reline::LineEditor
|
|
406
449
|
Reline::IOGate.move_cursor_up(@first_line_started_from + @started_from - @scroll_partial_screen)
|
407
450
|
Reline::IOGate.move_cursor_column(0)
|
408
451
|
@scroll_partial_screen = nil
|
409
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines
|
452
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
|
410
453
|
if @previous_line_index
|
411
454
|
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
412
455
|
else
|
@@ -452,7 +495,7 @@ class Reline::LineEditor
|
|
452
495
|
end
|
453
496
|
line = modify_lines(new_lines)[@line_index]
|
454
497
|
clear_dialog
|
455
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines
|
498
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
|
456
499
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
457
500
|
move_cursor_down(@highest_in_all - (@first_line_started_from + @highest_in_this - 1) - 1)
|
458
501
|
scroll_down(1)
|
@@ -461,7 +504,7 @@ class Reline::LineEditor
|
|
461
504
|
else
|
462
505
|
if not rendered and not @in_pasting
|
463
506
|
line = modify_lines(whole_lines)[@line_index]
|
464
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines
|
507
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
|
465
508
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
466
509
|
end
|
467
510
|
render_dialog((prompt_width + @cursor) % @screen_size.last)
|
@@ -548,7 +591,7 @@ class Reline::LineEditor
|
|
548
591
|
|
549
592
|
class Dialog
|
550
593
|
attr_reader :name, :contents, :width
|
551
|
-
attr_accessor :scroll_top, :column, :vertical_offset, :lines_backup, :trap_key
|
594
|
+
attr_accessor :scroll_top, :scrollbar_pos, :pointer, :column, :vertical_offset, :lines_backup, :trap_key
|
552
595
|
|
553
596
|
def initialize(name, config, proc_scope)
|
554
597
|
@name = name
|
@@ -556,6 +599,7 @@ class Reline::LineEditor
|
|
556
599
|
@proc_scope = proc_scope
|
557
600
|
@width = nil
|
558
601
|
@scroll_top = 0
|
602
|
+
@trap_key = nil
|
559
603
|
end
|
560
604
|
|
561
605
|
def set_cursor_pos(col, row)
|
@@ -593,11 +637,15 @@ class Reline::LineEditor
|
|
593
637
|
end
|
594
638
|
|
595
639
|
def add_dialog_proc(name, p, context = nil)
|
596
|
-
|
597
|
-
@dialogs
|
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
|
598
646
|
end
|
599
647
|
|
600
|
-
|
648
|
+
DIALOG_DEFAULT_HEIGHT = 20
|
601
649
|
private def render_dialog(cursor_column)
|
602
650
|
@dialogs.each do |dialog|
|
603
651
|
render_each_dialog(dialog, cursor_column)
|
@@ -610,6 +658,7 @@ class Reline::LineEditor
|
|
610
658
|
|
611
659
|
private def render_each_dialog(dialog, cursor_column)
|
612
660
|
if @in_pasting
|
661
|
+
clear_each_dialog(dialog)
|
613
662
|
dialog.contents = nil
|
614
663
|
dialog.trap_key = nil
|
615
664
|
return
|
@@ -631,56 +680,65 @@ class Reline::LineEditor
|
|
631
680
|
end
|
632
681
|
old_dialog = dialog.clone
|
633
682
|
dialog.contents = dialog_render_info.contents
|
634
|
-
pointer =
|
683
|
+
pointer = dialog.pointer
|
635
684
|
if dialog_render_info.width
|
636
685
|
dialog.width = dialog_render_info.width
|
637
686
|
else
|
638
687
|
dialog.width = dialog.contents.map { |l| calculate_width(l, true) }.max
|
639
688
|
end
|
640
|
-
height = dialog_render_info.height ||
|
689
|
+
height = dialog_render_info.height || DIALOG_DEFAULT_HEIGHT
|
641
690
|
height = dialog.contents.size if dialog.contents.size < height
|
642
691
|
if dialog.contents.size > height
|
643
|
-
if
|
644
|
-
if
|
692
|
+
if dialog.pointer
|
693
|
+
if dialog.pointer < 0
|
645
694
|
dialog.scroll_top = 0
|
646
|
-
elsif (
|
647
|
-
dialog.scroll_top =
|
648
|
-
elsif (
|
649
|
-
dialog.scroll_top =
|
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
|
650
699
|
end
|
651
|
-
pointer =
|
700
|
+
pointer = dialog.pointer - dialog.scroll_top
|
652
701
|
end
|
653
702
|
dialog.contents = dialog.contents[dialog.scroll_top, height]
|
654
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
|
655
716
|
upper_space = @first_line_started_from - @started_from
|
656
|
-
lower_space = @highest_in_all - @first_line_started_from - @started_from - 1
|
657
717
|
dialog.column = dialog_render_info.pos.x
|
658
|
-
|
718
|
+
dialog.width += @block_elem_width if dialog.scrollbar_pos
|
719
|
+
diff = (dialog.column + dialog.width) - (@screen_size.last)
|
659
720
|
if diff > 0
|
660
721
|
dialog.column -= diff
|
661
722
|
end
|
662
|
-
if (
|
723
|
+
if (@rest_height - dialog_render_info.pos.y) >= height
|
663
724
|
dialog.vertical_offset = dialog_render_info.pos.y + 1
|
664
725
|
elsif upper_space >= height
|
665
726
|
dialog.vertical_offset = dialog_render_info.pos.y - height
|
666
727
|
else
|
667
|
-
if (
|
728
|
+
if (@rest_height - dialog_render_info.pos.y) < height
|
668
729
|
scroll_down(height + dialog_render_info.pos.y)
|
669
730
|
move_cursor_up(height + dialog_render_info.pos.y)
|
670
731
|
end
|
671
732
|
dialog.vertical_offset = dialog_render_info.pos.y + 1
|
672
733
|
end
|
673
734
|
Reline::IOGate.hide_cursor
|
735
|
+
if dialog.column < 0
|
736
|
+
dialog.column = 0
|
737
|
+
dialog.width = @screen_size.last
|
738
|
+
end
|
674
739
|
reset_dialog(dialog, old_dialog)
|
675
740
|
move_cursor_down(dialog.vertical_offset)
|
676
741
|
Reline::IOGate.move_cursor_column(dialog.column)
|
677
|
-
if dialog_render_info.scrollbar and dialog_render_info.contents.size > height
|
678
|
-
bar_max_height = height * 2
|
679
|
-
moving_distance = (dialog_render_info.contents.size - height) * 2
|
680
|
-
position_ratio = dialog.scroll_top.zero? ? 0.0 : ((dialog.scroll_top * 2).to_f / moving_distance)
|
681
|
-
bar_height = (bar_max_height * ((dialog.contents.size * 2).to_f / (dialog_render_info.contents.size * 2))).floor.to_i
|
682
|
-
position = ((bar_max_height - bar_height) * position_ratio).floor.to_i
|
683
|
-
end
|
684
742
|
dialog.contents.each_with_index do |item, i|
|
685
743
|
if i == pointer
|
686
744
|
bg_color = '45'
|
@@ -691,27 +749,26 @@ class Reline::LineEditor
|
|
691
749
|
bg_color = '46'
|
692
750
|
end
|
693
751
|
end
|
694
|
-
|
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)
|
695
754
|
@output.write "\e[#{bg_color}m#{str}"
|
696
|
-
if
|
755
|
+
if dialog.scrollbar_pos and (dialog.scrollbar_pos != old_dialog.scrollbar_pos or dialog.column != old_dialog.column)
|
697
756
|
@output.write "\e[37m"
|
698
|
-
if
|
699
|
-
@output.write
|
700
|
-
elsif
|
701
|
-
@output.write
|
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
|
702
761
|
str += ''
|
703
|
-
elsif
|
704
|
-
@output.write
|
762
|
+
elsif dialog.scrollbar_pos <= (i * 2 + 1) and (i * 2) < (dialog.scrollbar_pos + bar_height)
|
763
|
+
@output.write @lower_half_block
|
705
764
|
else
|
706
|
-
@output.write ' '
|
765
|
+
@output.write ' ' * @block_elem_width
|
707
766
|
end
|
708
|
-
@output.write "\e[39m"
|
709
767
|
end
|
710
|
-
@output.write "\e[
|
768
|
+
@output.write "\e[0m"
|
711
769
|
Reline::IOGate.move_cursor_column(dialog.column)
|
712
770
|
move_cursor_down(1) if i < (dialog.contents.size - 1)
|
713
771
|
end
|
714
|
-
dialog.width += 1 if dialog_render_info.scrollbar and dialog_render_info.contents.size > height
|
715
772
|
Reline::IOGate.move_cursor_column(cursor_column)
|
716
773
|
move_cursor_up(dialog.vertical_offset + dialog.contents.size - 1)
|
717
774
|
Reline::IOGate.show_cursor
|
@@ -726,7 +783,7 @@ class Reline::LineEditor
|
|
726
783
|
|
727
784
|
private def reset_dialog(dialog, old_dialog)
|
728
785
|
return if dialog.lines_backup.nil? or old_dialog.contents.nil?
|
729
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines]
|
786
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines])
|
730
787
|
visual_lines = []
|
731
788
|
visual_start = nil
|
732
789
|
dialog.lines_backup[:lines].each_with_index { |l, i|
|
@@ -749,12 +806,12 @@ class Reline::LineEditor
|
|
749
806
|
line_num.times do |i|
|
750
807
|
Reline::IOGate.move_cursor_column(old_dialog.column)
|
751
808
|
if visual_lines[start + i].nil?
|
752
|
-
s = ' ' *
|
809
|
+
s = ' ' * old_dialog.width
|
753
810
|
else
|
754
|
-
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column,
|
755
|
-
s = padding_space_with_escape_sequences(s,
|
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)
|
756
813
|
end
|
757
|
-
@output.write "\e[
|
814
|
+
@output.write "\e[0m#{s}\e[0m"
|
758
815
|
move_cursor_down(1) if i < (line_num - 1)
|
759
816
|
end
|
760
817
|
move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
|
@@ -767,12 +824,12 @@ class Reline::LineEditor
|
|
767
824
|
line_num.times do |i|
|
768
825
|
Reline::IOGate.move_cursor_column(old_dialog.column)
|
769
826
|
if visual_lines[start + i].nil?
|
770
|
-
s = ' ' *
|
827
|
+
s = ' ' * old_dialog.width
|
771
828
|
else
|
772
|
-
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column,
|
773
|
-
s = padding_space_with_escape_sequences(s,
|
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)
|
774
831
|
end
|
775
|
-
@output.write "\e[
|
832
|
+
@output.write "\e[0m#{s}\e[0m"
|
776
833
|
move_cursor_down(1) if i < (line_num - 1)
|
777
834
|
end
|
778
835
|
move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff)
|
@@ -791,7 +848,7 @@ class Reline::LineEditor
|
|
791
848
|
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, width)
|
792
849
|
s = padding_space_with_escape_sequences(s, dialog.width)
|
793
850
|
end
|
794
|
-
@output.write "\e[
|
851
|
+
@output.write "\e[0m#{s}\e[0m"
|
795
852
|
move_cursor_down(1) if i < (line_num - 1)
|
796
853
|
end
|
797
854
|
move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
|
@@ -808,10 +865,11 @@ class Reline::LineEditor
|
|
808
865
|
s = ' ' * width
|
809
866
|
else
|
810
867
|
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column + dialog.width, width)
|
811
|
-
|
868
|
+
rerender_width = old_dialog.width - dialog.width
|
869
|
+
s = padding_space_with_escape_sequences(s, rerender_width)
|
812
870
|
end
|
813
871
|
Reline::IOGate.move_cursor_column(dialog.column + dialog.width)
|
814
|
-
@output.write "\e[
|
872
|
+
@output.write "\e[0m#{s}\e[0m"
|
815
873
|
move_cursor_down(1) if i < (line_num - 1)
|
816
874
|
end
|
817
875
|
move_cursor_up(old_dialog.vertical_offset + line_num - 1 + y_diff)
|
@@ -828,7 +886,7 @@ class Reline::LineEditor
|
|
828
886
|
private def clear_each_dialog(dialog)
|
829
887
|
dialog.trap_key = nil
|
830
888
|
return unless dialog.contents
|
831
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines]
|
889
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines])
|
832
890
|
visual_lines = []
|
833
891
|
visual_lines_under_dialog = []
|
834
892
|
visual_start = nil
|
@@ -851,10 +909,10 @@ class Reline::LineEditor
|
|
851
909
|
Reline::IOGate.move_cursor_column(dialog.column)
|
852
910
|
str = Reline::Unicode.take_range(visual_lines_under_dialog[i], dialog.column, dialog.width)
|
853
911
|
str = padding_space_with_escape_sequences(str, dialog.width)
|
854
|
-
@output.write "\e[
|
912
|
+
@output.write "\e[0m#{str}\e[0m"
|
855
913
|
else
|
856
914
|
Reline::IOGate.move_cursor_column(dialog.column)
|
857
|
-
@output.write "\e[
|
915
|
+
@output.write "\e[0m#{' ' * dialog.width}\e[0m"
|
858
916
|
end
|
859
917
|
move_cursor_down(1) if i < (dialog_vertical_size - 1)
|
860
918
|
end
|
@@ -913,7 +971,7 @@ class Reline::LineEditor
|
|
913
971
|
end
|
914
972
|
|
915
973
|
def just_move_cursor
|
916
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines
|
974
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines)
|
917
975
|
move_cursor_up(@started_from)
|
918
976
|
new_first_line_started_from =
|
919
977
|
if @line_index.zero?
|
@@ -950,7 +1008,7 @@ class Reline::LineEditor
|
|
950
1008
|
else
|
951
1009
|
new_lines = whole_lines
|
952
1010
|
end
|
953
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines
|
1011
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
|
954
1012
|
all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
|
955
1013
|
diff = all_height - @highest_in_all
|
956
1014
|
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
@@ -997,7 +1055,7 @@ class Reline::LineEditor
|
|
997
1055
|
Reline::IOGate.move_cursor_column(0)
|
998
1056
|
back = 0
|
999
1057
|
new_buffer = whole_lines
|
1000
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer
|
1058
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer)
|
1001
1059
|
new_buffer.each_with_index do |line, index|
|
1002
1060
|
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
1003
1061
|
width = prompt_width + calculate_width(line)
|
@@ -1204,7 +1262,7 @@ class Reline::LineEditor
|
|
1204
1262
|
height = render_partial(prompt, prompt_width, line, back, with_control: false)
|
1205
1263
|
end
|
1206
1264
|
if index < (@buffer_of_lines.size - 1)
|
1207
|
-
move_cursor_down(
|
1265
|
+
move_cursor_down(1)
|
1208
1266
|
back += height
|
1209
1267
|
end
|
1210
1268
|
end
|
@@ -1387,7 +1445,10 @@ class Reline::LineEditor
|
|
1387
1445
|
end
|
1388
1446
|
@waiting_operator_proc = nil
|
1389
1447
|
@waiting_operator_vi_arg = nil
|
1390
|
-
@vi_arg
|
1448
|
+
if @vi_arg
|
1449
|
+
@rerender_all = true
|
1450
|
+
@vi_arg = nil
|
1451
|
+
end
|
1391
1452
|
else
|
1392
1453
|
block.(false)
|
1393
1454
|
end
|
@@ -1438,7 +1499,10 @@ class Reline::LineEditor
|
|
1438
1499
|
wrap_method_call(method_symbol, method_obj, key) if method_obj
|
1439
1500
|
end
|
1440
1501
|
@kill_ring.process
|
1441
|
-
@vi_arg
|
1502
|
+
if @vi_arg
|
1503
|
+
@rerender_al = true
|
1504
|
+
@vi_arg = nil
|
1505
|
+
end
|
1442
1506
|
elsif @vi_arg
|
1443
1507
|
if key.chr =~ /[0-9]/
|
1444
1508
|
ed_argument_digit(key)
|
@@ -1455,7 +1519,10 @@ class Reline::LineEditor
|
|
1455
1519
|
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
1456
1520
|
end
|
1457
1521
|
@kill_ring.process
|
1458
|
-
@vi_arg
|
1522
|
+
if @vi_arg
|
1523
|
+
@rerender_all = true
|
1524
|
+
@vi_arg = nil
|
1525
|
+
end
|
1459
1526
|
end
|
1460
1527
|
elsif @waiting_proc
|
1461
1528
|
@waiting_proc.(key)
|
@@ -1894,6 +1961,8 @@ class Reline::LineEditor
|
|
1894
1961
|
end
|
1895
1962
|
end
|
1896
1963
|
|
1964
|
+
# Editline:: +ed-unassigned+ This editor command always results in an error.
|
1965
|
+
# GNU Readline:: There is no corresponding macro.
|
1897
1966
|
private def ed_unassigned(key) end # do nothing
|
1898
1967
|
|
1899
1968
|
private def process_insert(force: false)
|
@@ -1911,6 +1980,19 @@ class Reline::LineEditor
|
|
1911
1980
|
@continuous_insertion_buffer.clear
|
1912
1981
|
end
|
1913
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.
|
1914
1996
|
private def ed_insert(key)
|
1915
1997
|
str = nil
|
1916
1998
|
width = nil
|
@@ -1947,8 +2029,16 @@ class Reline::LineEditor
|
|
1947
2029
|
last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
1948
2030
|
@byte_pointer += bytesize
|
1949
2031
|
last_mbchar = @line.byteslice((@byte_pointer - bytesize - last_byte_size), last_byte_size)
|
1950
|
-
|
1951
|
-
|
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
|
1952
2042
|
end
|
1953
2043
|
@cursor += width
|
1954
2044
|
@cursor_max += width
|
@@ -1961,6 +2051,8 @@ class Reline::LineEditor
|
|
1961
2051
|
arg.times do
|
1962
2052
|
if key == "\C-j".ord or key == "\C-m".ord
|
1963
2053
|
key_newline(key)
|
2054
|
+
elsif key == 0
|
2055
|
+
# Ignore NUL.
|
1964
2056
|
else
|
1965
2057
|
ed_insert(key)
|
1966
2058
|
end
|
@@ -2395,6 +2487,7 @@ class Reline::LineEditor
|
|
2395
2487
|
arg -= 1
|
2396
2488
|
ed_prev_history(key, arg: arg) if arg > 0
|
2397
2489
|
end
|
2490
|
+
alias_method :previous_history, :ed_prev_history
|
2398
2491
|
|
2399
2492
|
private def ed_next_history(key, arg: 1)
|
2400
2493
|
if @is_multiline and @line_index < (@buffer_of_lines.size - 1)
|
@@ -2442,6 +2535,7 @@ class Reline::LineEditor
|
|
2442
2535
|
arg -= 1
|
2443
2536
|
ed_next_history(key, arg: arg) if arg > 0
|
2444
2537
|
end
|
2538
|
+
alias_method :next_history, :ed_next_history
|
2445
2539
|
|
2446
2540
|
private def ed_newline(key)
|
2447
2541
|
process_insert(force: true)
|
@@ -2476,7 +2570,7 @@ class Reline::LineEditor
|
|
2476
2570
|
end
|
2477
2571
|
end
|
2478
2572
|
|
2479
|
-
private def em_delete_prev_char(key)
|
2573
|
+
private def em_delete_prev_char(key, arg: 1)
|
2480
2574
|
if @is_multiline and @cursor == 0 and @line_index > 0
|
2481
2575
|
@buffer_of_lines[@line_index] = @line
|
2482
2576
|
@cursor = calculate_width(@buffer_of_lines[@line_index - 1])
|
@@ -2494,9 +2588,16 @@ class Reline::LineEditor
|
|
2494
2588
|
@cursor -= width
|
2495
2589
|
@cursor_max -= width
|
2496
2590
|
end
|
2591
|
+
arg -= 1
|
2592
|
+
em_delete_prev_char(key, arg: arg) if arg > 0
|
2497
2593
|
end
|
2498
2594
|
alias_method :backward_delete_char, :em_delete_prev_char
|
2499
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.
|
2500
2601
|
private def ed_kill_line(key)
|
2501
2602
|
if @line.bytesize > @byte_pointer
|
2502
2603
|
@line, deleted = byteslice!(@line, @byte_pointer, @line.bytesize - @byte_pointer)
|
@@ -2513,8 +2614,14 @@ class Reline::LineEditor
|
|
2513
2614
|
@rest_height += 1
|
2514
2615
|
end
|
2515
2616
|
end
|
2617
|
+
alias_method :kill_line, :ed_kill_line
|
2516
2618
|
|
2517
|
-
|
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)
|
2518
2625
|
if @byte_pointer > 0
|
2519
2626
|
@line, deleted = byteslice!(@line, 0, @byte_pointer)
|
2520
2627
|
@byte_pointer = 0
|
@@ -2523,7 +2630,22 @@ class Reline::LineEditor
|
|
2523
2630
|
@cursor = 0
|
2524
2631
|
end
|
2525
2632
|
end
|
2526
|
-
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
|
2527
2649
|
|
2528
2650
|
private def em_delete(key)
|
2529
2651
|
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
|
@@ -3028,7 +3150,14 @@ class Reline::LineEditor
|
|
3028
3150
|
|
3029
3151
|
private def ed_argument_digit(key)
|
3030
3152
|
if @vi_arg.nil?
|
3031
|
-
|
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
|
3032
3161
|
@vi_arg = key.chr.to_i
|
3033
3162
|
end
|
3034
3163
|
else
|