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