reline 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/reline/ansi.rb +2 -0
- data/lib/reline/general_io.rb +3 -9
- data/lib/reline/kill_ring.rb +1 -1
- data/lib/reline/line_editor.rb +594 -1299
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +3 -1
- data/lib/reline.rb +21 -22
- metadata +3 -3
data/lib/reline/line_editor.rb
CHANGED
@@ -6,7 +6,6 @@ require 'tempfile'
|
|
6
6
|
class Reline::LineEditor
|
7
7
|
# TODO: undo
|
8
8
|
# TODO: Use "private alias_method" idiom after drop Ruby 2.5.
|
9
|
-
attr_reader :line
|
10
9
|
attr_reader :byte_pointer
|
11
10
|
attr_accessor :confirm_multiline_termination_proc
|
12
11
|
attr_accessor :completion_proc
|
@@ -14,7 +13,6 @@ class Reline::LineEditor
|
|
14
13
|
attr_accessor :output_modifier_proc
|
15
14
|
attr_accessor :prompt_proc
|
16
15
|
attr_accessor :auto_indent_proc
|
17
|
-
attr_accessor :pre_input_hook
|
18
16
|
attr_accessor :dig_perfect_match_proc
|
19
17
|
attr_writer :output
|
20
18
|
|
@@ -48,15 +46,17 @@ class Reline::LineEditor
|
|
48
46
|
PERFECT_MATCH = :perfect_match
|
49
47
|
end
|
50
48
|
|
49
|
+
RenderedScreen = Struct.new(:base_y, :lines, :cursor_y, keyword_init: true)
|
50
|
+
|
51
51
|
CompletionJourneyData = Struct.new(:preposing, :postposing, :list, :pointer)
|
52
52
|
MenuInfo = Struct.new(:target, :list)
|
53
53
|
|
54
|
-
PROMPT_LIST_CACHE_TIMEOUT = 0.5
|
55
54
|
MINIMUM_SCROLLBAR_HEIGHT = 1
|
56
55
|
|
57
56
|
def initialize(config, encoding)
|
58
57
|
@config = config
|
59
58
|
@completion_append_character = ''
|
59
|
+
@screen_size = Reline::IOGate.get_screen_size
|
60
60
|
reset_variables(encoding: encoding)
|
61
61
|
end
|
62
62
|
|
@@ -65,73 +65,38 @@ class Reline::LineEditor
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def set_pasting_state(in_pasting)
|
68
|
+
# While pasting, text to be inserted is stored to @continuous_insertion_buffer.
|
69
|
+
# After pasting, this buffer should be force inserted.
|
70
|
+
process_insert(force: true) if @in_pasting && !in_pasting
|
68
71
|
@in_pasting = in_pasting
|
69
72
|
end
|
70
73
|
|
71
|
-
def simplified_rendering?
|
72
|
-
if finished?
|
73
|
-
false
|
74
|
-
elsif @just_cursor_moving and not @rerender_all
|
75
|
-
true
|
76
|
-
else
|
77
|
-
not @rerender_all and not finished? and @in_pasting
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
74
|
private def check_mode_string
|
82
|
-
mode_string = nil
|
83
75
|
if @config.show_mode_in_prompt
|
84
76
|
if @config.editing_mode_is?(:vi_command)
|
85
|
-
|
77
|
+
@config.vi_cmd_mode_string
|
86
78
|
elsif @config.editing_mode_is?(:vi_insert)
|
87
|
-
|
79
|
+
@config.vi_ins_mode_string
|
88
80
|
elsif @config.editing_mode_is?(:emacs)
|
89
|
-
|
81
|
+
@config.emacs_mode_string
|
90
82
|
else
|
91
|
-
|
83
|
+
'?'
|
92
84
|
end
|
93
85
|
end
|
94
|
-
if mode_string != @prev_mode_string
|
95
|
-
@rerender_all = true
|
96
|
-
end
|
97
|
-
@prev_mode_string = mode_string
|
98
|
-
mode_string
|
99
86
|
end
|
100
87
|
|
101
|
-
private def check_multiline_prompt(buffer,
|
88
|
+
private def check_multiline_prompt(buffer, mode_string)
|
102
89
|
if @vi_arg
|
103
90
|
prompt = "(arg: #{@vi_arg}) "
|
104
|
-
@rerender_all = true
|
105
91
|
elsif @searching_prompt
|
106
92
|
prompt = @searching_prompt
|
107
|
-
@rerender_all = true
|
108
93
|
else
|
109
94
|
prompt = @prompt
|
110
95
|
end
|
111
|
-
if simplified_rendering? && !force_recalc
|
112
|
-
mode_string = check_mode_string
|
113
|
-
prompt = mode_string + prompt if mode_string
|
114
|
-
return [prompt, calculate_width(prompt, true), [prompt] * buffer.size]
|
115
|
-
end
|
116
96
|
if @prompt_proc
|
117
|
-
|
118
|
-
if @cached_prompt_list
|
119
|
-
if @just_cursor_moving
|
120
|
-
use_cached_prompt_list = true
|
121
|
-
elsif Time.now.to_f < (@prompt_cache_time + PROMPT_LIST_CACHE_TIMEOUT) and buffer.size == @cached_prompt_list.size
|
122
|
-
use_cached_prompt_list = true
|
123
|
-
end
|
124
|
-
end
|
125
|
-
use_cached_prompt_list = false if @rerender_all
|
126
|
-
if use_cached_prompt_list
|
127
|
-
prompt_list = @cached_prompt_list
|
128
|
-
else
|
129
|
-
prompt_list = @cached_prompt_list = @prompt_proc.(buffer).map { |pr| pr.gsub("\n", "\\n") }
|
130
|
-
@prompt_cache_time = Time.now.to_f
|
131
|
-
end
|
97
|
+
prompt_list = @prompt_proc.(buffer).map { |pr| pr.gsub("\n", "\\n") }
|
132
98
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
133
99
|
prompt_list = [prompt] if prompt_list.empty?
|
134
|
-
mode_string = check_mode_string
|
135
100
|
prompt_list = prompt_list.map{ |pr| mode_string + pr } if mode_string
|
136
101
|
prompt = prompt_list[@line_index]
|
137
102
|
prompt = prompt_list[0] if prompt.nil?
|
@@ -141,21 +106,17 @@ class Reline::LineEditor
|
|
141
106
|
prompt_list << prompt_list.last
|
142
107
|
end
|
143
108
|
end
|
144
|
-
|
145
|
-
[prompt, prompt_width, prompt_list]
|
109
|
+
prompt_list
|
146
110
|
else
|
147
|
-
mode_string = check_mode_string
|
148
111
|
prompt = mode_string + prompt if mode_string
|
149
|
-
|
150
|
-
[prompt, prompt_width, nil]
|
112
|
+
[prompt] * buffer.size
|
151
113
|
end
|
152
114
|
end
|
153
115
|
|
154
116
|
def reset(prompt = '', encoding:)
|
155
|
-
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
156
117
|
@screen_size = Reline::IOGate.get_screen_size
|
157
|
-
@screen_height = @screen_size.first
|
158
118
|
reset_variables(prompt, encoding: encoding)
|
119
|
+
@rendered_screen.base_y = Reline::IOGate.cursor_pos.y
|
159
120
|
Reline::IOGate.set_winch_handler do
|
160
121
|
@resized = true
|
161
122
|
end
|
@@ -184,54 +145,25 @@ class Reline::LineEditor
|
|
184
145
|
|
185
146
|
def resize
|
186
147
|
return unless @resized
|
187
|
-
|
188
|
-
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
189
|
-
old_screen_size = @screen_size
|
148
|
+
|
190
149
|
@screen_size = Reline::IOGate.get_screen_size
|
191
|
-
@
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer)
|
199
|
-
new_buffer.each_with_index do |line, index|
|
200
|
-
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
201
|
-
width = prompt_width + calculate_width(line)
|
202
|
-
height = calculate_height_by_width(width)
|
203
|
-
back += height
|
204
|
-
end
|
205
|
-
@highest_in_all = back
|
206
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
207
|
-
@first_line_started_from =
|
208
|
-
if @line_index.zero?
|
209
|
-
0
|
210
|
-
else
|
211
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
212
|
-
end
|
213
|
-
if @prompt_proc
|
214
|
-
prompt = prompt_list[@line_index]
|
215
|
-
prompt_width = calculate_width(prompt, true)
|
216
|
-
end
|
217
|
-
calculate_nearest_cursor
|
218
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
219
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
220
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
221
|
-
@rerender_all = true
|
222
|
-
end
|
150
|
+
@resized = false
|
151
|
+
scroll_into_view
|
152
|
+
Reline::IOGate.move_cursor_up @rendered_screen.cursor_y
|
153
|
+
@rendered_screen.base_y = Reline::IOGate.cursor_pos.y
|
154
|
+
@rendered_screen.lines = []
|
155
|
+
@rendered_screen.cursor_y = 0
|
156
|
+
render_differential
|
223
157
|
end
|
224
158
|
|
225
159
|
def set_signal_handlers
|
226
160
|
@old_trap = Signal.trap('INT') {
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
Reline::IOGate.move_cursor_column(0)
|
234
|
-
scroll_down(1)
|
161
|
+
clear_dialogs
|
162
|
+
scrolldown = render_differential
|
163
|
+
Reline::IOGate.scroll_down scrolldown
|
164
|
+
Reline::IOGate.move_cursor_column 0
|
165
|
+
@rendered_screen.lines = []
|
166
|
+
@rendered_screen.cursor_y = 0
|
235
167
|
case @old_trap
|
236
168
|
when 'DEFAULT', 'SYSTEM_DEFAULT'
|
237
169
|
raise Interrupt
|
@@ -260,7 +192,6 @@ class Reline::LineEditor
|
|
260
192
|
@is_multiline = false
|
261
193
|
@finished = false
|
262
194
|
@cleared = false
|
263
|
-
@rerender_all = false
|
264
195
|
@history_pointer = nil
|
265
196
|
@kill_ring ||= Reline::KillRing.new
|
266
197
|
@vi_clipboard = ''
|
@@ -272,43 +203,29 @@ class Reline::LineEditor
|
|
272
203
|
@completion_state = CompletionState::NORMAL
|
273
204
|
@perfect_matched = nil
|
274
205
|
@menu_info = nil
|
275
|
-
@first_prompt = true
|
276
206
|
@searching_prompt = nil
|
277
207
|
@first_char = true
|
278
|
-
@
|
279
|
-
@just_cursor_moving = nil
|
280
|
-
@cached_prompt_list = nil
|
281
|
-
@prompt_cache_time = nil
|
208
|
+
@just_cursor_moving = false
|
282
209
|
@eof = false
|
283
210
|
@continuous_insertion_buffer = String.new(encoding: @encoding)
|
284
|
-
@scroll_partial_screen =
|
285
|
-
@prev_mode_string = nil
|
211
|
+
@scroll_partial_screen = 0
|
286
212
|
@drop_terminate_spaces = false
|
287
213
|
@in_pasting = false
|
288
214
|
@auto_indent_proc = nil
|
289
215
|
@dialogs = []
|
290
|
-
@previous_rendered_dialog_y = 0
|
291
|
-
@last_key = nil
|
292
216
|
@resized = false
|
217
|
+
@cache = {}
|
218
|
+
@rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0)
|
293
219
|
reset_line
|
294
220
|
end
|
295
221
|
|
296
222
|
def reset_line
|
297
|
-
@cursor = 0
|
298
|
-
@cursor_max = 0
|
299
223
|
@byte_pointer = 0
|
300
224
|
@buffer_of_lines = [String.new(encoding: @encoding)]
|
301
225
|
@line_index = 0
|
302
|
-
@
|
303
|
-
@line = @buffer_of_lines[0]
|
304
|
-
@first_line_started_from = 0
|
305
|
-
@move_up = 0
|
306
|
-
@started_from = 0
|
307
|
-
@highest_in_this = 1
|
308
|
-
@highest_in_all = 1
|
226
|
+
@cache.clear
|
309
227
|
@line_backup_in_history = nil
|
310
228
|
@multibyte_buffer = String.new(encoding: 'ASCII-8BIT')
|
311
|
-
@check_new_auto_indent = false
|
312
229
|
end
|
313
230
|
|
314
231
|
def multiline_on
|
@@ -319,68 +236,44 @@ class Reline::LineEditor
|
|
319
236
|
@is_multiline = false
|
320
237
|
end
|
321
238
|
|
322
|
-
private def calculate_height_by_lines(lines, prompt)
|
323
|
-
result = 0
|
324
|
-
prompt_list = prompt.is_a?(Array) ? prompt : nil
|
325
|
-
lines.each_with_index { |line, i|
|
326
|
-
prompt = prompt_list[i] if prompt_list and prompt_list[i]
|
327
|
-
result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
|
328
|
-
}
|
329
|
-
result
|
330
|
-
end
|
331
|
-
|
332
239
|
private def insert_new_line(cursor_line, next_line)
|
333
|
-
@line = cursor_line
|
334
240
|
@buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding))
|
335
|
-
@
|
241
|
+
@buffer_of_lines[@line_index] = cursor_line
|
336
242
|
@line_index += 1
|
337
|
-
@
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
243
|
+
@byte_pointer = 0
|
244
|
+
if @auto_indent_proc && !@in_pasting
|
245
|
+
if next_line.empty?
|
246
|
+
(
|
247
|
+
# For compatibility, use this calculation instead of just `process_auto_indent @line_index - 1, cursor_dependent: false`
|
248
|
+
indent1 = @auto_indent_proc.(@buffer_of_lines.take(@line_index - 1).push(''), @line_index - 1, 0, true)
|
249
|
+
indent2 = @auto_indent_proc.(@buffer_of_lines.take(@line_index), @line_index - 1, @buffer_of_lines[@line_index - 1].bytesize, false)
|
250
|
+
indent = indent2 || indent1
|
251
|
+
@buffer_of_lines[@line_index - 1] = ' ' * indent + @buffer_of_lines[@line_index - 1].gsub(/\A */, '')
|
252
|
+
)
|
253
|
+
process_auto_indent @line_index, add_newline: true
|
254
|
+
else
|
255
|
+
process_auto_indent @line_index - 1, cursor_dependent: false
|
256
|
+
process_auto_indent @line_index, add_newline: true # Need for compatibility
|
257
|
+
process_auto_indent @line_index, cursor_dependent: false
|
258
|
+
end
|
259
|
+
end
|
342
260
|
end
|
343
261
|
|
344
262
|
private def split_by_width(str, max_width)
|
345
263
|
Reline::Unicode.split_by_width(str, max_width, @encoding)
|
346
264
|
end
|
347
265
|
|
348
|
-
|
349
|
-
|
350
|
-
Reline::IOGate.move_cursor_down(val)
|
351
|
-
@rest_height -= val
|
352
|
-
else
|
353
|
-
Reline::IOGate.move_cursor_down(@rest_height)
|
354
|
-
Reline::IOGate.scroll_down(val - @rest_height)
|
355
|
-
@rest_height = 0
|
356
|
-
end
|
357
|
-
end
|
358
|
-
|
359
|
-
private def move_cursor_up(val)
|
360
|
-
if val > 0
|
361
|
-
Reline::IOGate.move_cursor_up(val)
|
362
|
-
@rest_height += val
|
363
|
-
elsif val < 0
|
364
|
-
move_cursor_down(-val)
|
365
|
-
end
|
366
|
-
end
|
367
|
-
|
368
|
-
private def move_cursor_down(val)
|
369
|
-
if val > 0
|
370
|
-
Reline::IOGate.move_cursor_down(val)
|
371
|
-
@rest_height -= val
|
372
|
-
@rest_height = 0 if @rest_height < 0
|
373
|
-
elsif val < 0
|
374
|
-
move_cursor_up(-val)
|
375
|
-
end
|
266
|
+
def current_byte_pointer_cursor
|
267
|
+
calculate_width(current_line.byteslice(0, @byte_pointer))
|
376
268
|
end
|
377
269
|
|
378
|
-
private def calculate_nearest_cursor(
|
270
|
+
private def calculate_nearest_cursor(cursor)
|
271
|
+
line_to_calc = current_line
|
379
272
|
new_cursor_max = calculate_width(line_to_calc)
|
380
273
|
new_cursor = 0
|
381
274
|
new_byte_pointer = 0
|
382
275
|
height = 1
|
383
|
-
max_width =
|
276
|
+
max_width = screen_width
|
384
277
|
if @config.editing_mode_is?(:vi_command)
|
385
278
|
last_byte_size = Reline::Unicode.get_prev_mbchar_size(line_to_calc, line_to_calc.bytesize)
|
386
279
|
if last_byte_size > 0
|
@@ -406,110 +299,241 @@ class Reline::LineEditor
|
|
406
299
|
end
|
407
300
|
new_byte_pointer += gc.bytesize
|
408
301
|
end
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
[new_cursor, new_cursor_max, new_started_from, new_byte_pointer]
|
302
|
+
@byte_pointer = new_byte_pointer
|
303
|
+
end
|
304
|
+
|
305
|
+
def with_cache(key, *deps)
|
306
|
+
cached_deps, value = @cache[key]
|
307
|
+
if cached_deps != deps
|
308
|
+
@cache[key] = [deps, value = yield(*deps, cached_deps, value)]
|
417
309
|
end
|
310
|
+
value
|
418
311
|
end
|
419
312
|
|
420
|
-
def
|
421
|
-
|
422
|
-
|
423
|
-
|
313
|
+
def modified_lines
|
314
|
+
with_cache(__method__, whole_lines, finished?) do |whole, complete|
|
315
|
+
modify_lines(whole, complete)
|
316
|
+
end
|
424
317
|
end
|
425
318
|
|
426
|
-
def
|
427
|
-
|
428
|
-
|
429
|
-
scroll_down(@highest_in_all - @first_line_started_from)
|
430
|
-
@rerender_all = true
|
319
|
+
def prompt_list
|
320
|
+
with_cache(__method__, whole_lines, check_mode_string, @vi_arg, @searching_prompt) do |lines, mode_string|
|
321
|
+
check_multiline_prompt(lines, mode_string)
|
431
322
|
end
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
323
|
+
end
|
324
|
+
|
325
|
+
def screen_height
|
326
|
+
@screen_size.first
|
327
|
+
end
|
328
|
+
|
329
|
+
def screen_width
|
330
|
+
@screen_size.last
|
331
|
+
end
|
332
|
+
|
333
|
+
def screen_scroll_top
|
334
|
+
@scroll_partial_screen
|
335
|
+
end
|
336
|
+
|
337
|
+
def wrapped_lines
|
338
|
+
with_cache(__method__, @buffer_of_lines.size, modified_lines, prompt_list, screen_width) do |n, lines, prompts, width, prev_cache_key, cached_value|
|
339
|
+
prev_n, prev_lines, prev_prompts, prev_width = prev_cache_key
|
340
|
+
cached_wraps = {}
|
341
|
+
if prev_width == width
|
342
|
+
prev_n.times do |i|
|
343
|
+
cached_wraps[[prev_prompts[i], prev_lines[i]]] = cached_value[i]
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
n.times.map do |i|
|
348
|
+
prompt = prompts[i]
|
349
|
+
line = lines[i]
|
350
|
+
cached_wraps[[prompt, line]] || split_by_width("#{prompt}#{line}", width).first.compact
|
351
|
+
end
|
442
352
|
end
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
|
450
|
-
modify_lines(new_lines).each_with_index do |line, index|
|
451
|
-
@output.write "#{prompt_list ? prompt_list[index] : prompt}#{line}\r\n"
|
452
|
-
Reline::IOGate.erase_after_cursor
|
453
|
-
end
|
454
|
-
@output.flush
|
455
|
-
clear_dialog(cursor_column)
|
456
|
-
return
|
353
|
+
end
|
354
|
+
|
355
|
+
def calculate_overlay_levels(overlay_levels)
|
356
|
+
levels = []
|
357
|
+
overlay_levels.each do |x, w, l|
|
358
|
+
levels.fill(l, x, w)
|
457
359
|
end
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
rerender_changed_current_line
|
473
|
-
@previous_line_index = nil
|
474
|
-
rendered = true
|
475
|
-
elsif @rerender_all
|
476
|
-
rerender_all_lines
|
477
|
-
@rerender_all = false
|
478
|
-
rendered = true
|
360
|
+
levels
|
361
|
+
end
|
362
|
+
|
363
|
+
def render_line_differential(old_items, new_items)
|
364
|
+
old_levels = calculate_overlay_levels(old_items.zip(new_items).each_with_index.map {|((x, w, c), (nx, _nw, nc)), i| [x, w, c == nc && x == nx ? i : -1] if x }.compact)
|
365
|
+
new_levels = calculate_overlay_levels(new_items.each_with_index.map { |(x, w), i| [x, w, i] if x }.compact).take(screen_width)
|
366
|
+
base_x = 0
|
367
|
+
new_levels.zip(old_levels).chunk { |n, o| n == o ? :skip : n || :blank }.each do |level, chunk|
|
368
|
+
width = chunk.size
|
369
|
+
if level == :skip
|
370
|
+
# do nothing
|
371
|
+
elsif level == :blank
|
372
|
+
Reline::IOGate.move_cursor_column base_x
|
373
|
+
@output.write "#{Reline::IOGate::RESET_COLOR}#{' ' * width}"
|
479
374
|
else
|
375
|
+
x, w, content = new_items[level]
|
376
|
+
content = Reline::Unicode.take_range(content, base_x - x, width) unless x == base_x && w == width
|
377
|
+
Reline::IOGate.move_cursor_column base_x
|
378
|
+
@output.write "#{Reline::IOGate::RESET_COLOR}#{content}#{Reline::IOGate::RESET_COLOR}"
|
480
379
|
end
|
380
|
+
base_x += width
|
481
381
|
end
|
482
|
-
if
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
382
|
+
if old_levels.size > new_levels.size
|
383
|
+
Reline::IOGate.move_cursor_column new_levels.size
|
384
|
+
Reline::IOGate.erase_after_cursor
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
# Calculate cursor position in word wrapped content.
|
389
|
+
def wrapped_cursor_position
|
390
|
+
prompt_width = calculate_width(prompt_list[@line_index], true)
|
391
|
+
line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer)
|
392
|
+
wrapped_line_before_cursor = split_by_width(' ' * prompt_width + line_before_cursor, screen_width).first.compact
|
393
|
+
wrapped_cursor_y = wrapped_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
|
394
|
+
wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last)
|
395
|
+
[wrapped_cursor_x, wrapped_cursor_y]
|
396
|
+
end
|
397
|
+
|
398
|
+
def clear_dialogs
|
399
|
+
@dialogs.each do |dialog|
|
400
|
+
dialog.contents = nil
|
401
|
+
dialog.trap_key = nil
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
def update_dialogs(key = nil)
|
406
|
+
wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
|
407
|
+
@dialogs.each do |dialog|
|
408
|
+
dialog.trap_key = nil
|
409
|
+
update_each_dialog(dialog, wrapped_cursor_x, wrapped_cursor_y - screen_scroll_top, key)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
def render_finished
|
414
|
+
clear_rendered_lines
|
415
|
+
render_full_content
|
416
|
+
end
|
417
|
+
|
418
|
+
def clear_rendered_lines
|
419
|
+
Reline::IOGate.move_cursor_up @rendered_screen.cursor_y
|
420
|
+
Reline::IOGate.move_cursor_column 0
|
421
|
+
|
422
|
+
num_lines = @rendered_screen.lines.size
|
423
|
+
return unless num_lines && num_lines >= 1
|
424
|
+
|
425
|
+
Reline::IOGate.move_cursor_down num_lines - 1
|
426
|
+
(num_lines - 1).times do
|
427
|
+
Reline::IOGate.erase_after_cursor
|
428
|
+
Reline::IOGate.move_cursor_up 1
|
429
|
+
end
|
430
|
+
Reline::IOGate.erase_after_cursor
|
431
|
+
@rendered_screen.lines = []
|
432
|
+
@rendered_screen.cursor_y = 0
|
433
|
+
end
|
434
|
+
|
435
|
+
def render_full_content
|
436
|
+
lines = @buffer_of_lines.size.times.map do |i|
|
437
|
+
line = prompt_list[i] + modified_lines[i]
|
438
|
+
wrapped_lines, = split_by_width(line, screen_width)
|
439
|
+
wrapped_lines.last.empty? ? "#{line} " : line
|
440
|
+
end
|
441
|
+
@output.puts lines.map { |l| "#{l}\r\n" }.join
|
442
|
+
end
|
443
|
+
|
444
|
+
def print_nomultiline_prompt(prompt)
|
445
|
+
return unless prompt && !@is_multiline
|
446
|
+
|
447
|
+
# Readline's test `TestRelineAsReadline#test_readline` requires first output to be prompt, not cursor reset escape sequence.
|
448
|
+
@rendered_screen.lines = [[[0, Reline::Unicode.calculate_width(prompt, true), prompt]]]
|
449
|
+
@rendered_screen.cursor_y = 0
|
450
|
+
@output.write prompt
|
451
|
+
end
|
452
|
+
|
453
|
+
def render_differential
|
454
|
+
wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
|
455
|
+
|
456
|
+
rendered_lines = @rendered_screen.lines
|
457
|
+
new_lines = wrapped_lines.flatten[screen_scroll_top, screen_height].map do |l|
|
458
|
+
[[0, Reline::Unicode.calculate_width(l, true), l]]
|
459
|
+
end
|
460
|
+
if @menu_info
|
461
|
+
@menu_info.list.sort!.each do |item|
|
462
|
+
new_lines << [[0, Reline::Unicode.calculate_width(item), item]]
|
501
463
|
end
|
502
|
-
@
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
464
|
+
@menu_info = nil # TODO: do not change state here
|
465
|
+
end
|
466
|
+
|
467
|
+
@dialogs.each_with_index do |dialog, index|
|
468
|
+
next unless dialog.contents
|
469
|
+
|
470
|
+
x_range, y_range = dialog_range dialog, wrapped_cursor_y - screen_scroll_top
|
471
|
+
y_range.each do |row|
|
472
|
+
next if row < 0 || row >= screen_height
|
473
|
+
dialog_rows = new_lines[row] ||= []
|
474
|
+
dialog_rows[index + 1] = [x_range.begin, dialog.width, dialog.contents[row - y_range.begin]]
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
cursor_y = @rendered_screen.cursor_y
|
479
|
+
if new_lines != rendered_lines
|
480
|
+
# Hide cursor while rendering to avoid cursor flickering.
|
481
|
+
Reline::IOGate.hide_cursor
|
482
|
+
num_lines = [[new_lines.size, rendered_lines.size].max, screen_height].min
|
483
|
+
if @rendered_screen.base_y + num_lines > screen_height
|
484
|
+
Reline::IOGate.scroll_down(num_lines - cursor_y - 1)
|
485
|
+
@rendered_screen.base_y = screen_height - num_lines
|
486
|
+
cursor_y = num_lines - 1
|
487
|
+
end
|
488
|
+
num_lines.times do |i|
|
489
|
+
rendered_line = rendered_lines[i] || []
|
490
|
+
line_to_render = new_lines[i] || []
|
491
|
+
next if rendered_line == line_to_render
|
492
|
+
|
493
|
+
Reline::IOGate.move_cursor_down i - cursor_y
|
494
|
+
cursor_y = i
|
495
|
+
unless rendered_lines[i]
|
496
|
+
Reline::IOGate.move_cursor_column 0
|
497
|
+
Reline::IOGate.erase_after_cursor
|
498
|
+
end
|
499
|
+
render_line_differential(rendered_line, line_to_render)
|
511
500
|
end
|
501
|
+
@rendered_screen.lines = new_lines
|
502
|
+
Reline::IOGate.show_cursor
|
512
503
|
end
|
504
|
+
y = wrapped_cursor_y - screen_scroll_top
|
505
|
+
Reline::IOGate.move_cursor_column wrapped_cursor_x
|
506
|
+
Reline::IOGate.move_cursor_down y - cursor_y
|
507
|
+
@rendered_screen.cursor_y = y
|
508
|
+
new_lines.size - y
|
509
|
+
end
|
510
|
+
|
511
|
+
def current_row
|
512
|
+
wrapped_lines.flatten[wrapped_cursor_y]
|
513
|
+
end
|
514
|
+
|
515
|
+
def upper_space_height(wrapped_cursor_y)
|
516
|
+
wrapped_cursor_y - screen_scroll_top
|
517
|
+
end
|
518
|
+
|
519
|
+
def rest_height(wrapped_cursor_y)
|
520
|
+
screen_height - wrapped_cursor_y + screen_scroll_top - @rendered_screen.base_y - 1
|
521
|
+
end
|
522
|
+
|
523
|
+
def handle_cleared
|
524
|
+
return unless @cleared
|
525
|
+
|
526
|
+
@cleared = false
|
527
|
+
Reline::IOGate.clear_screen
|
528
|
+
@screen_size = Reline::IOGate.get_screen_size
|
529
|
+
@rendered_screen.lines = []
|
530
|
+
@rendered_screen.base_y = 0
|
531
|
+
@rendered_screen.cursor_y = 0
|
532
|
+
end
|
533
|
+
|
534
|
+
def rerender
|
535
|
+
handle_cleared
|
536
|
+
render_differential unless @in_pasting
|
513
537
|
end
|
514
538
|
|
515
539
|
class DialogProcScope
|
@@ -563,17 +587,16 @@ class Reline::LineEditor
|
|
563
587
|
end
|
564
588
|
|
565
589
|
def screen_width
|
566
|
-
@line_editor.
|
590
|
+
@line_editor.screen_width
|
567
591
|
end
|
568
592
|
|
569
593
|
def screen_height
|
570
|
-
@line_editor.
|
594
|
+
@line_editor.screen_height
|
571
595
|
end
|
572
596
|
|
573
597
|
def preferred_dialog_height
|
574
|
-
|
575
|
-
|
576
|
-
[cursor_pos.y - scroll_partial_screen, rest_height, (screen_height + 6) / 5].max
|
598
|
+
_wrapped_cursor_x, wrapped_cursor_y = @line_editor.wrapped_cursor_position
|
599
|
+
[@line_editor.upper_space_height(wrapped_cursor_y), @line_editor.rest_height(wrapped_cursor_y), (screen_height + 6) / 5].max
|
577
600
|
end
|
578
601
|
|
579
602
|
def completion_journey_data
|
@@ -646,14 +669,6 @@ class Reline::LineEditor
|
|
646
669
|
end
|
647
670
|
|
648
671
|
DIALOG_DEFAULT_HEIGHT = 20
|
649
|
-
private def render_dialog(cursor_column)
|
650
|
-
changes = @dialogs.map do |dialog|
|
651
|
-
old_dialog = dialog.dup
|
652
|
-
update_each_dialog(dialog, cursor_column)
|
653
|
-
[old_dialog, dialog]
|
654
|
-
end
|
655
|
-
render_dialog_changes(changes, cursor_column)
|
656
|
-
end
|
657
672
|
|
658
673
|
private def padding_space_with_escape_sequences(str, width)
|
659
674
|
padding_width = width - calculate_width(str, true)
|
@@ -662,118 +677,15 @@ class Reline::LineEditor
|
|
662
677
|
str + (' ' * padding_width)
|
663
678
|
end
|
664
679
|
|
665
|
-
private def range_subtract(base_ranges, subtract_ranges)
|
666
|
-
indices = base_ranges.flat_map(&:to_a).uniq.sort - subtract_ranges.flat_map(&:to_a)
|
667
|
-
chunks = indices.chunk_while { |a, b| a + 1 == b }
|
668
|
-
chunks.map { |a| a.first...a.last + 1 }
|
669
|
-
end
|
670
|
-
|
671
680
|
private def dialog_range(dialog, dialog_y)
|
672
681
|
x_range = dialog.column...dialog.column + dialog.width
|
673
682
|
y_range = dialog_y + dialog.vertical_offset...dialog_y + dialog.vertical_offset + dialog.contents.size
|
674
683
|
[x_range, y_range]
|
675
684
|
end
|
676
685
|
|
677
|
-
private def
|
678
|
-
|
679
|
-
|
680
|
-
new_dialog_ranges = {}
|
681
|
-
new_dialog_contents = {}
|
682
|
-
changes.each do |old_dialog, new_dialog|
|
683
|
-
if old_dialog.contents
|
684
|
-
x_range, y_range = dialog_range(old_dialog, @previous_rendered_dialog_y)
|
685
|
-
y_range.each do |y|
|
686
|
-
(old_dialog_ranges[y] ||= []) << x_range
|
687
|
-
end
|
688
|
-
end
|
689
|
-
if new_dialog.contents
|
690
|
-
x_range, y_range = dialog_range(new_dialog, @first_line_started_from + @started_from)
|
691
|
-
y_range.each do |y|
|
692
|
-
(new_dialog_ranges[y] ||= []) << x_range
|
693
|
-
(new_dialog_contents[y] ||= []) << [x_range, new_dialog.contents[y - y_range.begin]]
|
694
|
-
end
|
695
|
-
end
|
696
|
-
end
|
697
|
-
return if old_dialog_ranges.empty? && new_dialog_ranges.empty?
|
698
|
-
|
699
|
-
# Calculate x-coordinate ranges to restore text that was hidden behind dialogs for each line
|
700
|
-
ranges_to_restore = {}
|
701
|
-
subtract_cache = {}
|
702
|
-
old_dialog_ranges.each do |y, old_x_ranges|
|
703
|
-
new_x_ranges = new_dialog_ranges[y] || []
|
704
|
-
ranges = subtract_cache[[old_x_ranges, new_x_ranges]] ||= range_subtract(old_x_ranges, new_x_ranges)
|
705
|
-
ranges_to_restore[y] = ranges if ranges.any?
|
706
|
-
end
|
707
|
-
|
708
|
-
# Create visual_lines for restoring text hidden behind dialogs
|
709
|
-
if ranges_to_restore.any?
|
710
|
-
lines = whole_lines
|
711
|
-
prompt, _prompt_width, prompt_list = check_multiline_prompt(lines, force_recalc: true)
|
712
|
-
modified_lines = modify_lines(lines, force_recalc: true)
|
713
|
-
visual_lines = []
|
714
|
-
modified_lines.each_with_index { |l, i|
|
715
|
-
pr = prompt_list ? prompt_list[i] : prompt
|
716
|
-
vl, = split_by_width(pr + l, @screen_size.last)
|
717
|
-
vl.compact!
|
718
|
-
visual_lines.concat(vl)
|
719
|
-
}
|
720
|
-
end
|
721
|
-
|
722
|
-
# Clear and rerender all dialogs line by line
|
723
|
-
Reline::IOGate.hide_cursor
|
724
|
-
ymin, ymax = (ranges_to_restore.keys + new_dialog_ranges.keys).minmax
|
725
|
-
scroll_partial_screen = @scroll_partial_screen || 0
|
726
|
-
screen_y_range = scroll_partial_screen..(scroll_partial_screen + @screen_height - 1)
|
727
|
-
ymin = ymin.clamp(screen_y_range.begin, screen_y_range.end)
|
728
|
-
ymax = ymax.clamp(screen_y_range.begin, screen_y_range.end)
|
729
|
-
dialog_y = @first_line_started_from + @started_from
|
730
|
-
cursor_y = dialog_y
|
731
|
-
if @highest_in_all <= ymax
|
732
|
-
scroll_down(ymax - cursor_y)
|
733
|
-
move_cursor_up(ymax - cursor_y)
|
734
|
-
end
|
735
|
-
(ymin..ymax).each do |y|
|
736
|
-
move_cursor_down(y - cursor_y)
|
737
|
-
cursor_y = y
|
738
|
-
new_x_ranges = new_dialog_ranges[y]
|
739
|
-
restore_ranges = ranges_to_restore[y]
|
740
|
-
# Restore text that was hidden behind dialogs
|
741
|
-
if restore_ranges
|
742
|
-
line = visual_lines[y] || ''
|
743
|
-
restore_ranges.each do |range|
|
744
|
-
col = range.begin
|
745
|
-
width = range.end - range.begin
|
746
|
-
s = padding_space_with_escape_sequences(Reline::Unicode.take_range(line, col, width), width)
|
747
|
-
Reline::IOGate.move_cursor_column(col)
|
748
|
-
@output.write "\e[0m#{s}\e[0m"
|
749
|
-
end
|
750
|
-
max_column = [calculate_width(line, true), new_x_ranges&.map(&:end)&.max || 0].max
|
751
|
-
if max_column < restore_ranges.map(&:end).max
|
752
|
-
Reline::IOGate.move_cursor_column(max_column)
|
753
|
-
Reline::IOGate.erase_after_cursor
|
754
|
-
end
|
755
|
-
end
|
756
|
-
# Render dialog contents
|
757
|
-
new_dialog_contents[y]&.each do |x_range, content|
|
758
|
-
Reline::IOGate.move_cursor_column(x_range.begin)
|
759
|
-
@output.write "\e[0m#{content}\e[0m"
|
760
|
-
end
|
761
|
-
end
|
762
|
-
move_cursor_up(cursor_y - dialog_y)
|
763
|
-
Reline::IOGate.move_cursor_column(cursor_column)
|
764
|
-
Reline::IOGate.show_cursor
|
765
|
-
|
766
|
-
@previous_rendered_dialog_y = dialog_y
|
767
|
-
end
|
768
|
-
|
769
|
-
private def update_each_dialog(dialog, cursor_column)
|
770
|
-
if @in_pasting
|
771
|
-
dialog.contents = nil
|
772
|
-
dialog.trap_key = nil
|
773
|
-
return
|
774
|
-
end
|
775
|
-
dialog.set_cursor_pos(cursor_column, @first_line_started_from + @started_from)
|
776
|
-
dialog_render_info = dialog.call(@last_key)
|
686
|
+
private def update_each_dialog(dialog, cursor_column, cursor_row, key = nil)
|
687
|
+
dialog.set_cursor_pos(cursor_column, cursor_row)
|
688
|
+
dialog_render_info = dialog.call(key)
|
777
689
|
if dialog_render_info.nil? or dialog_render_info.contents.nil? or dialog_render_info.contents.empty?
|
778
690
|
dialog.contents = nil
|
779
691
|
dialog.trap_key = nil
|
@@ -813,23 +725,22 @@ class Reline::LineEditor
|
|
813
725
|
else
|
814
726
|
scrollbar_pos = nil
|
815
727
|
end
|
816
|
-
upper_space = @first_line_started_from - @started_from
|
817
728
|
dialog.column = dialog_render_info.pos.x
|
818
729
|
dialog.width += @block_elem_width if scrollbar_pos
|
819
|
-
diff = (dialog.column + dialog.width) -
|
730
|
+
diff = (dialog.column + dialog.width) - screen_width
|
820
731
|
if diff > 0
|
821
732
|
dialog.column -= diff
|
822
733
|
end
|
823
|
-
if (
|
734
|
+
if rest_height(screen_scroll_top + cursor_row) - dialog_render_info.pos.y >= height
|
824
735
|
dialog.vertical_offset = dialog_render_info.pos.y + 1
|
825
|
-
elsif
|
736
|
+
elsif cursor_row >= height
|
826
737
|
dialog.vertical_offset = dialog_render_info.pos.y - height
|
827
738
|
else
|
828
739
|
dialog.vertical_offset = dialog_render_info.pos.y + 1
|
829
740
|
end
|
830
741
|
if dialog.column < 0
|
831
742
|
dialog.column = 0
|
832
|
-
dialog.width =
|
743
|
+
dialog.width = screen_width
|
833
744
|
end
|
834
745
|
face = Reline::Face[dialog_render_info.face || :default]
|
835
746
|
scrollbar_sgr = face[:scrollbar]
|
@@ -856,373 +767,14 @@ class Reline::LineEditor
|
|
856
767
|
end
|
857
768
|
end
|
858
769
|
|
859
|
-
private def
|
860
|
-
|
861
|
-
old_dialog = dialog.dup
|
862
|
-
dialog.contents = nil
|
863
|
-
[old_dialog, dialog]
|
864
|
-
end
|
865
|
-
render_dialog_changes(changes, cursor_column)
|
866
|
-
end
|
867
|
-
|
868
|
-
private def clear_dialog_with_trap_key(cursor_column)
|
869
|
-
clear_dialog(cursor_column)
|
870
|
-
@dialogs.each do |dialog|
|
871
|
-
dialog.trap_key = nil
|
872
|
-
end
|
873
|
-
end
|
874
|
-
|
875
|
-
private def calculate_scroll_partial_screen(highest_in_all, cursor_y)
|
876
|
-
if @screen_height < highest_in_all
|
877
|
-
old_scroll_partial_screen = @scroll_partial_screen
|
878
|
-
if cursor_y == 0
|
879
|
-
@scroll_partial_screen = 0
|
880
|
-
elsif cursor_y == (highest_in_all - 1)
|
881
|
-
@scroll_partial_screen = highest_in_all - @screen_height
|
882
|
-
else
|
883
|
-
if @scroll_partial_screen
|
884
|
-
if cursor_y <= @scroll_partial_screen
|
885
|
-
@scroll_partial_screen = cursor_y
|
886
|
-
elsif (@scroll_partial_screen + @screen_height - 1) < cursor_y
|
887
|
-
@scroll_partial_screen = cursor_y - (@screen_height - 1)
|
888
|
-
end
|
889
|
-
else
|
890
|
-
if cursor_y > (@screen_height - 1)
|
891
|
-
@scroll_partial_screen = cursor_y - (@screen_height - 1)
|
892
|
-
else
|
893
|
-
@scroll_partial_screen = 0
|
894
|
-
end
|
895
|
-
end
|
896
|
-
end
|
897
|
-
if @scroll_partial_screen != old_scroll_partial_screen
|
898
|
-
@rerender_all = true
|
899
|
-
end
|
900
|
-
else
|
901
|
-
if @scroll_partial_screen
|
902
|
-
@rerender_all = true
|
903
|
-
end
|
904
|
-
@scroll_partial_screen = nil
|
905
|
-
end
|
906
|
-
end
|
907
|
-
|
908
|
-
private def rerender_added_newline(prompt, prompt_width, prompt_list)
|
909
|
-
@buffer_of_lines[@previous_line_index] = @line
|
910
|
-
@line = @buffer_of_lines[@line_index]
|
911
|
-
@previous_line_index = nil
|
912
|
-
if @in_pasting
|
913
|
-
scroll_down(1)
|
914
|
-
else
|
915
|
-
lines = whole_lines
|
916
|
-
prev_line_prompt = @prompt_proc ? prompt_list[@line_index - 1] : prompt
|
917
|
-
prev_line_prompt_width = @prompt_proc ? calculate_width(prev_line_prompt, true) : prompt_width
|
918
|
-
prev_line = modify_lines(lines)[@line_index - 1]
|
919
|
-
move_cursor_up(@started_from)
|
920
|
-
render_partial(prev_line_prompt, prev_line_prompt_width, prev_line, @first_line_started_from + @started_from, with_control: false)
|
921
|
-
scroll_down(1)
|
922
|
-
render_partial(prompt, prompt_width, @line, @first_line_started_from + @started_from + 1, with_control: false)
|
923
|
-
end
|
924
|
-
@cursor = @cursor_max = calculate_width(@line)
|
925
|
-
@byte_pointer = @line.bytesize
|
926
|
-
@highest_in_all += @highest_in_this
|
927
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
928
|
-
@first_line_started_from += @started_from + 1
|
929
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
930
|
-
end
|
931
|
-
|
932
|
-
def just_move_cursor
|
933
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines)
|
934
|
-
move_cursor_up(@started_from)
|
935
|
-
new_first_line_started_from =
|
936
|
-
if @line_index.zero?
|
937
|
-
0
|
938
|
-
else
|
939
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
940
|
-
end
|
941
|
-
first_line_diff = new_first_line_started_from - @first_line_started_from
|
942
|
-
@cursor, @cursor_max, _, @byte_pointer = calculate_nearest_cursor(@buffer_of_lines[@line_index], @cursor, @started_from, @byte_pointer, false)
|
943
|
-
new_started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
944
|
-
calculate_scroll_partial_screen(@highest_in_all, new_first_line_started_from + new_started_from)
|
945
|
-
@previous_line_index = nil
|
946
|
-
@line = @buffer_of_lines[@line_index]
|
947
|
-
if @rerender_all
|
948
|
-
rerender_all_lines
|
949
|
-
@rerender_all = false
|
950
|
-
true
|
951
|
-
else
|
952
|
-
@first_line_started_from = new_first_line_started_from
|
953
|
-
@started_from = new_started_from
|
954
|
-
move_cursor_down(first_line_diff + @started_from)
|
955
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
956
|
-
false
|
957
|
-
end
|
958
|
-
end
|
959
|
-
|
960
|
-
private def rerender_changed_current_line
|
961
|
-
new_lines = whole_lines
|
962
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines)
|
963
|
-
all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
|
964
|
-
diff = all_height - @highest_in_all
|
965
|
-
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
966
|
-
if diff > 0
|
967
|
-
scroll_down(diff)
|
968
|
-
move_cursor_up(all_height - 1)
|
969
|
-
elsif diff < 0
|
970
|
-
(-diff).times do
|
971
|
-
Reline::IOGate.move_cursor_column(0)
|
972
|
-
Reline::IOGate.erase_after_cursor
|
973
|
-
move_cursor_up(1)
|
974
|
-
end
|
975
|
-
move_cursor_up(all_height - 1)
|
976
|
-
else
|
977
|
-
move_cursor_up(all_height - 1)
|
978
|
-
end
|
979
|
-
@highest_in_all = all_height
|
980
|
-
back = render_whole_lines(new_lines, prompt_list || prompt, prompt_width)
|
981
|
-
move_cursor_up(back)
|
982
|
-
if @previous_line_index
|
983
|
-
@buffer_of_lines[@previous_line_index] = @line
|
984
|
-
@line = @buffer_of_lines[@line_index]
|
985
|
-
end
|
986
|
-
@first_line_started_from =
|
987
|
-
if @line_index.zero?
|
988
|
-
0
|
989
|
-
else
|
990
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
991
|
-
end
|
992
|
-
if @prompt_proc
|
993
|
-
prompt = prompt_list[@line_index]
|
994
|
-
prompt_width = calculate_width(prompt, true)
|
995
|
-
end
|
996
|
-
move_cursor_down(@first_line_started_from)
|
997
|
-
calculate_nearest_cursor
|
998
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
999
|
-
move_cursor_down(@started_from)
|
1000
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
1001
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
1002
|
-
end
|
1003
|
-
|
1004
|
-
private def rerender_all_lines
|
1005
|
-
move_cursor_up(@first_line_started_from + @started_from)
|
1006
|
-
Reline::IOGate.move_cursor_column(0)
|
1007
|
-
back = 0
|
1008
|
-
new_buffer = whole_lines
|
1009
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer)
|
1010
|
-
new_buffer.each_with_index do |line, index|
|
1011
|
-
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
1012
|
-
width = prompt_width + calculate_width(line)
|
1013
|
-
height = calculate_height_by_width(width)
|
1014
|
-
back += height
|
1015
|
-
end
|
1016
|
-
old_highest_in_all = @highest_in_all
|
1017
|
-
if @line_index.zero?
|
1018
|
-
new_first_line_started_from = 0
|
1019
|
-
else
|
1020
|
-
new_first_line_started_from = calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
1021
|
-
end
|
1022
|
-
new_started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
1023
|
-
calculate_scroll_partial_screen(back, new_first_line_started_from + new_started_from)
|
1024
|
-
if @scroll_partial_screen
|
1025
|
-
move_cursor_up(@first_line_started_from + @started_from)
|
1026
|
-
scroll_down(@screen_height - 1)
|
1027
|
-
move_cursor_up(@screen_height)
|
1028
|
-
Reline::IOGate.move_cursor_column(0)
|
1029
|
-
elsif back > old_highest_in_all
|
1030
|
-
scroll_down(back - 1)
|
1031
|
-
move_cursor_up(back - 1)
|
1032
|
-
elsif back < old_highest_in_all
|
1033
|
-
scroll_down(back)
|
1034
|
-
Reline::IOGate.erase_after_cursor
|
1035
|
-
(old_highest_in_all - back - 1).times do
|
1036
|
-
scroll_down(1)
|
1037
|
-
Reline::IOGate.erase_after_cursor
|
1038
|
-
end
|
1039
|
-
move_cursor_up(old_highest_in_all - 1)
|
1040
|
-
end
|
1041
|
-
render_whole_lines(new_buffer, prompt_list || prompt, prompt_width)
|
1042
|
-
if @prompt_proc
|
1043
|
-
prompt = prompt_list[@line_index]
|
1044
|
-
prompt_width = calculate_width(prompt, true)
|
1045
|
-
end
|
1046
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
1047
|
-
@highest_in_all = back
|
1048
|
-
@first_line_started_from = new_first_line_started_from
|
1049
|
-
@started_from = new_started_from
|
1050
|
-
if @scroll_partial_screen
|
1051
|
-
Reline::IOGate.move_cursor_up(@screen_height - (@first_line_started_from + @started_from - @scroll_partial_screen) - 1)
|
1052
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
1053
|
-
else
|
1054
|
-
move_cursor_down(@first_line_started_from + @started_from - back + 1)
|
1055
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
1056
|
-
end
|
1057
|
-
end
|
1058
|
-
|
1059
|
-
private def render_whole_lines(lines, prompt, prompt_width)
|
1060
|
-
rendered_height = 0
|
1061
|
-
modify_lines(lines).each_with_index do |line, index|
|
1062
|
-
if prompt.is_a?(Array)
|
1063
|
-
line_prompt = prompt[index]
|
1064
|
-
prompt_width = calculate_width(line_prompt, true)
|
1065
|
-
else
|
1066
|
-
line_prompt = prompt
|
1067
|
-
end
|
1068
|
-
height = render_partial(line_prompt, prompt_width, line, rendered_height, with_control: false)
|
1069
|
-
if index < (lines.size - 1)
|
1070
|
-
if @scroll_partial_screen
|
1071
|
-
if (@scroll_partial_screen - height) < rendered_height and (@scroll_partial_screen + @screen_height - 1) >= (rendered_height + height)
|
1072
|
-
move_cursor_down(1)
|
1073
|
-
end
|
1074
|
-
else
|
1075
|
-
scroll_down(1)
|
1076
|
-
end
|
1077
|
-
rendered_height += height
|
1078
|
-
else
|
1079
|
-
rendered_height += height - 1
|
1080
|
-
end
|
1081
|
-
end
|
1082
|
-
rendered_height
|
1083
|
-
end
|
1084
|
-
|
1085
|
-
private def render_partial(prompt, prompt_width, line_to_render, this_started_from, with_control: true)
|
1086
|
-
visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
|
1087
|
-
cursor_up_from_last_line = 0
|
1088
|
-
if @scroll_partial_screen
|
1089
|
-
last_visual_line = this_started_from + (height - 1)
|
1090
|
-
last_screen_line = @scroll_partial_screen + (@screen_height - 1)
|
1091
|
-
if (@scroll_partial_screen - this_started_from) >= height
|
1092
|
-
# Render nothing because this line is before the screen.
|
1093
|
-
visual_lines = []
|
1094
|
-
elsif this_started_from > last_screen_line
|
1095
|
-
# Render nothing because this line is after the screen.
|
1096
|
-
visual_lines = []
|
1097
|
-
else
|
1098
|
-
deleted_lines_before_screen = []
|
1099
|
-
if @scroll_partial_screen > this_started_from and last_visual_line >= @scroll_partial_screen
|
1100
|
-
# A part of visual lines are before the screen.
|
1101
|
-
deleted_lines_before_screen = visual_lines.shift((@scroll_partial_screen - this_started_from) * 2)
|
1102
|
-
deleted_lines_before_screen.compact!
|
1103
|
-
end
|
1104
|
-
if this_started_from <= last_screen_line and last_screen_line < last_visual_line
|
1105
|
-
# A part of visual lines are after the screen.
|
1106
|
-
visual_lines.pop((last_visual_line - last_screen_line) * 2)
|
1107
|
-
end
|
1108
|
-
move_cursor_up(deleted_lines_before_screen.size - @started_from)
|
1109
|
-
cursor_up_from_last_line = @started_from - deleted_lines_before_screen.size
|
1110
|
-
end
|
1111
|
-
end
|
1112
|
-
if with_control
|
1113
|
-
if height > @highest_in_this
|
1114
|
-
diff = height - @highest_in_this
|
1115
|
-
scroll_down(diff)
|
1116
|
-
@highest_in_all += diff
|
1117
|
-
@highest_in_this = height
|
1118
|
-
move_cursor_up(diff)
|
1119
|
-
elsif height < @highest_in_this
|
1120
|
-
diff = @highest_in_this - height
|
1121
|
-
@highest_in_all -= diff
|
1122
|
-
@highest_in_this = height
|
1123
|
-
end
|
1124
|
-
move_cursor_up(@started_from)
|
1125
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
1126
|
-
cursor_up_from_last_line = height - 1 - @started_from
|
1127
|
-
end
|
1128
|
-
if Reline::Unicode::CSI_REGEXP.match?(prompt + line_to_render)
|
1129
|
-
@output.write "\e[0m" # clear character decorations
|
1130
|
-
end
|
1131
|
-
visual_lines.each_with_index do |line, index|
|
1132
|
-
Reline::IOGate.move_cursor_column(0)
|
1133
|
-
if line.nil?
|
1134
|
-
if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
1135
|
-
# reaches the end of line
|
1136
|
-
if Reline::IOGate.win? and Reline::IOGate.win_legacy_console?
|
1137
|
-
# A newline is automatically inserted if a character is rendered at
|
1138
|
-
# eol on command prompt.
|
1139
|
-
else
|
1140
|
-
# When the cursor is at the end of the line and erases characters
|
1141
|
-
# after the cursor, some terminals delete the character at the
|
1142
|
-
# cursor position.
|
1143
|
-
move_cursor_down(1)
|
1144
|
-
Reline::IOGate.move_cursor_column(0)
|
1145
|
-
end
|
1146
|
-
else
|
1147
|
-
Reline::IOGate.erase_after_cursor
|
1148
|
-
move_cursor_down(1)
|
1149
|
-
Reline::IOGate.move_cursor_column(0)
|
1150
|
-
end
|
1151
|
-
next
|
1152
|
-
end
|
1153
|
-
@output.write line
|
1154
|
-
if Reline::IOGate.win? and Reline::IOGate.win_legacy_console? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last
|
1155
|
-
# A newline is automatically inserted if a character is rendered at eol on command prompt.
|
1156
|
-
@rest_height -= 1 if @rest_height > 0
|
1157
|
-
end
|
1158
|
-
@output.flush
|
1159
|
-
if @first_prompt
|
1160
|
-
@first_prompt = false
|
1161
|
-
@pre_input_hook&.call
|
1162
|
-
end
|
1163
|
-
end
|
1164
|
-
unless visual_lines.empty?
|
1165
|
-
Reline::IOGate.erase_after_cursor
|
1166
|
-
Reline::IOGate.move_cursor_column(0)
|
1167
|
-
end
|
1168
|
-
if with_control
|
1169
|
-
# Just after rendring, so the cursor is on the last line.
|
1170
|
-
if finished?
|
1171
|
-
Reline::IOGate.move_cursor_column(0)
|
1172
|
-
else
|
1173
|
-
# Moves up from bottom of lines to the cursor position.
|
1174
|
-
move_cursor_up(cursor_up_from_last_line)
|
1175
|
-
# This logic is buggy if a fullwidth char is wrapped because there is only one halfwidth at end of a line.
|
1176
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
1177
|
-
end
|
1178
|
-
end
|
1179
|
-
height
|
1180
|
-
end
|
1181
|
-
|
1182
|
-
private def modify_lines(before, force_recalc: false)
|
1183
|
-
return before if !force_recalc && (before.nil? || before.empty? || simplified_rendering?)
|
1184
|
-
|
1185
|
-
if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: finished?)
|
770
|
+
private def modify_lines(before, complete)
|
771
|
+
if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: complete)
|
1186
772
|
after.lines("\n").map { |l| l.chomp('') }
|
1187
773
|
else
|
1188
774
|
before
|
1189
775
|
end
|
1190
776
|
end
|
1191
777
|
|
1192
|
-
private def show_menu
|
1193
|
-
scroll_down(@highest_in_all - @first_line_started_from)
|
1194
|
-
@rerender_all = true
|
1195
|
-
@menu_info.list.sort!.each do |item|
|
1196
|
-
Reline::IOGate.move_cursor_column(0)
|
1197
|
-
@output.write item
|
1198
|
-
@output.flush
|
1199
|
-
scroll_down(1)
|
1200
|
-
end
|
1201
|
-
scroll_down(@highest_in_all - 1)
|
1202
|
-
move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
|
1203
|
-
end
|
1204
|
-
|
1205
|
-
private def clear_screen_buffer(prompt, prompt_list, prompt_width)
|
1206
|
-
Reline::IOGate.clear_screen
|
1207
|
-
back = 0
|
1208
|
-
modify_lines(whole_lines).each_with_index do |line, index|
|
1209
|
-
if @prompt_proc
|
1210
|
-
pr = prompt_list[index]
|
1211
|
-
height = render_partial(pr, calculate_width(pr), line, back, with_control: false)
|
1212
|
-
else
|
1213
|
-
height = render_partial(prompt, prompt_width, line, back, with_control: false)
|
1214
|
-
end
|
1215
|
-
if index < (@buffer_of_lines.size - 1)
|
1216
|
-
move_cursor_down(1)
|
1217
|
-
back += height
|
1218
|
-
end
|
1219
|
-
end
|
1220
|
-
move_cursor_up(back)
|
1221
|
-
move_cursor_down(@first_line_started_from + @started_from)
|
1222
|
-
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
1223
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
1224
|
-
end
|
1225
|
-
|
1226
778
|
def editing_mode
|
1227
779
|
@config.editing_mode
|
1228
780
|
end
|
@@ -1256,7 +808,7 @@ class Reline::LineEditor
|
|
1256
808
|
item_mbchars = item.grapheme_clusters
|
1257
809
|
end
|
1258
810
|
size = [memo_mbchars.size, item_mbchars.size].min
|
1259
|
-
result = ''
|
811
|
+
result = +''
|
1260
812
|
size.times do |i|
|
1261
813
|
if @config.completion_ignore_case
|
1262
814
|
if memo_mbchars[i].casecmp?(item_mbchars[i])
|
@@ -1312,10 +864,8 @@ class Reline::LineEditor
|
|
1312
864
|
@completion_state = CompletionState::MENU
|
1313
865
|
end
|
1314
866
|
if not just_show_list and target < completed
|
1315
|
-
@
|
867
|
+
@buffer_of_lines[@line_index] = (preposing + completed + completion_append_character.to_s + postposing).split("\n")[@line_index] || String.new(encoding: @encoding)
|
1316
868
|
line_to_pointer = (preposing + completed + completion_append_character.to_s).split("\n")[@line_index] || String.new(encoding: @encoding)
|
1317
|
-
@cursor_max = calculate_width(@line)
|
1318
|
-
@cursor = calculate_width(line_to_pointer)
|
1319
869
|
@byte_pointer = line_to_pointer.bytesize
|
1320
870
|
end
|
1321
871
|
end
|
@@ -1358,35 +908,31 @@ class Reline::LineEditor
|
|
1358
908
|
end
|
1359
909
|
end
|
1360
910
|
completed = @completion_journey_data.list[@completion_journey_data.pointer]
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
line_to_pointer = String.new(encoding: @encoding) if line_to_pointer.nil?
|
1365
|
-
@cursor_max = calculate_width(@line)
|
1366
|
-
@cursor = calculate_width(line_to_pointer)
|
1367
|
-
@byte_pointer = line_to_pointer.bytesize
|
911
|
+
line_to_pointer = (@completion_journey_data.preposing + completed).split("\n")[@line_index] || String.new(encoding: @encoding)
|
912
|
+
new_line = line_to_pointer + (@completion_journey_data.postposing.split("\n").first || '')
|
913
|
+
set_current_line(new_line, line_to_pointer.bytesize)
|
1368
914
|
end
|
1369
915
|
|
1370
916
|
private def run_for_operators(key, method_symbol, &block)
|
1371
917
|
if @waiting_operator_proc
|
1372
918
|
if VI_MOTIONS.include?(method_symbol)
|
1373
|
-
|
919
|
+
old_byte_pointer = @byte_pointer
|
1374
920
|
@vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg&.> 1
|
1375
921
|
block.(true)
|
1376
922
|
unless @waiting_proc
|
1377
|
-
|
1378
|
-
@
|
1379
|
-
@waiting_operator_proc.(
|
923
|
+
byte_pointer_diff = @byte_pointer - old_byte_pointer
|
924
|
+
@byte_pointer = old_byte_pointer
|
925
|
+
@waiting_operator_proc.(byte_pointer_diff)
|
1380
926
|
else
|
1381
927
|
old_waiting_proc = @waiting_proc
|
1382
928
|
old_waiting_operator_proc = @waiting_operator_proc
|
1383
929
|
current_waiting_operator_proc = @waiting_operator_proc
|
1384
930
|
@waiting_proc = proc { |k|
|
1385
|
-
|
931
|
+
old_byte_pointer = @byte_pointer
|
1386
932
|
old_waiting_proc.(k)
|
1387
|
-
|
1388
|
-
@
|
1389
|
-
current_waiting_operator_proc.(
|
933
|
+
byte_pointer_diff = @byte_pointer - old_byte_pointer
|
934
|
+
@byte_pointer = old_byte_pointer
|
935
|
+
current_waiting_operator_proc.(byte_pointer_diff)
|
1390
936
|
@waiting_operator_proc = old_waiting_operator_proc
|
1391
937
|
}
|
1392
938
|
end
|
@@ -1397,7 +943,6 @@ class Reline::LineEditor
|
|
1397
943
|
@waiting_operator_proc = nil
|
1398
944
|
@waiting_operator_vi_arg = nil
|
1399
945
|
if @vi_arg
|
1400
|
-
@rerender_all = true
|
1401
946
|
@vi_arg = nil
|
1402
947
|
end
|
1403
948
|
else
|
@@ -1451,7 +996,6 @@ class Reline::LineEditor
|
|
1451
996
|
end
|
1452
997
|
@kill_ring.process
|
1453
998
|
if @vi_arg
|
1454
|
-
@rerender_al = true
|
1455
999
|
@vi_arg = nil
|
1456
1000
|
end
|
1457
1001
|
elsif @vi_arg
|
@@ -1471,7 +1015,6 @@ class Reline::LineEditor
|
|
1471
1015
|
end
|
1472
1016
|
@kill_ring.process
|
1473
1017
|
if @vi_arg
|
1474
|
-
@rerender_all = true
|
1475
1018
|
@vi_arg = nil
|
1476
1019
|
end
|
1477
1020
|
end
|
@@ -1493,7 +1036,6 @@ class Reline::LineEditor
|
|
1493
1036
|
end
|
1494
1037
|
|
1495
1038
|
private def normal_char(key)
|
1496
|
-
method_symbol = method_obj = nil
|
1497
1039
|
if key.combined_char.is_a?(Symbol)
|
1498
1040
|
process_key(key.combined_char, key.combined_char)
|
1499
1041
|
return
|
@@ -1523,32 +1065,37 @@ class Reline::LineEditor
|
|
1523
1065
|
end
|
1524
1066
|
@multibyte_buffer.clear
|
1525
1067
|
end
|
1526
|
-
if @config.editing_mode_is?(:vi_command) and @
|
1527
|
-
byte_size = Reline::Unicode.get_prev_mbchar_size(@
|
1068
|
+
if @config.editing_mode_is?(:vi_command) and @byte_pointer > 0 and @byte_pointer == current_line.bytesize
|
1069
|
+
byte_size = Reline::Unicode.get_prev_mbchar_size(@buffer_of_lines[@line_index], @byte_pointer)
|
1528
1070
|
@byte_pointer -= byte_size
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1071
|
+
end
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
def update(key)
|
1075
|
+
modified = input_key(key)
|
1076
|
+
unless @in_pasting
|
1077
|
+
scroll_into_view
|
1078
|
+
@just_cursor_moving = !modified
|
1079
|
+
update_dialogs(key)
|
1080
|
+
@just_cursor_moving = false
|
1532
1081
|
end
|
1533
1082
|
end
|
1534
1083
|
|
1535
1084
|
def input_key(key)
|
1536
|
-
@last_key = key
|
1537
1085
|
@config.reset_oneshot_key_bindings
|
1538
1086
|
@dialogs.each do |dialog|
|
1539
1087
|
if key.char.instance_of?(Symbol) and key.char == dialog.name
|
1540
1088
|
return
|
1541
1089
|
end
|
1542
1090
|
end
|
1543
|
-
@just_cursor_moving = nil
|
1544
1091
|
if key.char.nil?
|
1545
1092
|
if @first_char
|
1546
|
-
@
|
1093
|
+
@eof = true
|
1547
1094
|
end
|
1548
1095
|
finish
|
1549
1096
|
return
|
1550
1097
|
end
|
1551
|
-
|
1098
|
+
old_lines = @buffer_of_lines.dup
|
1552
1099
|
@first_char = false
|
1553
1100
|
completion_occurs = false
|
1554
1101
|
if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
|
@@ -1591,19 +1138,20 @@ class Reline::LineEditor
|
|
1591
1138
|
@completion_state = CompletionState::NORMAL
|
1592
1139
|
@completion_journey_data = nil
|
1593
1140
|
end
|
1594
|
-
if
|
1595
|
-
|
1596
|
-
@just_cursor_moving = true
|
1597
|
-
elsif @previous_line_index.nil? and @buffer_of_lines[@line_index] == @line and old_line == @line
|
1598
|
-
@just_cursor_moving = true
|
1599
|
-
else
|
1600
|
-
@just_cursor_moving = false
|
1601
|
-
end
|
1141
|
+
if @in_pasting
|
1142
|
+
clear_dialogs
|
1602
1143
|
else
|
1603
|
-
|
1144
|
+
return old_lines != @buffer_of_lines
|
1145
|
+
end
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
def scroll_into_view
|
1149
|
+
_wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
|
1150
|
+
if wrapped_cursor_y < screen_scroll_top
|
1151
|
+
@scroll_partial_screen = wrapped_cursor_y
|
1604
1152
|
end
|
1605
|
-
if
|
1606
|
-
|
1153
|
+
if wrapped_cursor_y >= screen_scroll_top + screen_height
|
1154
|
+
@scroll_partial_screen = wrapped_cursor_y - screen_height + 1
|
1607
1155
|
end
|
1608
1156
|
end
|
1609
1157
|
|
@@ -1637,43 +1185,39 @@ class Reline::LineEditor
|
|
1637
1185
|
result
|
1638
1186
|
end
|
1639
1187
|
|
1640
|
-
private def process_auto_indent
|
1641
|
-
return if
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
|
1653
|
-
|
1654
|
-
|
1655
|
-
|
1656
|
-
|
1657
|
-
|
1658
|
-
|
1659
|
-
|
1660
|
-
|
1661
|
-
|
1662
|
-
|
1663
|
-
|
1664
|
-
|
1665
|
-
|
1666
|
-
|
1667
|
-
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1671
|
-
@cursor += new_indent - prev_indent
|
1672
|
-
@cursor_max = calculate_width(@line)
|
1673
|
-
@byte_pointer += new_indent - prev_indent
|
1674
|
-
end
|
1188
|
+
private def process_auto_indent(line_index = @line_index, cursor_dependent: true, add_newline: false)
|
1189
|
+
return if @in_pasting
|
1190
|
+
return unless @auto_indent_proc
|
1191
|
+
|
1192
|
+
line = @buffer_of_lines[line_index]
|
1193
|
+
byte_pointer = cursor_dependent && @line_index == line_index ? @byte_pointer : line.bytesize
|
1194
|
+
new_indent = @auto_indent_proc.(@buffer_of_lines.take(line_index + 1).push(''), line_index, byte_pointer, add_newline)
|
1195
|
+
return unless new_indent
|
1196
|
+
|
1197
|
+
@buffer_of_lines[line_index] = ' ' * new_indent + line.lstrip
|
1198
|
+
if @line_index == line_index
|
1199
|
+
old_indent = line[/\A */].size
|
1200
|
+
@byte_pointer = [@byte_pointer + new_indent - old_indent, 0].max
|
1201
|
+
end
|
1202
|
+
end
|
1203
|
+
|
1204
|
+
def line()
|
1205
|
+
current_line unless eof?
|
1206
|
+
end
|
1207
|
+
|
1208
|
+
def current_line
|
1209
|
+
@buffer_of_lines[@line_index]
|
1210
|
+
end
|
1211
|
+
|
1212
|
+
def set_current_line(line, byte_pointer = nil)
|
1213
|
+
cursor = current_byte_pointer_cursor
|
1214
|
+
@buffer_of_lines[@line_index] = line
|
1215
|
+
if byte_pointer
|
1216
|
+
@byte_pointer = byte_pointer
|
1217
|
+
else
|
1218
|
+
calculate_nearest_cursor(cursor)
|
1675
1219
|
end
|
1676
|
-
|
1220
|
+
process_auto_indent
|
1677
1221
|
end
|
1678
1222
|
|
1679
1223
|
def retrieve_completion_block(set_completion_quote_character = false)
|
@@ -1687,7 +1231,7 @@ class Reline::LineEditor
|
|
1687
1231
|
else
|
1688
1232
|
quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
|
1689
1233
|
end
|
1690
|
-
before =
|
1234
|
+
before = current_line.byteslice(0, @byte_pointer)
|
1691
1235
|
rest = nil
|
1692
1236
|
break_pointer = nil
|
1693
1237
|
quote = nil
|
@@ -1695,7 +1239,7 @@ class Reline::LineEditor
|
|
1695
1239
|
escaped_quote = nil
|
1696
1240
|
i = 0
|
1697
1241
|
while i < @byte_pointer do
|
1698
|
-
slice =
|
1242
|
+
slice = current_line.byteslice(i, @byte_pointer - i)
|
1699
1243
|
unless slice.valid_encoding?
|
1700
1244
|
i += 1
|
1701
1245
|
next
|
@@ -1717,15 +1261,15 @@ class Reline::LineEditor
|
|
1717
1261
|
elsif word_break_regexp and not quote and slice =~ word_break_regexp
|
1718
1262
|
rest = $'
|
1719
1263
|
i += 1
|
1720
|
-
before =
|
1264
|
+
before = current_line.byteslice(i, @byte_pointer - i)
|
1721
1265
|
break_pointer = i
|
1722
1266
|
else
|
1723
1267
|
i += 1
|
1724
1268
|
end
|
1725
1269
|
end
|
1726
|
-
postposing =
|
1270
|
+
postposing = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
|
1727
1271
|
if rest
|
1728
|
-
preposing =
|
1272
|
+
preposing = current_line.byteslice(0, break_pointer)
|
1729
1273
|
target = rest
|
1730
1274
|
if set_completion_quote_character and quote
|
1731
1275
|
Reline.core.instance_variable_set(:@completion_quote_character, quote)
|
@@ -1736,7 +1280,7 @@ class Reline::LineEditor
|
|
1736
1280
|
else
|
1737
1281
|
preposing = ''
|
1738
1282
|
if break_pointer
|
1739
|
-
preposing =
|
1283
|
+
preposing = current_line.byteslice(0, break_pointer)
|
1740
1284
|
else
|
1741
1285
|
preposing = ''
|
1742
1286
|
end
|
@@ -1756,106 +1300,67 @@ class Reline::LineEditor
|
|
1756
1300
|
|
1757
1301
|
def confirm_multiline_termination
|
1758
1302
|
temp_buffer = @buffer_of_lines.dup
|
1759
|
-
if @previous_line_index and @line_index == (@buffer_of_lines.size - 1)
|
1760
|
-
temp_buffer[@previous_line_index] = @line
|
1761
|
-
else
|
1762
|
-
temp_buffer[@line_index] = @line
|
1763
|
-
end
|
1764
1303
|
@confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n")
|
1765
1304
|
end
|
1766
1305
|
|
1767
1306
|
def insert_text(text)
|
1768
|
-
|
1769
|
-
|
1770
|
-
@line += text
|
1307
|
+
if @buffer_of_lines[@line_index].bytesize == @byte_pointer
|
1308
|
+
@buffer_of_lines[@line_index] += text
|
1771
1309
|
else
|
1772
|
-
@
|
1310
|
+
@buffer_of_lines[@line_index] = byteinsert(@buffer_of_lines[@line_index], @byte_pointer, text)
|
1773
1311
|
end
|
1774
1312
|
@byte_pointer += text.bytesize
|
1775
|
-
|
1776
|
-
@cursor_max += width
|
1313
|
+
process_auto_indent
|
1777
1314
|
end
|
1778
1315
|
|
1779
1316
|
def delete_text(start = nil, length = nil)
|
1780
1317
|
if start.nil? and length.nil?
|
1781
1318
|
if @is_multiline
|
1782
1319
|
if @buffer_of_lines.size == 1
|
1783
|
-
@
|
1320
|
+
@buffer_of_lines[@line_index] = ''
|
1784
1321
|
@byte_pointer = 0
|
1785
|
-
@cursor = 0
|
1786
|
-
@cursor_max = 0
|
1787
1322
|
elsif @line_index == (@buffer_of_lines.size - 1) and @line_index > 0
|
1788
1323
|
@buffer_of_lines.pop
|
1789
1324
|
@line_index -= 1
|
1790
|
-
@line = @buffer_of_lines[@line_index]
|
1791
1325
|
@byte_pointer = 0
|
1792
|
-
@cursor = 0
|
1793
|
-
@cursor_max = calculate_width(@line)
|
1794
1326
|
elsif @line_index < (@buffer_of_lines.size - 1)
|
1795
1327
|
@buffer_of_lines.delete_at(@line_index)
|
1796
|
-
@line = @buffer_of_lines[@line_index]
|
1797
1328
|
@byte_pointer = 0
|
1798
|
-
@cursor = 0
|
1799
|
-
@cursor_max = calculate_width(@line)
|
1800
1329
|
end
|
1801
1330
|
else
|
1802
|
-
|
1803
|
-
@byte_pointer = 0
|
1804
|
-
@cursor = 0
|
1805
|
-
@cursor_max = 0
|
1331
|
+
set_current_line('', 0)
|
1806
1332
|
end
|
1807
1333
|
elsif not start.nil? and not length.nil?
|
1808
|
-
if
|
1809
|
-
before =
|
1810
|
-
after =
|
1811
|
-
|
1812
|
-
@byte_pointer = @line.bytesize if @byte_pointer > @line.bytesize
|
1813
|
-
str = @line.byteslice(0, @byte_pointer)
|
1814
|
-
@cursor = calculate_width(str)
|
1815
|
-
@cursor_max = calculate_width(@line)
|
1334
|
+
if current_line
|
1335
|
+
before = current_line.byteslice(0, start)
|
1336
|
+
after = current_line.byteslice(start + length, current_line.bytesize)
|
1337
|
+
set_current_line(before + after)
|
1816
1338
|
end
|
1817
1339
|
elsif start.is_a?(Range)
|
1818
1340
|
range = start
|
1819
1341
|
first = range.first
|
1820
1342
|
last = range.last
|
1821
|
-
last =
|
1822
|
-
last +=
|
1823
|
-
first +=
|
1343
|
+
last = current_line.bytesize - 1 if last > current_line.bytesize
|
1344
|
+
last += current_line.bytesize if last < 0
|
1345
|
+
first += current_line.bytesize if first < 0
|
1824
1346
|
range = range.exclude_end? ? first...last : first..last
|
1825
|
-
|
1826
|
-
|
1827
|
-
str = @line.byteslice(0, @byte_pointer)
|
1828
|
-
@cursor = calculate_width(str)
|
1829
|
-
@cursor_max = calculate_width(@line)
|
1347
|
+
line = current_line.bytes.reject.with_index{ |c, i| range.include?(i) }.map{ |c| c.chr(Encoding::ASCII_8BIT) }.join.force_encoding(@encoding)
|
1348
|
+
set_current_line(line)
|
1830
1349
|
else
|
1831
|
-
|
1832
|
-
@byte_pointer = @line.bytesize if @byte_pointer > @line.bytesize
|
1833
|
-
str = @line.byteslice(0, @byte_pointer)
|
1834
|
-
@cursor = calculate_width(str)
|
1835
|
-
@cursor_max = calculate_width(@line)
|
1350
|
+
set_current_line(current_line.byteslice(0, start))
|
1836
1351
|
end
|
1837
1352
|
end
|
1838
1353
|
|
1839
1354
|
def byte_pointer=(val)
|
1840
1355
|
@byte_pointer = val
|
1841
|
-
str = @line.byteslice(0, @byte_pointer)
|
1842
|
-
@cursor = calculate_width(str)
|
1843
|
-
@cursor_max = calculate_width(@line)
|
1844
1356
|
end
|
1845
1357
|
|
1846
1358
|
def whole_lines
|
1847
|
-
|
1848
|
-
temp_lines = @buffer_of_lines.dup
|
1849
|
-
temp_lines[index] = @line
|
1850
|
-
temp_lines
|
1359
|
+
@buffer_of_lines.dup
|
1851
1360
|
end
|
1852
1361
|
|
1853
1362
|
def whole_buffer
|
1854
|
-
|
1855
|
-
nil
|
1856
|
-
else
|
1857
|
-
whole_lines.join("\n")
|
1858
|
-
end
|
1363
|
+
whole_lines.join("\n")
|
1859
1364
|
end
|
1860
1365
|
|
1861
1366
|
def finished?
|
@@ -1864,7 +1369,6 @@ class Reline::LineEditor
|
|
1864
1369
|
|
1865
1370
|
def finish
|
1866
1371
|
@finished = true
|
1867
|
-
@rerender_all = true
|
1868
1372
|
@config.reset
|
1869
1373
|
end
|
1870
1374
|
|
@@ -1895,14 +1399,9 @@ class Reline::LineEditor
|
|
1895
1399
|
|
1896
1400
|
private def key_newline(key)
|
1897
1401
|
if @is_multiline
|
1898
|
-
|
1899
|
-
|
1900
|
-
end
|
1901
|
-
next_line = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
|
1902
|
-
cursor_line = @line.byteslice(0, @byte_pointer)
|
1402
|
+
next_line = current_line.byteslice(@byte_pointer, current_line.bytesize - @byte_pointer)
|
1403
|
+
cursor_line = current_line.byteslice(0, @byte_pointer)
|
1903
1404
|
insert_new_line(cursor_line, next_line)
|
1904
|
-
@cursor = 0
|
1905
|
-
@check_new_auto_indent = true unless @in_pasting
|
1906
1405
|
end
|
1907
1406
|
end
|
1908
1407
|
|
@@ -1912,16 +1411,7 @@ class Reline::LineEditor
|
|
1912
1411
|
|
1913
1412
|
private def process_insert(force: false)
|
1914
1413
|
return if @continuous_insertion_buffer.empty? or (@in_pasting and not force)
|
1915
|
-
|
1916
|
-
bytesize = @continuous_insertion_buffer.bytesize
|
1917
|
-
if @cursor == @cursor_max
|
1918
|
-
@line += @continuous_insertion_buffer
|
1919
|
-
else
|
1920
|
-
@line = byteinsert(@line, @byte_pointer, @continuous_insertion_buffer)
|
1921
|
-
end
|
1922
|
-
@byte_pointer += bytesize
|
1923
|
-
@cursor += width
|
1924
|
-
@cursor_max += width
|
1414
|
+
insert_text(@continuous_insertion_buffer)
|
1925
1415
|
@continuous_insertion_buffer.clear
|
1926
1416
|
end
|
1927
1417
|
|
@@ -1939,9 +1429,6 @@ class Reline::LineEditor
|
|
1939
1429
|
# million.
|
1940
1430
|
# GNU Readline:: +self-insert+ (a, b, A, 1, !, …) Insert yourself.
|
1941
1431
|
private def ed_insert(key)
|
1942
|
-
str = nil
|
1943
|
-
width = nil
|
1944
|
-
bytesize = nil
|
1945
1432
|
if key.instance_of?(String)
|
1946
1433
|
begin
|
1947
1434
|
key.encode(Encoding::UTF_8)
|
@@ -1949,7 +1436,6 @@ class Reline::LineEditor
|
|
1949
1436
|
return
|
1950
1437
|
end
|
1951
1438
|
str = key
|
1952
|
-
bytesize = key.bytesize
|
1953
1439
|
else
|
1954
1440
|
begin
|
1955
1441
|
key.chr.encode(Encoding::UTF_8)
|
@@ -1957,7 +1443,6 @@ class Reline::LineEditor
|
|
1957
1443
|
return
|
1958
1444
|
end
|
1959
1445
|
str = key.chr
|
1960
|
-
bytesize = 1
|
1961
1446
|
end
|
1962
1447
|
if @in_pasting
|
1963
1448
|
@continuous_insertion_buffer << str
|
@@ -1965,28 +1450,8 @@ class Reline::LineEditor
|
|
1965
1450
|
elsif not @continuous_insertion_buffer.empty?
|
1966
1451
|
process_insert
|
1967
1452
|
end
|
1968
|
-
|
1969
|
-
|
1970
|
-
@line += str
|
1971
|
-
else
|
1972
|
-
@line = byteinsert(@line, @byte_pointer, str)
|
1973
|
-
end
|
1974
|
-
last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
1975
|
-
@byte_pointer += bytesize
|
1976
|
-
last_mbchar = @line.byteslice((@byte_pointer - bytesize - last_byte_size), last_byte_size)
|
1977
|
-
combined_char = last_mbchar + str
|
1978
|
-
if last_byte_size != 0 and combined_char.grapheme_clusters.size == 1
|
1979
|
-
# combined char
|
1980
|
-
last_mbchar_width = Reline::Unicode.get_mbchar_width(last_mbchar)
|
1981
|
-
combined_char_width = Reline::Unicode.get_mbchar_width(combined_char)
|
1982
|
-
if combined_char_width > last_mbchar_width
|
1983
|
-
width = combined_char_width - last_mbchar_width
|
1984
|
-
else
|
1985
|
-
width = 0
|
1986
|
-
end
|
1987
|
-
end
|
1988
|
-
@cursor += width
|
1989
|
-
@cursor_max += width
|
1453
|
+
|
1454
|
+
insert_text(str)
|
1990
1455
|
end
|
1991
1456
|
alias_method :ed_digit, :ed_insert
|
1992
1457
|
alias_method :self_insert, :ed_insert
|
@@ -2008,18 +1473,11 @@ class Reline::LineEditor
|
|
2008
1473
|
alias_method :quoted_insert, :ed_quoted_insert
|
2009
1474
|
|
2010
1475
|
private def ed_next_char(key, arg: 1)
|
2011
|
-
byte_size = Reline::Unicode.get_next_mbchar_size(
|
2012
|
-
if (@byte_pointer <
|
2013
|
-
mbchar = @line.byteslice(@byte_pointer, byte_size)
|
2014
|
-
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2015
|
-
@cursor += width if width
|
1476
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
1477
|
+
if (@byte_pointer < current_line.bytesize)
|
2016
1478
|
@byte_pointer += byte_size
|
2017
|
-
elsif @is_multiline and @config.editing_mode_is?(:emacs) and @byte_pointer ==
|
2018
|
-
next_line = @buffer_of_lines[@line_index + 1]
|
2019
|
-
@cursor = 0
|
1479
|
+
elsif @is_multiline and @config.editing_mode_is?(:emacs) and @byte_pointer == current_line.bytesize and @line_index < @buffer_of_lines.size - 1
|
2020
1480
|
@byte_pointer = 0
|
2021
|
-
@cursor_max = calculate_width(next_line)
|
2022
|
-
@previous_line_index = @line_index
|
2023
1481
|
@line_index += 1
|
2024
1482
|
end
|
2025
1483
|
arg -= 1
|
@@ -2028,19 +1486,12 @@ class Reline::LineEditor
|
|
2028
1486
|
alias_method :forward_char, :ed_next_char
|
2029
1487
|
|
2030
1488
|
private def ed_prev_char(key, arg: 1)
|
2031
|
-
if @
|
2032
|
-
byte_size = Reline::Unicode.get_prev_mbchar_size(
|
1489
|
+
if @byte_pointer > 0
|
1490
|
+
byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer)
|
2033
1491
|
@byte_pointer -= byte_size
|
2034
|
-
mbchar = @line.byteslice(@byte_pointer, byte_size)
|
2035
|
-
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2036
|
-
@cursor -= width
|
2037
1492
|
elsif @is_multiline and @config.editing_mode_is?(:emacs) and @byte_pointer == 0 and @line_index > 0
|
2038
|
-
prev_line = @buffer_of_lines[@line_index - 1]
|
2039
|
-
@cursor = calculate_width(prev_line)
|
2040
|
-
@byte_pointer = prev_line.bytesize
|
2041
|
-
@cursor_max = calculate_width(prev_line)
|
2042
|
-
@previous_line_index = @line_index
|
2043
1493
|
@line_index -= 1
|
1494
|
+
@byte_pointer = current_line.bytesize
|
2044
1495
|
end
|
2045
1496
|
arg -= 1
|
2046
1497
|
ed_prev_char(key, arg: arg) if arg > 0
|
@@ -2048,24 +1499,18 @@ class Reline::LineEditor
|
|
2048
1499
|
alias_method :backward_char, :ed_prev_char
|
2049
1500
|
|
2050
1501
|
private def vi_first_print(key)
|
2051
|
-
@byte_pointer,
|
1502
|
+
@byte_pointer, = Reline::Unicode.vi_first_print(current_line)
|
2052
1503
|
end
|
2053
1504
|
|
2054
1505
|
private def ed_move_to_beg(key)
|
2055
|
-
@byte_pointer =
|
1506
|
+
@byte_pointer = 0
|
2056
1507
|
end
|
2057
1508
|
alias_method :beginning_of_line, :ed_move_to_beg
|
2058
1509
|
|
2059
1510
|
private def ed_move_to_end(key)
|
2060
1511
|
@byte_pointer = 0
|
2061
|
-
@
|
2062
|
-
|
2063
|
-
while @byte_pointer < @line.bytesize
|
2064
|
-
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2065
|
-
if byte_size > 0
|
2066
|
-
mbchar = @line.byteslice(@byte_pointer, byte_size)
|
2067
|
-
@cursor += Reline::Unicode.get_mbchar_width(mbchar)
|
2068
|
-
end
|
1512
|
+
while @byte_pointer < current_line.bytesize
|
1513
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
2069
1514
|
@byte_pointer += byte_size
|
2070
1515
|
end
|
2071
1516
|
end
|
@@ -2167,19 +1612,16 @@ class Reline::LineEditor
|
|
2167
1612
|
@buffer_of_lines = hit.split("\n")
|
2168
1613
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2169
1614
|
@line_index = @buffer_of_lines.size - 1
|
2170
|
-
@
|
2171
|
-
@byte_pointer = @line.bytesize
|
2172
|
-
@cursor = @cursor_max = calculate_width(@line)
|
2173
|
-
@rerender_all = true
|
1615
|
+
@byte_pointer = current_line.bytesize
|
2174
1616
|
@searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
|
2175
1617
|
else
|
2176
|
-
@
|
1618
|
+
@buffer_of_lines = [hit]
|
1619
|
+
@byte_pointer = hit.bytesize
|
2177
1620
|
@searching_prompt = "(%s)`%s': %s" % [prompt_name, search_word, hit]
|
2178
1621
|
end
|
2179
1622
|
last_hit = hit
|
2180
1623
|
else
|
2181
1624
|
if @is_multiline
|
2182
|
-
@rerender_all = true
|
2183
1625
|
@searching_prompt = "(failed %s)`%s'" % [prompt_name, search_word]
|
2184
1626
|
else
|
2185
1627
|
@searching_prompt = "(failed %s)`%s': %s" % [prompt_name, search_word, last_hit]
|
@@ -2194,7 +1636,7 @@ class Reline::LineEditor
|
|
2194
1636
|
if @is_multiline
|
2195
1637
|
@line_backup_in_history = whole_buffer
|
2196
1638
|
else
|
2197
|
-
@line_backup_in_history =
|
1639
|
+
@line_backup_in_history = current_line
|
2198
1640
|
end
|
2199
1641
|
end
|
2200
1642
|
searcher = generate_searcher
|
@@ -2214,35 +1656,26 @@ class Reline::LineEditor
|
|
2214
1656
|
@buffer_of_lines = buffer.split("\n")
|
2215
1657
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2216
1658
|
@line_index = @buffer_of_lines.size - 1
|
2217
|
-
@line = @buffer_of_lines.last
|
2218
|
-
@rerender_all = true
|
2219
1659
|
else
|
2220
|
-
@
|
1660
|
+
@buffer_of_lines = [buffer]
|
2221
1661
|
end
|
2222
1662
|
@searching_prompt = nil
|
2223
1663
|
@waiting_proc = nil
|
2224
|
-
@
|
2225
|
-
@cursor = @byte_pointer = 0
|
2226
|
-
@rerender_all = true
|
2227
|
-
@cached_prompt_list = nil
|
1664
|
+
@byte_pointer = 0
|
2228
1665
|
searcher.resume(-1)
|
2229
1666
|
when "\C-g".ord
|
2230
1667
|
if @is_multiline
|
2231
1668
|
@buffer_of_lines = @line_backup_in_history.split("\n")
|
2232
1669
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2233
1670
|
@line_index = @buffer_of_lines.size - 1
|
2234
|
-
@line = @buffer_of_lines.last
|
2235
|
-
@rerender_all = true
|
2236
1671
|
else
|
2237
|
-
@
|
1672
|
+
@buffer_of_lines = [@line_backup_in_history]
|
2238
1673
|
end
|
2239
1674
|
@history_pointer = nil
|
2240
1675
|
@searching_prompt = nil
|
2241
1676
|
@waiting_proc = nil
|
2242
1677
|
@line_backup_in_history = nil
|
2243
|
-
@
|
2244
|
-
@cursor = @byte_pointer = 0
|
2245
|
-
@rerender_all = true
|
1678
|
+
@byte_pointer = 0
|
2246
1679
|
else
|
2247
1680
|
chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
|
2248
1681
|
if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == "\C-?".ord or k == "\C-r".ord or k == "\C-s".ord
|
@@ -2258,18 +1691,13 @@ class Reline::LineEditor
|
|
2258
1691
|
@buffer_of_lines = line.split("\n")
|
2259
1692
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2260
1693
|
@line_index = @buffer_of_lines.size - 1
|
2261
|
-
@line = @buffer_of_lines.last
|
2262
|
-
@rerender_all = true
|
2263
1694
|
else
|
2264
|
-
@line_backup_in_history =
|
2265
|
-
@
|
1695
|
+
@line_backup_in_history = current_line
|
1696
|
+
@buffer_of_lines = [line]
|
2266
1697
|
end
|
2267
1698
|
@searching_prompt = nil
|
2268
1699
|
@waiting_proc = nil
|
2269
|
-
@
|
2270
|
-
@cursor = @byte_pointer = 0
|
2271
|
-
@rerender_all = true
|
2272
|
-
@cached_prompt_list = nil
|
1700
|
+
@byte_pointer = 0
|
2273
1701
|
searcher.resume(-1)
|
2274
1702
|
end
|
2275
1703
|
end
|
@@ -2290,9 +1718,9 @@ class Reline::LineEditor
|
|
2290
1718
|
history = nil
|
2291
1719
|
h_pointer = nil
|
2292
1720
|
line_no = nil
|
2293
|
-
substr =
|
1721
|
+
substr = current_line.slice(0, @byte_pointer)
|
2294
1722
|
if @history_pointer.nil?
|
2295
|
-
return if not
|
1723
|
+
return if not current_line.empty? and substr.empty?
|
2296
1724
|
history = Reline::HISTORY
|
2297
1725
|
elsif @history_pointer.zero?
|
2298
1726
|
history = nil
|
@@ -2318,23 +1746,23 @@ class Reline::LineEditor
|
|
2318
1746
|
end
|
2319
1747
|
return if h_pointer.nil?
|
2320
1748
|
@history_pointer = h_pointer
|
1749
|
+
cursor = current_byte_pointer_cursor
|
2321
1750
|
if @is_multiline
|
2322
1751
|
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
2323
1752
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2324
1753
|
@line_index = line_no
|
2325
|
-
|
2326
|
-
@rerender_all = true
|
1754
|
+
calculate_nearest_cursor(cursor)
|
2327
1755
|
else
|
2328
|
-
@
|
1756
|
+
@buffer_of_lines = [Reline::HISTORY[@history_pointer]]
|
1757
|
+
calculate_nearest_cursor(cursor)
|
2329
1758
|
end
|
2330
|
-
@cursor_max = calculate_width(@line)
|
2331
1759
|
arg -= 1
|
2332
1760
|
ed_search_prev_history(key, arg: arg) if arg > 0
|
2333
1761
|
end
|
2334
1762
|
alias_method :history_search_backward, :ed_search_prev_history
|
2335
1763
|
|
2336
1764
|
private def ed_search_next_history(key, arg: 1)
|
2337
|
-
substr =
|
1765
|
+
substr = current_line.slice(0, @byte_pointer)
|
2338
1766
|
if @history_pointer.nil?
|
2339
1767
|
return
|
2340
1768
|
elsif @history_pointer == (Reline::HISTORY.size - 1) and not substr.empty?
|
@@ -2365,21 +1793,21 @@ class Reline::LineEditor
|
|
2365
1793
|
if @history_pointer.nil? and substr.empty?
|
2366
1794
|
@buffer_of_lines = []
|
2367
1795
|
@line_index = 0
|
1796
|
+
@byte_pointer = 0
|
2368
1797
|
else
|
1798
|
+
cursor = current_byte_pointer_cursor
|
2369
1799
|
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
2370
1800
|
@line_index = line_no
|
1801
|
+
calculate_nearest_cursor(cursor)
|
2371
1802
|
end
|
2372
1803
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2373
|
-
@line = @buffer_of_lines[@line_index]
|
2374
|
-
@rerender_all = true
|
2375
1804
|
else
|
2376
1805
|
if @history_pointer.nil? and substr.empty?
|
2377
|
-
|
1806
|
+
set_current_line('', 0)
|
2378
1807
|
else
|
2379
|
-
|
1808
|
+
set_current_line(Reline::HISTORY[@history_pointer])
|
2380
1809
|
end
|
2381
1810
|
end
|
2382
|
-
@cursor_max = calculate_width(@line)
|
2383
1811
|
arg -= 1
|
2384
1812
|
ed_search_next_history(key, arg: arg) if arg > 0
|
2385
1813
|
end
|
@@ -2387,8 +1815,9 @@ class Reline::LineEditor
|
|
2387
1815
|
|
2388
1816
|
private def ed_prev_history(key, arg: 1)
|
2389
1817
|
if @is_multiline and @line_index > 0
|
2390
|
-
|
1818
|
+
cursor = current_byte_pointer_cursor
|
2391
1819
|
@line_index -= 1
|
1820
|
+
calculate_nearest_cursor(cursor)
|
2392
1821
|
return
|
2393
1822
|
end
|
2394
1823
|
if Reline::HISTORY.empty?
|
@@ -2396,16 +1825,17 @@ class Reline::LineEditor
|
|
2396
1825
|
end
|
2397
1826
|
if @history_pointer.nil?
|
2398
1827
|
@history_pointer = Reline::HISTORY.size - 1
|
1828
|
+
cursor = current_byte_pointer_cursor
|
2399
1829
|
if @is_multiline
|
2400
1830
|
@line_backup_in_history = whole_buffer
|
2401
1831
|
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
2402
1832
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2403
1833
|
@line_index = @buffer_of_lines.size - 1
|
2404
|
-
|
2405
|
-
@rerender_all = true
|
1834
|
+
calculate_nearest_cursor(cursor)
|
2406
1835
|
else
|
2407
|
-
@line_backup_in_history =
|
2408
|
-
@
|
1836
|
+
@line_backup_in_history = whole_buffer
|
1837
|
+
@buffer_of_lines = [Reline::HISTORY[@history_pointer]]
|
1838
|
+
calculate_nearest_cursor(cursor)
|
2409
1839
|
end
|
2410
1840
|
elsif @history_pointer.zero?
|
2411
1841
|
return
|
@@ -2416,20 +1846,16 @@ class Reline::LineEditor
|
|
2416
1846
|
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
2417
1847
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2418
1848
|
@line_index = @buffer_of_lines.size - 1
|
2419
|
-
@line = @buffer_of_lines.last
|
2420
|
-
@rerender_all = true
|
2421
1849
|
else
|
2422
|
-
Reline::HISTORY[@history_pointer] =
|
1850
|
+
Reline::HISTORY[@history_pointer] = whole_buffer
|
2423
1851
|
@history_pointer -= 1
|
2424
|
-
@
|
1852
|
+
@buffer_of_lines = [Reline::HISTORY[@history_pointer]]
|
2425
1853
|
end
|
2426
1854
|
end
|
2427
1855
|
if @config.editing_mode_is?(:emacs, :vi_insert)
|
2428
|
-
@
|
2429
|
-
@byte_pointer = @line.bytesize
|
1856
|
+
@byte_pointer = current_line.bytesize
|
2430
1857
|
elsif @config.editing_mode_is?(:vi_command)
|
2431
|
-
@byte_pointer =
|
2432
|
-
@cursor_max = calculate_width(@line)
|
1858
|
+
@byte_pointer = 0
|
2433
1859
|
end
|
2434
1860
|
arg -= 1
|
2435
1861
|
ed_prev_history(key, arg: arg) if arg > 0
|
@@ -2438,8 +1864,9 @@ class Reline::LineEditor
|
|
2438
1864
|
|
2439
1865
|
private def ed_next_history(key, arg: 1)
|
2440
1866
|
if @is_multiline and @line_index < (@buffer_of_lines.size - 1)
|
2441
|
-
|
1867
|
+
cursor = current_byte_pointer_cursor
|
2442
1868
|
@line_index += 1
|
1869
|
+
calculate_nearest_cursor(cursor)
|
2443
1870
|
return
|
2444
1871
|
end
|
2445
1872
|
if @history_pointer.nil?
|
@@ -2450,11 +1877,9 @@ class Reline::LineEditor
|
|
2450
1877
|
@buffer_of_lines = @line_backup_in_history.split("\n")
|
2451
1878
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2452
1879
|
@line_index = 0
|
2453
|
-
@line = @buffer_of_lines.first
|
2454
|
-
@rerender_all = true
|
2455
1880
|
else
|
2456
1881
|
@history_pointer = nil
|
2457
|
-
@
|
1882
|
+
@buffer_of_lines = [@line_backup_in_history]
|
2458
1883
|
end
|
2459
1884
|
else
|
2460
1885
|
if @is_multiline
|
@@ -2463,21 +1888,16 @@ class Reline::LineEditor
|
|
2463
1888
|
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
2464
1889
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2465
1890
|
@line_index = 0
|
2466
|
-
@line = @buffer_of_lines.first
|
2467
|
-
@rerender_all = true
|
2468
1891
|
else
|
2469
|
-
Reline::HISTORY[@history_pointer] =
|
1892
|
+
Reline::HISTORY[@history_pointer] = whole_buffer
|
2470
1893
|
@history_pointer += 1
|
2471
|
-
@
|
1894
|
+
@buffer_of_lines = [Reline::HISTORY[@history_pointer]]
|
2472
1895
|
end
|
2473
1896
|
end
|
2474
|
-
@line = '' unless @line
|
2475
1897
|
if @config.editing_mode_is?(:emacs, :vi_insert)
|
2476
|
-
@
|
2477
|
-
@byte_pointer = @line.bytesize
|
1898
|
+
@byte_pointer = current_line.bytesize
|
2478
1899
|
elsif @config.editing_mode_is?(:vi_command)
|
2479
|
-
@byte_pointer =
|
2480
|
-
@cursor_max = calculate_width(@line)
|
1900
|
+
@byte_pointer = 0
|
2481
1901
|
end
|
2482
1902
|
arg -= 1
|
2483
1903
|
ed_next_history(key, arg: arg) if arg > 0
|
@@ -2503,14 +1923,14 @@ class Reline::LineEditor
|
|
2503
1923
|
end
|
2504
1924
|
else
|
2505
1925
|
# should check confirm_multiline_termination to finish?
|
2506
|
-
@previous_line_index = @line_index
|
2507
1926
|
@line_index = @buffer_of_lines.size - 1
|
1927
|
+
@byte_pointer = current_line.bytesize
|
2508
1928
|
finish
|
2509
1929
|
end
|
2510
1930
|
end
|
2511
1931
|
else
|
2512
1932
|
if @history_pointer
|
2513
|
-
Reline::HISTORY[@history_pointer] =
|
1933
|
+
Reline::HISTORY[@history_pointer] = whole_buffer
|
2514
1934
|
@history_pointer = nil
|
2515
1935
|
end
|
2516
1936
|
finish
|
@@ -2518,25 +1938,18 @@ class Reline::LineEditor
|
|
2518
1938
|
end
|
2519
1939
|
|
2520
1940
|
private def em_delete_prev_char(key, arg: 1)
|
2521
|
-
|
2522
|
-
@
|
2523
|
-
|
2524
|
-
|
2525
|
-
|
2526
|
-
@
|
2527
|
-
|
2528
|
-
|
2529
|
-
|
2530
|
-
|
2531
|
-
byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
2532
|
-
@byte_pointer -= byte_size
|
2533
|
-
@line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
|
2534
|
-
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2535
|
-
@cursor -= width
|
2536
|
-
@cursor_max -= width
|
1941
|
+
arg.times do
|
1942
|
+
if @is_multiline and @byte_pointer == 0 and @line_index > 0
|
1943
|
+
@byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
|
1944
|
+
@buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
|
1945
|
+
@line_index -= 1
|
1946
|
+
elsif @byte_pointer > 0
|
1947
|
+
byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer)
|
1948
|
+
line, = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
|
1949
|
+
set_current_line(line, @byte_pointer - byte_size)
|
1950
|
+
end
|
2537
1951
|
end
|
2538
|
-
|
2539
|
-
em_delete_prev_char(key, arg: arg) if arg > 0
|
1952
|
+
process_auto_indent
|
2540
1953
|
end
|
2541
1954
|
alias_method :backward_delete_char, :em_delete_prev_char
|
2542
1955
|
|
@@ -2546,19 +1959,12 @@ class Reline::LineEditor
|
|
2546
1959
|
# the line. With a negative numeric argument, kill backward
|
2547
1960
|
# from the cursor to the beginning of the current line.
|
2548
1961
|
private def ed_kill_line(key)
|
2549
|
-
if
|
2550
|
-
|
2551
|
-
|
2552
|
-
@cursor = @cursor_max = calculate_width(@line)
|
1962
|
+
if current_line.bytesize > @byte_pointer
|
1963
|
+
line, deleted = byteslice!(current_line, @byte_pointer, current_line.bytesize - @byte_pointer)
|
1964
|
+
set_current_line(line, line.bytesize)
|
2553
1965
|
@kill_ring.append(deleted)
|
2554
|
-
elsif @is_multiline and @byte_pointer ==
|
2555
|
-
|
2556
|
-
@byte_pointer = @line.bytesize
|
2557
|
-
@line += @buffer_of_lines.delete_at(@line_index + 1)
|
2558
|
-
@cursor_max = calculate_width(@line)
|
2559
|
-
@buffer_of_lines[@line_index] = @line
|
2560
|
-
@rerender_all = true
|
2561
|
-
@rest_height += 1
|
1966
|
+
elsif @is_multiline and @byte_pointer == current_line.bytesize and @buffer_of_lines.size > @line_index + 1
|
1967
|
+
set_current_line(current_line + @buffer_of_lines.delete_at(@line_index + 1), current_line.bytesize)
|
2562
1968
|
end
|
2563
1969
|
end
|
2564
1970
|
alias_method :kill_line, :ed_kill_line
|
@@ -2577,11 +1983,9 @@ class Reline::LineEditor
|
|
2577
1983
|
# to the beginning of the current line.
|
2578
1984
|
private def vi_kill_line_prev(key)
|
2579
1985
|
if @byte_pointer > 0
|
2580
|
-
|
2581
|
-
|
1986
|
+
line, deleted = byteslice!(current_line, 0, @byte_pointer)
|
1987
|
+
set_current_line(line, 0)
|
2582
1988
|
@kill_ring.append(deleted, true)
|
2583
|
-
@cursor_max = calculate_width(@line)
|
2584
|
-
@cursor = 0
|
2585
1989
|
end
|
2586
1990
|
end
|
2587
1991
|
alias_method :unix_line_discard, :vi_kill_line_prev
|
@@ -2591,45 +1995,30 @@ class Reline::LineEditor
|
|
2591
1995
|
# GNU Readline:: +kill-whole-line+ (not bound) Kill all characters on the
|
2592
1996
|
# current line, no matter where point is.
|
2593
1997
|
private def em_kill_line(key)
|
2594
|
-
if
|
2595
|
-
@kill_ring.append(
|
2596
|
-
|
2597
|
-
@byte_pointer = 0
|
2598
|
-
@cursor_max = 0
|
2599
|
-
@cursor = 0
|
1998
|
+
if current_line.size > 0
|
1999
|
+
@kill_ring.append(current_line.dup, true)
|
2000
|
+
set_current_line('', 0)
|
2600
2001
|
end
|
2601
2002
|
end
|
2602
2003
|
alias_method :kill_whole_line, :em_kill_line
|
2603
2004
|
|
2604
2005
|
private def em_delete(key)
|
2605
|
-
if
|
2606
|
-
@line = nil
|
2607
|
-
if @buffer_of_lines.size > 1
|
2608
|
-
scroll_down(@highest_in_all - @first_line_started_from)
|
2609
|
-
end
|
2610
|
-
Reline::IOGate.move_cursor_column(0)
|
2006
|
+
if current_line.empty? and (not @is_multiline or @buffer_of_lines.size == 1) and key == "\C-d".ord
|
2611
2007
|
@eof = true
|
2612
2008
|
finish
|
2613
|
-
elsif @byte_pointer <
|
2614
|
-
splitted_last =
|
2009
|
+
elsif @byte_pointer < current_line.bytesize
|
2010
|
+
splitted_last = current_line.byteslice(@byte_pointer, current_line.bytesize)
|
2615
2011
|
mbchar = splitted_last.grapheme_clusters.first
|
2616
|
-
|
2617
|
-
|
2618
|
-
|
2619
|
-
|
2620
|
-
@cursor = calculate_width(@line)
|
2621
|
-
@byte_pointer = @line.bytesize
|
2622
|
-
@line += @buffer_of_lines.delete_at(@line_index + 1)
|
2623
|
-
@cursor_max = calculate_width(@line)
|
2624
|
-
@buffer_of_lines[@line_index] = @line
|
2625
|
-
@rerender_all = true
|
2626
|
-
@rest_height += 1
|
2012
|
+
line, = byteslice!(current_line, @byte_pointer, mbchar.bytesize)
|
2013
|
+
set_current_line(line)
|
2014
|
+
elsif @is_multiline and @byte_pointer == current_line.bytesize and @buffer_of_lines.size > @line_index + 1
|
2015
|
+
set_current_line(current_line + @buffer_of_lines.delete_at(@line_index + 1), current_line.bytesize)
|
2627
2016
|
end
|
2628
2017
|
end
|
2629
2018
|
alias_method :delete_char, :em_delete
|
2630
2019
|
|
2631
2020
|
private def em_delete_or_list(key)
|
2632
|
-
if
|
2021
|
+
if current_line.empty? or @byte_pointer < current_line.bytesize
|
2633
2022
|
em_delete(key)
|
2634
2023
|
else # show completed list
|
2635
2024
|
result = call_completion_proc
|
@@ -2642,29 +2031,16 @@ class Reline::LineEditor
|
|
2642
2031
|
|
2643
2032
|
private def em_yank(key)
|
2644
2033
|
yanked = @kill_ring.yank
|
2645
|
-
if yanked
|
2646
|
-
@line = byteinsert(@line, @byte_pointer, yanked)
|
2647
|
-
yanked_width = calculate_width(yanked)
|
2648
|
-
@cursor += yanked_width
|
2649
|
-
@cursor_max += yanked_width
|
2650
|
-
@byte_pointer += yanked.bytesize
|
2651
|
-
end
|
2034
|
+
insert_text(yanked) if yanked
|
2652
2035
|
end
|
2653
2036
|
alias_method :yank, :em_yank
|
2654
2037
|
|
2655
2038
|
private def em_yank_pop(key)
|
2656
2039
|
yanked, prev_yank = @kill_ring.yank_pop
|
2657
2040
|
if yanked
|
2658
|
-
|
2659
|
-
@
|
2660
|
-
|
2661
|
-
@byte_pointer -= prev_yank.bytesize
|
2662
|
-
@line, = byteslice!(@line, @byte_pointer, prev_yank.bytesize)
|
2663
|
-
@line = byteinsert(@line, @byte_pointer, yanked)
|
2664
|
-
yanked_width = calculate_width(yanked)
|
2665
|
-
@cursor += yanked_width
|
2666
|
-
@cursor_max += yanked_width
|
2667
|
-
@byte_pointer += yanked.bytesize
|
2041
|
+
line, = byteslice!(current_line, @byte_pointer - prev_yank.bytesize, prev_yank.bytesize)
|
2042
|
+
set_current_line(line, @byte_pointer - prev_yank.bytesize)
|
2043
|
+
insert_text(yanked)
|
2668
2044
|
end
|
2669
2045
|
end
|
2670
2046
|
alias_method :yank_pop, :em_yank_pop
|
@@ -2675,131 +2051,112 @@ class Reline::LineEditor
|
|
2675
2051
|
alias_method :clear_screen, :ed_clear_screen
|
2676
2052
|
|
2677
2053
|
private def em_next_word(key)
|
2678
|
-
if
|
2679
|
-
byte_size,
|
2054
|
+
if current_line.bytesize > @byte_pointer
|
2055
|
+
byte_size, _ = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
2680
2056
|
@byte_pointer += byte_size
|
2681
|
-
@cursor += width
|
2682
2057
|
end
|
2683
2058
|
end
|
2684
2059
|
alias_method :forward_word, :em_next_word
|
2685
2060
|
|
2686
2061
|
private def ed_prev_word(key)
|
2687
2062
|
if @byte_pointer > 0
|
2688
|
-
byte_size,
|
2063
|
+
byte_size, _ = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
|
2689
2064
|
@byte_pointer -= byte_size
|
2690
|
-
@cursor -= width
|
2691
2065
|
end
|
2692
2066
|
end
|
2693
2067
|
alias_method :backward_word, :ed_prev_word
|
2694
2068
|
|
2695
2069
|
private def em_delete_next_word(key)
|
2696
|
-
if
|
2697
|
-
byte_size,
|
2698
|
-
|
2070
|
+
if current_line.bytesize > @byte_pointer
|
2071
|
+
byte_size, _ = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
2072
|
+
line, word = byteslice!(current_line, @byte_pointer, byte_size)
|
2073
|
+
set_current_line(line)
|
2699
2074
|
@kill_ring.append(word)
|
2700
|
-
@cursor_max -= width
|
2701
2075
|
end
|
2702
2076
|
end
|
2703
2077
|
alias_method :kill_word, :em_delete_next_word
|
2704
2078
|
|
2705
2079
|
private def ed_delete_prev_word(key)
|
2706
2080
|
if @byte_pointer > 0
|
2707
|
-
byte_size,
|
2708
|
-
|
2081
|
+
byte_size, _ = Reline::Unicode.em_backward_word(current_line, @byte_pointer)
|
2082
|
+
line, word = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
|
2083
|
+
set_current_line(line, @byte_pointer - byte_size)
|
2709
2084
|
@kill_ring.append(word, true)
|
2710
|
-
@byte_pointer -= byte_size
|
2711
|
-
@cursor -= width
|
2712
|
-
@cursor_max -= width
|
2713
2085
|
end
|
2714
2086
|
end
|
2715
2087
|
alias_method :backward_kill_word, :ed_delete_prev_word
|
2716
2088
|
|
2717
2089
|
private def ed_transpose_chars(key)
|
2718
2090
|
if @byte_pointer > 0
|
2719
|
-
if @
|
2720
|
-
byte_size = Reline::Unicode.get_next_mbchar_size(
|
2721
|
-
mbchar = @line.byteslice(@byte_pointer, byte_size)
|
2722
|
-
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2723
|
-
@cursor += width
|
2091
|
+
if @byte_pointer < current_line.bytesize
|
2092
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
2724
2093
|
@byte_pointer += byte_size
|
2725
2094
|
end
|
2726
|
-
back1_byte_size = Reline::Unicode.get_prev_mbchar_size(
|
2095
|
+
back1_byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer)
|
2727
2096
|
if (@byte_pointer - back1_byte_size) > 0
|
2728
|
-
back2_byte_size = Reline::Unicode.get_prev_mbchar_size(
|
2097
|
+
back2_byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer - back1_byte_size)
|
2729
2098
|
back2_pointer = @byte_pointer - back1_byte_size - back2_byte_size
|
2730
|
-
|
2731
|
-
|
2099
|
+
line, back2_mbchar = byteslice!(current_line, back2_pointer, back2_byte_size)
|
2100
|
+
set_current_line(byteinsert(line, @byte_pointer - back2_byte_size, back2_mbchar))
|
2732
2101
|
end
|
2733
2102
|
end
|
2734
2103
|
end
|
2735
2104
|
alias_method :transpose_chars, :ed_transpose_chars
|
2736
2105
|
|
2737
2106
|
private def ed_transpose_words(key)
|
2738
|
-
left_word_start, middle_start, right_word_start, after_start = Reline::Unicode.ed_transpose_words(
|
2739
|
-
before =
|
2740
|
-
left_word =
|
2741
|
-
middle =
|
2742
|
-
right_word =
|
2743
|
-
after =
|
2107
|
+
left_word_start, middle_start, right_word_start, after_start = Reline::Unicode.ed_transpose_words(current_line, @byte_pointer)
|
2108
|
+
before = current_line.byteslice(0, left_word_start)
|
2109
|
+
left_word = current_line.byteslice(left_word_start, middle_start - left_word_start)
|
2110
|
+
middle = current_line.byteslice(middle_start, right_word_start - middle_start)
|
2111
|
+
right_word = current_line.byteslice(right_word_start, after_start - right_word_start)
|
2112
|
+
after = current_line.byteslice(after_start, current_line.bytesize - after_start)
|
2744
2113
|
return if left_word.empty? or right_word.empty?
|
2745
|
-
@line = before + right_word + middle + left_word + after
|
2746
2114
|
from_head_to_left_word = before + right_word + middle + left_word
|
2747
|
-
|
2748
|
-
@cursor = calculate_width(from_head_to_left_word)
|
2115
|
+
set_current_line(from_head_to_left_word + after, from_head_to_left_word.bytesize)
|
2749
2116
|
end
|
2750
2117
|
alias_method :transpose_words, :ed_transpose_words
|
2751
2118
|
|
2752
2119
|
private def em_capitol_case(key)
|
2753
|
-
if
|
2754
|
-
byte_size, _, new_str = Reline::Unicode.em_forward_word_with_capitalization(
|
2755
|
-
before =
|
2756
|
-
after =
|
2757
|
-
|
2758
|
-
@byte_pointer += new_str.bytesize
|
2759
|
-
@cursor += calculate_width(new_str)
|
2120
|
+
if current_line.bytesize > @byte_pointer
|
2121
|
+
byte_size, _, new_str = Reline::Unicode.em_forward_word_with_capitalization(current_line, @byte_pointer)
|
2122
|
+
before = current_line.byteslice(0, @byte_pointer)
|
2123
|
+
after = current_line.byteslice((@byte_pointer + byte_size)..-1)
|
2124
|
+
set_current_line(before + new_str + after, @byte_pointer + new_str.bytesize)
|
2760
2125
|
end
|
2761
2126
|
end
|
2762
2127
|
alias_method :capitalize_word, :em_capitol_case
|
2763
2128
|
|
2764
2129
|
private def em_lower_case(key)
|
2765
|
-
if
|
2766
|
-
byte_size, = Reline::Unicode.em_forward_word(
|
2767
|
-
part =
|
2130
|
+
if current_line.bytesize > @byte_pointer
|
2131
|
+
byte_size, = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
2132
|
+
part = current_line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
|
2768
2133
|
mbchar =~ /[A-Z]/ ? mbchar.downcase : mbchar
|
2769
2134
|
}.join
|
2770
|
-
rest =
|
2771
|
-
|
2772
|
-
|
2773
|
-
@cursor = calculate_width(@line)
|
2774
|
-
@cursor_max = @cursor + calculate_width(rest)
|
2775
|
-
@line += rest
|
2135
|
+
rest = current_line.byteslice((@byte_pointer + byte_size)..-1)
|
2136
|
+
line = current_line.byteslice(0, @byte_pointer) + part
|
2137
|
+
set_current_line(line + rest, line.bytesize)
|
2776
2138
|
end
|
2777
2139
|
end
|
2778
2140
|
alias_method :downcase_word, :em_lower_case
|
2779
2141
|
|
2780
2142
|
private def em_upper_case(key)
|
2781
|
-
if
|
2782
|
-
byte_size, = Reline::Unicode.em_forward_word(
|
2783
|
-
part =
|
2143
|
+
if current_line.bytesize > @byte_pointer
|
2144
|
+
byte_size, = Reline::Unicode.em_forward_word(current_line, @byte_pointer)
|
2145
|
+
part = current_line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
|
2784
2146
|
mbchar =~ /[a-z]/ ? mbchar.upcase : mbchar
|
2785
2147
|
}.join
|
2786
|
-
rest =
|
2787
|
-
|
2788
|
-
|
2789
|
-
@cursor = calculate_width(@line)
|
2790
|
-
@cursor_max = @cursor + calculate_width(rest)
|
2791
|
-
@line += rest
|
2148
|
+
rest = current_line.byteslice((@byte_pointer + byte_size)..-1)
|
2149
|
+
line = current_line.byteslice(0, @byte_pointer) + part
|
2150
|
+
set_current_line(line + rest, line.bytesize)
|
2792
2151
|
end
|
2793
2152
|
end
|
2794
2153
|
alias_method :upcase_word, :em_upper_case
|
2795
2154
|
|
2796
2155
|
private def em_kill_region(key)
|
2797
2156
|
if @byte_pointer > 0
|
2798
|
-
byte_size,
|
2799
|
-
|
2800
|
-
@byte_pointer
|
2801
|
-
@cursor -= width
|
2802
|
-
@cursor_max -= width
|
2157
|
+
byte_size, _ = Reline::Unicode.em_big_backward_word(current_line, @byte_pointer)
|
2158
|
+
line, deleted = byteslice!(current_line, @byte_pointer - byte_size, byte_size)
|
2159
|
+
set_current_line(line, @byte_pointer - byte_size)
|
2803
2160
|
@kill_ring.append(deleted, true)
|
2804
2161
|
end
|
2805
2162
|
end
|
@@ -2827,10 +2184,9 @@ class Reline::LineEditor
|
|
2827
2184
|
alias_method :vi_movement_mode, :vi_command_mode
|
2828
2185
|
|
2829
2186
|
private def vi_next_word(key, arg: 1)
|
2830
|
-
if
|
2831
|
-
byte_size,
|
2187
|
+
if current_line.bytesize > @byte_pointer
|
2188
|
+
byte_size, _ = Reline::Unicode.vi_forward_word(current_line, @byte_pointer, @drop_terminate_spaces)
|
2832
2189
|
@byte_pointer += byte_size
|
2833
|
-
@cursor += width
|
2834
2190
|
end
|
2835
2191
|
arg -= 1
|
2836
2192
|
vi_next_word(key, arg: arg) if arg > 0
|
@@ -2838,38 +2194,32 @@ class Reline::LineEditor
|
|
2838
2194
|
|
2839
2195
|
private def vi_prev_word(key, arg: 1)
|
2840
2196
|
if @byte_pointer > 0
|
2841
|
-
byte_size,
|
2197
|
+
byte_size, _ = Reline::Unicode.vi_backward_word(current_line, @byte_pointer)
|
2842
2198
|
@byte_pointer -= byte_size
|
2843
|
-
@cursor -= width
|
2844
2199
|
end
|
2845
2200
|
arg -= 1
|
2846
2201
|
vi_prev_word(key, arg: arg) if arg > 0
|
2847
2202
|
end
|
2848
2203
|
|
2849
2204
|
private def vi_end_word(key, arg: 1, inclusive: false)
|
2850
|
-
if
|
2851
|
-
byte_size,
|
2205
|
+
if current_line.bytesize > @byte_pointer
|
2206
|
+
byte_size, _ = Reline::Unicode.vi_forward_end_word(current_line, @byte_pointer)
|
2852
2207
|
@byte_pointer += byte_size
|
2853
|
-
@cursor += width
|
2854
2208
|
end
|
2855
2209
|
arg -= 1
|
2856
2210
|
if inclusive and arg.zero?
|
2857
|
-
byte_size = Reline::Unicode.get_next_mbchar_size(
|
2211
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
2858
2212
|
if byte_size > 0
|
2859
|
-
c = @line.byteslice(@byte_pointer, byte_size)
|
2860
|
-
width = Reline::Unicode.get_mbchar_width(c)
|
2861
2213
|
@byte_pointer += byte_size
|
2862
|
-
@cursor += width
|
2863
2214
|
end
|
2864
2215
|
end
|
2865
2216
|
vi_end_word(key, arg: arg) if arg > 0
|
2866
2217
|
end
|
2867
2218
|
|
2868
2219
|
private def vi_next_big_word(key, arg: 1)
|
2869
|
-
if
|
2870
|
-
byte_size,
|
2220
|
+
if current_line.bytesize > @byte_pointer
|
2221
|
+
byte_size, _ = Reline::Unicode.vi_big_forward_word(current_line, @byte_pointer)
|
2871
2222
|
@byte_pointer += byte_size
|
2872
|
-
@cursor += width
|
2873
2223
|
end
|
2874
2224
|
arg -= 1
|
2875
2225
|
vi_next_big_word(key, arg: arg) if arg > 0
|
@@ -2877,50 +2227,39 @@ class Reline::LineEditor
|
|
2877
2227
|
|
2878
2228
|
private def vi_prev_big_word(key, arg: 1)
|
2879
2229
|
if @byte_pointer > 0
|
2880
|
-
byte_size,
|
2230
|
+
byte_size, _ = Reline::Unicode.vi_big_backward_word(current_line, @byte_pointer)
|
2881
2231
|
@byte_pointer -= byte_size
|
2882
|
-
@cursor -= width
|
2883
2232
|
end
|
2884
2233
|
arg -= 1
|
2885
2234
|
vi_prev_big_word(key, arg: arg) if arg > 0
|
2886
2235
|
end
|
2887
2236
|
|
2888
2237
|
private def vi_end_big_word(key, arg: 1, inclusive: false)
|
2889
|
-
if
|
2890
|
-
byte_size,
|
2238
|
+
if current_line.bytesize > @byte_pointer
|
2239
|
+
byte_size, _ = Reline::Unicode.vi_big_forward_end_word(current_line, @byte_pointer)
|
2891
2240
|
@byte_pointer += byte_size
|
2892
|
-
@cursor += width
|
2893
2241
|
end
|
2894
2242
|
arg -= 1
|
2895
2243
|
if inclusive and arg.zero?
|
2896
|
-
byte_size = Reline::Unicode.get_next_mbchar_size(
|
2244
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
2897
2245
|
if byte_size > 0
|
2898
|
-
c = @line.byteslice(@byte_pointer, byte_size)
|
2899
|
-
width = Reline::Unicode.get_mbchar_width(c)
|
2900
2246
|
@byte_pointer += byte_size
|
2901
|
-
@cursor += width
|
2902
2247
|
end
|
2903
2248
|
end
|
2904
2249
|
vi_end_big_word(key, arg: arg) if arg > 0
|
2905
2250
|
end
|
2906
2251
|
|
2907
2252
|
private def vi_delete_prev_char(key)
|
2908
|
-
if @is_multiline and @
|
2909
|
-
@buffer_of_lines[@line_index] = @line
|
2910
|
-
@cursor = calculate_width(@buffer_of_lines[@line_index - 1])
|
2253
|
+
if @is_multiline and @byte_pointer == 0 and @line_index > 0
|
2911
2254
|
@byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
|
2912
2255
|
@buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
|
2913
2256
|
@line_index -= 1
|
2914
|
-
|
2915
|
-
|
2916
|
-
|
2917
|
-
elsif @cursor > 0
|
2918
|
-
byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
2257
|
+
process_auto_indent cursor_dependent: false
|
2258
|
+
elsif @byte_pointer > 0
|
2259
|
+
byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer)
|
2919
2260
|
@byte_pointer -= byte_size
|
2920
|
-
|
2921
|
-
|
2922
|
-
@cursor -= width
|
2923
|
-
@cursor_max -= width
|
2261
|
+
line, _ = byteslice!(current_line, @byte_pointer, byte_size)
|
2262
|
+
set_current_line(line)
|
2924
2263
|
end
|
2925
2264
|
end
|
2926
2265
|
|
@@ -2935,16 +2274,14 @@ class Reline::LineEditor
|
|
2935
2274
|
end
|
2936
2275
|
|
2937
2276
|
private def ed_delete_prev_char(key, arg: 1)
|
2938
|
-
deleted = ''
|
2277
|
+
deleted = +''
|
2939
2278
|
arg.times do
|
2940
|
-
if @
|
2941
|
-
byte_size = Reline::Unicode.get_prev_mbchar_size(
|
2279
|
+
if @byte_pointer > 0
|
2280
|
+
byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer)
|
2942
2281
|
@byte_pointer -= byte_size
|
2943
|
-
|
2282
|
+
line, mbchar = byteslice!(current_line, @byte_pointer, byte_size)
|
2283
|
+
set_current_line(line)
|
2944
2284
|
deleted.prepend(mbchar)
|
2945
|
-
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2946
|
-
@cursor -= width
|
2947
|
-
@cursor_max -= width
|
2948
2285
|
end
|
2949
2286
|
end
|
2950
2287
|
copy_for_vi(deleted)
|
@@ -2952,20 +2289,18 @@ class Reline::LineEditor
|
|
2952
2289
|
|
2953
2290
|
private def vi_zero(key)
|
2954
2291
|
@byte_pointer = 0
|
2955
|
-
@cursor = 0
|
2956
2292
|
end
|
2957
2293
|
|
2958
2294
|
private def vi_change_meta(key, arg: 1)
|
2959
2295
|
@drop_terminate_spaces = true
|
2960
|
-
@waiting_operator_proc = proc { |
|
2296
|
+
@waiting_operator_proc = proc { |byte_pointer_diff|
|
2961
2297
|
if byte_pointer_diff > 0
|
2962
|
-
|
2298
|
+
line, cut = byteslice!(current_line, @byte_pointer, byte_pointer_diff)
|
2963
2299
|
elsif byte_pointer_diff < 0
|
2964
|
-
|
2300
|
+
line, cut = byteslice!(current_line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
2965
2301
|
end
|
2302
|
+
set_current_line(line)
|
2966
2303
|
copy_for_vi(cut)
|
2967
|
-
@cursor += cursor_diff if cursor_diff < 0
|
2968
|
-
@cursor_max -= cursor_diff.abs
|
2969
2304
|
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
2970
2305
|
@config.editing_mode = :vi_insert
|
2971
2306
|
@drop_terminate_spaces = false
|
@@ -2974,26 +2309,24 @@ class Reline::LineEditor
|
|
2974
2309
|
end
|
2975
2310
|
|
2976
2311
|
private def vi_delete_meta(key, arg: 1)
|
2977
|
-
@waiting_operator_proc = proc { |
|
2312
|
+
@waiting_operator_proc = proc { |byte_pointer_diff|
|
2978
2313
|
if byte_pointer_diff > 0
|
2979
|
-
|
2314
|
+
line, cut = byteslice!(current_line, @byte_pointer, byte_pointer_diff)
|
2980
2315
|
elsif byte_pointer_diff < 0
|
2981
|
-
|
2316
|
+
line, cut = byteslice!(current_line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
2982
2317
|
end
|
2983
2318
|
copy_for_vi(cut)
|
2984
|
-
|
2985
|
-
@cursor_max -= cursor_diff.abs
|
2986
|
-
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
2319
|
+
set_current_line(line || '', @byte_pointer + (byte_pointer_diff < 0 ? byte_pointer_diff : 0))
|
2987
2320
|
}
|
2988
2321
|
@waiting_operator_vi_arg = arg
|
2989
2322
|
end
|
2990
2323
|
|
2991
2324
|
private def vi_yank(key, arg: 1)
|
2992
|
-
@waiting_operator_proc = proc { |
|
2325
|
+
@waiting_operator_proc = proc { |byte_pointer_diff|
|
2993
2326
|
if byte_pointer_diff > 0
|
2994
|
-
cut =
|
2327
|
+
cut = current_line.byteslice(@byte_pointer, byte_pointer_diff)
|
2995
2328
|
elsif byte_pointer_diff < 0
|
2996
|
-
cut =
|
2329
|
+
cut = current_line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
2997
2330
|
end
|
2998
2331
|
copy_for_vi(cut)
|
2999
2332
|
}
|
@@ -3001,12 +2334,8 @@ class Reline::LineEditor
|
|
3001
2334
|
end
|
3002
2335
|
|
3003
2336
|
private def vi_list_or_eof(key)
|
3004
|
-
if (not @is_multiline and
|
3005
|
-
|
3006
|
-
if @buffer_of_lines.size > 1
|
3007
|
-
scroll_down(@highest_in_all - @first_line_started_from)
|
3008
|
-
end
|
3009
|
-
Reline::IOGate.move_cursor_column(0)
|
2337
|
+
if (not @is_multiline and current_line.empty?) or (@is_multiline and current_line.empty? and @buffer_of_lines.size == 1)
|
2338
|
+
set_current_line('', 0)
|
3010
2339
|
@eof = true
|
3011
2340
|
finish
|
3012
2341
|
else
|
@@ -3017,18 +2346,15 @@ class Reline::LineEditor
|
|
3017
2346
|
alias_method :vi_eof_maybe, :vi_list_or_eof
|
3018
2347
|
|
3019
2348
|
private def ed_delete_next_char(key, arg: 1)
|
3020
|
-
byte_size = Reline::Unicode.get_next_mbchar_size(
|
3021
|
-
unless
|
3022
|
-
|
2349
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
2350
|
+
unless current_line.empty? || byte_size == 0
|
2351
|
+
line, mbchar = byteslice!(current_line, @byte_pointer, byte_size)
|
3023
2352
|
copy_for_vi(mbchar)
|
3024
|
-
|
3025
|
-
|
3026
|
-
|
3027
|
-
|
3028
|
-
|
3029
|
-
width = Reline::Unicode.get_mbchar_width(mbchar)
|
3030
|
-
@byte_pointer -= byte_size
|
3031
|
-
@cursor -= width
|
2353
|
+
if @byte_pointer > 0 && current_line.bytesize == @byte_pointer + byte_size
|
2354
|
+
byte_size = Reline::Unicode.get_prev_mbchar_size(line, @byte_pointer)
|
2355
|
+
set_current_line(line, @byte_pointer - byte_size)
|
2356
|
+
else
|
2357
|
+
set_current_line(line, @byte_pointer)
|
3032
2358
|
end
|
3033
2359
|
end
|
3034
2360
|
arg -= 1
|
@@ -3041,20 +2367,14 @@ class Reline::LineEditor
|
|
3041
2367
|
end
|
3042
2368
|
if @history_pointer.nil?
|
3043
2369
|
@history_pointer = 0
|
3044
|
-
@line_backup_in_history =
|
3045
|
-
|
3046
|
-
@cursor_max = calculate_width(@line)
|
3047
|
-
@cursor = 0
|
3048
|
-
@byte_pointer = 0
|
2370
|
+
@line_backup_in_history = current_line
|
2371
|
+
set_current_line(Reline::HISTORY[@history_pointer], 0)
|
3049
2372
|
elsif @history_pointer.zero?
|
3050
2373
|
return
|
3051
2374
|
else
|
3052
|
-
Reline::HISTORY[@history_pointer] =
|
2375
|
+
Reline::HISTORY[@history_pointer] = current_line
|
3053
2376
|
@history_pointer = 0
|
3054
|
-
|
3055
|
-
@cursor_max = calculate_width(@line)
|
3056
|
-
@cursor = 0
|
3057
|
-
@byte_pointer = 0
|
2377
|
+
set_current_line(Reline::HISTORY[@history_pointer], 0)
|
3058
2378
|
end
|
3059
2379
|
end
|
3060
2380
|
|
@@ -3063,7 +2383,7 @@ class Reline::LineEditor
|
|
3063
2383
|
if @is_multiline
|
3064
2384
|
fp.write whole_lines.join("\n")
|
3065
2385
|
else
|
3066
|
-
fp.write
|
2386
|
+
fp.write current_line
|
3067
2387
|
end
|
3068
2388
|
fp.path
|
3069
2389
|
}
|
@@ -3072,21 +2392,16 @@ class Reline::LineEditor
|
|
3072
2392
|
@buffer_of_lines = File.read(path).split("\n")
|
3073
2393
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
3074
2394
|
@line_index = 0
|
3075
|
-
@line = @buffer_of_lines[@line_index]
|
3076
|
-
@rerender_all = true
|
3077
2395
|
else
|
3078
|
-
@
|
2396
|
+
@buffer_of_lines = File.read(path).split("\n")
|
3079
2397
|
end
|
3080
2398
|
finish
|
3081
2399
|
end
|
3082
2400
|
|
3083
2401
|
private def vi_paste_prev(key, arg: 1)
|
3084
2402
|
if @vi_clipboard.size > 0
|
3085
|
-
@line = byteinsert(@line, @byte_pointer, @vi_clipboard)
|
3086
|
-
@cursor_max += calculate_width(@vi_clipboard)
|
3087
2403
|
cursor_point = @vi_clipboard.grapheme_clusters[0..-2].join
|
3088
|
-
@
|
3089
|
-
@byte_pointer += cursor_point.bytesize
|
2404
|
+
set_current_line(byteinsert(current_line, @byte_pointer, @vi_clipboard), @byte_pointer + cursor_point.bytesize)
|
3090
2405
|
end
|
3091
2406
|
arg -= 1
|
3092
2407
|
vi_paste_prev(key, arg: arg) if arg > 0
|
@@ -3094,11 +2409,9 @@ class Reline::LineEditor
|
|
3094
2409
|
|
3095
2410
|
private def vi_paste_next(key, arg: 1)
|
3096
2411
|
if @vi_clipboard.size > 0
|
3097
|
-
byte_size = Reline::Unicode.get_next_mbchar_size(
|
3098
|
-
|
3099
|
-
@
|
3100
|
-
@cursor += calculate_width(@vi_clipboard)
|
3101
|
-
@byte_pointer += @vi_clipboard.bytesize
|
2412
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
2413
|
+
line = byteinsert(current_line, @byte_pointer + byte_size, @vi_clipboard)
|
2414
|
+
set_current_line(line, @byte_pointer + @vi_clipboard.bytesize)
|
3102
2415
|
end
|
3103
2416
|
arg -= 1
|
3104
2417
|
vi_paste_next(key, arg: arg) if arg > 0
|
@@ -3122,12 +2435,13 @@ class Reline::LineEditor
|
|
3122
2435
|
end
|
3123
2436
|
|
3124
2437
|
private def vi_to_column(key, arg: 0)
|
3125
|
-
|
2438
|
+
current_row_width = calculate_width(current_row)
|
2439
|
+
@byte_pointer, = current_line.grapheme_clusters.inject([0, 0]) { |total, gc|
|
3126
2440
|
# total has [byte_size, cursor]
|
3127
2441
|
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
|
3128
2442
|
if (total.last + mbchar_width) >= arg
|
3129
2443
|
break total
|
3130
|
-
elsif (total.last + mbchar_width) >=
|
2444
|
+
elsif (total.last + mbchar_width) >= current_row_width
|
3131
2445
|
break total
|
3132
2446
|
else
|
3133
2447
|
total = [total.first + gc.bytesize, total.last + mbchar_width]
|
@@ -3139,26 +2453,22 @@ class Reline::LineEditor
|
|
3139
2453
|
private def vi_replace_char(key, arg: 1)
|
3140
2454
|
@waiting_proc = ->(k) {
|
3141
2455
|
if arg == 1
|
3142
|
-
byte_size = Reline::Unicode.get_next_mbchar_size(
|
3143
|
-
before =
|
2456
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
2457
|
+
before = current_line.byteslice(0, @byte_pointer)
|
3144
2458
|
remaining_point = @byte_pointer + byte_size
|
3145
|
-
after =
|
3146
|
-
|
3147
|
-
@cursor_max = calculate_width(@line)
|
2459
|
+
after = current_line.byteslice(remaining_point, current_line.bytesize - remaining_point)
|
2460
|
+
set_current_line(before + k.chr + after)
|
3148
2461
|
@waiting_proc = nil
|
3149
2462
|
elsif arg > 1
|
3150
2463
|
byte_size = 0
|
3151
2464
|
arg.times do
|
3152
|
-
byte_size += Reline::Unicode.get_next_mbchar_size(
|
2465
|
+
byte_size += Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer + byte_size)
|
3153
2466
|
end
|
3154
|
-
before =
|
2467
|
+
before = current_line.byteslice(0, @byte_pointer)
|
3155
2468
|
remaining_point = @byte_pointer + byte_size
|
3156
|
-
after =
|
2469
|
+
after = current_line.byteslice(remaining_point, current_line.bytesize - remaining_point)
|
3157
2470
|
replaced = k.chr * arg
|
3158
|
-
|
3159
|
-
@byte_pointer += replaced.bytesize
|
3160
|
-
@cursor += calculate_width(replaced)
|
3161
|
-
@cursor_max = calculate_width(@line)
|
2471
|
+
set_current_line(before + replaced + after, @byte_pointer + replaced.bytesize)
|
3162
2472
|
@waiting_proc = nil
|
3163
2473
|
end
|
3164
2474
|
}
|
@@ -3181,7 +2491,7 @@ class Reline::LineEditor
|
|
3181
2491
|
prev_total = nil
|
3182
2492
|
total = nil
|
3183
2493
|
found = false
|
3184
|
-
|
2494
|
+
current_line.byteslice(@byte_pointer..-1).grapheme_clusters.each do |mbchar|
|
3185
2495
|
# total has [byte_size, cursor]
|
3186
2496
|
unless total
|
3187
2497
|
# skip cursor point
|
@@ -3201,21 +2511,16 @@ class Reline::LineEditor
|
|
3201
2511
|
end
|
3202
2512
|
end
|
3203
2513
|
if not need_prev_char and found and total
|
3204
|
-
byte_size,
|
2514
|
+
byte_size, _ = total
|
3205
2515
|
@byte_pointer += byte_size
|
3206
|
-
@cursor += width
|
3207
2516
|
elsif need_prev_char and found and prev_total
|
3208
|
-
byte_size,
|
2517
|
+
byte_size, _ = prev_total
|
3209
2518
|
@byte_pointer += byte_size
|
3210
|
-
@cursor += width
|
3211
2519
|
end
|
3212
2520
|
if inclusive
|
3213
|
-
byte_size = Reline::Unicode.get_next_mbchar_size(
|
2521
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
3214
2522
|
if byte_size > 0
|
3215
|
-
c = @line.byteslice(@byte_pointer, byte_size)
|
3216
|
-
width = Reline::Unicode.get_mbchar_width(c)
|
3217
2523
|
@byte_pointer += byte_size
|
3218
|
-
@cursor += width
|
3219
2524
|
end
|
3220
2525
|
end
|
3221
2526
|
@waiting_proc = nil
|
@@ -3238,7 +2543,7 @@ class Reline::LineEditor
|
|
3238
2543
|
prev_total = nil
|
3239
2544
|
total = nil
|
3240
2545
|
found = false
|
3241
|
-
|
2546
|
+
current_line.byteslice(0..@byte_pointer).grapheme_clusters.reverse_each do |mbchar|
|
3242
2547
|
# total has [byte_size, cursor]
|
3243
2548
|
unless total
|
3244
2549
|
# skip cursor point
|
@@ -3258,26 +2563,19 @@ class Reline::LineEditor
|
|
3258
2563
|
end
|
3259
2564
|
end
|
3260
2565
|
if not need_next_char and found and total
|
3261
|
-
byte_size,
|
2566
|
+
byte_size, _ = total
|
3262
2567
|
@byte_pointer -= byte_size
|
3263
|
-
@cursor -= width
|
3264
2568
|
elsif need_next_char and found and prev_total
|
3265
|
-
byte_size,
|
2569
|
+
byte_size, _ = prev_total
|
3266
2570
|
@byte_pointer -= byte_size
|
3267
|
-
@cursor -= width
|
3268
2571
|
end
|
3269
2572
|
@waiting_proc = nil
|
3270
2573
|
end
|
3271
2574
|
|
3272
2575
|
private def vi_join_lines(key, arg: 1)
|
3273
2576
|
if @is_multiline and @buffer_of_lines.size > @line_index + 1
|
3274
|
-
|
3275
|
-
|
3276
|
-
@line += ' ' + @buffer_of_lines.delete_at(@line_index + 1).lstrip
|
3277
|
-
@cursor_max = calculate_width(@line)
|
3278
|
-
@buffer_of_lines[@line_index] = @line
|
3279
|
-
@rerender_all = true
|
3280
|
-
@rest_height += 1
|
2577
|
+
next_line = @buffer_of_lines.delete_at(@line_index + 1).lstrip
|
2578
|
+
set_current_line(current_line + ' ' + next_line, current_line.bytesize)
|
3281
2579
|
end
|
3282
2580
|
arg -= 1
|
3283
2581
|
vi_join_lines(key, arg: arg) if arg > 0
|
@@ -3291,10 +2589,7 @@ class Reline::LineEditor
|
|
3291
2589
|
private def em_exchange_mark(key)
|
3292
2590
|
return unless @mark_pointer
|
3293
2591
|
new_pointer = [@byte_pointer, @line_index]
|
3294
|
-
@previous_line_index = @line_index
|
3295
2592
|
@byte_pointer, @line_index = @mark_pointer
|
3296
|
-
@cursor = calculate_width(@line.byteslice(0, @byte_pointer))
|
3297
|
-
@cursor_max = calculate_width(@line)
|
3298
2593
|
@mark_pointer = new_pointer
|
3299
2594
|
end
|
3300
2595
|
alias_method :exchange_point_and_mark, :em_exchange_mark
|