reline 0.6.2 → 0.7.0
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/io/ansi.rb +6 -16
- data/lib/reline/io/windows.rb +49 -23
- data/lib/reline/io.rb +8 -2
- data/lib/reline/key_actor/emacs.rb +2 -2
- data/lib/reline/line_editor.rb +85 -57
- data/lib/reline/unicode.rb +1 -1
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +10 -9
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1a8724bc788e9aab24b52fd980230e32b799b9ce5d95d8dcb41e56a2eb953ac4
|
|
4
|
+
data.tar.gz: f5a979522d2f280c9b45b78782329254b8ab40ad95587afb011748f57b93b35f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b89c6c314821ff7e4a5bbb724197a6930c61b0cfc2f9b01eba6e0cf356d3d66425f8121527dd68f3ee639f0a11bd062ff6ddc253c27c5e9ad6d1fae333d4f840
|
|
7
|
+
data.tar.gz: ae7d3567919f3272bea1cce00435d9d9bd917288f203b6ae816d3856070115b63d40f691596bfefa37aa0beb9bcd8e71db7b82b73692b7c51c24b9f7f1ec9af0
|
data/lib/reline/io/ansi.rb
CHANGED
|
@@ -114,13 +114,15 @@ class Reline::ANSI < Reline::IO
|
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
def inner_getc(timeout_second)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
while true
|
|
118
|
+
# @buf may change while handling signal, so we need to check it in each loop iteration.
|
|
119
|
+
return @buf.shift unless @buf.empty?
|
|
120
|
+
break if @input.wait_readable(0.01)
|
|
121
|
+
|
|
121
122
|
timeout_second -= 0.01
|
|
122
123
|
return nil if timeout_second <= 0
|
|
123
124
|
|
|
125
|
+
# handle_signal may call cursor_pos which modifies @buf
|
|
124
126
|
Reline.core.line_editor.handle_signal
|
|
125
127
|
end
|
|
126
128
|
c = @input.getbyte
|
|
@@ -167,17 +169,6 @@ class Reline::ANSI < Reline::IO
|
|
|
167
169
|
@buf.unshift(c)
|
|
168
170
|
end
|
|
169
171
|
|
|
170
|
-
def retrieve_keybuffer
|
|
171
|
-
begin
|
|
172
|
-
return unless @input.wait_readable(0.001)
|
|
173
|
-
str = @input.read_nonblock(1024)
|
|
174
|
-
str.bytes.each do |c|
|
|
175
|
-
@buf.push(c)
|
|
176
|
-
end
|
|
177
|
-
rescue EOFError
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
|
|
181
172
|
def get_screen_size
|
|
182
173
|
s = @input.winsize
|
|
183
174
|
return s if s[0] > 0 && s[1] > 0
|
|
@@ -309,7 +300,6 @@ class Reline::ANSI < Reline::IO
|
|
|
309
300
|
def prep
|
|
310
301
|
# Enable bracketed paste
|
|
311
302
|
write "\e[?2004h" if Reline.core.config.enable_bracketed_paste && both_tty?
|
|
312
|
-
retrieve_keybuffer
|
|
313
303
|
nil
|
|
314
304
|
end
|
|
315
305
|
|
data/lib/reline/io/windows.rb
CHANGED
|
@@ -31,6 +31,42 @@ class Reline::Windows < Reline::IO
|
|
|
31
31
|
@SetConsoleMode = Win32API.new('kernel32', 'SetConsoleMode', ['L', 'L'], 'L')
|
|
32
32
|
@WaitForSingleObject = Win32API.new('kernel32', 'WaitForSingleObject', ['L', 'L'], 'L')
|
|
33
33
|
|
|
34
|
+
# Win32API does not have pointer size integer.
|
|
35
|
+
# Current process pseudo handle (-1)LL seems to fail to be passed to DuplicateHandle.
|
|
36
|
+
# @GetCurrentProcess = Win32API.new('kernel32', 'GetCurrentProcess', [], 'L')
|
|
37
|
+
@GetCurrentProcessId = Win32API.new('kernel32', 'GetCurrentProcessId', [], 'L')
|
|
38
|
+
@OpenProcess = Win32API.new('kernel32', 'OpenProcess', ['L', 'L', 'L'], 'L')
|
|
39
|
+
@CloseHandle = Win32API.new('kernel32', 'CloseHandle', ['L'], 'L')
|
|
40
|
+
@DuplicateHandle = Win32API.new('kernel32', 'DuplicateHandle', ['L', 'L', 'L', 'P', 'I', 'I', 'I'], 'L')
|
|
41
|
+
|
|
42
|
+
current_process_handle = @OpenProcess.call(
|
|
43
|
+
0x0040, # PROCESS_DUP_HANDLE
|
|
44
|
+
0, # bInheritHandle
|
|
45
|
+
@GetCurrentProcessId.call()
|
|
46
|
+
)
|
|
47
|
+
duplicate_handle = proc { |handle|
|
|
48
|
+
dupHandle = "\0" * 8
|
|
49
|
+
@DuplicateHandle.call(
|
|
50
|
+
current_process_handle,
|
|
51
|
+
handle,
|
|
52
|
+
current_process_handle,
|
|
53
|
+
dupHandle,
|
|
54
|
+
0, # dwDesiredAccess
|
|
55
|
+
0, # bInheritHandle
|
|
56
|
+
2 # dwOptions = DUPLICATE_SAME_ACCESS
|
|
57
|
+
)
|
|
58
|
+
dupHandle.unpack1("J")
|
|
59
|
+
}
|
|
60
|
+
if (dup = duplicate_handle.call(@hConsoleHandle)) != 0
|
|
61
|
+
@hConsoleHandle = dup
|
|
62
|
+
at_exit { @CloseHandle.call(@hConsoleHandle) }
|
|
63
|
+
end
|
|
64
|
+
if (dup = duplicate_handle.call(@hConsoleInputHandle)) != 0
|
|
65
|
+
@hConsoleInputHandle = dup
|
|
66
|
+
at_exit { @CloseHandle.call(@hConsoleInputHandle) }
|
|
67
|
+
end
|
|
68
|
+
@CloseHandle.call(current_process_handle)
|
|
69
|
+
|
|
34
70
|
@legacy_console = getconsolemode & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0
|
|
35
71
|
end
|
|
36
72
|
|
|
@@ -162,24 +198,14 @@ class Reline::Windows < Reline::IO
|
|
|
162
198
|
ENABLE_WRAP_AT_EOL_OUTPUT = 2
|
|
163
199
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
|
164
200
|
|
|
165
|
-
# Calling Win32API with console handle is reported to fail after executing some external command.
|
|
166
|
-
# We need to refresh console handle and retry the call again.
|
|
167
|
-
private def call_with_console_handle(win32func, *args)
|
|
168
|
-
val = win32func.call(@hConsoleHandle, *args)
|
|
169
|
-
return val if val != 0
|
|
170
|
-
|
|
171
|
-
@hConsoleHandle = @GetStdHandle.call(STD_OUTPUT_HANDLE)
|
|
172
|
-
win32func.call(@hConsoleHandle, *args)
|
|
173
|
-
end
|
|
174
|
-
|
|
175
201
|
private def getconsolemode
|
|
176
202
|
mode = +"\0\0\0\0"
|
|
177
|
-
|
|
203
|
+
@GetConsoleMode.call(@hConsoleHandle, mode)
|
|
178
204
|
mode.unpack1('L')
|
|
179
205
|
end
|
|
180
206
|
|
|
181
207
|
private def setconsolemode(mode)
|
|
182
|
-
|
|
208
|
+
@SetConsoleMode.call(@hConsoleHandle, mode)
|
|
183
209
|
end
|
|
184
210
|
|
|
185
211
|
#if @legacy_console
|
|
@@ -355,7 +381,7 @@ class Reline::Windows < Reline::IO
|
|
|
355
381
|
# [18,2] dwMaximumWindowSize.X
|
|
356
382
|
# [20,2] dwMaximumWindowSize.Y
|
|
357
383
|
csbi = 0.chr * 22
|
|
358
|
-
if
|
|
384
|
+
if @GetConsoleScreenBufferInfo.call(@hConsoleHandle, csbi) != 0
|
|
359
385
|
# returns [width, height, x, y, attributes, left, top, right, bottom]
|
|
360
386
|
csbi.unpack("s9")
|
|
361
387
|
else
|
|
@@ -377,7 +403,7 @@ class Reline::Windows < Reline::IO
|
|
|
377
403
|
|
|
378
404
|
def move_cursor_column(val)
|
|
379
405
|
_, _, _, y, = get_console_screen_buffer_info
|
|
380
|
-
|
|
406
|
+
@SetConsoleCursorPosition.call(@hConsoleHandle, y * 65536 + val) if y
|
|
381
407
|
end
|
|
382
408
|
|
|
383
409
|
def move_cursor_up(val)
|
|
@@ -386,7 +412,7 @@ class Reline::Windows < Reline::IO
|
|
|
386
412
|
return unless y
|
|
387
413
|
y = (y - top) - val
|
|
388
414
|
y = 0 if y < 0
|
|
389
|
-
|
|
415
|
+
@SetConsoleCursorPosition.call(@hConsoleHandle, (y + top) * 65536 + x)
|
|
390
416
|
elsif val < 0
|
|
391
417
|
move_cursor_down(-val)
|
|
392
418
|
end
|
|
@@ -399,7 +425,7 @@ class Reline::Windows < Reline::IO
|
|
|
399
425
|
screen_height = bottom - top
|
|
400
426
|
y = (y - top) + val
|
|
401
427
|
y = screen_height if y > screen_height
|
|
402
|
-
|
|
428
|
+
@SetConsoleCursorPosition.call(@hConsoleHandle, (y + top) * 65536 + x)
|
|
403
429
|
elsif val < 0
|
|
404
430
|
move_cursor_up(-val)
|
|
405
431
|
end
|
|
@@ -409,8 +435,8 @@ class Reline::Windows < Reline::IO
|
|
|
409
435
|
width, _, x, y, attributes, = get_console_screen_buffer_info
|
|
410
436
|
return unless x
|
|
411
437
|
written = 0.chr * 4
|
|
412
|
-
|
|
413
|
-
|
|
438
|
+
@FillConsoleOutputCharacter.call(@hConsoleHandle, 0x20, width - x, y * 65536 + x, written)
|
|
439
|
+
@FillConsoleOutputAttribute.call(@hConsoleHandle, attributes, width - x, y * 65536 + x, written)
|
|
414
440
|
end
|
|
415
441
|
|
|
416
442
|
# This only works when the cursor is at the bottom of the scroll range
|
|
@@ -428,9 +454,9 @@ class Reline::Windows < Reline::IO
|
|
|
428
454
|
fill_length = width * (bottom - top + 1)
|
|
429
455
|
screen_topleft = top * 65536
|
|
430
456
|
written = 0.chr * 4
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
457
|
+
@FillConsoleOutputCharacter.call(@hConsoleHandle, 0x20, fill_length, screen_topleft, written)
|
|
458
|
+
@FillConsoleOutputAttribute.call(@hConsoleHandle, attributes, fill_length, screen_topleft, written)
|
|
459
|
+
@SetConsoleCursorPosition.call(@hConsoleHandle, screen_topleft)
|
|
434
460
|
else
|
|
435
461
|
@output.write "\e[2J" "\e[H"
|
|
436
462
|
end
|
|
@@ -444,14 +470,14 @@ class Reline::Windows < Reline::IO
|
|
|
444
470
|
size = 100
|
|
445
471
|
visible = 0 # 0 means false
|
|
446
472
|
cursor_info = [size, visible].pack('Li')
|
|
447
|
-
|
|
473
|
+
@SetConsoleCursorInfo.call(@hConsoleHandle, cursor_info)
|
|
448
474
|
end
|
|
449
475
|
|
|
450
476
|
def show_cursor
|
|
451
477
|
size = 100
|
|
452
478
|
visible = 1 # 1 means true
|
|
453
479
|
cursor_info = [size, visible].pack('Li')
|
|
454
|
-
|
|
480
|
+
@SetConsoleCursorInfo.call(@hConsoleHandle, cursor_info)
|
|
455
481
|
end
|
|
456
482
|
|
|
457
483
|
def set_winch_handler(&handler)
|
data/lib/reline/io.rb
CHANGED
|
@@ -10,15 +10,21 @@ module Reline
|
|
|
10
10
|
require 'reline/io/ansi'
|
|
11
11
|
|
|
12
12
|
case RbConfig::CONFIG['host_os']
|
|
13
|
-
when /mswin|
|
|
13
|
+
when /mswin|mingw|bccwin|wince|emc/
|
|
14
14
|
require 'reline/io/windows'
|
|
15
15
|
io = Reline::Windows.new
|
|
16
|
-
if io.msys_tty?
|
|
16
|
+
if io.msys_tty? || !STDIN.tty?
|
|
17
|
+
# In either case stdin is not a console (a Cygwin/MSYS pty pipe such
|
|
18
|
+
# as mintty, or a redirect), so the Win32 console input API cannot
|
|
19
|
+
# read it.
|
|
17
20
|
Reline::ANSI.new
|
|
18
21
|
else
|
|
19
22
|
io
|
|
20
23
|
end
|
|
21
24
|
else
|
|
25
|
+
# Ruby built with the msys/cygwin runtime also reaches here. Its tty layer
|
|
26
|
+
# speaks ANSI in any terminal (mintty pty or Windows console), so the
|
|
27
|
+
# Win32 console API must not be used. https://github.com/ruby/reline/issues/903
|
|
22
28
|
Reline::ANSI.new
|
|
23
29
|
end
|
|
24
30
|
end
|
|
@@ -307,7 +307,7 @@ module Reline::KeyActor
|
|
|
307
307
|
# 152 M-^X
|
|
308
308
|
nil,
|
|
309
309
|
# 153 M-^Y
|
|
310
|
-
|
|
310
|
+
nil,
|
|
311
311
|
# 154 M-^Z
|
|
312
312
|
nil,
|
|
313
313
|
# 155 M-^[
|
|
@@ -499,7 +499,7 @@ module Reline::KeyActor
|
|
|
499
499
|
# 248 M-x
|
|
500
500
|
nil,
|
|
501
501
|
# 249 M-y
|
|
502
|
-
|
|
502
|
+
:em_yank_pop,
|
|
503
503
|
# 250 M-z
|
|
504
504
|
nil,
|
|
505
505
|
# 251 M-{
|
data/lib/reline/line_editor.rb
CHANGED
|
@@ -13,6 +13,7 @@ class Reline::LineEditor
|
|
|
13
13
|
attr_accessor :prompt_proc
|
|
14
14
|
attr_accessor :auto_indent_proc
|
|
15
15
|
attr_accessor :dig_perfect_match_proc
|
|
16
|
+
attr_accessor :rprompt
|
|
16
17
|
|
|
17
18
|
VI_MOTIONS = %i{
|
|
18
19
|
ed_prev_char
|
|
@@ -43,7 +44,6 @@ class Reline::LineEditor
|
|
|
43
44
|
RenderedScreen = Struct.new(:base_y, :lines, :cursor_y, keyword_init: true)
|
|
44
45
|
|
|
45
46
|
CompletionJourneyState = Struct.new(:line_index, :pre, :target, :post, :list, :pointer)
|
|
46
|
-
NullActionState = [nil, nil].freeze
|
|
47
47
|
|
|
48
48
|
class MenuInfo
|
|
49
49
|
attr_reader :list
|
|
@@ -223,7 +223,7 @@ class Reline::LineEditor
|
|
|
223
223
|
|
|
224
224
|
def reset_variables(prompt = '')
|
|
225
225
|
@prompt = prompt.gsub("\n", "\\n")
|
|
226
|
-
@
|
|
226
|
+
@mark_position = nil
|
|
227
227
|
@is_multiline = false
|
|
228
228
|
@finished = false
|
|
229
229
|
@history_pointer = nil
|
|
@@ -253,8 +253,8 @@ class Reline::LineEditor
|
|
|
253
253
|
@undo_redo_history = [[[""], 0, 0]]
|
|
254
254
|
@undo_redo_index = 0
|
|
255
255
|
@restoring = false
|
|
256
|
-
@prev_action_state =
|
|
257
|
-
@next_action_state =
|
|
256
|
+
@prev_action_state = {}
|
|
257
|
+
@next_action_state = {}
|
|
258
258
|
reset_line
|
|
259
259
|
end
|
|
260
260
|
|
|
@@ -476,6 +476,20 @@ class Reline::LineEditor
|
|
|
476
476
|
prompt_width = Reline::Unicode.calculate_width(prompt, true)
|
|
477
477
|
[[0, prompt_width, prompt], [prompt_width, Reline::Unicode.calculate_width(line, true), line]]
|
|
478
478
|
end
|
|
479
|
+
|
|
480
|
+
# Add rprompt to the first visible line if set and there's room
|
|
481
|
+
if @rprompt && !@rprompt.empty? && new_lines[0]
|
|
482
|
+
rprompt_width = Reline::Unicode.calculate_width(@rprompt, true)
|
|
483
|
+
right_col = screen_width - rprompt_width
|
|
484
|
+
first_line = new_lines[0]
|
|
485
|
+
# Calculate the end of the current content (prompt + input)
|
|
486
|
+
content_end = first_line.sum { |_, width, _| width }
|
|
487
|
+
# Only show rprompt if there's at least 1 char gap between content and rprompt
|
|
488
|
+
if right_col > content_end
|
|
489
|
+
first_line << [right_col, rprompt_width, @rprompt]
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
|
|
479
493
|
if @menu_info
|
|
480
494
|
@menu_info.lines(screen_width).each do |item|
|
|
481
495
|
new_lines << [[0, Reline::Unicode.calculate_width(item), item]]
|
|
@@ -491,8 +505,8 @@ class Reline::LineEditor
|
|
|
491
505
|
next if row < 0 || row >= screen_height
|
|
492
506
|
|
|
493
507
|
dialog_rows = new_lines[row] ||= []
|
|
494
|
-
# index 0 is for prompt, index 1 is for line, index 2.. is for dialog
|
|
495
|
-
dialog_rows[index +
|
|
508
|
+
# index 0 is for prompt, index 1 is for line, index 2 is for rprompt, index 3.. is for dialog
|
|
509
|
+
dialog_rows[index + 3] = [x_range.begin, dialog.width, dialog.contents[row - y_range.begin]]
|
|
496
510
|
end
|
|
497
511
|
end
|
|
498
512
|
|
|
@@ -948,7 +962,7 @@ class Reline::LineEditor
|
|
|
948
962
|
|
|
949
963
|
def wrap_method_call(method_symbol, key, with_operator)
|
|
950
964
|
if @waiting_proc
|
|
951
|
-
@waiting_proc.call(key)
|
|
965
|
+
@waiting_proc.call(key, method_symbol)
|
|
952
966
|
return
|
|
953
967
|
end
|
|
954
968
|
|
|
@@ -1022,7 +1036,7 @@ class Reline::LineEditor
|
|
|
1022
1036
|
@byte_pointer -= byte_size
|
|
1023
1037
|
end
|
|
1024
1038
|
|
|
1025
|
-
@prev_action_state, @next_action_state = @next_action_state,
|
|
1039
|
+
@prev_action_state, @next_action_state = @next_action_state, {}
|
|
1026
1040
|
|
|
1027
1041
|
unless @completion_occurs
|
|
1028
1042
|
@completion_state = CompletionState::NORMAL
|
|
@@ -1434,21 +1448,24 @@ class Reline::LineEditor
|
|
|
1434
1448
|
end
|
|
1435
1449
|
alias_method :end_of_line, :ed_move_to_end
|
|
1436
1450
|
|
|
1437
|
-
private def generate_searcher(
|
|
1451
|
+
private def generate_searcher(direction)
|
|
1438
1452
|
search_word = String.new(encoding: encoding)
|
|
1439
1453
|
hit_pointer = nil
|
|
1440
|
-
lambda do |key|
|
|
1454
|
+
lambda do |key, key_symbol|
|
|
1441
1455
|
search_again = false
|
|
1442
|
-
case
|
|
1443
|
-
when
|
|
1456
|
+
case key_symbol
|
|
1457
|
+
when :em_delete_prev_char, :backward_delete_char
|
|
1444
1458
|
grapheme_clusters = search_word.grapheme_clusters
|
|
1445
1459
|
if grapheme_clusters.size > 0
|
|
1446
1460
|
grapheme_clusters.pop
|
|
1447
1461
|
search_word = grapheme_clusters.join
|
|
1448
1462
|
end
|
|
1449
|
-
when
|
|
1450
|
-
search_again =
|
|
1451
|
-
|
|
1463
|
+
when :reverse_search_history, :vi_search_prev
|
|
1464
|
+
search_again = direction == :reverse
|
|
1465
|
+
direction = :reverse
|
|
1466
|
+
when :forward_search_history, :vi_search_next
|
|
1467
|
+
search_again = direction == :forward
|
|
1468
|
+
direction = :forward
|
|
1452
1469
|
else
|
|
1453
1470
|
search_word << key
|
|
1454
1471
|
end
|
|
@@ -1462,11 +1479,11 @@ class Reline::LineEditor
|
|
|
1462
1479
|
search_word = Reline.last_incremental_search
|
|
1463
1480
|
end
|
|
1464
1481
|
if @history_pointer
|
|
1465
|
-
case
|
|
1466
|
-
when
|
|
1482
|
+
case direction
|
|
1483
|
+
when :reverse
|
|
1467
1484
|
history_pointer_base = 0
|
|
1468
1485
|
history = Reline::HISTORY[0..(@history_pointer - 1)]
|
|
1469
|
-
when
|
|
1486
|
+
when :forward
|
|
1470
1487
|
history_pointer_base = @history_pointer + 1
|
|
1471
1488
|
history = Reline::HISTORY[(@history_pointer + 1)..-1]
|
|
1472
1489
|
end
|
|
@@ -1475,11 +1492,11 @@ class Reline::LineEditor
|
|
|
1475
1492
|
history = Reline::HISTORY
|
|
1476
1493
|
end
|
|
1477
1494
|
elsif @history_pointer
|
|
1478
|
-
case
|
|
1479
|
-
when
|
|
1495
|
+
case direction
|
|
1496
|
+
when :reverse
|
|
1480
1497
|
history_pointer_base = 0
|
|
1481
1498
|
history = Reline::HISTORY[0..@history_pointer]
|
|
1482
|
-
when
|
|
1499
|
+
when :forward
|
|
1483
1500
|
history_pointer_base = @history_pointer
|
|
1484
1501
|
history = Reline::HISTORY[@history_pointer..-1]
|
|
1485
1502
|
end
|
|
@@ -1487,12 +1504,12 @@ class Reline::LineEditor
|
|
|
1487
1504
|
history_pointer_base = 0
|
|
1488
1505
|
history = Reline::HISTORY
|
|
1489
1506
|
end
|
|
1490
|
-
case
|
|
1491
|
-
when
|
|
1507
|
+
case direction
|
|
1508
|
+
when :reverse
|
|
1492
1509
|
hit_index = history.rindex { |item|
|
|
1493
1510
|
item.include?(search_word)
|
|
1494
1511
|
}
|
|
1495
|
-
when
|
|
1512
|
+
when :forward
|
|
1496
1513
|
hit_index = history.index { |item|
|
|
1497
1514
|
item.include?(search_word)
|
|
1498
1515
|
}
|
|
@@ -1502,31 +1519,28 @@ class Reline::LineEditor
|
|
|
1502
1519
|
hit = Reline::HISTORY[hit_pointer]
|
|
1503
1520
|
end
|
|
1504
1521
|
end
|
|
1505
|
-
|
|
1506
|
-
when "\C-r"
|
|
1507
|
-
prompt_name = 'reverse-i-search'
|
|
1508
|
-
when "\C-s"
|
|
1509
|
-
prompt_name = 'i-search'
|
|
1510
|
-
end
|
|
1522
|
+
prompt_name = direction == :forward ? 'i-search' : 'reverse-i-search'
|
|
1511
1523
|
prompt_name = "failed #{prompt_name}" unless hit
|
|
1512
1524
|
[search_word, prompt_name, hit_pointer]
|
|
1513
1525
|
end
|
|
1514
1526
|
end
|
|
1515
1527
|
|
|
1516
|
-
private def incremental_search_history(
|
|
1528
|
+
private def incremental_search_history(direction)
|
|
1517
1529
|
backup = @buffer_of_lines.dup, @line_index, @byte_pointer, @history_pointer, @line_backup_in_history
|
|
1518
|
-
searcher = generate_searcher(
|
|
1519
|
-
|
|
1530
|
+
searcher = generate_searcher(direction)
|
|
1531
|
+
prompt_name = direction == :forward ? 'i-search' : 'reverse-i-search'
|
|
1532
|
+
@searching_prompt = "(#{prompt_name})`': "
|
|
1520
1533
|
termination_keys = ["\C-j"]
|
|
1521
1534
|
termination_keys.concat(@config.isearch_terminators.chars) if @config.isearch_terminators
|
|
1522
|
-
|
|
1535
|
+
accept_key_syms = [:em_delete_prev_char, :backward_delete_char, :vi_search_prev, :vi_search_next, :reverse_search_history, :forward_search_history]
|
|
1536
|
+
@waiting_proc = ->(k, key_symbol) {
|
|
1523
1537
|
if k == "\C-g"
|
|
1524
1538
|
# cancel search and restore buffer
|
|
1525
1539
|
@buffer_of_lines, @line_index, @byte_pointer, @history_pointer, @line_backup_in_history = backup
|
|
1526
1540
|
@searching_prompt = nil
|
|
1527
1541
|
@waiting_proc = nil
|
|
1528
|
-
elsif !termination_keys.include?(k) && (k.match?(/[[:print:]]/) ||
|
|
1529
|
-
search_word, prompt_name, hit_pointer = searcher.call(k)
|
|
1542
|
+
elsif !termination_keys.include?(k) && (k.match?(/[[:print:]]/) || accept_key_syms.include?(key_symbol))
|
|
1543
|
+
search_word, prompt_name, hit_pointer = searcher.call(k, key_symbol)
|
|
1530
1544
|
Reline.last_incremental_search = search_word
|
|
1531
1545
|
@searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
|
|
1532
1546
|
@searching_prompt += ': ' unless @is_multiline
|
|
@@ -1541,12 +1555,12 @@ class Reline::LineEditor
|
|
|
1541
1555
|
end
|
|
1542
1556
|
|
|
1543
1557
|
private def vi_search_prev(key)
|
|
1544
|
-
incremental_search_history(
|
|
1558
|
+
incremental_search_history(:reverse)
|
|
1545
1559
|
end
|
|
1546
1560
|
alias_method :reverse_search_history, :vi_search_prev
|
|
1547
1561
|
|
|
1548
1562
|
private def vi_search_next(key)
|
|
1549
|
-
incremental_search_history(
|
|
1563
|
+
incremental_search_history(:forward)
|
|
1550
1564
|
end
|
|
1551
1565
|
alias_method :forward_search_history, :vi_search_next
|
|
1552
1566
|
|
|
@@ -1779,17 +1793,24 @@ class Reline::LineEditor
|
|
|
1779
1793
|
|
|
1780
1794
|
private def em_yank(key)
|
|
1781
1795
|
yanked = @kill_ring.yank
|
|
1782
|
-
|
|
1796
|
+
return unless yanked
|
|
1797
|
+
|
|
1798
|
+
before_cursor = current_line.byteslice(0, @byte_pointer)
|
|
1799
|
+
after_cursor = current_line.byteslice(@byte_pointer, current_line.bytesize)
|
|
1800
|
+
set_current_line(before_cursor + yanked + after_cursor, before_cursor.bytesize + yanked.bytesize)
|
|
1801
|
+
set_next_action_state(:em_yank_line, [before_cursor, after_cursor])
|
|
1783
1802
|
end
|
|
1784
1803
|
alias_method :yank, :em_yank
|
|
1785
1804
|
|
|
1786
1805
|
private def em_yank_pop(key)
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1806
|
+
before_cursor, after_cursor = prev_action_state_value(:em_yank_line)
|
|
1807
|
+
return unless before_cursor and after_cursor
|
|
1808
|
+
|
|
1809
|
+
yanked, = @kill_ring.yank_pop
|
|
1810
|
+
return unless yanked
|
|
1811
|
+
|
|
1812
|
+
set_current_line(before_cursor + yanked + after_cursor, before_cursor.bytesize + yanked.bytesize)
|
|
1813
|
+
set_next_action_state(:em_yank_line, [before_cursor, after_cursor])
|
|
1793
1814
|
end
|
|
1794
1815
|
alias_method :yank_pop, :em_yank_pop
|
|
1795
1816
|
|
|
@@ -2182,7 +2203,7 @@ class Reline::LineEditor
|
|
|
2182
2203
|
end
|
|
2183
2204
|
|
|
2184
2205
|
private def vi_replace_char(key, arg: 1)
|
|
2185
|
-
@waiting_proc = ->(k) {
|
|
2206
|
+
@waiting_proc = ->(k, _sym) {
|
|
2186
2207
|
if arg == 1
|
|
2187
2208
|
byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
|
|
2188
2209
|
before = current_line.byteslice(0, @byte_pointer)
|
|
@@ -2206,11 +2227,11 @@ class Reline::LineEditor
|
|
|
2206
2227
|
end
|
|
2207
2228
|
|
|
2208
2229
|
private def vi_next_char(key, arg: 1, inclusive: false)
|
|
2209
|
-
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, inclusive: inclusive) }
|
|
2230
|
+
@waiting_proc = ->(key_for_proc, _sym) { search_next_char(key_for_proc, arg, inclusive: inclusive) }
|
|
2210
2231
|
end
|
|
2211
2232
|
|
|
2212
2233
|
private def vi_to_next_char(key, arg: 1, inclusive: false)
|
|
2213
|
-
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, need_prev_char: true, inclusive: inclusive) }
|
|
2234
|
+
@waiting_proc = ->(key_for_proc, _sym) { search_next_char(key_for_proc, arg, need_prev_char: true, inclusive: inclusive) }
|
|
2214
2235
|
end
|
|
2215
2236
|
|
|
2216
2237
|
private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
|
|
@@ -2253,11 +2274,11 @@ class Reline::LineEditor
|
|
|
2253
2274
|
end
|
|
2254
2275
|
|
|
2255
2276
|
private def vi_prev_char(key, arg: 1)
|
|
2256
|
-
@waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg) }
|
|
2277
|
+
@waiting_proc = ->(key_for_proc, _sym) { search_prev_char(key_for_proc, arg) }
|
|
2257
2278
|
end
|
|
2258
2279
|
|
|
2259
2280
|
private def vi_to_prev_char(key, arg: 1)
|
|
2260
|
-
@waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg, true) }
|
|
2281
|
+
@waiting_proc = ->(key_for_proc, _sym) { search_prev_char(key_for_proc, arg, true) }
|
|
2261
2282
|
end
|
|
2262
2283
|
|
|
2263
2284
|
private def search_prev_char(key, arg, need_next_char = false)
|
|
@@ -2303,15 +2324,22 @@ class Reline::LineEditor
|
|
|
2303
2324
|
end
|
|
2304
2325
|
|
|
2305
2326
|
private def em_set_mark(key)
|
|
2306
|
-
|
|
2327
|
+
cursor_column = Reline::Unicode.calculate_width(current_line.byteslice(0, @byte_pointer))
|
|
2328
|
+
@mark_position = [@line_index, cursor_column]
|
|
2307
2329
|
end
|
|
2308
2330
|
alias_method :set_mark, :em_set_mark
|
|
2309
2331
|
|
|
2310
2332
|
private def em_exchange_mark(key)
|
|
2311
|
-
return unless @
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
@
|
|
2333
|
+
return unless @mark_position
|
|
2334
|
+
line_index, cursor_column = @mark_position
|
|
2335
|
+
em_set_mark(key)
|
|
2336
|
+
if @buffer_of_lines.size <= line_index
|
|
2337
|
+
@line_index = @buffer_of_lines.size - 1
|
|
2338
|
+
@byte_pointer = current_line.bytesize
|
|
2339
|
+
else
|
|
2340
|
+
@line_index = line_index
|
|
2341
|
+
calculate_nearest_cursor(cursor_column)
|
|
2342
|
+
end
|
|
2315
2343
|
end
|
|
2316
2344
|
alias_method :exchange_point_and_mark, :em_exchange_mark
|
|
2317
2345
|
|
|
@@ -2343,11 +2371,11 @@ class Reline::LineEditor
|
|
|
2343
2371
|
end
|
|
2344
2372
|
|
|
2345
2373
|
private def prev_action_state_value(type)
|
|
2346
|
-
@prev_action_state[
|
|
2374
|
+
@prev_action_state[type]
|
|
2347
2375
|
end
|
|
2348
2376
|
|
|
2349
2377
|
private def set_next_action_state(type, value)
|
|
2350
|
-
@next_action_state =
|
|
2378
|
+
@next_action_state[type] = value
|
|
2351
2379
|
end
|
|
2352
2380
|
|
|
2353
2381
|
private def re_read_init_file(_key)
|
data/lib/reline/unicode.rb
CHANGED
|
@@ -37,7 +37,7 @@ class Reline::Unicode
|
|
|
37
37
|
|
|
38
38
|
NON_PRINTING_START = "\1"
|
|
39
39
|
NON_PRINTING_END = "\2"
|
|
40
|
-
CSI_REGEXP = /\e\[[\
|
|
40
|
+
CSI_REGEXP = /\e\[[\x30-\x3f]*[\x20-\x2f]*[a-zA-Z]/
|
|
41
41
|
OSC_REGEXP = /\e\]\d+(?:;[^;\a\e]+)*(?:\a|\e\\)/
|
|
42
42
|
WIDTH_SCANNER = /\G(?:(#{NON_PRINTING_START})|(#{NON_PRINTING_END})|(#{CSI_REGEXP})|(#{OSC_REGEXP})|(\X))/o
|
|
43
43
|
|
data/lib/reline/version.rb
CHANGED
data/lib/reline.rb
CHANGED
|
@@ -247,19 +247,19 @@ module Reline
|
|
|
247
247
|
} # :nodoc:
|
|
248
248
|
Reline::DEFAULT_DIALOG_CONTEXT = Array.new # :nodoc:
|
|
249
249
|
|
|
250
|
-
def readmultiline(
|
|
250
|
+
def readmultiline(_prompt = '', _add_history = false, prompt: _prompt, add_history: _add_history, rprompt: nil, &confirm_multiline_termination)
|
|
251
251
|
@mutex.synchronize do
|
|
252
252
|
unless confirm_multiline_termination
|
|
253
253
|
raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
|
|
254
254
|
end
|
|
255
255
|
|
|
256
256
|
io_gate.with_raw_input do
|
|
257
|
-
inner_readline(prompt,
|
|
257
|
+
inner_readline(prompt, add_history, true, rprompt: rprompt, &confirm_multiline_termination)
|
|
258
258
|
end
|
|
259
259
|
|
|
260
260
|
whole_buffer = line_editor.whole_buffer.dup
|
|
261
261
|
whole_buffer.taint if RUBY_VERSION < '2.7'
|
|
262
|
-
if
|
|
262
|
+
if add_history and whole_buffer and whole_buffer.chomp("\n").size > 0
|
|
263
263
|
Reline::HISTORY << whole_buffer
|
|
264
264
|
end
|
|
265
265
|
|
|
@@ -273,15 +273,15 @@ module Reline
|
|
|
273
273
|
end
|
|
274
274
|
end
|
|
275
275
|
|
|
276
|
-
def readline(
|
|
276
|
+
def readline(_prompt = '', _add_history = false, prompt: _prompt, add_history: _add_history, rprompt: nil)
|
|
277
277
|
@mutex.synchronize do
|
|
278
278
|
io_gate.with_raw_input do
|
|
279
|
-
inner_readline(prompt,
|
|
279
|
+
inner_readline(prompt, add_history, false, rprompt: rprompt)
|
|
280
280
|
end
|
|
281
281
|
|
|
282
282
|
line = line_editor.line.dup
|
|
283
283
|
line.taint if RUBY_VERSION < '2.7'
|
|
284
|
-
if
|
|
284
|
+
if add_history and line and line.chomp("\n").size > 0
|
|
285
285
|
Reline::HISTORY << line.chomp("\n")
|
|
286
286
|
end
|
|
287
287
|
|
|
@@ -290,7 +290,7 @@ module Reline
|
|
|
290
290
|
end
|
|
291
291
|
end
|
|
292
292
|
|
|
293
|
-
private def inner_readline(prompt,
|
|
293
|
+
private def inner_readline(prompt, add_history, multiline, rprompt: nil, &confirm_multiline_termination)
|
|
294
294
|
if ENV['RELINE_STDERR_TTY']
|
|
295
295
|
if io_gate.win?
|
|
296
296
|
$stderr = File.open(ENV['RELINE_STDERR_TTY'], 'a')
|
|
@@ -323,6 +323,7 @@ module Reline
|
|
|
323
323
|
line_editor.prompt_proc = prompt_proc
|
|
324
324
|
line_editor.auto_indent_proc = auto_indent_proc
|
|
325
325
|
line_editor.dig_perfect_match_proc = dig_perfect_match_proc
|
|
326
|
+
line_editor.rprompt = rprompt&.encode(encoding)
|
|
326
327
|
|
|
327
328
|
pre_input_hook&.call
|
|
328
329
|
|
|
@@ -442,13 +443,13 @@ module Reline
|
|
|
442
443
|
##
|
|
443
444
|
# :singleton-method: readmultiline
|
|
444
445
|
# :call-seq:
|
|
445
|
-
# readmultiline(prompt = '',
|
|
446
|
+
# readmultiline(prompt = '', add_history = false, &confirm_multiline_termination) -> string or nil
|
|
446
447
|
def_single_delegators :core, :readmultiline
|
|
447
448
|
|
|
448
449
|
##
|
|
449
450
|
# :singleton-method: readline
|
|
450
451
|
# :call-seq:
|
|
451
|
-
# readline(prompt = '',
|
|
452
|
+
# readline(prompt = '', add_history = false) -> string or nil
|
|
452
453
|
def_single_delegators :core, :readline
|
|
453
454
|
def_single_delegators :core, :completion_case_fold, :completion_case_fold=
|
|
454
455
|
def_single_delegators :core, :completion_quote_character
|