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