reline 0.1.9 → 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 +17 -3
- data/lib/reline/ansi.rb +44 -3
- data/lib/reline/config.rb +1 -0
- data/lib/reline/key_actor/emacs.rb +1 -1
- data/lib/reline/kill_ring.rb +12 -0
- data/lib/reline/line_editor.rb +364 -172
- data/lib/reline/line_editor.rb.orig +2606 -0
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +8 -1
- metadata +4 -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
|
@@ -243,6 +248,10 @@ module Reline
|
|
243
248
|
line_editor.input_key(c)
|
244
249
|
line_editor.rerender
|
245
250
|
}
|
251
|
+
if @bracketed_paste_finished
|
252
|
+
line_editor.rerender_all
|
253
|
+
@bracketed_paste_finished = false
|
254
|
+
end
|
246
255
|
}
|
247
256
|
if prev_pasting_state == true and not Reline::IOGate.in_pasting? and not line_editor.finished?
|
248
257
|
prev_pasting_state = false
|
@@ -275,8 +284,13 @@ module Reline
|
|
275
284
|
buffer = []
|
276
285
|
loop do
|
277
286
|
c = Reline::IOGate.getc
|
278
|
-
|
279
|
-
|
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
|
280
294
|
case result
|
281
295
|
when :matched
|
282
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,8 +81,48 @@ 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
|
+
|
83
124
|
def self.in_pasting?
|
84
|
-
not Reline::IOGate.empty_buffer?
|
125
|
+
@@in_bracketed_paste_mode or (not Reline::IOGate.empty_buffer?)
|
85
126
|
end
|
86
127
|
|
87
128
|
def self.empty_buffer?
|
@@ -131,7 +172,7 @@ class Reline::ANSI
|
|
131
172
|
|
132
173
|
def self.cursor_pos
|
133
174
|
begin
|
134
|
-
res = ''
|
175
|
+
res = +''
|
135
176
|
m = nil
|
136
177
|
@@input.raw do |stdin|
|
137
178
|
@@output << "\e[6n"
|
data/lib/reline/config.rb
CHANGED
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,6 +50,8 @@ 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 = ''
|
@@ -59,6 +61,8 @@ class Reline::LineEditor
|
|
59
61
|
def simplified_rendering?
|
60
62
|
if finished?
|
61
63
|
false
|
64
|
+
elsif @just_cursor_moving and not @rerender_all
|
65
|
+
true
|
62
66
|
else
|
63
67
|
not @rerender_all and not finished? and Reline::IOGate.in_pasting?
|
64
68
|
end
|
@@ -76,7 +80,20 @@ class Reline::LineEditor
|
|
76
80
|
end
|
77
81
|
return [prompt, calculate_width(prompt, true), [prompt] * buffer.size] if simplified_rendering?
|
78
82
|
if @prompt_proc
|
79
|
-
|
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
|
80
97
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
81
98
|
if @config.show_mode_in_prompt
|
82
99
|
if @config.editing_mode_is?(:vi_command)
|
@@ -114,6 +131,7 @@ class Reline::LineEditor
|
|
114
131
|
def reset(prompt = '', encoding:)
|
115
132
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
116
133
|
@screen_size = Reline::IOGate.get_screen_size
|
134
|
+
@screen_height = @screen_size.first
|
117
135
|
reset_variables(prompt, encoding: encoding)
|
118
136
|
@old_trap = Signal.trap('SIGINT') {
|
119
137
|
@old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
|
@@ -123,6 +141,7 @@ class Reline::LineEditor
|
|
123
141
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
124
142
|
old_screen_size = @screen_size
|
125
143
|
@screen_size = Reline::IOGate.get_screen_size
|
144
|
+
@screen_height = @screen_size.first
|
126
145
|
if old_screen_size.last < @screen_size.last # columns increase
|
127
146
|
@rerender_all = true
|
128
147
|
rerender
|
@@ -174,7 +193,7 @@ class Reline::LineEditor
|
|
174
193
|
@cleared = false
|
175
194
|
@rerender_all = false
|
176
195
|
@history_pointer = nil
|
177
|
-
@kill_ring
|
196
|
+
@kill_ring ||= Reline::KillRing.new
|
178
197
|
@vi_clipboard = ''
|
179
198
|
@vi_arg = nil
|
180
199
|
@waiting_proc = nil
|
@@ -188,8 +207,12 @@ class Reline::LineEditor
|
|
188
207
|
@searching_prompt = nil
|
189
208
|
@first_char = true
|
190
209
|
@add_newline_to_end_of_buffer = false
|
210
|
+
@just_cursor_moving = nil
|
211
|
+
@cached_prompt_list = nil
|
212
|
+
@prompt_cache_time = nil
|
191
213
|
@eof = false
|
192
214
|
@continuous_insertion_buffer = String.new(encoding: @encoding)
|
215
|
+
@scroll_partial_screen = nil
|
193
216
|
reset_line
|
194
217
|
end
|
195
218
|
|
@@ -234,6 +257,7 @@ class Reline::LineEditor
|
|
234
257
|
@buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding))
|
235
258
|
@previous_line_index = @line_index
|
236
259
|
@line_index += 1
|
260
|
+
@just_cursor_moving = false
|
237
261
|
end
|
238
262
|
|
239
263
|
private def calculate_height_by_width(width)
|
@@ -274,28 +298,28 @@ class Reline::LineEditor
|
|
274
298
|
end
|
275
299
|
end
|
276
300
|
|
277
|
-
private def calculate_nearest_cursor
|
278
|
-
|
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)
|
279
303
|
new_cursor = 0
|
280
304
|
new_byte_pointer = 0
|
281
305
|
height = 1
|
282
306
|
max_width = @screen_size.last
|
283
307
|
if @config.editing_mode_is?(:vi_command)
|
284
|
-
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)
|
285
309
|
if last_byte_size > 0
|
286
|
-
last_mbchar =
|
310
|
+
last_mbchar = line_to_calc.byteslice(line_to_calc.bytesize - last_byte_size, last_byte_size)
|
287
311
|
last_width = Reline::Unicode.get_mbchar_width(last_mbchar)
|
288
|
-
|
312
|
+
end_of_line_cursor = new_cursor_max - last_width
|
289
313
|
else
|
290
|
-
|
314
|
+
end_of_line_cursor = new_cursor_max
|
291
315
|
end
|
292
316
|
else
|
293
|
-
|
317
|
+
end_of_line_cursor = new_cursor_max
|
294
318
|
end
|
295
|
-
|
319
|
+
line_to_calc.encode(Encoding::UTF_8).grapheme_clusters.each do |gc|
|
296
320
|
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
|
297
321
|
now = new_cursor + mbchar_width
|
298
|
-
if now >
|
322
|
+
if now > end_of_line_cursor or now > cursor
|
299
323
|
break
|
300
324
|
end
|
301
325
|
new_cursor += mbchar_width
|
@@ -304,13 +328,20 @@ class Reline::LineEditor
|
|
304
328
|
end
|
305
329
|
new_byte_pointer += gc.bytesize
|
306
330
|
end
|
307
|
-
|
308
|
-
|
309
|
-
|
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
|
310
340
|
end
|
311
341
|
|
312
342
|
def rerender_all
|
313
343
|
@rerender_all = true
|
344
|
+
process_insert(force: true)
|
314
345
|
rerender
|
315
346
|
end
|
316
347
|
|
@@ -319,183 +350,53 @@ class Reline::LineEditor
|
|
319
350
|
if @menu_info
|
320
351
|
scroll_down(@highest_in_all - @first_line_started_from)
|
321
352
|
@rerender_all = true
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
@output.flush
|
326
|
-
scroll_down(1)
|
327
|
-
end
|
328
|
-
scroll_down(@highest_in_all - 1)
|
329
|
-
move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
|
353
|
+
end
|
354
|
+
if @menu_info
|
355
|
+
show_menu
|
330
356
|
@menu_info = nil
|
331
357
|
end
|
332
358
|
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
333
359
|
if @cleared
|
334
|
-
|
360
|
+
clear_screen_buffer(prompt, prompt_list, prompt_width)
|
335
361
|
@cleared = false
|
336
|
-
back = 0
|
337
|
-
modify_lines(whole_lines).each_with_index do |line, index|
|
338
|
-
if @prompt_proc
|
339
|
-
pr = prompt_list[index]
|
340
|
-
height = render_partial(pr, calculate_width(pr), line, false)
|
341
|
-
else
|
342
|
-
height = render_partial(prompt, prompt_width, line, false)
|
343
|
-
end
|
344
|
-
if index < (@buffer_of_lines.size - 1)
|
345
|
-
move_cursor_down(height)
|
346
|
-
back += height
|
347
|
-
end
|
348
|
-
end
|
349
|
-
move_cursor_up(back)
|
350
|
-
move_cursor_down(@first_line_started_from + @started_from)
|
351
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
352
362
|
return
|
353
363
|
end
|
354
364
|
new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
|
355
365
|
# FIXME: end of logical line sometimes breaks
|
356
366
|
if @add_newline_to_end_of_buffer
|
357
|
-
|
358
|
-
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
359
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
360
|
-
@buffer_of_lines[@previous_line_index] = @line
|
361
|
-
@line = @buffer_of_lines[@line_index]
|
362
|
-
render_partial(prompt, prompt_width, @line, false)
|
363
|
-
@cursor = @cursor_max = calculate_width(@line)
|
364
|
-
@byte_pointer = @line.bytesize
|
365
|
-
@highest_in_all += @highest_in_this
|
366
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
367
|
-
@first_line_started_from += @started_from + 1
|
368
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
369
|
-
@previous_line_index = nil
|
367
|
+
rerender_added_newline
|
370
368
|
@add_newline_to_end_of_buffer = false
|
371
|
-
|
372
|
-
if @
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
elsif diff < 0
|
385
|
-
(-diff).times do
|
386
|
-
Reline::IOGate.move_cursor_column(0)
|
387
|
-
Reline::IOGate.erase_after_cursor
|
388
|
-
move_cursor_up(1)
|
389
|
-
end
|
390
|
-
move_cursor_up(all_height - 1)
|
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
|
391
382
|
else
|
392
|
-
move_cursor_up(all_height - 1)
|
393
|
-
end
|
394
|
-
@highest_in_all = all_height
|
395
|
-
back = 0
|
396
|
-
modify_lines(new_lines).each_with_index do |line, index|
|
397
|
-
if @prompt_proc
|
398
|
-
prompt = prompt_list[index]
|
399
|
-
prompt_width = calculate_width(prompt, true)
|
400
|
-
end
|
401
|
-
height = render_partial(prompt, prompt_width, line, false)
|
402
|
-
if index < (new_lines.size - 1)
|
403
|
-
scroll_down(1)
|
404
|
-
back += height
|
405
|
-
else
|
406
|
-
back += height - 1
|
407
|
-
end
|
408
|
-
end
|
409
|
-
move_cursor_up(back)
|
410
|
-
if @previous_line_index
|
411
|
-
@buffer_of_lines[@previous_line_index] = @line
|
412
|
-
@line = @buffer_of_lines[@line_index]
|
413
|
-
end
|
414
|
-
@first_line_started_from =
|
415
|
-
if @line_index.zero?
|
416
|
-
0
|
417
|
-
else
|
418
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
419
|
-
end
|
420
|
-
if @prompt_proc
|
421
|
-
prompt = prompt_list[@line_index]
|
422
|
-
prompt_width = calculate_width(prompt, true)
|
423
|
-
end
|
424
|
-
move_cursor_down(@first_line_started_from)
|
425
|
-
calculate_nearest_cursor
|
426
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
427
|
-
move_cursor_down(@started_from)
|
428
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
429
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
430
|
-
@previous_line_index = nil
|
431
|
-
rendered = true
|
432
|
-
elsif @rerender_all
|
433
|
-
move_cursor_up(@first_line_started_from + @started_from)
|
434
|
-
Reline::IOGate.move_cursor_column(0)
|
435
|
-
back = 0
|
436
|
-
new_buffer = whole_lines
|
437
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
438
|
-
new_buffer.each_with_index do |line, index|
|
439
|
-
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
440
|
-
width = prompt_width + calculate_width(line)
|
441
|
-
height = calculate_height_by_width(width)
|
442
|
-
back += height
|
443
|
-
end
|
444
|
-
if back > @highest_in_all
|
445
|
-
scroll_down(back - 1)
|
446
|
-
move_cursor_up(back - 1)
|
447
|
-
elsif back < @highest_in_all
|
448
|
-
scroll_down(back)
|
449
|
-
Reline::IOGate.erase_after_cursor
|
450
|
-
(@highest_in_all - back - 1).times do
|
451
|
-
scroll_down(1)
|
452
|
-
Reline::IOGate.erase_after_cursor
|
453
|
-
end
|
454
|
-
move_cursor_up(@highest_in_all - 1)
|
455
|
-
end
|
456
|
-
modify_lines(new_buffer).each_with_index do |line, index|
|
457
|
-
if @prompt_proc
|
458
|
-
prompt = prompt_list[index]
|
459
|
-
prompt_width = calculate_width(prompt, true)
|
460
|
-
end
|
461
|
-
render_partial(prompt, prompt_width, line, false)
|
462
|
-
if index < (new_buffer.size - 1)
|
463
|
-
move_cursor_down(1)
|
464
|
-
end
|
465
383
|
end
|
466
|
-
move_cursor_up(back - 1)
|
467
|
-
if @prompt_proc
|
468
|
-
prompt = prompt_list[@line_index]
|
469
|
-
prompt_width = calculate_width(prompt, true)
|
470
|
-
end
|
471
|
-
@highest_in_all = back
|
472
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
473
|
-
@first_line_started_from =
|
474
|
-
if @line_index.zero?
|
475
|
-
0
|
476
|
-
else
|
477
|
-
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
478
|
-
end
|
479
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
480
|
-
move_cursor_down(@first_line_started_from + @started_from)
|
481
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
482
|
-
@rerender_all = false
|
483
|
-
rendered = true
|
484
384
|
end
|
485
385
|
line = modify_lines(whole_lines)[@line_index]
|
486
386
|
if @is_multiline
|
487
387
|
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
488
388
|
if finished?
|
489
389
|
# Always rerender on finish because output_modifier_proc may return a different output.
|
490
|
-
render_partial(prompt, prompt_width, line)
|
390
|
+
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
491
391
|
scroll_down(1)
|
492
392
|
Reline::IOGate.move_cursor_column(0)
|
493
393
|
Reline::IOGate.erase_after_cursor
|
494
394
|
elsif not rendered
|
495
|
-
render_partial(prompt, prompt_width, line)
|
395
|
+
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
496
396
|
end
|
397
|
+
@buffer_of_lines[@line_index] = @line
|
497
398
|
else
|
498
|
-
render_partial(prompt, prompt_width, line)
|
399
|
+
render_partial(prompt, prompt_width, line, 0)
|
499
400
|
if finished?
|
500
401
|
scroll_down(1)
|
501
402
|
Reline::IOGate.move_cursor_column(0)
|
@@ -504,8 +405,237 @@ class Reline::LineEditor
|
|
504
405
|
end
|
505
406
|
end
|
506
407
|
|
507
|
-
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)
|
508
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
|
509
639
|
if with_control
|
510
640
|
if height > @highest_in_this
|
511
641
|
diff = height - @highest_in_this
|
@@ -519,10 +649,14 @@ class Reline::LineEditor
|
|
519
649
|
@highest_in_this = height
|
520
650
|
end
|
521
651
|
move_cursor_up(@started_from)
|
652
|
+
cursor_up_from_last_line = height - 1 - @started_from
|
522
653
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
523
654
|
end
|
524
|
-
Reline::
|
655
|
+
if Reline::Unicode::CSI_REGEXP.match?(prompt + line_to_render)
|
656
|
+
@output.write "\e[0m" # clear character decorations
|
657
|
+
end
|
525
658
|
visual_lines.each_with_index do |line, index|
|
659
|
+
Reline::IOGate.move_cursor_column(0)
|
526
660
|
if line.nil?
|
527
661
|
if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
528
662
|
# reaches the end of line
|
@@ -554,15 +688,18 @@ class Reline::LineEditor
|
|
554
688
|
@pre_input_hook&.call
|
555
689
|
end
|
556
690
|
end
|
557
|
-
|
558
|
-
|
691
|
+
unless visual_lines.empty?
|
692
|
+
Reline::IOGate.erase_after_cursor
|
693
|
+
Reline::IOGate.move_cursor_column(0)
|
694
|
+
end
|
559
695
|
if with_control
|
560
696
|
# Just after rendring, so the cursor is on the last line.
|
561
697
|
if finished?
|
562
698
|
Reline::IOGate.move_cursor_column(0)
|
563
699
|
else
|
564
700
|
# Moves up from bottom of lines to the cursor position.
|
565
|
-
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.
|
566
703
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
567
704
|
end
|
568
705
|
end
|
@@ -579,6 +716,39 @@ class Reline::LineEditor
|
|
579
716
|
end
|
580
717
|
end
|
581
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
|
+
|
582
752
|
def editing_mode
|
583
753
|
@config.editing_mode
|
584
754
|
end
|
@@ -866,6 +1036,7 @@ class Reline::LineEditor
|
|
866
1036
|
end
|
867
1037
|
|
868
1038
|
def input_key(key)
|
1039
|
+
@just_cursor_moving = nil
|
869
1040
|
if key.char.nil?
|
870
1041
|
if @first_char
|
871
1042
|
@line = nil
|
@@ -873,6 +1044,7 @@ class Reline::LineEditor
|
|
873
1044
|
finish
|
874
1045
|
return
|
875
1046
|
end
|
1047
|
+
old_line = @line.dup
|
876
1048
|
@first_char = false
|
877
1049
|
completion_occurs = false
|
878
1050
|
if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
|
@@ -901,6 +1073,17 @@ class Reline::LineEditor
|
|
901
1073
|
unless completion_occurs
|
902
1074
|
@completion_state = CompletionState::NORMAL
|
903
1075
|
end
|
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
|
904
1087
|
if @is_multiline and @auto_indent_proc and not simplified_rendering?
|
905
1088
|
process_auto_indent
|
906
1089
|
end
|
@@ -1193,7 +1376,12 @@ class Reline::LineEditor
|
|
1193
1376
|
else
|
1194
1377
|
@line = byteinsert(@line, @byte_pointer, str)
|
1195
1378
|
end
|
1379
|
+
last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
1196
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
|
1384
|
+
end
|
1197
1385
|
@cursor += width
|
1198
1386
|
@cursor_max += width
|
1199
1387
|
end
|
@@ -1761,6 +1949,7 @@ class Reline::LineEditor
|
|
1761
1949
|
@cursor = 0
|
1762
1950
|
end
|
1763
1951
|
end
|
1952
|
+
alias_method :kill_line, :em_kill_line
|
1764
1953
|
|
1765
1954
|
private def em_delete(key)
|
1766
1955
|
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
|
@@ -1811,6 +2000,7 @@ class Reline::LineEditor
|
|
1811
2000
|
@byte_pointer += yanked.bytesize
|
1812
2001
|
end
|
1813
2002
|
end
|
2003
|
+
alias_method :yank, :em_yank
|
1814
2004
|
|
1815
2005
|
private def em_yank_pop(key)
|
1816
2006
|
yanked, prev_yank = @kill_ring.yank_pop
|
@@ -1827,6 +2017,7 @@ class Reline::LineEditor
|
|
1827
2017
|
@byte_pointer += yanked.bytesize
|
1828
2018
|
end
|
1829
2019
|
end
|
2020
|
+
alias_method :yank_pop, :em_yank_pop
|
1830
2021
|
|
1831
2022
|
private def ed_clear_screen(key)
|
1832
2023
|
@cleared = true
|
@@ -1957,9 +2148,10 @@ class Reline::LineEditor
|
|
1957
2148
|
@byte_pointer -= byte_size
|
1958
2149
|
@cursor -= width
|
1959
2150
|
@cursor_max -= width
|
1960
|
-
@kill_ring.append(deleted)
|
2151
|
+
@kill_ring.append(deleted, true)
|
1961
2152
|
end
|
1962
2153
|
end
|
2154
|
+
alias_method :unix_word_rubout, :em_kill_region
|
1963
2155
|
|
1964
2156
|
private def copy_for_vi(text)
|
1965
2157
|
if @config.editing_mode_is?(:vi_insert) or @config.editing_mode_is?(:vi_command)
|