reline 0.1.4 → 0.1.5
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 +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: []
|