reline 0.5.0.pre.1 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/reline/ansi.rb +14 -6
- data/lib/reline/face.rb +3 -3
- data/lib/reline/general_io.rb +3 -8
- data/lib/reline/history.rb +1 -1
- data/lib/reline/kill_ring.rb +2 -2
- data/lib/reline/line_editor.rb +243 -228
- data/lib/reline/terminfo.rb +7 -14
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +4 -2
- data/lib/reline.rb +44 -59
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47d262bd9b8ab9b3fb314863af068783c217d53177f947372eb979df2ceaf3cf
|
4
|
+
data.tar.gz: 8112b4441c7946a697adf3d16ec79531fc744fac132ea3cca89ef16b9b6518d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22570f23bfa74602ef627bf977982304b0e5849cbc52abb33f321a32b3860348eabb3efd88bfcfa6b46032433a012813a0fa9aa70422438d0f0694a724ca3055
|
7
|
+
data.tar.gz: 6a30e3c69c1a64149a5cd0517e3c46c6dfb9d724532ded4ab4475c6b595bcf5cdf6b6ffd1b9d32156b8a8a447885bb918d35f78be90934a2e2513f87fdf40bb0
|
data/lib/reline/ansi.rb
CHANGED
@@ -3,6 +3,8 @@ require 'io/wait'
|
|
3
3
|
require_relative 'terminfo'
|
4
4
|
|
5
5
|
class Reline::ANSI
|
6
|
+
RESET_COLOR = "\e[0m"
|
7
|
+
|
6
8
|
CAPNAME_KEY_BINDINGS = {
|
7
9
|
'khome' => :ed_move_to_beg,
|
8
10
|
'kend' => :ed_move_to_end,
|
@@ -149,7 +151,11 @@ class Reline::ANSI
|
|
149
151
|
end
|
150
152
|
|
151
153
|
def self.with_raw_input
|
152
|
-
@@input.
|
154
|
+
if @@input.tty?
|
155
|
+
@@input.raw(intr: true) { yield }
|
156
|
+
else
|
157
|
+
yield
|
158
|
+
end
|
153
159
|
end
|
154
160
|
|
155
161
|
@@buf = []
|
@@ -157,11 +163,13 @@ class Reline::ANSI
|
|
157
163
|
unless @@buf.empty?
|
158
164
|
return @@buf.shift
|
159
165
|
end
|
160
|
-
until
|
161
|
-
timeout_second -= 0.
|
166
|
+
until @@input.wait_readable(0.01)
|
167
|
+
timeout_second -= 0.01
|
162
168
|
return nil if timeout_second <= 0
|
163
|
-
|
169
|
+
|
170
|
+
Reline.core.line_editor.handle_signal
|
164
171
|
end
|
172
|
+
c = @@input.getbyte
|
165
173
|
(c == 0x16 && @@input.raw(min: 0, time: 0, &:getbyte)) || c
|
166
174
|
rescue Errno::EIO
|
167
175
|
# Maybe the I/O has been closed.
|
@@ -307,7 +315,7 @@ class Reline::ANSI
|
|
307
315
|
end
|
308
316
|
|
309
317
|
def self.hide_cursor
|
310
|
-
if Reline::Terminfo.enabled?
|
318
|
+
if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
|
311
319
|
begin
|
312
320
|
@@output.write Reline::Terminfo.tigetstr('civis')
|
313
321
|
rescue Reline::Terminfo::TerminfoError
|
@@ -319,7 +327,7 @@ class Reline::ANSI
|
|
319
327
|
end
|
320
328
|
|
321
329
|
def self.show_cursor
|
322
|
-
if Reline::Terminfo.enabled?
|
330
|
+
if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
|
323
331
|
begin
|
324
332
|
@@output.write Reline::Terminfo.tigetstr('cnorm')
|
325
333
|
rescue Reline::Terminfo::TerminfoError
|
data/lib/reline/face.rb
CHANGED
@@ -186,9 +186,9 @@ class Reline::Face
|
|
186
186
|
conf.define :scrollbar, style: :reset
|
187
187
|
end
|
188
188
|
config(:completion_dialog) do |conf|
|
189
|
-
conf.define :default, foreground: :
|
190
|
-
conf.define :enhanced, foreground: :
|
191
|
-
conf.define :scrollbar, foreground: :white, background: :
|
189
|
+
conf.define :default, foreground: :bright_white, background: :gray
|
190
|
+
conf.define :enhanced, foreground: :black, background: :white
|
191
|
+
conf.define :scrollbar, foreground: :white, background: :gray
|
192
192
|
end
|
193
193
|
end
|
194
194
|
|
data/lib/reline/general_io.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'io/wait'
|
2
2
|
|
3
3
|
class Reline::GeneralIO
|
4
|
+
RESET_COLOR = '' # Do not send color reset sequence
|
5
|
+
|
4
6
|
def self.reset(encoding: nil)
|
5
7
|
@@pasting = false
|
6
8
|
if encoding
|
@@ -44,6 +46,7 @@ class Reline::GeneralIO
|
|
44
46
|
end
|
45
47
|
c = nil
|
46
48
|
loop do
|
49
|
+
Reline.core.line_editor.handle_signal
|
47
50
|
result = @@input.wait_readable(0.1)
|
48
51
|
next if result.nil?
|
49
52
|
c = @@input.read(1)
|
@@ -100,14 +103,6 @@ class Reline::GeneralIO
|
|
100
103
|
@@pasting
|
101
104
|
end
|
102
105
|
|
103
|
-
def self.start_pasting
|
104
|
-
@@pasting = true
|
105
|
-
end
|
106
|
-
|
107
|
-
def self.finish_pasting
|
108
|
-
@@pasting = false
|
109
|
-
end
|
110
|
-
|
111
106
|
def self.prep
|
112
107
|
end
|
113
108
|
|
data/lib/reline/history.rb
CHANGED
@@ -62,7 +62,7 @@ class Reline::History < Array
|
|
62
62
|
private def check_index(index)
|
63
63
|
index += size if index < 0
|
64
64
|
if index < -2147483648 or 2147483647 < index
|
65
|
-
raise RangeError.new("integer #{index} too big to convert to
|
65
|
+
raise RangeError.new("integer #{index} too big to convert to 'int'")
|
66
66
|
end
|
67
67
|
# If history_size is negative, history size is unlimited.
|
68
68
|
if @config.history_size.positive?
|
data/lib/reline/kill_ring.rb
CHANGED
@@ -14,7 +14,7 @@ class Reline::KillRing
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def ==(other)
|
17
|
-
|
17
|
+
equal?(other)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -68,7 +68,7 @@ class Reline::KillRing
|
|
68
68
|
def append(string, before_p = false)
|
69
69
|
case @state
|
70
70
|
when State::FRESH, State::YANK
|
71
|
-
@ring << RingPoint.new(string)
|
71
|
+
@ring << RingPoint.new(+string)
|
72
72
|
@state = State::CONTINUED
|
73
73
|
when State::CONTINUED, State::PROCESSED
|
74
74
|
if before_p
|
data/lib/reline/line_editor.rb
CHANGED
@@ -41,21 +41,42 @@ class Reline::LineEditor
|
|
41
41
|
NORMAL = :normal
|
42
42
|
COMPLETION = :completion
|
43
43
|
MENU = :menu
|
44
|
-
JOURNEY = :journey
|
45
44
|
MENU_WITH_PERFECT_MATCH = :menu_with_perfect_match
|
46
45
|
PERFECT_MATCH = :perfect_match
|
47
46
|
end
|
48
47
|
|
49
|
-
|
50
|
-
|
48
|
+
RenderedScreen = Struct.new(:base_y, :lines, :cursor_y, keyword_init: true)
|
49
|
+
|
50
|
+
CompletionJourneyState = Struct.new(:line_index, :pre, :target, :post, :list, :pointer)
|
51
|
+
|
52
|
+
class MenuInfo
|
53
|
+
attr_reader :list
|
54
|
+
|
55
|
+
def initialize(list)
|
56
|
+
@list = list
|
57
|
+
end
|
58
|
+
|
59
|
+
def lines(screen_width)
|
60
|
+
return [] if @list.empty?
|
61
|
+
|
62
|
+
list = @list.sort
|
63
|
+
sizes = list.map { |item| Reline::Unicode.calculate_width(item) }
|
64
|
+
item_width = sizes.max + 2
|
65
|
+
num_cols = [screen_width / item_width, 1].max
|
66
|
+
num_rows = list.size.fdiv(num_cols).ceil
|
67
|
+
list_with_padding = list.zip(sizes).map { |item, size| item + ' ' * (item_width - size) }
|
68
|
+
aligned = (list_with_padding + [nil] * (num_rows * num_cols - list_with_padding.size)).each_slice(num_rows).to_a.transpose
|
69
|
+
aligned.map do |row|
|
70
|
+
row.join.rstrip
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
51
74
|
|
52
|
-
PROMPT_LIST_CACHE_TIMEOUT = 0.5
|
53
75
|
MINIMUM_SCROLLBAR_HEIGHT = 1
|
54
76
|
|
55
77
|
def initialize(config, encoding)
|
56
78
|
@config = config
|
57
79
|
@completion_append_character = ''
|
58
|
-
@cursor_base_y = 0
|
59
80
|
@screen_size = Reline::IOGate.get_screen_size
|
60
81
|
reset_variables(encoding: encoding)
|
61
82
|
end
|
@@ -65,6 +86,9 @@ class Reline::LineEditor
|
|
65
86
|
end
|
66
87
|
|
67
88
|
def set_pasting_state(in_pasting)
|
89
|
+
# While pasting, text to be inserted is stored to @continuous_insertion_buffer.
|
90
|
+
# After pasting, this buffer should be force inserted.
|
91
|
+
process_insert(force: true) if @in_pasting && !in_pasting
|
68
92
|
@in_pasting = in_pasting
|
69
93
|
end
|
70
94
|
|
@@ -82,7 +106,7 @@ class Reline::LineEditor
|
|
82
106
|
end
|
83
107
|
end
|
84
108
|
|
85
|
-
private def check_multiline_prompt(buffer)
|
109
|
+
private def check_multiline_prompt(buffer, mode_string)
|
86
110
|
if @vi_arg
|
87
111
|
prompt = "(arg: #{@vi_arg}) "
|
88
112
|
elsif @searching_prompt
|
@@ -94,7 +118,6 @@ class Reline::LineEditor
|
|
94
118
|
prompt_list = @prompt_proc.(buffer).map { |pr| pr.gsub("\n", "\\n") }
|
95
119
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
96
120
|
prompt_list = [prompt] if prompt_list.empty?
|
97
|
-
mode_string = check_mode_string
|
98
121
|
prompt_list = prompt_list.map{ |pr| mode_string + pr } if mode_string
|
99
122
|
prompt = prompt_list[@line_index]
|
100
123
|
prompt = prompt_list[0] if prompt.nil?
|
@@ -106,19 +129,15 @@ class Reline::LineEditor
|
|
106
129
|
end
|
107
130
|
prompt_list
|
108
131
|
else
|
109
|
-
mode_string = check_mode_string
|
110
132
|
prompt = mode_string + prompt if mode_string
|
111
133
|
[prompt] * buffer.size
|
112
134
|
end
|
113
135
|
end
|
114
136
|
|
115
137
|
def reset(prompt = '', encoding:)
|
116
|
-
@cursor_base_y = Reline::IOGate.cursor_pos.y
|
117
138
|
@screen_size = Reline::IOGate.get_screen_size
|
118
139
|
reset_variables(prompt, encoding: encoding)
|
119
|
-
Reline::IOGate.
|
120
|
-
@resized = true
|
121
|
-
end
|
140
|
+
@rendered_screen.base_y = Reline::IOGate.cursor_pos.y
|
122
141
|
if ENV.key?('RELINE_ALT_SCROLLBAR')
|
123
142
|
@full_block = '::'
|
124
143
|
@upper_half_block = "''"
|
@@ -142,41 +161,53 @@ class Reline::LineEditor
|
|
142
161
|
end
|
143
162
|
end
|
144
163
|
|
145
|
-
def
|
164
|
+
def handle_signal
|
165
|
+
handle_interrupted
|
166
|
+
handle_resized
|
167
|
+
end
|
168
|
+
|
169
|
+
private def handle_resized
|
146
170
|
return unless @resized
|
147
171
|
|
148
|
-
Reline::IOGate.hide_cursor
|
149
172
|
@screen_size = Reline::IOGate.get_screen_size
|
150
173
|
@resized = false
|
151
174
|
scroll_into_view
|
152
|
-
Reline::IOGate.move_cursor_up @cursor_y
|
153
|
-
@
|
154
|
-
@
|
155
|
-
@
|
175
|
+
Reline::IOGate.move_cursor_up @rendered_screen.cursor_y
|
176
|
+
@rendered_screen.base_y = Reline::IOGate.cursor_pos.y
|
177
|
+
@rendered_screen.lines = []
|
178
|
+
@rendered_screen.cursor_y = 0
|
156
179
|
render_differential
|
157
|
-
|
180
|
+
end
|
181
|
+
|
182
|
+
private def handle_interrupted
|
183
|
+
return unless @interrupted
|
184
|
+
|
185
|
+
@interrupted = false
|
186
|
+
clear_dialogs
|
187
|
+
scrolldown = render_differential
|
188
|
+
Reline::IOGate.scroll_down scrolldown
|
189
|
+
Reline::IOGate.move_cursor_column 0
|
190
|
+
@rendered_screen.lines = []
|
191
|
+
@rendered_screen.cursor_y = 0
|
192
|
+
case @old_trap
|
193
|
+
when 'DEFAULT', 'SYSTEM_DEFAULT'
|
194
|
+
raise Interrupt
|
195
|
+
when 'IGNORE'
|
196
|
+
# Do nothing
|
197
|
+
when 'EXIT'
|
198
|
+
exit
|
199
|
+
else
|
200
|
+
@old_trap.call if @old_trap.respond_to?(:call)
|
201
|
+
end
|
158
202
|
end
|
159
203
|
|
160
204
|
def set_signal_handlers
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
@rendered_screen_cache = nil
|
168
|
-
Reline::IOGate.show_cursor
|
169
|
-
case @old_trap
|
170
|
-
when 'DEFAULT', 'SYSTEM_DEFAULT'
|
171
|
-
raise Interrupt
|
172
|
-
when 'IGNORE'
|
173
|
-
# Do nothing
|
174
|
-
when 'EXIT'
|
175
|
-
exit
|
176
|
-
else
|
177
|
-
@old_trap.call if @old_trap.respond_to?(:call)
|
178
|
-
end
|
179
|
-
}
|
205
|
+
Reline::IOGate.set_winch_handler do
|
206
|
+
@resized = true
|
207
|
+
end
|
208
|
+
@old_trap = Signal.trap('INT') do
|
209
|
+
@interrupted = true
|
210
|
+
end
|
180
211
|
end
|
181
212
|
|
182
213
|
def finalize
|
@@ -193,7 +224,6 @@ class Reline::LineEditor
|
|
193
224
|
@encoding = encoding
|
194
225
|
@is_multiline = false
|
195
226
|
@finished = false
|
196
|
-
@cleared = false
|
197
227
|
@history_pointer = nil
|
198
228
|
@kill_ring ||= Reline::KillRing.new
|
199
229
|
@vi_clipboard = ''
|
@@ -201,13 +231,13 @@ class Reline::LineEditor
|
|
201
231
|
@waiting_proc = nil
|
202
232
|
@waiting_operator_proc = nil
|
203
233
|
@waiting_operator_vi_arg = nil
|
204
|
-
@
|
234
|
+
@completion_journey_state = nil
|
205
235
|
@completion_state = CompletionState::NORMAL
|
206
236
|
@perfect_matched = nil
|
207
237
|
@menu_info = nil
|
208
|
-
@first_prompt = true
|
209
238
|
@searching_prompt = nil
|
210
239
|
@first_char = true
|
240
|
+
@just_cursor_moving = false
|
211
241
|
@eof = false
|
212
242
|
@continuous_insertion_buffer = String.new(encoding: @encoding)
|
213
243
|
@scroll_partial_screen = 0
|
@@ -215,10 +245,10 @@ class Reline::LineEditor
|
|
215
245
|
@in_pasting = false
|
216
246
|
@auto_indent_proc = nil
|
217
247
|
@dialogs = []
|
248
|
+
@interrupted = false
|
218
249
|
@resized = false
|
219
|
-
@cursor_y = 0
|
220
250
|
@cache = {}
|
221
|
-
@
|
251
|
+
@rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0)
|
222
252
|
reset_line
|
223
253
|
end
|
224
254
|
|
@@ -266,20 +296,6 @@ class Reline::LineEditor
|
|
266
296
|
Reline::Unicode.split_by_width(str, max_width, @encoding)
|
267
297
|
end
|
268
298
|
|
269
|
-
private def scroll_down(val)
|
270
|
-
if @cursor_base_y + @cursor_y + val < screen_height
|
271
|
-
Reline::IOGate.move_cursor_down(val)
|
272
|
-
@cursor_y += val
|
273
|
-
else
|
274
|
-
move = screen_height - @cursor_base_y - @cursor_y - 1
|
275
|
-
scroll = val - move
|
276
|
-
Reline::IOGate.scroll_down(move)
|
277
|
-
Reline::IOGate.scroll_down(scroll)
|
278
|
-
@cursor_y += move
|
279
|
-
@cursor_base_y = [@cursor_base_y - scroll, 0].max
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
299
|
def current_byte_pointer_cursor
|
284
300
|
calculate_width(current_line.byteslice(0, @byte_pointer))
|
285
301
|
end
|
@@ -334,8 +350,8 @@ class Reline::LineEditor
|
|
334
350
|
end
|
335
351
|
|
336
352
|
def prompt_list
|
337
|
-
with_cache(__method__, whole_lines, @vi_arg, @searching_prompt) do |lines|
|
338
|
-
check_multiline_prompt(lines)
|
353
|
+
with_cache(__method__, whole_lines, check_mode_string, @vi_arg, @searching_prompt) do |lines, mode_string|
|
354
|
+
check_multiline_prompt(lines, mode_string)
|
339
355
|
end
|
340
356
|
end
|
341
357
|
|
@@ -387,12 +403,12 @@ class Reline::LineEditor
|
|
387
403
|
# do nothing
|
388
404
|
elsif level == :blank
|
389
405
|
Reline::IOGate.move_cursor_column base_x
|
390
|
-
@output.write "
|
406
|
+
@output.write "#{Reline::IOGate::RESET_COLOR}#{' ' * width}"
|
391
407
|
else
|
392
408
|
x, w, content = new_items[level]
|
393
409
|
content = Reline::Unicode.take_range(content, base_x - x, width) unless x == base_x && w == width
|
394
410
|
Reline::IOGate.move_cursor_column base_x
|
395
|
-
@output.write "
|
411
|
+
@output.write "#{Reline::IOGate::RESET_COLOR}#{content}#{Reline::IOGate::RESET_COLOR}"
|
396
412
|
end
|
397
413
|
base_x += width
|
398
414
|
end
|
@@ -402,12 +418,14 @@ class Reline::LineEditor
|
|
402
418
|
end
|
403
419
|
end
|
404
420
|
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
[
|
421
|
+
# Calculate cursor position in word wrapped content.
|
422
|
+
def wrapped_cursor_position
|
423
|
+
prompt_width = calculate_width(prompt_list[@line_index], true)
|
424
|
+
line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer)
|
425
|
+
wrapped_line_before_cursor = split_by_width(' ' * prompt_width + line_before_cursor, screen_width).first.compact
|
426
|
+
wrapped_cursor_y = wrapped_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
|
427
|
+
wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last)
|
428
|
+
[wrapped_cursor_x, wrapped_cursor_y]
|
411
429
|
end
|
412
430
|
|
413
431
|
def clear_dialogs
|
@@ -418,19 +436,23 @@ class Reline::LineEditor
|
|
418
436
|
end
|
419
437
|
|
420
438
|
def update_dialogs(key = nil)
|
421
|
-
|
422
|
-
editor_cursor_x, editor_cursor_y = editor_cursor_position
|
439
|
+
wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
|
423
440
|
@dialogs.each do |dialog|
|
424
441
|
dialog.trap_key = nil
|
425
|
-
update_each_dialog(dialog,
|
442
|
+
update_each_dialog(dialog, wrapped_cursor_x, wrapped_cursor_y - screen_scroll_top, key)
|
426
443
|
end
|
427
444
|
end
|
428
445
|
|
446
|
+
def render_finished
|
447
|
+
clear_rendered_lines
|
448
|
+
render_full_content
|
449
|
+
end
|
450
|
+
|
429
451
|
def clear_rendered_lines
|
430
|
-
Reline::IOGate.move_cursor_up @cursor_y
|
452
|
+
Reline::IOGate.move_cursor_up @rendered_screen.cursor_y
|
431
453
|
Reline::IOGate.move_cursor_column 0
|
432
454
|
|
433
|
-
num_lines = @
|
455
|
+
num_lines = @rendered_screen.lines.size
|
434
456
|
return unless num_lines && num_lines >= 1
|
435
457
|
|
436
458
|
Reline::IOGate.move_cursor_down num_lines - 1
|
@@ -439,7 +461,8 @@ class Reline::LineEditor
|
|
439
461
|
Reline::IOGate.move_cursor_up 1
|
440
462
|
end
|
441
463
|
Reline::IOGate.erase_after_cursor
|
442
|
-
@
|
464
|
+
@rendered_screen.lines = []
|
465
|
+
@rendered_screen.cursor_y = 0
|
443
466
|
end
|
444
467
|
|
445
468
|
def render_full_content
|
@@ -455,23 +478,20 @@ class Reline::LineEditor
|
|
455
478
|
return unless prompt && !@is_multiline
|
456
479
|
|
457
480
|
# Readline's test `TestRelineAsReadline#test_readline` requires first output to be prompt, not cursor reset escape sequence.
|
458
|
-
@
|
481
|
+
@rendered_screen.lines = [[[0, Reline::Unicode.calculate_width(prompt, true), prompt]]]
|
482
|
+
@rendered_screen.cursor_y = 0
|
459
483
|
@output.write prompt
|
460
484
|
end
|
461
485
|
|
462
486
|
def render_differential
|
463
|
-
|
464
|
-
update_dialogs
|
465
|
-
end
|
466
|
-
|
467
|
-
editor_cursor_x, editor_cursor_y = editor_cursor_position
|
487
|
+
wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
|
468
488
|
|
469
|
-
rendered_lines = @
|
489
|
+
rendered_lines = @rendered_screen.lines
|
470
490
|
new_lines = wrapped_lines.flatten[screen_scroll_top, screen_height].map do |l|
|
471
491
|
[[0, Reline::Unicode.calculate_width(l, true), l]]
|
472
492
|
end
|
473
493
|
if @menu_info
|
474
|
-
@menu_info.
|
494
|
+
@menu_info.lines(screen_width).each do |item|
|
475
495
|
new_lines << [[0, Reline::Unicode.calculate_width(item), item]]
|
476
496
|
end
|
477
497
|
@menu_info = nil # TODO: do not change state here
|
@@ -480,7 +500,7 @@ class Reline::LineEditor
|
|
480
500
|
@dialogs.each_with_index do |dialog, index|
|
481
501
|
next unless dialog.contents
|
482
502
|
|
483
|
-
x_range, y_range = dialog_range dialog,
|
503
|
+
x_range, y_range = dialog_range dialog, wrapped_cursor_y - screen_scroll_top
|
484
504
|
y_range.each do |row|
|
485
505
|
next if row < 0 || row >= screen_height
|
486
506
|
dialog_rows = new_lines[row] ||= []
|
@@ -488,77 +508,58 @@ class Reline::LineEditor
|
|
488
508
|
end
|
489
509
|
end
|
490
510
|
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
511
|
+
cursor_y = @rendered_screen.cursor_y
|
512
|
+
if new_lines != rendered_lines
|
513
|
+
# Hide cursor while rendering to avoid cursor flickering.
|
514
|
+
Reline::IOGate.hide_cursor
|
515
|
+
num_lines = [[new_lines.size, rendered_lines.size].max, screen_height].min
|
516
|
+
if @rendered_screen.base_y + num_lines > screen_height
|
517
|
+
Reline::IOGate.scroll_down(num_lines - cursor_y - 1)
|
518
|
+
@rendered_screen.base_y = screen_height - num_lines
|
519
|
+
cursor_y = num_lines - 1
|
520
|
+
end
|
521
|
+
num_lines.times do |i|
|
522
|
+
rendered_line = rendered_lines[i] || []
|
523
|
+
line_to_render = new_lines[i] || []
|
524
|
+
next if rendered_line == line_to_render
|
525
|
+
|
526
|
+
Reline::IOGate.move_cursor_down i - cursor_y
|
527
|
+
cursor_y = i
|
528
|
+
unless rendered_lines[i]
|
529
|
+
Reline::IOGate.move_cursor_column 0
|
530
|
+
Reline::IOGate.erase_after_cursor
|
531
|
+
end
|
532
|
+
render_line_differential(rendered_line, line_to_render)
|
504
533
|
end
|
505
|
-
|
534
|
+
@rendered_screen.lines = new_lines
|
535
|
+
Reline::IOGate.show_cursor
|
506
536
|
end
|
507
|
-
|
508
|
-
|
509
|
-
Reline::IOGate.
|
510
|
-
|
511
|
-
|
512
|
-
new_lines.size - @cursor_y
|
537
|
+
y = wrapped_cursor_y - screen_scroll_top
|
538
|
+
Reline::IOGate.move_cursor_column wrapped_cursor_x
|
539
|
+
Reline::IOGate.move_cursor_down y - cursor_y
|
540
|
+
@rendered_screen.cursor_y = y
|
541
|
+
new_lines.size - y
|
513
542
|
end
|
514
543
|
|
515
544
|
def current_row
|
516
|
-
wrapped_lines.flatten[
|
517
|
-
end
|
518
|
-
|
519
|
-
def upper_space_height
|
520
|
-
@cursor_y - 1
|
545
|
+
wrapped_lines.flatten[wrapped_cursor_y]
|
521
546
|
end
|
522
547
|
|
523
|
-
def
|
524
|
-
|
548
|
+
def upper_space_height(wrapped_cursor_y)
|
549
|
+
wrapped_cursor_y - screen_scroll_top
|
525
550
|
end
|
526
551
|
|
527
|
-
def
|
528
|
-
|
529
|
-
process_insert(force: true)
|
530
|
-
handle_cleared
|
531
|
-
render_differential unless @in_pasting
|
532
|
-
Reline::IOGate.show_cursor
|
533
|
-
end
|
534
|
-
|
535
|
-
def handle_cleared
|
536
|
-
return unless @cleared
|
537
|
-
|
538
|
-
@cleared = false
|
539
|
-
@rendered_screen_cache = nil
|
540
|
-
Reline::IOGate.clear_screen
|
541
|
-
@screen_size = Reline::IOGate.get_screen_size
|
542
|
-
@cursor_base_y = 0
|
543
|
-
@cursor_y = 0
|
544
|
-
scroll_into_view
|
545
|
-
render_differential
|
552
|
+
def rest_height(wrapped_cursor_y)
|
553
|
+
screen_height - wrapped_cursor_y + screen_scroll_top - @rendered_screen.base_y - 1
|
546
554
|
end
|
547
555
|
|
548
556
|
def rerender
|
549
|
-
|
550
|
-
finished = finished?
|
551
|
-
handle_cleared
|
552
|
-
if finished
|
553
|
-
clear_rendered_lines
|
554
|
-
render_full_content
|
555
|
-
elsif !@in_pasting
|
556
|
-
render_differential
|
557
|
-
end
|
558
|
-
Reline::IOGate.show_cursor
|
557
|
+
render_differential unless @in_pasting
|
559
558
|
end
|
560
559
|
|
561
560
|
class DialogProcScope
|
561
|
+
CompletionJourneyData = Struct.new(:preposing, :postposing, :list, :pointer)
|
562
|
+
|
562
563
|
def initialize(line_editor, config, proc_to_exec, context)
|
563
564
|
@line_editor = line_editor
|
564
565
|
@config = config
|
@@ -617,12 +618,12 @@ class Reline::LineEditor
|
|
617
618
|
end
|
618
619
|
|
619
620
|
def preferred_dialog_height
|
620
|
-
|
621
|
-
[
|
621
|
+
_wrapped_cursor_x, wrapped_cursor_y = @line_editor.wrapped_cursor_position
|
622
|
+
[@line_editor.upper_space_height(wrapped_cursor_y), @line_editor.rest_height(wrapped_cursor_y), (screen_height + 6) / 5].max
|
622
623
|
end
|
623
624
|
|
624
625
|
def completion_journey_data
|
625
|
-
@line_editor.
|
626
|
+
@line_editor.dialog_proc_scope_completion_journey_data
|
626
627
|
end
|
627
628
|
|
628
629
|
def config
|
@@ -747,16 +748,15 @@ class Reline::LineEditor
|
|
747
748
|
else
|
748
749
|
scrollbar_pos = nil
|
749
750
|
end
|
750
|
-
upper_space = upper_space_height
|
751
751
|
dialog.column = dialog_render_info.pos.x
|
752
752
|
dialog.width += @block_elem_width if scrollbar_pos
|
753
753
|
diff = (dialog.column + dialog.width) - screen_width
|
754
754
|
if diff > 0
|
755
755
|
dialog.column -= diff
|
756
756
|
end
|
757
|
-
if (
|
757
|
+
if rest_height(screen_scroll_top + cursor_row) - dialog_render_info.pos.y >= height
|
758
758
|
dialog.vertical_offset = dialog_render_info.pos.y + 1
|
759
|
-
elsif
|
759
|
+
elsif cursor_row >= height
|
760
760
|
dialog.vertical_offset = dialog_render_info.pos.y - height
|
761
761
|
else
|
762
762
|
dialog.vertical_offset = dialog_render_info.pos.y + 1
|
@@ -794,7 +794,7 @@ class Reline::LineEditor
|
|
794
794
|
if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: complete)
|
795
795
|
after.lines("\n").map { |l| l.chomp('') }
|
796
796
|
else
|
797
|
-
before
|
797
|
+
before.map { |l| Reline::Unicode.escape_for_print(l) }
|
798
798
|
end
|
799
799
|
end
|
800
800
|
|
@@ -802,8 +802,8 @@ class Reline::LineEditor
|
|
802
802
|
@config.editing_mode
|
803
803
|
end
|
804
804
|
|
805
|
-
private def menu(
|
806
|
-
@menu_info = MenuInfo.new(
|
805
|
+
private def menu(_target, list)
|
806
|
+
@menu_info = MenuInfo.new(list)
|
807
807
|
end
|
808
808
|
|
809
809
|
private def complete_internal_proc(list, is_menu)
|
@@ -831,7 +831,7 @@ class Reline::LineEditor
|
|
831
831
|
item_mbchars = item.grapheme_clusters
|
832
832
|
end
|
833
833
|
size = [memo_mbchars.size, item_mbchars.size].min
|
834
|
-
result = ''
|
834
|
+
result = +''
|
835
835
|
size.times do |i|
|
836
836
|
if @config.completion_ignore_case
|
837
837
|
if memo_mbchars[i].casecmp?(item_mbchars[i])
|
@@ -852,9 +852,9 @@ class Reline::LineEditor
|
|
852
852
|
[target, preposing, completed, postposing]
|
853
853
|
end
|
854
854
|
|
855
|
-
private def complete(list, just_show_list
|
855
|
+
private def complete(list, just_show_list)
|
856
856
|
case @completion_state
|
857
|
-
when CompletionState::NORMAL
|
857
|
+
when CompletionState::NORMAL
|
858
858
|
@completion_state = CompletionState::COMPLETION
|
859
859
|
when CompletionState::PERFECT_MATCH
|
860
860
|
@dig_perfect_match_proc&.(@perfect_matched)
|
@@ -888,52 +888,50 @@ class Reline::LineEditor
|
|
888
888
|
end
|
889
889
|
if not just_show_list and target < completed
|
890
890
|
@buffer_of_lines[@line_index] = (preposing + completed + completion_append_character.to_s + postposing).split("\n")[@line_index] || String.new(encoding: @encoding)
|
891
|
-
line_to_pointer = (preposing + completed + completion_append_character.to_s).split("\n")
|
891
|
+
line_to_pointer = (preposing + completed + completion_append_character.to_s).split("\n")[@line_index] || String.new(encoding: @encoding)
|
892
892
|
@byte_pointer = line_to_pointer.bytesize
|
893
893
|
end
|
894
894
|
end
|
895
895
|
end
|
896
896
|
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
@
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
end
|
917
|
-
end
|
918
|
-
@completion_state = CompletionState::JOURNEY
|
919
|
-
else
|
920
|
-
case direction
|
921
|
-
when :up
|
922
|
-
@completion_journey_data.pointer -= 1
|
923
|
-
if @completion_journey_data.pointer < 0
|
924
|
-
@completion_journey_data.pointer = @completion_journey_data.list.size - 1
|
925
|
-
end
|
926
|
-
when :down
|
927
|
-
@completion_journey_data.pointer += 1
|
928
|
-
if @completion_journey_data.pointer >= @completion_journey_data.list.size
|
929
|
-
@completion_journey_data.pointer = 0
|
930
|
-
end
|
931
|
-
end
|
897
|
+
def dialog_proc_scope_completion_journey_data
|
898
|
+
return nil unless @completion_journey_state
|
899
|
+
line_index = @completion_journey_state.line_index
|
900
|
+
pre_lines = @buffer_of_lines[0...line_index].map { |line| line + "\n" }
|
901
|
+
post_lines = @buffer_of_lines[(line_index + 1)..-1].map { |line| line + "\n" }
|
902
|
+
DialogProcScope::CompletionJourneyData.new(
|
903
|
+
pre_lines.join + @completion_journey_state.pre,
|
904
|
+
@completion_journey_state.post + post_lines.join,
|
905
|
+
@completion_journey_state.list,
|
906
|
+
@completion_journey_state.pointer
|
907
|
+
)
|
908
|
+
end
|
909
|
+
|
910
|
+
private def move_completed_list(direction)
|
911
|
+
@completion_journey_state ||= retrieve_completion_journey_state
|
912
|
+
return false unless @completion_journey_state
|
913
|
+
|
914
|
+
if (delta = { up: -1, down: +1 }[direction])
|
915
|
+
@completion_journey_state.pointer = (@completion_journey_state.pointer + delta) % @completion_journey_state.list.size
|
932
916
|
end
|
933
|
-
completed = @
|
934
|
-
|
935
|
-
|
936
|
-
|
917
|
+
completed = @completion_journey_state.list[@completion_journey_state.pointer]
|
918
|
+
set_current_line(@completion_journey_state.pre + completed + @completion_journey_state.post, @completion_journey_state.pre.bytesize + completed.bytesize)
|
919
|
+
true
|
920
|
+
end
|
921
|
+
|
922
|
+
private def retrieve_completion_journey_state
|
923
|
+
preposing, target, postposing = retrieve_completion_block
|
924
|
+
list = call_completion_proc
|
925
|
+
return unless list.is_a?(Array)
|
926
|
+
|
927
|
+
candidates = list.select{ |item| item.start_with?(target) }
|
928
|
+
return if candidates.empty?
|
929
|
+
|
930
|
+
pre = preposing.split("\n", -1).last || ''
|
931
|
+
post = postposing.split("\n", -1).first || ''
|
932
|
+
CompletionJourneyState.new(
|
933
|
+
@line_index, pre, target, post, [target] + candidates, 0
|
934
|
+
)
|
937
935
|
end
|
938
936
|
|
939
937
|
private def run_for_operators(key, method_symbol, &block)
|
@@ -1122,59 +1120,65 @@ class Reline::LineEditor
|
|
1122
1120
|
@first_char = false
|
1123
1121
|
completion_occurs = false
|
1124
1122
|
if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
|
1125
|
-
|
1126
|
-
|
1127
|
-
if
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1123
|
+
if !@config.disable_completion
|
1124
|
+
process_insert(force: true)
|
1125
|
+
if @config.autocompletion
|
1126
|
+
@completion_state = CompletionState::NORMAL
|
1127
|
+
completion_occurs = move_completed_list(:down)
|
1128
|
+
else
|
1129
|
+
@completion_journey_state = nil
|
1130
|
+
result = call_completion_proc
|
1131
|
+
if result.is_a?(Array)
|
1132
|
+
completion_occurs = true
|
1133
|
+
complete(result, false)
|
1134
1134
|
end
|
1135
1135
|
end
|
1136
1136
|
end
|
1137
1137
|
elsif @config.editing_mode_is?(:emacs, :vi_insert) and key.char == :completion_journey_up
|
1138
1138
|
if not @config.disable_completion and @config.autocompletion
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
process_insert
|
1143
|
-
move_completed_list(result, :up)
|
1144
|
-
end
|
1139
|
+
process_insert(force: true)
|
1140
|
+
@completion_state = CompletionState::NORMAL
|
1141
|
+
completion_occurs = move_completed_list(:up)
|
1145
1142
|
end
|
1146
|
-
elsif
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
|
1153
|
-
end
|
1143
|
+
elsif @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
|
1144
|
+
# In vi mode, move completed list even if autocompletion is off
|
1145
|
+
if not @config.disable_completion
|
1146
|
+
process_insert(force: true)
|
1147
|
+
@completion_state = CompletionState::NORMAL
|
1148
|
+
completion_occurs = move_completed_list("\C-p".ord == key.char ? :up : :down)
|
1154
1149
|
end
|
1155
1150
|
elsif Symbol === key.char and respond_to?(key.char, true)
|
1156
1151
|
process_key(key.char, key.char)
|
1157
1152
|
else
|
1158
1153
|
normal_char(key)
|
1159
1154
|
end
|
1155
|
+
|
1160
1156
|
unless completion_occurs
|
1161
1157
|
@completion_state = CompletionState::NORMAL
|
1162
|
-
@
|
1158
|
+
@completion_journey_state = nil
|
1163
1159
|
end
|
1160
|
+
|
1164
1161
|
if @in_pasting
|
1165
1162
|
clear_dialogs
|
1166
|
-
|
1167
|
-
return old_lines != @buffer_of_lines
|
1163
|
+
return
|
1168
1164
|
end
|
1165
|
+
|
1166
|
+
modified = old_lines != @buffer_of_lines
|
1167
|
+
if !completion_occurs && modified && !@config.disable_completion && @config.autocompletion
|
1168
|
+
# Auto complete starts only when edited
|
1169
|
+
process_insert(force: true)
|
1170
|
+
@completion_journey_state = retrieve_completion_journey_state
|
1171
|
+
end
|
1172
|
+
modified
|
1169
1173
|
end
|
1170
1174
|
|
1171
1175
|
def scroll_into_view
|
1172
|
-
|
1173
|
-
if
|
1174
|
-
@scroll_partial_screen =
|
1176
|
+
_wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
|
1177
|
+
if wrapped_cursor_y < screen_scroll_top
|
1178
|
+
@scroll_partial_screen = wrapped_cursor_y
|
1175
1179
|
end
|
1176
|
-
if
|
1177
|
-
@scroll_partial_screen =
|
1180
|
+
if wrapped_cursor_y >= screen_scroll_top + screen_height
|
1181
|
+
@scroll_partial_screen = wrapped_cursor_y - screen_height + 1
|
1178
1182
|
end
|
1179
1183
|
end
|
1180
1184
|
|
@@ -1217,10 +1221,11 @@ class Reline::LineEditor
|
|
1217
1221
|
new_indent = @auto_indent_proc.(@buffer_of_lines.take(line_index + 1).push(''), line_index, byte_pointer, add_newline)
|
1218
1222
|
return unless new_indent
|
1219
1223
|
|
1220
|
-
|
1224
|
+
new_line = ' ' * new_indent + line.lstrip
|
1225
|
+
@buffer_of_lines[line_index] = new_line
|
1221
1226
|
if @line_index == line_index
|
1222
|
-
|
1223
|
-
@byte_pointer = [@byte_pointer +
|
1227
|
+
indent_diff = new_line.bytesize - line.bytesize
|
1228
|
+
@byte_pointer = [@byte_pointer + indent_diff, 0].max
|
1224
1229
|
end
|
1225
1230
|
end
|
1226
1231
|
|
@@ -1233,7 +1238,6 @@ class Reline::LineEditor
|
|
1233
1238
|
end
|
1234
1239
|
|
1235
1240
|
def set_current_line(line, byte_pointer = nil)
|
1236
|
-
@modified = true
|
1237
1241
|
cursor = current_byte_pointer_cursor
|
1238
1242
|
@buffer_of_lines[@line_index] = line
|
1239
1243
|
if byte_pointer
|
@@ -1993,6 +1997,13 @@ class Reline::LineEditor
|
|
1993
1997
|
end
|
1994
1998
|
alias_method :kill_line, :ed_kill_line
|
1995
1999
|
|
2000
|
+
# Editline:: +vi_change_to_eol+ (vi command: +C+) + Kill and change from the cursor to the end of the line.
|
2001
|
+
private def vi_change_to_eol(key)
|
2002
|
+
ed_kill_line(key)
|
2003
|
+
|
2004
|
+
@config.editing_mode = :vi_insert
|
2005
|
+
end
|
2006
|
+
|
1996
2007
|
# Editline:: +vi-kill-line-prev+ (vi: +Ctrl-U+) Delete the string from the
|
1997
2008
|
# beginning of the edit buffer to the cursor and save it to the
|
1998
2009
|
# cut buffer.
|
@@ -2037,7 +2048,7 @@ class Reline::LineEditor
|
|
2037
2048
|
private def em_delete_or_list(key)
|
2038
2049
|
if current_line.empty? or @byte_pointer < current_line.bytesize
|
2039
2050
|
em_delete(key)
|
2040
|
-
|
2051
|
+
elsif !@config.autocompletion # show completed list
|
2041
2052
|
result = call_completion_proc
|
2042
2053
|
if result.is_a?(Array)
|
2043
2054
|
complete(result, true)
|
@@ -2063,7 +2074,11 @@ class Reline::LineEditor
|
|
2063
2074
|
alias_method :yank_pop, :em_yank_pop
|
2064
2075
|
|
2065
2076
|
private def ed_clear_screen(key)
|
2066
|
-
|
2077
|
+
Reline::IOGate.clear_screen
|
2078
|
+
@screen_size = Reline::IOGate.get_screen_size
|
2079
|
+
@rendered_screen.lines = []
|
2080
|
+
@rendered_screen.base_y = 0
|
2081
|
+
@rendered_screen.cursor_y = 0
|
2067
2082
|
end
|
2068
2083
|
alias_method :clear_screen, :ed_clear_screen
|
2069
2084
|
|
@@ -2291,7 +2306,7 @@ class Reline::LineEditor
|
|
2291
2306
|
end
|
2292
2307
|
|
2293
2308
|
private def ed_delete_prev_char(key, arg: 1)
|
2294
|
-
deleted = ''
|
2309
|
+
deleted = +''
|
2295
2310
|
arg.times do
|
2296
2311
|
if @byte_pointer > 0
|
2297
2312
|
byte_size = Reline::Unicode.get_prev_mbchar_size(current_line, @byte_pointer)
|
data/lib/reline/terminfo.rb
CHANGED
@@ -80,23 +80,11 @@ module Reline::Terminfo
|
|
80
80
|
def self.setupterm(term, fildes)
|
81
81
|
errret_int = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT)
|
82
82
|
ret = @setupterm.(term, fildes, errret_int)
|
83
|
-
errret = errret_int[0, Fiddle::SIZEOF_INT].unpack1('i')
|
84
83
|
case ret
|
85
84
|
when 0 # OK
|
86
|
-
|
85
|
+
@term_supported = true
|
87
86
|
when -1 # ERR
|
88
|
-
|
89
|
-
when 1
|
90
|
-
raise TerminfoError.new('The terminal is hardcopy, cannot be used for curses applications.')
|
91
|
-
when 0
|
92
|
-
raise TerminfoError.new('The terminal could not be found, or that it is a generic type, having too little information for curses applications to run.')
|
93
|
-
when -1
|
94
|
-
raise TerminfoError.new('The terminfo database could not be found.')
|
95
|
-
else # unknown
|
96
|
-
-1
|
97
|
-
end
|
98
|
-
else # unknown
|
99
|
-
-2
|
87
|
+
@term_supported = false
|
100
88
|
end
|
101
89
|
end
|
102
90
|
|
@@ -148,9 +136,14 @@ module Reline::Terminfo
|
|
148
136
|
num
|
149
137
|
end
|
150
138
|
|
139
|
+
# NOTE: This means Fiddle and curses are enabled.
|
151
140
|
def self.enabled?
|
152
141
|
true
|
153
142
|
end
|
143
|
+
|
144
|
+
def self.term_supported?
|
145
|
+
@term_supported
|
146
|
+
end
|
154
147
|
end if Reline::Terminfo.curses_dl
|
155
148
|
|
156
149
|
module Reline::Terminfo
|
data/lib/reline/version.rb
CHANGED
data/lib/reline/windows.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'fiddle/import'
|
2
2
|
|
3
3
|
class Reline::Windows
|
4
|
+
RESET_COLOR = "\e[0m"
|
5
|
+
|
4
6
|
def self.encoding
|
5
7
|
Encoding::UTF_8
|
6
8
|
end
|
@@ -85,7 +87,7 @@ class Reline::Windows
|
|
85
87
|
def call(*args)
|
86
88
|
import = @proto.split("")
|
87
89
|
args.each_with_index do |x, i|
|
88
|
-
args[i], = [x == 0 ? nil : x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
|
90
|
+
args[i], = [x == 0 ? nil : +x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
|
89
91
|
args[i], = [x].pack("I").unpack("i") if import[i] == "I"
|
90
92
|
end
|
91
93
|
ret, = @func.call(*args)
|
@@ -257,7 +259,7 @@ class Reline::Windows
|
|
257
259
|
def self.check_input_event
|
258
260
|
num_of_events = 0.chr * 8
|
259
261
|
while @@output_buf.empty?
|
260
|
-
Reline.core.line_editor.
|
262
|
+
Reline.core.line_editor.handle_signal
|
261
263
|
if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
|
262
264
|
# prevent for background consolemode change
|
263
265
|
@@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
|
data/lib/reline.rb
CHANGED
@@ -75,10 +75,10 @@ module Reline
|
|
75
75
|
|
76
76
|
def initialize
|
77
77
|
self.output = STDOUT
|
78
|
+
@mutex = Mutex.new
|
78
79
|
@dialog_proc_list = {}
|
79
80
|
yield self
|
80
81
|
@completion_quote_character = nil
|
81
|
-
@bracketed_paste_finished = false
|
82
82
|
end
|
83
83
|
|
84
84
|
def io_gate
|
@@ -220,26 +220,16 @@ module Reline
|
|
220
220
|
|
221
221
|
Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE = ->() {
|
222
222
|
# autocomplete
|
223
|
-
return
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
result = completion_journey_data.list.dup
|
234
|
-
result.shift
|
235
|
-
pointer = completion_journey_data.pointer - 1
|
236
|
-
else
|
237
|
-
result = call_completion_proc_with_checking_args(pre, target, post)
|
238
|
-
pointer = nil
|
239
|
-
end
|
240
|
-
if result and result.size == 1 and result[0] == target and pointer != 0
|
241
|
-
result = nil
|
242
|
-
end
|
223
|
+
return unless config.autocompletion
|
224
|
+
|
225
|
+
journey_data = completion_journey_data
|
226
|
+
return unless journey_data
|
227
|
+
|
228
|
+
target = journey_data.list[journey_data.pointer]
|
229
|
+
result = journey_data.list.drop(1)
|
230
|
+
pointer = journey_data.pointer - 1
|
231
|
+
return if target.empty? || (result == [target] && pointer < 0)
|
232
|
+
|
243
233
|
target_width = Reline::Unicode.calculate_width(target)
|
244
234
|
x = cursor_pos.x - target_width
|
245
235
|
if x < 0
|
@@ -265,12 +255,15 @@ module Reline
|
|
265
255
|
Reline::DEFAULT_DIALOG_CONTEXT = Array.new
|
266
256
|
|
267
257
|
def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
|
268
|
-
|
269
|
-
io_gate.with_raw_input do
|
258
|
+
@mutex.synchronize do
|
270
259
|
unless confirm_multiline_termination
|
271
260
|
raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
|
272
261
|
end
|
273
|
-
|
262
|
+
|
263
|
+
Reline.update_iogate
|
264
|
+
io_gate.with_raw_input do
|
265
|
+
inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
|
266
|
+
end
|
274
267
|
|
275
268
|
whole_buffer = line_editor.whole_buffer.dup
|
276
269
|
whole_buffer.taint if RUBY_VERSION < '2.7'
|
@@ -280,6 +273,7 @@ module Reline
|
|
280
273
|
|
281
274
|
if line_editor.eof?
|
282
275
|
line_editor.reset_line
|
276
|
+
# Return nil if the input is aborted by C-d.
|
283
277
|
nil
|
284
278
|
else
|
285
279
|
whole_buffer
|
@@ -288,17 +282,21 @@ module Reline
|
|
288
282
|
end
|
289
283
|
|
290
284
|
def readline(prompt = '', add_hist = false)
|
291
|
-
|
292
|
-
|
285
|
+
@mutex.synchronize do
|
286
|
+
Reline.update_iogate
|
287
|
+
io_gate.with_raw_input do
|
288
|
+
inner_readline(prompt, add_hist, false)
|
289
|
+
end
|
293
290
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
291
|
+
line = line_editor.line.dup
|
292
|
+
line.taint if RUBY_VERSION < '2.7'
|
293
|
+
if add_hist and line and line.chomp("\n").size > 0
|
294
|
+
Reline::HISTORY << line.chomp("\n")
|
295
|
+
end
|
299
296
|
|
300
|
-
|
301
|
-
|
297
|
+
line_editor.reset_line if line_editor.line.nil?
|
298
|
+
line
|
299
|
+
end
|
302
300
|
end
|
303
301
|
|
304
302
|
private def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
|
@@ -331,8 +329,10 @@ module Reline
|
|
331
329
|
line_editor.auto_indent_proc = auto_indent_proc
|
332
330
|
line_editor.dig_perfect_match_proc = dig_perfect_match_proc
|
333
331
|
pre_input_hook&.call
|
334
|
-
|
335
|
-
|
332
|
+
unless Reline::IOGate == Reline::GeneralIO
|
333
|
+
@dialog_proc_list.each_pair do |name_sym, d|
|
334
|
+
line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
|
335
|
+
end
|
336
336
|
end
|
337
337
|
|
338
338
|
unless config.test_mode
|
@@ -342,45 +342,31 @@ module Reline
|
|
342
342
|
end
|
343
343
|
|
344
344
|
line_editor.print_nomultiline_prompt(prompt)
|
345
|
+
line_editor.update_dialogs
|
345
346
|
line_editor.rerender
|
346
347
|
|
347
348
|
begin
|
348
349
|
line_editor.set_signal_handlers
|
349
|
-
prev_pasting_state = false
|
350
350
|
loop do
|
351
|
-
prev_pasting_state = io_gate.in_pasting?
|
352
351
|
read_io(config.keyseq_timeout) { |inputs|
|
353
352
|
line_editor.set_pasting_state(io_gate.in_pasting?)
|
354
|
-
inputs.each { |
|
355
|
-
line_editor.rerender
|
356
|
-
if @bracketed_paste_finished
|
357
|
-
line_editor.rerender_all
|
358
|
-
@bracketed_paste_finished = false
|
359
|
-
end
|
353
|
+
inputs.each { |key| line_editor.update(key) }
|
360
354
|
}
|
361
|
-
if
|
362
|
-
line_editor.
|
363
|
-
|
364
|
-
|
355
|
+
if line_editor.finished?
|
356
|
+
line_editor.render_finished
|
357
|
+
break
|
358
|
+
else
|
359
|
+
line_editor.set_pasting_state(io_gate.in_pasting?)
|
360
|
+
line_editor.rerender
|
365
361
|
end
|
366
|
-
break if line_editor.finished?
|
367
362
|
end
|
368
363
|
io_gate.move_cursor_column(0)
|
369
364
|
rescue Errno::EIO
|
370
365
|
# Maybe the I/O has been closed.
|
371
|
-
|
366
|
+
ensure
|
372
367
|
line_editor.finalize
|
373
368
|
io_gate.deprep(otio)
|
374
|
-
raise e
|
375
|
-
rescue Exception
|
376
|
-
# Including Interrupt
|
377
|
-
line_editor.finalize
|
378
|
-
io_gate.deprep(otio)
|
379
|
-
raise
|
380
369
|
end
|
381
|
-
|
382
|
-
line_editor.finalize
|
383
|
-
io_gate.deprep(otio)
|
384
370
|
end
|
385
371
|
|
386
372
|
# GNU Readline waits for "keyseq-timeout" milliseconds to see if the ESC
|
@@ -398,7 +384,6 @@ module Reline
|
|
398
384
|
c = io_gate.getc(Float::INFINITY)
|
399
385
|
if c == -1
|
400
386
|
result = :unmatched
|
401
|
-
@bracketed_paste_finished = true
|
402
387
|
else
|
403
388
|
buffer << c
|
404
389
|
result = key_stroke.match_status(buffer)
|
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.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aycabta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-04-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: io-console
|
@@ -57,7 +57,10 @@ files:
|
|
57
57
|
homepage: https://github.com/ruby/reline
|
58
58
|
licenses:
|
59
59
|
- Ruby
|
60
|
-
metadata:
|
60
|
+
metadata:
|
61
|
+
bug_tracker_uri: https://github.com/ruby/reline/issues
|
62
|
+
changelog_uri: https://github.com/ruby/reline/releases
|
63
|
+
source_code_uri: https://github.com/ruby/reline
|
61
64
|
post_install_message:
|
62
65
|
rdoc_options: []
|
63
66
|
require_paths:
|
@@ -69,11 +72,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
72
|
version: '2.6'
|
70
73
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
74
|
requirements:
|
72
|
-
- - "
|
75
|
+
- - ">="
|
73
76
|
- !ruby/object:Gem::Version
|
74
|
-
version:
|
77
|
+
version: '0'
|
75
78
|
requirements: []
|
76
|
-
rubygems_version: 3.
|
79
|
+
rubygems_version: 3.5.3
|
77
80
|
signing_key:
|
78
81
|
specification_version: 4
|
79
82
|
summary: Alternative GNU Readline or Editline implementation by pure Ruby.
|