reline 0.2.8.pre.9 → 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/lib/reline/ansi.rb +50 -23
- data/lib/reline/config.rb +5 -0
- 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 +206 -92
- data/lib/reline/terminfo.rb +50 -5
- 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 +29 -21
- metadata +5 -5
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,6 +151,73 @@ 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)
|
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
|
153
221
|
@old_trap = Signal.trap('INT') {
|
154
222
|
clear_dialog
|
155
223
|
if @scroll_partial_screen
|
@@ -167,7 +235,7 @@ 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
241
|
begin
|
@@ -177,44 +245,6 @@ class Reline::LineEditor
|
|
177
245
|
}
|
178
246
|
rescue ArgumentError
|
179
247
|
end
|
180
|
-
Reline::IOGate.set_winch_handler do
|
181
|
-
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
182
|
-
old_screen_size = @screen_size
|
183
|
-
@screen_size = Reline::IOGate.get_screen_size
|
184
|
-
@screen_height = @screen_size.first
|
185
|
-
if old_screen_size.last < @screen_size.last # columns increase
|
186
|
-
@rerender_all = true
|
187
|
-
rerender
|
188
|
-
else
|
189
|
-
back = 0
|
190
|
-
new_buffer = whole_lines
|
191
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
192
|
-
new_buffer.each_with_index do |line, index|
|
193
|
-
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
194
|
-
width = prompt_width + calculate_width(line)
|
195
|
-
height = calculate_height_by_width(width)
|
196
|
-
back += height
|
197
|
-
end
|
198
|
-
@highest_in_all = back
|
199
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
200
|
-
@first_line_started_from =
|
201
|
-
if @line_index.zero?
|
202
|
-
0
|
203
|
-
else
|
204
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
205
|
-
end
|
206
|
-
if @prompt_proc
|
207
|
-
prompt = prompt_list[@line_index]
|
208
|
-
prompt_width = calculate_width(prompt, true)
|
209
|
-
end
|
210
|
-
calculate_nearest_cursor
|
211
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
212
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
213
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
214
|
-
@rerender_all = true
|
215
|
-
end
|
216
|
-
end
|
217
|
-
@block_elem_width = Reline::Unicode.calculate_width('█')
|
218
248
|
end
|
219
249
|
|
220
250
|
def finalize
|
@@ -230,7 +260,7 @@ class Reline::LineEditor
|
|
230
260
|
end
|
231
261
|
|
232
262
|
def reset_variables(prompt = '', encoding:)
|
233
|
-
@prompt = prompt
|
263
|
+
@prompt = prompt.gsub("\n", "\\n")
|
234
264
|
@mark_pointer = nil
|
235
265
|
@encoding = encoding
|
236
266
|
@is_multiline = false
|
@@ -264,6 +294,7 @@ class Reline::LineEditor
|
|
264
294
|
@auto_indent_proc = nil
|
265
295
|
@dialogs = []
|
266
296
|
@last_key = nil
|
297
|
+
@resized = false
|
267
298
|
reset_line
|
268
299
|
end
|
269
300
|
|
@@ -407,7 +438,7 @@ class Reline::LineEditor
|
|
407
438
|
show_menu
|
408
439
|
@menu_info = nil
|
409
440
|
end
|
410
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines
|
441
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
|
411
442
|
if @cleared
|
412
443
|
clear_screen_buffer(prompt, prompt_list, prompt_width)
|
413
444
|
@cleared = false
|
@@ -418,7 +449,7 @@ class Reline::LineEditor
|
|
418
449
|
Reline::IOGate.move_cursor_up(@first_line_started_from + @started_from - @scroll_partial_screen)
|
419
450
|
Reline::IOGate.move_cursor_column(0)
|
420
451
|
@scroll_partial_screen = nil
|
421
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines
|
452
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
|
422
453
|
if @previous_line_index
|
423
454
|
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
424
455
|
else
|
@@ -464,7 +495,7 @@ class Reline::LineEditor
|
|
464
495
|
end
|
465
496
|
line = modify_lines(new_lines)[@line_index]
|
466
497
|
clear_dialog
|
467
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines
|
498
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
|
468
499
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
469
500
|
move_cursor_down(@highest_in_all - (@first_line_started_from + @highest_in_this - 1) - 1)
|
470
501
|
scroll_down(1)
|
@@ -473,7 +504,7 @@ class Reline::LineEditor
|
|
473
504
|
else
|
474
505
|
if not rendered and not @in_pasting
|
475
506
|
line = modify_lines(whole_lines)[@line_index]
|
476
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines
|
507
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines)
|
477
508
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
478
509
|
end
|
479
510
|
render_dialog((prompt_width + @cursor) % @screen_size.last)
|
@@ -560,7 +591,7 @@ class Reline::LineEditor
|
|
560
591
|
|
561
592
|
class Dialog
|
562
593
|
attr_reader :name, :contents, :width
|
563
|
-
attr_accessor :scroll_top, :scrollbar_pos, :column, :vertical_offset, :lines_backup, :trap_key
|
594
|
+
attr_accessor :scroll_top, :scrollbar_pos, :pointer, :column, :vertical_offset, :lines_backup, :trap_key
|
564
595
|
|
565
596
|
def initialize(name, config, proc_scope)
|
566
597
|
@name = name
|
@@ -568,6 +599,7 @@ class Reline::LineEditor
|
|
568
599
|
@proc_scope = proc_scope
|
569
600
|
@width = nil
|
570
601
|
@scroll_top = 0
|
602
|
+
@trap_key = nil
|
571
603
|
end
|
572
604
|
|
573
605
|
def set_cursor_pos(col, row)
|
@@ -605,11 +637,15 @@ class Reline::LineEditor
|
|
605
637
|
end
|
606
638
|
|
607
639
|
def add_dialog_proc(name, p, context = nil)
|
608
|
-
|
609
|
-
@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
|
610
646
|
end
|
611
647
|
|
612
|
-
|
648
|
+
DIALOG_DEFAULT_HEIGHT = 20
|
613
649
|
private def render_dialog(cursor_column)
|
614
650
|
@dialogs.each do |dialog|
|
615
651
|
render_each_dialog(dialog, cursor_column)
|
@@ -622,6 +658,7 @@ class Reline::LineEditor
|
|
622
658
|
|
623
659
|
private def render_each_dialog(dialog, cursor_column)
|
624
660
|
if @in_pasting
|
661
|
+
clear_each_dialog(dialog)
|
625
662
|
dialog.contents = nil
|
626
663
|
dialog.trap_key = nil
|
627
664
|
return
|
@@ -643,27 +680,30 @@ class Reline::LineEditor
|
|
643
680
|
end
|
644
681
|
old_dialog = dialog.clone
|
645
682
|
dialog.contents = dialog_render_info.contents
|
646
|
-
pointer =
|
683
|
+
pointer = dialog.pointer
|
647
684
|
if dialog_render_info.width
|
648
685
|
dialog.width = dialog_render_info.width
|
649
686
|
else
|
650
687
|
dialog.width = dialog.contents.map { |l| calculate_width(l, true) }.max
|
651
688
|
end
|
652
|
-
height = dialog_render_info.height ||
|
689
|
+
height = dialog_render_info.height || DIALOG_DEFAULT_HEIGHT
|
653
690
|
height = dialog.contents.size if dialog.contents.size < height
|
654
691
|
if dialog.contents.size > height
|
655
|
-
if
|
656
|
-
if
|
692
|
+
if dialog.pointer
|
693
|
+
if dialog.pointer < 0
|
657
694
|
dialog.scroll_top = 0
|
658
|
-
elsif (
|
659
|
-
dialog.scroll_top =
|
660
|
-
elsif (
|
661
|
-
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
|
662
699
|
end
|
663
|
-
pointer =
|
700
|
+
pointer = dialog.pointer - dialog.scroll_top
|
664
701
|
end
|
665
702
|
dialog.contents = dialog.contents[dialog.scroll_top, height]
|
666
703
|
end
|
704
|
+
if dialog.contents and dialog.scroll_top >= dialog.contents.size
|
705
|
+
dialog.scroll_top = dialog.contents.size - height
|
706
|
+
end
|
667
707
|
if dialog_render_info.scrollbar and dialog_render_info.contents.size > height
|
668
708
|
bar_max_height = height * 2
|
669
709
|
moving_distance = (dialog_render_info.contents.size - height) * 2
|
@@ -674,25 +714,28 @@ class Reline::LineEditor
|
|
674
714
|
dialog.scrollbar_pos = nil
|
675
715
|
end
|
676
716
|
upper_space = @first_line_started_from - @started_from
|
677
|
-
lower_space = @highest_in_all - @first_line_started_from - @started_from - 1
|
678
717
|
dialog.column = dialog_render_info.pos.x
|
679
|
-
|
718
|
+
dialog.width += @block_elem_width if dialog.scrollbar_pos
|
719
|
+
diff = (dialog.column + dialog.width) - (@screen_size.last)
|
680
720
|
if diff > 0
|
681
721
|
dialog.column -= diff
|
682
722
|
end
|
683
|
-
if (
|
723
|
+
if (@rest_height - dialog_render_info.pos.y) >= height
|
684
724
|
dialog.vertical_offset = dialog_render_info.pos.y + 1
|
685
725
|
elsif upper_space >= height
|
686
726
|
dialog.vertical_offset = dialog_render_info.pos.y - height
|
687
727
|
else
|
688
|
-
if (
|
728
|
+
if (@rest_height - dialog_render_info.pos.y) < height
|
689
729
|
scroll_down(height + dialog_render_info.pos.y)
|
690
730
|
move_cursor_up(height + dialog_render_info.pos.y)
|
691
731
|
end
|
692
732
|
dialog.vertical_offset = dialog_render_info.pos.y + 1
|
693
733
|
end
|
694
734
|
Reline::IOGate.hide_cursor
|
695
|
-
dialog.
|
735
|
+
if dialog.column < 0
|
736
|
+
dialog.column = 0
|
737
|
+
dialog.width = @screen_size.last
|
738
|
+
end
|
696
739
|
reset_dialog(dialog, old_dialog)
|
697
740
|
move_cursor_down(dialog.vertical_offset)
|
698
741
|
Reline::IOGate.move_cursor_column(dialog.column)
|
@@ -711,19 +754,18 @@ class Reline::LineEditor
|
|
711
754
|
@output.write "\e[#{bg_color}m#{str}"
|
712
755
|
if dialog.scrollbar_pos and (dialog.scrollbar_pos != old_dialog.scrollbar_pos or dialog.column != old_dialog.column)
|
713
756
|
@output.write "\e[37m"
|
714
|
-
if dialog.scrollbar_pos <= (i * 2) and (i * 2 +
|
715
|
-
@output.write
|
757
|
+
if dialog.scrollbar_pos <= (i * 2) and (i * 2 + 1) < (dialog.scrollbar_pos + bar_height)
|
758
|
+
@output.write @full_block
|
716
759
|
elsif dialog.scrollbar_pos <= (i * 2) and (i * 2) < (dialog.scrollbar_pos + bar_height)
|
717
|
-
@output.write
|
760
|
+
@output.write @upper_half_block
|
718
761
|
str += ''
|
719
|
-
elsif dialog.scrollbar_pos <= (i * 2 +
|
720
|
-
@output.write
|
762
|
+
elsif dialog.scrollbar_pos <= (i * 2 + 1) and (i * 2) < (dialog.scrollbar_pos + bar_height)
|
763
|
+
@output.write @lower_half_block
|
721
764
|
else
|
722
765
|
@output.write ' ' * @block_elem_width
|
723
766
|
end
|
724
|
-
@output.write "\e[39m"
|
725
767
|
end
|
726
|
-
@output.write "\e[
|
768
|
+
@output.write "\e[0m"
|
727
769
|
Reline::IOGate.move_cursor_column(dialog.column)
|
728
770
|
move_cursor_down(1) if i < (dialog.contents.size - 1)
|
729
771
|
end
|
@@ -741,7 +783,7 @@ class Reline::LineEditor
|
|
741
783
|
|
742
784
|
private def reset_dialog(dialog, old_dialog)
|
743
785
|
return if dialog.lines_backup.nil? or old_dialog.contents.nil?
|
744
|
-
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])
|
745
787
|
visual_lines = []
|
746
788
|
visual_start = nil
|
747
789
|
dialog.lines_backup[:lines].each_with_index { |l, i|
|
@@ -769,7 +811,7 @@ class Reline::LineEditor
|
|
769
811
|
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, old_dialog.width)
|
770
812
|
s = padding_space_with_escape_sequences(s, old_dialog.width)
|
771
813
|
end
|
772
|
-
@output.write "\e[
|
814
|
+
@output.write "\e[0m#{s}\e[0m"
|
773
815
|
move_cursor_down(1) if i < (line_num - 1)
|
774
816
|
end
|
775
817
|
move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
|
@@ -787,7 +829,7 @@ class Reline::LineEditor
|
|
787
829
|
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, old_dialog.width)
|
788
830
|
s = padding_space_with_escape_sequences(s, old_dialog.width)
|
789
831
|
end
|
790
|
-
@output.write "\e[
|
832
|
+
@output.write "\e[0m#{s}\e[0m"
|
791
833
|
move_cursor_down(1) if i < (line_num - 1)
|
792
834
|
end
|
793
835
|
move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff)
|
@@ -806,7 +848,7 @@ class Reline::LineEditor
|
|
806
848
|
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, width)
|
807
849
|
s = padding_space_with_escape_sequences(s, dialog.width)
|
808
850
|
end
|
809
|
-
@output.write "\e[
|
851
|
+
@output.write "\e[0m#{s}\e[0m"
|
810
852
|
move_cursor_down(1) if i < (line_num - 1)
|
811
853
|
end
|
812
854
|
move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
|
@@ -823,10 +865,11 @@ class Reline::LineEditor
|
|
823
865
|
s = ' ' * width
|
824
866
|
else
|
825
867
|
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column + dialog.width, width)
|
826
|
-
|
868
|
+
rerender_width = old_dialog.width - dialog.width
|
869
|
+
s = padding_space_with_escape_sequences(s, rerender_width)
|
827
870
|
end
|
828
871
|
Reline::IOGate.move_cursor_column(dialog.column + dialog.width)
|
829
|
-
@output.write "\e[
|
872
|
+
@output.write "\e[0m#{s}\e[0m"
|
830
873
|
move_cursor_down(1) if i < (line_num - 1)
|
831
874
|
end
|
832
875
|
move_cursor_up(old_dialog.vertical_offset + line_num - 1 + y_diff)
|
@@ -843,7 +886,7 @@ class Reline::LineEditor
|
|
843
886
|
private def clear_each_dialog(dialog)
|
844
887
|
dialog.trap_key = nil
|
845
888
|
return unless dialog.contents
|
846
|
-
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])
|
847
890
|
visual_lines = []
|
848
891
|
visual_lines_under_dialog = []
|
849
892
|
visual_start = nil
|
@@ -866,10 +909,10 @@ class Reline::LineEditor
|
|
866
909
|
Reline::IOGate.move_cursor_column(dialog.column)
|
867
910
|
str = Reline::Unicode.take_range(visual_lines_under_dialog[i], dialog.column, dialog.width)
|
868
911
|
str = padding_space_with_escape_sequences(str, dialog.width)
|
869
|
-
@output.write "\e[
|
912
|
+
@output.write "\e[0m#{str}\e[0m"
|
870
913
|
else
|
871
914
|
Reline::IOGate.move_cursor_column(dialog.column)
|
872
|
-
@output.write "\e[
|
915
|
+
@output.write "\e[0m#{' ' * dialog.width}\e[0m"
|
873
916
|
end
|
874
917
|
move_cursor_down(1) if i < (dialog_vertical_size - 1)
|
875
918
|
end
|
@@ -928,7 +971,7 @@ class Reline::LineEditor
|
|
928
971
|
end
|
929
972
|
|
930
973
|
def just_move_cursor
|
931
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines
|
974
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines)
|
932
975
|
move_cursor_up(@started_from)
|
933
976
|
new_first_line_started_from =
|
934
977
|
if @line_index.zero?
|
@@ -965,7 +1008,7 @@ class Reline::LineEditor
|
|
965
1008
|
else
|
966
1009
|
new_lines = whole_lines
|
967
1010
|
end
|
968
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines
|
1011
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
|
969
1012
|
all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
|
970
1013
|
diff = all_height - @highest_in_all
|
971
1014
|
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
@@ -1012,7 +1055,7 @@ class Reline::LineEditor
|
|
1012
1055
|
Reline::IOGate.move_cursor_column(0)
|
1013
1056
|
back = 0
|
1014
1057
|
new_buffer = whole_lines
|
1015
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer
|
1058
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer)
|
1016
1059
|
new_buffer.each_with_index do |line, index|
|
1017
1060
|
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
1018
1061
|
width = prompt_width + calculate_width(line)
|
@@ -1402,7 +1445,10 @@ class Reline::LineEditor
|
|
1402
1445
|
end
|
1403
1446
|
@waiting_operator_proc = nil
|
1404
1447
|
@waiting_operator_vi_arg = nil
|
1405
|
-
@vi_arg
|
1448
|
+
if @vi_arg
|
1449
|
+
@rerender_all = true
|
1450
|
+
@vi_arg = nil
|
1451
|
+
end
|
1406
1452
|
else
|
1407
1453
|
block.(false)
|
1408
1454
|
end
|
@@ -1453,7 +1499,10 @@ class Reline::LineEditor
|
|
1453
1499
|
wrap_method_call(method_symbol, method_obj, key) if method_obj
|
1454
1500
|
end
|
1455
1501
|
@kill_ring.process
|
1456
|
-
@vi_arg
|
1502
|
+
if @vi_arg
|
1503
|
+
@rerender_al = true
|
1504
|
+
@vi_arg = nil
|
1505
|
+
end
|
1457
1506
|
elsif @vi_arg
|
1458
1507
|
if key.chr =~ /[0-9]/
|
1459
1508
|
ed_argument_digit(key)
|
@@ -1470,7 +1519,10 @@ class Reline::LineEditor
|
|
1470
1519
|
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
1471
1520
|
end
|
1472
1521
|
@kill_ring.process
|
1473
|
-
@vi_arg
|
1522
|
+
if @vi_arg
|
1523
|
+
@rerender_all = true
|
1524
|
+
@vi_arg = nil
|
1525
|
+
end
|
1474
1526
|
end
|
1475
1527
|
elsif @waiting_proc
|
1476
1528
|
@waiting_proc.(key)
|
@@ -1909,6 +1961,8 @@ class Reline::LineEditor
|
|
1909
1961
|
end
|
1910
1962
|
end
|
1911
1963
|
|
1964
|
+
# Editline:: +ed-unassigned+ This editor command always results in an error.
|
1965
|
+
# GNU Readline:: There is no corresponding macro.
|
1912
1966
|
private def ed_unassigned(key) end # do nothing
|
1913
1967
|
|
1914
1968
|
private def process_insert(force: false)
|
@@ -1926,6 +1980,19 @@ class Reline::LineEditor
|
|
1926
1980
|
@continuous_insertion_buffer.clear
|
1927
1981
|
end
|
1928
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.
|
1929
1996
|
private def ed_insert(key)
|
1930
1997
|
str = nil
|
1931
1998
|
width = nil
|
@@ -1962,8 +2029,16 @@ class Reline::LineEditor
|
|
1962
2029
|
last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
1963
2030
|
@byte_pointer += bytesize
|
1964
2031
|
last_mbchar = @line.byteslice((@byte_pointer - bytesize - last_byte_size), last_byte_size)
|
1965
|
-
|
1966
|
-
|
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
|
1967
2042
|
end
|
1968
2043
|
@cursor += width
|
1969
2044
|
@cursor_max += width
|
@@ -1976,6 +2051,8 @@ class Reline::LineEditor
|
|
1976
2051
|
arg.times do
|
1977
2052
|
if key == "\C-j".ord or key == "\C-m".ord
|
1978
2053
|
key_newline(key)
|
2054
|
+
elsif key == 0
|
2055
|
+
# Ignore NUL.
|
1979
2056
|
else
|
1980
2057
|
ed_insert(key)
|
1981
2058
|
end
|
@@ -2410,6 +2487,7 @@ class Reline::LineEditor
|
|
2410
2487
|
arg -= 1
|
2411
2488
|
ed_prev_history(key, arg: arg) if arg > 0
|
2412
2489
|
end
|
2490
|
+
alias_method :previous_history, :ed_prev_history
|
2413
2491
|
|
2414
2492
|
private def ed_next_history(key, arg: 1)
|
2415
2493
|
if @is_multiline and @line_index < (@buffer_of_lines.size - 1)
|
@@ -2457,6 +2535,7 @@ class Reline::LineEditor
|
|
2457
2535
|
arg -= 1
|
2458
2536
|
ed_next_history(key, arg: arg) if arg > 0
|
2459
2537
|
end
|
2538
|
+
alias_method :next_history, :ed_next_history
|
2460
2539
|
|
2461
2540
|
private def ed_newline(key)
|
2462
2541
|
process_insert(force: true)
|
@@ -2491,7 +2570,7 @@ class Reline::LineEditor
|
|
2491
2570
|
end
|
2492
2571
|
end
|
2493
2572
|
|
2494
|
-
private def em_delete_prev_char(key)
|
2573
|
+
private def em_delete_prev_char(key, arg: 1)
|
2495
2574
|
if @is_multiline and @cursor == 0 and @line_index > 0
|
2496
2575
|
@buffer_of_lines[@line_index] = @line
|
2497
2576
|
@cursor = calculate_width(@buffer_of_lines[@line_index - 1])
|
@@ -2509,9 +2588,16 @@ class Reline::LineEditor
|
|
2509
2588
|
@cursor -= width
|
2510
2589
|
@cursor_max -= width
|
2511
2590
|
end
|
2591
|
+
arg -= 1
|
2592
|
+
em_delete_prev_char(key, arg: arg) if arg > 0
|
2512
2593
|
end
|
2513
2594
|
alias_method :backward_delete_char, :em_delete_prev_char
|
2514
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.
|
2515
2601
|
private def ed_kill_line(key)
|
2516
2602
|
if @line.bytesize > @byte_pointer
|
2517
2603
|
@line, deleted = byteslice!(@line, @byte_pointer, @line.bytesize - @byte_pointer)
|
@@ -2528,8 +2614,14 @@ class Reline::LineEditor
|
|
2528
2614
|
@rest_height += 1
|
2529
2615
|
end
|
2530
2616
|
end
|
2617
|
+
alias_method :kill_line, :ed_kill_line
|
2531
2618
|
|
2532
|
-
|
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)
|
2533
2625
|
if @byte_pointer > 0
|
2534
2626
|
@line, deleted = byteslice!(@line, 0, @byte_pointer)
|
2535
2627
|
@byte_pointer = 0
|
@@ -2538,7 +2630,22 @@ class Reline::LineEditor
|
|
2538
2630
|
@cursor = 0
|
2539
2631
|
end
|
2540
2632
|
end
|
2541
|
-
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
|
2542
2649
|
|
2543
2650
|
private def em_delete(key)
|
2544
2651
|
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
|
@@ -3043,7 +3150,14 @@ class Reline::LineEditor
|
|
3043
3150
|
|
3044
3151
|
private def ed_argument_digit(key)
|
3045
3152
|
if @vi_arg.nil?
|
3046
|
-
|
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
|
3047
3161
|
@vi_arg = key.chr.to_i
|
3048
3162
|
end
|
3049
3163
|
else
|