reline 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/reline.rb +23 -5
- data/lib/reline/ansi.rb +44 -3
- data/lib/reline/config.rb +3 -1
- data/lib/reline/general_io.rb +15 -1
- data/lib/reline/key_actor/emacs.rb +1 -1
- data/lib/reline/key_actor/vi_command.rb +2 -2
- data/lib/reline/kill_ring.rb +12 -0
- data/lib/reline/line_editor.rb +552 -232
- data/lib/reline/line_editor.rb.orig +2606 -0
- data/lib/reline/sibori.rb +14 -0
- data/lib/reline/unicode.rb +63 -33
- data/lib/reline/unicode/east_asian_width.rb +13 -13
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +8 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a20f90b83534916dd8575a842ef2cbe968879cc4b2d1826b5374137f554249a
|
4
|
+
data.tar.gz: add41e0e8f89fa39b8064c9d45bc0f17ac1b374578ae25872e24c50cb2e0e761
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ebd4286538633a6bb87dd2007bdd6bf389ef87aa5dd7406288ae8f52cacc1e0d7f87c4d3ae4bbac3fa125004d5f65b5740c6b8d2ec014e130160d7bd22c6ec1e
|
7
|
+
data.tar.gz: 3a418c2aa202ac4710bb7d90d0017870c066e17ece72c644f90e4d37b9faadb02636150d332c00845c5ca60a421abd1394255f77e95be7626cfce046705b7ed0
|
data/lib/reline.rb
CHANGED
@@ -36,7 +36,6 @@ module Reline
|
|
36
36
|
attr_accessor :config
|
37
37
|
attr_accessor :key_stroke
|
38
38
|
attr_accessor :line_editor
|
39
|
-
attr_accessor :ambiguous_width
|
40
39
|
attr_accessor :last_incremental_search
|
41
40
|
attr_reader :output
|
42
41
|
|
@@ -44,6 +43,7 @@ module Reline
|
|
44
43
|
self.output = STDOUT
|
45
44
|
yield self
|
46
45
|
@completion_quote_character = nil
|
46
|
+
@bracketed_paste_finished = false
|
47
47
|
end
|
48
48
|
|
49
49
|
def encoding
|
@@ -199,7 +199,11 @@ module Reline
|
|
199
199
|
|
200
200
|
private def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
|
201
201
|
if ENV['RELINE_STDERR_TTY']
|
202
|
-
|
202
|
+
if Reline::IOGate.win?
|
203
|
+
$stderr = File.open(ENV['RELINE_STDERR_TTY'], 'a')
|
204
|
+
else
|
205
|
+
$stderr.reopen(ENV['RELINE_STDERR_TTY'], 'w')
|
206
|
+
end
|
203
207
|
$stderr.sync = true
|
204
208
|
$stderr.puts "Reline is used by #{Process.pid}"
|
205
209
|
end
|
@@ -243,6 +247,10 @@ module Reline
|
|
243
247
|
line_editor.input_key(c)
|
244
248
|
line_editor.rerender
|
245
249
|
}
|
250
|
+
if @bracketed_paste_finished
|
251
|
+
line_editor.rerender_all
|
252
|
+
@bracketed_paste_finished = false
|
253
|
+
end
|
246
254
|
}
|
247
255
|
if prev_pasting_state == true and not Reline::IOGate.in_pasting? and not line_editor.finished?
|
248
256
|
prev_pasting_state = false
|
@@ -275,8 +283,13 @@ module Reline
|
|
275
283
|
buffer = []
|
276
284
|
loop do
|
277
285
|
c = Reline::IOGate.getc
|
278
|
-
|
279
|
-
|
286
|
+
if c == -1
|
287
|
+
result = :unmatched
|
288
|
+
@bracketed_paste_finished = true
|
289
|
+
else
|
290
|
+
buffer << c
|
291
|
+
result = key_stroke.match_status(buffer)
|
292
|
+
end
|
280
293
|
case result
|
281
294
|
when :matched
|
282
295
|
expanded = key_stroke.expand(buffer).map{ |expanded_c|
|
@@ -342,9 +355,14 @@ module Reline
|
|
342
355
|
end
|
343
356
|
end
|
344
357
|
|
358
|
+
def ambiguous_width
|
359
|
+
may_req_ambiguous_char_width unless defined? @ambiguous_width
|
360
|
+
@ambiguous_width
|
361
|
+
end
|
362
|
+
|
345
363
|
private def may_req_ambiguous_char_width
|
346
364
|
@ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
|
347
|
-
return if ambiguous_width
|
365
|
+
return if @ambiguous_width
|
348
366
|
Reline::IOGate.move_cursor_column(0)
|
349
367
|
begin
|
350
368
|
output.write "\u{25bd}"
|
data/lib/reline/ansi.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'io/console'
|
2
|
+
require 'timeout'
|
2
3
|
|
3
4
|
class Reline::ANSI
|
4
5
|
def self.encoding
|
@@ -67,7 +68,7 @@ class Reline::ANSI
|
|
67
68
|
end
|
68
69
|
|
69
70
|
@@buf = []
|
70
|
-
def self.
|
71
|
+
def self.inner_getc
|
71
72
|
unless @@buf.empty?
|
72
73
|
return @@buf.shift
|
73
74
|
end
|
@@ -80,8 +81,48 @@ class Reline::ANSI
|
|
80
81
|
nil
|
81
82
|
end
|
82
83
|
|
84
|
+
@@in_bracketed_paste_mode = false
|
85
|
+
START_BRACKETED_PASTE = String.new("\e[200~,", encoding: Encoding::ASCII_8BIT)
|
86
|
+
END_BRACKETED_PASTE = String.new("\e[200~.", encoding: Encoding::ASCII_8BIT)
|
87
|
+
def self.getc_with_bracketed_paste
|
88
|
+
buffer = String.new(encoding: Encoding::ASCII_8BIT)
|
89
|
+
buffer << inner_getc
|
90
|
+
while START_BRACKETED_PASTE.start_with?(buffer) or END_BRACKETED_PASTE.start_with?(buffer) do
|
91
|
+
if START_BRACKETED_PASTE == buffer
|
92
|
+
@@in_bracketed_paste_mode = true
|
93
|
+
return inner_getc
|
94
|
+
elsif END_BRACKETED_PASTE == buffer
|
95
|
+
@@in_bracketed_paste_mode = false
|
96
|
+
ungetc(-1)
|
97
|
+
return inner_getc
|
98
|
+
end
|
99
|
+
begin
|
100
|
+
succ_c = nil
|
101
|
+
Timeout.timeout(Reline.core.config.keyseq_timeout * 100) {
|
102
|
+
succ_c = inner_getc
|
103
|
+
}
|
104
|
+
rescue Timeout::Error
|
105
|
+
break
|
106
|
+
else
|
107
|
+
buffer << succ_c
|
108
|
+
end
|
109
|
+
end
|
110
|
+
buffer.bytes.reverse_each do |ch|
|
111
|
+
ungetc ch
|
112
|
+
end
|
113
|
+
inner_getc
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.getc
|
117
|
+
if Reline.core.config.enable_bracketed_paste
|
118
|
+
getc_with_bracketed_paste
|
119
|
+
else
|
120
|
+
inner_getc
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
83
124
|
def self.in_pasting?
|
84
|
-
not Reline::IOGate.empty_buffer?
|
125
|
+
@@in_bracketed_paste_mode or (not Reline::IOGate.empty_buffer?)
|
85
126
|
end
|
86
127
|
|
87
128
|
def self.empty_buffer?
|
@@ -131,7 +172,7 @@ class Reline::ANSI
|
|
131
172
|
|
132
173
|
def self.cursor_pos
|
133
174
|
begin
|
134
|
-
res = ''
|
175
|
+
res = +''
|
135
176
|
m = nil
|
136
177
|
@@input.raw do |stdin|
|
137
178
|
@@output << "\e[6n"
|
data/lib/reline/config.rb
CHANGED
@@ -37,6 +37,8 @@ class Reline::Config
|
|
37
37
|
vi-cmd-mode-icon
|
38
38
|
vi-ins-mode-icon
|
39
39
|
emacs-mode-string
|
40
|
+
enable-bracketed-paste
|
41
|
+
isearch-terminators
|
40
42
|
}
|
41
43
|
VARIABLE_NAME_SYMBOLS = VARIABLE_NAMES.map { |v| :"#{v.tr(?-, ?_)}" }
|
42
44
|
VARIABLE_NAME_SYMBOLS.each do |v|
|
@@ -237,7 +239,7 @@ class Reline::Config
|
|
237
239
|
when 'completion-query-items'
|
238
240
|
@completion_query_items = value.to_i
|
239
241
|
when 'isearch-terminators'
|
240
|
-
@isearch_terminators =
|
242
|
+
@isearch_terminators = retrieve_string(value)
|
241
243
|
when 'editing-mode'
|
242
244
|
case value
|
243
245
|
when 'emacs'
|
data/lib/reline/general_io.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'timeout'
|
2
2
|
|
3
3
|
class Reline::GeneralIO
|
4
|
+
def self.reset
|
5
|
+
@@pasting = false
|
6
|
+
end
|
7
|
+
|
4
8
|
def self.encoding
|
5
9
|
RUBY_PLATFORM =~ /mswin|mingw/ ? Encoding::UTF_8 : Encoding::default_external
|
6
10
|
end
|
@@ -67,8 +71,18 @@ class Reline::GeneralIO
|
|
67
71
|
def self.set_winch_handler(&handler)
|
68
72
|
end
|
69
73
|
|
74
|
+
@@pasting = false
|
75
|
+
|
70
76
|
def self.in_pasting?
|
71
|
-
|
77
|
+
@@pasting
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.start_pasting
|
81
|
+
@@pasting = true
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.finish_pasting
|
85
|
+
@@pasting = false
|
72
86
|
end
|
73
87
|
|
74
88
|
def self.prep
|
@@ -17,7 +17,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
|
|
17
17
|
# 7 ^G
|
18
18
|
:ed_unassigned,
|
19
19
|
# 8 ^H
|
20
|
-
:
|
20
|
+
:ed_unassigned,
|
21
21
|
# 9 ^I
|
22
22
|
:ed_unassigned,
|
23
23
|
# 10 ^J
|
@@ -255,7 +255,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
|
|
255
255
|
# 126 ~
|
256
256
|
:vi_change_case,
|
257
257
|
# 127 ^?
|
258
|
-
:
|
258
|
+
:ed_unassigned,
|
259
259
|
# 128 M-^@
|
260
260
|
:ed_unassigned,
|
261
261
|
# 129 M-^A
|
data/lib/reline/kill_ring.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
class Reline::KillRing
|
2
|
+
include Enumerable
|
3
|
+
|
2
4
|
module State
|
3
5
|
FRESH = :fresh
|
4
6
|
CONTINUED = :continued
|
@@ -110,4 +112,14 @@ class Reline::KillRing
|
|
110
112
|
nil
|
111
113
|
end
|
112
114
|
end
|
115
|
+
|
116
|
+
def each
|
117
|
+
start = head = @ring.head
|
118
|
+
loop do
|
119
|
+
break if head.nil?
|
120
|
+
yield head.str
|
121
|
+
head = head.backward
|
122
|
+
break if head == start
|
123
|
+
end
|
124
|
+
end
|
113
125
|
end
|
data/lib/reline/line_editor.rb
CHANGED
@@ -50,6 +50,8 @@ class Reline::LineEditor
|
|
50
50
|
CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer)
|
51
51
|
MenuInfo = Struct.new('MenuInfo', :target, :list)
|
52
52
|
|
53
|
+
PROMPT_LIST_CACHE_TIMEOUT = 0.5
|
54
|
+
|
53
55
|
def initialize(config, encoding)
|
54
56
|
@config = config
|
55
57
|
@completion_append_character = ''
|
@@ -59,11 +61,33 @@ class Reline::LineEditor
|
|
59
61
|
def simplified_rendering?
|
60
62
|
if finished?
|
61
63
|
false
|
64
|
+
elsif @just_cursor_moving and not @rerender_all
|
65
|
+
true
|
62
66
|
else
|
63
67
|
not @rerender_all and not finished? and Reline::IOGate.in_pasting?
|
64
68
|
end
|
65
69
|
end
|
66
70
|
|
71
|
+
private def check_mode_icon
|
72
|
+
mode_icon = nil
|
73
|
+
if @config.show_mode_in_prompt
|
74
|
+
if @config.editing_mode_is?(:vi_command)
|
75
|
+
mode_icon = @config.vi_cmd_mode_icon
|
76
|
+
elsif @config.editing_mode_is?(:vi_insert)
|
77
|
+
mode_icon = @config.vi_ins_mode_icon
|
78
|
+
elsif @config.editing_mode_is?(:emacs)
|
79
|
+
mode_icon = @config.emacs_mode_string
|
80
|
+
else
|
81
|
+
mode_icon = '?'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
if mode_icon != @prev_mode_icon
|
85
|
+
@rerender_all = true
|
86
|
+
end
|
87
|
+
@prev_mode_icon = mode_icon
|
88
|
+
mode_icon
|
89
|
+
end
|
90
|
+
|
67
91
|
private def check_multiline_prompt(buffer, prompt)
|
68
92
|
if @vi_arg
|
69
93
|
prompt = "(arg: #{@vi_arg}) "
|
@@ -74,39 +98,37 @@ class Reline::LineEditor
|
|
74
98
|
else
|
75
99
|
prompt = @prompt
|
76
100
|
end
|
77
|
-
|
101
|
+
if simplified_rendering?
|
102
|
+
mode_icon = check_mode_icon
|
103
|
+
prompt = mode_icon + prompt if mode_icon
|
104
|
+
return [prompt, calculate_width(prompt, true), [prompt] * buffer.size]
|
105
|
+
end
|
78
106
|
if @prompt_proc
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
mode_icon = @config.vi_ins_mode_icon
|
86
|
-
elsif @config.editing_mode_is?(:emacs)
|
87
|
-
mode_icon = @config.emacs_mode_string
|
88
|
-
else
|
89
|
-
mode_icon = '?'
|
107
|
+
use_cached_prompt_list = false
|
108
|
+
if @cached_prompt_list
|
109
|
+
if @just_cursor_moving
|
110
|
+
use_cached_prompt_list = true
|
111
|
+
elsif Time.now.to_f < (@prompt_cache_time + PROMPT_LIST_CACHE_TIMEOUT) and buffer.size == @cached_prompt_list.size
|
112
|
+
use_cached_prompt_list = true
|
90
113
|
end
|
91
|
-
prompt_list.map!{ |pr| mode_icon + pr }
|
92
114
|
end
|
115
|
+
if use_cached_prompt_list
|
116
|
+
prompt_list = @cached_prompt_list
|
117
|
+
else
|
118
|
+
prompt_list = @cached_prompt_list = @prompt_proc.(buffer)
|
119
|
+
@prompt_cache_time = Time.now.to_f
|
120
|
+
end
|
121
|
+
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
122
|
+
mode_icon = check_mode_icon
|
123
|
+
prompt_list = prompt_list.map{ |pr| mode_icon + pr } if mode_icon
|
93
124
|
prompt = prompt_list[@line_index]
|
125
|
+
prompt = prompt_list[0] if prompt.nil?
|
94
126
|
prompt_width = calculate_width(prompt, true)
|
95
127
|
[prompt, prompt_width, prompt_list]
|
96
128
|
else
|
129
|
+
mode_icon = check_mode_icon
|
130
|
+
prompt = mode_icon + prompt if mode_icon
|
97
131
|
prompt_width = calculate_width(prompt, true)
|
98
|
-
if @config.show_mode_in_prompt
|
99
|
-
if @config.editing_mode_is?(:vi_command)
|
100
|
-
mode_icon = @config.vi_cmd_mode_icon
|
101
|
-
elsif @config.editing_mode_is?(:vi_insert)
|
102
|
-
mode_icon = @config.vi_ins_mode_icon
|
103
|
-
elsif @config.editing_mode_is?(:emacs)
|
104
|
-
mode_icon = @config.emacs_mode_string
|
105
|
-
else
|
106
|
-
mode_icon = '?'
|
107
|
-
end
|
108
|
-
prompt = mode_icon + prompt
|
109
|
-
end
|
110
132
|
[prompt, prompt_width, nil]
|
111
133
|
end
|
112
134
|
end
|
@@ -114,6 +136,7 @@ class Reline::LineEditor
|
|
114
136
|
def reset(prompt = '', encoding:)
|
115
137
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
116
138
|
@screen_size = Reline::IOGate.get_screen_size
|
139
|
+
@screen_height = @screen_size.first
|
117
140
|
reset_variables(prompt, encoding: encoding)
|
118
141
|
@old_trap = Signal.trap('SIGINT') {
|
119
142
|
@old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
|
@@ -123,6 +146,7 @@ class Reline::LineEditor
|
|
123
146
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
124
147
|
old_screen_size = @screen_size
|
125
148
|
@screen_size = Reline::IOGate.get_screen_size
|
149
|
+
@screen_height = @screen_size.first
|
126
150
|
if old_screen_size.last < @screen_size.last # columns increase
|
127
151
|
@rerender_all = true
|
128
152
|
rerender
|
@@ -174,11 +198,12 @@ class Reline::LineEditor
|
|
174
198
|
@cleared = false
|
175
199
|
@rerender_all = false
|
176
200
|
@history_pointer = nil
|
177
|
-
@kill_ring
|
201
|
+
@kill_ring ||= Reline::KillRing.new
|
178
202
|
@vi_clipboard = ''
|
179
203
|
@vi_arg = nil
|
180
204
|
@waiting_proc = nil
|
181
205
|
@waiting_operator_proc = nil
|
206
|
+
@waiting_operator_vi_arg = nil
|
182
207
|
@completion_journey_data = nil
|
183
208
|
@completion_state = CompletionState::NORMAL
|
184
209
|
@perfect_matched = nil
|
@@ -186,7 +211,15 @@ class Reline::LineEditor
|
|
186
211
|
@first_prompt = true
|
187
212
|
@searching_prompt = nil
|
188
213
|
@first_char = true
|
214
|
+
@add_newline_to_end_of_buffer = false
|
215
|
+
@just_cursor_moving = nil
|
216
|
+
@cached_prompt_list = nil
|
217
|
+
@prompt_cache_time = nil
|
189
218
|
@eof = false
|
219
|
+
@continuous_insertion_buffer = String.new(encoding: @encoding)
|
220
|
+
@scroll_partial_screen = nil
|
221
|
+
@prev_mode_icon = nil
|
222
|
+
@drop_terminate_spaces = false
|
190
223
|
reset_line
|
191
224
|
end
|
192
225
|
|
@@ -231,6 +264,7 @@ class Reline::LineEditor
|
|
231
264
|
@buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding))
|
232
265
|
@previous_line_index = @line_index
|
233
266
|
@line_index += 1
|
267
|
+
@just_cursor_moving = false
|
234
268
|
end
|
235
269
|
|
236
270
|
private def calculate_height_by_width(width)
|
@@ -271,28 +305,28 @@ class Reline::LineEditor
|
|
271
305
|
end
|
272
306
|
end
|
273
307
|
|
274
|
-
private def calculate_nearest_cursor
|
275
|
-
|
308
|
+
private def calculate_nearest_cursor(line_to_calc = @line, cursor = @cursor, started_from = @started_from, byte_pointer = @byte_pointer, update = true)
|
309
|
+
new_cursor_max = calculate_width(line_to_calc)
|
276
310
|
new_cursor = 0
|
277
311
|
new_byte_pointer = 0
|
278
312
|
height = 1
|
279
313
|
max_width = @screen_size.last
|
280
314
|
if @config.editing_mode_is?(:vi_command)
|
281
|
-
last_byte_size = Reline::Unicode.get_prev_mbchar_size(
|
315
|
+
last_byte_size = Reline::Unicode.get_prev_mbchar_size(line_to_calc, line_to_calc.bytesize)
|
282
316
|
if last_byte_size > 0
|
283
|
-
last_mbchar =
|
317
|
+
last_mbchar = line_to_calc.byteslice(line_to_calc.bytesize - last_byte_size, last_byte_size)
|
284
318
|
last_width = Reline::Unicode.get_mbchar_width(last_mbchar)
|
285
|
-
|
319
|
+
end_of_line_cursor = new_cursor_max - last_width
|
286
320
|
else
|
287
|
-
|
321
|
+
end_of_line_cursor = new_cursor_max
|
288
322
|
end
|
289
323
|
else
|
290
|
-
|
324
|
+
end_of_line_cursor = new_cursor_max
|
291
325
|
end
|
292
|
-
|
326
|
+
line_to_calc.encode(Encoding::UTF_8).grapheme_clusters.each do |gc|
|
293
327
|
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
|
294
328
|
now = new_cursor + mbchar_width
|
295
|
-
if now >
|
329
|
+
if now > end_of_line_cursor or now > cursor
|
296
330
|
break
|
297
331
|
end
|
298
332
|
new_cursor += mbchar_width
|
@@ -301,13 +335,20 @@ class Reline::LineEditor
|
|
301
335
|
end
|
302
336
|
new_byte_pointer += gc.bytesize
|
303
337
|
end
|
304
|
-
|
305
|
-
|
306
|
-
|
338
|
+
new_started_from = height - 1
|
339
|
+
if update
|
340
|
+
@cursor = new_cursor
|
341
|
+
@cursor_max = new_cursor_max
|
342
|
+
@started_from = new_started_from
|
343
|
+
@byte_pointer = new_byte_pointer
|
344
|
+
else
|
345
|
+
[new_cursor, new_cursor_max, new_started_from, new_byte_pointer]
|
346
|
+
end
|
307
347
|
end
|
308
348
|
|
309
349
|
def rerender_all
|
310
350
|
@rerender_all = true
|
351
|
+
process_insert(force: true)
|
311
352
|
rerender
|
312
353
|
end
|
313
354
|
|
@@ -316,168 +357,53 @@ class Reline::LineEditor
|
|
316
357
|
if @menu_info
|
317
358
|
scroll_down(@highest_in_all - @first_line_started_from)
|
318
359
|
@rerender_all = true
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
@output.flush
|
323
|
-
scroll_down(1)
|
324
|
-
end
|
325
|
-
scroll_down(@highest_in_all - 1)
|
326
|
-
move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
|
360
|
+
end
|
361
|
+
if @menu_info
|
362
|
+
show_menu
|
327
363
|
@menu_info = nil
|
328
364
|
end
|
329
365
|
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
330
366
|
if @cleared
|
331
|
-
|
367
|
+
clear_screen_buffer(prompt, prompt_list, prompt_width)
|
332
368
|
@cleared = false
|
333
|
-
back = 0
|
334
|
-
modify_lines(whole_lines).each_with_index do |line, index|
|
335
|
-
if @prompt_proc
|
336
|
-
pr = prompt_list[index]
|
337
|
-
height = render_partial(pr, calculate_width(pr), line, false)
|
338
|
-
else
|
339
|
-
height = render_partial(prompt, prompt_width, line, false)
|
340
|
-
end
|
341
|
-
if index < (@buffer_of_lines.size - 1)
|
342
|
-
move_cursor_down(height)
|
343
|
-
back += height
|
344
|
-
end
|
345
|
-
end
|
346
|
-
move_cursor_up(back)
|
347
|
-
move_cursor_down(@first_line_started_from + @started_from)
|
348
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
349
369
|
return
|
350
370
|
end
|
351
371
|
new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
|
352
372
|
# FIXME: end of logical line sometimes breaks
|
353
|
-
if @
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
Reline::IOGate.erase_after_cursor
|
370
|
-
move_cursor_up(1)
|
371
|
-
end
|
372
|
-
move_cursor_up(all_height - 1)
|
373
|
+
if @add_newline_to_end_of_buffer
|
374
|
+
rerender_added_newline
|
375
|
+
@add_newline_to_end_of_buffer = false
|
376
|
+
else
|
377
|
+
if @just_cursor_moving and not @rerender_all
|
378
|
+
rendered = just_move_cursor
|
379
|
+
@just_cursor_moving = false
|
380
|
+
return
|
381
|
+
elsif @previous_line_index or new_highest_in_this != @highest_in_this
|
382
|
+
rerender_changed_current_line
|
383
|
+
@previous_line_index = nil
|
384
|
+
rendered = true
|
385
|
+
elsif @rerender_all
|
386
|
+
rerender_all_lines
|
387
|
+
@rerender_all = false
|
388
|
+
rendered = true
|
373
389
|
else
|
374
|
-
move_cursor_up(all_height - 1)
|
375
|
-
end
|
376
|
-
@highest_in_all = all_height
|
377
|
-
back = 0
|
378
|
-
modify_lines(new_lines).each_with_index do |line, index|
|
379
|
-
if @prompt_proc
|
380
|
-
prompt = prompt_list[index]
|
381
|
-
prompt_width = calculate_width(prompt, true)
|
382
|
-
end
|
383
|
-
height = render_partial(prompt, prompt_width, line, false)
|
384
|
-
if index < (new_lines.size - 1)
|
385
|
-
scroll_down(1)
|
386
|
-
back += height
|
387
|
-
else
|
388
|
-
back += height - 1
|
389
|
-
end
|
390
390
|
end
|
391
|
-
move_cursor_up(back)
|
392
|
-
if @previous_line_index
|
393
|
-
@buffer_of_lines[@previous_line_index] = @line
|
394
|
-
@line = @buffer_of_lines[@line_index]
|
395
|
-
end
|
396
|
-
@first_line_started_from =
|
397
|
-
if @line_index.zero?
|
398
|
-
0
|
399
|
-
else
|
400
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
401
|
-
end
|
402
|
-
if @prompt_proc
|
403
|
-
prompt = prompt_list[@line_index]
|
404
|
-
prompt_width = calculate_width(prompt, true)
|
405
|
-
end
|
406
|
-
move_cursor_down(@first_line_started_from)
|
407
|
-
calculate_nearest_cursor
|
408
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
409
|
-
move_cursor_down(@started_from)
|
410
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
411
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
412
|
-
@previous_line_index = nil
|
413
|
-
rendered = true
|
414
|
-
elsif @rerender_all
|
415
|
-
move_cursor_up(@first_line_started_from + @started_from)
|
416
|
-
Reline::IOGate.move_cursor_column(0)
|
417
|
-
back = 0
|
418
|
-
new_buffer = whole_lines
|
419
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
420
|
-
new_buffer.each_with_index do |line, index|
|
421
|
-
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
422
|
-
width = prompt_width + calculate_width(line)
|
423
|
-
height = calculate_height_by_width(width)
|
424
|
-
back += height
|
425
|
-
end
|
426
|
-
if back > @highest_in_all
|
427
|
-
scroll_down(back - 1)
|
428
|
-
move_cursor_up(back - 1)
|
429
|
-
elsif back < @highest_in_all
|
430
|
-
scroll_down(back)
|
431
|
-
Reline::IOGate.erase_after_cursor
|
432
|
-
(@highest_in_all - back - 1).times do
|
433
|
-
scroll_down(1)
|
434
|
-
Reline::IOGate.erase_after_cursor
|
435
|
-
end
|
436
|
-
move_cursor_up(@highest_in_all - 1)
|
437
|
-
end
|
438
|
-
modify_lines(new_buffer).each_with_index do |line, index|
|
439
|
-
if @prompt_proc
|
440
|
-
prompt = prompt_list[index]
|
441
|
-
prompt_width = calculate_width(prompt, true)
|
442
|
-
end
|
443
|
-
render_partial(prompt, prompt_width, line, false)
|
444
|
-
if index < (new_buffer.size - 1)
|
445
|
-
move_cursor_down(1)
|
446
|
-
end
|
447
|
-
end
|
448
|
-
move_cursor_up(back - 1)
|
449
|
-
if @prompt_proc
|
450
|
-
prompt = prompt_list[@line_index]
|
451
|
-
prompt_width = calculate_width(prompt, true)
|
452
|
-
end
|
453
|
-
@highest_in_all = back
|
454
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
455
|
-
@first_line_started_from =
|
456
|
-
if @line_index.zero?
|
457
|
-
0
|
458
|
-
else
|
459
|
-
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
460
|
-
end
|
461
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
462
|
-
move_cursor_down(@first_line_started_from + @started_from)
|
463
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
464
|
-
@rerender_all = false
|
465
|
-
rendered = true
|
466
391
|
end
|
467
392
|
line = modify_lines(whole_lines)[@line_index]
|
468
393
|
if @is_multiline
|
469
394
|
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
470
395
|
if finished?
|
471
396
|
# Always rerender on finish because output_modifier_proc may return a different output.
|
472
|
-
render_partial(prompt, prompt_width, line)
|
397
|
+
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
473
398
|
scroll_down(1)
|
474
399
|
Reline::IOGate.move_cursor_column(0)
|
475
400
|
Reline::IOGate.erase_after_cursor
|
476
401
|
elsif not rendered
|
477
|
-
render_partial(prompt, prompt_width, line)
|
402
|
+
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
478
403
|
end
|
404
|
+
@buffer_of_lines[@line_index] = @line
|
479
405
|
else
|
480
|
-
render_partial(prompt, prompt_width, line)
|
406
|
+
render_partial(prompt, prompt_width, line, 0)
|
481
407
|
if finished?
|
482
408
|
scroll_down(1)
|
483
409
|
Reline::IOGate.move_cursor_column(0)
|
@@ -486,8 +412,237 @@ class Reline::LineEditor
|
|
486
412
|
end
|
487
413
|
end
|
488
414
|
|
489
|
-
private def
|
415
|
+
private def calculate_scroll_partial_screen(highest_in_all, cursor_y)
|
416
|
+
if @screen_height < highest_in_all
|
417
|
+
old_scroll_partial_screen = @scroll_partial_screen
|
418
|
+
if cursor_y == 0
|
419
|
+
@scroll_partial_screen = 0
|
420
|
+
elsif cursor_y == (highest_in_all - 1)
|
421
|
+
@scroll_partial_screen = highest_in_all - @screen_height
|
422
|
+
else
|
423
|
+
if @scroll_partial_screen
|
424
|
+
if cursor_y <= @scroll_partial_screen
|
425
|
+
@scroll_partial_screen = cursor_y
|
426
|
+
elsif (@scroll_partial_screen + @screen_height - 1) < cursor_y
|
427
|
+
@scroll_partial_screen = cursor_y - (@screen_height - 1)
|
428
|
+
end
|
429
|
+
else
|
430
|
+
if cursor_y > (@screen_height - 1)
|
431
|
+
@scroll_partial_screen = cursor_y - (@screen_height - 1)
|
432
|
+
else
|
433
|
+
@scroll_partial_screen = 0
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
if @scroll_partial_screen != old_scroll_partial_screen
|
438
|
+
@rerender_all = true
|
439
|
+
end
|
440
|
+
else
|
441
|
+
if @scroll_partial_screen
|
442
|
+
@rerender_all = true
|
443
|
+
end
|
444
|
+
@scroll_partial_screen = nil
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
private def rerender_added_newline
|
449
|
+
scroll_down(1)
|
450
|
+
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
451
|
+
prompt, prompt_width, = check_multiline_prompt(new_lines, prompt)
|
452
|
+
@buffer_of_lines[@previous_line_index] = @line
|
453
|
+
@line = @buffer_of_lines[@line_index]
|
454
|
+
render_partial(prompt, prompt_width, @line, @first_line_started_from + @started_from + 1, with_control: false)
|
455
|
+
@cursor = @cursor_max = calculate_width(@line)
|
456
|
+
@byte_pointer = @line.bytesize
|
457
|
+
@highest_in_all += @highest_in_this
|
458
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
459
|
+
@first_line_started_from += @started_from + 1
|
460
|
+
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
461
|
+
@previous_line_index = nil
|
462
|
+
end
|
463
|
+
|
464
|
+
def just_move_cursor
|
465
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines, prompt)
|
466
|
+
move_cursor_up(@started_from)
|
467
|
+
new_first_line_started_from =
|
468
|
+
if @line_index.zero?
|
469
|
+
0
|
470
|
+
else
|
471
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
472
|
+
end
|
473
|
+
first_line_diff = new_first_line_started_from - @first_line_started_from
|
474
|
+
new_cursor, _, new_started_from, _ = calculate_nearest_cursor(@line, @cursor, @started_from, @byte_pointer, false)
|
475
|
+
new_started_from = calculate_height_by_width(prompt_width + new_cursor) - 1
|
476
|
+
calculate_scroll_partial_screen(@highest_in_all, new_first_line_started_from + new_started_from)
|
477
|
+
@previous_line_index = nil
|
478
|
+
if @rerender_all
|
479
|
+
@line = @buffer_of_lines[@line_index]
|
480
|
+
rerender_all_lines
|
481
|
+
@rerender_all = false
|
482
|
+
true
|
483
|
+
else
|
484
|
+
@line = @buffer_of_lines[@line_index]
|
485
|
+
@first_line_started_from = new_first_line_started_from
|
486
|
+
@started_from = new_started_from
|
487
|
+
@cursor = new_cursor
|
488
|
+
move_cursor_down(first_line_diff + @started_from)
|
489
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
490
|
+
false
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
private def rerender_changed_current_line
|
495
|
+
if @previous_line_index
|
496
|
+
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
497
|
+
else
|
498
|
+
new_lines = whole_lines
|
499
|
+
end
|
500
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
501
|
+
all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
|
502
|
+
diff = all_height - @highest_in_all
|
503
|
+
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
504
|
+
if diff > 0
|
505
|
+
scroll_down(diff)
|
506
|
+
move_cursor_up(all_height - 1)
|
507
|
+
elsif diff < 0
|
508
|
+
(-diff).times do
|
509
|
+
Reline::IOGate.move_cursor_column(0)
|
510
|
+
Reline::IOGate.erase_after_cursor
|
511
|
+
move_cursor_up(1)
|
512
|
+
end
|
513
|
+
move_cursor_up(all_height - 1)
|
514
|
+
else
|
515
|
+
move_cursor_up(all_height - 1)
|
516
|
+
end
|
517
|
+
@highest_in_all = all_height
|
518
|
+
back = render_whole_lines(new_lines, prompt_list || prompt, prompt_width)
|
519
|
+
move_cursor_up(back)
|
520
|
+
if @previous_line_index
|
521
|
+
@buffer_of_lines[@previous_line_index] = @line
|
522
|
+
@line = @buffer_of_lines[@line_index]
|
523
|
+
end
|
524
|
+
@first_line_started_from =
|
525
|
+
if @line_index.zero?
|
526
|
+
0
|
527
|
+
else
|
528
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
529
|
+
end
|
530
|
+
if @prompt_proc
|
531
|
+
prompt = prompt_list[@line_index]
|
532
|
+
prompt_width = calculate_width(prompt, true)
|
533
|
+
end
|
534
|
+
move_cursor_down(@first_line_started_from)
|
535
|
+
calculate_nearest_cursor
|
536
|
+
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
537
|
+
move_cursor_down(@started_from)
|
538
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
539
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
540
|
+
end
|
541
|
+
|
542
|
+
private def rerender_all_lines
|
543
|
+
move_cursor_up(@first_line_started_from + @started_from)
|
544
|
+
Reline::IOGate.move_cursor_column(0)
|
545
|
+
back = 0
|
546
|
+
new_buffer = whole_lines
|
547
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
548
|
+
new_buffer.each_with_index do |line, index|
|
549
|
+
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
550
|
+
width = prompt_width + calculate_width(line)
|
551
|
+
height = calculate_height_by_width(width)
|
552
|
+
back += height
|
553
|
+
end
|
554
|
+
old_highest_in_all = @highest_in_all
|
555
|
+
if @line_index.zero?
|
556
|
+
new_first_line_started_from = 0
|
557
|
+
else
|
558
|
+
new_first_line_started_from = calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
559
|
+
end
|
560
|
+
new_started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
561
|
+
if back > old_highest_in_all
|
562
|
+
scroll_down(back - 1)
|
563
|
+
move_cursor_up(back - 1)
|
564
|
+
elsif back < old_highest_in_all
|
565
|
+
scroll_down(back)
|
566
|
+
Reline::IOGate.erase_after_cursor
|
567
|
+
(old_highest_in_all - back - 1).times do
|
568
|
+
scroll_down(1)
|
569
|
+
Reline::IOGate.erase_after_cursor
|
570
|
+
end
|
571
|
+
move_cursor_up(old_highest_in_all - 1)
|
572
|
+
end
|
573
|
+
calculate_scroll_partial_screen(back, new_first_line_started_from + new_started_from)
|
574
|
+
render_whole_lines(new_buffer, prompt_list || prompt, prompt_width)
|
575
|
+
if @prompt_proc
|
576
|
+
prompt = prompt_list[@line_index]
|
577
|
+
prompt_width = calculate_width(prompt, true)
|
578
|
+
end
|
579
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
580
|
+
@highest_in_all = back
|
581
|
+
@first_line_started_from = new_first_line_started_from
|
582
|
+
@started_from = new_started_from
|
583
|
+
if @scroll_partial_screen
|
584
|
+
Reline::IOGate.move_cursor_up(@screen_height - (@first_line_started_from + @started_from - @scroll_partial_screen) - 1)
|
585
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
586
|
+
else
|
587
|
+
move_cursor_down(@first_line_started_from + @started_from - back + 1)
|
588
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
589
|
+
end
|
590
|
+
end
|
591
|
+
|
592
|
+
private def render_whole_lines(lines, prompt, prompt_width)
|
593
|
+
rendered_height = 0
|
594
|
+
modify_lines(lines).each_with_index do |line, index|
|
595
|
+
if prompt.is_a?(Array)
|
596
|
+
line_prompt = prompt[index]
|
597
|
+
prompt_width = calculate_width(line_prompt, true)
|
598
|
+
else
|
599
|
+
line_prompt = prompt
|
600
|
+
end
|
601
|
+
height = render_partial(line_prompt, prompt_width, line, rendered_height, with_control: false)
|
602
|
+
if index < (lines.size - 1)
|
603
|
+
if @scroll_partial_screen
|
604
|
+
if (@scroll_partial_screen - height) < rendered_height and (@scroll_partial_screen + @screen_height - 1) >= (rendered_height + height)
|
605
|
+
move_cursor_down(1)
|
606
|
+
end
|
607
|
+
else
|
608
|
+
scroll_down(1)
|
609
|
+
end
|
610
|
+
rendered_height += height
|
611
|
+
else
|
612
|
+
rendered_height += height - 1
|
613
|
+
end
|
614
|
+
end
|
615
|
+
rendered_height
|
616
|
+
end
|
617
|
+
|
618
|
+
private def render_partial(prompt, prompt_width, line_to_render, this_started_from, with_control: true)
|
490
619
|
visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
|
620
|
+
cursor_up_from_last_line = 0
|
621
|
+
# TODO: This logic would be sometimes buggy if this logical line isn't the current @line_index.
|
622
|
+
if @scroll_partial_screen
|
623
|
+
last_visual_line = this_started_from + (height - 1)
|
624
|
+
last_screen_line = @scroll_partial_screen + (@screen_height - 1)
|
625
|
+
if (@scroll_partial_screen - this_started_from) >= height
|
626
|
+
# Render nothing because this line is before the screen.
|
627
|
+
visual_lines = []
|
628
|
+
elsif this_started_from > last_screen_line
|
629
|
+
# Render nothing because this line is after the screen.
|
630
|
+
visual_lines = []
|
631
|
+
else
|
632
|
+
deleted_lines_before_screen = []
|
633
|
+
if @scroll_partial_screen > this_started_from and last_visual_line >= @scroll_partial_screen
|
634
|
+
# A part of visual lines are before the screen.
|
635
|
+
deleted_lines_before_screen = visual_lines.shift((@scroll_partial_screen - this_started_from) * 2)
|
636
|
+
deleted_lines_before_screen.compact!
|
637
|
+
end
|
638
|
+
if this_started_from <= last_screen_line and last_screen_line < last_visual_line
|
639
|
+
# A part of visual lines are after the screen.
|
640
|
+
visual_lines.pop((last_visual_line - last_screen_line) * 2)
|
641
|
+
end
|
642
|
+
move_cursor_up(deleted_lines_before_screen.size - @started_from)
|
643
|
+
cursor_up_from_last_line = @started_from - deleted_lines_before_screen.size
|
644
|
+
end
|
645
|
+
end
|
491
646
|
if with_control
|
492
647
|
if height > @highest_in_this
|
493
648
|
diff = height - @highest_in_this
|
@@ -501,10 +656,14 @@ class Reline::LineEditor
|
|
501
656
|
@highest_in_this = height
|
502
657
|
end
|
503
658
|
move_cursor_up(@started_from)
|
659
|
+
cursor_up_from_last_line = height - 1 - @started_from
|
504
660
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
505
661
|
end
|
506
|
-
Reline::
|
662
|
+
if Reline::Unicode::CSI_REGEXP.match?(prompt + line_to_render)
|
663
|
+
@output.write "\e[0m" # clear character decorations
|
664
|
+
end
|
507
665
|
visual_lines.each_with_index do |line, index|
|
666
|
+
Reline::IOGate.move_cursor_column(0)
|
508
667
|
if line.nil?
|
509
668
|
if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
510
669
|
# reaches the end of line
|
@@ -536,15 +695,18 @@ class Reline::LineEditor
|
|
536
695
|
@pre_input_hook&.call
|
537
696
|
end
|
538
697
|
end
|
539
|
-
|
540
|
-
|
698
|
+
unless visual_lines.empty?
|
699
|
+
Reline::IOGate.erase_after_cursor
|
700
|
+
Reline::IOGate.move_cursor_column(0)
|
701
|
+
end
|
541
702
|
if with_control
|
542
703
|
# Just after rendring, so the cursor is on the last line.
|
543
704
|
if finished?
|
544
705
|
Reline::IOGate.move_cursor_column(0)
|
545
706
|
else
|
546
707
|
# Moves up from bottom of lines to the cursor position.
|
547
|
-
move_cursor_up(
|
708
|
+
move_cursor_up(cursor_up_from_last_line)
|
709
|
+
# This logic is buggy if a fullwidth char is wrapped because there is only one halfwidth at end of a line.
|
548
710
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
549
711
|
end
|
550
712
|
end
|
@@ -561,6 +723,39 @@ class Reline::LineEditor
|
|
561
723
|
end
|
562
724
|
end
|
563
725
|
|
726
|
+
private def show_menu
|
727
|
+
scroll_down(@highest_in_all - @first_line_started_from)
|
728
|
+
@rerender_all = true
|
729
|
+
@menu_info.list.sort!.each do |item|
|
730
|
+
Reline::IOGate.move_cursor_column(0)
|
731
|
+
@output.write item
|
732
|
+
@output.flush
|
733
|
+
scroll_down(1)
|
734
|
+
end
|
735
|
+
scroll_down(@highest_in_all - 1)
|
736
|
+
move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
|
737
|
+
end
|
738
|
+
|
739
|
+
private def clear_screen_buffer(prompt, prompt_list, prompt_width)
|
740
|
+
Reline::IOGate.clear_screen
|
741
|
+
back = 0
|
742
|
+
modify_lines(whole_lines).each_with_index do |line, index|
|
743
|
+
if @prompt_proc
|
744
|
+
pr = prompt_list[index]
|
745
|
+
height = render_partial(pr, calculate_width(pr), line, back, with_control: false)
|
746
|
+
else
|
747
|
+
height = render_partial(prompt, prompt_width, line, back, with_control: false)
|
748
|
+
end
|
749
|
+
if index < (@buffer_of_lines.size - 1)
|
750
|
+
move_cursor_down(height)
|
751
|
+
back += height
|
752
|
+
end
|
753
|
+
end
|
754
|
+
move_cursor_up(back)
|
755
|
+
move_cursor_down(@first_line_started_from + @started_from)
|
756
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
757
|
+
end
|
758
|
+
|
564
759
|
def editing_mode
|
565
760
|
@config.editing_mode
|
566
761
|
end
|
@@ -697,7 +892,8 @@ class Reline::LineEditor
|
|
697
892
|
if @waiting_operator_proc
|
698
893
|
if VI_MOTIONS.include?(method_symbol)
|
699
894
|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
|
700
|
-
|
895
|
+
@vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg > 1
|
896
|
+
block.(true)
|
701
897
|
unless @waiting_proc
|
702
898
|
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
|
703
899
|
@cursor, @byte_pointer = old_cursor, old_byte_pointer
|
@@ -705,27 +901,56 @@ class Reline::LineEditor
|
|
705
901
|
else
|
706
902
|
old_waiting_proc = @waiting_proc
|
707
903
|
old_waiting_operator_proc = @waiting_operator_proc
|
904
|
+
current_waiting_operator_proc = @waiting_operator_proc
|
708
905
|
@waiting_proc = proc { |k|
|
709
906
|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
|
710
907
|
old_waiting_proc.(k)
|
711
908
|
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
|
712
909
|
@cursor, @byte_pointer = old_cursor, old_byte_pointer
|
713
|
-
|
910
|
+
current_waiting_operator_proc.(cursor_diff, byte_pointer_diff)
|
714
911
|
@waiting_operator_proc = old_waiting_operator_proc
|
715
912
|
}
|
716
913
|
end
|
717
914
|
else
|
718
915
|
# Ignores operator when not motion is given.
|
719
|
-
block.()
|
916
|
+
block.(false)
|
720
917
|
end
|
721
918
|
@waiting_operator_proc = nil
|
919
|
+
@waiting_operator_vi_arg = nil
|
920
|
+
@vi_arg = nil
|
722
921
|
else
|
723
|
-
block.()
|
922
|
+
block.(false)
|
724
923
|
end
|
725
924
|
end
|
726
925
|
|
727
926
|
private def argumentable?(method_obj)
|
728
|
-
method_obj and method_obj.parameters.
|
927
|
+
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :arg }
|
928
|
+
end
|
929
|
+
|
930
|
+
private def inclusive?(method_obj)
|
931
|
+
# If a motion method with the keyword argument "inclusive" follows the
|
932
|
+
# operator, it must contain the character at the cursor position.
|
933
|
+
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :inclusive }
|
934
|
+
end
|
935
|
+
|
936
|
+
def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
|
937
|
+
if @config.editing_mode_is?(:emacs, :vi_insert) and @waiting_proc.nil? and @waiting_operator_proc.nil?
|
938
|
+
not_insertion = method_symbol != :ed_insert
|
939
|
+
process_insert(force: not_insertion)
|
940
|
+
end
|
941
|
+
if @vi_arg and argumentable?(method_obj)
|
942
|
+
if with_operator and inclusive?(method_obj)
|
943
|
+
method_obj.(key, arg: @vi_arg, inclusive: true)
|
944
|
+
else
|
945
|
+
method_obj.(key, arg: @vi_arg)
|
946
|
+
end
|
947
|
+
else
|
948
|
+
if with_operator and inclusive?(method_obj)
|
949
|
+
method_obj.(key, inclusive: true)
|
950
|
+
else
|
951
|
+
method_obj.(key)
|
952
|
+
end
|
953
|
+
end
|
729
954
|
end
|
730
955
|
|
731
956
|
private def process_key(key, method_symbol)
|
@@ -736,11 +961,11 @@ class Reline::LineEditor
|
|
736
961
|
end
|
737
962
|
if method_symbol and key.is_a?(Symbol)
|
738
963
|
if @vi_arg and argumentable?(method_obj)
|
739
|
-
run_for_operators(key, method_symbol) do
|
740
|
-
method_obj
|
964
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
965
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
741
966
|
end
|
742
967
|
else
|
743
|
-
method_obj
|
968
|
+
wrap_method_call(method_symbol, method_obj, key) if method_obj
|
744
969
|
end
|
745
970
|
@kill_ring.process
|
746
971
|
@vi_arg = nil
|
@@ -749,15 +974,15 @@ class Reline::LineEditor
|
|
749
974
|
ed_argument_digit(key)
|
750
975
|
else
|
751
976
|
if argumentable?(method_obj)
|
752
|
-
run_for_operators(key, method_symbol) do
|
753
|
-
method_obj
|
977
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
978
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
754
979
|
end
|
755
980
|
elsif @waiting_proc
|
756
981
|
@waiting_proc.(key)
|
757
982
|
elsif method_obj
|
758
|
-
method_obj
|
983
|
+
wrap_method_call(method_symbol, method_obj, key)
|
759
984
|
else
|
760
|
-
ed_insert(key)
|
985
|
+
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
761
986
|
end
|
762
987
|
@kill_ring.process
|
763
988
|
@vi_arg = nil
|
@@ -767,15 +992,15 @@ class Reline::LineEditor
|
|
767
992
|
@kill_ring.process
|
768
993
|
elsif method_obj
|
769
994
|
if method_symbol == :ed_argument_digit
|
770
|
-
method_obj
|
995
|
+
wrap_method_call(method_symbol, method_obj, key)
|
771
996
|
else
|
772
|
-
run_for_operators(key, method_symbol) do
|
773
|
-
method_obj
|
997
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
998
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
774
999
|
end
|
775
1000
|
end
|
776
1001
|
@kill_ring.process
|
777
1002
|
else
|
778
|
-
ed_insert(key)
|
1003
|
+
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
779
1004
|
end
|
780
1005
|
end
|
781
1006
|
|
@@ -818,6 +1043,7 @@ class Reline::LineEditor
|
|
818
1043
|
end
|
819
1044
|
|
820
1045
|
def input_key(key)
|
1046
|
+
@just_cursor_moving = nil
|
821
1047
|
if key.char.nil?
|
822
1048
|
if @first_char
|
823
1049
|
@line = nil
|
@@ -825,6 +1051,7 @@ class Reline::LineEditor
|
|
825
1051
|
finish
|
826
1052
|
return
|
827
1053
|
end
|
1054
|
+
old_line = @line.dup
|
828
1055
|
@first_char = false
|
829
1056
|
completion_occurs = false
|
830
1057
|
if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
|
@@ -832,6 +1059,7 @@ class Reline::LineEditor
|
|
832
1059
|
result = call_completion_proc
|
833
1060
|
if result.is_a?(Array)
|
834
1061
|
completion_occurs = true
|
1062
|
+
process_insert
|
835
1063
|
complete(result)
|
836
1064
|
end
|
837
1065
|
end
|
@@ -840,6 +1068,7 @@ class Reline::LineEditor
|
|
840
1068
|
result = call_completion_proc
|
841
1069
|
if result.is_a?(Array)
|
842
1070
|
completion_occurs = true
|
1071
|
+
process_insert
|
843
1072
|
move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
|
844
1073
|
end
|
845
1074
|
end
|
@@ -851,6 +1080,17 @@ class Reline::LineEditor
|
|
851
1080
|
unless completion_occurs
|
852
1081
|
@completion_state = CompletionState::NORMAL
|
853
1082
|
end
|
1083
|
+
if not Reline::IOGate.in_pasting? and @just_cursor_moving.nil?
|
1084
|
+
if @previous_line_index and @buffer_of_lines[@previous_line_index] == @line
|
1085
|
+
@just_cursor_moving = true
|
1086
|
+
elsif @previous_line_index.nil? and @buffer_of_lines[@line_index] == @line and old_line == @line
|
1087
|
+
@just_cursor_moving = true
|
1088
|
+
else
|
1089
|
+
@just_cursor_moving = false
|
1090
|
+
end
|
1091
|
+
else
|
1092
|
+
@just_cursor_moving = false
|
1093
|
+
end
|
854
1094
|
if @is_multiline and @auto_indent_proc and not simplified_rendering?
|
855
1095
|
process_auto_indent
|
856
1096
|
end
|
@@ -1082,6 +1322,9 @@ class Reline::LineEditor
|
|
1082
1322
|
|
1083
1323
|
private def key_newline(key)
|
1084
1324
|
if @is_multiline
|
1325
|
+
if (@buffer_of_lines.size - 1) == @line_index and @line.bytesize == @byte_pointer
|
1326
|
+
@add_newline_to_end_of_buffer = true
|
1327
|
+
end
|
1085
1328
|
next_line = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
|
1086
1329
|
cursor_line = @line.byteslice(0, @byte_pointer)
|
1087
1330
|
insert_new_line(cursor_line, next_line)
|
@@ -1092,38 +1335,62 @@ class Reline::LineEditor
|
|
1092
1335
|
|
1093
1336
|
private def ed_unassigned(key) end # do nothing
|
1094
1337
|
|
1338
|
+
private def process_insert(force: false)
|
1339
|
+
return if @continuous_insertion_buffer.empty? or (Reline::IOGate.in_pasting? and not force)
|
1340
|
+
width = Reline::Unicode.calculate_width(@continuous_insertion_buffer)
|
1341
|
+
bytesize = @continuous_insertion_buffer.bytesize
|
1342
|
+
if @cursor == @cursor_max
|
1343
|
+
@line += @continuous_insertion_buffer
|
1344
|
+
else
|
1345
|
+
@line = byteinsert(@line, @byte_pointer, @continuous_insertion_buffer)
|
1346
|
+
end
|
1347
|
+
@byte_pointer += bytesize
|
1348
|
+
@cursor += width
|
1349
|
+
@cursor_max += width
|
1350
|
+
@continuous_insertion_buffer.clear
|
1351
|
+
end
|
1352
|
+
|
1095
1353
|
private def ed_insert(key)
|
1354
|
+
str = nil
|
1355
|
+
width = nil
|
1356
|
+
bytesize = nil
|
1096
1357
|
if key.instance_of?(String)
|
1097
1358
|
begin
|
1098
1359
|
key.encode(Encoding::UTF_8)
|
1099
1360
|
rescue Encoding::UndefinedConversionError
|
1100
1361
|
return
|
1101
1362
|
end
|
1102
|
-
|
1103
|
-
|
1104
|
-
@line += key
|
1105
|
-
else
|
1106
|
-
@line = byteinsert(@line, @byte_pointer, key)
|
1107
|
-
end
|
1108
|
-
@byte_pointer += key.bytesize
|
1109
|
-
@cursor += width
|
1110
|
-
@cursor_max += width
|
1363
|
+
str = key
|
1364
|
+
bytesize = key.bytesize
|
1111
1365
|
else
|
1112
1366
|
begin
|
1113
1367
|
key.chr.encode(Encoding::UTF_8)
|
1114
1368
|
rescue Encoding::UndefinedConversionError
|
1115
1369
|
return
|
1116
1370
|
end
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1371
|
+
str = key.chr
|
1372
|
+
bytesize = 1
|
1373
|
+
end
|
1374
|
+
if Reline::IOGate.in_pasting?
|
1375
|
+
@continuous_insertion_buffer << str
|
1376
|
+
return
|
1377
|
+
elsif not @continuous_insertion_buffer.empty?
|
1378
|
+
process_insert
|
1379
|
+
end
|
1380
|
+
width = Reline::Unicode.get_mbchar_width(str)
|
1381
|
+
if @cursor == @cursor_max
|
1382
|
+
@line += str
|
1383
|
+
else
|
1384
|
+
@line = byteinsert(@line, @byte_pointer, str)
|
1126
1385
|
end
|
1386
|
+
last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
1387
|
+
@byte_pointer += bytesize
|
1388
|
+
last_mbchar = @line.byteslice((@byte_pointer - bytesize - last_byte_size), last_byte_size)
|
1389
|
+
if last_byte_size != 0 and (last_mbchar + str).grapheme_clusters.size == 1
|
1390
|
+
width = 0
|
1391
|
+
end
|
1392
|
+
@cursor += width
|
1393
|
+
@cursor_max += width
|
1127
1394
|
end
|
1128
1395
|
alias_method :ed_digit, :ed_insert
|
1129
1396
|
alias_method :self_insert, :ed_insert
|
@@ -1180,6 +1447,7 @@ class Reline::LineEditor
|
|
1180
1447
|
arg -= 1
|
1181
1448
|
ed_prev_char(key, arg: arg) if arg > 0
|
1182
1449
|
end
|
1450
|
+
alias_method :backward_char, :ed_prev_char
|
1183
1451
|
|
1184
1452
|
private def vi_first_print(key)
|
1185
1453
|
@byte_pointer, @cursor = Reline::Unicode.vi_first_print(@line)
|
@@ -1332,9 +1600,11 @@ class Reline::LineEditor
|
|
1332
1600
|
searcher = generate_searcher
|
1333
1601
|
searcher.resume(key)
|
1334
1602
|
@searching_prompt = "(reverse-i-search)`': "
|
1603
|
+
termination_keys = ["\C-j".ord]
|
1604
|
+
termination_keys.concat(@config.isearch_terminators&.chars&.map(&:ord)) if @config.isearch_terminators
|
1335
1605
|
@waiting_proc = ->(k) {
|
1336
1606
|
case k
|
1337
|
-
when
|
1607
|
+
when *termination_keys
|
1338
1608
|
if @history_pointer
|
1339
1609
|
buffer = Reline::HISTORY[@history_pointer]
|
1340
1610
|
else
|
@@ -1353,6 +1623,8 @@ class Reline::LineEditor
|
|
1353
1623
|
@waiting_proc = nil
|
1354
1624
|
@cursor_max = calculate_width(@line)
|
1355
1625
|
@cursor = @byte_pointer = 0
|
1626
|
+
@rerender_all = true
|
1627
|
+
@cached_prompt_list = nil
|
1356
1628
|
searcher.resume(-1)
|
1357
1629
|
when "\C-g".ord
|
1358
1630
|
if @is_multiline
|
@@ -1396,6 +1668,8 @@ class Reline::LineEditor
|
|
1396
1668
|
@waiting_proc = nil
|
1397
1669
|
@cursor_max = calculate_width(@line)
|
1398
1670
|
@cursor = @byte_pointer = 0
|
1671
|
+
@rerender_all = true
|
1672
|
+
@cached_prompt_list = nil
|
1399
1673
|
searcher.resume(-1)
|
1400
1674
|
end
|
1401
1675
|
end
|
@@ -1609,6 +1883,7 @@ class Reline::LineEditor
|
|
1609
1883
|
end
|
1610
1884
|
|
1611
1885
|
private def ed_newline(key)
|
1886
|
+
process_insert(force: true)
|
1612
1887
|
if @is_multiline
|
1613
1888
|
if @config.editing_mode_is?(:vi_command)
|
1614
1889
|
if @line_index < (@buffer_of_lines.size - 1)
|
@@ -1687,6 +1962,7 @@ class Reline::LineEditor
|
|
1687
1962
|
@cursor = 0
|
1688
1963
|
end
|
1689
1964
|
end
|
1965
|
+
alias_method :kill_line, :em_kill_line
|
1690
1966
|
|
1691
1967
|
private def em_delete(key)
|
1692
1968
|
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
|
@@ -1737,6 +2013,7 @@ class Reline::LineEditor
|
|
1737
2013
|
@byte_pointer += yanked.bytesize
|
1738
2014
|
end
|
1739
2015
|
end
|
2016
|
+
alias_method :yank, :em_yank
|
1740
2017
|
|
1741
2018
|
private def em_yank_pop(key)
|
1742
2019
|
yanked, prev_yank = @kill_ring.yank_pop
|
@@ -1753,6 +2030,7 @@ class Reline::LineEditor
|
|
1753
2030
|
@byte_pointer += yanked.bytesize
|
1754
2031
|
end
|
1755
2032
|
end
|
2033
|
+
alias_method :yank_pop, :em_yank_pop
|
1756
2034
|
|
1757
2035
|
private def ed_clear_screen(key)
|
1758
2036
|
@cleared = true
|
@@ -1883,9 +2161,10 @@ class Reline::LineEditor
|
|
1883
2161
|
@byte_pointer -= byte_size
|
1884
2162
|
@cursor -= width
|
1885
2163
|
@cursor_max -= width
|
1886
|
-
@kill_ring.append(deleted)
|
2164
|
+
@kill_ring.append(deleted, true)
|
1887
2165
|
end
|
1888
2166
|
end
|
2167
|
+
alias_method :unix_word_rubout, :em_kill_region
|
1889
2168
|
|
1890
2169
|
private def copy_for_vi(text)
|
1891
2170
|
if @config.editing_mode_is?(:vi_insert) or @config.editing_mode_is?(:vi_command)
|
@@ -1906,11 +2185,11 @@ class Reline::LineEditor
|
|
1906
2185
|
ed_prev_char(key)
|
1907
2186
|
@config.editing_mode = :vi_command
|
1908
2187
|
end
|
1909
|
-
alias_method :
|
2188
|
+
alias_method :vi_movement_mode, :vi_command_mode
|
1910
2189
|
|
1911
2190
|
private def vi_next_word(key, arg: 1)
|
1912
2191
|
if @line.bytesize > @byte_pointer
|
1913
|
-
byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer)
|
2192
|
+
byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer, @drop_terminate_spaces)
|
1914
2193
|
@byte_pointer += byte_size
|
1915
2194
|
@cursor += width
|
1916
2195
|
end
|
@@ -1928,13 +2207,22 @@ class Reline::LineEditor
|
|
1928
2207
|
vi_prev_word(key, arg: arg) if arg > 0
|
1929
2208
|
end
|
1930
2209
|
|
1931
|
-
private def vi_end_word(key, arg: 1)
|
2210
|
+
private def vi_end_word(key, arg: 1, inclusive: false)
|
1932
2211
|
if @line.bytesize > @byte_pointer
|
1933
2212
|
byte_size, width = Reline::Unicode.vi_forward_end_word(@line, @byte_pointer)
|
1934
2213
|
@byte_pointer += byte_size
|
1935
2214
|
@cursor += width
|
1936
2215
|
end
|
1937
2216
|
arg -= 1
|
2217
|
+
if inclusive and arg.zero?
|
2218
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2219
|
+
if byte_size > 0
|
2220
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
2221
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
2222
|
+
@byte_pointer += byte_size
|
2223
|
+
@cursor += width
|
2224
|
+
end
|
2225
|
+
end
|
1938
2226
|
vi_end_word(key, arg: arg) if arg > 0
|
1939
2227
|
end
|
1940
2228
|
|
@@ -1958,13 +2246,22 @@ class Reline::LineEditor
|
|
1958
2246
|
vi_prev_big_word(key, arg: arg) if arg > 0
|
1959
2247
|
end
|
1960
2248
|
|
1961
|
-
private def vi_end_big_word(key, arg: 1)
|
2249
|
+
private def vi_end_big_word(key, arg: 1, inclusive: false)
|
1962
2250
|
if @line.bytesize > @byte_pointer
|
1963
2251
|
byte_size, width = Reline::Unicode.vi_big_forward_end_word(@line, @byte_pointer)
|
1964
2252
|
@byte_pointer += byte_size
|
1965
2253
|
@cursor += width
|
1966
2254
|
end
|
1967
2255
|
arg -= 1
|
2256
|
+
if inclusive and arg.zero?
|
2257
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2258
|
+
if byte_size > 0
|
2259
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
2260
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
2261
|
+
@byte_pointer += byte_size
|
2262
|
+
@cursor += width
|
2263
|
+
end
|
2264
|
+
end
|
1968
2265
|
vi_end_big_word(key, arg: arg) if arg > 0
|
1969
2266
|
end
|
1970
2267
|
|
@@ -2019,7 +2316,8 @@ class Reline::LineEditor
|
|
2019
2316
|
@cursor = 0
|
2020
2317
|
end
|
2021
2318
|
|
2022
|
-
private def vi_change_meta(key)
|
2319
|
+
private def vi_change_meta(key, arg: 1)
|
2320
|
+
@drop_terminate_spaces = true
|
2023
2321
|
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2024
2322
|
if byte_pointer_diff > 0
|
2025
2323
|
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
@@ -2031,10 +2329,12 @@ class Reline::LineEditor
|
|
2031
2329
|
@cursor_max -= cursor_diff.abs
|
2032
2330
|
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
2033
2331
|
@config.editing_mode = :vi_insert
|
2332
|
+
@drop_terminate_spaces = false
|
2034
2333
|
}
|
2334
|
+
@waiting_operator_vi_arg = arg
|
2035
2335
|
end
|
2036
2336
|
|
2037
|
-
private def vi_delete_meta(key)
|
2337
|
+
private def vi_delete_meta(key, arg: 1)
|
2038
2338
|
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2039
2339
|
if byte_pointer_diff > 0
|
2040
2340
|
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
@@ -2046,9 +2346,19 @@ class Reline::LineEditor
|
|
2046
2346
|
@cursor_max -= cursor_diff.abs
|
2047
2347
|
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
2048
2348
|
}
|
2349
|
+
@waiting_operator_vi_arg = arg
|
2049
2350
|
end
|
2050
2351
|
|
2051
|
-
private def vi_yank(key)
|
2352
|
+
private def vi_yank(key, arg: 1)
|
2353
|
+
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2354
|
+
if byte_pointer_diff > 0
|
2355
|
+
cut = @line.byteslice(@byte_pointer, byte_pointer_diff)
|
2356
|
+
elsif byte_pointer_diff < 0
|
2357
|
+
cut = @line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
2358
|
+
end
|
2359
|
+
copy_for_vi(cut)
|
2360
|
+
}
|
2361
|
+
@waiting_operator_vi_arg = arg
|
2052
2362
|
end
|
2053
2363
|
|
2054
2364
|
private def vi_list_or_eof(key)
|
@@ -2171,7 +2481,7 @@ class Reline::LineEditor
|
|
2171
2481
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2172
2482
|
before = @line.byteslice(0, @byte_pointer)
|
2173
2483
|
remaining_point = @byte_pointer + byte_size
|
2174
|
-
after = @line.byteslice(remaining_point, @line.
|
2484
|
+
after = @line.byteslice(remaining_point, @line.bytesize - remaining_point)
|
2175
2485
|
@line = before + k.chr + after
|
2176
2486
|
@cursor_max = calculate_width(@line)
|
2177
2487
|
@waiting_proc = nil
|
@@ -2182,7 +2492,7 @@ class Reline::LineEditor
|
|
2182
2492
|
end
|
2183
2493
|
before = @line.byteslice(0, @byte_pointer)
|
2184
2494
|
remaining_point = @byte_pointer + byte_size
|
2185
|
-
after = @line.byteslice(remaining_point, @line.
|
2495
|
+
after = @line.byteslice(remaining_point, @line.bytesize - remaining_point)
|
2186
2496
|
replaced = k.chr * arg
|
2187
2497
|
@line = before + replaced + after
|
2188
2498
|
@byte_pointer += replaced.bytesize
|
@@ -2193,15 +2503,15 @@ class Reline::LineEditor
|
|
2193
2503
|
}
|
2194
2504
|
end
|
2195
2505
|
|
2196
|
-
private def vi_next_char(key, arg: 1)
|
2197
|
-
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg) }
|
2506
|
+
private def vi_next_char(key, arg: 1, inclusive: false)
|
2507
|
+
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, inclusive: inclusive) }
|
2198
2508
|
end
|
2199
2509
|
|
2200
|
-
private def vi_to_next_char(key, arg: 1)
|
2201
|
-
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, true) }
|
2510
|
+
private def vi_to_next_char(key, arg: 1, inclusive: false)
|
2511
|
+
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, need_prev_char: true, inclusive: inclusive) }
|
2202
2512
|
end
|
2203
2513
|
|
2204
|
-
private def search_next_char(key, arg, need_prev_char
|
2514
|
+
private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
|
2205
2515
|
if key.instance_of?(String)
|
2206
2516
|
inputed_char = key
|
2207
2517
|
else
|
@@ -2238,6 +2548,15 @@ class Reline::LineEditor
|
|
2238
2548
|
@byte_pointer += byte_size
|
2239
2549
|
@cursor += width
|
2240
2550
|
end
|
2551
|
+
if inclusive
|
2552
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2553
|
+
if byte_size > 0
|
2554
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
2555
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
2556
|
+
@byte_pointer += byte_size
|
2557
|
+
@cursor += width
|
2558
|
+
end
|
2559
|
+
end
|
2241
2560
|
@waiting_proc = nil
|
2242
2561
|
end
|
2243
2562
|
|
@@ -2309,6 +2628,7 @@ class Reline::LineEditor
|
|
2309
2628
|
alias_method :set_mark, :em_set_mark
|
2310
2629
|
|
2311
2630
|
private def em_exchange_mark(key)
|
2631
|
+
return unless @mark_pointer
|
2312
2632
|
new_pointer = [@byte_pointer, @line_index]
|
2313
2633
|
@previous_line_index = @line_index
|
2314
2634
|
@byte_pointer, @line_index = @mark_pointer
|