reline 0.1.7 → 0.2.1
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 +4 -0
- data/lib/reline.rb +23 -5
- data/lib/reline/ansi.rb +44 -3
- data/lib/reline/config.rb +9 -7
- 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 +518 -218
- data/lib/reline/unicode.rb +38 -18
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +8 -1
- data/license_of_rb-readline +25 -0
- metadata +4 -5
- data/lib/reline/line_editor.rb.orig +0 -2384
- data/lib/reline/line_editor.rb.rej +0 -81
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ad1ae315ec2e7717822c6dd2cde886639d5becb7ccf552a64921e12b8452107
|
4
|
+
data.tar.gz: 191aa399008fb54a8ee3048f5ef96b260d1937fba8203977e15321f45a3fd962
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea45bb1e825b2c17a9d0965a5bfc33e0a42c9c6eb8a29ae3f0500b6de197dc124ba6a99c8e41b6692fa04d3086f7ba1dbb359d9ff0c4a53520246a598719d2f2
|
7
|
+
data.tar.gz: 6e8780a82c7e59aa9c1f0d6283b7c5f09ea5bb66dda6209aa85cef3bec3ccf1079d4a3cc113b33403e6ee274403f1f0c021f892c871123a255985965b017d798
|
data/README.md
CHANGED
@@ -11,3 +11,7 @@ Reline is compatible with the API of Ruby's stdlib 'readline', GNU Readline and
|
|
11
11
|
## License
|
12
12
|
|
13
13
|
The gem is available as open source under the terms of the [Ruby License](https://www.ruby-lang.org/en/about/license.txt).
|
14
|
+
|
15
|
+
## Acknowledgments for [rb-readline](https://github.com/ConnorAtherton/rb-readline)
|
16
|
+
|
17
|
+
In developing Reline, we have used some of the rb-readline implementation, so this library includes [copyright notice, list of conditions and the disclaimer](license_of_rb-readline) under the 3-Clause BSD License. Reline would never have been developed without rb-readline. Thank you for the tremendous accomplishments.
|
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
@@ -34,9 +34,11 @@ class Reline::Config
|
|
34
34
|
show-all-if-unmodified
|
35
35
|
visible-stats
|
36
36
|
show-mode-in-prompt
|
37
|
-
vi-cmd-mode-
|
38
|
-
vi-ins-mode-
|
37
|
+
vi-cmd-mode-string
|
38
|
+
vi-ins-mode-string
|
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|
|
@@ -54,8 +56,8 @@ class Reline::Config
|
|
54
56
|
@key_actors[:emacs] = Reline::KeyActor::Emacs.new
|
55
57
|
@key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new
|
56
58
|
@key_actors[:vi_command] = Reline::KeyActor::ViCommand.new
|
57
|
-
@
|
58
|
-
@
|
59
|
+
@vi_cmd_mode_string = '(cmd)'
|
60
|
+
@vi_ins_mode_string = '(ins)'
|
59
61
|
@emacs_mode_string = '@'
|
60
62
|
# https://tiswww.case.edu/php/chet/readline/readline.html#IDX25
|
61
63
|
@history_size = -1 # unlimited
|
@@ -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'
|
@@ -268,9 +270,9 @@ class Reline::Config
|
|
268
270
|
@show_mode_in_prompt = false
|
269
271
|
end
|
270
272
|
when 'vi-cmd-mode-string'
|
271
|
-
@
|
273
|
+
@vi_cmd_mode_string = retrieve_string(value)
|
272
274
|
when 'vi-ins-mode-string'
|
273
|
-
@
|
275
|
+
@vi_ins_mode_string = retrieve_string(value)
|
274
276
|
when 'emacs-mode-string'
|
275
277
|
@emacs_mode_string = retrieve_string(value)
|
276
278
|
when *VARIABLE_NAMES then
|
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_string
|
72
|
+
mode_string = nil
|
73
|
+
if @config.show_mode_in_prompt
|
74
|
+
if @config.editing_mode_is?(:vi_command)
|
75
|
+
mode_string = @config.vi_cmd_mode_string
|
76
|
+
elsif @config.editing_mode_is?(:vi_insert)
|
77
|
+
mode_string = @config.vi_ins_mode_string
|
78
|
+
elsif @config.editing_mode_is?(:emacs)
|
79
|
+
mode_string = @config.emacs_mode_string
|
80
|
+
else
|
81
|
+
mode_string = '?'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
if mode_string != @prev_mode_string
|
85
|
+
@rerender_all = true
|
86
|
+
end
|
87
|
+
@prev_mode_string = mode_string
|
88
|
+
mode_string
|
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,44 @@ class Reline::LineEditor
|
|
74
98
|
else
|
75
99
|
prompt = @prompt
|
76
100
|
end
|
77
|
-
|
101
|
+
if simplified_rendering?
|
102
|
+
mode_string = check_mode_string
|
103
|
+
prompt = mode_string + prompt if mode_string
|
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
|
+
use_cached_prompt_list = false if @rerender_all
|
116
|
+
if use_cached_prompt_list
|
117
|
+
prompt_list = @cached_prompt_list
|
118
|
+
else
|
119
|
+
prompt_list = @cached_prompt_list = @prompt_proc.(buffer)
|
120
|
+
@prompt_cache_time = Time.now.to_f
|
121
|
+
end
|
122
|
+
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
123
|
+
mode_string = check_mode_string
|
124
|
+
prompt_list = prompt_list.map{ |pr| mode_string + pr } if mode_string
|
93
125
|
prompt = prompt_list[@line_index]
|
126
|
+
prompt = prompt_list[0] if prompt.nil?
|
127
|
+
prompt = prompt_list.last if prompt.nil?
|
128
|
+
if buffer.size > prompt_list.size
|
129
|
+
(buffer.size - prompt_list.size).times do
|
130
|
+
prompt_list << prompt_list.last
|
131
|
+
end
|
132
|
+
end
|
94
133
|
prompt_width = calculate_width(prompt, true)
|
95
134
|
[prompt, prompt_width, prompt_list]
|
96
135
|
else
|
136
|
+
mode_string = check_mode_string
|
137
|
+
prompt = mode_string + prompt if mode_string
|
97
138
|
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
139
|
[prompt, prompt_width, nil]
|
111
140
|
end
|
112
141
|
end
|
@@ -114,6 +143,7 @@ class Reline::LineEditor
|
|
114
143
|
def reset(prompt = '', encoding:)
|
115
144
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
116
145
|
@screen_size = Reline::IOGate.get_screen_size
|
146
|
+
@screen_height = @screen_size.first
|
117
147
|
reset_variables(prompt, encoding: encoding)
|
118
148
|
@old_trap = Signal.trap('SIGINT') {
|
119
149
|
@old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
|
@@ -123,6 +153,7 @@ class Reline::LineEditor
|
|
123
153
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
124
154
|
old_screen_size = @screen_size
|
125
155
|
@screen_size = Reline::IOGate.get_screen_size
|
156
|
+
@screen_height = @screen_size.first
|
126
157
|
if old_screen_size.last < @screen_size.last # columns increase
|
127
158
|
@rerender_all = true
|
128
159
|
rerender
|
@@ -174,11 +205,12 @@ class Reline::LineEditor
|
|
174
205
|
@cleared = false
|
175
206
|
@rerender_all = false
|
176
207
|
@history_pointer = nil
|
177
|
-
@kill_ring
|
208
|
+
@kill_ring ||= Reline::KillRing.new
|
178
209
|
@vi_clipboard = ''
|
179
210
|
@vi_arg = nil
|
180
211
|
@waiting_proc = nil
|
181
212
|
@waiting_operator_proc = nil
|
213
|
+
@waiting_operator_vi_arg = nil
|
182
214
|
@completion_journey_data = nil
|
183
215
|
@completion_state = CompletionState::NORMAL
|
184
216
|
@perfect_matched = nil
|
@@ -186,8 +218,15 @@ class Reline::LineEditor
|
|
186
218
|
@first_prompt = true
|
187
219
|
@searching_prompt = nil
|
188
220
|
@first_char = true
|
221
|
+
@add_newline_to_end_of_buffer = false
|
222
|
+
@just_cursor_moving = nil
|
223
|
+
@cached_prompt_list = nil
|
224
|
+
@prompt_cache_time = nil
|
189
225
|
@eof = false
|
190
226
|
@continuous_insertion_buffer = String.new(encoding: @encoding)
|
227
|
+
@scroll_partial_screen = nil
|
228
|
+
@prev_mode_string = nil
|
229
|
+
@drop_terminate_spaces = false
|
191
230
|
reset_line
|
192
231
|
end
|
193
232
|
|
@@ -232,6 +271,7 @@ class Reline::LineEditor
|
|
232
271
|
@buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding))
|
233
272
|
@previous_line_index = @line_index
|
234
273
|
@line_index += 1
|
274
|
+
@just_cursor_moving = false
|
235
275
|
end
|
236
276
|
|
237
277
|
private def calculate_height_by_width(width)
|
@@ -272,28 +312,28 @@ class Reline::LineEditor
|
|
272
312
|
end
|
273
313
|
end
|
274
314
|
|
275
|
-
private def calculate_nearest_cursor
|
276
|
-
|
315
|
+
private def calculate_nearest_cursor(line_to_calc = @line, cursor = @cursor, started_from = @started_from, byte_pointer = @byte_pointer, update = true)
|
316
|
+
new_cursor_max = calculate_width(line_to_calc)
|
277
317
|
new_cursor = 0
|
278
318
|
new_byte_pointer = 0
|
279
319
|
height = 1
|
280
320
|
max_width = @screen_size.last
|
281
321
|
if @config.editing_mode_is?(:vi_command)
|
282
|
-
last_byte_size = Reline::Unicode.get_prev_mbchar_size(
|
322
|
+
last_byte_size = Reline::Unicode.get_prev_mbchar_size(line_to_calc, line_to_calc.bytesize)
|
283
323
|
if last_byte_size > 0
|
284
|
-
last_mbchar =
|
324
|
+
last_mbchar = line_to_calc.byteslice(line_to_calc.bytesize - last_byte_size, last_byte_size)
|
285
325
|
last_width = Reline::Unicode.get_mbchar_width(last_mbchar)
|
286
|
-
|
326
|
+
end_of_line_cursor = new_cursor_max - last_width
|
287
327
|
else
|
288
|
-
|
328
|
+
end_of_line_cursor = new_cursor_max
|
289
329
|
end
|
290
330
|
else
|
291
|
-
|
331
|
+
end_of_line_cursor = new_cursor_max
|
292
332
|
end
|
293
|
-
|
333
|
+
line_to_calc.encode(Encoding::UTF_8).grapheme_clusters.each do |gc|
|
294
334
|
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
|
295
335
|
now = new_cursor + mbchar_width
|
296
|
-
if now >
|
336
|
+
if now > end_of_line_cursor or now > cursor
|
297
337
|
break
|
298
338
|
end
|
299
339
|
new_cursor += mbchar_width
|
@@ -302,13 +342,20 @@ class Reline::LineEditor
|
|
302
342
|
end
|
303
343
|
new_byte_pointer += gc.bytesize
|
304
344
|
end
|
305
|
-
|
306
|
-
|
307
|
-
|
345
|
+
new_started_from = height - 1
|
346
|
+
if update
|
347
|
+
@cursor = new_cursor
|
348
|
+
@cursor_max = new_cursor_max
|
349
|
+
@started_from = new_started_from
|
350
|
+
@byte_pointer = new_byte_pointer
|
351
|
+
else
|
352
|
+
[new_cursor, new_cursor_max, new_started_from, new_byte_pointer]
|
353
|
+
end
|
308
354
|
end
|
309
355
|
|
310
356
|
def rerender_all
|
311
357
|
@rerender_all = true
|
358
|
+
process_insert(force: true)
|
312
359
|
rerender
|
313
360
|
end
|
314
361
|
|
@@ -317,168 +364,53 @@ class Reline::LineEditor
|
|
317
364
|
if @menu_info
|
318
365
|
scroll_down(@highest_in_all - @first_line_started_from)
|
319
366
|
@rerender_all = true
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
@output.flush
|
324
|
-
scroll_down(1)
|
325
|
-
end
|
326
|
-
scroll_down(@highest_in_all - 1)
|
327
|
-
move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
|
367
|
+
end
|
368
|
+
if @menu_info
|
369
|
+
show_menu
|
328
370
|
@menu_info = nil
|
329
371
|
end
|
330
372
|
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
331
373
|
if @cleared
|
332
|
-
|
374
|
+
clear_screen_buffer(prompt, prompt_list, prompt_width)
|
333
375
|
@cleared = false
|
334
|
-
back = 0
|
335
|
-
modify_lines(whole_lines).each_with_index do |line, index|
|
336
|
-
if @prompt_proc
|
337
|
-
pr = prompt_list[index]
|
338
|
-
height = render_partial(pr, calculate_width(pr), line, false)
|
339
|
-
else
|
340
|
-
height = render_partial(prompt, prompt_width, line, false)
|
341
|
-
end
|
342
|
-
if index < (@buffer_of_lines.size - 1)
|
343
|
-
move_cursor_down(height)
|
344
|
-
back += height
|
345
|
-
end
|
346
|
-
end
|
347
|
-
move_cursor_up(back)
|
348
|
-
move_cursor_down(@first_line_started_from + @started_from)
|
349
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
350
376
|
return
|
351
377
|
end
|
352
378
|
new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
|
353
379
|
# FIXME: end of logical line sometimes breaks
|
354
|
-
if @
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
Reline::IOGate.erase_after_cursor
|
371
|
-
move_cursor_up(1)
|
372
|
-
end
|
373
|
-
move_cursor_up(all_height - 1)
|
380
|
+
if @add_newline_to_end_of_buffer
|
381
|
+
rerender_added_newline
|
382
|
+
@add_newline_to_end_of_buffer = false
|
383
|
+
else
|
384
|
+
if @just_cursor_moving and not @rerender_all
|
385
|
+
rendered = just_move_cursor
|
386
|
+
@just_cursor_moving = false
|
387
|
+
return
|
388
|
+
elsif @previous_line_index or new_highest_in_this != @highest_in_this
|
389
|
+
rerender_changed_current_line
|
390
|
+
@previous_line_index = nil
|
391
|
+
rendered = true
|
392
|
+
elsif @rerender_all
|
393
|
+
rerender_all_lines
|
394
|
+
@rerender_all = false
|
395
|
+
rendered = true
|
374
396
|
else
|
375
|
-
move_cursor_up(all_height - 1)
|
376
|
-
end
|
377
|
-
@highest_in_all = all_height
|
378
|
-
back = 0
|
379
|
-
modify_lines(new_lines).each_with_index do |line, index|
|
380
|
-
if @prompt_proc
|
381
|
-
prompt = prompt_list[index]
|
382
|
-
prompt_width = calculate_width(prompt, true)
|
383
|
-
end
|
384
|
-
height = render_partial(prompt, prompt_width, line, false)
|
385
|
-
if index < (new_lines.size - 1)
|
386
|
-
scroll_down(1)
|
387
|
-
back += height
|
388
|
-
else
|
389
|
-
back += height - 1
|
390
|
-
end
|
391
397
|
end
|
392
|
-
move_cursor_up(back)
|
393
|
-
if @previous_line_index
|
394
|
-
@buffer_of_lines[@previous_line_index] = @line
|
395
|
-
@line = @buffer_of_lines[@line_index]
|
396
|
-
end
|
397
|
-
@first_line_started_from =
|
398
|
-
if @line_index.zero?
|
399
|
-
0
|
400
|
-
else
|
401
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
402
|
-
end
|
403
|
-
if @prompt_proc
|
404
|
-
prompt = prompt_list[@line_index]
|
405
|
-
prompt_width = calculate_width(prompt, true)
|
406
|
-
end
|
407
|
-
move_cursor_down(@first_line_started_from)
|
408
|
-
calculate_nearest_cursor
|
409
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
410
|
-
move_cursor_down(@started_from)
|
411
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
412
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
413
|
-
@previous_line_index = nil
|
414
|
-
rendered = true
|
415
|
-
elsif @rerender_all
|
416
|
-
move_cursor_up(@first_line_started_from + @started_from)
|
417
|
-
Reline::IOGate.move_cursor_column(0)
|
418
|
-
back = 0
|
419
|
-
new_buffer = whole_lines
|
420
|
-
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
421
|
-
new_buffer.each_with_index do |line, index|
|
422
|
-
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
423
|
-
width = prompt_width + calculate_width(line)
|
424
|
-
height = calculate_height_by_width(width)
|
425
|
-
back += height
|
426
|
-
end
|
427
|
-
if back > @highest_in_all
|
428
|
-
scroll_down(back - 1)
|
429
|
-
move_cursor_up(back - 1)
|
430
|
-
elsif back < @highest_in_all
|
431
|
-
scroll_down(back)
|
432
|
-
Reline::IOGate.erase_after_cursor
|
433
|
-
(@highest_in_all - back - 1).times do
|
434
|
-
scroll_down(1)
|
435
|
-
Reline::IOGate.erase_after_cursor
|
436
|
-
end
|
437
|
-
move_cursor_up(@highest_in_all - 1)
|
438
|
-
end
|
439
|
-
modify_lines(new_buffer).each_with_index do |line, index|
|
440
|
-
if @prompt_proc
|
441
|
-
prompt = prompt_list[index]
|
442
|
-
prompt_width = calculate_width(prompt, true)
|
443
|
-
end
|
444
|
-
render_partial(prompt, prompt_width, line, false)
|
445
|
-
if index < (new_buffer.size - 1)
|
446
|
-
move_cursor_down(1)
|
447
|
-
end
|
448
|
-
end
|
449
|
-
move_cursor_up(back - 1)
|
450
|
-
if @prompt_proc
|
451
|
-
prompt = prompt_list[@line_index]
|
452
|
-
prompt_width = calculate_width(prompt, true)
|
453
|
-
end
|
454
|
-
@highest_in_all = back
|
455
|
-
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
456
|
-
@first_line_started_from =
|
457
|
-
if @line_index.zero?
|
458
|
-
0
|
459
|
-
else
|
460
|
-
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
461
|
-
end
|
462
|
-
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
463
|
-
move_cursor_down(@first_line_started_from + @started_from)
|
464
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
465
|
-
@rerender_all = false
|
466
|
-
rendered = true
|
467
398
|
end
|
468
399
|
line = modify_lines(whole_lines)[@line_index]
|
469
400
|
if @is_multiline
|
470
401
|
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
471
402
|
if finished?
|
472
403
|
# Always rerender on finish because output_modifier_proc may return a different output.
|
473
|
-
render_partial(prompt, prompt_width, line)
|
404
|
+
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
474
405
|
scroll_down(1)
|
475
406
|
Reline::IOGate.move_cursor_column(0)
|
476
407
|
Reline::IOGate.erase_after_cursor
|
477
408
|
elsif not rendered
|
478
|
-
render_partial(prompt, prompt_width, line)
|
409
|
+
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
479
410
|
end
|
411
|
+
@buffer_of_lines[@line_index] = @line
|
480
412
|
else
|
481
|
-
render_partial(prompt, prompt_width, line)
|
413
|
+
render_partial(prompt, prompt_width, line, 0)
|
482
414
|
if finished?
|
483
415
|
scroll_down(1)
|
484
416
|
Reline::IOGate.move_cursor_column(0)
|
@@ -487,8 +419,239 @@ class Reline::LineEditor
|
|
487
419
|
end
|
488
420
|
end
|
489
421
|
|
490
|
-
private def
|
422
|
+
private def calculate_scroll_partial_screen(highest_in_all, cursor_y)
|
423
|
+
if @screen_height < highest_in_all
|
424
|
+
old_scroll_partial_screen = @scroll_partial_screen
|
425
|
+
if cursor_y == 0
|
426
|
+
@scroll_partial_screen = 0
|
427
|
+
elsif cursor_y == (highest_in_all - 1)
|
428
|
+
@scroll_partial_screen = highest_in_all - @screen_height
|
429
|
+
else
|
430
|
+
if @scroll_partial_screen
|
431
|
+
if cursor_y <= @scroll_partial_screen
|
432
|
+
@scroll_partial_screen = cursor_y
|
433
|
+
elsif (@scroll_partial_screen + @screen_height - 1) < cursor_y
|
434
|
+
@scroll_partial_screen = cursor_y - (@screen_height - 1)
|
435
|
+
end
|
436
|
+
else
|
437
|
+
if cursor_y > (@screen_height - 1)
|
438
|
+
@scroll_partial_screen = cursor_y - (@screen_height - 1)
|
439
|
+
else
|
440
|
+
@scroll_partial_screen = 0
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
if @scroll_partial_screen != old_scroll_partial_screen
|
445
|
+
@rerender_all = true
|
446
|
+
end
|
447
|
+
else
|
448
|
+
if @scroll_partial_screen
|
449
|
+
@rerender_all = true
|
450
|
+
end
|
451
|
+
@scroll_partial_screen = nil
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
private def rerender_added_newline
|
456
|
+
scroll_down(1)
|
457
|
+
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
458
|
+
prompt, prompt_width, = check_multiline_prompt(new_lines, prompt)
|
459
|
+
@buffer_of_lines[@previous_line_index] = @line
|
460
|
+
@line = @buffer_of_lines[@line_index]
|
461
|
+
render_partial(prompt, prompt_width, @line, @first_line_started_from + @started_from + 1, with_control: false)
|
462
|
+
@cursor = @cursor_max = calculate_width(@line)
|
463
|
+
@byte_pointer = @line.bytesize
|
464
|
+
@highest_in_all += @highest_in_this
|
465
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
466
|
+
@first_line_started_from += @started_from + 1
|
467
|
+
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
468
|
+
@previous_line_index = nil
|
469
|
+
end
|
470
|
+
|
471
|
+
def just_move_cursor
|
472
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines, prompt)
|
473
|
+
move_cursor_up(@started_from)
|
474
|
+
new_first_line_started_from =
|
475
|
+
if @line_index.zero?
|
476
|
+
0
|
477
|
+
else
|
478
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
479
|
+
end
|
480
|
+
first_line_diff = new_first_line_started_from - @first_line_started_from
|
481
|
+
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)
|
482
|
+
new_started_from = calculate_height_by_width(prompt_width + new_cursor) - 1
|
483
|
+
calculate_scroll_partial_screen(@highest_in_all, new_first_line_started_from + new_started_from)
|
484
|
+
@previous_line_index = nil
|
485
|
+
if @rerender_all
|
486
|
+
@line = @buffer_of_lines[@line_index]
|
487
|
+
rerender_all_lines
|
488
|
+
@rerender_all = false
|
489
|
+
true
|
490
|
+
else
|
491
|
+
@line = @buffer_of_lines[@line_index]
|
492
|
+
@first_line_started_from = new_first_line_started_from
|
493
|
+
@started_from = new_started_from
|
494
|
+
@cursor = new_cursor
|
495
|
+
@cursor_max = new_cursor_max
|
496
|
+
@byte_pointer = new_byte_pointer
|
497
|
+
move_cursor_down(first_line_diff + @started_from)
|
498
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
499
|
+
false
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
private def rerender_changed_current_line
|
504
|
+
if @previous_line_index
|
505
|
+
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
506
|
+
else
|
507
|
+
new_lines = whole_lines
|
508
|
+
end
|
509
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
510
|
+
all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
|
511
|
+
diff = all_height - @highest_in_all
|
512
|
+
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
513
|
+
if diff > 0
|
514
|
+
scroll_down(diff)
|
515
|
+
move_cursor_up(all_height - 1)
|
516
|
+
elsif diff < 0
|
517
|
+
(-diff).times do
|
518
|
+
Reline::IOGate.move_cursor_column(0)
|
519
|
+
Reline::IOGate.erase_after_cursor
|
520
|
+
move_cursor_up(1)
|
521
|
+
end
|
522
|
+
move_cursor_up(all_height - 1)
|
523
|
+
else
|
524
|
+
move_cursor_up(all_height - 1)
|
525
|
+
end
|
526
|
+
@highest_in_all = all_height
|
527
|
+
back = render_whole_lines(new_lines, prompt_list || prompt, prompt_width)
|
528
|
+
move_cursor_up(back)
|
529
|
+
if @previous_line_index
|
530
|
+
@buffer_of_lines[@previous_line_index] = @line
|
531
|
+
@line = @buffer_of_lines[@line_index]
|
532
|
+
end
|
533
|
+
@first_line_started_from =
|
534
|
+
if @line_index.zero?
|
535
|
+
0
|
536
|
+
else
|
537
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
538
|
+
end
|
539
|
+
if @prompt_proc
|
540
|
+
prompt = prompt_list[@line_index]
|
541
|
+
prompt_width = calculate_width(prompt, true)
|
542
|
+
end
|
543
|
+
move_cursor_down(@first_line_started_from)
|
544
|
+
calculate_nearest_cursor
|
545
|
+
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
546
|
+
move_cursor_down(@started_from)
|
547
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
548
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
549
|
+
end
|
550
|
+
|
551
|
+
private def rerender_all_lines
|
552
|
+
move_cursor_up(@first_line_started_from + @started_from)
|
553
|
+
Reline::IOGate.move_cursor_column(0)
|
554
|
+
back = 0
|
555
|
+
new_buffer = whole_lines
|
556
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
557
|
+
new_buffer.each_with_index do |line, index|
|
558
|
+
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
559
|
+
width = prompt_width + calculate_width(line)
|
560
|
+
height = calculate_height_by_width(width)
|
561
|
+
back += height
|
562
|
+
end
|
563
|
+
old_highest_in_all = @highest_in_all
|
564
|
+
if @line_index.zero?
|
565
|
+
new_first_line_started_from = 0
|
566
|
+
else
|
567
|
+
new_first_line_started_from = calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
568
|
+
end
|
569
|
+
new_started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
570
|
+
if back > old_highest_in_all
|
571
|
+
scroll_down(back - 1)
|
572
|
+
move_cursor_up(back - 1)
|
573
|
+
elsif back < old_highest_in_all
|
574
|
+
scroll_down(back)
|
575
|
+
Reline::IOGate.erase_after_cursor
|
576
|
+
(old_highest_in_all - back - 1).times do
|
577
|
+
scroll_down(1)
|
578
|
+
Reline::IOGate.erase_after_cursor
|
579
|
+
end
|
580
|
+
move_cursor_up(old_highest_in_all - 1)
|
581
|
+
end
|
582
|
+
calculate_scroll_partial_screen(back, new_first_line_started_from + new_started_from)
|
583
|
+
render_whole_lines(new_buffer, prompt_list || prompt, prompt_width)
|
584
|
+
if @prompt_proc
|
585
|
+
prompt = prompt_list[@line_index]
|
586
|
+
prompt_width = calculate_width(prompt, true)
|
587
|
+
end
|
588
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
589
|
+
@highest_in_all = back
|
590
|
+
@first_line_started_from = new_first_line_started_from
|
591
|
+
@started_from = new_started_from
|
592
|
+
if @scroll_partial_screen
|
593
|
+
Reline::IOGate.move_cursor_up(@screen_height - (@first_line_started_from + @started_from - @scroll_partial_screen) - 1)
|
594
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
595
|
+
else
|
596
|
+
move_cursor_down(@first_line_started_from + @started_from - back + 1)
|
597
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
private def render_whole_lines(lines, prompt, prompt_width)
|
602
|
+
rendered_height = 0
|
603
|
+
modify_lines(lines).each_with_index do |line, index|
|
604
|
+
if prompt.is_a?(Array)
|
605
|
+
line_prompt = prompt[index]
|
606
|
+
prompt_width = calculate_width(line_prompt, true)
|
607
|
+
else
|
608
|
+
line_prompt = prompt
|
609
|
+
end
|
610
|
+
height = render_partial(line_prompt, prompt_width, line, rendered_height, with_control: false)
|
611
|
+
if index < (lines.size - 1)
|
612
|
+
if @scroll_partial_screen
|
613
|
+
if (@scroll_partial_screen - height) < rendered_height and (@scroll_partial_screen + @screen_height - 1) >= (rendered_height + height)
|
614
|
+
move_cursor_down(1)
|
615
|
+
end
|
616
|
+
else
|
617
|
+
scroll_down(1)
|
618
|
+
end
|
619
|
+
rendered_height += height
|
620
|
+
else
|
621
|
+
rendered_height += height - 1
|
622
|
+
end
|
623
|
+
end
|
624
|
+
rendered_height
|
625
|
+
end
|
626
|
+
|
627
|
+
private def render_partial(prompt, prompt_width, line_to_render, this_started_from, with_control: true)
|
491
628
|
visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
|
629
|
+
cursor_up_from_last_line = 0
|
630
|
+
# TODO: This logic would be sometimes buggy if this logical line isn't the current @line_index.
|
631
|
+
if @scroll_partial_screen
|
632
|
+
last_visual_line = this_started_from + (height - 1)
|
633
|
+
last_screen_line = @scroll_partial_screen + (@screen_height - 1)
|
634
|
+
if (@scroll_partial_screen - this_started_from) >= height
|
635
|
+
# Render nothing because this line is before the screen.
|
636
|
+
visual_lines = []
|
637
|
+
elsif this_started_from > last_screen_line
|
638
|
+
# Render nothing because this line is after the screen.
|
639
|
+
visual_lines = []
|
640
|
+
else
|
641
|
+
deleted_lines_before_screen = []
|
642
|
+
if @scroll_partial_screen > this_started_from and last_visual_line >= @scroll_partial_screen
|
643
|
+
# A part of visual lines are before the screen.
|
644
|
+
deleted_lines_before_screen = visual_lines.shift((@scroll_partial_screen - this_started_from) * 2)
|
645
|
+
deleted_lines_before_screen.compact!
|
646
|
+
end
|
647
|
+
if this_started_from <= last_screen_line and last_screen_line < last_visual_line
|
648
|
+
# A part of visual lines are after the screen.
|
649
|
+
visual_lines.pop((last_visual_line - last_screen_line) * 2)
|
650
|
+
end
|
651
|
+
move_cursor_up(deleted_lines_before_screen.size - @started_from)
|
652
|
+
cursor_up_from_last_line = @started_from - deleted_lines_before_screen.size
|
653
|
+
end
|
654
|
+
end
|
492
655
|
if with_control
|
493
656
|
if height > @highest_in_this
|
494
657
|
diff = height - @highest_in_this
|
@@ -502,10 +665,14 @@ class Reline::LineEditor
|
|
502
665
|
@highest_in_this = height
|
503
666
|
end
|
504
667
|
move_cursor_up(@started_from)
|
668
|
+
cursor_up_from_last_line = height - 1 - @started_from
|
505
669
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
506
670
|
end
|
507
|
-
Reline::
|
671
|
+
if Reline::Unicode::CSI_REGEXP.match?(prompt + line_to_render)
|
672
|
+
@output.write "\e[0m" # clear character decorations
|
673
|
+
end
|
508
674
|
visual_lines.each_with_index do |line, index|
|
675
|
+
Reline::IOGate.move_cursor_column(0)
|
509
676
|
if line.nil?
|
510
677
|
if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
511
678
|
# reaches the end of line
|
@@ -537,15 +704,18 @@ class Reline::LineEditor
|
|
537
704
|
@pre_input_hook&.call
|
538
705
|
end
|
539
706
|
end
|
540
|
-
|
541
|
-
|
707
|
+
unless visual_lines.empty?
|
708
|
+
Reline::IOGate.erase_after_cursor
|
709
|
+
Reline::IOGate.move_cursor_column(0)
|
710
|
+
end
|
542
711
|
if with_control
|
543
712
|
# Just after rendring, so the cursor is on the last line.
|
544
713
|
if finished?
|
545
714
|
Reline::IOGate.move_cursor_column(0)
|
546
715
|
else
|
547
716
|
# Moves up from bottom of lines to the cursor position.
|
548
|
-
move_cursor_up(
|
717
|
+
move_cursor_up(cursor_up_from_last_line)
|
718
|
+
# This logic is buggy if a fullwidth char is wrapped because there is only one halfwidth at end of a line.
|
549
719
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
550
720
|
end
|
551
721
|
end
|
@@ -562,6 +732,39 @@ class Reline::LineEditor
|
|
562
732
|
end
|
563
733
|
end
|
564
734
|
|
735
|
+
private def show_menu
|
736
|
+
scroll_down(@highest_in_all - @first_line_started_from)
|
737
|
+
@rerender_all = true
|
738
|
+
@menu_info.list.sort!.each do |item|
|
739
|
+
Reline::IOGate.move_cursor_column(0)
|
740
|
+
@output.write item
|
741
|
+
@output.flush
|
742
|
+
scroll_down(1)
|
743
|
+
end
|
744
|
+
scroll_down(@highest_in_all - 1)
|
745
|
+
move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
|
746
|
+
end
|
747
|
+
|
748
|
+
private def clear_screen_buffer(prompt, prompt_list, prompt_width)
|
749
|
+
Reline::IOGate.clear_screen
|
750
|
+
back = 0
|
751
|
+
modify_lines(whole_lines).each_with_index do |line, index|
|
752
|
+
if @prompt_proc
|
753
|
+
pr = prompt_list[index]
|
754
|
+
height = render_partial(pr, calculate_width(pr), line, back, with_control: false)
|
755
|
+
else
|
756
|
+
height = render_partial(prompt, prompt_width, line, back, with_control: false)
|
757
|
+
end
|
758
|
+
if index < (@buffer_of_lines.size - 1)
|
759
|
+
move_cursor_down(height)
|
760
|
+
back += height
|
761
|
+
end
|
762
|
+
end
|
763
|
+
move_cursor_up(back)
|
764
|
+
move_cursor_down(@first_line_started_from + @started_from)
|
765
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
766
|
+
end
|
767
|
+
|
565
768
|
def editing_mode
|
566
769
|
@config.editing_mode
|
567
770
|
end
|
@@ -698,7 +901,8 @@ class Reline::LineEditor
|
|
698
901
|
if @waiting_operator_proc
|
699
902
|
if VI_MOTIONS.include?(method_symbol)
|
700
903
|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
|
701
|
-
|
904
|
+
@vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg > 1
|
905
|
+
block.(true)
|
702
906
|
unless @waiting_proc
|
703
907
|
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
|
704
908
|
@cursor, @byte_pointer = old_cursor, old_byte_pointer
|
@@ -706,38 +910,55 @@ class Reline::LineEditor
|
|
706
910
|
else
|
707
911
|
old_waiting_proc = @waiting_proc
|
708
912
|
old_waiting_operator_proc = @waiting_operator_proc
|
913
|
+
current_waiting_operator_proc = @waiting_operator_proc
|
709
914
|
@waiting_proc = proc { |k|
|
710
915
|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
|
711
916
|
old_waiting_proc.(k)
|
712
917
|
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
|
713
918
|
@cursor, @byte_pointer = old_cursor, old_byte_pointer
|
714
|
-
|
919
|
+
current_waiting_operator_proc.(cursor_diff, byte_pointer_diff)
|
715
920
|
@waiting_operator_proc = old_waiting_operator_proc
|
716
921
|
}
|
717
922
|
end
|
718
923
|
else
|
719
924
|
# Ignores operator when not motion is given.
|
720
|
-
block.()
|
925
|
+
block.(false)
|
721
926
|
end
|
722
927
|
@waiting_operator_proc = nil
|
928
|
+
@waiting_operator_vi_arg = nil
|
929
|
+
@vi_arg = nil
|
723
930
|
else
|
724
|
-
block.()
|
931
|
+
block.(false)
|
725
932
|
end
|
726
933
|
end
|
727
934
|
|
728
935
|
private def argumentable?(method_obj)
|
729
|
-
method_obj and method_obj.parameters.
|
936
|
+
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :arg }
|
937
|
+
end
|
938
|
+
|
939
|
+
private def inclusive?(method_obj)
|
940
|
+
# If a motion method with the keyword argument "inclusive" follows the
|
941
|
+
# operator, it must contain the character at the cursor position.
|
942
|
+
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :inclusive }
|
730
943
|
end
|
731
944
|
|
732
|
-
def wrap_method_call(method_symbol, method_obj, key)
|
945
|
+
def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
|
733
946
|
if @config.editing_mode_is?(:emacs, :vi_insert) and @waiting_proc.nil? and @waiting_operator_proc.nil?
|
734
947
|
not_insertion = method_symbol != :ed_insert
|
735
948
|
process_insert(force: not_insertion)
|
736
949
|
end
|
737
|
-
if @vi_arg
|
738
|
-
|
950
|
+
if @vi_arg and argumentable?(method_obj)
|
951
|
+
if with_operator and inclusive?(method_obj)
|
952
|
+
method_obj.(key, arg: @vi_arg, inclusive: true)
|
953
|
+
else
|
954
|
+
method_obj.(key, arg: @vi_arg)
|
955
|
+
end
|
739
956
|
else
|
740
|
-
method_obj
|
957
|
+
if with_operator and inclusive?(method_obj)
|
958
|
+
method_obj.(key, inclusive: true)
|
959
|
+
else
|
960
|
+
method_obj.(key)
|
961
|
+
end
|
741
962
|
end
|
742
963
|
end
|
743
964
|
|
@@ -749,8 +970,8 @@ class Reline::LineEditor
|
|
749
970
|
end
|
750
971
|
if method_symbol and key.is_a?(Symbol)
|
751
972
|
if @vi_arg and argumentable?(method_obj)
|
752
|
-
run_for_operators(key, method_symbol) do
|
753
|
-
wrap_method_call(method_symbol, method_obj, key)
|
973
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
974
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
754
975
|
end
|
755
976
|
else
|
756
977
|
wrap_method_call(method_symbol, method_obj, key) if method_obj
|
@@ -762,15 +983,15 @@ class Reline::LineEditor
|
|
762
983
|
ed_argument_digit(key)
|
763
984
|
else
|
764
985
|
if argumentable?(method_obj)
|
765
|
-
run_for_operators(key, method_symbol) do
|
766
|
-
wrap_method_call(method_symbol, method_obj, key)
|
986
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
987
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
767
988
|
end
|
768
989
|
elsif @waiting_proc
|
769
990
|
@waiting_proc.(key)
|
770
991
|
elsif method_obj
|
771
992
|
wrap_method_call(method_symbol, method_obj, key)
|
772
993
|
else
|
773
|
-
ed_insert(key)
|
994
|
+
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
774
995
|
end
|
775
996
|
@kill_ring.process
|
776
997
|
@vi_arg = nil
|
@@ -782,13 +1003,13 @@ class Reline::LineEditor
|
|
782
1003
|
if method_symbol == :ed_argument_digit
|
783
1004
|
wrap_method_call(method_symbol, method_obj, key)
|
784
1005
|
else
|
785
|
-
run_for_operators(key, method_symbol) do
|
786
|
-
wrap_method_call(method_symbol, method_obj, key)
|
1006
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
1007
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
787
1008
|
end
|
788
1009
|
end
|
789
1010
|
@kill_ring.process
|
790
1011
|
else
|
791
|
-
ed_insert(key)
|
1012
|
+
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
792
1013
|
end
|
793
1014
|
end
|
794
1015
|
|
@@ -831,6 +1052,7 @@ class Reline::LineEditor
|
|
831
1052
|
end
|
832
1053
|
|
833
1054
|
def input_key(key)
|
1055
|
+
@just_cursor_moving = nil
|
834
1056
|
if key.char.nil?
|
835
1057
|
if @first_char
|
836
1058
|
@line = nil
|
@@ -838,6 +1060,7 @@ class Reline::LineEditor
|
|
838
1060
|
finish
|
839
1061
|
return
|
840
1062
|
end
|
1063
|
+
old_line = @line.dup
|
841
1064
|
@first_char = false
|
842
1065
|
completion_occurs = false
|
843
1066
|
if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
|
@@ -866,6 +1089,17 @@ class Reline::LineEditor
|
|
866
1089
|
unless completion_occurs
|
867
1090
|
@completion_state = CompletionState::NORMAL
|
868
1091
|
end
|
1092
|
+
if not Reline::IOGate.in_pasting? and @just_cursor_moving.nil?
|
1093
|
+
if @previous_line_index and @buffer_of_lines[@previous_line_index] == @line
|
1094
|
+
@just_cursor_moving = true
|
1095
|
+
elsif @previous_line_index.nil? and @buffer_of_lines[@line_index] == @line and old_line == @line
|
1096
|
+
@just_cursor_moving = true
|
1097
|
+
else
|
1098
|
+
@just_cursor_moving = false
|
1099
|
+
end
|
1100
|
+
else
|
1101
|
+
@just_cursor_moving = false
|
1102
|
+
end
|
869
1103
|
if @is_multiline and @auto_indent_proc and not simplified_rendering?
|
870
1104
|
process_auto_indent
|
871
1105
|
end
|
@@ -904,6 +1138,7 @@ class Reline::LineEditor
|
|
904
1138
|
new_lines = whole_lines
|
905
1139
|
end
|
906
1140
|
new_indent = @auto_indent_proc.(new_lines, @line_index, @byte_pointer, @check_new_auto_indent)
|
1141
|
+
new_indent = @cursor_max if new_indent&.> @cursor_max
|
907
1142
|
if new_indent&.>= 0
|
908
1143
|
md = new_lines[@line_index].match(/\A */)
|
909
1144
|
prev_indent = md[0].count(' ')
|
@@ -1097,11 +1332,14 @@ class Reline::LineEditor
|
|
1097
1332
|
|
1098
1333
|
private def key_newline(key)
|
1099
1334
|
if @is_multiline
|
1335
|
+
if (@buffer_of_lines.size - 1) == @line_index and @line.bytesize == @byte_pointer
|
1336
|
+
@add_newline_to_end_of_buffer = true
|
1337
|
+
end
|
1100
1338
|
next_line = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
|
1101
1339
|
cursor_line = @line.byteslice(0, @byte_pointer)
|
1102
1340
|
insert_new_line(cursor_line, next_line)
|
1103
1341
|
@cursor = 0
|
1104
|
-
@check_new_auto_indent = true
|
1342
|
+
@check_new_auto_indent = true unless Reline::IOGate.in_pasting?
|
1105
1343
|
end
|
1106
1344
|
end
|
1107
1345
|
|
@@ -1146,6 +1384,8 @@ class Reline::LineEditor
|
|
1146
1384
|
if Reline::IOGate.in_pasting?
|
1147
1385
|
@continuous_insertion_buffer << str
|
1148
1386
|
return
|
1387
|
+
elsif not @continuous_insertion_buffer.empty?
|
1388
|
+
process_insert
|
1149
1389
|
end
|
1150
1390
|
width = Reline::Unicode.get_mbchar_width(str)
|
1151
1391
|
if @cursor == @cursor_max
|
@@ -1153,7 +1393,12 @@ class Reline::LineEditor
|
|
1153
1393
|
else
|
1154
1394
|
@line = byteinsert(@line, @byte_pointer, str)
|
1155
1395
|
end
|
1396
|
+
last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
1156
1397
|
@byte_pointer += bytesize
|
1398
|
+
last_mbchar = @line.byteslice((@byte_pointer - bytesize - last_byte_size), last_byte_size)
|
1399
|
+
if last_byte_size != 0 and (last_mbchar + str).grapheme_clusters.size == 1
|
1400
|
+
width = 0
|
1401
|
+
end
|
1157
1402
|
@cursor += width
|
1158
1403
|
@cursor_max += width
|
1159
1404
|
end
|
@@ -1212,6 +1457,7 @@ class Reline::LineEditor
|
|
1212
1457
|
arg -= 1
|
1213
1458
|
ed_prev_char(key, arg: arg) if arg > 0
|
1214
1459
|
end
|
1460
|
+
alias_method :backward_char, :ed_prev_char
|
1215
1461
|
|
1216
1462
|
private def vi_first_print(key)
|
1217
1463
|
@byte_pointer, @cursor = Reline::Unicode.vi_first_print(@line)
|
@@ -1364,9 +1610,11 @@ class Reline::LineEditor
|
|
1364
1610
|
searcher = generate_searcher
|
1365
1611
|
searcher.resume(key)
|
1366
1612
|
@searching_prompt = "(reverse-i-search)`': "
|
1613
|
+
termination_keys = ["\C-j".ord]
|
1614
|
+
termination_keys.concat(@config.isearch_terminators&.chars&.map(&:ord)) if @config.isearch_terminators
|
1367
1615
|
@waiting_proc = ->(k) {
|
1368
1616
|
case k
|
1369
|
-
when
|
1617
|
+
when *termination_keys
|
1370
1618
|
if @history_pointer
|
1371
1619
|
buffer = Reline::HISTORY[@history_pointer]
|
1372
1620
|
else
|
@@ -1385,6 +1633,8 @@ class Reline::LineEditor
|
|
1385
1633
|
@waiting_proc = nil
|
1386
1634
|
@cursor_max = calculate_width(@line)
|
1387
1635
|
@cursor = @byte_pointer = 0
|
1636
|
+
@rerender_all = true
|
1637
|
+
@cached_prompt_list = nil
|
1388
1638
|
searcher.resume(-1)
|
1389
1639
|
when "\C-g".ord
|
1390
1640
|
if @is_multiline
|
@@ -1428,6 +1678,8 @@ class Reline::LineEditor
|
|
1428
1678
|
@waiting_proc = nil
|
1429
1679
|
@cursor_max = calculate_width(@line)
|
1430
1680
|
@cursor = @byte_pointer = 0
|
1681
|
+
@rerender_all = true
|
1682
|
+
@cached_prompt_list = nil
|
1431
1683
|
searcher.resume(-1)
|
1432
1684
|
end
|
1433
1685
|
end
|
@@ -1480,7 +1732,7 @@ class Reline::LineEditor
|
|
1480
1732
|
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
1481
1733
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1482
1734
|
@line_index = line_no
|
1483
|
-
@line = @buffer_of_lines
|
1735
|
+
@line = @buffer_of_lines[@line_index]
|
1484
1736
|
@rerender_all = true
|
1485
1737
|
else
|
1486
1738
|
@line = Reline::HISTORY[@history_pointer]
|
@@ -1528,7 +1780,7 @@ class Reline::LineEditor
|
|
1528
1780
|
@line_index = line_no
|
1529
1781
|
end
|
1530
1782
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1531
|
-
@line = @buffer_of_lines
|
1783
|
+
@line = @buffer_of_lines[@line_index]
|
1532
1784
|
@rerender_all = true
|
1533
1785
|
else
|
1534
1786
|
if @history_pointer.nil? and substr.empty?
|
@@ -1720,6 +1972,7 @@ class Reline::LineEditor
|
|
1720
1972
|
@cursor = 0
|
1721
1973
|
end
|
1722
1974
|
end
|
1975
|
+
alias_method :kill_line, :em_kill_line
|
1723
1976
|
|
1724
1977
|
private def em_delete(key)
|
1725
1978
|
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
|
@@ -1770,6 +2023,7 @@ class Reline::LineEditor
|
|
1770
2023
|
@byte_pointer += yanked.bytesize
|
1771
2024
|
end
|
1772
2025
|
end
|
2026
|
+
alias_method :yank, :em_yank
|
1773
2027
|
|
1774
2028
|
private def em_yank_pop(key)
|
1775
2029
|
yanked, prev_yank = @kill_ring.yank_pop
|
@@ -1786,6 +2040,7 @@ class Reline::LineEditor
|
|
1786
2040
|
@byte_pointer += yanked.bytesize
|
1787
2041
|
end
|
1788
2042
|
end
|
2043
|
+
alias_method :yank_pop, :em_yank_pop
|
1789
2044
|
|
1790
2045
|
private def ed_clear_screen(key)
|
1791
2046
|
@cleared = true
|
@@ -1916,9 +2171,10 @@ class Reline::LineEditor
|
|
1916
2171
|
@byte_pointer -= byte_size
|
1917
2172
|
@cursor -= width
|
1918
2173
|
@cursor_max -= width
|
1919
|
-
@kill_ring.append(deleted)
|
2174
|
+
@kill_ring.append(deleted, true)
|
1920
2175
|
end
|
1921
2176
|
end
|
2177
|
+
alias_method :unix_word_rubout, :em_kill_region
|
1922
2178
|
|
1923
2179
|
private def copy_for_vi(text)
|
1924
2180
|
if @config.editing_mode_is?(:vi_insert) or @config.editing_mode_is?(:vi_command)
|
@@ -1939,11 +2195,11 @@ class Reline::LineEditor
|
|
1939
2195
|
ed_prev_char(key)
|
1940
2196
|
@config.editing_mode = :vi_command
|
1941
2197
|
end
|
1942
|
-
alias_method :
|
2198
|
+
alias_method :vi_movement_mode, :vi_command_mode
|
1943
2199
|
|
1944
2200
|
private def vi_next_word(key, arg: 1)
|
1945
2201
|
if @line.bytesize > @byte_pointer
|
1946
|
-
byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer)
|
2202
|
+
byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer, @drop_terminate_spaces)
|
1947
2203
|
@byte_pointer += byte_size
|
1948
2204
|
@cursor += width
|
1949
2205
|
end
|
@@ -1961,13 +2217,22 @@ class Reline::LineEditor
|
|
1961
2217
|
vi_prev_word(key, arg: arg) if arg > 0
|
1962
2218
|
end
|
1963
2219
|
|
1964
|
-
private def vi_end_word(key, arg: 1)
|
2220
|
+
private def vi_end_word(key, arg: 1, inclusive: false)
|
1965
2221
|
if @line.bytesize > @byte_pointer
|
1966
2222
|
byte_size, width = Reline::Unicode.vi_forward_end_word(@line, @byte_pointer)
|
1967
2223
|
@byte_pointer += byte_size
|
1968
2224
|
@cursor += width
|
1969
2225
|
end
|
1970
2226
|
arg -= 1
|
2227
|
+
if inclusive and arg.zero?
|
2228
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2229
|
+
if byte_size > 0
|
2230
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
2231
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
2232
|
+
@byte_pointer += byte_size
|
2233
|
+
@cursor += width
|
2234
|
+
end
|
2235
|
+
end
|
1971
2236
|
vi_end_word(key, arg: arg) if arg > 0
|
1972
2237
|
end
|
1973
2238
|
|
@@ -1991,13 +2256,22 @@ class Reline::LineEditor
|
|
1991
2256
|
vi_prev_big_word(key, arg: arg) if arg > 0
|
1992
2257
|
end
|
1993
2258
|
|
1994
|
-
private def vi_end_big_word(key, arg: 1)
|
2259
|
+
private def vi_end_big_word(key, arg: 1, inclusive: false)
|
1995
2260
|
if @line.bytesize > @byte_pointer
|
1996
2261
|
byte_size, width = Reline::Unicode.vi_big_forward_end_word(@line, @byte_pointer)
|
1997
2262
|
@byte_pointer += byte_size
|
1998
2263
|
@cursor += width
|
1999
2264
|
end
|
2000
2265
|
arg -= 1
|
2266
|
+
if inclusive and arg.zero?
|
2267
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2268
|
+
if byte_size > 0
|
2269
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
2270
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
2271
|
+
@byte_pointer += byte_size
|
2272
|
+
@cursor += width
|
2273
|
+
end
|
2274
|
+
end
|
2001
2275
|
vi_end_big_word(key, arg: arg) if arg > 0
|
2002
2276
|
end
|
2003
2277
|
|
@@ -2052,7 +2326,8 @@ class Reline::LineEditor
|
|
2052
2326
|
@cursor = 0
|
2053
2327
|
end
|
2054
2328
|
|
2055
|
-
private def vi_change_meta(key)
|
2329
|
+
private def vi_change_meta(key, arg: 1)
|
2330
|
+
@drop_terminate_spaces = true
|
2056
2331
|
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2057
2332
|
if byte_pointer_diff > 0
|
2058
2333
|
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
@@ -2064,10 +2339,12 @@ class Reline::LineEditor
|
|
2064
2339
|
@cursor_max -= cursor_diff.abs
|
2065
2340
|
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
2066
2341
|
@config.editing_mode = :vi_insert
|
2342
|
+
@drop_terminate_spaces = false
|
2067
2343
|
}
|
2344
|
+
@waiting_operator_vi_arg = arg
|
2068
2345
|
end
|
2069
2346
|
|
2070
|
-
private def vi_delete_meta(key)
|
2347
|
+
private def vi_delete_meta(key, arg: 1)
|
2071
2348
|
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2072
2349
|
if byte_pointer_diff > 0
|
2073
2350
|
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
@@ -2079,9 +2356,19 @@ class Reline::LineEditor
|
|
2079
2356
|
@cursor_max -= cursor_diff.abs
|
2080
2357
|
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
2081
2358
|
}
|
2359
|
+
@waiting_operator_vi_arg = arg
|
2082
2360
|
end
|
2083
2361
|
|
2084
|
-
private def vi_yank(key)
|
2362
|
+
private def vi_yank(key, arg: 1)
|
2363
|
+
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2364
|
+
if byte_pointer_diff > 0
|
2365
|
+
cut = @line.byteslice(@byte_pointer, byte_pointer_diff)
|
2366
|
+
elsif byte_pointer_diff < 0
|
2367
|
+
cut = @line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
2368
|
+
end
|
2369
|
+
copy_for_vi(cut)
|
2370
|
+
}
|
2371
|
+
@waiting_operator_vi_arg = arg
|
2085
2372
|
end
|
2086
2373
|
|
2087
2374
|
private def vi_list_or_eof(key)
|
@@ -2108,6 +2395,9 @@ class Reline::LineEditor
|
|
2108
2395
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2109
2396
|
@cursor_max -= width
|
2110
2397
|
if @cursor > 0 and @cursor >= @cursor_max
|
2398
|
+
byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
2399
|
+
mbchar = @line.byteslice(@byte_pointer - byte_size, byte_size)
|
2400
|
+
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2111
2401
|
@byte_pointer -= byte_size
|
2112
2402
|
@cursor -= width
|
2113
2403
|
end
|
@@ -2204,7 +2494,7 @@ class Reline::LineEditor
|
|
2204
2494
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2205
2495
|
before = @line.byteslice(0, @byte_pointer)
|
2206
2496
|
remaining_point = @byte_pointer + byte_size
|
2207
|
-
after = @line.byteslice(remaining_point, @line.
|
2497
|
+
after = @line.byteslice(remaining_point, @line.bytesize - remaining_point)
|
2208
2498
|
@line = before + k.chr + after
|
2209
2499
|
@cursor_max = calculate_width(@line)
|
2210
2500
|
@waiting_proc = nil
|
@@ -2215,7 +2505,7 @@ class Reline::LineEditor
|
|
2215
2505
|
end
|
2216
2506
|
before = @line.byteslice(0, @byte_pointer)
|
2217
2507
|
remaining_point = @byte_pointer + byte_size
|
2218
|
-
after = @line.byteslice(remaining_point, @line.
|
2508
|
+
after = @line.byteslice(remaining_point, @line.bytesize - remaining_point)
|
2219
2509
|
replaced = k.chr * arg
|
2220
2510
|
@line = before + replaced + after
|
2221
2511
|
@byte_pointer += replaced.bytesize
|
@@ -2226,15 +2516,15 @@ class Reline::LineEditor
|
|
2226
2516
|
}
|
2227
2517
|
end
|
2228
2518
|
|
2229
|
-
private def vi_next_char(key, arg: 1)
|
2230
|
-
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg) }
|
2519
|
+
private def vi_next_char(key, arg: 1, inclusive: false)
|
2520
|
+
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, inclusive: inclusive) }
|
2231
2521
|
end
|
2232
2522
|
|
2233
|
-
private def vi_to_next_char(key, arg: 1)
|
2234
|
-
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, true) }
|
2523
|
+
private def vi_to_next_char(key, arg: 1, inclusive: false)
|
2524
|
+
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, need_prev_char: true, inclusive: inclusive) }
|
2235
2525
|
end
|
2236
2526
|
|
2237
|
-
private def search_next_char(key, arg, need_prev_char
|
2527
|
+
private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
|
2238
2528
|
if key.instance_of?(String)
|
2239
2529
|
inputed_char = key
|
2240
2530
|
else
|
@@ -2271,6 +2561,15 @@ class Reline::LineEditor
|
|
2271
2561
|
@byte_pointer += byte_size
|
2272
2562
|
@cursor += width
|
2273
2563
|
end
|
2564
|
+
if inclusive
|
2565
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2566
|
+
if byte_size > 0
|
2567
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
2568
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
2569
|
+
@byte_pointer += byte_size
|
2570
|
+
@cursor += width
|
2571
|
+
end
|
2572
|
+
end
|
2274
2573
|
@waiting_proc = nil
|
2275
2574
|
end
|
2276
2575
|
|
@@ -2342,6 +2641,7 @@ class Reline::LineEditor
|
|
2342
2641
|
alias_method :set_mark, :em_set_mark
|
2343
2642
|
|
2344
2643
|
private def em_exchange_mark(key)
|
2644
|
+
return unless @mark_pointer
|
2345
2645
|
new_pointer = [@byte_pointer, @line_index]
|
2346
2646
|
@previous_line_index = @line_index
|
2347
2647
|
@byte_pointer, @line_index = @mark_pointer
|