reline 0.1.10 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +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
|