reline 0.1.10 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -0
- data/lib/reline.rb +8 -2
- data/lib/reline/config.rb +8 -7
- data/lib/reline/line_editor.rb +149 -54
- data/lib/reline/line_editor.rb.orig +157 -67
- data/lib/reline/unicode.rb +6 -4
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +25 -0
- data/license_of_rb-readline +25 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f165aeb5368335d41b08932f34d0457d612fe4dbe334748964c2bafaaa518b54
|
4
|
+
data.tar.gz: 4e268dfea3a18f61b6f0bdbbf4d7dd292b3f2d62e178835c971f7b9d3da96613
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c56912f662f9c0e53d793ff108ad44fa2ef231b07fa579ab11e793639b27dd076b6dcb54c7dcc1f68c52a73fbabddf8f41a9d8ea8ea53c06cee481628ea9e18
|
7
|
+
data.tar.gz: acc258dcbe8b5a3dbe536cc3084906bae2006972f7a3e34353f7f87bbb9b78d692e68b479abb412aae18c396efa2f8dc0210fabc04451cce3f48e7ac8a572093
|
data/README.md
CHANGED
@@ -11,3 +11,7 @@ Reline is compatible with the API of Ruby's stdlib 'readline', GNU Readline and
|
|
11
11
|
## License
|
12
12
|
|
13
13
|
The gem is available as open source under the terms of the [Ruby License](https://www.ruby-lang.org/en/about/license.txt).
|
14
|
+
|
15
|
+
## Acknowledgments for [rb-readline](https://github.com/ConnorAtherton/rb-readline)
|
16
|
+
|
17
|
+
In developing Reline, we have used some of the rb-readline implementation, so this library includes [copyright notice, list of conditions and the disclaimer](license_of_rb-readline) under the 3-Clause BSD License. Reline would never have been developed without rb-readline. Thank you for the tremendous accomplishments.
|
data/lib/reline.rb
CHANGED
@@ -36,7 +36,6 @@ module Reline
|
|
36
36
|
attr_accessor :config
|
37
37
|
attr_accessor :key_stroke
|
38
38
|
attr_accessor :line_editor
|
39
|
-
attr_accessor :ambiguous_width
|
40
39
|
attr_accessor :last_incremental_search
|
41
40
|
attr_reader :output
|
42
41
|
|
@@ -244,6 +243,7 @@ module Reline
|
|
244
243
|
loop do
|
245
244
|
prev_pasting_state = Reline::IOGate.in_pasting?
|
246
245
|
read_io(config.keyseq_timeout) { |inputs|
|
246
|
+
line_editor.set_pasting_state(Reline::IOGate.in_pasting?)
|
247
247
|
inputs.each { |c|
|
248
248
|
line_editor.input_key(c)
|
249
249
|
line_editor.rerender
|
@@ -254,6 +254,7 @@ module Reline
|
|
254
254
|
end
|
255
255
|
}
|
256
256
|
if prev_pasting_state == true and not Reline::IOGate.in_pasting? and not line_editor.finished?
|
257
|
+
line_editor.set_pasting_state(false)
|
257
258
|
prev_pasting_state = false
|
258
259
|
line_editor.rerender_all
|
259
260
|
end
|
@@ -356,9 +357,14 @@ module Reline
|
|
356
357
|
end
|
357
358
|
end
|
358
359
|
|
360
|
+
def ambiguous_width
|
361
|
+
may_req_ambiguous_char_width unless defined? @ambiguous_width
|
362
|
+
@ambiguous_width
|
363
|
+
end
|
364
|
+
|
359
365
|
private def may_req_ambiguous_char_width
|
360
366
|
@ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
|
361
|
-
return if ambiguous_width
|
367
|
+
return if @ambiguous_width
|
362
368
|
Reline::IOGate.move_cursor_column(0)
|
363
369
|
begin
|
364
370
|
output.write "\u{25bd}"
|
data/lib/reline/config.rb
CHANGED
@@ -34,10 +34,11 @@ class Reline::Config
|
|
34
34
|
show-all-if-unmodified
|
35
35
|
visible-stats
|
36
36
|
show-mode-in-prompt
|
37
|
-
vi-cmd-mode-
|
38
|
-
vi-ins-mode-
|
37
|
+
vi-cmd-mode-string
|
38
|
+
vi-ins-mode-string
|
39
39
|
emacs-mode-string
|
40
40
|
enable-bracketed-paste
|
41
|
+
isearch-terminators
|
41
42
|
}
|
42
43
|
VARIABLE_NAME_SYMBOLS = VARIABLE_NAMES.map { |v| :"#{v.tr(?-, ?_)}" }
|
43
44
|
VARIABLE_NAME_SYMBOLS.each do |v|
|
@@ -55,8 +56,8 @@ class Reline::Config
|
|
55
56
|
@key_actors[:emacs] = Reline::KeyActor::Emacs.new
|
56
57
|
@key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new
|
57
58
|
@key_actors[:vi_command] = Reline::KeyActor::ViCommand.new
|
58
|
-
@
|
59
|
-
@
|
59
|
+
@vi_cmd_mode_string = '(cmd)'
|
60
|
+
@vi_ins_mode_string = '(ins)'
|
60
61
|
@emacs_mode_string = '@'
|
61
62
|
# https://tiswww.case.edu/php/chet/readline/readline.html#IDX25
|
62
63
|
@history_size = -1 # unlimited
|
@@ -238,7 +239,7 @@ class Reline::Config
|
|
238
239
|
when 'completion-query-items'
|
239
240
|
@completion_query_items = value.to_i
|
240
241
|
when 'isearch-terminators'
|
241
|
-
@isearch_terminators =
|
242
|
+
@isearch_terminators = retrieve_string(value)
|
242
243
|
when 'editing-mode'
|
243
244
|
case value
|
244
245
|
when 'emacs'
|
@@ -269,9 +270,9 @@ class Reline::Config
|
|
269
270
|
@show_mode_in_prompt = false
|
270
271
|
end
|
271
272
|
when 'vi-cmd-mode-string'
|
272
|
-
@
|
273
|
+
@vi_cmd_mode_string = retrieve_string(value)
|
273
274
|
when 'vi-ins-mode-string'
|
274
|
-
@
|
275
|
+
@vi_ins_mode_string = retrieve_string(value)
|
275
276
|
when 'emacs-mode-string'
|
276
277
|
@emacs_mode_string = retrieve_string(value)
|
277
278
|
when *VARIABLE_NAMES then
|
data/lib/reline/line_editor.rb
CHANGED
@@ -58,14 +58,38 @@ class Reline::LineEditor
|
|
58
58
|
reset_variables(encoding: encoding)
|
59
59
|
end
|
60
60
|
|
61
|
+
def set_pasting_state(in_pasting)
|
62
|
+
@in_pasting = in_pasting
|
63
|
+
end
|
64
|
+
|
61
65
|
def simplified_rendering?
|
62
66
|
if finished?
|
63
67
|
false
|
64
68
|
elsif @just_cursor_moving and not @rerender_all
|
65
69
|
true
|
66
70
|
else
|
67
|
-
not @rerender_all and not finished? and
|
71
|
+
not @rerender_all and not finished? and @in_pasting
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private def check_mode_string
|
76
|
+
mode_string = nil
|
77
|
+
if @config.show_mode_in_prompt
|
78
|
+
if @config.editing_mode_is?(:vi_command)
|
79
|
+
mode_string = @config.vi_cmd_mode_string
|
80
|
+
elsif @config.editing_mode_is?(:vi_insert)
|
81
|
+
mode_string = @config.vi_ins_mode_string
|
82
|
+
elsif @config.editing_mode_is?(:emacs)
|
83
|
+
mode_string = @config.emacs_mode_string
|
84
|
+
else
|
85
|
+
mode_string = '?'
|
86
|
+
end
|
68
87
|
end
|
88
|
+
if mode_string != @prev_mode_string
|
89
|
+
@rerender_all = true
|
90
|
+
end
|
91
|
+
@prev_mode_string = mode_string
|
92
|
+
mode_string
|
69
93
|
end
|
70
94
|
|
71
95
|
private def check_multiline_prompt(buffer, prompt)
|
@@ -78,7 +102,11 @@ class Reline::LineEditor
|
|
78
102
|
else
|
79
103
|
prompt = @prompt
|
80
104
|
end
|
81
|
-
|
105
|
+
if simplified_rendering?
|
106
|
+
mode_string = check_mode_string
|
107
|
+
prompt = mode_string + prompt if mode_string
|
108
|
+
return [prompt, calculate_width(prompt, true), [prompt] * buffer.size]
|
109
|
+
end
|
82
110
|
if @prompt_proc
|
83
111
|
use_cached_prompt_list = false
|
84
112
|
if @cached_prompt_list
|
@@ -88,6 +116,7 @@ class Reline::LineEditor
|
|
88
116
|
use_cached_prompt_list = true
|
89
117
|
end
|
90
118
|
end
|
119
|
+
use_cached_prompt_list = false if @rerender_all
|
91
120
|
if use_cached_prompt_list
|
92
121
|
prompt_list = @cached_prompt_list
|
93
122
|
else
|
@@ -95,35 +124,23 @@ class Reline::LineEditor
|
|
95
124
|
@prompt_cache_time = Time.now.to_f
|
96
125
|
end
|
97
126
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
98
|
-
if
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
127
|
+
prompt_list = [prompt] if prompt_list.empty?
|
128
|
+
mode_string = check_mode_string
|
129
|
+
prompt_list = prompt_list.map{ |pr| mode_string + pr } if mode_string
|
130
|
+
prompt = prompt_list[@line_index]
|
131
|
+
prompt = prompt_list[0] if prompt.nil?
|
132
|
+
prompt = prompt_list.last if prompt.nil?
|
133
|
+
if buffer.size > prompt_list.size
|
134
|
+
(buffer.size - prompt_list.size).times do
|
135
|
+
prompt_list << prompt_list.last
|
107
136
|
end
|
108
|
-
prompt_list.map!{ |pr| mode_icon + pr }
|
109
137
|
end
|
110
|
-
prompt = prompt_list[@line_index]
|
111
138
|
prompt_width = calculate_width(prompt, true)
|
112
139
|
[prompt, prompt_width, prompt_list]
|
113
140
|
else
|
141
|
+
mode_string = check_mode_string
|
142
|
+
prompt = mode_string + prompt if mode_string
|
114
143
|
prompt_width = calculate_width(prompt, true)
|
115
|
-
if @config.show_mode_in_prompt
|
116
|
-
if @config.editing_mode_is?(:vi_command)
|
117
|
-
mode_icon = @config.vi_cmd_mode_icon
|
118
|
-
elsif @config.editing_mode_is?(:vi_insert)
|
119
|
-
mode_icon = @config.vi_ins_mode_icon
|
120
|
-
elsif @config.editing_mode_is?(:emacs)
|
121
|
-
mode_icon = @config.emacs_mode_string
|
122
|
-
else
|
123
|
-
mode_icon = '?'
|
124
|
-
end
|
125
|
-
prompt = mode_icon + prompt
|
126
|
-
end
|
127
144
|
[prompt, prompt_width, nil]
|
128
145
|
end
|
129
146
|
end
|
@@ -134,6 +151,13 @@ class Reline::LineEditor
|
|
134
151
|
@screen_height = @screen_size.first
|
135
152
|
reset_variables(prompt, encoding: encoding)
|
136
153
|
@old_trap = Signal.trap('SIGINT') {
|
154
|
+
if @scroll_partial_screen
|
155
|
+
move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1)
|
156
|
+
else
|
157
|
+
move_cursor_down(@highest_in_all - @line_index - 1)
|
158
|
+
end
|
159
|
+
Reline::IOGate.move_cursor_column(0)
|
160
|
+
scroll_down(1)
|
137
161
|
@old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
|
138
162
|
raise Interrupt
|
139
163
|
}
|
@@ -213,6 +237,10 @@ class Reline::LineEditor
|
|
213
237
|
@eof = false
|
214
238
|
@continuous_insertion_buffer = String.new(encoding: @encoding)
|
215
239
|
@scroll_partial_screen = nil
|
240
|
+
@prev_mode_string = nil
|
241
|
+
@drop_terminate_spaces = false
|
242
|
+
@in_pasting = false
|
243
|
+
@auto_indent_proc = nil
|
216
244
|
reset_line
|
217
245
|
end
|
218
246
|
|
@@ -316,8 +344,9 @@ class Reline::LineEditor
|
|
316
344
|
else
|
317
345
|
end_of_line_cursor = new_cursor_max
|
318
346
|
end
|
319
|
-
line_to_calc.
|
320
|
-
|
347
|
+
line_to_calc.grapheme_clusters.each do |gc|
|
348
|
+
mbchar = gc.encode(Encoding::UTF_8)
|
349
|
+
mbchar_width = Reline::Unicode.get_mbchar_width(mbchar)
|
321
350
|
now = new_cursor + mbchar_width
|
322
351
|
if now > end_of_line_cursor or now > cursor
|
323
352
|
break
|
@@ -361,10 +390,29 @@ class Reline::LineEditor
|
|
361
390
|
@cleared = false
|
362
391
|
return
|
363
392
|
end
|
393
|
+
if @is_multiline and finished? and @scroll_partial_screen
|
394
|
+
# Re-output all code higher than the screen when finished.
|
395
|
+
Reline::IOGate.move_cursor_up(@first_line_started_from + @started_from - @scroll_partial_screen)
|
396
|
+
Reline::IOGate.move_cursor_column(0)
|
397
|
+
@scroll_partial_screen = nil
|
398
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
399
|
+
if @previous_line_index
|
400
|
+
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
401
|
+
else
|
402
|
+
new_lines = whole_lines
|
403
|
+
end
|
404
|
+
modify_lines(new_lines).each_with_index do |line, index|
|
405
|
+
@output.write "#{prompt_list ? prompt_list[index] : prompt}#{line}\n"
|
406
|
+
Reline::IOGate.erase_after_cursor
|
407
|
+
end
|
408
|
+
@output.flush
|
409
|
+
return
|
410
|
+
end
|
364
411
|
new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
|
365
412
|
# FIXME: end of logical line sometimes breaks
|
413
|
+
rendered = false
|
366
414
|
if @add_newline_to_end_of_buffer
|
367
|
-
rerender_added_newline
|
415
|
+
rerender_added_newline(prompt, prompt_width)
|
368
416
|
@add_newline_to_end_of_buffer = false
|
369
417
|
else
|
370
418
|
if @just_cursor_moving and not @rerender_all
|
@@ -382,20 +430,32 @@ class Reline::LineEditor
|
|
382
430
|
else
|
383
431
|
end
|
384
432
|
end
|
385
|
-
line = modify_lines(whole_lines)[@line_index]
|
386
433
|
if @is_multiline
|
387
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
388
434
|
if finished?
|
389
435
|
# Always rerender on finish because output_modifier_proc may return a different output.
|
436
|
+
if @previous_line_index
|
437
|
+
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
438
|
+
else
|
439
|
+
new_lines = whole_lines
|
440
|
+
end
|
441
|
+
line = modify_lines(new_lines)[@line_index]
|
442
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
390
443
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
444
|
+
move_cursor_down(@highest_in_all - (@first_line_started_from + @highest_in_this - 1) - 1)
|
391
445
|
scroll_down(1)
|
392
446
|
Reline::IOGate.move_cursor_column(0)
|
393
447
|
Reline::IOGate.erase_after_cursor
|
394
448
|
elsif not rendered
|
395
|
-
|
449
|
+
unless @in_pasting
|
450
|
+
line = modify_lines(whole_lines)[@line_index]
|
451
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
452
|
+
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
453
|
+
end
|
396
454
|
end
|
397
455
|
@buffer_of_lines[@line_index] = @line
|
456
|
+
@rest_height = 0 if @scroll_partial_screen
|
398
457
|
else
|
458
|
+
line = modify_lines(whole_lines)[@line_index]
|
399
459
|
render_partial(prompt, prompt_width, line, 0)
|
400
460
|
if finished?
|
401
461
|
scroll_down(1)
|
@@ -438,13 +498,13 @@ class Reline::LineEditor
|
|
438
498
|
end
|
439
499
|
end
|
440
500
|
|
441
|
-
private def rerender_added_newline
|
501
|
+
private def rerender_added_newline(prompt, prompt_width)
|
442
502
|
scroll_down(1)
|
443
|
-
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
444
|
-
prompt, prompt_width, = check_multiline_prompt(new_lines, prompt)
|
445
503
|
@buffer_of_lines[@previous_line_index] = @line
|
446
504
|
@line = @buffer_of_lines[@line_index]
|
447
|
-
|
505
|
+
unless @in_pasting
|
506
|
+
render_partial(prompt, prompt_width, @line, @first_line_started_from + @started_from + 1, with_control: false)
|
507
|
+
end
|
448
508
|
@cursor = @cursor_max = calculate_width(@line)
|
449
509
|
@byte_pointer = @line.bytesize
|
450
510
|
@highest_in_all += @highest_in_this
|
@@ -464,7 +524,7 @@ class Reline::LineEditor
|
|
464
524
|
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
465
525
|
end
|
466
526
|
first_line_diff = new_first_line_started_from - @first_line_started_from
|
467
|
-
new_cursor,
|
527
|
+
new_cursor, new_cursor_max, new_started_from, new_byte_pointer = calculate_nearest_cursor(@buffer_of_lines[@line_index], @cursor, @started_from, @byte_pointer, false)
|
468
528
|
new_started_from = calculate_height_by_width(prompt_width + new_cursor) - 1
|
469
529
|
calculate_scroll_partial_screen(@highest_in_all, new_first_line_started_from + new_started_from)
|
470
530
|
@previous_line_index = nil
|
@@ -478,6 +538,8 @@ class Reline::LineEditor
|
|
478
538
|
@first_line_started_from = new_first_line_started_from
|
479
539
|
@started_from = new_started_from
|
480
540
|
@cursor = new_cursor
|
541
|
+
@cursor_max = new_cursor_max
|
542
|
+
@byte_pointer = new_byte_pointer
|
481
543
|
move_cursor_down(first_line_diff + @started_from)
|
482
544
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
483
545
|
false
|
@@ -551,7 +613,13 @@ class Reline::LineEditor
|
|
551
613
|
new_first_line_started_from = calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
552
614
|
end
|
553
615
|
new_started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
554
|
-
|
616
|
+
calculate_scroll_partial_screen(back, new_first_line_started_from + new_started_from)
|
617
|
+
if @scroll_partial_screen
|
618
|
+
move_cursor_up(@first_line_started_from + @started_from)
|
619
|
+
scroll_down(@screen_height - 1)
|
620
|
+
move_cursor_up(@screen_height)
|
621
|
+
Reline::IOGate.move_cursor_column(0)
|
622
|
+
elsif back > old_highest_in_all
|
555
623
|
scroll_down(back - 1)
|
556
624
|
move_cursor_up(back - 1)
|
557
625
|
elsif back < old_highest_in_all
|
@@ -563,7 +631,6 @@ class Reline::LineEditor
|
|
563
631
|
end
|
564
632
|
move_cursor_up(old_highest_in_all - 1)
|
565
633
|
end
|
566
|
-
calculate_scroll_partial_screen(back, new_first_line_started_from + new_started_from)
|
567
634
|
render_whole_lines(new_buffer, prompt_list || prompt, prompt_width)
|
568
635
|
if @prompt_proc
|
569
636
|
prompt = prompt_list[@line_index]
|
@@ -649,8 +716,8 @@ class Reline::LineEditor
|
|
649
716
|
@highest_in_this = height
|
650
717
|
end
|
651
718
|
move_cursor_up(@started_from)
|
652
|
-
cursor_up_from_last_line = height - 1 - @started_from
|
653
719
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
720
|
+
cursor_up_from_last_line = height - 1 - @started_from
|
654
721
|
end
|
655
722
|
if Reline::Unicode::CSI_REGEXP.match?(prompt + line_to_render)
|
656
723
|
@output.write "\e[0m" # clear character decorations
|
@@ -660,7 +727,7 @@ class Reline::LineEditor
|
|
660
727
|
if line.nil?
|
661
728
|
if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
662
729
|
# reaches the end of line
|
663
|
-
if Reline::IOGate.win?
|
730
|
+
if Reline::IOGate.win? and Reline::IOGate.win_legacy_console?
|
664
731
|
# A newline is automatically inserted if a character is rendered at
|
665
732
|
# eol on command prompt.
|
666
733
|
else
|
@@ -678,7 +745,7 @@ class Reline::LineEditor
|
|
678
745
|
next
|
679
746
|
end
|
680
747
|
@output.write line
|
681
|
-
if Reline::IOGate.win? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last
|
748
|
+
if Reline::IOGate.win? and Reline::IOGate.win_legacy_console? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last
|
682
749
|
# A newline is automatically inserted if a character is rendered at eol on command prompt.
|
683
750
|
@rest_height -= 1 if @rest_height > 0
|
684
751
|
end
|
@@ -1073,7 +1140,7 @@ class Reline::LineEditor
|
|
1073
1140
|
unless completion_occurs
|
1074
1141
|
@completion_state = CompletionState::NORMAL
|
1075
1142
|
end
|
1076
|
-
if not
|
1143
|
+
if not @in_pasting and @just_cursor_moving.nil?
|
1077
1144
|
if @previous_line_index and @buffer_of_lines[@previous_line_index] == @line
|
1078
1145
|
@just_cursor_moving = true
|
1079
1146
|
elsif @previous_line_index.nil? and @buffer_of_lines[@line_index] == @line and old_line == @line
|
@@ -1122,6 +1189,7 @@ class Reline::LineEditor
|
|
1122
1189
|
new_lines = whole_lines
|
1123
1190
|
end
|
1124
1191
|
new_indent = @auto_indent_proc.(new_lines, @line_index, @byte_pointer, @check_new_auto_indent)
|
1192
|
+
new_indent = @cursor_max if new_indent&.> @cursor_max
|
1125
1193
|
if new_indent&.>= 0
|
1126
1194
|
md = new_lines[@line_index].match(/\A */)
|
1127
1195
|
prev_indent = md[0].count(' ')
|
@@ -1276,7 +1344,11 @@ class Reline::LineEditor
|
|
1276
1344
|
if @buffer_of_lines.size == 1 and @line.nil?
|
1277
1345
|
nil
|
1278
1346
|
else
|
1279
|
-
|
1347
|
+
if @previous_line_index
|
1348
|
+
whole_lines(index: @previous_line_index, line: @line).join("\n")
|
1349
|
+
else
|
1350
|
+
whole_lines.join("\n")
|
1351
|
+
end
|
1280
1352
|
end
|
1281
1353
|
end
|
1282
1354
|
|
@@ -1322,14 +1394,14 @@ class Reline::LineEditor
|
|
1322
1394
|
cursor_line = @line.byteslice(0, @byte_pointer)
|
1323
1395
|
insert_new_line(cursor_line, next_line)
|
1324
1396
|
@cursor = 0
|
1325
|
-
@check_new_auto_indent = true
|
1397
|
+
@check_new_auto_indent = true unless @in_pasting
|
1326
1398
|
end
|
1327
1399
|
end
|
1328
1400
|
|
1329
1401
|
private def ed_unassigned(key) end # do nothing
|
1330
1402
|
|
1331
1403
|
private def process_insert(force: false)
|
1332
|
-
return if @continuous_insertion_buffer.empty? or (
|
1404
|
+
return if @continuous_insertion_buffer.empty? or (@in_pasting and not force)
|
1333
1405
|
width = Reline::Unicode.calculate_width(@continuous_insertion_buffer)
|
1334
1406
|
bytesize = @continuous_insertion_buffer.bytesize
|
1335
1407
|
if @cursor == @cursor_max
|
@@ -1364,7 +1436,7 @@ class Reline::LineEditor
|
|
1364
1436
|
str = key.chr
|
1365
1437
|
bytesize = 1
|
1366
1438
|
end
|
1367
|
-
if
|
1439
|
+
if @in_pasting
|
1368
1440
|
@continuous_insertion_buffer << str
|
1369
1441
|
return
|
1370
1442
|
elsif not @continuous_insertion_buffer.empty?
|
@@ -1593,9 +1665,11 @@ class Reline::LineEditor
|
|
1593
1665
|
searcher = generate_searcher
|
1594
1666
|
searcher.resume(key)
|
1595
1667
|
@searching_prompt = "(reverse-i-search)`': "
|
1668
|
+
termination_keys = ["\C-j".ord]
|
1669
|
+
termination_keys.concat(@config.isearch_terminators&.chars&.map(&:ord)) if @config.isearch_terminators
|
1596
1670
|
@waiting_proc = ->(k) {
|
1597
1671
|
case k
|
1598
|
-
when
|
1672
|
+
when *termination_keys
|
1599
1673
|
if @history_pointer
|
1600
1674
|
buffer = Reline::HISTORY[@history_pointer]
|
1601
1675
|
else
|
@@ -1614,6 +1688,8 @@ class Reline::LineEditor
|
|
1614
1688
|
@waiting_proc = nil
|
1615
1689
|
@cursor_max = calculate_width(@line)
|
1616
1690
|
@cursor = @byte_pointer = 0
|
1691
|
+
@rerender_all = true
|
1692
|
+
@cached_prompt_list = nil
|
1617
1693
|
searcher.resume(-1)
|
1618
1694
|
when "\C-g".ord
|
1619
1695
|
if @is_multiline
|
@@ -1657,6 +1733,8 @@ class Reline::LineEditor
|
|
1657
1733
|
@waiting_proc = nil
|
1658
1734
|
@cursor_max = calculate_width(@line)
|
1659
1735
|
@cursor = @byte_pointer = 0
|
1736
|
+
@rerender_all = true
|
1737
|
+
@cached_prompt_list = nil
|
1660
1738
|
searcher.resume(-1)
|
1661
1739
|
end
|
1662
1740
|
end
|
@@ -1709,7 +1787,7 @@ class Reline::LineEditor
|
|
1709
1787
|
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
1710
1788
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1711
1789
|
@line_index = line_no
|
1712
|
-
@line = @buffer_of_lines
|
1790
|
+
@line = @buffer_of_lines[@line_index]
|
1713
1791
|
@rerender_all = true
|
1714
1792
|
else
|
1715
1793
|
@line = Reline::HISTORY[@history_pointer]
|
@@ -1757,7 +1835,7 @@ class Reline::LineEditor
|
|
1757
1835
|
@line_index = line_no
|
1758
1836
|
end
|
1759
1837
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1760
|
-
@line = @buffer_of_lines
|
1838
|
+
@line = @buffer_of_lines[@line_index]
|
1761
1839
|
@rerender_all = true
|
1762
1840
|
else
|
1763
1841
|
if @history_pointer.nil? and substr.empty?
|
@@ -2176,7 +2254,7 @@ class Reline::LineEditor
|
|
2176
2254
|
|
2177
2255
|
private def vi_next_word(key, arg: 1)
|
2178
2256
|
if @line.bytesize > @byte_pointer
|
2179
|
-
byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer)
|
2257
|
+
byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer, @drop_terminate_spaces)
|
2180
2258
|
@byte_pointer += byte_size
|
2181
2259
|
@cursor += width
|
2182
2260
|
end
|
@@ -2304,6 +2382,7 @@ class Reline::LineEditor
|
|
2304
2382
|
end
|
2305
2383
|
|
2306
2384
|
private def vi_change_meta(key, arg: 1)
|
2385
|
+
@drop_terminate_spaces = true
|
2307
2386
|
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2308
2387
|
if byte_pointer_diff > 0
|
2309
2388
|
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
@@ -2315,6 +2394,7 @@ class Reline::LineEditor
|
|
2315
2394
|
@cursor_max -= cursor_diff.abs
|
2316
2395
|
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
2317
2396
|
@config.editing_mode = :vi_insert
|
2397
|
+
@drop_terminate_spaces = false
|
2318
2398
|
}
|
2319
2399
|
@waiting_operator_vi_arg = arg
|
2320
2400
|
end
|
@@ -2370,6 +2450,9 @@ class Reline::LineEditor
|
|
2370
2450
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2371
2451
|
@cursor_max -= width
|
2372
2452
|
if @cursor > 0 and @cursor >= @cursor_max
|
2453
|
+
byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
2454
|
+
mbchar = @line.byteslice(@byte_pointer - byte_size, byte_size)
|
2455
|
+
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2373
2456
|
@byte_pointer -= byte_size
|
2374
2457
|
@cursor -= width
|
2375
2458
|
end
|
@@ -2403,11 +2486,23 @@ class Reline::LineEditor
|
|
2403
2486
|
|
2404
2487
|
private def vi_histedit(key)
|
2405
2488
|
path = Tempfile.open { |fp|
|
2406
|
-
|
2489
|
+
if @is_multiline
|
2490
|
+
fp.write whole_lines.join("\n")
|
2491
|
+
else
|
2492
|
+
fp.write @line
|
2493
|
+
end
|
2407
2494
|
fp.path
|
2408
2495
|
}
|
2409
2496
|
system("#{ENV['EDITOR']} #{path}")
|
2410
|
-
@
|
2497
|
+
if @is_multiline
|
2498
|
+
@buffer_of_lines = File.read(path).split("\n")
|
2499
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2500
|
+
@line_index = 0
|
2501
|
+
@line = @buffer_of_lines[@line_index]
|
2502
|
+
@rerender_all = true
|
2503
|
+
else
|
2504
|
+
@line = File.read(path)
|
2505
|
+
end
|
2411
2506
|
finish
|
2412
2507
|
end
|
2413
2508
|
|
@@ -2466,7 +2561,7 @@ class Reline::LineEditor
|
|
2466
2561
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2467
2562
|
before = @line.byteslice(0, @byte_pointer)
|
2468
2563
|
remaining_point = @byte_pointer + byte_size
|
2469
|
-
after = @line.byteslice(remaining_point, @line.
|
2564
|
+
after = @line.byteslice(remaining_point, @line.bytesize - remaining_point)
|
2470
2565
|
@line = before + k.chr + after
|
2471
2566
|
@cursor_max = calculate_width(@line)
|
2472
2567
|
@waiting_proc = nil
|
@@ -2477,7 +2572,7 @@ class Reline::LineEditor
|
|
2477
2572
|
end
|
2478
2573
|
before = @line.byteslice(0, @byte_pointer)
|
2479
2574
|
remaining_point = @byte_pointer + byte_size
|
2480
|
-
after = @line.byteslice(remaining_point, @line.
|
2575
|
+
after = @line.byteslice(remaining_point, @line.bytesize - remaining_point)
|
2481
2576
|
replaced = k.chr * arg
|
2482
2577
|
@line = before + replaced + after
|
2483
2578
|
@byte_pointer += replaced.bytesize
|
@@ -58,16 +58,40 @@ class Reline::LineEditor
|
|
58
58
|
reset_variables(encoding: encoding)
|
59
59
|
end
|
60
60
|
|
61
|
+
def set_pasting_state(in_pasting)
|
62
|
+
@in_pasting = in_pasting
|
63
|
+
end
|
64
|
+
|
61
65
|
def simplified_rendering?
|
62
66
|
if finished?
|
63
67
|
false
|
64
68
|
elsif @just_cursor_moving and not @rerender_all
|
65
69
|
true
|
66
70
|
else
|
67
|
-
not @rerender_all and not finished? and
|
71
|
+
not @rerender_all and not finished? and @in_pasting
|
68
72
|
end
|
69
73
|
end
|
70
74
|
|
75
|
+
private def check_mode_string
|
76
|
+
mode_string = nil
|
77
|
+
if @config.show_mode_in_prompt
|
78
|
+
if @config.editing_mode_is?(:vi_command)
|
79
|
+
mode_string = @config.vi_cmd_mode_string
|
80
|
+
elsif @config.editing_mode_is?(:vi_insert)
|
81
|
+
mode_string = @config.vi_ins_mode_string
|
82
|
+
elsif @config.editing_mode_is?(:emacs)
|
83
|
+
mode_string = @config.emacs_mode_string
|
84
|
+
else
|
85
|
+
mode_string = '?'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
if mode_string != @prev_mode_string
|
89
|
+
@rerender_all = true
|
90
|
+
end
|
91
|
+
@prev_mode_string = mode_string
|
92
|
+
mode_string
|
93
|
+
end
|
94
|
+
|
71
95
|
private def check_multiline_prompt(buffer, prompt)
|
72
96
|
if @vi_arg
|
73
97
|
prompt = "(arg: #{@vi_arg}) "
|
@@ -78,44 +102,44 @@ class Reline::LineEditor
|
|
78
102
|
else
|
79
103
|
prompt = @prompt
|
80
104
|
end
|
81
|
-
|
105
|
+
if simplified_rendering?
|
106
|
+
mode_string = check_mode_string
|
107
|
+
prompt = mode_string + prompt if mode_string
|
108
|
+
return [prompt, calculate_width(prompt, true), [prompt] * buffer.size]
|
109
|
+
end
|
82
110
|
if @prompt_proc
|
83
|
-
|
111
|
+
use_cached_prompt_list = false
|
112
|
+
if @cached_prompt_list
|
113
|
+
if @just_cursor_moving
|
114
|
+
use_cached_prompt_list = true
|
115
|
+
elsif Time.now.to_f < (@prompt_cache_time + PROMPT_LIST_CACHE_TIMEOUT) and buffer.size == @cached_prompt_list.size
|
116
|
+
use_cached_prompt_list = true
|
117
|
+
end
|
118
|
+
end
|
119
|
+
use_cached_prompt_list = false if @rerender_all
|
120
|
+
if use_cached_prompt_list
|
84
121
|
prompt_list = @cached_prompt_list
|
85
122
|
else
|
86
123
|
prompt_list = @cached_prompt_list = @prompt_proc.(buffer)
|
87
124
|
@prompt_cache_time = Time.now.to_f
|
88
125
|
end
|
89
126
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
mode_icon = '?'
|
127
|
+
mode_string = check_mode_string
|
128
|
+
prompt_list = prompt_list.map{ |pr| mode_string + pr } if mode_string
|
129
|
+
prompt = prompt_list[@line_index]
|
130
|
+
prompt = prompt_list[0] if prompt.nil?
|
131
|
+
prompt = prompt_list.last if prompt.nil?
|
132
|
+
if buffer.size > prompt_list.size
|
133
|
+
(buffer.size - prompt_list.size).times do
|
134
|
+
prompt_list << prompt_list.last
|
99
135
|
end
|
100
|
-
prompt_list.map!{ |pr| mode_icon + pr }
|
101
136
|
end
|
102
|
-
prompt = prompt_list[@line_index]
|
103
137
|
prompt_width = calculate_width(prompt, true)
|
104
138
|
[prompt, prompt_width, prompt_list]
|
105
139
|
else
|
140
|
+
mode_string = check_mode_string
|
141
|
+
prompt = mode_string + prompt if mode_string
|
106
142
|
prompt_width = calculate_width(prompt, true)
|
107
|
-
if @config.show_mode_in_prompt
|
108
|
-
if @config.editing_mode_is?(:vi_command)
|
109
|
-
mode_icon = @config.vi_cmd_mode_icon
|
110
|
-
elsif @config.editing_mode_is?(:vi_insert)
|
111
|
-
mode_icon = @config.vi_ins_mode_icon
|
112
|
-
elsif @config.editing_mode_is?(:emacs)
|
113
|
-
mode_icon = @config.emacs_mode_string
|
114
|
-
else
|
115
|
-
mode_icon = '?'
|
116
|
-
end
|
117
|
-
prompt = mode_icon + prompt
|
118
|
-
end
|
119
143
|
[prompt, prompt_width, nil]
|
120
144
|
end
|
121
145
|
end
|
@@ -126,6 +150,13 @@ class Reline::LineEditor
|
|
126
150
|
@screen_height = @screen_size.first
|
127
151
|
reset_variables(prompt, encoding: encoding)
|
128
152
|
@old_trap = Signal.trap('SIGINT') {
|
153
|
+
if @scroll_partial_screen
|
154
|
+
move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1)
|
155
|
+
else
|
156
|
+
move_cursor_down(@highest_in_all - @line_index - 1)
|
157
|
+
end
|
158
|
+
Reline::IOGate.move_cursor_column(0)
|
159
|
+
scroll_down(1)
|
129
160
|
@old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
|
130
161
|
raise Interrupt
|
131
162
|
}
|
@@ -185,7 +216,7 @@ class Reline::LineEditor
|
|
185
216
|
@cleared = false
|
186
217
|
@rerender_all = false
|
187
218
|
@history_pointer = nil
|
188
|
-
@kill_ring
|
219
|
+
@kill_ring ||= Reline::KillRing.new
|
189
220
|
@vi_clipboard = ''
|
190
221
|
@vi_arg = nil
|
191
222
|
@waiting_proc = nil
|
@@ -205,6 +236,10 @@ class Reline::LineEditor
|
|
205
236
|
@eof = false
|
206
237
|
@continuous_insertion_buffer = String.new(encoding: @encoding)
|
207
238
|
@scroll_partial_screen = nil
|
239
|
+
@prev_mode_string = nil
|
240
|
+
@drop_terminate_spaces = false
|
241
|
+
@in_pasting = false
|
242
|
+
@auto_indent_proc = nil
|
208
243
|
reset_line
|
209
244
|
end
|
210
245
|
|
@@ -353,10 +388,24 @@ class Reline::LineEditor
|
|
353
388
|
@cleared = false
|
354
389
|
return
|
355
390
|
end
|
391
|
+
if @is_multiline and finished? and @scroll_partial_screen
|
392
|
+
# Re-output all code higher than the screen when finished.
|
393
|
+
Reline::IOGate.move_cursor_up(@first_line_started_from + @started_from - @scroll_partial_screen)
|
394
|
+
Reline::IOGate.move_cursor_column(0)
|
395
|
+
@scroll_partial_screen = nil
|
396
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
397
|
+
modify_lines(whole_lines).each_with_index do |line, index|
|
398
|
+
@output.write "#{prompt_list ? prompt_list[index] : prompt}#{line}\n"
|
399
|
+
Reline::IOGate.erase_after_cursor
|
400
|
+
end
|
401
|
+
@output.flush
|
402
|
+
return
|
403
|
+
end
|
356
404
|
new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
|
357
405
|
# FIXME: end of logical line sometimes breaks
|
406
|
+
rendered = false
|
358
407
|
if @add_newline_to_end_of_buffer
|
359
|
-
rerender_added_newline
|
408
|
+
rerender_added_newline(prompt, prompt_width)
|
360
409
|
@add_newline_to_end_of_buffer = false
|
361
410
|
else
|
362
411
|
if @just_cursor_moving and not @rerender_all
|
@@ -374,20 +423,27 @@ class Reline::LineEditor
|
|
374
423
|
else
|
375
424
|
end
|
376
425
|
end
|
377
|
-
line = modify_lines(whole_lines)[@line_index]
|
378
426
|
if @is_multiline
|
379
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
380
427
|
if finished?
|
381
428
|
# Always rerender on finish because output_modifier_proc may return a different output.
|
429
|
+
line = modify_lines(whole_lines)[@line_index]
|
430
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
382
431
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
432
|
+
move_cursor_down(@highest_in_all - (@first_line_started_from + @highest_in_this - 1) - 1)
|
383
433
|
scroll_down(1)
|
384
434
|
Reline::IOGate.move_cursor_column(0)
|
385
435
|
Reline::IOGate.erase_after_cursor
|
386
436
|
elsif not rendered
|
387
|
-
|
437
|
+
unless @in_pasting
|
438
|
+
line = modify_lines(whole_lines)[@line_index]
|
439
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
440
|
+
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
441
|
+
end
|
388
442
|
end
|
389
443
|
@buffer_of_lines[@line_index] = @line
|
444
|
+
@rest_height = 0 if @scroll_partial_screen
|
390
445
|
else
|
446
|
+
line = modify_lines(whole_lines)[@line_index]
|
391
447
|
render_partial(prompt, prompt_width, line, 0)
|
392
448
|
if finished?
|
393
449
|
scroll_down(1)
|
@@ -430,13 +486,13 @@ class Reline::LineEditor
|
|
430
486
|
end
|
431
487
|
end
|
432
488
|
|
433
|
-
private def rerender_added_newline
|
489
|
+
private def rerender_added_newline(prompt, prompt_width)
|
434
490
|
scroll_down(1)
|
435
|
-
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
436
|
-
prompt, prompt_width, = check_multiline_prompt(new_lines, prompt)
|
437
491
|
@buffer_of_lines[@previous_line_index] = @line
|
438
492
|
@line = @buffer_of_lines[@line_index]
|
439
|
-
|
493
|
+
unless @in_pasting
|
494
|
+
render_partial(prompt, prompt_width, @line, @first_line_started_from + @started_from + 1, with_control: false)
|
495
|
+
end
|
440
496
|
@cursor = @cursor_max = calculate_width(@line)
|
441
497
|
@byte_pointer = @line.bytesize
|
442
498
|
@highest_in_all += @highest_in_this
|
@@ -456,7 +512,7 @@ class Reline::LineEditor
|
|
456
512
|
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
457
513
|
end
|
458
514
|
first_line_diff = new_first_line_started_from - @first_line_started_from
|
459
|
-
new_cursor,
|
515
|
+
new_cursor, new_cursor_max, new_started_from, new_byte_pointer = calculate_nearest_cursor(@buffer_of_lines[@line_index], @cursor, @started_from, @byte_pointer, false)
|
460
516
|
new_started_from = calculate_height_by_width(prompt_width + new_cursor) - 1
|
461
517
|
calculate_scroll_partial_screen(@highest_in_all, new_first_line_started_from + new_started_from)
|
462
518
|
@previous_line_index = nil
|
@@ -470,6 +526,8 @@ class Reline::LineEditor
|
|
470
526
|
@first_line_started_from = new_first_line_started_from
|
471
527
|
@started_from = new_started_from
|
472
528
|
@cursor = new_cursor
|
529
|
+
@cursor_max = new_cursor_max
|
530
|
+
@byte_pointer = new_byte_pointer
|
473
531
|
move_cursor_down(first_line_diff + @started_from)
|
474
532
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
475
533
|
false
|
@@ -543,7 +601,13 @@ class Reline::LineEditor
|
|
543
601
|
new_first_line_started_from = calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
544
602
|
end
|
545
603
|
new_started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
546
|
-
|
604
|
+
calculate_scroll_partial_screen(back, new_first_line_started_from + new_started_from)
|
605
|
+
if @scroll_partial_screen
|
606
|
+
move_cursor_up(@first_line_started_from + @started_from)
|
607
|
+
scroll_down(@screen_height - 1)
|
608
|
+
move_cursor_up(@screen_height)
|
609
|
+
Reline::IOGate.move_cursor_column(0)
|
610
|
+
elsif back > old_highest_in_all
|
547
611
|
scroll_down(back - 1)
|
548
612
|
move_cursor_up(back - 1)
|
549
613
|
elsif back < old_highest_in_all
|
@@ -555,7 +619,6 @@ class Reline::LineEditor
|
|
555
619
|
end
|
556
620
|
move_cursor_up(old_highest_in_all - 1)
|
557
621
|
end
|
558
|
-
calculate_scroll_partial_screen(back, new_first_line_started_from + new_started_from)
|
559
622
|
render_whole_lines(new_buffer, prompt_list || prompt, prompt_width)
|
560
623
|
if @prompt_proc
|
561
624
|
prompt = prompt_list[@line_index]
|
@@ -576,7 +639,6 @@ class Reline::LineEditor
|
|
576
639
|
|
577
640
|
private def render_whole_lines(lines, prompt, prompt_width)
|
578
641
|
rendered_height = 0
|
579
|
-
@output.write "\e[0m" # clear character decorations
|
580
642
|
modify_lines(lines).each_with_index do |line, index|
|
581
643
|
if prompt.is_a?(Array)
|
582
644
|
line_prompt = prompt[index]
|
@@ -645,21 +707,20 @@ class Reline::LineEditor
|
|
645
707
|
cursor_up_from_last_line = height - 1 - @started_from
|
646
708
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
647
709
|
end
|
710
|
+
if Reline::Unicode::CSI_REGEXP.match?(prompt + line_to_render)
|
711
|
+
@output.write "\e[0m" # clear character decorations
|
712
|
+
end
|
648
713
|
visual_lines.each_with_index do |line, index|
|
649
714
|
Reline::IOGate.move_cursor_column(0)
|
650
715
|
if line.nil?
|
651
716
|
if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
652
|
-
#
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
# cursor position.
|
660
|
-
move_cursor_down(1)
|
661
|
-
Reline::IOGate.move_cursor_column(0)
|
662
|
-
end
|
717
|
+
# Reaches the end of line.
|
718
|
+
#
|
719
|
+
# When the cursor is at the end of the line and erases characters
|
720
|
+
# after the cursor, some terminals delete the character at the
|
721
|
+
# cursor position.
|
722
|
+
move_cursor_down(1)
|
723
|
+
Reline::IOGate.move_cursor_column(0)
|
663
724
|
else
|
664
725
|
Reline::IOGate.erase_after_cursor
|
665
726
|
move_cursor_down(1)
|
@@ -668,10 +729,6 @@ class Reline::LineEditor
|
|
668
729
|
next
|
669
730
|
end
|
670
731
|
@output.write line
|
671
|
-
if Reline::IOGate.win? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last
|
672
|
-
# A newline is automatically inserted if a character is rendered at eol on command prompt.
|
673
|
-
@rest_height -= 1 if @rest_height > 0
|
674
|
-
end
|
675
732
|
@output.flush
|
676
733
|
if @first_prompt
|
677
734
|
@first_prompt = false
|
@@ -1063,7 +1120,7 @@ class Reline::LineEditor
|
|
1063
1120
|
unless completion_occurs
|
1064
1121
|
@completion_state = CompletionState::NORMAL
|
1065
1122
|
end
|
1066
|
-
if not
|
1123
|
+
if not @in_pasting and @just_cursor_moving.nil?
|
1067
1124
|
if @previous_line_index and @buffer_of_lines[@previous_line_index] == @line
|
1068
1125
|
@just_cursor_moving = true
|
1069
1126
|
elsif @previous_line_index.nil? and @buffer_of_lines[@line_index] == @line and old_line == @line
|
@@ -1112,6 +1169,7 @@ class Reline::LineEditor
|
|
1112
1169
|
new_lines = whole_lines
|
1113
1170
|
end
|
1114
1171
|
new_indent = @auto_indent_proc.(new_lines, @line_index, @byte_pointer, @check_new_auto_indent)
|
1172
|
+
new_indent = @cursor_max if new_indent&.> @cursor_max
|
1115
1173
|
if new_indent&.>= 0
|
1116
1174
|
md = new_lines[@line_index].match(/\A */)
|
1117
1175
|
prev_indent = md[0].count(' ')
|
@@ -1312,14 +1370,14 @@ class Reline::LineEditor
|
|
1312
1370
|
cursor_line = @line.byteslice(0, @byte_pointer)
|
1313
1371
|
insert_new_line(cursor_line, next_line)
|
1314
1372
|
@cursor = 0
|
1315
|
-
@check_new_auto_indent = true
|
1373
|
+
@check_new_auto_indent = true unless @in_pasting
|
1316
1374
|
end
|
1317
1375
|
end
|
1318
1376
|
|
1319
1377
|
private def ed_unassigned(key) end # do nothing
|
1320
1378
|
|
1321
1379
|
private def process_insert(force: false)
|
1322
|
-
return if @continuous_insertion_buffer.empty? or (
|
1380
|
+
return if @continuous_insertion_buffer.empty? or (@in_pasting and not force)
|
1323
1381
|
width = Reline::Unicode.calculate_width(@continuous_insertion_buffer)
|
1324
1382
|
bytesize = @continuous_insertion_buffer.bytesize
|
1325
1383
|
if @cursor == @cursor_max
|
@@ -1354,7 +1412,7 @@ class Reline::LineEditor
|
|
1354
1412
|
str = key.chr
|
1355
1413
|
bytesize = 1
|
1356
1414
|
end
|
1357
|
-
if
|
1415
|
+
if @in_pasting
|
1358
1416
|
@continuous_insertion_buffer << str
|
1359
1417
|
return
|
1360
1418
|
elsif not @continuous_insertion_buffer.empty?
|
@@ -1366,7 +1424,12 @@ class Reline::LineEditor
|
|
1366
1424
|
else
|
1367
1425
|
@line = byteinsert(@line, @byte_pointer, str)
|
1368
1426
|
end
|
1427
|
+
last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
1369
1428
|
@byte_pointer += bytesize
|
1429
|
+
last_mbchar = @line.byteslice((@byte_pointer - bytesize - last_byte_size), last_byte_size)
|
1430
|
+
if last_byte_size != 0 and (last_mbchar + str).grapheme_clusters.size == 1
|
1431
|
+
width = 0
|
1432
|
+
end
|
1370
1433
|
@cursor += width
|
1371
1434
|
@cursor_max += width
|
1372
1435
|
end
|
@@ -1578,9 +1641,11 @@ class Reline::LineEditor
|
|
1578
1641
|
searcher = generate_searcher
|
1579
1642
|
searcher.resume(key)
|
1580
1643
|
@searching_prompt = "(reverse-i-search)`': "
|
1644
|
+
termination_keys = ["\C-j".ord]
|
1645
|
+
termination_keys.concat(@config.isearch_terminators&.chars&.map(&:ord)) if @config.isearch_terminators
|
1581
1646
|
@waiting_proc = ->(k) {
|
1582
1647
|
case k
|
1583
|
-
when
|
1648
|
+
when *termination_keys
|
1584
1649
|
if @history_pointer
|
1585
1650
|
buffer = Reline::HISTORY[@history_pointer]
|
1586
1651
|
else
|
@@ -1599,6 +1664,8 @@ class Reline::LineEditor
|
|
1599
1664
|
@waiting_proc = nil
|
1600
1665
|
@cursor_max = calculate_width(@line)
|
1601
1666
|
@cursor = @byte_pointer = 0
|
1667
|
+
@rerender_all = true
|
1668
|
+
@cached_prompt_list = nil
|
1602
1669
|
searcher.resume(-1)
|
1603
1670
|
when "\C-g".ord
|
1604
1671
|
if @is_multiline
|
@@ -1642,6 +1709,8 @@ class Reline::LineEditor
|
|
1642
1709
|
@waiting_proc = nil
|
1643
1710
|
@cursor_max = calculate_width(@line)
|
1644
1711
|
@cursor = @byte_pointer = 0
|
1712
|
+
@rerender_all = true
|
1713
|
+
@cached_prompt_list = nil
|
1645
1714
|
searcher.resume(-1)
|
1646
1715
|
end
|
1647
1716
|
end
|
@@ -1694,7 +1763,7 @@ class Reline::LineEditor
|
|
1694
1763
|
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
1695
1764
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1696
1765
|
@line_index = line_no
|
1697
|
-
@line = @buffer_of_lines
|
1766
|
+
@line = @buffer_of_lines[@line_index]
|
1698
1767
|
@rerender_all = true
|
1699
1768
|
else
|
1700
1769
|
@line = Reline::HISTORY[@history_pointer]
|
@@ -1742,7 +1811,7 @@ class Reline::LineEditor
|
|
1742
1811
|
@line_index = line_no
|
1743
1812
|
end
|
1744
1813
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1745
|
-
@line = @buffer_of_lines
|
1814
|
+
@line = @buffer_of_lines[@line_index]
|
1746
1815
|
@rerender_all = true
|
1747
1816
|
else
|
1748
1817
|
if @history_pointer.nil? and substr.empty?
|
@@ -1934,6 +2003,7 @@ class Reline::LineEditor
|
|
1934
2003
|
@cursor = 0
|
1935
2004
|
end
|
1936
2005
|
end
|
2006
|
+
alias_method :kill_line, :em_kill_line
|
1937
2007
|
|
1938
2008
|
private def em_delete(key)
|
1939
2009
|
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
|
@@ -1984,6 +2054,7 @@ class Reline::LineEditor
|
|
1984
2054
|
@byte_pointer += yanked.bytesize
|
1985
2055
|
end
|
1986
2056
|
end
|
2057
|
+
alias_method :yank, :em_yank
|
1987
2058
|
|
1988
2059
|
private def em_yank_pop(key)
|
1989
2060
|
yanked, prev_yank = @kill_ring.yank_pop
|
@@ -2000,6 +2071,7 @@ class Reline::LineEditor
|
|
2000
2071
|
@byte_pointer += yanked.bytesize
|
2001
2072
|
end
|
2002
2073
|
end
|
2074
|
+
alias_method :yank_pop, :em_yank_pop
|
2003
2075
|
|
2004
2076
|
private def ed_clear_screen(key)
|
2005
2077
|
@cleared = true
|
@@ -2130,9 +2202,10 @@ class Reline::LineEditor
|
|
2130
2202
|
@byte_pointer -= byte_size
|
2131
2203
|
@cursor -= width
|
2132
2204
|
@cursor_max -= width
|
2133
|
-
@kill_ring.append(deleted)
|
2205
|
+
@kill_ring.append(deleted, true)
|
2134
2206
|
end
|
2135
2207
|
end
|
2208
|
+
alias_method :unix_word_rubout, :em_kill_region
|
2136
2209
|
|
2137
2210
|
private def copy_for_vi(text)
|
2138
2211
|
if @config.editing_mode_is?(:vi_insert) or @config.editing_mode_is?(:vi_command)
|
@@ -2157,7 +2230,7 @@ class Reline::LineEditor
|
|
2157
2230
|
|
2158
2231
|
private def vi_next_word(key, arg: 1)
|
2159
2232
|
if @line.bytesize > @byte_pointer
|
2160
|
-
byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer)
|
2233
|
+
byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer, @drop_terminate_spaces)
|
2161
2234
|
@byte_pointer += byte_size
|
2162
2235
|
@cursor += width
|
2163
2236
|
end
|
@@ -2285,6 +2358,7 @@ class Reline::LineEditor
|
|
2285
2358
|
end
|
2286
2359
|
|
2287
2360
|
private def vi_change_meta(key, arg: 1)
|
2361
|
+
@drop_terminate_spaces = true
|
2288
2362
|
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2289
2363
|
if byte_pointer_diff > 0
|
2290
2364
|
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
@@ -2296,6 +2370,7 @@ class Reline::LineEditor
|
|
2296
2370
|
@cursor_max -= cursor_diff.abs
|
2297
2371
|
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
2298
2372
|
@config.editing_mode = :vi_insert
|
2373
|
+
@drop_terminate_spaces = false
|
2299
2374
|
}
|
2300
2375
|
@waiting_operator_vi_arg = arg
|
2301
2376
|
end
|
@@ -2351,6 +2426,9 @@ class Reline::LineEditor
|
|
2351
2426
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2352
2427
|
@cursor_max -= width
|
2353
2428
|
if @cursor > 0 and @cursor >= @cursor_max
|
2429
|
+
byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
2430
|
+
mbchar = @line.byteslice(@byte_pointer - byte_size, byte_size)
|
2431
|
+
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2354
2432
|
@byte_pointer -= byte_size
|
2355
2433
|
@cursor -= width
|
2356
2434
|
end
|
@@ -2384,11 +2462,23 @@ class Reline::LineEditor
|
|
2384
2462
|
|
2385
2463
|
private def vi_histedit(key)
|
2386
2464
|
path = Tempfile.open { |fp|
|
2387
|
-
|
2465
|
+
if @is_multiline
|
2466
|
+
fp.write whole_lines.join("\n")
|
2467
|
+
else
|
2468
|
+
fp.write @line
|
2469
|
+
end
|
2388
2470
|
fp.path
|
2389
2471
|
}
|
2390
2472
|
system("#{ENV['EDITOR']} #{path}")
|
2391
|
-
@
|
2473
|
+
if @is_multiline
|
2474
|
+
@buffer_of_lines = File.read(path).split("\n")
|
2475
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2476
|
+
@line_index = 0
|
2477
|
+
@line = @buffer_of_lines[@line_index]
|
2478
|
+
@rerender_all = true
|
2479
|
+
else
|
2480
|
+
@line = File.read(path)
|
2481
|
+
end
|
2392
2482
|
finish
|
2393
2483
|
end
|
2394
2484
|
|
@@ -2447,7 +2537,7 @@ class Reline::LineEditor
|
|
2447
2537
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2448
2538
|
before = @line.byteslice(0, @byte_pointer)
|
2449
2539
|
remaining_point = @byte_pointer + byte_size
|
2450
|
-
after = @line.byteslice(remaining_point, @line.
|
2540
|
+
after = @line.byteslice(remaining_point, @line.bytesize - remaining_point)
|
2451
2541
|
@line = before + k.chr + after
|
2452
2542
|
@cursor_max = calculate_width(@line)
|
2453
2543
|
@waiting_proc = nil
|
@@ -2458,7 +2548,7 @@ class Reline::LineEditor
|
|
2458
2548
|
end
|
2459
2549
|
before = @line.byteslice(0, @byte_pointer)
|
2460
2550
|
remaining_point = @byte_pointer + byte_size
|
2461
|
-
after = @line.byteslice(remaining_point, @line.
|
2551
|
+
after = @line.byteslice(remaining_point, @line.bytesize - remaining_point)
|
2462
2552
|
replaced = k.chr * arg
|
2463
2553
|
@line = before + replaced + after
|
2464
2554
|
@byte_pointer += replaced.bytesize
|
data/lib/reline/unicode.rb
CHANGED
@@ -108,6 +108,7 @@ class Reline::Unicode
|
|
108
108
|
end
|
109
109
|
m = mbchar.encode(Encoding::UTF_8).match(MBCharWidthRE)
|
110
110
|
case
|
111
|
+
when m.nil? then 1 # TODO should be U+FFFD � REPLACEMENT CHARACTER
|
111
112
|
when m[:width_2_1], m[:width_2_2] then 2
|
112
113
|
when m[:width_3] then 3
|
113
114
|
when m[:width_0] then 0
|
@@ -458,8 +459,8 @@ class Reline::Unicode
|
|
458
459
|
[byte_size, width]
|
459
460
|
end
|
460
461
|
|
461
|
-
def self.vi_forward_word(line, byte_pointer)
|
462
|
-
if
|
462
|
+
def self.vi_forward_word(line, byte_pointer, drop_terminate_spaces = false)
|
463
|
+
if line.bytesize > byte_pointer
|
463
464
|
size = get_next_mbchar_size(line, byte_pointer)
|
464
465
|
mbchar = line.byteslice(byte_pointer, size)
|
465
466
|
if mbchar =~ /\w/
|
@@ -474,7 +475,7 @@ class Reline::Unicode
|
|
474
475
|
else
|
475
476
|
return [0, 0]
|
476
477
|
end
|
477
|
-
while
|
478
|
+
while line.bytesize > (byte_pointer + byte_size)
|
478
479
|
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
479
480
|
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
480
481
|
case started_by
|
@@ -488,7 +489,8 @@ class Reline::Unicode
|
|
488
489
|
width += get_mbchar_width(mbchar)
|
489
490
|
byte_size += size
|
490
491
|
end
|
491
|
-
|
492
|
+
return [byte_size, width] if drop_terminate_spaces
|
493
|
+
while line.bytesize > (byte_pointer + byte_size)
|
492
494
|
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
493
495
|
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
494
496
|
break if mbchar =~ /\S/
|
data/lib/reline/version.rb
CHANGED
data/lib/reline/windows.rb
CHANGED
@@ -9,6 +9,10 @@ class Reline::Windows
|
|
9
9
|
true
|
10
10
|
end
|
11
11
|
|
12
|
+
def self.win_legacy_console?
|
13
|
+
@@legacy_console
|
14
|
+
end
|
15
|
+
|
12
16
|
RAW_KEYSTROKE_CONFIG = {
|
13
17
|
[224, 72] => :ed_prev_history, # ↑
|
14
18
|
[224, 80] => :ed_next_history, # ↓
|
@@ -94,6 +98,26 @@ class Reline::Windows
|
|
94
98
|
@@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
95
99
|
@@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
|
96
100
|
|
101
|
+
@@GetConsoleMode = Win32API.new('kernel32', 'GetConsoleMode', ['L', 'P'], 'L')
|
102
|
+
@@SetConsoleMode = Win32API.new('kernel32', 'SetConsoleMode', ['L', 'L'], 'L')
|
103
|
+
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
104
|
+
|
105
|
+
private_class_method def self.getconsolemode
|
106
|
+
mode = "\000\000\000\000"
|
107
|
+
@@GetConsoleMode.call(@@hConsoleHandle, mode)
|
108
|
+
mode.unpack1('L')
|
109
|
+
end
|
110
|
+
|
111
|
+
private_class_method def self.setconsolemode(mode)
|
112
|
+
@@SetConsoleMode.call(@@hConsoleHandle, mode)
|
113
|
+
end
|
114
|
+
|
115
|
+
@@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
|
116
|
+
#if @@legacy_console
|
117
|
+
# setconsolemode(getconsolemode() | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
118
|
+
# @@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
|
119
|
+
#end
|
120
|
+
|
97
121
|
@@input_buf = []
|
98
122
|
@@output_buf = []
|
99
123
|
|
@@ -258,6 +282,7 @@ class Reline::Windows
|
|
258
282
|
cursor = csbi[4, 4].unpack('L').first
|
259
283
|
written = 0.chr * 4
|
260
284
|
@@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, get_screen_size.last - cursor_pos.x, cursor, written)
|
285
|
+
@@FillConsoleOutputAttribute.call(@@hConsoleHandle, 0, get_screen_size.last - cursor_pos.x, cursor, written)
|
261
286
|
end
|
262
287
|
|
263
288
|
def self.scroll_down(val)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Copyright (c) 2009, Park Heesob
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
* Redistributions in binary form must reproduce the above copyright notice
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
* Neither the name of Park Heesob nor the names of its contributors
|
13
|
+
may be used to endorse or promote products derived from this software
|
14
|
+
without specific prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
17
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
18
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
19
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
20
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
21
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
22
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
23
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
24
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aycabta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: io-console
|
@@ -109,6 +109,7 @@ files:
|
|
109
109
|
- lib/reline/unicode/east_asian_width.rb
|
110
110
|
- lib/reline/version.rb
|
111
111
|
- lib/reline/windows.rb
|
112
|
+
- license_of_rb-readline
|
112
113
|
homepage: https://github.com/ruby/reline
|
113
114
|
licenses:
|
114
115
|
- Ruby
|