reline 0.1.4 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/reline.rb +16 -6
- data/lib/reline/ansi.rb +48 -7
- data/lib/reline/config.rb +37 -4
- data/lib/reline/general_io.rb +18 -0
- data/lib/reline/key_actor/vi_command.rb +2 -2
- data/lib/reline/key_stroke.rb +2 -0
- data/lib/reline/line_editor.rb +233 -130
- data/lib/reline/sibori.rb +170 -0
- data/lib/reline/unicode.rb +112 -15
- data/lib/reline/unicode/east_asian_width.rb +1149 -1130
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +26 -3
- metadata +21 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 981888e54748ace72084309bf4d0b832503970956188b3865629da1c577b5edb
|
4
|
+
data.tar.gz: 11815b8b07d66ef247ab83e37d6c8884fdc4745e681b298237e2e32b631c4cbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b9df7fa6a4e7196773314b8e5287021fbdfcdd81372740c0c32d60c03cc4aed8fda02f5b14e337c428c160b1ff5a2c4144f5a5866af8f57e50fcb25b51b9099
|
7
|
+
data.tar.gz: 50920eaeb2ef94d831c4eb4d798e41ccf9c7a826f237b1fc3343969e6af2ea05332cb38e4638c2fa439a29231a3789b0f4d0ee731d3106aaa6f6d08b12cbcb39
|
data/lib/reline.rb
CHANGED
@@ -7,6 +7,7 @@ require 'reline/key_actor'
|
|
7
7
|
require 'reline/key_stroke'
|
8
8
|
require 'reline/line_editor'
|
9
9
|
require 'reline/history'
|
10
|
+
require 'rbconfig'
|
10
11
|
|
11
12
|
module Reline
|
12
13
|
FILENAME_COMPLETION_PROC = nil
|
@@ -98,22 +99,22 @@ module Reline
|
|
98
99
|
end
|
99
100
|
|
100
101
|
def completion_proc=(p)
|
101
|
-
raise ArgumentError unless p.respond_to?(:call)
|
102
|
+
raise ArgumentError unless p.respond_to?(:call) or p.nil?
|
102
103
|
@completion_proc = p
|
103
104
|
end
|
104
105
|
|
105
106
|
def output_modifier_proc=(p)
|
106
|
-
raise ArgumentError unless p.respond_to?(:call)
|
107
|
+
raise ArgumentError unless p.respond_to?(:call) or p.nil?
|
107
108
|
@output_modifier_proc = p
|
108
109
|
end
|
109
110
|
|
110
111
|
def prompt_proc=(p)
|
111
|
-
raise ArgumentError unless p.respond_to?(:call)
|
112
|
+
raise ArgumentError unless p.respond_to?(:call) or p.nil?
|
112
113
|
@prompt_proc = p
|
113
114
|
end
|
114
115
|
|
115
116
|
def auto_indent_proc=(p)
|
116
|
-
raise ArgumentError unless p.respond_to?(:call)
|
117
|
+
raise ArgumentError unless p.respond_to?(:call) or p.nil?
|
117
118
|
@auto_indent_proc = p
|
118
119
|
end
|
119
120
|
|
@@ -122,7 +123,7 @@ module Reline
|
|
122
123
|
end
|
123
124
|
|
124
125
|
def dig_perfect_match_proc=(p)
|
125
|
-
raise ArgumentError unless p.respond_to?(:call)
|
126
|
+
raise ArgumentError unless p.respond_to?(:call) or p.nil?
|
126
127
|
@dig_perfect_match_proc = p
|
127
128
|
end
|
128
129
|
|
@@ -222,7 +223,6 @@ module Reline
|
|
222
223
|
line_editor.auto_indent_proc = auto_indent_proc
|
223
224
|
line_editor.dig_perfect_match_proc = dig_perfect_match_proc
|
224
225
|
line_editor.pre_input_hook = pre_input_hook
|
225
|
-
line_editor.rerender
|
226
226
|
|
227
227
|
unless config.test_mode
|
228
228
|
config.read
|
@@ -232,17 +232,27 @@ module Reline
|
|
232
232
|
end
|
233
233
|
end
|
234
234
|
|
235
|
+
line_editor.rerender
|
236
|
+
|
235
237
|
begin
|
238
|
+
prev_pasting_state = false
|
236
239
|
loop do
|
240
|
+
prev_pasting_state = Reline::IOGate.in_pasting?
|
237
241
|
read_io(config.keyseq_timeout) { |inputs|
|
238
242
|
inputs.each { |c|
|
239
243
|
line_editor.input_key(c)
|
240
244
|
line_editor.rerender
|
241
245
|
}
|
242
246
|
}
|
247
|
+
if prev_pasting_state == true and not Reline::IOGate.in_pasting? and not line_editor.finished?
|
248
|
+
prev_pasting_state = false
|
249
|
+
line_editor.rerender_all
|
250
|
+
end
|
243
251
|
break if line_editor.finished?
|
244
252
|
end
|
245
253
|
Reline::IOGate.move_cursor_column(0)
|
254
|
+
rescue Errno::EIO
|
255
|
+
# Maybe the I/O has been closed.
|
246
256
|
rescue StandardError => e
|
247
257
|
line_editor.finalize
|
248
258
|
Reline::IOGate.deprep(otio)
|
data/lib/reline/ansi.rb
CHANGED
@@ -28,12 +28,22 @@ class Reline::ANSI
|
|
28
28
|
[27, 71, 67] => :ed_next_char, # →
|
29
29
|
[27, 71, 68] => :ed_prev_char, # ←
|
30
30
|
|
31
|
+
# urxvt / exoterm
|
32
|
+
[27, 91, 55, 126] => :ed_move_to_beg, # Home
|
33
|
+
[27, 91, 56, 126] => :ed_move_to_end, # End
|
34
|
+
|
31
35
|
# GNOME
|
32
36
|
[27, 79, 72] => :ed_move_to_beg, # Home
|
33
37
|
[27, 79, 70] => :ed_move_to_end, # End
|
34
38
|
# Del is 0x08
|
35
39
|
# Arrow keys are the same of KDE
|
36
40
|
|
41
|
+
# iTerm2
|
42
|
+
[27, 27, 91, 67] => :em_next_word, # Option+→
|
43
|
+
[27, 27, 91, 68] => :ed_prev_word, # Option+←
|
44
|
+
[195, 166] => :em_next_word, # Option+f
|
45
|
+
[195, 162] => :ed_prev_word, # Option+b
|
46
|
+
|
37
47
|
# others
|
38
48
|
[27, 32] => :em_set_mark, # M-<space>
|
39
49
|
[24, 24] => :em_exchange_mark, # C-x C-x TODO also add Windows
|
@@ -61,8 +71,29 @@ class Reline::ANSI
|
|
61
71
|
unless @@buf.empty?
|
62
72
|
return @@buf.shift
|
63
73
|
end
|
64
|
-
c = @@input.raw(intr: true, &:getbyte)
|
74
|
+
until c = @@input.raw(intr: true, &:getbyte)
|
75
|
+
sleep 0.1
|
76
|
+
end
|
65
77
|
(c == 0x16 && @@input.raw(min: 0, tim: 0, &:getbyte)) || c
|
78
|
+
rescue Errno::EIO
|
79
|
+
# Maybe the I/O has been closed.
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.in_pasting?
|
84
|
+
not Reline::IOGate.empty_buffer?
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.empty_buffer?
|
88
|
+
unless @@buf.empty?
|
89
|
+
return false
|
90
|
+
end
|
91
|
+
rs, = IO.select([@@input], [], [], 0.00001)
|
92
|
+
if rs and rs[0]
|
93
|
+
false
|
94
|
+
else
|
95
|
+
true
|
96
|
+
end
|
66
97
|
end
|
67
98
|
|
68
99
|
def self.ungetc(c)
|
@@ -105,10 +136,13 @@ class Reline::ANSI
|
|
105
136
|
@@input.raw do |stdin|
|
106
137
|
@@output << "\e[6n"
|
107
138
|
@@output.flush
|
108
|
-
|
109
|
-
|
139
|
+
loop do
|
140
|
+
c = stdin.getc
|
141
|
+
next if c.nil?
|
142
|
+
res << c
|
143
|
+
m = res.match(/\e\[(?<row>\d+);(?<column>\d+)R/)
|
144
|
+
break if m
|
110
145
|
end
|
111
|
-
m = res.match(/\e\[(?<row>\d+);(?<column>\d+)/)
|
112
146
|
(m.pre_match + m.post_match).chars.reverse_each do |ch|
|
113
147
|
stdin.ungetc ch
|
114
148
|
end
|
@@ -116,9 +150,16 @@ class Reline::ANSI
|
|
116
150
|
column = m[:column].to_i - 1
|
117
151
|
row = m[:row].to_i - 1
|
118
152
|
rescue Errno::ENOTTY
|
119
|
-
|
120
|
-
|
121
|
-
|
153
|
+
begin
|
154
|
+
buf = @@output.pread(@@output.pos, 0)
|
155
|
+
row = buf.count("\n")
|
156
|
+
column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
|
157
|
+
rescue Errno::ESPIPE
|
158
|
+
# Just returns column 1 for ambiguous width because this I/O is not
|
159
|
+
# tty and can't seek.
|
160
|
+
row = 0
|
161
|
+
column = 1
|
162
|
+
end
|
122
163
|
end
|
123
164
|
Reline::CursorPos.new(column, row)
|
124
165
|
end
|
data/lib/reline/config.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'pathname'
|
2
|
-
|
3
1
|
class Reline::Config
|
4
2
|
attr_reader :test_mode
|
5
3
|
|
@@ -35,6 +33,10 @@ class Reline::Config
|
|
35
33
|
show-all-if-ambiguous
|
36
34
|
show-all-if-unmodified
|
37
35
|
visible-stats
|
36
|
+
show-mode-in-prompt
|
37
|
+
vi-cmd-mode-icon
|
38
|
+
vi-ins-mode-icon
|
39
|
+
emacs-mode-string
|
38
40
|
}
|
39
41
|
VARIABLE_NAME_SYMBOLS = VARIABLE_NAMES.map { |v| :"#{v.tr(?-, ?_)}" }
|
40
42
|
VARIABLE_NAME_SYMBOLS.each do |v|
|
@@ -52,6 +54,10 @@ class Reline::Config
|
|
52
54
|
@key_actors[:emacs] = Reline::KeyActor::Emacs.new
|
53
55
|
@key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new
|
54
56
|
@key_actors[:vi_command] = Reline::KeyActor::ViCommand.new
|
57
|
+
@vi_cmd_mode_icon = '(cmd)'
|
58
|
+
@vi_ins_mode_icon = '(ins)'
|
59
|
+
@emacs_mode_string = '@'
|
60
|
+
# https://tiswww.case.edu/php/chet/readline/readline.html#IDX25
|
55
61
|
@history_size = -1 # unlimited
|
56
62
|
@keyseq_timeout = 500
|
57
63
|
@test_mode = false
|
@@ -159,7 +165,7 @@ class Reline::Config
|
|
159
165
|
|
160
166
|
case line
|
161
167
|
when /^set +([^ ]+) +([^ ]+)/i
|
162
|
-
var, value = $1.downcase, $2
|
168
|
+
var, value = $1.downcase, $2
|
163
169
|
bind_variable(var, value)
|
164
170
|
next
|
165
171
|
when /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
|
@@ -209,7 +215,11 @@ class Reline::Config
|
|
209
215
|
def bind_variable(name, value)
|
210
216
|
case name
|
211
217
|
when 'history-size'
|
212
|
-
|
218
|
+
begin
|
219
|
+
@history_size = Integer(value)
|
220
|
+
rescue ArgumentError
|
221
|
+
@history_size = 500
|
222
|
+
end
|
213
223
|
when 'bell-style'
|
214
224
|
@bell_style =
|
215
225
|
case value
|
@@ -248,12 +258,35 @@ class Reline::Config
|
|
248
258
|
end
|
249
259
|
when 'keyseq-timeout'
|
250
260
|
@keyseq_timeout = value.to_i
|
261
|
+
when 'show-mode-in-prompt'
|
262
|
+
case value
|
263
|
+
when 'off'
|
264
|
+
@show_mode_in_prompt = false
|
265
|
+
when 'on'
|
266
|
+
@show_mode_in_prompt = true
|
267
|
+
else
|
268
|
+
@show_mode_in_prompt = false
|
269
|
+
end
|
270
|
+
when 'vi-cmd-mode-string'
|
271
|
+
@vi_cmd_mode_icon = retrieve_string(value)
|
272
|
+
when 'vi-ins-mode-string'
|
273
|
+
@vi_ins_mode_icon = retrieve_string(value)
|
274
|
+
when 'emacs-mode-string'
|
275
|
+
@emacs_mode_string = retrieve_string(value)
|
251
276
|
when *VARIABLE_NAMES then
|
252
277
|
variable_name = :"@#{name.tr(?-, ?_)}"
|
253
278
|
instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
|
254
279
|
end
|
255
280
|
end
|
256
281
|
|
282
|
+
def retrieve_string(str)
|
283
|
+
if str =~ /\A"(.*)"\z/
|
284
|
+
parse_keyseq($1).map(&:chr).join
|
285
|
+
else
|
286
|
+
parse_keyseq(str).map(&:chr).join
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
257
290
|
def bind_key(key, func_name)
|
258
291
|
if key =~ /\A"(.*)"\z/
|
259
292
|
keyseq = parse_keyseq($1)
|
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,6 +71,20 @@ class Reline::GeneralIO
|
|
67
71
|
def self.set_winch_handler(&handler)
|
68
72
|
end
|
69
73
|
|
74
|
+
@@pasting = false
|
75
|
+
|
76
|
+
def self.in_pasting?
|
77
|
+
@@pasting
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.start_pasting
|
81
|
+
@@pasting = true
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.finish_pasting
|
85
|
+
@@pasting = false
|
86
|
+
end
|
87
|
+
|
70
88
|
def self.prep
|
71
89
|
end
|
72
90
|
|
@@ -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/key_stroke.rb
CHANGED
data/lib/reline/line_editor.rb
CHANGED
@@ -2,7 +2,6 @@ require 'reline/kill_ring'
|
|
2
2
|
require 'reline/unicode'
|
3
3
|
|
4
4
|
require 'tempfile'
|
5
|
-
require 'pathname'
|
6
5
|
|
7
6
|
class Reline::LineEditor
|
8
7
|
# TODO: undo
|
@@ -51,18 +50,20 @@ class Reline::LineEditor
|
|
51
50
|
CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer)
|
52
51
|
MenuInfo = Struct.new('MenuInfo', :target, :list)
|
53
52
|
|
54
|
-
CSI_REGEXP = /\e\[[\d;]*[ABCDEFGHJKSTfminsuhl]/
|
55
|
-
OSC_REGEXP = /\e\]\d+(?:;[^;]+)*\a/
|
56
|
-
NON_PRINTING_START = "\1"
|
57
|
-
NON_PRINTING_END = "\2"
|
58
|
-
WIDTH_SCANNER = /\G(?:#{NON_PRINTING_START}|#{NON_PRINTING_END}|#{CSI_REGEXP}|#{OSC_REGEXP}|\X)/
|
59
|
-
|
60
53
|
def initialize(config, encoding)
|
61
54
|
@config = config
|
62
55
|
@completion_append_character = ''
|
63
56
|
reset_variables(encoding: encoding)
|
64
57
|
end
|
65
58
|
|
59
|
+
def simplified_rendering?
|
60
|
+
if finished?
|
61
|
+
false
|
62
|
+
else
|
63
|
+
not @rerender_all and not finished? and Reline::IOGate.in_pasting?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
66
67
|
private def check_multiline_prompt(buffer, prompt)
|
67
68
|
if @vi_arg
|
68
69
|
prompt = "(arg: #{@vi_arg}) "
|
@@ -73,14 +74,39 @@ class Reline::LineEditor
|
|
73
74
|
else
|
74
75
|
prompt = @prompt
|
75
76
|
end
|
77
|
+
return [prompt, calculate_width(prompt, true), [prompt] * buffer.size] if simplified_rendering?
|
76
78
|
if @prompt_proc
|
77
79
|
prompt_list = @prompt_proc.(buffer)
|
78
80
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
81
|
+
if @config.show_mode_in_prompt
|
82
|
+
if @config.editing_mode_is?(:vi_command)
|
83
|
+
mode_icon = @config.vi_cmd_mode_icon
|
84
|
+
elsif @config.editing_mode_is?(:vi_insert)
|
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 = '?'
|
90
|
+
end
|
91
|
+
prompt_list.map!{ |pr| mode_icon + pr }
|
92
|
+
end
|
79
93
|
prompt = prompt_list[@line_index]
|
80
94
|
prompt_width = calculate_width(prompt, true)
|
81
95
|
[prompt, prompt_width, prompt_list]
|
82
96
|
else
|
83
97
|
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
|
84
110
|
[prompt, prompt_width, nil]
|
85
111
|
end
|
86
112
|
end
|
@@ -116,7 +142,7 @@ class Reline::LineEditor
|
|
116
142
|
if @line_index.zero?
|
117
143
|
0
|
118
144
|
else
|
119
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
|
145
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
120
146
|
end
|
121
147
|
if @prompt_proc
|
122
148
|
prompt = prompt_list[@line_index]
|
@@ -153,6 +179,7 @@ class Reline::LineEditor
|
|
153
179
|
@vi_arg = nil
|
154
180
|
@waiting_proc = nil
|
155
181
|
@waiting_operator_proc = nil
|
182
|
+
@waiting_operator_vi_arg = nil
|
156
183
|
@completion_journey_data = nil
|
157
184
|
@completion_state = CompletionState::NORMAL
|
158
185
|
@perfect_matched = nil
|
@@ -160,7 +187,9 @@ class Reline::LineEditor
|
|
160
187
|
@first_prompt = true
|
161
188
|
@searching_prompt = nil
|
162
189
|
@first_char = true
|
190
|
+
@add_newline_to_end_of_buffer = false
|
163
191
|
@eof = false
|
192
|
+
@continuous_insertion_buffer = String.new(encoding: @encoding)
|
164
193
|
reset_line
|
165
194
|
end
|
166
195
|
|
@@ -190,10 +219,10 @@ class Reline::LineEditor
|
|
190
219
|
@is_multiline = false
|
191
220
|
end
|
192
221
|
|
193
|
-
private def calculate_height_by_lines(lines,
|
222
|
+
private def calculate_height_by_lines(lines, prompt)
|
194
223
|
result = 0
|
224
|
+
prompt_list = prompt.is_a?(Array) ? prompt : nil
|
195
225
|
lines.each_with_index { |line, i|
|
196
|
-
prompt = ''
|
197
226
|
prompt = prompt_list[i] if prompt_list and prompt_list[i]
|
198
227
|
result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
|
199
228
|
}
|
@@ -211,40 +240,8 @@ class Reline::LineEditor
|
|
211
240
|
width.div(@screen_size.last) + 1
|
212
241
|
end
|
213
242
|
|
214
|
-
private def split_by_width(
|
215
|
-
|
216
|
-
height = 1
|
217
|
-
width = 0
|
218
|
-
rest = "#{prompt}#{str}".encode(Encoding::UTF_8)
|
219
|
-
in_zero_width = false
|
220
|
-
rest.scan(WIDTH_SCANNER) do |gc|
|
221
|
-
case gc
|
222
|
-
when NON_PRINTING_START
|
223
|
-
in_zero_width = true
|
224
|
-
when NON_PRINTING_END
|
225
|
-
in_zero_width = false
|
226
|
-
when CSI_REGEXP, OSC_REGEXP
|
227
|
-
lines.last << gc
|
228
|
-
else
|
229
|
-
unless in_zero_width
|
230
|
-
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
|
231
|
-
if (width += mbchar_width) > max_width
|
232
|
-
width = mbchar_width
|
233
|
-
lines << nil
|
234
|
-
lines << String.new(encoding: @encoding)
|
235
|
-
height += 1
|
236
|
-
end
|
237
|
-
end
|
238
|
-
lines.last << gc
|
239
|
-
end
|
240
|
-
end
|
241
|
-
# The cursor moves to next line in first
|
242
|
-
if width == max_width
|
243
|
-
lines << nil
|
244
|
-
lines << String.new(encoding: @encoding)
|
245
|
-
height += 1
|
246
|
-
end
|
247
|
-
[lines, height]
|
243
|
+
private def split_by_width(str, max_width)
|
244
|
+
Reline::Unicode.split_by_width(str, max_width, @encoding)
|
248
245
|
end
|
249
246
|
|
250
247
|
private def scroll_down(val)
|
@@ -312,6 +309,11 @@ class Reline::LineEditor
|
|
312
309
|
@byte_pointer = new_byte_pointer
|
313
310
|
end
|
314
311
|
|
312
|
+
def rerender_all
|
313
|
+
@rerender_all = true
|
314
|
+
rerender
|
315
|
+
end
|
316
|
+
|
315
317
|
def rerender
|
316
318
|
return if @line.nil?
|
317
319
|
if @menu_info
|
@@ -351,14 +353,29 @@ class Reline::LineEditor
|
|
351
353
|
end
|
352
354
|
new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
|
353
355
|
# FIXME: end of logical line sometimes breaks
|
354
|
-
if @
|
356
|
+
if @add_newline_to_end_of_buffer
|
357
|
+
scroll_down(1)
|
358
|
+
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
359
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
360
|
+
@buffer_of_lines[@previous_line_index] = @line
|
361
|
+
@line = @buffer_of_lines[@line_index]
|
362
|
+
render_partial(prompt, prompt_width, @line, false)
|
363
|
+
@cursor = @cursor_max = calculate_width(@line)
|
364
|
+
@byte_pointer = @line.bytesize
|
365
|
+
@highest_in_all += @highest_in_this
|
366
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
367
|
+
@first_line_started_from += @started_from + 1
|
368
|
+
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
369
|
+
@previous_line_index = nil
|
370
|
+
@add_newline_to_end_of_buffer = false
|
371
|
+
elsif @previous_line_index or new_highest_in_this != @highest_in_this
|
355
372
|
if @previous_line_index
|
356
373
|
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
357
374
|
else
|
358
375
|
new_lines = whole_lines
|
359
376
|
end
|
360
377
|
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
361
|
-
all_height = calculate_height_by_lines(new_lines, prompt_list)
|
378
|
+
all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
|
362
379
|
diff = all_height - @highest_in_all
|
363
380
|
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
364
381
|
if diff > 0
|
@@ -398,7 +415,7 @@ class Reline::LineEditor
|
|
398
415
|
if @line_index.zero?
|
399
416
|
0
|
400
417
|
else
|
401
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
|
418
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
402
419
|
end
|
403
420
|
if @prompt_proc
|
404
421
|
prompt = prompt_list[@line_index]
|
@@ -457,7 +474,7 @@ class Reline::LineEditor
|
|
457
474
|
if @line_index.zero?
|
458
475
|
0
|
459
476
|
else
|
460
|
-
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list)
|
477
|
+
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
461
478
|
end
|
462
479
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
463
480
|
move_cursor_down(@first_line_started_from + @started_from)
|
@@ -488,7 +505,7 @@ class Reline::LineEditor
|
|
488
505
|
end
|
489
506
|
|
490
507
|
private def render_partial(prompt, prompt_width, line_to_render, with_control = true)
|
491
|
-
visual_lines, height = split_by_width(
|
508
|
+
visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
|
492
509
|
if with_control
|
493
510
|
if height > @highest_in_this
|
494
511
|
diff = height - @highest_in_this
|
@@ -507,8 +524,18 @@ class Reline::LineEditor
|
|
507
524
|
Reline::IOGate.move_cursor_column(0)
|
508
525
|
visual_lines.each_with_index do |line, index|
|
509
526
|
if line.nil?
|
510
|
-
if
|
511
|
-
#
|
527
|
+
if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
528
|
+
# reaches the end of line
|
529
|
+
if Reline::IOGate.win?
|
530
|
+
# A newline is automatically inserted if a character is rendered at
|
531
|
+
# eol on command prompt.
|
532
|
+
else
|
533
|
+
# When the cursor is at the end of the line and erases characters
|
534
|
+
# after the cursor, some terminals delete the character at the
|
535
|
+
# cursor position.
|
536
|
+
move_cursor_down(1)
|
537
|
+
Reline::IOGate.move_cursor_column(0)
|
538
|
+
end
|
512
539
|
else
|
513
540
|
Reline::IOGate.erase_after_cursor
|
514
541
|
move_cursor_down(1)
|
@@ -528,22 +555,25 @@ class Reline::LineEditor
|
|
528
555
|
end
|
529
556
|
end
|
530
557
|
Reline::IOGate.erase_after_cursor
|
558
|
+
Reline::IOGate.move_cursor_column(0)
|
531
559
|
if with_control
|
532
|
-
|
560
|
+
# Just after rendring, so the cursor is on the last line.
|
533
561
|
if finished?
|
534
|
-
|
562
|
+
Reline::IOGate.move_cursor_column(0)
|
563
|
+
else
|
564
|
+
# Moves up from bottom of lines to the cursor position.
|
565
|
+
move_cursor_up(height - 1 - @started_from)
|
566
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
535
567
|
end
|
536
|
-
move_cursor_down(@started_from)
|
537
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
538
568
|
end
|
539
569
|
height
|
540
570
|
end
|
541
571
|
|
542
572
|
private def modify_lines(before)
|
543
|
-
return before if before.nil? || before.empty?
|
573
|
+
return before if before.nil? || before.empty? || simplified_rendering?
|
544
574
|
|
545
575
|
if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: finished?)
|
546
|
-
after.lines("\n"
|
576
|
+
after.lines("\n").map { |l| l.chomp('') }
|
547
577
|
else
|
548
578
|
before
|
549
579
|
end
|
@@ -568,7 +598,7 @@ class Reline::LineEditor
|
|
568
598
|
else
|
569
599
|
i&.start_with?(target)
|
570
600
|
end
|
571
|
-
}
|
601
|
+
}.uniq
|
572
602
|
if is_menu
|
573
603
|
menu(target, list)
|
574
604
|
return nil
|
@@ -685,7 +715,8 @@ class Reline::LineEditor
|
|
685
715
|
if @waiting_operator_proc
|
686
716
|
if VI_MOTIONS.include?(method_symbol)
|
687
717
|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
|
688
|
-
|
718
|
+
@vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg > 1
|
719
|
+
block.(true)
|
689
720
|
unless @waiting_proc
|
690
721
|
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
|
691
722
|
@cursor, @byte_pointer = old_cursor, old_byte_pointer
|
@@ -693,27 +724,56 @@ class Reline::LineEditor
|
|
693
724
|
else
|
694
725
|
old_waiting_proc = @waiting_proc
|
695
726
|
old_waiting_operator_proc = @waiting_operator_proc
|
727
|
+
current_waiting_operator_proc = @waiting_operator_proc
|
696
728
|
@waiting_proc = proc { |k|
|
697
729
|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
|
698
730
|
old_waiting_proc.(k)
|
699
731
|
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
|
700
732
|
@cursor, @byte_pointer = old_cursor, old_byte_pointer
|
701
|
-
|
733
|
+
current_waiting_operator_proc.(cursor_diff, byte_pointer_diff)
|
702
734
|
@waiting_operator_proc = old_waiting_operator_proc
|
703
735
|
}
|
704
736
|
end
|
705
737
|
else
|
706
738
|
# Ignores operator when not motion is given.
|
707
|
-
block.()
|
739
|
+
block.(false)
|
708
740
|
end
|
709
741
|
@waiting_operator_proc = nil
|
742
|
+
@waiting_operator_vi_arg = nil
|
743
|
+
@vi_arg = nil
|
710
744
|
else
|
711
|
-
block.()
|
745
|
+
block.(false)
|
712
746
|
end
|
713
747
|
end
|
714
748
|
|
715
749
|
private def argumentable?(method_obj)
|
716
|
-
method_obj and method_obj.parameters.
|
750
|
+
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :arg }
|
751
|
+
end
|
752
|
+
|
753
|
+
private def inclusive?(method_obj)
|
754
|
+
# If a motion method with the keyword argument "inclusive" follows the
|
755
|
+
# operator, it must contain the character at the cursor position.
|
756
|
+
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :inclusive }
|
757
|
+
end
|
758
|
+
|
759
|
+
def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
|
760
|
+
if @config.editing_mode_is?(:emacs, :vi_insert) and @waiting_proc.nil? and @waiting_operator_proc.nil?
|
761
|
+
not_insertion = method_symbol != :ed_insert
|
762
|
+
process_insert(force: not_insertion)
|
763
|
+
end
|
764
|
+
if @vi_arg and argumentable?(method_obj)
|
765
|
+
if with_operator and inclusive?(method_obj)
|
766
|
+
method_obj.(key, arg: @vi_arg, inclusive: true)
|
767
|
+
else
|
768
|
+
method_obj.(key, arg: @vi_arg)
|
769
|
+
end
|
770
|
+
else
|
771
|
+
if with_operator and inclusive?(method_obj)
|
772
|
+
method_obj.(key, inclusive: true)
|
773
|
+
else
|
774
|
+
method_obj.(key)
|
775
|
+
end
|
776
|
+
end
|
717
777
|
end
|
718
778
|
|
719
779
|
private def process_key(key, method_symbol)
|
@@ -724,11 +784,11 @@ class Reline::LineEditor
|
|
724
784
|
end
|
725
785
|
if method_symbol and key.is_a?(Symbol)
|
726
786
|
if @vi_arg and argumentable?(method_obj)
|
727
|
-
run_for_operators(key, method_symbol) do
|
728
|
-
method_obj
|
787
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
788
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
729
789
|
end
|
730
790
|
else
|
731
|
-
method_obj
|
791
|
+
wrap_method_call(method_symbol, method_obj, key) if method_obj
|
732
792
|
end
|
733
793
|
@kill_ring.process
|
734
794
|
@vi_arg = nil
|
@@ -737,15 +797,15 @@ class Reline::LineEditor
|
|
737
797
|
ed_argument_digit(key)
|
738
798
|
else
|
739
799
|
if argumentable?(method_obj)
|
740
|
-
run_for_operators(key, method_symbol) do
|
741
|
-
method_obj
|
800
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
801
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
742
802
|
end
|
743
803
|
elsif @waiting_proc
|
744
804
|
@waiting_proc.(key)
|
745
805
|
elsif method_obj
|
746
|
-
method_obj
|
806
|
+
wrap_method_call(method_symbol, method_obj, key)
|
747
807
|
else
|
748
|
-
ed_insert(key)
|
808
|
+
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
749
809
|
end
|
750
810
|
@kill_ring.process
|
751
811
|
@vi_arg = nil
|
@@ -755,15 +815,15 @@ class Reline::LineEditor
|
|
755
815
|
@kill_ring.process
|
756
816
|
elsif method_obj
|
757
817
|
if method_symbol == :ed_argument_digit
|
758
|
-
method_obj
|
818
|
+
wrap_method_call(method_symbol, method_obj, key)
|
759
819
|
else
|
760
|
-
run_for_operators(key, method_symbol) do
|
761
|
-
method_obj
|
820
|
+
run_for_operators(key, method_symbol) do |with_operator|
|
821
|
+
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
762
822
|
end
|
763
823
|
end
|
764
824
|
@kill_ring.process
|
765
825
|
else
|
766
|
-
ed_insert(key)
|
826
|
+
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
767
827
|
end
|
768
828
|
end
|
769
829
|
|
@@ -820,6 +880,7 @@ class Reline::LineEditor
|
|
820
880
|
result = call_completion_proc
|
821
881
|
if result.is_a?(Array)
|
822
882
|
completion_occurs = true
|
883
|
+
process_insert
|
823
884
|
complete(result)
|
824
885
|
end
|
825
886
|
end
|
@@ -828,6 +889,7 @@ class Reline::LineEditor
|
|
828
889
|
result = call_completion_proc
|
829
890
|
if result.is_a?(Array)
|
830
891
|
completion_occurs = true
|
892
|
+
process_insert
|
831
893
|
move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
|
832
894
|
end
|
833
895
|
end
|
@@ -839,7 +901,7 @@ class Reline::LineEditor
|
|
839
901
|
unless completion_occurs
|
840
902
|
@completion_state = CompletionState::NORMAL
|
841
903
|
end
|
842
|
-
if @is_multiline and @auto_indent_proc
|
904
|
+
if @is_multiline and @auto_indent_proc and not simplified_rendering?
|
843
905
|
process_auto_indent
|
844
906
|
end
|
845
907
|
end
|
@@ -1041,6 +1103,7 @@ class Reline::LineEditor
|
|
1041
1103
|
|
1042
1104
|
def finish
|
1043
1105
|
@finished = true
|
1106
|
+
@rerender_all = true
|
1044
1107
|
@config.reset
|
1045
1108
|
end
|
1046
1109
|
|
@@ -1058,29 +1121,7 @@ class Reline::LineEditor
|
|
1058
1121
|
end
|
1059
1122
|
|
1060
1123
|
private def calculate_width(str, allow_escape_code = false)
|
1061
|
-
|
1062
|
-
width = 0
|
1063
|
-
rest = str.encode(Encoding::UTF_8)
|
1064
|
-
in_zero_width = false
|
1065
|
-
rest.scan(WIDTH_SCANNER) do |gc|
|
1066
|
-
case gc
|
1067
|
-
when NON_PRINTING_START
|
1068
|
-
in_zero_width = true
|
1069
|
-
when NON_PRINTING_END
|
1070
|
-
in_zero_width = false
|
1071
|
-
when CSI_REGEXP, OSC_REGEXP
|
1072
|
-
else
|
1073
|
-
unless in_zero_width
|
1074
|
-
width += Reline::Unicode.get_mbchar_width(gc)
|
1075
|
-
end
|
1076
|
-
end
|
1077
|
-
end
|
1078
|
-
width
|
1079
|
-
else
|
1080
|
-
str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |w, gc|
|
1081
|
-
w + Reline::Unicode.get_mbchar_width(gc)
|
1082
|
-
}
|
1083
|
-
end
|
1124
|
+
Reline::Unicode.calculate_width(str, allow_escape_code)
|
1084
1125
|
end
|
1085
1126
|
|
1086
1127
|
private def key_delete(key)
|
@@ -1091,6 +1132,9 @@ class Reline::LineEditor
|
|
1091
1132
|
|
1092
1133
|
private def key_newline(key)
|
1093
1134
|
if @is_multiline
|
1135
|
+
if (@buffer_of_lines.size - 1) == @line_index and @line.bytesize == @byte_pointer
|
1136
|
+
@add_newline_to_end_of_buffer = true
|
1137
|
+
end
|
1094
1138
|
next_line = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
|
1095
1139
|
cursor_line = @line.byteslice(0, @byte_pointer)
|
1096
1140
|
insert_new_line(cursor_line, next_line)
|
@@ -1101,38 +1145,57 @@ class Reline::LineEditor
|
|
1101
1145
|
|
1102
1146
|
private def ed_unassigned(key) end # do nothing
|
1103
1147
|
|
1148
|
+
private def process_insert(force: false)
|
1149
|
+
return if @continuous_insertion_buffer.empty? or (Reline::IOGate.in_pasting? and not force)
|
1150
|
+
width = Reline::Unicode.calculate_width(@continuous_insertion_buffer)
|
1151
|
+
bytesize = @continuous_insertion_buffer.bytesize
|
1152
|
+
if @cursor == @cursor_max
|
1153
|
+
@line += @continuous_insertion_buffer
|
1154
|
+
else
|
1155
|
+
@line = byteinsert(@line, @byte_pointer, @continuous_insertion_buffer)
|
1156
|
+
end
|
1157
|
+
@byte_pointer += bytesize
|
1158
|
+
@cursor += width
|
1159
|
+
@cursor_max += width
|
1160
|
+
@continuous_insertion_buffer.clear
|
1161
|
+
end
|
1162
|
+
|
1104
1163
|
private def ed_insert(key)
|
1164
|
+
str = nil
|
1165
|
+
width = nil
|
1166
|
+
bytesize = nil
|
1105
1167
|
if key.instance_of?(String)
|
1106
1168
|
begin
|
1107
1169
|
key.encode(Encoding::UTF_8)
|
1108
1170
|
rescue Encoding::UndefinedConversionError
|
1109
1171
|
return
|
1110
1172
|
end
|
1111
|
-
|
1112
|
-
|
1113
|
-
@line += key
|
1114
|
-
else
|
1115
|
-
@line = byteinsert(@line, @byte_pointer, key)
|
1116
|
-
end
|
1117
|
-
@byte_pointer += key.bytesize
|
1118
|
-
@cursor += width
|
1119
|
-
@cursor_max += width
|
1173
|
+
str = key
|
1174
|
+
bytesize = key.bytesize
|
1120
1175
|
else
|
1121
1176
|
begin
|
1122
1177
|
key.chr.encode(Encoding::UTF_8)
|
1123
1178
|
rescue Encoding::UndefinedConversionError
|
1124
1179
|
return
|
1125
1180
|
end
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
@cursor_max += width
|
1181
|
+
str = key.chr
|
1182
|
+
bytesize = 1
|
1183
|
+
end
|
1184
|
+
if Reline::IOGate.in_pasting?
|
1185
|
+
@continuous_insertion_buffer << str
|
1186
|
+
return
|
1187
|
+
elsif not @continuous_insertion_buffer.empty?
|
1188
|
+
process_insert
|
1135
1189
|
end
|
1190
|
+
width = Reline::Unicode.get_mbchar_width(str)
|
1191
|
+
if @cursor == @cursor_max
|
1192
|
+
@line += str
|
1193
|
+
else
|
1194
|
+
@line = byteinsert(@line, @byte_pointer, str)
|
1195
|
+
end
|
1196
|
+
@byte_pointer += bytesize
|
1197
|
+
@cursor += width
|
1198
|
+
@cursor_max += width
|
1136
1199
|
end
|
1137
1200
|
alias_method :ed_digit, :ed_insert
|
1138
1201
|
alias_method :self_insert, :ed_insert
|
@@ -1189,6 +1252,7 @@ class Reline::LineEditor
|
|
1189
1252
|
arg -= 1
|
1190
1253
|
ed_prev_char(key, arg: arg) if arg > 0
|
1191
1254
|
end
|
1255
|
+
alias_method :backward_char, :ed_prev_char
|
1192
1256
|
|
1193
1257
|
private def vi_first_print(key)
|
1194
1258
|
@byte_pointer, @cursor = Reline::Unicode.vi_first_print(@line)
|
@@ -1258,7 +1322,7 @@ class Reline::LineEditor
|
|
1258
1322
|
if search_word.empty? and Reline.last_incremental_search
|
1259
1323
|
search_word = Reline.last_incremental_search
|
1260
1324
|
end
|
1261
|
-
if @history_pointer
|
1325
|
+
if @history_pointer
|
1262
1326
|
case prev_search_key
|
1263
1327
|
when "\C-r".ord
|
1264
1328
|
history_pointer_base = 0
|
@@ -1618,6 +1682,7 @@ class Reline::LineEditor
|
|
1618
1682
|
end
|
1619
1683
|
|
1620
1684
|
private def ed_newline(key)
|
1685
|
+
process_insert(force: true)
|
1621
1686
|
if @is_multiline
|
1622
1687
|
if @config.editing_mode_is?(:vi_command)
|
1623
1688
|
if @line_index < (@buffer_of_lines.size - 1)
|
@@ -1915,7 +1980,7 @@ class Reline::LineEditor
|
|
1915
1980
|
ed_prev_char(key)
|
1916
1981
|
@config.editing_mode = :vi_command
|
1917
1982
|
end
|
1918
|
-
alias_method :
|
1983
|
+
alias_method :vi_movement_mode, :vi_command_mode
|
1919
1984
|
|
1920
1985
|
private def vi_next_word(key, arg: 1)
|
1921
1986
|
if @line.bytesize > @byte_pointer
|
@@ -1937,13 +2002,22 @@ class Reline::LineEditor
|
|
1937
2002
|
vi_prev_word(key, arg: arg) if arg > 0
|
1938
2003
|
end
|
1939
2004
|
|
1940
|
-
private def vi_end_word(key, arg: 1)
|
2005
|
+
private def vi_end_word(key, arg: 1, inclusive: false)
|
1941
2006
|
if @line.bytesize > @byte_pointer
|
1942
2007
|
byte_size, width = Reline::Unicode.vi_forward_end_word(@line, @byte_pointer)
|
1943
2008
|
@byte_pointer += byte_size
|
1944
2009
|
@cursor += width
|
1945
2010
|
end
|
1946
2011
|
arg -= 1
|
2012
|
+
if inclusive and arg.zero?
|
2013
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2014
|
+
if byte_size > 0
|
2015
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
2016
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
2017
|
+
@byte_pointer += byte_size
|
2018
|
+
@cursor += width
|
2019
|
+
end
|
2020
|
+
end
|
1947
2021
|
vi_end_word(key, arg: arg) if arg > 0
|
1948
2022
|
end
|
1949
2023
|
|
@@ -1967,13 +2041,22 @@ class Reline::LineEditor
|
|
1967
2041
|
vi_prev_big_word(key, arg: arg) if arg > 0
|
1968
2042
|
end
|
1969
2043
|
|
1970
|
-
private def vi_end_big_word(key, arg: 1)
|
2044
|
+
private def vi_end_big_word(key, arg: 1, inclusive: false)
|
1971
2045
|
if @line.bytesize > @byte_pointer
|
1972
2046
|
byte_size, width = Reline::Unicode.vi_big_forward_end_word(@line, @byte_pointer)
|
1973
2047
|
@byte_pointer += byte_size
|
1974
2048
|
@cursor += width
|
1975
2049
|
end
|
1976
2050
|
arg -= 1
|
2051
|
+
if inclusive and arg.zero?
|
2052
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2053
|
+
if byte_size > 0
|
2054
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
2055
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
2056
|
+
@byte_pointer += byte_size
|
2057
|
+
@cursor += width
|
2058
|
+
end
|
2059
|
+
end
|
1977
2060
|
vi_end_big_word(key, arg: arg) if arg > 0
|
1978
2061
|
end
|
1979
2062
|
|
@@ -2028,7 +2111,7 @@ class Reline::LineEditor
|
|
2028
2111
|
@cursor = 0
|
2029
2112
|
end
|
2030
2113
|
|
2031
|
-
private def vi_change_meta(key)
|
2114
|
+
private def vi_change_meta(key, arg: 1)
|
2032
2115
|
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2033
2116
|
if byte_pointer_diff > 0
|
2034
2117
|
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
@@ -2041,9 +2124,10 @@ class Reline::LineEditor
|
|
2041
2124
|
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
2042
2125
|
@config.editing_mode = :vi_insert
|
2043
2126
|
}
|
2127
|
+
@waiting_operator_vi_arg = arg
|
2044
2128
|
end
|
2045
2129
|
|
2046
|
-
private def vi_delete_meta(key)
|
2130
|
+
private def vi_delete_meta(key, arg: 1)
|
2047
2131
|
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2048
2132
|
if byte_pointer_diff > 0
|
2049
2133
|
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
@@ -2055,9 +2139,19 @@ class Reline::LineEditor
|
|
2055
2139
|
@cursor_max -= cursor_diff.abs
|
2056
2140
|
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
2057
2141
|
}
|
2142
|
+
@waiting_operator_vi_arg = arg
|
2058
2143
|
end
|
2059
2144
|
|
2060
|
-
private def vi_yank(key)
|
2145
|
+
private def vi_yank(key, arg: 1)
|
2146
|
+
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
2147
|
+
if byte_pointer_diff > 0
|
2148
|
+
cut = @line.byteslice(@byte_pointer, byte_pointer_diff)
|
2149
|
+
elsif byte_pointer_diff < 0
|
2150
|
+
cut = @line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
2151
|
+
end
|
2152
|
+
copy_for_vi(cut)
|
2153
|
+
}
|
2154
|
+
@waiting_operator_vi_arg = arg
|
2061
2155
|
end
|
2062
2156
|
|
2063
2157
|
private def vi_list_or_eof(key)
|
@@ -2121,7 +2215,7 @@ class Reline::LineEditor
|
|
2121
2215
|
fp.path
|
2122
2216
|
}
|
2123
2217
|
system("#{ENV['EDITOR']} #{path}")
|
2124
|
-
@line =
|
2218
|
+
@line = File.read(path)
|
2125
2219
|
finish
|
2126
2220
|
end
|
2127
2221
|
|
@@ -2202,15 +2296,15 @@ class Reline::LineEditor
|
|
2202
2296
|
}
|
2203
2297
|
end
|
2204
2298
|
|
2205
|
-
private def vi_next_char(key, arg: 1)
|
2206
|
-
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg) }
|
2299
|
+
private def vi_next_char(key, arg: 1, inclusive: false)
|
2300
|
+
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, inclusive: inclusive) }
|
2207
2301
|
end
|
2208
2302
|
|
2209
|
-
private def vi_to_next_char(key, arg: 1)
|
2210
|
-
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, true) }
|
2303
|
+
private def vi_to_next_char(key, arg: 1, inclusive: false)
|
2304
|
+
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, need_prev_char: true, inclusive: inclusive) }
|
2211
2305
|
end
|
2212
2306
|
|
2213
|
-
private def search_next_char(key, arg, need_prev_char
|
2307
|
+
private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
|
2214
2308
|
if key.instance_of?(String)
|
2215
2309
|
inputed_char = key
|
2216
2310
|
else
|
@@ -2247,6 +2341,15 @@ class Reline::LineEditor
|
|
2247
2341
|
@byte_pointer += byte_size
|
2248
2342
|
@cursor += width
|
2249
2343
|
end
|
2344
|
+
if inclusive
|
2345
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
2346
|
+
if byte_size > 0
|
2347
|
+
c = @line.byteslice(@byte_pointer, byte_size)
|
2348
|
+
width = Reline::Unicode.get_mbchar_width(c)
|
2349
|
+
@byte_pointer += byte_size
|
2350
|
+
@cursor += width
|
2351
|
+
end
|
2352
|
+
end
|
2250
2353
|
@waiting_proc = nil
|
2251
2354
|
end
|
2252
2355
|
|
@@ -2318,10 +2421,10 @@ class Reline::LineEditor
|
|
2318
2421
|
alias_method :set_mark, :em_set_mark
|
2319
2422
|
|
2320
2423
|
private def em_exchange_mark(key)
|
2424
|
+
return unless @mark_pointer
|
2321
2425
|
new_pointer = [@byte_pointer, @line_index]
|
2322
2426
|
@previous_line_index = @line_index
|
2323
2427
|
@byte_pointer, @line_index = @mark_pointer
|
2324
|
-
@byte_pointer, @line_index = @mark_pointer
|
2325
2428
|
@cursor = calculate_width(@line.byteslice(0, @byte_pointer))
|
2326
2429
|
@cursor_max = calculate_width(@line)
|
2327
2430
|
@mark_pointer = new_pointer
|