reline 0.1.5 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/reline.rb +23 -3
- data/lib/reline/ansi.rb +59 -2
- data/lib/reline/config.rb +1 -0
- data/lib/reline/general_io.rb +18 -0
- data/lib/reline/key_actor/emacs.rb +1 -1
- data/lib/reline/key_actor/vi_command.rb +2 -2
- data/lib/reline/kill_ring.rb +12 -0
- data/lib/reline/line_editor.rb +527 -206
- data/lib/reline/line_editor.rb.orig +2606 -0
- data/lib/reline/sibori.rb +170 -0
- data/lib/reline/unicode.rb +58 -29
- data/lib/reline/unicode/east_asian_width.rb +1149 -1130
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +22 -1
- metadata +19 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 99b4f2cf521a3b774a0c763517fc373fdc2bf1603e64ab8e853513ed64946074
|
4
|
+
data.tar.gz: 4665d8f6c3db58c573186b4fb245478a1190fdb5da5f9c5f0d569bcfb2cba2f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b66388f8b9008f5008902e662133312006263622cf7d12d458f5611446b536baf7ff66197209e7b82ada8d34d1b0c8d6eb40c6be35315134c289ded8cc421f7
|
7
|
+
data.tar.gz: 74ce2b03a99eb548c25050f606140a9fd62351e2cbe20037977f416cc20153f3d711e946c84c4857e66b078a33b7dd12a359f429a2c31091ece7e8f776204931
|
data/lib/reline.rb
CHANGED
@@ -44,6 +44,7 @@ module Reline
|
|
44
44
|
self.output = STDOUT
|
45
45
|
yield self
|
46
46
|
@completion_quote_character = nil
|
47
|
+
@bracketed_paste_finished = false
|
47
48
|
end
|
48
49
|
|
49
50
|
def encoding
|
@@ -199,7 +200,11 @@ module Reline
|
|
199
200
|
|
200
201
|
private def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
|
201
202
|
if ENV['RELINE_STDERR_TTY']
|
202
|
-
|
203
|
+
if Reline::IOGate.win?
|
204
|
+
$stderr = File.open(ENV['RELINE_STDERR_TTY'], 'a')
|
205
|
+
else
|
206
|
+
$stderr.reopen(ENV['RELINE_STDERR_TTY'], 'w')
|
207
|
+
end
|
203
208
|
$stderr.sync = true
|
204
209
|
$stderr.puts "Reline is used by #{Process.pid}"
|
205
210
|
end
|
@@ -235,13 +240,23 @@ module Reline
|
|
235
240
|
line_editor.rerender
|
236
241
|
|
237
242
|
begin
|
243
|
+
prev_pasting_state = false
|
238
244
|
loop do
|
245
|
+
prev_pasting_state = Reline::IOGate.in_pasting?
|
239
246
|
read_io(config.keyseq_timeout) { |inputs|
|
240
247
|
inputs.each { |c|
|
241
248
|
line_editor.input_key(c)
|
242
249
|
line_editor.rerender
|
243
250
|
}
|
251
|
+
if @bracketed_paste_finished
|
252
|
+
line_editor.rerender_all
|
253
|
+
@bracketed_paste_finished = false
|
254
|
+
end
|
244
255
|
}
|
256
|
+
if prev_pasting_state == true and not Reline::IOGate.in_pasting? and not line_editor.finished?
|
257
|
+
prev_pasting_state = false
|
258
|
+
line_editor.rerender_all
|
259
|
+
end
|
245
260
|
break if line_editor.finished?
|
246
261
|
end
|
247
262
|
Reline::IOGate.move_cursor_column(0)
|
@@ -269,8 +284,13 @@ module Reline
|
|
269
284
|
buffer = []
|
270
285
|
loop do
|
271
286
|
c = Reline::IOGate.getc
|
272
|
-
|
273
|
-
|
287
|
+
if c == -1
|
288
|
+
result = :unmatched
|
289
|
+
@bracketed_paste_finished = true
|
290
|
+
else
|
291
|
+
buffer << c
|
292
|
+
result = key_stroke.match_status(buffer)
|
293
|
+
end
|
274
294
|
case result
|
275
295
|
when :matched
|
276
296
|
expanded = key_stroke.expand(buffer).map{ |expanded_c|
|
data/lib/reline/ansi.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'io/console'
|
2
|
+
require 'timeout'
|
2
3
|
|
3
4
|
class Reline::ANSI
|
4
5
|
def self.encoding
|
@@ -67,7 +68,7 @@ class Reline::ANSI
|
|
67
68
|
end
|
68
69
|
|
69
70
|
@@buf = []
|
70
|
-
def self.
|
71
|
+
def self.inner_getc
|
71
72
|
unless @@buf.empty?
|
72
73
|
return @@buf.shift
|
73
74
|
end
|
@@ -80,6 +81,62 @@ class Reline::ANSI
|
|
80
81
|
nil
|
81
82
|
end
|
82
83
|
|
84
|
+
@@in_bracketed_paste_mode = false
|
85
|
+
START_BRACKETED_PASTE = String.new("\e[200~,", encoding: Encoding::ASCII_8BIT)
|
86
|
+
END_BRACKETED_PASTE = String.new("\e[200~.", encoding: Encoding::ASCII_8BIT)
|
87
|
+
def self.getc_with_bracketed_paste
|
88
|
+
buffer = String.new(encoding: Encoding::ASCII_8BIT)
|
89
|
+
buffer << inner_getc
|
90
|
+
while START_BRACKETED_PASTE.start_with?(buffer) or END_BRACKETED_PASTE.start_with?(buffer) do
|
91
|
+
if START_BRACKETED_PASTE == buffer
|
92
|
+
@@in_bracketed_paste_mode = true
|
93
|
+
return inner_getc
|
94
|
+
elsif END_BRACKETED_PASTE == buffer
|
95
|
+
@@in_bracketed_paste_mode = false
|
96
|
+
ungetc(-1)
|
97
|
+
return inner_getc
|
98
|
+
end
|
99
|
+
begin
|
100
|
+
succ_c = nil
|
101
|
+
Timeout.timeout(Reline.core.config.keyseq_timeout * 100) {
|
102
|
+
succ_c = inner_getc
|
103
|
+
}
|
104
|
+
rescue Timeout::Error
|
105
|
+
break
|
106
|
+
else
|
107
|
+
buffer << succ_c
|
108
|
+
end
|
109
|
+
end
|
110
|
+
buffer.bytes.reverse_each do |ch|
|
111
|
+
ungetc ch
|
112
|
+
end
|
113
|
+
inner_getc
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.getc
|
117
|
+
if Reline.core.config.enable_bracketed_paste
|
118
|
+
getc_with_bracketed_paste
|
119
|
+
else
|
120
|
+
inner_getc
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.in_pasting?
|
125
|
+
@@in_bracketed_paste_mode or (not Reline::IOGate.empty_buffer?)
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.empty_buffer?
|
129
|
+
unless @@buf.empty?
|
130
|
+
return false
|
131
|
+
end
|
132
|
+
rs, = IO.select([@@input], [], [], 0.00001)
|
133
|
+
if rs and rs[0]
|
134
|
+
false
|
135
|
+
else
|
136
|
+
true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
83
140
|
def self.ungetc(c)
|
84
141
|
@@buf.unshift(c)
|
85
142
|
end
|
@@ -115,7 +172,7 @@ class Reline::ANSI
|
|
115
172
|
|
116
173
|
def self.cursor_pos
|
117
174
|
begin
|
118
|
-
res = ''
|
175
|
+
res = +''
|
119
176
|
m = nil
|
120
177
|
@@input.raw do |stdin|
|
121
178
|
@@output << "\e[6n"
|
data/lib/reline/config.rb
CHANGED
data/lib/reline/general_io.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'timeout'
|
2
2
|
|
3
3
|
class Reline::GeneralIO
|
4
|
+
def self.reset
|
5
|
+
@@pasting = false
|
6
|
+
end
|
7
|
+
|
4
8
|
def self.encoding
|
5
9
|
RUBY_PLATFORM =~ /mswin|mingw/ ? Encoding::UTF_8 : Encoding::default_external
|
6
10
|
end
|
@@ -67,6 +71,20 @@ class Reline::GeneralIO
|
|
67
71
|
def self.set_winch_handler(&handler)
|
68
72
|
end
|
69
73
|
|
74
|
+
@@pasting = false
|
75
|
+
|
76
|
+
def self.in_pasting?
|
77
|
+
@@pasting
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.start_pasting
|
81
|
+
@@pasting = true
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.finish_pasting
|
85
|
+
@@pasting = false
|
86
|
+
end
|
87
|
+
|
70
88
|
def self.prep
|
71
89
|
end
|
72
90
|
|
@@ -17,7 +17,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
|
|
17
17
|
# 7 ^G
|
18
18
|
:ed_unassigned,
|
19
19
|
# 8 ^H
|
20
|
-
:
|
20
|
+
:ed_unassigned,
|
21
21
|
# 9 ^I
|
22
22
|
:ed_unassigned,
|
23
23
|
# 10 ^J
|
@@ -255,7 +255,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
|
|
255
255
|
# 126 ~
|
256
256
|
:vi_change_case,
|
257
257
|
# 127 ^?
|
258
|
-
:
|
258
|
+
:ed_unassigned,
|
259
259
|
# 128 M-^@
|
260
260
|
:ed_unassigned,
|
261
261
|
# 129 M-^A
|
data/lib/reline/kill_ring.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
class Reline::KillRing
|
2
|
+
include Enumerable
|
3
|
+
|
2
4
|
module State
|
3
5
|
FRESH = :fresh
|
4
6
|
CONTINUED = :continued
|
@@ -110,4 +112,14 @@ class Reline::KillRing
|
|
110
112
|
nil
|
111
113
|
end
|
112
114
|
end
|
115
|
+
|
116
|
+
def each
|
117
|
+
start = head = @ring.head
|
118
|
+
loop do
|
119
|
+
break if head.nil?
|
120
|
+
yield head.str
|
121
|
+
head = head.backward
|
122
|
+
break if head == start
|
123
|
+
end
|
124
|
+
end
|
113
125
|
end
|
data/lib/reline/line_editor.rb
CHANGED
@@ -50,12 +50,24 @@ class Reline::LineEditor
|
|
50
50
|
CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer)
|
51
51
|
MenuInfo = Struct.new('MenuInfo', :target, :list)
|
52
52
|
|
53
|
+
PROMPT_LIST_CACHE_TIMEOUT = 0.5
|
54
|
+
|
53
55
|
def initialize(config, encoding)
|
54
56
|
@config = config
|
55
57
|
@completion_append_character = ''
|
56
58
|
reset_variables(encoding: encoding)
|
57
59
|
end
|
58
60
|
|
61
|
+
def simplified_rendering?
|
62
|
+
if finished?
|
63
|
+
false
|
64
|
+
elsif @just_cursor_moving and not @rerender_all
|
65
|
+
true
|
66
|
+
else
|
67
|
+
not @rerender_all and not finished? and Reline::IOGate.in_pasting?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
59
71
|
private def check_multiline_prompt(buffer, prompt)
|
60
72
|
if @vi_arg
|
61
73
|
prompt = "(arg: #{@vi_arg}) "
|
@@ -66,8 +78,22 @@ class Reline::LineEditor
|
|
66
78
|
else
|
67
79
|
prompt = @prompt
|
68
80
|
end
|
81
|
+
return [prompt, calculate_width(prompt, true), [prompt] * buffer.size] if simplified_rendering?
|
69
82
|
if @prompt_proc
|
70
|
-
|
83
|
+
use_cached_prompt_list = false
|
84
|
+
if @cached_prompt_list
|
85
|
+
if @just_cursor_moving
|
86
|
+
use_cached_prompt_list = true
|
87
|
+
elsif Time.now.to_f < (@prompt_cache_time + PROMPT_LIST_CACHE_TIMEOUT) and buffer.size == @cached_prompt_list.size
|
88
|
+
use_cached_prompt_list = true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
if use_cached_prompt_list
|
92
|
+
prompt_list = @cached_prompt_list
|
93
|
+
else
|
94
|
+
prompt_list = @cached_prompt_list = @prompt_proc.(buffer)
|
95
|
+
@prompt_cache_time = Time.now.to_f
|
96
|
+
end
|
71
97
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
72
98
|
if @config.show_mode_in_prompt
|
73
99
|
if @config.editing_mode_is?(:vi_command)
|
@@ -105,6 +131,7 @@ class Reline::LineEditor
|
|
105
131
|
def reset(prompt = '', encoding:)
|
106
132
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
107
133
|
@screen_size = Reline::IOGate.get_screen_size
|
134
|
+
@screen_height = @screen_size.first
|
108
135
|
reset_variables(prompt, encoding: encoding)
|
109
136
|
@old_trap = Signal.trap('SIGINT') {
|
110
137
|
@old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
|
@@ -114,6 +141,7 @@ class Reline::LineEditor
|
|
114
141
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
115
142
|
old_screen_size = @screen_size
|
116
143
|
@screen_size = Reline::IOGate.get_screen_size
|
144
|
+
@screen_height = @screen_size.first
|
117
145
|
if old_screen_size.last < @screen_size.last # columns increase
|
118
146
|
@rerender_all = true
|
119
147
|
rerender
|
@@ -165,11 +193,12 @@ class Reline::LineEditor
|
|
165
193
|
@cleared = false
|
166
194
|
@rerender_all = false
|
167
195
|
@history_pointer = nil
|
168
|
-
@kill_ring
|
196
|
+
@kill_ring ||= Reline::KillRing.new
|
169
197
|
@vi_clipboard = ''
|
170
198
|
@vi_arg = nil
|
171
199
|
@waiting_proc = nil
|
172
200
|
@waiting_operator_proc = nil
|
201
|
+
@waiting_operator_vi_arg = nil
|
173
202
|
@completion_journey_data = nil
|
174
203
|
@completion_state = CompletionState::NORMAL
|
175
204
|
@perfect_matched = nil
|
@@ -177,7 +206,13 @@ class Reline::LineEditor
|
|
177
206
|
@first_prompt = true
|
178
207
|
@searching_prompt = nil
|
179
208
|
@first_char = true
|
209
|
+
@add_newline_to_end_of_buffer = false
|
210
|
+
@just_cursor_moving = nil
|
211
|
+
@cached_prompt_list = nil
|
212
|
+
@prompt_cache_time = nil
|
180
213
|
@eof = false
|
214
|
+
@continuous_insertion_buffer = String.new(encoding: @encoding)
|
215
|
+
@scroll_partial_screen = nil
|
181
216
|
reset_line
|
182
217
|
end
|
183
218
|
|
@@ -222,6 +257,7 @@ class Reline::LineEditor
|
|
222
257
|
@buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding))
|
223
258
|
@previous_line_index = @line_index
|
224
259
|
@line_index += 1
|
260
|
+
@just_cursor_moving = false
|
225
261
|
end
|
226
262
|
|
227
263
|
private def calculate_height_by_width(width)
|
@@ -262,28 +298,28 @@ class Reline::LineEditor
|
|
262
298
|
end
|
263
299
|
end
|
264
300
|
|
265
|
-
private def calculate_nearest_cursor
|
266
|
-
|
301
|
+
private def calculate_nearest_cursor(line_to_calc = @line, cursor = @cursor, started_from = @started_from, byte_pointer = @byte_pointer, update = true)
|
302
|
+
new_cursor_max = calculate_width(line_to_calc)
|
267
303
|
new_cursor = 0
|
268
304
|
new_byte_pointer = 0
|
269
305
|
height = 1
|
270
306
|
max_width = @screen_size.last
|
271
307
|
if @config.editing_mode_is?(:vi_command)
|
272
|
-
last_byte_size = Reline::Unicode.get_prev_mbchar_size(
|
308
|
+
last_byte_size = Reline::Unicode.get_prev_mbchar_size(line_to_calc, line_to_calc.bytesize)
|
273
309
|
if last_byte_size > 0
|
274
|
-
last_mbchar =
|
310
|
+
last_mbchar = line_to_calc.byteslice(line_to_calc.bytesize - last_byte_size, last_byte_size)
|
275
311
|
last_width = Reline::Unicode.get_mbchar_width(last_mbchar)
|
276
|
-
|
312
|
+
end_of_line_cursor = new_cursor_max - last_width
|
277
313
|
else
|
278
|
-
|
314
|
+
end_of_line_cursor = new_cursor_max
|
279
315
|
end
|
280
316
|
else
|
281
|
-
|
317
|
+
end_of_line_cursor = new_cursor_max
|
282
318
|
end
|
283
|
-
|
319
|
+
line_to_calc.encode(Encoding::UTF_8).grapheme_clusters.each do |gc|
|
284
320
|
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
|
285
321
|
now = new_cursor + mbchar_width
|
286
|
-
if now >
|
322
|
+
if now > end_of_line_cursor or now > cursor
|
287
323
|
break
|
288
324
|
end
|
289
325
|
new_cursor += mbchar_width
|
@@ -292,9 +328,21 @@ class Reline::LineEditor
|
|
292
328
|
end
|
293
329
|
new_byte_pointer += gc.bytesize
|
294
330
|
end
|
295
|
-
|
296
|
-
|
297
|
-
|
331
|
+
new_started_from = height - 1
|
332
|
+
if update
|
333
|
+
@cursor = new_cursor
|
334
|
+
@cursor_max = new_cursor_max
|
335
|
+
@started_from = new_started_from
|
336
|
+
@byte_pointer = new_byte_pointer
|
337
|
+
else
|
338
|
+
[new_cursor, new_cursor_max, new_started_from, new_byte_pointer]
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
def rerender_all
|
343
|
+
@rerender_all = true
|
344
|
+
process_insert(force: true)
|
345
|
+
rerender
|
298
346
|
end
|
299
347
|
|
300
348
|
def rerender
|
@@ -302,168 +350,53 @@ class Reline::LineEditor
|
|
302
350
|
if @menu_info
|
303
351
|
scroll_down(@highest_in_all - @first_line_started_from)
|
304
352
|
@rerender_all = true
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
@output.flush
|
309
|
-
scroll_down(1)
|
310
|
-
end
|
311
|
-
scroll_down(@highest_in_all - 1)
|
312
|
-
move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
|
353
|
+
end
|
354
|
+
if @menu_info
|
355
|
+
show_menu
|
313
356
|
@menu_info = nil
|
314
357
|
end
|
315
358
|
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
316
359
|
if @cleared
|
317
|
-
|
360
|
+
clear_screen_buffer(prompt, prompt_list, prompt_width)
|
318
361
|
@cleared = false
|
319
|
-
back = 0
|
320
|
-
modify_lines(whole_lines).each_with_index do |line, index|
|
321
|
-
if @prompt_proc
|
322
|
-
pr = prompt_list[index]
|
323
|
-
height = render_partial(pr, calculate_width(pr), line, false)
|
324
|
-
else
|
325
|
-
height = render_partial(prompt, prompt_width, line, false)
|
326
|
-
end
|
327
|
-
if index < (@buffer_of_lines.size - 1)
|
328
|
-
move_cursor_down(height)
|
329
|
-
back += height
|
330
|
-
end
|
331
|
-
end
|
332
|
-
move_cursor_up(back)
|
333
|
-
move_cursor_down(@first_line_started_from + @started_from)
|
334
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
335
362
|
return
|
336
363
|
end
|
337
364
|
new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
|
338
365
|
# FIXME: end of logical line sometimes breaks
|
339
|
-
if @
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
Reline::IOGate.erase_after_cursor
|
356
|
-
move_cursor_up(1)
|
357
|
-
end
|
358
|
-
move_cursor_up(all_height - 1)
|
366
|
+
if @add_newline_to_end_of_buffer
|
367
|
+
rerender_added_newline
|
368
|
+
@add_newline_to_end_of_buffer = false
|
369
|
+
else
|
370
|
+
if @just_cursor_moving and not @rerender_all
|
371
|
+
rendered = just_move_cursor
|
372
|
+
@just_cursor_moving = false
|
373
|
+
return
|
374
|
+
elsif @previous_line_index or new_highest_in_this != @highest_in_this
|
375
|
+
rerender_changed_current_line
|
376
|
+
@previous_line_index = nil
|
377
|
+
rendered = true
|
378
|
+
elsif @rerender_all
|
379
|
+
rerender_all_lines
|
380
|
+
@rerender_all = false
|
381
|
+
rendered = true
|
359
382
|
else
|
360
|
-
move_cursor_up(all_height - 1)
|
361
|
-
end
|
362
|
-
@highest_in_all = all_height
|
363
|
-
back = 0
|
364
|
-
modify_lines(new_lines).each_with_index do |line, index|
|
365
|
-
if @prompt_proc
|
366
|
-
prompt = prompt_list[index]
|
367
|
-
prompt_width = calculate_width(prompt, true)
|
368
|
-
end
|
369
|
-
height = render_partial(prompt, prompt_width, line, false)
|
370
|
-
if index < (new_lines.size - 1)
|
371
|
-
scroll_down(1)
|
372
|
-
back += height
|
373
|
-
else
|
374
|
-
back += height - 1
|
375
|
-
end
|
376
|
-
end
|
377
|
-
move_cursor_up(back)
|
378
|
-
if @previous_line_index
|
379
|
-
@buffer_of_lines[@previous_line_index] = @line
|
380
|
-
@line = @buffer_of_lines[@line_index]
|
381
|
-
end
|
382
|
-
@first_line_started_from =
|
383
|
-
if @line_index.zero?
|
384
|
-
0
|
385
|
-
else
|
386
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
387
|
-
end
|
388
|
-
if @prompt_proc
|
389
|
-
prompt = prompt_list[@line_index]
|
390
|
-
prompt_width = calculate_width(prompt, true)
|
391
|
-
end
|
392
|
-
move_cursor_down(@first_line_started_from)
|
393
|
-
calculate_nearest_cursor
|
394
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
395
|
-
move_cursor_down(@started_from)
|
396
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
397
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
398
|
-
@previous_line_index = nil
|
399
|
-
rendered = true
|
400
|
-
elsif @rerender_all
|
401
|
-
move_cursor_up(@first_line_started_from + @started_from)
|
402
|
-
Reline::IOGate.move_cursor_column(0)
|
403
|
-
back = 0
|
404
|
-
new_buffer = whole_lines
|
405
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
406
|
-
new_buffer.each_with_index do |line, index|
|
407
|
-
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
408
|
-
width = prompt_width + calculate_width(line)
|
409
|
-
height = calculate_height_by_width(width)
|
410
|
-
back += height
|
411
|
-
end
|
412
|
-
if back > @highest_in_all
|
413
|
-
scroll_down(back - 1)
|
414
|
-
move_cursor_up(back - 1)
|
415
|
-
elsif back < @highest_in_all
|
416
|
-
scroll_down(back)
|
417
|
-
Reline::IOGate.erase_after_cursor
|
418
|
-
(@highest_in_all - back - 1).times do
|
419
|
-
scroll_down(1)
|
420
|
-
Reline::IOGate.erase_after_cursor
|
421
|
-
end
|
422
|
-
move_cursor_up(@highest_in_all - 1)
|
423
|
-
end
|
424
|
-
modify_lines(new_buffer).each_with_index do |line, index|
|
425
|
-
if @prompt_proc
|
426
|
-
prompt = prompt_list[index]
|
427
|
-
prompt_width = calculate_width(prompt, true)
|
428
|
-
end
|
429
|
-
render_partial(prompt, prompt_width, line, false)
|
430
|
-
if index < (new_buffer.size - 1)
|
431
|
-
move_cursor_down(1)
|
432
|
-
end
|
433
383
|
end
|
434
|
-
move_cursor_up(back - 1)
|
435
|
-
if @prompt_proc
|
436
|
-
prompt = prompt_list[@line_index]
|
437
|
-
prompt_width = calculate_width(prompt, true)
|
438
|
-
end
|
439
|
-
@highest_in_all = back
|
440
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
441
|
-
@first_line_started_from =
|
442
|
-
if @line_index.zero?
|
443
|
-
0
|
444
|
-
else
|
445
|
-
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
446
|
-
end
|
447
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
448
|
-
move_cursor_down(@first_line_started_from + @started_from)
|
449
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
450
|
-
@rerender_all = false
|
451
|
-
rendered = true
|
452
384
|
end
|
453
385
|
line = modify_lines(whole_lines)[@line_index]
|
454
386
|
if @is_multiline
|
455
387
|
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
456
388
|
if finished?
|
457
389
|
# Always rerender on finish because output_modifier_proc may return a different output.
|
458
|
-
render_partial(prompt, prompt_width, line)
|
390
|
+
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
459
391
|
scroll_down(1)
|
460
392
|
Reline::IOGate.move_cursor_column(0)
|
461
393
|
Reline::IOGate.erase_after_cursor
|
462
394
|
elsif not rendered
|
463
|
-
render_partial(prompt, prompt_width, line)
|
395
|
+
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
464
396
|
end
|
397
|
+
@buffer_of_lines[@line_index] = @line
|
465
398
|
else
|
466
|
-
render_partial(prompt, prompt_width, line)
|
399
|
+
render_partial(prompt, prompt_width, line, 0)
|
467
400
|
if finished?
|
468
401
|
scroll_down(1)
|
469
402
|
Reline::IOGate.move_cursor_column(0)
|
@@ -472,8 +405,237 @@ class Reline::LineEditor
|
|
472
405
|
end
|
473
406
|
end
|
474
407
|
|
475
|
-
private def
|
408
|
+
private def calculate_scroll_partial_screen(highest_in_all, cursor_y)
|
409
|
+
if @screen_height < highest_in_all
|
410
|
+
old_scroll_partial_screen = @scroll_partial_screen
|
411
|
+
if cursor_y == 0
|
412
|
+
@scroll_partial_screen = 0
|
413
|
+
elsif cursor_y == (highest_in_all - 1)
|
414
|
+
@scroll_partial_screen = highest_in_all - @screen_height
|
415
|
+
else
|
416
|
+
if @scroll_partial_screen
|
417
|
+
if cursor_y <= @scroll_partial_screen
|
418
|
+
@scroll_partial_screen = cursor_y
|
419
|
+
elsif (@scroll_partial_screen + @screen_height - 1) < cursor_y
|
420
|
+
@scroll_partial_screen = cursor_y - (@screen_height - 1)
|
421
|
+
end
|
422
|
+
else
|
423
|
+
if cursor_y > (@screen_height - 1)
|
424
|
+
@scroll_partial_screen = cursor_y - (@screen_height - 1)
|
425
|
+
else
|
426
|
+
@scroll_partial_screen = 0
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
if @scroll_partial_screen != old_scroll_partial_screen
|
431
|
+
@rerender_all = true
|
432
|
+
end
|
433
|
+
else
|
434
|
+
if @scroll_partial_screen
|
435
|
+
@rerender_all = true
|
436
|
+
end
|
437
|
+
@scroll_partial_screen = nil
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
private def rerender_added_newline
|
442
|
+
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
|
+
@buffer_of_lines[@previous_line_index] = @line
|
446
|
+
@line = @buffer_of_lines[@line_index]
|
447
|
+
render_partial(prompt, prompt_width, @line, @first_line_started_from + @started_from + 1, with_control: false)
|
448
|
+
@cursor = @cursor_max = calculate_width(@line)
|
449
|
+
@byte_pointer = @line.bytesize
|
450
|
+
@highest_in_all += @highest_in_this
|
451
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
452
|
+
@first_line_started_from += @started_from + 1
|
453
|
+
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
454
|
+
@previous_line_index = nil
|
455
|
+
end
|
456
|
+
|
457
|
+
def just_move_cursor
|
458
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines, prompt)
|
459
|
+
move_cursor_up(@started_from)
|
460
|
+
new_first_line_started_from =
|
461
|
+
if @line_index.zero?
|
462
|
+
0
|
463
|
+
else
|
464
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
465
|
+
end
|
466
|
+
first_line_diff = new_first_line_started_from - @first_line_started_from
|
467
|
+
new_cursor, _, new_started_from, _ = calculate_nearest_cursor(@line, @cursor, @started_from, @byte_pointer, false)
|
468
|
+
new_started_from = calculate_height_by_width(prompt_width + new_cursor) - 1
|
469
|
+
calculate_scroll_partial_screen(@highest_in_all, new_first_line_started_from + new_started_from)
|
470
|
+
@previous_line_index = nil
|
471
|
+
if @rerender_all
|
472
|
+
@line = @buffer_of_lines[@line_index]
|
473
|
+
rerender_all_lines
|
474
|
+
@rerender_all = false
|
475
|
+
true
|
476
|
+
else
|
477
|
+
@line = @buffer_of_lines[@line_index]
|
478
|
+
@first_line_started_from = new_first_line_started_from
|
479
|
+
@started_from = new_started_from
|
480
|
+
@cursor = new_cursor
|
481
|
+
move_cursor_down(first_line_diff + @started_from)
|
482
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
483
|
+
false
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
private def rerender_changed_current_line
|
488
|
+
if @previous_line_index
|
489
|
+
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
490
|
+
else
|
491
|
+
new_lines = whole_lines
|
492
|
+
end
|
493
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
494
|
+
all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
|
495
|
+
diff = all_height - @highest_in_all
|
496
|
+
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
497
|
+
if diff > 0
|
498
|
+
scroll_down(diff)
|
499
|
+
move_cursor_up(all_height - 1)
|
500
|
+
elsif diff < 0
|
501
|
+
(-diff).times do
|
502
|
+
Reline::IOGate.move_cursor_column(0)
|
503
|
+
Reline::IOGate.erase_after_cursor
|
504
|
+
move_cursor_up(1)
|
505
|
+
end
|
506
|
+
move_cursor_up(all_height - 1)
|
507
|
+
else
|
508
|
+
move_cursor_up(all_height - 1)
|
509
|
+
end
|
510
|
+
@highest_in_all = all_height
|
511
|
+
back = render_whole_lines(new_lines, prompt_list || prompt, prompt_width)
|
512
|
+
move_cursor_up(back)
|
513
|
+
if @previous_line_index
|
514
|
+
@buffer_of_lines[@previous_line_index] = @line
|
515
|
+
@line = @buffer_of_lines[@line_index]
|
516
|
+
end
|
517
|
+
@first_line_started_from =
|
518
|
+
if @line_index.zero?
|
519
|
+
0
|
520
|
+
else
|
521
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
522
|
+
end
|
523
|
+
if @prompt_proc
|
524
|
+
prompt = prompt_list[@line_index]
|
525
|
+
prompt_width = calculate_width(prompt, true)
|
526
|
+
end
|
527
|
+
move_cursor_down(@first_line_started_from)
|
528
|
+
calculate_nearest_cursor
|
529
|
+
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
530
|
+
move_cursor_down(@started_from)
|
531
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
532
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
533
|
+
end
|
534
|
+
|
535
|
+
private def rerender_all_lines
|
536
|
+
move_cursor_up(@first_line_started_from + @started_from)
|
537
|
+
Reline::IOGate.move_cursor_column(0)
|
538
|
+
back = 0
|
539
|
+
new_buffer = whole_lines
|
540
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
541
|
+
new_buffer.each_with_index do |line, index|
|
542
|
+
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
543
|
+
width = prompt_width + calculate_width(line)
|
544
|
+
height = calculate_height_by_width(width)
|
545
|
+
back += height
|
546
|
+
end
|
547
|
+
old_highest_in_all = @highest_in_all
|
548
|
+
if @line_index.zero?
|
549
|
+
new_first_line_started_from = 0
|
550
|
+
else
|
551
|
+
new_first_line_started_from = calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
552
|
+
end
|
553
|
+
new_started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
554
|
+
if back > old_highest_in_all
|
555
|
+
scroll_down(back - 1)
|
556
|
+
move_cursor_up(back - 1)
|
557
|
+
elsif back < old_highest_in_all
|
558
|
+
scroll_down(back)
|
559
|
+
Reline::IOGate.erase_after_cursor
|
560
|
+
(old_highest_in_all - back - 1).times do
|
561
|
+
scroll_down(1)
|
562
|
+
Reline::IOGate.erase_after_cursor
|
563
|
+
end
|
564
|
+
move_cursor_up(old_highest_in_all - 1)
|
565
|
+
end
|
566
|
+
calculate_scroll_partial_screen(back, new_first_line_started_from + new_started_from)
|
567
|
+
render_whole_lines(new_buffer, prompt_list || prompt, prompt_width)
|
568
|
+
if @prompt_proc
|
569
|
+
prompt = prompt_list[@line_index]
|
570
|
+
prompt_width = calculate_width(prompt, true)
|
571
|
+
end
|
572
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
573
|
+
@highest_in_all = back
|
574
|
+
@first_line_started_from = new_first_line_started_from
|
575
|
+
@started_from = new_started_from
|
576
|
+
if @scroll_partial_screen
|
577
|
+
Reline::IOGate.move_cursor_up(@screen_height - (@first_line_started_from + @started_from - @scroll_partial_screen) - 1)
|
578
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
579
|
+
else
|
580
|
+
move_cursor_down(@first_line_started_from + @started_from - back + 1)
|
581
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
private def render_whole_lines(lines, prompt, prompt_width)
|
586
|
+
rendered_height = 0
|
587
|
+
modify_lines(lines).each_with_index do |line, index|
|
588
|
+
if prompt.is_a?(Array)
|
589
|
+
line_prompt = prompt[index]
|
590
|
+
prompt_width = calculate_width(line_prompt, true)
|
591
|
+
else
|
592
|
+
line_prompt = prompt
|
593
|
+
end
|
594
|
+
height = render_partial(line_prompt, prompt_width, line, rendered_height, with_control: false)
|
595
|
+
if index < (lines.size - 1)
|
596
|
+
if @scroll_partial_screen
|
597
|
+
if (@scroll_partial_screen - height) < rendered_height and (@scroll_partial_screen + @screen_height - 1) >= (rendered_height + height)
|
598
|
+
move_cursor_down(1)
|
599
|
+
end
|
600
|
+
else
|
601
|
+
scroll_down(1)
|
602
|
+
end
|
603
|
+
rendered_height += height
|
604
|
+
else
|
605
|
+
rendered_height += height - 1
|
606
|
+
end
|
607
|
+
end
|
608
|
+
rendered_height
|
609
|
+
end
|
610
|
+
|
611
|
+
private def render_partial(prompt, prompt_width, line_to_render, this_started_from, with_control: true)
|
476
612
|
visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
|
613
|
+
cursor_up_from_last_line = 0
|
614
|
+
# TODO: This logic would be sometimes buggy if this logical line isn't the current @line_index.
|
615
|
+
if @scroll_partial_screen
|
616
|
+
last_visual_line = this_started_from + (height - 1)
|
617
|
+
last_screen_line = @scroll_partial_screen + (@screen_height - 1)
|
618
|
+
if (@scroll_partial_screen - this_started_from) >= height
|
619
|
+
# Render nothing because this line is before the screen.
|
620
|
+
visual_lines = []
|
621
|
+
elsif this_started_from > last_screen_line
|
622
|
+
# Render nothing because this line is after the screen.
|
623
|
+
visual_lines = []
|
624
|
+
else
|
625
|
+
deleted_lines_before_screen = []
|
626
|
+
if @scroll_partial_screen > this_started_from and last_visual_line >= @scroll_partial_screen
|
627
|
+
# A part of visual lines are before the screen.
|
628
|
+
deleted_lines_before_screen = visual_lines.shift((@scroll_partial_screen - this_started_from) * 2)
|
629
|
+
deleted_lines_before_screen.compact!
|
630
|
+
end
|
631
|
+
if this_started_from <= last_screen_line and last_screen_line < last_visual_line
|
632
|
+
# A part of visual lines are after the screen.
|
633
|
+
visual_lines.pop((last_visual_line - last_screen_line) * 2)
|
634
|
+
end
|
635
|
+
move_cursor_up(deleted_lines_before_screen.size - @started_from)
|
636
|
+
cursor_up_from_last_line = @started_from - deleted_lines_before_screen.size
|
637
|
+
end
|
638
|
+
end
|
477
639
|
if with_control
|
478
640
|
if height > @highest_in_this
|
479
641
|
diff = height - @highest_in_this
|
@@ -487,10 +649,14 @@ class Reline::LineEditor
|
|
487
649
|
@highest_in_this = height
|
488
650
|
end
|
489
651
|
move_cursor_up(@started_from)
|
652
|
+
cursor_up_from_last_line = height - 1 - @started_from
|
490
653
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
491
654
|
end
|
492
|
-
Reline::
|
655
|
+
if Reline::Unicode::CSI_REGEXP.match?(prompt + line_to_render)
|
656
|
+
@output.write "\e[0m" # clear character decorations
|
657
|
+
end
|
493
658
|
visual_lines.each_with_index do |line, index|
|
659
|
+
Reline::IOGate.move_cursor_column(0)
|
494
660
|
if line.nil?
|
495
661
|
if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
496
662
|
# reaches the end of line
|
@@ -522,14 +688,18 @@ class Reline::LineEditor
|
|
522
688
|
@pre_input_hook&.call
|
523
689
|
end
|
524
690
|
end
|
525
|
-
|
691
|
+
unless visual_lines.empty?
|
692
|
+
Reline::IOGate.erase_after_cursor
|
693
|
+
Reline::IOGate.move_cursor_column(0)
|
694
|
+
end
|
526
695
|
if with_control
|
527
696
|
# Just after rendring, so the cursor is on the last line.
|
528
697
|
if finished?
|
529
698
|
Reline::IOGate.move_cursor_column(0)
|
530
699
|
else
|
531
700
|
# Moves up from bottom of lines to the cursor position.
|
532
|
-
move_cursor_up(
|
701
|
+
move_cursor_up(cursor_up_from_last_line)
|
702
|
+
# This logic is buggy if a fullwidth char is wrapped because there is only one halfwidth at end of a line.
|
533
703
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
534
704
|
end
|
535
705
|
end
|
@@ -537,7 +707,7 @@ class Reline::LineEditor
|
|
537
707
|
end
|
538
708
|
|
539
709
|
private def modify_lines(before)
|
540
|
-
return before if before.nil? || before.empty?
|
710
|
+
return before if before.nil? || before.empty? || simplified_rendering?
|
541
711
|
|
542
712
|
if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: finished?)
|
543
713
|
after.lines("\n").map { |l| l.chomp('') }
|
@@ -546,6 +716,39 @@ class Reline::LineEditor
|
|
546
716
|
end
|
547
717
|
end
|
548
718
|
|
719
|
+
private def show_menu
|
720
|
+
scroll_down(@highest_in_all - @first_line_started_from)
|
721
|
+
@rerender_all = true
|
722
|
+
@menu_info.list.sort!.each do |item|
|
723
|
+
Reline::IOGate.move_cursor_column(0)
|
724
|
+
@output.write item
|
725
|
+
@output.flush
|
726
|
+
scroll_down(1)
|
727
|
+
end
|
728
|
+
scroll_down(@highest_in_all - 1)
|
729
|
+
move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
|
730
|
+
end
|
731
|
+
|
732
|
+
private def clear_screen_buffer(prompt, prompt_list, prompt_width)
|
733
|
+
Reline::IOGate.clear_screen
|
734
|
+
back = 0
|
735
|
+
modify_lines(whole_lines).each_with_index do |line, index|
|
736
|
+
if @prompt_proc
|
737
|
+
pr = prompt_list[index]
|
738
|
+
height = render_partial(pr, calculate_width(pr), line, back, with_control: false)
|
739
|
+
else
|
740
|
+
height = render_partial(prompt, prompt_width, line, back, with_control: false)
|
741
|
+
end
|
742
|
+
if index < (@buffer_of_lines.size - 1)
|
743
|
+
move_cursor_down(height)
|
744
|
+
back += height
|
745
|
+
end
|
746
|
+
end
|
747
|
+
move_cursor_up(back)
|
748
|
+
move_cursor_down(@first_line_started_from + @started_from)
|
749
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
750
|
+
end
|
751
|
+
|
549
752
|
def editing_mode
|
550
753
|
@config.editing_mode
|
551
754
|
end
|
@@ -565,7 +768,7 @@ class Reline::LineEditor
|
|
565
768
|
else
|
566
769
|
i&.start_with?(target)
|
567
770
|
end
|
568
|
-
}
|
771
|
+
}.uniq
|
569
772
|
if is_menu
|
570
773
|
menu(target, list)
|
571
774
|
return nil
|
@@ -682,7 +885,8 @@ class Reline::LineEditor
|
|
682
885
|
if @waiting_operator_proc
|
683
886
|
if VI_MOTIONS.include?(method_symbol)
|
684
887
|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
|
685
|
-
|
888
|
+
@vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg > 1
|
889
|
+
block.(true)
|
686
890
|
unless @waiting_proc
|
687
891
|
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
|
688
892
|
@cursor, @byte_pointer = old_cursor, old_byte_pointer
|
@@ -690,27 +894,56 @@ class Reline::LineEditor
|
|
690
894
|
else
|
691
895
|
old_waiting_proc = @waiting_proc
|
692
896
|
old_waiting_operator_proc = @waiting_operator_proc
|
897
|
+
current_waiting_operator_proc = @waiting_operator_proc
|
693
898
|
@waiting_proc = proc { |k|
|
694
899
|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
|
695
900
|
old_waiting_proc.(k)
|
696
901
|
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
|
697
902
|
@cursor, @byte_pointer = old_cursor, old_byte_pointer
|
698
|
-
|
903
|
+
current_waiting_operator_proc.(cursor_diff, byte_pointer_diff)
|
699
904
|
@waiting_operator_proc = old_waiting_operator_proc
|
700
905
|
}
|
701
906
|
end
|
702
907
|
else
|
703
908
|
# Ignores operator when not motion is given.
|
704
|
-
block.()
|
909
|
+
block.(false)
|
705
910
|
end
|
706
911
|
@waiting_operator_proc = nil
|
912
|
+
@waiting_operator_vi_arg = nil
|
913
|
+
@vi_arg = nil
|
707
914
|
else
|
708
|
-
block.()
|
915
|
+
block.(false)
|
709
916
|
end
|
710
917
|
end
|
711
918
|
|
712
919
|
private def argumentable?(method_obj)
|
713
|
-
method_obj and method_obj.parameters.
|
920
|
+
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :arg }
|
921
|
+
end
|
922
|
+
|
923
|
+
private def inclusive?(method_obj)
|
924
|
+
# If a motion method with the keyword argument "inclusive" follows the
|
925
|
+
# operator, it must contain the character at the cursor position.
|
926
|
+
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :inclusive }
|
927
|
+
end
|
928
|
+
|
929
|
+
def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
|
930
|
+
if @config.editing_mode_is?(:emacs, :vi_insert) and @waiting_proc.nil? and @waiting_operator_proc.nil?
|
931
|
+
not_insertion = method_symbol != :ed_insert
|
932
|
+
process_insert(force: not_insertion)
|
933
|
+
end
|
934
|
+
if @vi_arg and argumentable?(method_obj)
|
935
|
+
if with_operator and inclusive?(method_obj)
|
936
|
+
method_obj.(key, arg: @vi_arg, inclusive: true)
|
937
|
+
else
|
938
|
+
method_obj.(key, arg: @vi_arg)
|
939
|
+
end
|
940
|
+
else
|
941
|
+
if with_operator and inclusive?(method_obj)
|
942
|
+
method_obj.(key, inclusive: true)
|
943
|
+
else
|
944
|
+
method_obj.(key)
|
945
|
+
end
|
946
|
+
end
|
714
947
|
end
|
715
948
|
|
716
949
|
private def process_key(key, method_symbol)
|
@@ -721,11 +954,11 @@ class Reline::LineEditor
|
|
721
954
|
end
|
722
955
|
if method_symbol and key.is_a?(Symbol)
|
723
956
|
if @vi_arg and argumentable?(method_obj)
|
724
|
-
run_for_operators(key, method_symbol) do
|
725
|
-
method_obj
|
957
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
958
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
726
959
|
end
|
727
960
|
else
|
728
|
-
method_obj
|
961
|
+
wrap_method_call(method_symbol, method_obj, key) if method_obj
|
729
962
|
end
|
730
963
|
@kill_ring.process
|
731
964
|
@vi_arg = nil
|
@@ -734,15 +967,15 @@ class Reline::LineEditor
|
|
734
967
|
ed_argument_digit(key)
|
735
968
|
else
|
736
969
|
if argumentable?(method_obj)
|
737
|
-
run_for_operators(key, method_symbol) do
|
738
|
-
method_obj
|
970
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
971
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
739
972
|
end
|
740
973
|
elsif @waiting_proc
|
741
974
|
@waiting_proc.(key)
|
742
975
|
elsif method_obj
|
743
|
-
method_obj
|
976
|
+
wrap_method_call(method_symbol, method_obj, key)
|
744
977
|
else
|
745
|
-
ed_insert(key)
|
978
|
+
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
746
979
|
end
|
747
980
|
@kill_ring.process
|
748
981
|
@vi_arg = nil
|
@@ -752,15 +985,15 @@ class Reline::LineEditor
|
|
752
985
|
@kill_ring.process
|
753
986
|
elsif method_obj
|
754
987
|
if method_symbol == :ed_argument_digit
|
755
|
-
method_obj
|
988
|
+
wrap_method_call(method_symbol, method_obj, key)
|
756
989
|
else
|
757
|
-
run_for_operators(key, method_symbol) do
|
758
|
-
method_obj
|
990
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
991
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
759
992
|
end
|
760
993
|
end
|
761
994
|
@kill_ring.process
|
762
995
|
else
|
763
|
-
ed_insert(key)
|
996
|
+
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
764
997
|
end
|
765
998
|
end
|
766
999
|
|
@@ -803,6 +1036,7 @@ class Reline::LineEditor
|
|
803
1036
|
end
|
804
1037
|
|
805
1038
|
def input_key(key)
|
1039
|
+
@just_cursor_moving = nil
|
806
1040
|
if key.char.nil?
|
807
1041
|
if @first_char
|
808
1042
|
@line = nil
|
@@ -810,6 +1044,7 @@ class Reline::LineEditor
|
|
810
1044
|
finish
|
811
1045
|
return
|
812
1046
|
end
|
1047
|
+
old_line = @line.dup
|
813
1048
|
@first_char = false
|
814
1049
|
completion_occurs = false
|
815
1050
|
if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
|
@@ -817,6 +1052,7 @@ class Reline::LineEditor
|
|
817
1052
|
result = call_completion_proc
|
818
1053
|
if result.is_a?(Array)
|
819
1054
|
completion_occurs = true
|
1055
|
+
process_insert
|
820
1056
|
complete(result)
|
821
1057
|
end
|
822
1058
|
end
|
@@ -825,6 +1061,7 @@ class Reline::LineEditor
|
|
825
1061
|
result = call_completion_proc
|
826
1062
|
if result.is_a?(Array)
|
827
1063
|
completion_occurs = true
|
1064
|
+
process_insert
|
828
1065
|
move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
|
829
1066
|
end
|
830
1067
|
end
|
@@ -836,7 +1073,18 @@ class Reline::LineEditor
|
|
836
1073
|
unless completion_occurs
|
837
1074
|
@completion_state = CompletionState::NORMAL
|
838
1075
|
end
|
839
|
-
if
|
1076
|
+
if not Reline::IOGate.in_pasting? and @just_cursor_moving.nil?
|
1077
|
+
if @previous_line_index and @buffer_of_lines[@previous_line_index] == @line
|
1078
|
+
@just_cursor_moving = true
|
1079
|
+
elsif @previous_line_index.nil? and @buffer_of_lines[@line_index] == @line and old_line == @line
|
1080
|
+
@just_cursor_moving = true
|
1081
|
+
else
|
1082
|
+
@just_cursor_moving = false
|
1083
|
+
end
|
1084
|
+
else
|
1085
|
+
@just_cursor_moving = false
|
1086
|
+
end
|
1087
|
+
if @is_multiline and @auto_indent_proc and not simplified_rendering?
|
840
1088
|
process_auto_indent
|
841
1089
|
end
|
842
1090
|
end
|
@@ -1038,6 +1286,7 @@ class Reline::LineEditor
|
|
1038
1286
|
|
1039
1287
|
def finish
|
1040
1288
|
@finished = true
|
1289
|
+
@rerender_all = true
|
1041
1290
|
@config.reset
|
1042
1291
|
end
|
1043
1292
|
|
@@ -1066,6 +1315,9 @@ class Reline::LineEditor
|
|
1066
1315
|
|
1067
1316
|
private def key_newline(key)
|
1068
1317
|
if @is_multiline
|
1318
|
+
if (@buffer_of_lines.size - 1) == @line_index and @line.bytesize == @byte_pointer
|
1319
|
+
@add_newline_to_end_of_buffer = true
|
1320
|
+
end
|
1069
1321
|
next_line = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
|
1070
1322
|
cursor_line = @line.byteslice(0, @byte_pointer)
|
1071
1323
|
insert_new_line(cursor_line, next_line)
|
@@ -1076,38 +1328,62 @@ class Reline::LineEditor
|
|
1076
1328
|
|
1077
1329
|
private def ed_unassigned(key) end # do nothing
|
1078
1330
|
|
1331
|
+
private def process_insert(force: false)
|
1332
|
+
return if @continuous_insertion_buffer.empty? or (Reline::IOGate.in_pasting? and not force)
|
1333
|
+
width = Reline::Unicode.calculate_width(@continuous_insertion_buffer)
|
1334
|
+
bytesize = @continuous_insertion_buffer.bytesize
|
1335
|
+
if @cursor == @cursor_max
|
1336
|
+
@line += @continuous_insertion_buffer
|
1337
|
+
else
|
1338
|
+
@line = byteinsert(@line, @byte_pointer, @continuous_insertion_buffer)
|
1339
|
+
end
|
1340
|
+
@byte_pointer += bytesize
|
1341
|
+
@cursor += width
|
1342
|
+
@cursor_max += width
|
1343
|
+
@continuous_insertion_buffer.clear
|
1344
|
+
end
|
1345
|
+
|
1079
1346
|
private def ed_insert(key)
|
1347
|
+
str = nil
|
1348
|
+
width = nil
|
1349
|
+
bytesize = nil
|
1080
1350
|
if key.instance_of?(String)
|
1081
1351
|
begin
|
1082
1352
|
key.encode(Encoding::UTF_8)
|
1083
1353
|
rescue Encoding::UndefinedConversionError
|
1084
1354
|
return
|
1085
1355
|
end
|
1086
|
-
|
1087
|
-
|
1088
|
-
@line += key
|
1089
|
-
else
|
1090
|
-
@line = byteinsert(@line, @byte_pointer, key)
|
1091
|
-
end
|
1092
|
-
@byte_pointer += key.bytesize
|
1093
|
-
@cursor += width
|
1094
|
-
@cursor_max += width
|
1356
|
+
str = key
|
1357
|
+
bytesize = key.bytesize
|
1095
1358
|
else
|
1096
1359
|
begin
|
1097
1360
|
key.chr.encode(Encoding::UTF_8)
|
1098
1361
|
rescue Encoding::UndefinedConversionError
|
1099
1362
|
return
|
1100
1363
|
end
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1364
|
+
str = key.chr
|
1365
|
+
bytesize = 1
|
1366
|
+
end
|
1367
|
+
if Reline::IOGate.in_pasting?
|
1368
|
+
@continuous_insertion_buffer << str
|
1369
|
+
return
|
1370
|
+
elsif not @continuous_insertion_buffer.empty?
|
1371
|
+
process_insert
|
1372
|
+
end
|
1373
|
+
width = Reline::Unicode.get_mbchar_width(str)
|
1374
|
+
if @cursor == @cursor_max
|
1375
|
+
@line += str
|
1376
|
+
else
|
1377
|
+
@line = byteinsert(@line, @byte_pointer, str)
|
1378
|
+
end
|
1379
|
+
last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
1380
|
+
@byte_pointer += bytesize
|
1381
|
+
last_mbchar = @line.byteslice((@byte_pointer - bytesize - last_byte_size), last_byte_size)
|
1382
|
+
if last_byte_size != 0 and (last_mbchar + str).grapheme_clusters.size == 1
|
1383
|
+
width = 0
|
1110
1384
|
end
|
1385
|
+
@cursor += width
|
1386
|
+
@cursor_max += width
|
1111
1387
|
end
|
1112
1388
|
alias_method :ed_digit, :ed_insert
|
1113
1389
|
alias_method :self_insert, :ed_insert
|
@@ -1164,6 +1440,7 @@ class Reline::LineEditor
|
|
1164
1440
|
arg -= 1
|
1165
1441
|
ed_prev_char(key, arg: arg) if arg > 0
|
1166
1442
|
end
|
1443
|
+
alias_method :backward_char, :ed_prev_char
|
1167
1444
|
|
1168
1445
|
private def vi_first_print(key)
|
1169
1446
|
@byte_pointer, @cursor = Reline::Unicode.vi_first_print(@line)
|
@@ -1593,6 +1870,7 @@ class Reline::LineEditor
|
|
1593
1870
|
end
|
1594
1871
|
|
1595
1872
|
private def ed_newline(key)
|
1873
|
+
process_insert(force: true)
|
1596
1874
|
if @is_multiline
|
1597
1875
|
if @config.editing_mode_is?(:vi_command)
|
1598
1876
|
if @line_index < (@buffer_of_lines.size - 1)
|
@@ -1671,6 +1949,7 @@ class Reline::LineEditor
|
|
1671
1949
|
@cursor = 0
|
1672
1950
|
end
|
1673
1951
|
end
|
1952
|
+
alias_method :kill_line, :em_kill_line
|
1674
1953
|
|
1675
1954
|
private def em_delete(key)
|
1676
1955
|
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
|
@@ -1721,6 +2000,7 @@ class Reline::LineEditor
|
|
1721
2000
|
@byte_pointer += yanked.bytesize
|
1722
2001
|
end
|
1723
2002
|
end
|
2003
|
+
alias_method :yank, :em_yank
|
1724
2004
|
|
1725
2005
|
private def em_yank_pop(key)
|
1726
2006
|
yanked, prev_yank = @kill_ring.yank_pop
|
@@ -1737,6 +2017,7 @@ class Reline::LineEditor
|
|
1737
2017
|
@byte_pointer += yanked.bytesize
|
1738
2018
|
end
|
1739
2019
|
end
|
2020
|
+
alias_method :yank_pop, :em_yank_pop
|
1740
2021
|
|
1741
2022
|
private def ed_clear_screen(key)
|
1742
2023
|
@cleared = true
|
@@ -1867,9 +2148,10 @@ class Reline::LineEditor
|
|
1867
2148
|
@byte_pointer -= byte_size
|
1868
2149
|
@cursor -= width
|
1869
2150
|
@cursor_max -= width
|
1870
|
-
@kill_ring.append(deleted)
|
2151
|
+
@kill_ring.append(deleted, true)
|
1871
2152
|
end
|
1872
2153
|
end
|
2154
|
+
alias_method :unix_word_rubout, :em_kill_region
|
1873
2155
|
|
1874
2156
|
private def copy_for_vi(text)
|
1875
2157
|
if @config.editing_mode_is?(:vi_insert) or @config.editing_mode_is?(:vi_command)
|
@@ -1890,7 +2172,7 @@ class Reline::LineEditor
|
|
1890
2172
|
ed_prev_char(key)
|
1891
2173
|
@config.editing_mode = :vi_command
|
1892
2174
|
end
|
1893
|
-
alias_method :
|
2175
|
+
alias_method :vi_movement_mode, :vi_command_mode
|
1894
2176
|
|
1895
2177
|
private def vi_next_word(key, arg: 1)
|
1896
2178
|
if @line.bytesize > @byte_pointer
|
@@ -1912,13 +2194,22 @@ class Reline::LineEditor
|
|
1912
2194
|
vi_prev_word(key, arg: arg) if arg > 0
|
1913
2195
|
end
|
1914
2196
|
|
1915
|
-
private def vi_end_word(key, arg: 1)
|
2197
|
+
private def vi_end_word(key, arg: 1, inclusive: false)
|
1916
2198
|
if @line.bytesize > @byte_pointer
|
1917
2199
|
byte_size, width = Reline::Unicode.vi_forward_end_word(@line, @byte_pointer)
|
1918
2200
|
@byte_pointer += byte_size
|
1919
2201
|
@cursor += width
|
1920
2202
|
end
|
1921
2203
|
arg -= 1
|
2204
|
+
if inclusive and arg.zero?
|
2205
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2206
|
+
if byte_size > 0
|
2207
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
2208
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
2209
|
+
@byte_pointer += byte_size
|
2210
|
+
@cursor += width
|
2211
|
+
end
|
2212
|
+
end
|
1922
2213
|
vi_end_word(key, arg: arg) if arg > 0
|
1923
2214
|
end
|
1924
2215
|
|
@@ -1942,13 +2233,22 @@ class Reline::LineEditor
|
|
1942
2233
|
vi_prev_big_word(key, arg: arg) if arg > 0
|
1943
2234
|
end
|
1944
2235
|
|
1945
|
-
private def vi_end_big_word(key, arg: 1)
|
2236
|
+
private def vi_end_big_word(key, arg: 1, inclusive: false)
|
1946
2237
|
if @line.bytesize > @byte_pointer
|
1947
2238
|
byte_size, width = Reline::Unicode.vi_big_forward_end_word(@line, @byte_pointer)
|
1948
2239
|
@byte_pointer += byte_size
|
1949
2240
|
@cursor += width
|
1950
2241
|
end
|
1951
2242
|
arg -= 1
|
2243
|
+
if inclusive and arg.zero?
|
2244
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2245
|
+
if byte_size > 0
|
2246
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
2247
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
2248
|
+
@byte_pointer += byte_size
|
2249
|
+
@cursor += width
|
2250
|
+
end
|
2251
|
+
end
|
1952
2252
|
vi_end_big_word(key, arg: arg) if arg > 0
|
1953
2253
|
end
|
1954
2254
|
|
@@ -2003,7 +2303,7 @@ class Reline::LineEditor
|
|
2003
2303
|
@cursor = 0
|
2004
2304
|
end
|
2005
2305
|
|
2006
|
-
private def vi_change_meta(key)
|
2306
|
+
private def vi_change_meta(key, arg: 1)
|
2007
2307
|
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2008
2308
|
if byte_pointer_diff > 0
|
2009
2309
|
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
@@ -2016,9 +2316,10 @@ class Reline::LineEditor
|
|
2016
2316
|
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
2017
2317
|
@config.editing_mode = :vi_insert
|
2018
2318
|
}
|
2319
|
+
@waiting_operator_vi_arg = arg
|
2019
2320
|
end
|
2020
2321
|
|
2021
|
-
private def vi_delete_meta(key)
|
2322
|
+
private def vi_delete_meta(key, arg: 1)
|
2022
2323
|
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2023
2324
|
if byte_pointer_diff > 0
|
2024
2325
|
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
@@ -2030,9 +2331,19 @@ class Reline::LineEditor
|
|
2030
2331
|
@cursor_max -= cursor_diff.abs
|
2031
2332
|
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
2032
2333
|
}
|
2334
|
+
@waiting_operator_vi_arg = arg
|
2033
2335
|
end
|
2034
2336
|
|
2035
|
-
private def vi_yank(key)
|
2337
|
+
private def vi_yank(key, arg: 1)
|
2338
|
+
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2339
|
+
if byte_pointer_diff > 0
|
2340
|
+
cut = @line.byteslice(@byte_pointer, byte_pointer_diff)
|
2341
|
+
elsif byte_pointer_diff < 0
|
2342
|
+
cut = @line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
2343
|
+
end
|
2344
|
+
copy_for_vi(cut)
|
2345
|
+
}
|
2346
|
+
@waiting_operator_vi_arg = arg
|
2036
2347
|
end
|
2037
2348
|
|
2038
2349
|
private def vi_list_or_eof(key)
|
@@ -2177,15 +2488,15 @@ class Reline::LineEditor
|
|
2177
2488
|
}
|
2178
2489
|
end
|
2179
2490
|
|
2180
|
-
private def vi_next_char(key, arg: 1)
|
2181
|
-
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg) }
|
2491
|
+
private def vi_next_char(key, arg: 1, inclusive: false)
|
2492
|
+
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, inclusive: inclusive) }
|
2182
2493
|
end
|
2183
2494
|
|
2184
|
-
private def vi_to_next_char(key, arg: 1)
|
2185
|
-
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, true) }
|
2495
|
+
private def vi_to_next_char(key, arg: 1, inclusive: false)
|
2496
|
+
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, need_prev_char: true, inclusive: inclusive) }
|
2186
2497
|
end
|
2187
2498
|
|
2188
|
-
private def search_next_char(key, arg, need_prev_char
|
2499
|
+
private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
|
2189
2500
|
if key.instance_of?(String)
|
2190
2501
|
inputed_char = key
|
2191
2502
|
else
|
@@ -2222,6 +2533,15 @@ class Reline::LineEditor
|
|
2222
2533
|
@byte_pointer += byte_size
|
2223
2534
|
@cursor += width
|
2224
2535
|
end
|
2536
|
+
if inclusive
|
2537
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2538
|
+
if byte_size > 0
|
2539
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
2540
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
2541
|
+
@byte_pointer += byte_size
|
2542
|
+
@cursor += width
|
2543
|
+
end
|
2544
|
+
end
|
2225
2545
|
@waiting_proc = nil
|
2226
2546
|
end
|
2227
2547
|
|
@@ -2293,6 +2613,7 @@ class Reline::LineEditor
|
|
2293
2613
|
alias_method :set_mark, :em_set_mark
|
2294
2614
|
|
2295
2615
|
private def em_exchange_mark(key)
|
2616
|
+
return unless @mark_pointer
|
2296
2617
|
new_pointer = [@byte_pointer, @line_index]
|
2297
2618
|
@previous_line_index = @line_index
|
2298
2619
|
@byte_pointer, @line_index = @mark_pointer
|