reline 0.5.9 → 0.5.11
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/config.rb +41 -50
- data/lib/reline/face.rb +1 -1
- data/lib/reline/history.rb +3 -3
- data/lib/reline/io/ansi.rb +33 -49
- data/lib/reline/io/dumb.rb +2 -2
- data/lib/reline/io/windows.rb +83 -66
- data/lib/reline/line_editor.rb +114 -135
- data/lib/reline/terminfo.rb +1 -1
- data/lib/reline/unicode/east_asian_width.rb +1262 -1191
- data/lib/reline/unicode.rb +34 -43
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +11 -8
- metadata +3 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2e17ec5d6de83746c38c5e0764f7fa1ecda40f01c18b9e8cdf769e4bf2a155e
|
4
|
+
data.tar.gz: 7ce06466fae4c8115bb507141998d322595b9e924dfc1ad3001871265088a680
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31e90ffea6ac235aad5c2c243fbd41163142f94e044ed9f4980860263fab97154857483755dcacd259fa9b578b1c29be5b878cd7f883f18e7b92380cb5865ef3
|
7
|
+
data.tar.gz: 54b91a893c536b3e6b5c984c0fc2b053b8978ab586ae361446fb603c4eecfb0240a99c45b25e4996ff58d9ef15fa4e33bab5cef9cb4aef3dc7eab85998af3711
|
data/lib/reline/config.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Reline::Config
|
2
2
|
attr_reader :test_mode
|
3
3
|
|
4
|
-
KEYSEQ_PATTERN = /\\(?:C|Control)-[A-Za-z_]|\\(?:M|Meta)-[0-9A-Za-z_]|\\(?:C|Control)
|
4
|
+
KEYSEQ_PATTERN = /\\(?:C|Control)-[A-Za-z_]|\\(?:M|Meta)-[0-9A-Za-z_]|\\(?:C|Control)-\\(?:M|Meta)-[A-Za-z_]|\\(?:M|Meta)-\\(?:C|Control)-[A-Za-z_]|\\e|\\[\\\"\'abdfnrtv]|\\\d{1,3}|\\x\h{1,2}|./
|
5
5
|
|
6
6
|
class InvalidInputrc < RuntimeError
|
7
7
|
attr_accessor :file, :lineno
|
@@ -29,6 +29,17 @@ class Reline::Config
|
|
29
29
|
attr_accessor :autocompletion
|
30
30
|
|
31
31
|
def initialize
|
32
|
+
reset_variables
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset
|
36
|
+
if editing_mode_is?(:vi_command)
|
37
|
+
@editing_mode_label = :vi_insert
|
38
|
+
end
|
39
|
+
@oneshot_key_bindings.clear
|
40
|
+
end
|
41
|
+
|
42
|
+
def reset_variables
|
32
43
|
@additional_key_bindings = { # from inputrc
|
33
44
|
emacs: Reline::KeyActor::Base.new,
|
34
45
|
vi_insert: Reline::KeyActor::Base.new,
|
@@ -51,16 +62,11 @@ class Reline::Config
|
|
51
62
|
@keyseq_timeout = 500
|
52
63
|
@test_mode = false
|
53
64
|
@autocompletion = false
|
54
|
-
@convert_meta =
|
65
|
+
@convert_meta = seven_bit_encoding?(Reline::IOGate.encoding)
|
55
66
|
@loaded = false
|
56
67
|
@enable_bracketed_paste = true
|
57
|
-
|
58
|
-
|
59
|
-
def reset
|
60
|
-
if editing_mode_is?(:vi_command)
|
61
|
-
@editing_mode_label = :vi_insert
|
62
|
-
end
|
63
|
-
@oneshot_key_bindings.clear
|
68
|
+
@show_mode_in_prompt = false
|
69
|
+
@default_inputrc_path = nil
|
64
70
|
end
|
65
71
|
|
66
72
|
def editing_mode
|
@@ -188,13 +194,14 @@ class Reline::Config
|
|
188
194
|
# value ignores everything after a space, raw_value does not.
|
189
195
|
var, value, raw_value = $1.downcase, $2.partition(' ').first, $2
|
190
196
|
bind_variable(var, value, raw_value)
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
197
|
+
when /^\s*(?:M|Meta)-([a-zA-Z_])\s*:\s*(.*)\s*$/o
|
198
|
+
bind_key("\"\\M-#$1\"", $2)
|
199
|
+
when /^\s*(?:C|Control)-([a-zA-Z_])\s*:\s*(.*)\s*$/o
|
200
|
+
bind_key("\"\\C-#$1\"", $2)
|
201
|
+
when /^\s*(?:(?:C|Control)-(?:M|Meta)|(?:M|Meta)-(?:C|Control))-([a-zA-Z_])\s*:\s*(.*)\s*$/o
|
202
|
+
bind_key("\"\\M-\\C-#$1\"", $2)
|
203
|
+
when /^\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
|
204
|
+
bind_key($1, $2)
|
198
205
|
end
|
199
206
|
end
|
200
207
|
unless if_stack.empty?
|
@@ -245,22 +252,6 @@ class Reline::Config
|
|
245
252
|
rescue ArgumentError
|
246
253
|
@history_size = 500
|
247
254
|
end
|
248
|
-
when 'bell-style'
|
249
|
-
@bell_style =
|
250
|
-
case value
|
251
|
-
when 'none', 'off'
|
252
|
-
:none
|
253
|
-
when 'audible', 'on'
|
254
|
-
:audible
|
255
|
-
when 'visible'
|
256
|
-
:visible
|
257
|
-
else
|
258
|
-
:audible
|
259
|
-
end
|
260
|
-
when 'comment-begin'
|
261
|
-
@comment_begin = value.dup
|
262
|
-
when 'completion-query-items'
|
263
|
-
@completion_query_items = value.to_i
|
264
255
|
when 'isearch-terminators'
|
265
256
|
@isearch_terminators = retrieve_string(raw_value)
|
266
257
|
when 'editing-mode'
|
@@ -320,7 +311,12 @@ class Reline::Config
|
|
320
311
|
parse_keyseq(str).map { |c| c.chr(Reline.encoding_system_needs) }.join
|
321
312
|
end
|
322
313
|
|
323
|
-
def bind_key(key,
|
314
|
+
def bind_key(key, value)
|
315
|
+
keystroke, func = parse_key_binding(key, value)
|
316
|
+
@additional_key_bindings[@keymap_label].add(@keymap_prefix + keystroke, func) if keystroke
|
317
|
+
end
|
318
|
+
|
319
|
+
def parse_key_binding(key, func_name)
|
324
320
|
if key =~ /\A"(.*)"\z/
|
325
321
|
keyseq = parse_keyseq($1)
|
326
322
|
else
|
@@ -329,27 +325,19 @@ class Reline::Config
|
|
329
325
|
if func_name =~ /"(.*)"/
|
330
326
|
func = parse_keyseq($1)
|
331
327
|
else
|
332
|
-
func = func_name.tr(?-, ?_).to_sym # It must be macro.
|
328
|
+
func = func_name.split.first.tr(?-, ?_).to_sym # It must be macro.
|
333
329
|
end
|
334
330
|
[keyseq, func]
|
335
331
|
end
|
336
332
|
|
337
333
|
def key_notation_to_code(notation)
|
338
334
|
case notation
|
335
|
+
when /(?:\\(?:C|Control)-\\(?:M|Meta)|\\(?:M|Meta)-\\(?:C|Control))-([A-Za-z_])/
|
336
|
+
[?\e.ord, $1.ord % 32]
|
339
337
|
when /\\(?:C|Control)-([A-Za-z_])/
|
340
|
-
(
|
338
|
+
($1.upcase.ord % 32)
|
341
339
|
when /\\(?:M|Meta)-([0-9A-Za-z_])/
|
342
|
-
|
343
|
-
case $1
|
344
|
-
when /[0-9]/
|
345
|
-
?\M-0.bytes.first + (modified_key.ord - ?0.ord)
|
346
|
-
when /[A-Z]/
|
347
|
-
?\M-A.bytes.first + (modified_key.ord - ?A.ord)
|
348
|
-
when /[a-z]/
|
349
|
-
?\M-a.bytes.first + (modified_key.ord - ?a.ord)
|
350
|
-
end
|
351
|
-
when /\\(?:C|Control)-(?:M|Meta)-[A-Za-z_]/, /\\(?:M|Meta)-(?:C|Control)-[A-Za-z_]/
|
352
|
-
# 129 M-^A
|
340
|
+
[?\e.ord, $1.ord]
|
353
341
|
when /\\(\d{1,3})/ then $1.to_i(8) # octal
|
354
342
|
when /\\x(\h{1,2})/ then $1.to_i(16) # hexadecimal
|
355
343
|
when "\\e" then ?\e.ord
|
@@ -369,11 +357,14 @@ class Reline::Config
|
|
369
357
|
end
|
370
358
|
|
371
359
|
def parse_keyseq(str)
|
372
|
-
|
373
|
-
|
374
|
-
ret << key_notation_to_code($&)
|
360
|
+
str.scan(KEYSEQ_PATTERN).flat_map do |notation|
|
361
|
+
key_notation_to_code(notation)
|
375
362
|
end
|
376
|
-
|
363
|
+
end
|
364
|
+
|
365
|
+
def reload
|
366
|
+
reset_variables
|
367
|
+
read
|
377
368
|
end
|
378
369
|
|
379
370
|
private def seven_bit_encoding?(encoding)
|
data/lib/reline/face.rb
CHANGED
@@ -107,7 +107,7 @@ class Reline::Face
|
|
107
107
|
|
108
108
|
def sgr_rgb_256color(key, value)
|
109
109
|
# 256 colors are
|
110
|
-
# 0..15: standard colors,
|
110
|
+
# 0..15: standard colors, high intensity colors
|
111
111
|
# 16..232: 216 colors (R, G, B each 6 steps)
|
112
112
|
# 233..255: grayscale colors (24 steps)
|
113
113
|
# This methods converts rgb_expression to 216 colors
|
data/lib/reline/history.rb
CHANGED
@@ -19,7 +19,7 @@ class Reline::History < Array
|
|
19
19
|
|
20
20
|
def []=(index, val)
|
21
21
|
index = check_index(index)
|
22
|
-
super(index,
|
22
|
+
super(index, Reline::Unicode.safe_encode(val, Reline.encoding_system_needs))
|
23
23
|
end
|
24
24
|
|
25
25
|
def concat(*val)
|
@@ -45,7 +45,7 @@ class Reline::History < Array
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
super(*(val.map{ |v|
|
48
|
-
|
48
|
+
Reline::Unicode.safe_encode(v, Reline.encoding_system_needs)
|
49
49
|
}))
|
50
50
|
end
|
51
51
|
|
@@ -56,7 +56,7 @@ class Reline::History < Array
|
|
56
56
|
if @config.history_size.positive?
|
57
57
|
shift if size + 1 > @config.history_size
|
58
58
|
end
|
59
|
-
super(
|
59
|
+
super(Reline::Unicode.safe_encode(val, Reline.encoding_system_needs))
|
60
60
|
end
|
61
61
|
|
62
62
|
private def check_index(index)
|
data/lib/reline/io/ansi.rb
CHANGED
@@ -41,7 +41,7 @@ class Reline::ANSI < Reline::IO
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def encoding
|
44
|
-
Encoding.default_external
|
44
|
+
@input.external_encoding || Encoding.default_external
|
45
45
|
end
|
46
46
|
|
47
47
|
def set_default_key_bindings(config, allow_terminfo: true)
|
@@ -75,7 +75,10 @@ class Reline::ANSI < Reline::IO
|
|
75
75
|
|
76
76
|
def set_default_key_bindings_ansi_cursor(config)
|
77
77
|
ANSI_CURSOR_KEY_BINDINGS.each do |char, (default_func, modifiers)|
|
78
|
-
bindings = [
|
78
|
+
bindings = [
|
79
|
+
["\e[#{char}", default_func], # CSI + char
|
80
|
+
["\eO#{char}", default_func] # SS3 + char, application cursor key mode
|
81
|
+
]
|
79
82
|
if modifiers[:ctrl]
|
80
83
|
# CSI + ctrl_key_modifier + char
|
81
84
|
bindings << ["\e[1;5#{char}", modifiers[:ctrl]]
|
@@ -123,27 +126,9 @@ class Reline::ANSI < Reline::IO
|
|
123
126
|
[27, 91, 49, 126] => :ed_move_to_beg, # Home
|
124
127
|
[27, 91, 52, 126] => :ed_move_to_end, # End
|
125
128
|
|
126
|
-
# KDE
|
127
|
-
# Del is 0x08
|
128
|
-
[27, 71, 65] => :ed_prev_history, # ↑
|
129
|
-
[27, 71, 66] => :ed_next_history, # ↓
|
130
|
-
[27, 71, 67] => :ed_next_char, # →
|
131
|
-
[27, 71, 68] => :ed_prev_char, # ←
|
132
|
-
|
133
129
|
# urxvt / exoterm
|
134
130
|
[27, 91, 55, 126] => :ed_move_to_beg, # Home
|
135
131
|
[27, 91, 56, 126] => :ed_move_to_end, # End
|
136
|
-
|
137
|
-
# GNOME
|
138
|
-
[27, 79, 72] => :ed_move_to_beg, # Home
|
139
|
-
[27, 79, 70] => :ed_move_to_end, # End
|
140
|
-
# Del is 0x08
|
141
|
-
# Arrow keys are the same of KDE
|
142
|
-
|
143
|
-
[27, 79, 65] => :ed_prev_history, # ↑
|
144
|
-
[27, 79, 66] => :ed_next_history, # ↓
|
145
|
-
[27, 79, 67] => :ed_next_char, # →
|
146
|
-
[27, 79, 68] => :ed_prev_char, # ←
|
147
132
|
}.each_pair do |key, func|
|
148
133
|
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
149
134
|
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
@@ -245,39 +230,30 @@ class Reline::ANSI < Reline::IO
|
|
245
230
|
self
|
246
231
|
end
|
247
232
|
|
248
|
-
def
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
@
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
break if m
|
261
|
-
end
|
262
|
-
(m.pre_match + m.post_match).chars.reverse_each do |ch|
|
263
|
-
stdin.ungetc ch
|
233
|
+
private def cursor_pos_internal(timeout:)
|
234
|
+
match = nil
|
235
|
+
@input.raw do |stdin|
|
236
|
+
@output << "\e[6n"
|
237
|
+
@output.flush
|
238
|
+
timeout_at = Time.now + timeout
|
239
|
+
buf = +''
|
240
|
+
while (wait = timeout_at - Time.now) > 0 && stdin.wait_readable(wait)
|
241
|
+
buf << stdin.readpartial(1024)
|
242
|
+
if (match = buf.match(/\e\[(?<row>\d+);(?<column>\d+)R/))
|
243
|
+
buf = match.pre_match + match.post_match
|
244
|
+
break
|
264
245
|
end
|
265
246
|
end
|
266
|
-
|
267
|
-
|
268
|
-
else
|
269
|
-
begin
|
270
|
-
buf = @output.pread(@output.pos, 0)
|
271
|
-
row = buf.count("\n")
|
272
|
-
column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
|
273
|
-
rescue Errno::ESPIPE, IOError
|
274
|
-
# Just returns column 1 for ambiguous width because this I/O is not
|
275
|
-
# tty and can't seek.
|
276
|
-
row = 0
|
277
|
-
column = 1
|
247
|
+
buf.chars.reverse_each do |ch|
|
248
|
+
stdin.ungetc ch
|
278
249
|
end
|
279
250
|
end
|
280
|
-
|
251
|
+
[match[:column].to_i - 1, match[:row].to_i - 1] if match
|
252
|
+
end
|
253
|
+
|
254
|
+
def cursor_pos
|
255
|
+
col, row = cursor_pos_internal(timeout: 0.5) if both_tty?
|
256
|
+
Reline::CursorPos.new(col || 0, row || 0)
|
281
257
|
end
|
282
258
|
|
283
259
|
def both_tty?
|
@@ -347,6 +323,13 @@ class Reline::ANSI < Reline::IO
|
|
347
323
|
|
348
324
|
def set_winch_handler(&handler)
|
349
325
|
@old_winch_handler = Signal.trap('WINCH', &handler)
|
326
|
+
@old_cont_handler = Signal.trap('CONT') do
|
327
|
+
@input.raw!(intr: true) if @input.tty?
|
328
|
+
# Rerender the screen. Note that screen size might be changed while suspended.
|
329
|
+
handler.call
|
330
|
+
end
|
331
|
+
rescue ArgumentError
|
332
|
+
# Signal.trap may raise an ArgumentError if the platform doesn't support the signal.
|
350
333
|
end
|
351
334
|
|
352
335
|
def prep
|
@@ -360,5 +343,6 @@ class Reline::ANSI < Reline::IO
|
|
360
343
|
# Disable bracketed paste
|
361
344
|
@output.write "\e[?2004l" if Reline.core.config.enable_bracketed_paste && both_tty?
|
362
345
|
Signal.trap('WINCH', @old_winch_handler) if @old_winch_handler
|
346
|
+
Signal.trap('CONT', @old_cont_handler) if @old_cont_handler
|
363
347
|
end
|
364
348
|
end
|
data/lib/reline/io/dumb.rb
CHANGED
@@ -21,7 +21,7 @@ class Reline::Dumb < Reline::IO
|
|
21
21
|
elsif RUBY_PLATFORM =~ /mswin|mingw/
|
22
22
|
Encoding::UTF_8
|
23
23
|
else
|
24
|
-
Encoding::default_external
|
24
|
+
@input.external_encoding || Encoding::default_external
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -60,7 +60,7 @@ class Reline::Dumb < Reline::IO
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def cursor_pos
|
63
|
-
Reline::CursorPos.new(
|
63
|
+
Reline::CursorPos.new(0, 0)
|
64
64
|
end
|
65
65
|
|
66
66
|
def hide_cursor
|
data/lib/reline/io/windows.rb
CHANGED
@@ -157,16 +157,27 @@ class Reline::Windows < Reline::IO
|
|
157
157
|
STD_OUTPUT_HANDLE = -11
|
158
158
|
FILE_TYPE_PIPE = 0x0003
|
159
159
|
FILE_NAME_INFO = 2
|
160
|
+
ENABLE_WRAP_AT_EOL_OUTPUT = 2
|
160
161
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
161
162
|
|
163
|
+
# Calling Win32API with console handle is reported to fail after executing some external command.
|
164
|
+
# We need to refresh console handle and retry the call again.
|
165
|
+
private def call_with_console_handle(win32func, *args)
|
166
|
+
val = win32func.call(@hConsoleHandle, *args)
|
167
|
+
return val if val != 0
|
168
|
+
|
169
|
+
@hConsoleHandle = @GetStdHandle.call(STD_OUTPUT_HANDLE)
|
170
|
+
win32func.call(@hConsoleHandle, *args)
|
171
|
+
end
|
172
|
+
|
162
173
|
private def getconsolemode
|
163
|
-
mode = "\
|
164
|
-
@GetConsoleMode
|
174
|
+
mode = +"\0\0\0\0"
|
175
|
+
call_with_console_handle(@GetConsoleMode, mode)
|
165
176
|
mode.unpack1('L')
|
166
177
|
end
|
167
178
|
|
168
179
|
private def setconsolemode(mode)
|
169
|
-
@SetConsoleMode
|
180
|
+
call_with_console_handle(@SetConsoleMode, mode)
|
170
181
|
end
|
171
182
|
|
172
183
|
#if @legacy_console
|
@@ -242,7 +253,7 @@ class Reline::Windows < Reline::IO
|
|
242
253
|
|
243
254
|
key = KeyEventRecord.new(virtual_key_code, char_code, control_key_state)
|
244
255
|
|
245
|
-
match = KEY_MAP.find { |args,| key.
|
256
|
+
match = KEY_MAP.find { |args,| key.match?(**args) }
|
246
257
|
unless match.nil?
|
247
258
|
@output_buf.concat(match.last)
|
248
259
|
return
|
@@ -334,35 +345,38 @@ class Reline::Windows < Reline::IO
|
|
334
345
|
# [18,2] dwMaximumWindowSize.X
|
335
346
|
# [20,2] dwMaximumWindowSize.Y
|
336
347
|
csbi = 0.chr * 22
|
337
|
-
|
338
|
-
|
348
|
+
if call_with_console_handle(@GetConsoleScreenBufferInfo, csbi) != 0
|
349
|
+
# returns [width, height, x, y, attributes, left, top, right, bottom]
|
350
|
+
csbi.unpack("s9")
|
351
|
+
else
|
352
|
+
return nil
|
353
|
+
end
|
339
354
|
end
|
340
355
|
|
356
|
+
ALTERNATIVE_CSBI = [80, 24, 0, 0, 7, 0, 0, 79, 23].freeze
|
357
|
+
|
341
358
|
def get_screen_size
|
342
|
-
|
343
|
-
|
344
|
-
end
|
345
|
-
csbi[0, 4].unpack('SS').reverse
|
359
|
+
width, _, _, _, _, _, top, _, bottom = get_console_screen_buffer_info || ALTERNATIVE_CSBI
|
360
|
+
[bottom - top + 1, width]
|
346
361
|
end
|
347
362
|
|
348
363
|
def cursor_pos
|
349
|
-
|
350
|
-
|
351
|
-
end
|
352
|
-
x = csbi[4, 2].unpack1('s')
|
353
|
-
y = csbi[6, 2].unpack1('s')
|
354
|
-
Reline::CursorPos.new(x, y)
|
364
|
+
_, _, x, y, _, _, top, = get_console_screen_buffer_info || ALTERNATIVE_CSBI
|
365
|
+
Reline::CursorPos.new(x, y - top)
|
355
366
|
end
|
356
367
|
|
357
368
|
def move_cursor_column(val)
|
358
|
-
|
369
|
+
_, _, _, y, = get_console_screen_buffer_info
|
370
|
+
call_with_console_handle(@SetConsoleCursorPosition, y * 65536 + val) if y
|
359
371
|
end
|
360
372
|
|
361
373
|
def move_cursor_up(val)
|
362
374
|
if val > 0
|
363
|
-
|
375
|
+
_, _, x, y, _, _, top, = get_console_screen_buffer_info
|
376
|
+
return unless y
|
377
|
+
y = (y - top) - val
|
364
378
|
y = 0 if y < 0
|
365
|
-
@SetConsoleCursorPosition
|
379
|
+
call_with_console_handle(@SetConsoleCursorPosition, (y + top) * 65536 + x)
|
366
380
|
elsif val < 0
|
367
381
|
move_cursor_down(-val)
|
368
382
|
end
|
@@ -370,62 +384,43 @@ class Reline::Windows < Reline::IO
|
|
370
384
|
|
371
385
|
def move_cursor_down(val)
|
372
386
|
if val > 0
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
y =
|
377
|
-
|
387
|
+
_, _, x, y, _, _, top, _, bottom = get_console_screen_buffer_info
|
388
|
+
return unless y
|
389
|
+
screen_height = bottom - top
|
390
|
+
y = (y - top) + val
|
391
|
+
y = screen_height if y > screen_height
|
392
|
+
call_with_console_handle(@SetConsoleCursorPosition, (y + top) * 65536 + x)
|
378
393
|
elsif val < 0
|
379
394
|
move_cursor_up(-val)
|
380
395
|
end
|
381
396
|
end
|
382
397
|
|
383
398
|
def erase_after_cursor
|
384
|
-
|
385
|
-
|
386
|
-
cursor = csbi[4, 4].unpack1('L')
|
399
|
+
width, _, x, y, attributes, = get_console_screen_buffer_info
|
400
|
+
return unless x
|
387
401
|
written = 0.chr * 4
|
388
|
-
@FillConsoleOutputCharacter
|
389
|
-
@FillConsoleOutputAttribute
|
390
|
-
end
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
if @legacy_console || window_left != 0
|
400
|
-
# unless ENABLE_VIRTUAL_TERMINAL,
|
401
|
-
# if srWindow.Left != 0 then it's conhost.exe hosted console
|
402
|
-
# and puts "\n" causes horizontal scroll. its glitch.
|
403
|
-
# FYI irb write from culumn 1, so this gives no gain.
|
404
|
-
scroll_rectangle = [0, val, buffer_width, buffer_lines - val].pack('s4')
|
405
|
-
destination_origin = 0 # y * 65536 + x
|
406
|
-
fill = [' '.ord, attributes].pack('SS')
|
407
|
-
@ScrollConsoleScreenBuffer.call(@hConsoleHandle, scroll_rectangle, nil, destination_origin, fill)
|
408
|
-
else
|
409
|
-
origin_x = x + 1
|
410
|
-
origin_y = y - window_top + 1
|
411
|
-
@output.write [
|
412
|
-
(origin_y != screen_height) ? "\e[#{screen_height};H" : nil,
|
413
|
-
"\n" * val,
|
414
|
-
(origin_y != screen_height or !x.zero?) ? "\e[#{origin_y};#{origin_x}H" : nil
|
415
|
-
].join
|
416
|
-
end
|
402
|
+
call_with_console_handle(@FillConsoleOutputCharacter, 0x20, width - x, y * 65536 + x, written)
|
403
|
+
call_with_console_handle(@FillConsoleOutputAttribute, attributes, width - x, y * 65536 + x, written)
|
404
|
+
end
|
405
|
+
|
406
|
+
# This only works when the cursor is at the bottom of the scroll range
|
407
|
+
# For more details, see https://github.com/ruby/reline/pull/577#issuecomment-1646679623
|
408
|
+
def scroll_down(x)
|
409
|
+
return if x.zero?
|
410
|
+
# We use `\n` instead of CSI + S because CSI + S would cause https://github.com/ruby/reline/issues/576
|
411
|
+
@output.write "\n" * x
|
417
412
|
end
|
418
413
|
|
419
414
|
def clear_screen
|
420
415
|
if @legacy_console
|
421
|
-
|
422
|
-
|
423
|
-
fill_length =
|
424
|
-
screen_topleft =
|
416
|
+
width, _, _, _, attributes, _, top, _, bottom = get_console_screen_buffer_info
|
417
|
+
return unless width
|
418
|
+
fill_length = width * (bottom - top + 1)
|
419
|
+
screen_topleft = top * 65536
|
425
420
|
written = 0.chr * 4
|
426
|
-
@FillConsoleOutputCharacter
|
427
|
-
@FillConsoleOutputAttribute
|
428
|
-
@SetConsoleCursorPosition
|
421
|
+
call_with_console_handle(@FillConsoleOutputCharacter, 0x20, fill_length, screen_topleft, written)
|
422
|
+
call_with_console_handle(@FillConsoleOutputAttribute, attributes, fill_length, screen_topleft, written)
|
423
|
+
call_with_console_handle(@SetConsoleCursorPosition, screen_topleft)
|
429
424
|
else
|
430
425
|
@output.write "\e[2J" "\e[H"
|
431
426
|
end
|
@@ -439,14 +434,14 @@ class Reline::Windows < Reline::IO
|
|
439
434
|
size = 100
|
440
435
|
visible = 0 # 0 means false
|
441
436
|
cursor_info = [size, visible].pack('Li')
|
442
|
-
@SetConsoleCursorInfo
|
437
|
+
call_with_console_handle(@SetConsoleCursorInfo, cursor_info)
|
443
438
|
end
|
444
439
|
|
445
440
|
def show_cursor
|
446
441
|
size = 100
|
447
442
|
visible = 1 # 1 means true
|
448
443
|
cursor_info = [size, visible].pack('Li')
|
449
|
-
@SetConsoleCursorInfo
|
444
|
+
call_with_console_handle(@SetConsoleCursorInfo, cursor_info)
|
450
445
|
end
|
451
446
|
|
452
447
|
def set_winch_handler(&handler)
|
@@ -462,6 +457,28 @@ class Reline::Windows < Reline::IO
|
|
462
457
|
# do nothing
|
463
458
|
end
|
464
459
|
|
460
|
+
def disable_auto_linewrap(setting = true, &block)
|
461
|
+
mode = getconsolemode
|
462
|
+
if 0 == (mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
463
|
+
if block
|
464
|
+
begin
|
465
|
+
setconsolemode(mode & ~ENABLE_WRAP_AT_EOL_OUTPUT)
|
466
|
+
block.call
|
467
|
+
ensure
|
468
|
+
setconsolemode(mode | ENABLE_WRAP_AT_EOL_OUTPUT)
|
469
|
+
end
|
470
|
+
else
|
471
|
+
if setting
|
472
|
+
setconsolemode(mode & ~ENABLE_WRAP_AT_EOL_OUTPUT)
|
473
|
+
else
|
474
|
+
setconsolemode(mode | ENABLE_WRAP_AT_EOL_OUTPUT)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
else
|
478
|
+
block.call if block
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
465
482
|
class KeyEventRecord
|
466
483
|
|
467
484
|
attr_reader :virtual_key_code, :char_code, :control_key_state, :control_keys
|
@@ -491,7 +508,7 @@ class Reline::Windows < Reline::IO
|
|
491
508
|
# Verifies if the arguments match with this key event.
|
492
509
|
# Nil arguments are ignored, but at least one must be passed as non-nil.
|
493
510
|
# To verify that no control keys were pressed, pass an empty array: `control_keys: []`.
|
494
|
-
def
|
511
|
+
def match?(control_keys: nil, virtual_key_code: nil, char_code: nil)
|
495
512
|
raise ArgumentError, 'No argument was passed to match key event' if control_keys.nil? && virtual_key_code.nil? && char_code.nil?
|
496
513
|
|
497
514
|
(control_keys.nil? || [*control_keys].sort == @control_keys) &&
|