reline 0.5.0.pre.1 → 0.5.1
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/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.
|