reline 0.3.9 → 0.5.8
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/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.
|