reline 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/reline.rb +10 -6
- data/lib/reline/ansi.rb +32 -7
- data/lib/reline/config.rb +37 -4
- data/lib/reline/key_stroke.rb +2 -0
- data/lib/reline/line_editor.rb +55 -81
- data/lib/reline/unicode.rb +68 -0
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +12 -3
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb0a1da0d5f51d50b82a78a3084a69ca37821b541e3c362bcb2b9a10a04664a3
|
4
|
+
data.tar.gz: bc1860cf76efa34984aef1dbafb60d1f7781b86ba44a9392dc5fbd8025631121
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4691bf1855429c5f1c25b16f66bc1e4793290579ae9a7345d1dd97c2694863bec59bab30edb232665b38a1f302a4e601c99111c84e5c277651ab2e53d4353982
|
7
|
+
data.tar.gz: 9ba7b27a82ce1d823f394c78ba1d995b6c4f71ddecb13c17c27fb39a13b7503c3b84402feb0239eb33d19d87f00d06c0608e35439413f6c72d2bdea50e84d10e
|
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,6 +232,8 @@ module Reline
|
|
232
232
|
end
|
233
233
|
end
|
234
234
|
|
235
|
+
line_editor.rerender
|
236
|
+
|
235
237
|
begin
|
236
238
|
loop do
|
237
239
|
read_io(config.keyseq_timeout) { |inputs|
|
@@ -243,6 +245,8 @@ module Reline
|
|
243
245
|
break if line_editor.finished?
|
244
246
|
end
|
245
247
|
Reline::IOGate.move_cursor_column(0)
|
248
|
+
rescue Errno::EIO
|
249
|
+
# Maybe the I/O has been closed.
|
246
250
|
rescue StandardError => e
|
247
251
|
line_editor.finalize
|
248
252
|
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,13 @@ 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
|
66
81
|
end
|
67
82
|
|
68
83
|
def self.ungetc(c)
|
@@ -105,10 +120,13 @@ class Reline::ANSI
|
|
105
120
|
@@input.raw do |stdin|
|
106
121
|
@@output << "\e[6n"
|
107
122
|
@@output.flush
|
108
|
-
|
109
|
-
|
123
|
+
loop do
|
124
|
+
c = stdin.getc
|
125
|
+
next if c.nil?
|
126
|
+
res << c
|
127
|
+
m = res.match(/\e\[(?<row>\d+);(?<column>\d+)R/)
|
128
|
+
break if m
|
110
129
|
end
|
111
|
-
m = res.match(/\e\[(?<row>\d+);(?<column>\d+)/)
|
112
130
|
(m.pre_match + m.post_match).chars.reverse_each do |ch|
|
113
131
|
stdin.ungetc ch
|
114
132
|
end
|
@@ -116,9 +134,16 @@ class Reline::ANSI
|
|
116
134
|
column = m[:column].to_i - 1
|
117
135
|
row = m[:row].to_i - 1
|
118
136
|
rescue Errno::ENOTTY
|
119
|
-
|
120
|
-
|
121
|
-
|
137
|
+
begin
|
138
|
+
buf = @@output.pread(@@output.pos, 0)
|
139
|
+
row = buf.count("\n")
|
140
|
+
column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
|
141
|
+
rescue Errno::ESPIPE
|
142
|
+
# Just returns column 1 for ambiguous width because this I/O is not
|
143
|
+
# tty and can't seek.
|
144
|
+
row = 0
|
145
|
+
column = 1
|
146
|
+
end
|
122
147
|
end
|
123
148
|
Reline::CursorPos.new(column, row)
|
124
149
|
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/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,12 +50,6 @@ 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 = ''
|
@@ -76,11 +69,35 @@ class Reline::LineEditor
|
|
76
69
|
if @prompt_proc
|
77
70
|
prompt_list = @prompt_proc.(buffer)
|
78
71
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
72
|
+
if @config.show_mode_in_prompt
|
73
|
+
if @config.editing_mode_is?(:vi_command)
|
74
|
+
mode_icon = @config.vi_cmd_mode_icon
|
75
|
+
elsif @config.editing_mode_is?(:vi_insert)
|
76
|
+
mode_icon = @config.vi_ins_mode_icon
|
77
|
+
elsif @config.editing_mode_is?(:emacs)
|
78
|
+
mode_icon = @config.emacs_mode_string
|
79
|
+
else
|
80
|
+
mode_icon = '?'
|
81
|
+
end
|
82
|
+
prompt_list.map!{ |pr| mode_icon + pr }
|
83
|
+
end
|
79
84
|
prompt = prompt_list[@line_index]
|
80
85
|
prompt_width = calculate_width(prompt, true)
|
81
86
|
[prompt, prompt_width, prompt_list]
|
82
87
|
else
|
83
88
|
prompt_width = calculate_width(prompt, true)
|
89
|
+
if @config.show_mode_in_prompt
|
90
|
+
if @config.editing_mode_is?(:vi_command)
|
91
|
+
mode_icon = @config.vi_cmd_mode_icon
|
92
|
+
elsif @config.editing_mode_is?(:vi_insert)
|
93
|
+
mode_icon = @config.vi_ins_mode_icon
|
94
|
+
elsif @config.editing_mode_is?(:emacs)
|
95
|
+
mode_icon = @config.emacs_mode_string
|
96
|
+
else
|
97
|
+
mode_icon = '?'
|
98
|
+
end
|
99
|
+
prompt = mode_icon + prompt
|
100
|
+
end
|
84
101
|
[prompt, prompt_width, nil]
|
85
102
|
end
|
86
103
|
end
|
@@ -116,7 +133,7 @@ class Reline::LineEditor
|
|
116
133
|
if @line_index.zero?
|
117
134
|
0
|
118
135
|
else
|
119
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
|
136
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
120
137
|
end
|
121
138
|
if @prompt_proc
|
122
139
|
prompt = prompt_list[@line_index]
|
@@ -190,10 +207,10 @@ class Reline::LineEditor
|
|
190
207
|
@is_multiline = false
|
191
208
|
end
|
192
209
|
|
193
|
-
private def calculate_height_by_lines(lines,
|
210
|
+
private def calculate_height_by_lines(lines, prompt)
|
194
211
|
result = 0
|
212
|
+
prompt_list = prompt.is_a?(Array) ? prompt : nil
|
195
213
|
lines.each_with_index { |line, i|
|
196
|
-
prompt = ''
|
197
214
|
prompt = prompt_list[i] if prompt_list and prompt_list[i]
|
198
215
|
result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
|
199
216
|
}
|
@@ -211,40 +228,8 @@ class Reline::LineEditor
|
|
211
228
|
width.div(@screen_size.last) + 1
|
212
229
|
end
|
213
230
|
|
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]
|
231
|
+
private def split_by_width(str, max_width)
|
232
|
+
Reline::Unicode.split_by_width(str, max_width, @encoding)
|
248
233
|
end
|
249
234
|
|
250
235
|
private def scroll_down(val)
|
@@ -358,7 +343,7 @@ class Reline::LineEditor
|
|
358
343
|
new_lines = whole_lines
|
359
344
|
end
|
360
345
|
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
361
|
-
all_height = calculate_height_by_lines(new_lines, prompt_list)
|
346
|
+
all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
|
362
347
|
diff = all_height - @highest_in_all
|
363
348
|
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
364
349
|
if diff > 0
|
@@ -398,7 +383,7 @@ class Reline::LineEditor
|
|
398
383
|
if @line_index.zero?
|
399
384
|
0
|
400
385
|
else
|
401
|
-
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
|
386
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
402
387
|
end
|
403
388
|
if @prompt_proc
|
404
389
|
prompt = prompt_list[@line_index]
|
@@ -457,7 +442,7 @@ class Reline::LineEditor
|
|
457
442
|
if @line_index.zero?
|
458
443
|
0
|
459
444
|
else
|
460
|
-
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list)
|
445
|
+
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
461
446
|
end
|
462
447
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
463
448
|
move_cursor_down(@first_line_started_from + @started_from)
|
@@ -488,7 +473,7 @@ class Reline::LineEditor
|
|
488
473
|
end
|
489
474
|
|
490
475
|
private def render_partial(prompt, prompt_width, line_to_render, with_control = true)
|
491
|
-
visual_lines, height = split_by_width(
|
476
|
+
visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
|
492
477
|
if with_control
|
493
478
|
if height > @highest_in_this
|
494
479
|
diff = height - @highest_in_this
|
@@ -507,8 +492,18 @@ class Reline::LineEditor
|
|
507
492
|
Reline::IOGate.move_cursor_column(0)
|
508
493
|
visual_lines.each_with_index do |line, index|
|
509
494
|
if line.nil?
|
510
|
-
if
|
511
|
-
#
|
495
|
+
if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
496
|
+
# reaches the end of line
|
497
|
+
if Reline::IOGate.win?
|
498
|
+
# A newline is automatically inserted if a character is rendered at
|
499
|
+
# eol on command prompt.
|
500
|
+
else
|
501
|
+
# When the cursor is at the end of the line and erases characters
|
502
|
+
# after the cursor, some terminals delete the character at the
|
503
|
+
# cursor position.
|
504
|
+
move_cursor_down(1)
|
505
|
+
Reline::IOGate.move_cursor_column(0)
|
506
|
+
end
|
512
507
|
else
|
513
508
|
Reline::IOGate.erase_after_cursor
|
514
509
|
move_cursor_down(1)
|
@@ -529,12 +524,14 @@ class Reline::LineEditor
|
|
529
524
|
end
|
530
525
|
Reline::IOGate.erase_after_cursor
|
531
526
|
if with_control
|
532
|
-
|
527
|
+
# Just after rendring, so the cursor is on the last line.
|
533
528
|
if finished?
|
534
|
-
|
529
|
+
Reline::IOGate.move_cursor_column(0)
|
530
|
+
else
|
531
|
+
# Moves up from bottom of lines to the cursor position.
|
532
|
+
move_cursor_up(height - 1 - @started_from)
|
533
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
535
534
|
end
|
536
|
-
move_cursor_down(@started_from)
|
537
|
-
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
538
535
|
end
|
539
536
|
height
|
540
537
|
end
|
@@ -543,7 +540,7 @@ class Reline::LineEditor
|
|
543
540
|
return before if before.nil? || before.empty?
|
544
541
|
|
545
542
|
if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: finished?)
|
546
|
-
after.lines("\n"
|
543
|
+
after.lines("\n").map { |l| l.chomp('') }
|
547
544
|
else
|
548
545
|
before
|
549
546
|
end
|
@@ -1058,29 +1055,7 @@ class Reline::LineEditor
|
|
1058
1055
|
end
|
1059
1056
|
|
1060
1057
|
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
|
1058
|
+
Reline::Unicode.calculate_width(str, allow_escape_code)
|
1084
1059
|
end
|
1085
1060
|
|
1086
1061
|
private def key_delete(key)
|
@@ -1258,7 +1233,7 @@ class Reline::LineEditor
|
|
1258
1233
|
if search_word.empty? and Reline.last_incremental_search
|
1259
1234
|
search_word = Reline.last_incremental_search
|
1260
1235
|
end
|
1261
|
-
if @history_pointer
|
1236
|
+
if @history_pointer
|
1262
1237
|
case prev_search_key
|
1263
1238
|
when "\C-r".ord
|
1264
1239
|
history_pointer_base = 0
|
@@ -2121,7 +2096,7 @@ class Reline::LineEditor
|
|
2121
2096
|
fp.path
|
2122
2097
|
}
|
2123
2098
|
system("#{ENV['EDITOR']} #{path}")
|
2124
|
-
@line =
|
2099
|
+
@line = File.read(path)
|
2125
2100
|
finish
|
2126
2101
|
end
|
2127
2102
|
|
@@ -2321,7 +2296,6 @@ class Reline::LineEditor
|
|
2321
2296
|
new_pointer = [@byte_pointer, @line_index]
|
2322
2297
|
@previous_line_index = @line_index
|
2323
2298
|
@byte_pointer, @line_index = @mark_pointer
|
2324
|
-
@byte_pointer, @line_index = @mark_pointer
|
2325
2299
|
@cursor = calculate_width(@line.byteslice(0, @byte_pointer))
|
2326
2300
|
@cursor_max = calculate_width(@line)
|
2327
2301
|
@mark_pointer = new_pointer
|
data/lib/reline/unicode.rb
CHANGED
@@ -35,6 +35,12 @@ class Reline::Unicode
|
|
35
35
|
}
|
36
36
|
EscapedChars = EscapedPairs.keys.map(&:chr)
|
37
37
|
|
38
|
+
CSI_REGEXP = /\e\[[\d;]*[ABCDEFGHJKSTfminsuhl]/
|
39
|
+
OSC_REGEXP = /\e\]\d+(?:;[^;]+)*\a/
|
40
|
+
NON_PRINTING_START = "\1"
|
41
|
+
NON_PRINTING_END = "\2"
|
42
|
+
WIDTH_SCANNER = /\G(?:#{NON_PRINTING_START}|#{NON_PRINTING_END}|#{CSI_REGEXP}|#{OSC_REGEXP}|\X)/
|
43
|
+
|
38
44
|
def self.get_mbchar_byte_size_by_first_char(c)
|
39
45
|
# Checks UTF-8 character byte size
|
40
46
|
case c.ord
|
@@ -85,6 +91,68 @@ class Reline::Unicode
|
|
85
91
|
end
|
86
92
|
end
|
87
93
|
|
94
|
+
def self.calculate_width(str, allow_escape_code = false)
|
95
|
+
if allow_escape_code
|
96
|
+
width = 0
|
97
|
+
rest = str.encode(Encoding::UTF_8)
|
98
|
+
in_zero_width = false
|
99
|
+
rest.scan(WIDTH_SCANNER) do |gc|
|
100
|
+
case gc
|
101
|
+
when NON_PRINTING_START
|
102
|
+
in_zero_width = true
|
103
|
+
when NON_PRINTING_END
|
104
|
+
in_zero_width = false
|
105
|
+
when CSI_REGEXP, OSC_REGEXP
|
106
|
+
else
|
107
|
+
unless in_zero_width
|
108
|
+
width += get_mbchar_width(gc)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
width
|
113
|
+
else
|
114
|
+
str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |w, gc|
|
115
|
+
w + get_mbchar_width(gc)
|
116
|
+
}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.split_by_width(str, max_width, encoding = str.encoding)
|
121
|
+
lines = [String.new(encoding: encoding)]
|
122
|
+
height = 1
|
123
|
+
width = 0
|
124
|
+
rest = str.encode(Encoding::UTF_8)
|
125
|
+
in_zero_width = false
|
126
|
+
rest.scan(WIDTH_SCANNER) do |gc|
|
127
|
+
case gc
|
128
|
+
when NON_PRINTING_START
|
129
|
+
in_zero_width = true
|
130
|
+
when NON_PRINTING_END
|
131
|
+
in_zero_width = false
|
132
|
+
when CSI_REGEXP, OSC_REGEXP
|
133
|
+
lines.last << gc
|
134
|
+
else
|
135
|
+
unless in_zero_width
|
136
|
+
mbchar_width = get_mbchar_width(gc)
|
137
|
+
if (width += mbchar_width) > max_width
|
138
|
+
width = mbchar_width
|
139
|
+
lines << nil
|
140
|
+
lines << String.new(encoding: encoding)
|
141
|
+
height += 1
|
142
|
+
end
|
143
|
+
end
|
144
|
+
lines.last << gc
|
145
|
+
end
|
146
|
+
end
|
147
|
+
# The cursor moves to next line in first
|
148
|
+
if width == max_width
|
149
|
+
lines << nil
|
150
|
+
lines << String.new(encoding: encoding)
|
151
|
+
height += 1
|
152
|
+
end
|
153
|
+
[lines, height]
|
154
|
+
end
|
155
|
+
|
88
156
|
def self.get_next_mbchar_size(line, byte_pointer)
|
89
157
|
grapheme = line.byteslice(byte_pointer..-1).grapheme_clusters.first
|
90
158
|
grapheme ? grapheme.bytesize : 0
|
data/lib/reline/version.rb
CHANGED
data/lib/reline/windows.rb
CHANGED
@@ -92,6 +92,7 @@ class Reline::Windows
|
|
92
92
|
@@ReadConsoleInput = Win32API.new('kernel32', 'ReadConsoleInput', ['L', 'P', 'L', 'P'], 'L')
|
93
93
|
@@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
|
94
94
|
@@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
95
|
+
@@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
|
95
96
|
|
96
97
|
@@input_buf = []
|
97
98
|
@@output_buf = []
|
@@ -249,9 +250,17 @@ class Reline::Windows
|
|
249
250
|
end
|
250
251
|
|
251
252
|
def self.clear_screen
|
252
|
-
|
253
|
-
|
254
|
-
|
253
|
+
csbi = 0.chr * 22
|
254
|
+
return if @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi) == 0
|
255
|
+
buffer_width = csbi[0, 2].unpack('S').first
|
256
|
+
attributes = csbi[8, 2].unpack('S').first
|
257
|
+
_window_left, window_top, _window_right, window_bottom = *csbi[10,8].unpack('S*')
|
258
|
+
fill_length = buffer_width * (window_bottom - window_top + 1)
|
259
|
+
screen_topleft = window_top * 65536
|
260
|
+
written = 0.chr * 4
|
261
|
+
@@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, fill_length, screen_topleft, written)
|
262
|
+
@@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, fill_length, screen_topleft, written)
|
263
|
+
@@SetConsoleCursorPosition.call(@@hConsoleHandle, screen_topleft)
|
255
264
|
end
|
256
265
|
|
257
266
|
def self.set_screen_size(rows, columns)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aycabta
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-09-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: io-console
|
@@ -95,9 +95,9 @@ files:
|
|
95
95
|
- lib/reline/windows.rb
|
96
96
|
homepage: https://github.com/ruby/reline
|
97
97
|
licenses:
|
98
|
-
- Ruby
|
98
|
+
- Ruby
|
99
99
|
metadata: {}
|
100
|
-
post_install_message:
|
100
|
+
post_install_message:
|
101
101
|
rdoc_options: []
|
102
102
|
require_paths:
|
103
103
|
- lib
|
@@ -113,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
113
|
version: '0'
|
114
114
|
requirements: []
|
115
115
|
rubygems_version: 3.1.2
|
116
|
-
signing_key:
|
116
|
+
signing_key:
|
117
117
|
specification_version: 4
|
118
118
|
summary: Alternative GNU Readline or Editline implementation by pure Ruby.
|
119
119
|
test_files: []
|