reline 0.3.9 → 0.5.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +8 -1
- data/lib/reline/ansi.rb +38 -39
- data/lib/reline/config.rb +35 -56
- data/lib/reline/face.rb +199 -0
- data/lib/reline/general_io.rb +4 -9
- data/lib/reline/history.rb +1 -1
- data/lib/reline/key_actor/base.rb +0 -4
- data/lib/reline/key_actor/emacs.rb +13 -13
- data/lib/reline/key_actor/vi_command.rb +23 -23
- data/lib/reline/key_actor/vi_insert.rb +5 -5
- data/lib/reline/kill_ring.rb +2 -2
- data/lib/reline/line_editor.rb +1038 -1784
- data/lib/reline/terminfo.rb +7 -14
- data/lib/reline/unicode.rb +66 -12
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +4 -2
- data/lib/reline.rb +75 -80
- metadata +8 -4
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/unicode.rb
CHANGED
@@ -43,11 +43,13 @@ class Reline::Unicode
|
|
43
43
|
|
44
44
|
def self.escape_for_print(str)
|
45
45
|
str.chars.map! { |gr|
|
46
|
-
|
47
|
-
|
48
|
-
escaped
|
49
|
-
else
|
46
|
+
case gr
|
47
|
+
when -"\n"
|
50
48
|
gr
|
49
|
+
when -"\t"
|
50
|
+
-' '
|
51
|
+
else
|
52
|
+
EscapedPairs[gr.ord] || gr
|
51
53
|
end
|
52
54
|
}.join
|
53
55
|
end
|
@@ -128,10 +130,10 @@ class Reline::Unicode
|
|
128
130
|
end
|
129
131
|
end
|
130
132
|
|
131
|
-
def self.split_by_width(str, max_width, encoding = str.encoding)
|
133
|
+
def self.split_by_width(str, max_width, encoding = str.encoding, offset: 0)
|
132
134
|
lines = [String.new(encoding: encoding)]
|
133
135
|
height = 1
|
134
|
-
width =
|
136
|
+
width = offset
|
135
137
|
rest = str.encode(Encoding::UTF_8)
|
136
138
|
in_zero_width = false
|
137
139
|
seq = String.new(encoding: encoding)
|
@@ -145,7 +147,13 @@ class Reline::Unicode
|
|
145
147
|
lines.last << NON_PRINTING_END
|
146
148
|
when csi
|
147
149
|
lines.last << csi
|
148
|
-
|
150
|
+
unless in_zero_width
|
151
|
+
if csi == -"\e[m" || csi == -"\e[0m"
|
152
|
+
seq.clear
|
153
|
+
else
|
154
|
+
seq << csi
|
155
|
+
end
|
156
|
+
end
|
149
157
|
when osc
|
150
158
|
lines.last << osc
|
151
159
|
seq << osc
|
@@ -173,32 +181,78 @@ class Reline::Unicode
|
|
173
181
|
|
174
182
|
# Take a chunk of a String cut by width with escape sequences.
|
175
183
|
def self.take_range(str, start_col, max_width)
|
184
|
+
take_mbchar_range(str, start_col, max_width).first
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.take_mbchar_range(str, start_col, width, cover_begin: false, cover_end: false, padding: false)
|
176
188
|
chunk = String.new(encoding: str.encoding)
|
189
|
+
|
190
|
+
end_col = start_col + width
|
177
191
|
total_width = 0
|
178
192
|
rest = str.encode(Encoding::UTF_8)
|
179
193
|
in_zero_width = false
|
194
|
+
chunk_start_col = nil
|
195
|
+
chunk_end_col = nil
|
196
|
+
has_csi = false
|
180
197
|
rest.scan(WIDTH_SCANNER) do |non_printing_start, non_printing_end, csi, osc, gc|
|
181
198
|
case
|
182
199
|
when non_printing_start
|
183
200
|
in_zero_width = true
|
201
|
+
chunk << NON_PRINTING_START
|
184
202
|
when non_printing_end
|
185
203
|
in_zero_width = false
|
204
|
+
chunk << NON_PRINTING_END
|
186
205
|
when csi
|
206
|
+
has_csi = true
|
187
207
|
chunk << csi
|
188
208
|
when osc
|
189
209
|
chunk << osc
|
190
210
|
when gc
|
191
211
|
if in_zero_width
|
192
212
|
chunk << gc
|
213
|
+
next
|
214
|
+
end
|
215
|
+
|
216
|
+
mbchar_width = get_mbchar_width(gc)
|
217
|
+
prev_width = total_width
|
218
|
+
total_width += mbchar_width
|
219
|
+
|
220
|
+
if (cover_begin || padding ? total_width <= start_col : prev_width < start_col)
|
221
|
+
# Current character haven't reached start_col yet
|
222
|
+
next
|
223
|
+
elsif padding && !cover_begin && prev_width < start_col && start_col < total_width
|
224
|
+
# Add preceding padding. This padding might have background color.
|
225
|
+
chunk << ' '
|
226
|
+
chunk_start_col ||= start_col
|
227
|
+
chunk_end_col = total_width
|
228
|
+
next
|
229
|
+
elsif (cover_end ? prev_width < end_col : total_width <= end_col)
|
230
|
+
# Current character is in the range
|
231
|
+
chunk << gc
|
232
|
+
chunk_start_col ||= prev_width
|
233
|
+
chunk_end_col = total_width
|
234
|
+
break if total_width >= end_col
|
193
235
|
else
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
236
|
+
# Current character exceeds end_col
|
237
|
+
if padding && end_col < total_width
|
238
|
+
# Add succeeding padding. This padding might have background color.
|
239
|
+
chunk << ' '
|
240
|
+
chunk_start_col ||= prev_width
|
241
|
+
chunk_end_col = end_col
|
242
|
+
end
|
243
|
+
break
|
198
244
|
end
|
199
245
|
end
|
200
246
|
end
|
201
|
-
|
247
|
+
chunk_start_col ||= start_col
|
248
|
+
chunk_end_col ||= start_col
|
249
|
+
if padding && chunk_end_col < end_col
|
250
|
+
# Append padding. This padding should not include background color.
|
251
|
+
chunk << "\e[0m" if has_csi
|
252
|
+
chunk << ' ' * (end_col - chunk_end_col)
|
253
|
+
chunk_end_col = end_col
|
254
|
+
end
|
255
|
+
[chunk, chunk_start_col, chunk_end_col - chunk_start_col]
|
202
256
|
end
|
203
257
|
|
204
258
|
def self.get_next_mbchar_size(line, byte_pointer)
|
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
@@ -7,6 +7,7 @@ require 'reline/key_stroke'
|
|
7
7
|
require 'reline/line_editor'
|
8
8
|
require 'reline/history'
|
9
9
|
require 'reline/terminfo'
|
10
|
+
require 'reline/face'
|
10
11
|
require 'rbconfig'
|
11
12
|
|
12
13
|
module Reline
|
@@ -36,10 +37,8 @@ module Reline
|
|
36
37
|
DialogRenderInfo = Struct.new(
|
37
38
|
:pos,
|
38
39
|
:contents,
|
39
|
-
:
|
40
|
-
:
|
41
|
-
:fg_color,
|
42
|
-
:pointer_fg_color,
|
40
|
+
:face,
|
41
|
+
:bg_color, # For the time being, this line should stay here for the compatibility with IRB.
|
43
42
|
:width,
|
44
43
|
:height,
|
45
44
|
:scrollbar,
|
@@ -76,10 +75,10 @@ module Reline
|
|
76
75
|
|
77
76
|
def initialize
|
78
77
|
self.output = STDOUT
|
78
|
+
@mutex = Mutex.new
|
79
79
|
@dialog_proc_list = {}
|
80
80
|
yield self
|
81
81
|
@completion_quote_character = nil
|
82
|
-
@bracketed_paste_finished = false
|
83
82
|
end
|
84
83
|
|
85
84
|
def io_gate
|
@@ -221,32 +220,25 @@ module Reline
|
|
221
220
|
|
222
221
|
Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE = ->() {
|
223
222
|
# autocomplete
|
224
|
-
return
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
if
|
234
|
-
|
235
|
-
result.shift
|
236
|
-
pointer = completion_journey_data.pointer - 1
|
237
|
-
else
|
238
|
-
result = call_completion_proc_with_checking_args(pre, target, post)
|
239
|
-
pointer = nil
|
240
|
-
end
|
241
|
-
if result and result.size == 1 and result[0] == target and pointer != 0
|
242
|
-
result = nil
|
243
|
-
end
|
223
|
+
return unless config.autocompletion
|
224
|
+
|
225
|
+
journey_data = completion_journey_data
|
226
|
+
return unless journey_data
|
227
|
+
|
228
|
+
target = journey_data.list.first
|
229
|
+
completed = journey_data.list[journey_data.pointer]
|
230
|
+
result = journey_data.list.drop(1)
|
231
|
+
pointer = journey_data.pointer - 1
|
232
|
+
return if completed.empty? || (result == [completed] && pointer < 0)
|
233
|
+
|
244
234
|
target_width = Reline::Unicode.calculate_width(target)
|
245
|
-
|
246
|
-
if x
|
247
|
-
|
235
|
+
completed_width = Reline::Unicode.calculate_width(completed)
|
236
|
+
if cursor_pos.x <= completed_width - target_width
|
237
|
+
# When target is rendered on the line above cursor position
|
238
|
+
x = screen_width - completed_width
|
248
239
|
y = -1
|
249
240
|
else
|
241
|
+
x = [cursor_pos.x - completed_width, 0].max
|
250
242
|
y = 0
|
251
243
|
end
|
252
244
|
cursor_pos_to_render = Reline::CursorPos.new(x, y)
|
@@ -260,21 +252,21 @@ module Reline
|
|
260
252
|
contents: result,
|
261
253
|
scrollbar: true,
|
262
254
|
height: [15, preferred_dialog_height].min,
|
263
|
-
|
264
|
-
pointer_bg_color: 45,
|
265
|
-
fg_color: 37,
|
266
|
-
pointer_fg_color: 37
|
255
|
+
face: :completion_dialog
|
267
256
|
)
|
268
257
|
}
|
269
258
|
Reline::DEFAULT_DIALOG_CONTEXT = Array.new
|
270
259
|
|
271
260
|
def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
|
272
|
-
|
273
|
-
io_gate.with_raw_input do
|
261
|
+
@mutex.synchronize do
|
274
262
|
unless confirm_multiline_termination
|
275
263
|
raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
|
276
264
|
end
|
277
|
-
|
265
|
+
|
266
|
+
Reline.update_iogate
|
267
|
+
io_gate.with_raw_input do
|
268
|
+
inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
|
269
|
+
end
|
278
270
|
|
279
271
|
whole_buffer = line_editor.whole_buffer.dup
|
280
272
|
whole_buffer.taint if RUBY_VERSION < '2.7'
|
@@ -282,23 +274,32 @@ module Reline
|
|
282
274
|
Reline::HISTORY << whole_buffer
|
283
275
|
end
|
284
276
|
|
285
|
-
|
286
|
-
|
277
|
+
if line_editor.eof?
|
278
|
+
line_editor.reset_line
|
279
|
+
# Return nil if the input is aborted by C-d.
|
280
|
+
nil
|
281
|
+
else
|
282
|
+
whole_buffer
|
283
|
+
end
|
287
284
|
end
|
288
285
|
end
|
289
286
|
|
290
287
|
def readline(prompt = '', add_hist = false)
|
291
|
-
|
292
|
-
|
288
|
+
@mutex.synchronize do
|
289
|
+
Reline.update_iogate
|
290
|
+
io_gate.with_raw_input do
|
291
|
+
inner_readline(prompt, add_hist, false)
|
292
|
+
end
|
293
293
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
294
|
+
line = line_editor.line.dup
|
295
|
+
line.taint if RUBY_VERSION < '2.7'
|
296
|
+
if add_hist and line and line.chomp("\n").size > 0
|
297
|
+
Reline::HISTORY << line.chomp("\n")
|
298
|
+
end
|
299
299
|
|
300
|
-
|
301
|
-
|
300
|
+
line_editor.reset_line if line_editor.line.nil?
|
301
|
+
line
|
302
|
+
end
|
302
303
|
end
|
303
304
|
|
304
305
|
private def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
|
@@ -311,6 +312,10 @@ module Reline
|
|
311
312
|
$stderr.sync = true
|
312
313
|
$stderr.puts "Reline is used by #{Process.pid}"
|
313
314
|
end
|
315
|
+
unless config.test_mode or config.loaded?
|
316
|
+
config.read
|
317
|
+
io_gate.set_default_key_bindings(config)
|
318
|
+
end
|
314
319
|
otio = io_gate.prep
|
315
320
|
|
316
321
|
may_req_ambiguous_char_width
|
@@ -330,58 +335,47 @@ module Reline
|
|
330
335
|
line_editor.prompt_proc = prompt_proc
|
331
336
|
line_editor.auto_indent_proc = auto_indent_proc
|
332
337
|
line_editor.dig_perfect_match_proc = dig_perfect_match_proc
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
unless config.test_mode
|
339
|
-
config.read
|
340
|
-
config.reset_default_key_bindings
|
341
|
-
io_gate.set_default_key_bindings(config)
|
338
|
+
pre_input_hook&.call
|
339
|
+
unless Reline::IOGate == Reline::GeneralIO
|
340
|
+
@dialog_proc_list.each_pair do |name_sym, d|
|
341
|
+
line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
|
342
|
+
end
|
342
343
|
end
|
343
344
|
|
345
|
+
line_editor.print_nomultiline_prompt(prompt)
|
346
|
+
line_editor.update_dialogs
|
344
347
|
line_editor.rerender
|
345
348
|
|
346
349
|
begin
|
347
350
|
line_editor.set_signal_handlers
|
348
|
-
prev_pasting_state = false
|
349
351
|
loop do
|
350
|
-
prev_pasting_state = io_gate.in_pasting?
|
351
352
|
read_io(config.keyseq_timeout) { |inputs|
|
352
353
|
line_editor.set_pasting_state(io_gate.in_pasting?)
|
353
|
-
inputs.each
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
354
|
+
inputs.each do |key|
|
355
|
+
if key.char == :bracketed_paste_start
|
356
|
+
text = io_gate.read_bracketed_paste
|
357
|
+
line_editor.insert_pasted_text(text)
|
358
|
+
line_editor.scroll_into_view
|
359
|
+
else
|
360
|
+
line_editor.update(key)
|
361
|
+
end
|
360
362
|
end
|
361
363
|
}
|
362
|
-
if
|
363
|
-
line_editor.
|
364
|
-
|
365
|
-
|
364
|
+
if line_editor.finished?
|
365
|
+
line_editor.render_finished
|
366
|
+
break
|
367
|
+
else
|
368
|
+
line_editor.set_pasting_state(io_gate.in_pasting?)
|
369
|
+
line_editor.rerender
|
366
370
|
end
|
367
|
-
break if line_editor.finished?
|
368
371
|
end
|
369
372
|
io_gate.move_cursor_column(0)
|
370
373
|
rescue Errno::EIO
|
371
374
|
# Maybe the I/O has been closed.
|
372
|
-
|
375
|
+
ensure
|
373
376
|
line_editor.finalize
|
374
377
|
io_gate.deprep(otio)
|
375
|
-
raise e
|
376
|
-
rescue Exception
|
377
|
-
# Including Interrupt
|
378
|
-
line_editor.finalize
|
379
|
-
io_gate.deprep(otio)
|
380
|
-
raise
|
381
378
|
end
|
382
|
-
|
383
|
-
line_editor.finalize
|
384
|
-
io_gate.deprep(otio)
|
385
379
|
end
|
386
380
|
|
387
381
|
# GNU Readline waits for "keyseq-timeout" milliseconds to see if the ESC
|
@@ -399,7 +393,6 @@ module Reline
|
|
399
393
|
c = io_gate.getc(Float::INFINITY)
|
400
394
|
if c == -1
|
401
395
|
result = :unmatched
|
402
|
-
@bracketed_paste_finished = true
|
403
396
|
else
|
404
397
|
buffer << c
|
405
398
|
result = key_stroke.match_status(buffer)
|
@@ -606,4 +599,6 @@ else
|
|
606
599
|
io
|
607
600
|
end
|
608
601
|
|
602
|
+
Reline::Face.load_initial_configs
|
603
|
+
|
609
604
|
Reline::HISTORY = Reline::History.new(Reline.core.config)
|
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.
|
4
|
+
version: 0.5.8
|
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-05-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: io-console
|
@@ -37,6 +37,7 @@ files:
|
|
37
37
|
- lib/reline.rb
|
38
38
|
- lib/reline/ansi.rb
|
39
39
|
- lib/reline/config.rb
|
40
|
+
- lib/reline/face.rb
|
40
41
|
- lib/reline/general_io.rb
|
41
42
|
- lib/reline/history.rb
|
42
43
|
- lib/reline/key_actor.rb
|
@@ -56,7 +57,10 @@ files:
|
|
56
57
|
homepage: https://github.com/ruby/reline
|
57
58
|
licenses:
|
58
59
|
- Ruby
|
59
|
-
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
|
60
64
|
post_install_message:
|
61
65
|
rdoc_options: []
|
62
66
|
require_paths:
|
@@ -72,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
76
|
- !ruby/object:Gem::Version
|
73
77
|
version: '0'
|
74
78
|
requirements: []
|
75
|
-
rubygems_version: 3.
|
79
|
+
rubygems_version: 3.5.9
|
76
80
|
signing_key:
|
77
81
|
specification_version: 4
|
78
82
|
summary: Alternative GNU Readline or Editline implementation by pure Ruby.
|