reline 0.2.5 → 0.3.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/README.md +46 -0
- data/lib/reline/ansi.rb +153 -62
- data/lib/reline/config.rb +62 -14
- data/lib/reline/general_io.rb +14 -4
- data/lib/reline/key_actor/base.rb +12 -0
- data/lib/reline/key_actor/emacs.rb +1 -1
- data/lib/reline/key_stroke.rb +64 -14
- data/lib/reline/line_editor.rb +638 -74
- data/lib/reline/terminfo.rb +171 -0
- data/lib/reline/unicode.rb +42 -3
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +281 -112
- data/lib/reline.rb +150 -35
- metadata +4 -59
data/lib/reline.rb
CHANGED
@@ -7,14 +7,33 @@ require 'reline/key_actor'
|
|
7
7
|
require 'reline/key_stroke'
|
8
8
|
require 'reline/line_editor'
|
9
9
|
require 'reline/history'
|
10
|
+
require 'reline/terminfo'
|
10
11
|
require 'rbconfig'
|
11
12
|
|
12
13
|
module Reline
|
13
14
|
FILENAME_COMPLETION_PROC = nil
|
14
15
|
USERNAME_COMPLETION_PROC = nil
|
15
16
|
|
16
|
-
|
17
|
+
class ConfigEncodingConversionError < StandardError; end
|
18
|
+
|
19
|
+
Key = Struct.new('Key', :char, :combined_char, :with_meta) do
|
20
|
+
def match?(other)
|
21
|
+
case other
|
22
|
+
when Reline::Key
|
23
|
+
(other.char.nil? or char.nil? or char == other.char) and
|
24
|
+
(other.combined_char.nil? or combined_char.nil? or combined_char == other.combined_char) and
|
25
|
+
(other.with_meta.nil? or with_meta.nil? or with_meta == other.with_meta)
|
26
|
+
when Integer, Symbol
|
27
|
+
(combined_char and combined_char == other) or
|
28
|
+
(combined_char.nil? and char and char == other)
|
29
|
+
else
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias_method :==, :match?
|
34
|
+
end
|
17
35
|
CursorPos = Struct.new(:x, :y)
|
36
|
+
DialogRenderInfo = Struct.new(:pos, :contents, :bg_color, :width, :height, :scrollbar, keyword_init: true)
|
18
37
|
|
19
38
|
class Core
|
20
39
|
ATTR_READER_NAMES = %i(
|
@@ -41,6 +60,7 @@ module Reline
|
|
41
60
|
|
42
61
|
def initialize
|
43
62
|
self.output = STDOUT
|
63
|
+
@dialog_proc_list = {}
|
44
64
|
yield self
|
45
65
|
@completion_quote_character = nil
|
46
66
|
@bracketed_paste_finished = false
|
@@ -103,6 +123,14 @@ module Reline
|
|
103
123
|
@completion_proc = p
|
104
124
|
end
|
105
125
|
|
126
|
+
def autocompletion
|
127
|
+
@config.autocompletion
|
128
|
+
end
|
129
|
+
|
130
|
+
def autocompletion=(val)
|
131
|
+
@config.autocompletion = val
|
132
|
+
end
|
133
|
+
|
106
134
|
def output_modifier_proc=(p)
|
107
135
|
raise ArgumentError unless p.respond_to?(:call) or p.nil?
|
108
136
|
@output_modifier_proc = p
|
@@ -127,6 +155,17 @@ module Reline
|
|
127
155
|
@dig_perfect_match_proc = p
|
128
156
|
end
|
129
157
|
|
158
|
+
DialogProc = Struct.new(:dialog_proc, :context)
|
159
|
+
def add_dialog_proc(name_sym, p, context = nil)
|
160
|
+
raise ArgumentError unless p.respond_to?(:call) or p.nil?
|
161
|
+
raise ArgumentError unless name_sym.instance_of?(Symbol)
|
162
|
+
@dialog_proc_list[name_sym] = DialogProc.new(p, context)
|
163
|
+
end
|
164
|
+
|
165
|
+
def dialog_proc(name_sym)
|
166
|
+
@dialog_proc_list[name_sym]
|
167
|
+
end
|
168
|
+
|
130
169
|
def input=(val)
|
131
170
|
raise TypeError unless val.respond_to?(:getc) or val.nil?
|
132
171
|
if val.respond_to?(:getc)
|
@@ -168,6 +207,46 @@ module Reline
|
|
168
207
|
Reline::IOGate.get_screen_size
|
169
208
|
end
|
170
209
|
|
210
|
+
Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE = ->() {
|
211
|
+
# autocomplete
|
212
|
+
return nil unless config.autocompletion
|
213
|
+
if just_cursor_moving and completion_journey_data.nil?
|
214
|
+
# Auto complete starts only when edited
|
215
|
+
return nil
|
216
|
+
end
|
217
|
+
pre, target, post = retrieve_completion_block(true)
|
218
|
+
if target.nil? or target.empty? or (completion_journey_data&.pointer == -1 and target.size <= 3)
|
219
|
+
return nil
|
220
|
+
end
|
221
|
+
if completion_journey_data and completion_journey_data.list
|
222
|
+
result = completion_journey_data.list.dup
|
223
|
+
result.shift
|
224
|
+
pointer = completion_journey_data.pointer - 1
|
225
|
+
else
|
226
|
+
result = call_completion_proc_with_checking_args(pre, target, post)
|
227
|
+
pointer = nil
|
228
|
+
end
|
229
|
+
if result and result.size == 1 and result[0] == target and pointer != 0
|
230
|
+
result = nil
|
231
|
+
end
|
232
|
+
target_width = Reline::Unicode.calculate_width(target)
|
233
|
+
x = cursor_pos.x - target_width
|
234
|
+
if x < 0
|
235
|
+
x = screen_width + x
|
236
|
+
y = -1
|
237
|
+
else
|
238
|
+
y = 0
|
239
|
+
end
|
240
|
+
cursor_pos_to_render = Reline::CursorPos.new(x, y)
|
241
|
+
if context and context.is_a?(Array)
|
242
|
+
context.clear
|
243
|
+
context.push(cursor_pos_to_render, result, pointer, dialog)
|
244
|
+
end
|
245
|
+
dialog.pointer = pointer
|
246
|
+
DialogRenderInfo.new(pos: cursor_pos_to_render, contents: result, scrollbar: true, height: 15)
|
247
|
+
}
|
248
|
+
Reline::DEFAULT_DIALOG_CONTEXT = Array.new
|
249
|
+
|
171
250
|
def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
|
172
251
|
unless confirm_multiline_termination
|
173
252
|
raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
|
@@ -227,18 +306,20 @@ module Reline
|
|
227
306
|
line_editor.auto_indent_proc = auto_indent_proc
|
228
307
|
line_editor.dig_perfect_match_proc = dig_perfect_match_proc
|
229
308
|
line_editor.pre_input_hook = pre_input_hook
|
309
|
+
@dialog_proc_list.each_pair do |name_sym, d|
|
310
|
+
line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
|
311
|
+
end
|
230
312
|
|
231
313
|
unless config.test_mode
|
232
314
|
config.read
|
233
315
|
config.reset_default_key_bindings
|
234
|
-
Reline::IOGate
|
235
|
-
config.add_default_key_binding(key, func)
|
236
|
-
end
|
316
|
+
Reline::IOGate.set_default_key_bindings(config)
|
237
317
|
end
|
238
318
|
|
239
319
|
line_editor.rerender
|
240
320
|
|
241
321
|
begin
|
322
|
+
line_editor.set_signal_handlers
|
242
323
|
prev_pasting_state = false
|
243
324
|
loop do
|
244
325
|
prev_pasting_state = Reline::IOGate.in_pasting?
|
@@ -267,17 +348,23 @@ module Reline
|
|
267
348
|
line_editor.finalize
|
268
349
|
Reline::IOGate.deprep(otio)
|
269
350
|
raise e
|
351
|
+
rescue Exception
|
352
|
+
# Including Interrupt
|
353
|
+
line_editor.finalize
|
354
|
+
Reline::IOGate.deprep(otio)
|
355
|
+
raise
|
270
356
|
end
|
271
357
|
|
272
358
|
line_editor.finalize
|
273
359
|
Reline::IOGate.deprep(otio)
|
274
360
|
end
|
275
361
|
|
276
|
-
#
|
277
|
-
#
|
278
|
-
#
|
279
|
-
#
|
280
|
-
#
|
362
|
+
# GNU Readline waits for "keyseq-timeout" milliseconds to see if the ESC
|
363
|
+
# is followed by a character, and times out and treats it as a standalone
|
364
|
+
# ESC if the second character does not arrive. If the second character
|
365
|
+
# comes before timed out, it is treated as a modifier key with the
|
366
|
+
# meta-property of meta-key, so that it can be distinguished from
|
367
|
+
# multibyte characters with the 8th bit turned on.
|
281
368
|
#
|
282
369
|
# GNU Readline will wait for the 2nd character with "keyseq-timeout"
|
283
370
|
# milli-seconds but wait forever after 3rd characters.
|
@@ -301,25 +388,9 @@ module Reline
|
|
301
388
|
break
|
302
389
|
when :matching
|
303
390
|
if buffer.size == 1
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
succ_c = Reline::IOGate.getc
|
308
|
-
}
|
309
|
-
rescue Timeout::Error # cancel matching only when first byte
|
310
|
-
block.([Reline::Key.new(c, c, false)])
|
311
|
-
break
|
312
|
-
else
|
313
|
-
if key_stroke.match_status(buffer.dup.push(succ_c)) == :unmatched
|
314
|
-
if c == "\e".ord
|
315
|
-
block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
|
316
|
-
else
|
317
|
-
block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
|
318
|
-
end
|
319
|
-
break
|
320
|
-
else
|
321
|
-
Reline::IOGate.ungetc(succ_c)
|
322
|
-
end
|
391
|
+
case read_2nd_character_of_key_sequence(keyseq_timeout, buffer, c, block)
|
392
|
+
when :break then break
|
393
|
+
when :next then next
|
323
394
|
end
|
324
395
|
end
|
325
396
|
when :unmatched
|
@@ -336,6 +407,38 @@ module Reline
|
|
336
407
|
end
|
337
408
|
end
|
338
409
|
|
410
|
+
private def read_2nd_character_of_key_sequence(keyseq_timeout, buffer, c, block)
|
411
|
+
begin
|
412
|
+
succ_c = nil
|
413
|
+
Timeout.timeout(keyseq_timeout / 1000.0) {
|
414
|
+
succ_c = Reline::IOGate.getc
|
415
|
+
}
|
416
|
+
rescue Timeout::Error # cancel matching only when first byte
|
417
|
+
block.([Reline::Key.new(c, c, false)])
|
418
|
+
return :break
|
419
|
+
else
|
420
|
+
case key_stroke.match_status(buffer.dup.push(succ_c))
|
421
|
+
when :unmatched
|
422
|
+
if c == "\e".ord
|
423
|
+
block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
|
424
|
+
else
|
425
|
+
block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
|
426
|
+
end
|
427
|
+
return :break
|
428
|
+
when :matching
|
429
|
+
Reline::IOGate.ungetc(succ_c)
|
430
|
+
return :next
|
431
|
+
when :matched
|
432
|
+
buffer << succ_c
|
433
|
+
expanded = key_stroke.expand(buffer).map{ |expanded_c|
|
434
|
+
Reline::Key.new(expanded_c, expanded_c, false)
|
435
|
+
}
|
436
|
+
block.(expanded)
|
437
|
+
return :break
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
339
442
|
private def read_escaped_key(keyseq_timeout, c, block)
|
340
443
|
begin
|
341
444
|
escaped_c = nil
|
@@ -364,7 +467,7 @@ module Reline
|
|
364
467
|
|
365
468
|
private def may_req_ambiguous_char_width
|
366
469
|
@ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
|
367
|
-
return if @ambiguous_width
|
470
|
+
return if defined? @ambiguous_width
|
368
471
|
Reline::IOGate.move_cursor_column(0)
|
369
472
|
begin
|
370
473
|
output.write "\u{25bd}"
|
@@ -387,7 +490,7 @@ module Reline
|
|
387
490
|
#--------------------------------------------------------
|
388
491
|
|
389
492
|
(Core::ATTR_READER_NAMES).each { |name|
|
390
|
-
def_single_delegators :core, "#{name}", "#{name}="
|
493
|
+
def_single_delegators :core, :"#{name}", :"#{name}="
|
391
494
|
}
|
392
495
|
def_single_delegators :core, :input=, :output=
|
393
496
|
def_single_delegators :core, :vi_editing_mode, :emacs_editing_mode
|
@@ -422,6 +525,9 @@ module Reline
|
|
422
525
|
def_single_delegators :core, :ambiguous_width
|
423
526
|
def_single_delegators :core, :last_incremental_search
|
424
527
|
def_single_delegators :core, :last_incremental_search=
|
528
|
+
def_single_delegators :core, :add_dialog_proc
|
529
|
+
def_single_delegators :core, :dialog_proc
|
530
|
+
def_single_delegators :core, :autocompletion, :autocompletion=
|
425
531
|
|
426
532
|
def_single_delegators :core, :readmultiline
|
427
533
|
def_instance_delegators self, :readmultiline
|
@@ -443,6 +549,7 @@ module Reline
|
|
443
549
|
core.completer_quote_characters = '"\''
|
444
550
|
core.filename_quote_characters = ""
|
445
551
|
core.special_prefixes = ""
|
552
|
+
core.add_dialog_proc(:autocomplete, Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE, Reline::DEFAULT_DIALOG_CONTEXT)
|
446
553
|
}
|
447
554
|
end
|
448
555
|
|
@@ -455,17 +562,25 @@ module Reline
|
|
455
562
|
end
|
456
563
|
end
|
457
564
|
|
565
|
+
require 'reline/general_io'
|
458
566
|
if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
459
567
|
require 'reline/windows'
|
460
568
|
if Reline::Windows.msys_tty?
|
461
|
-
|
462
|
-
|
569
|
+
Reline::IOGate = if ENV['TERM'] == 'dumb'
|
570
|
+
Reline::GeneralIO
|
571
|
+
else
|
572
|
+
require 'reline/ansi'
|
573
|
+
Reline::ANSI
|
574
|
+
end
|
463
575
|
else
|
464
576
|
Reline::IOGate = Reline::Windows
|
465
577
|
end
|
466
578
|
else
|
467
|
-
|
468
|
-
|
579
|
+
Reline::IOGate = if $stdout.isatty
|
580
|
+
require 'reline/ansi'
|
581
|
+
Reline::ANSI
|
582
|
+
else
|
583
|
+
Reline::GeneralIO
|
584
|
+
end
|
469
585
|
end
|
470
586
|
Reline::HISTORY = Reline::History.new(Reline.core.config)
|
471
|
-
require 'reline/general_io'
|
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.3.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: 2022-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: io-console
|
@@ -24,62 +24,6 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.5'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: bundler
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rake
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: test-unit
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: yamatanooroti
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 0.0.6
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 0.0.6
|
83
27
|
description: Alternative GNU Readline or Editline implementation by pure Ruby.
|
84
28
|
email:
|
85
29
|
- aycabta@gmail.com
|
@@ -104,6 +48,7 @@ files:
|
|
104
48
|
- lib/reline/kill_ring.rb
|
105
49
|
- lib/reline/line_editor.rb
|
106
50
|
- lib/reline/sibori.rb
|
51
|
+
- lib/reline/terminfo.rb
|
107
52
|
- lib/reline/unicode.rb
|
108
53
|
- lib/reline/unicode/east_asian_width.rb
|
109
54
|
- lib/reline/version.rb
|
@@ -128,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
73
|
- !ruby/object:Gem::Version
|
129
74
|
version: '0'
|
130
75
|
requirements: []
|
131
|
-
rubygems_version: 3.
|
76
|
+
rubygems_version: 3.0.3.1
|
132
77
|
signing_key:
|
133
78
|
specification_version: 4
|
134
79
|
summary: Alternative GNU Readline or Editline implementation by pure Ruby.
|