reline 0.0.0 → 0.0.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.rb +313 -94
- data/lib/reline/ansi.rb +60 -20
- data/lib/reline/config.rb +137 -85
- data/lib/reline/general_io.rb +64 -0
- data/lib/reline/history.rb +56 -0
- data/lib/reline/key_actor/emacs.rb +37 -38
- data/lib/reline/key_actor/vi_command.rb +34 -35
- data/lib/reline/key_actor/vi_insert.rb +160 -161
- data/lib/reline/key_stroke.rb +3 -24
- data/lib/reline/line_editor.rb +736 -217
- data/lib/reline/unicode.rb +113 -1
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +72 -12
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: beb24e6d3114b6debe540928f1299fd10d420ff447f90401c9a8249b12804b7d
|
4
|
+
data.tar.gz: 046dfcfa706a95f62acb152238cae3680f918f9af2c9a52eee4fa4e5771ebdbc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a9a1abfe4febd8da98052386b0cca9a4b3c9f333c0a53c439d5980cad64c3b2f9f75c35d2498e42beadd4cea2f6a7d90c005d0112317b8ea90f89fd45b0f8632
|
7
|
+
data.tar.gz: 6f482d37f4c85ab5f0a5df08e069f81c5289b7bedbcea105a0601a7174bd3ca314c0a04750e2bba91cb31ad7bdc7f56afa4f3b8398bf2bd80e635e356be12615
|
data/lib/reline.rb
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
require 'io/console'
|
2
|
+
require 'timeout'
|
2
3
|
require 'reline/version'
|
3
4
|
require 'reline/config'
|
4
5
|
require 'reline/key_actor'
|
5
6
|
require 'reline/key_stroke'
|
6
7
|
require 'reline/line_editor'
|
8
|
+
require 'reline/history'
|
7
9
|
|
8
10
|
module Reline
|
11
|
+
Key = Struct.new('Key', :char, :combined_char, :with_meta)
|
12
|
+
|
9
13
|
extend self
|
10
14
|
FILENAME_COMPLETION_PROC = nil
|
11
15
|
USERNAME_COMPLETION_PROC = nil
|
12
|
-
HISTORY = Array.new
|
13
16
|
|
14
17
|
if RUBY_PLATFORM =~ /mswin|mingw/
|
15
|
-
require 'Win32API'
|
16
18
|
IS_WINDOWS = true
|
17
19
|
else
|
18
20
|
IS_WINDOWS = false
|
@@ -20,44 +22,83 @@ module Reline
|
|
20
22
|
|
21
23
|
CursorPos = Struct.new(:x, :y)
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
attr_accessor :completer_word_break_characters
|
27
|
-
attr_reader :completion_append_character
|
28
|
-
attr_accessor :completion_case_fold
|
29
|
-
attr_accessor :filename_quote_characters
|
30
|
-
attr_writer :input
|
31
|
-
attr_writer :output
|
32
|
-
end
|
33
|
-
|
25
|
+
@@config = Reline::Config.new
|
26
|
+
@@key_stroke = Reline::KeyStroke.new(@@config)
|
27
|
+
@@line_editor = Reline::LineEditor.new(@@config)
|
34
28
|
@@ambiguous_width = nil
|
35
|
-
@@config = nil
|
36
29
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
30
|
+
HISTORY = History.new(@@config)
|
31
|
+
|
32
|
+
@@completion_append_character = nil
|
33
|
+
def self.completion_append_character
|
34
|
+
@@completion_append_character
|
35
|
+
end
|
41
36
|
def self.completion_append_character=(val)
|
42
37
|
if val.nil?
|
43
|
-
|
38
|
+
@@completion_append_character = nil
|
44
39
|
elsif val.size == 1
|
45
|
-
|
40
|
+
@@completion_append_character = val.encode(Encoding::default_external)
|
46
41
|
elsif val.size > 1
|
47
|
-
|
42
|
+
@@completion_append_character = val[0].encode(Encoding::default_external)
|
48
43
|
else
|
49
|
-
|
44
|
+
@@completion_append_character = nil
|
50
45
|
end
|
51
46
|
end
|
52
|
-
@completion_case_fold
|
53
|
-
@filename_quote_characters
|
54
47
|
|
55
48
|
@@basic_word_break_characters = " \t\n`><=;|&{("
|
56
49
|
def self.basic_word_break_characters
|
57
50
|
@@basic_word_break_characters
|
58
51
|
end
|
59
52
|
def self.basic_word_break_characters=(v)
|
60
|
-
@@basic_word_break_characters = v
|
53
|
+
@@basic_word_break_characters = v.encode(Encoding::default_external)
|
54
|
+
end
|
55
|
+
|
56
|
+
@@completer_word_break_characters = @@basic_word_break_characters.dup
|
57
|
+
def self.completer_word_break_characters
|
58
|
+
@@completer_word_break_characters
|
59
|
+
end
|
60
|
+
def self.completer_word_break_characters=(v)
|
61
|
+
@@completer_word_break_characters = v.encode(Encoding::default_external)
|
62
|
+
end
|
63
|
+
|
64
|
+
@@basic_quote_characters = '"\''
|
65
|
+
def self.basic_quote_characters
|
66
|
+
@@basic_quote_characters
|
67
|
+
end
|
68
|
+
def self.basic_quote_characters=(v)
|
69
|
+
@@basic_quote_characters = v.encode(Encoding::default_external)
|
70
|
+
end
|
71
|
+
|
72
|
+
@@completer_quote_characters = '"\''
|
73
|
+
def self.completer_quote_characters
|
74
|
+
@@completer_quote_characters
|
75
|
+
end
|
76
|
+
def self.completer_quote_characters=(v)
|
77
|
+
@@completer_quote_characters = v.encode(Encoding::default_external)
|
78
|
+
end
|
79
|
+
|
80
|
+
@@filename_quote_characters = ''
|
81
|
+
def self.filename_quote_characters
|
82
|
+
@@filename_quote_characters
|
83
|
+
end
|
84
|
+
def self.filename_quote_characters=(v)
|
85
|
+
@@filename_quote_characters = v.encode(Encoding::default_external)
|
86
|
+
end
|
87
|
+
|
88
|
+
@@special_prefixes = ''
|
89
|
+
def self.special_prefixes
|
90
|
+
@@special_prefixes
|
91
|
+
end
|
92
|
+
def self.special_prefixes=(v)
|
93
|
+
@@special_prefixes = v.encode(Encoding::default_external)
|
94
|
+
end
|
95
|
+
|
96
|
+
@@completion_case_fold = nil
|
97
|
+
def self.completion_case_fold
|
98
|
+
@@completion_case_fold
|
99
|
+
end
|
100
|
+
def self.completion_case_fold=(v)
|
101
|
+
@@completion_case_fold = v
|
61
102
|
end
|
62
103
|
|
63
104
|
@@completion_proc = nil
|
@@ -65,132 +106,310 @@ module Reline
|
|
65
106
|
@@completion_proc
|
66
107
|
end
|
67
108
|
def self.completion_proc=(p)
|
109
|
+
raise ArgumentError unless p.is_a?(Proc)
|
68
110
|
@@completion_proc = p
|
69
111
|
end
|
70
112
|
|
113
|
+
@@output_modifier_proc = nil
|
114
|
+
def self.output_modifier_proc
|
115
|
+
@@output_modifier_proc
|
116
|
+
end
|
117
|
+
def self.output_modifier_proc=(p)
|
118
|
+
raise ArgumentError unless p.is_a?(Proc)
|
119
|
+
@@output_modifier_proc = p
|
120
|
+
end
|
121
|
+
|
122
|
+
@@prompt_proc = nil
|
123
|
+
def self.prompt_proc
|
124
|
+
@@prompt_proc
|
125
|
+
end
|
126
|
+
def self.prompt_proc=(p)
|
127
|
+
raise ArgumentError unless p.is_a?(Proc)
|
128
|
+
@@prompt_proc = p
|
129
|
+
end
|
130
|
+
|
131
|
+
@@auto_indent_proc = nil
|
132
|
+
def self.auto_indent_proc
|
133
|
+
@@auto_indent_proc
|
134
|
+
end
|
135
|
+
def self.auto_indent_proc=(p)
|
136
|
+
raise ArgumentError unless p.is_a?(Proc)
|
137
|
+
@@auto_indent_proc = p
|
138
|
+
end
|
139
|
+
|
140
|
+
@@pre_input_hook = nil
|
141
|
+
def self.pre_input_hook
|
142
|
+
@@pre_input_hook
|
143
|
+
end
|
144
|
+
def self.pre_input_hook=(p)
|
145
|
+
@@pre_input_hook = p
|
146
|
+
end
|
147
|
+
|
71
148
|
@@dig_perfect_match_proc = nil
|
72
149
|
def self.dig_perfect_match_proc
|
73
150
|
@@dig_perfect_match_proc
|
74
151
|
end
|
75
152
|
def self.dig_perfect_match_proc=(p)
|
153
|
+
raise ArgumentError unless p.is_a?(Proc)
|
76
154
|
@@dig_perfect_match_proc = p
|
77
155
|
end
|
78
156
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
require 'reline/ansi'
|
157
|
+
def self.insert_text(text)
|
158
|
+
@@line_editor&.insert_text(text)
|
159
|
+
self
|
83
160
|
end
|
84
161
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
162
|
+
def self.redisplay
|
163
|
+
@@line_editor&.rerender
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.line_buffer
|
167
|
+
@@line_editor&.line
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.point
|
171
|
+
@@line_editor ? @@line_editor.byte_pointer : 0
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.point=(val)
|
175
|
+
@@line_editor.byte_pointer = val
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.delete_text(start = nil, length = nil)
|
179
|
+
@@line_editor&.delete_text(start, length)
|
180
|
+
end
|
181
|
+
|
182
|
+
private_class_method def self.test_mode
|
183
|
+
remove_const('IOGate') if const_defined?('IOGate')
|
184
|
+
const_set('IOGate', Reline::GeneralIO)
|
185
|
+
@@config.instance_variable_set(:@test_mode, true)
|
186
|
+
@@config.reset
|
187
|
+
end
|
188
|
+
|
189
|
+
def self.input=(val)
|
190
|
+
raise TypeError unless val.respond_to?(:getc) or val.nil?
|
191
|
+
if val.respond_to?(:getc)
|
192
|
+
if defined?(Reline::ANSI) and IOGate == Reline::ANSI
|
193
|
+
Reline::ANSI.input = val
|
194
|
+
elsif IOGate == Reline::GeneralIO
|
195
|
+
Reline::GeneralIO.input = val
|
196
|
+
end
|
95
197
|
end
|
96
|
-
|
97
|
-
|
198
|
+
end
|
199
|
+
|
200
|
+
@@output = STDOUT
|
201
|
+
def self.output=(val)
|
202
|
+
raise TypeError unless val.respond_to?(:write) or val.nil?
|
203
|
+
@@output = val
|
204
|
+
if defined?(Reline::ANSI) and IOGate == Reline::ANSI
|
205
|
+
Reline::ANSI.output = val
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.vi_editing_mode
|
210
|
+
@@config.editing_mode = :vi_insert
|
211
|
+
nil
|
212
|
+
end
|
213
|
+
|
214
|
+
def self.emacs_editing_mode
|
215
|
+
@@config.editing_mode = :emacs
|
216
|
+
nil
|
217
|
+
end
|
218
|
+
|
219
|
+
def self.vi_editing_mode?
|
220
|
+
@@config.editing_mode_is?(:vi_insert, :vi_command)
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.emacs_editing_mode?
|
224
|
+
@@config.editing_mode_is?(:emacs)
|
225
|
+
end
|
226
|
+
|
227
|
+
def self.get_screen_size
|
228
|
+
Reline::IOGate.get_screen_size
|
229
|
+
end
|
230
|
+
|
231
|
+
def eof?
|
232
|
+
@@line_editor.eof?
|
98
233
|
end
|
99
234
|
|
100
235
|
def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
|
101
|
-
|
102
|
-
|
103
|
-
else
|
104
|
-
inner_readline(prompt, add_hist, true)
|
236
|
+
unless confirm_multiline_termination
|
237
|
+
raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
|
105
238
|
end
|
239
|
+
inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
|
106
240
|
|
107
|
-
|
108
|
-
|
241
|
+
whole_buffer = @@line_editor.whole_buffer.dup
|
242
|
+
whole_buffer.taint
|
243
|
+
if add_hist and whole_buffer and whole_buffer.chomp.size > 0
|
244
|
+
Reline::HISTORY << whole_buffer
|
109
245
|
end
|
110
246
|
|
111
|
-
|
247
|
+
@@line_editor.reset_line if @@line_editor.whole_buffer.nil?
|
248
|
+
whole_buffer
|
112
249
|
end
|
113
250
|
|
114
251
|
def readline(prompt = '', add_hist = false)
|
115
252
|
inner_readline(prompt, add_hist, false)
|
116
253
|
|
117
|
-
|
118
|
-
|
254
|
+
line = @@line_editor.line.dup
|
255
|
+
line.taint
|
256
|
+
if add_hist and line and line.chomp.size > 0
|
257
|
+
Reline::HISTORY << line.chomp
|
119
258
|
end
|
120
259
|
|
121
|
-
|
260
|
+
@@line_editor.reset_line if @@line_editor.line.nil?
|
261
|
+
line
|
122
262
|
end
|
123
263
|
|
124
264
|
def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
|
125
|
-
if
|
126
|
-
|
127
|
-
|
265
|
+
if ENV['RELINE_STDERR_TTY']
|
266
|
+
$stderr.reopen(ENV['RELINE_STDERR_TTY'], 'w')
|
267
|
+
$stderr.sync = true
|
268
|
+
$stderr.puts "Reline is used by #{Process.pid}"
|
128
269
|
end
|
129
|
-
otio = prep
|
270
|
+
otio = Reline::IOGate.prep
|
130
271
|
|
131
272
|
may_req_ambiguous_char_width
|
132
|
-
|
273
|
+
@@line_editor.reset(prompt)
|
133
274
|
if multiline
|
134
|
-
|
275
|
+
@@line_editor.multiline_on
|
135
276
|
if block_given?
|
136
|
-
|
277
|
+
@@line_editor.confirm_multiline_termination_proc = confirm_multiline_termination
|
137
278
|
end
|
138
|
-
end
|
139
|
-
@line_editor.completion_proc = @@completion_proc
|
140
|
-
@line_editor.dig_perfect_match_proc = @@dig_perfect_match_proc
|
141
|
-
@line_editor.retrieve_completion_block = method(:retrieve_completion_block)
|
142
|
-
@line_editor.rerender
|
143
|
-
|
144
|
-
if IS_WINDOWS
|
145
|
-
config = {
|
146
|
-
key_mapping: {
|
147
|
-
[224, 72] => :ed_prev_history, # ↑
|
148
|
-
[224, 80] => :ed_next_history, # ↓
|
149
|
-
[224, 77] => :ed_next_char, # →
|
150
|
-
[224, 75] => :ed_prev_char # ←
|
151
|
-
}
|
152
|
-
}
|
153
279
|
else
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
280
|
+
@@line_editor.multiline_off
|
281
|
+
end
|
282
|
+
@@line_editor.output = @@output
|
283
|
+
@@line_editor.completion_proc = @@completion_proc
|
284
|
+
@@line_editor.output_modifier_proc = @@output_modifier_proc
|
285
|
+
@@line_editor.prompt_proc = @@prompt_proc
|
286
|
+
@@line_editor.auto_indent_proc = @@auto_indent_proc
|
287
|
+
@@line_editor.dig_perfect_match_proc = @@dig_perfect_match_proc
|
288
|
+
@@line_editor.pre_input_hook = @@pre_input_hook
|
289
|
+
@@line_editor.rerender
|
290
|
+
|
291
|
+
unless @@config.test_mode
|
292
|
+
@@config.read
|
293
|
+
@@config.reset_default_key_bindings
|
294
|
+
Reline::IOGate::RAW_KEYSTROKE_CONFIG.each_pair do |key, func|
|
295
|
+
@@config.add_default_key_binding(key, func)
|
296
|
+
end
|
162
297
|
end
|
163
298
|
|
164
|
-
key_stroke = Reline::KeyStroke.new(config)
|
165
299
|
begin
|
166
|
-
|
167
|
-
|
300
|
+
loop do
|
301
|
+
read_io(@@config.keyseq_timeout) { |inputs|
|
168
302
|
inputs.each { |c|
|
169
|
-
|
170
|
-
|
303
|
+
@@line_editor.input_key(c)
|
304
|
+
@@line_editor.rerender
|
171
305
|
}
|
172
306
|
}
|
173
|
-
break if
|
307
|
+
break if @@line_editor.finished?
|
174
308
|
end
|
175
|
-
Reline.move_cursor_column(0)
|
309
|
+
Reline::IOGate.move_cursor_column(0)
|
176
310
|
rescue StandardError => e
|
177
|
-
|
311
|
+
@@line_editor.finalize
|
312
|
+
Reline::IOGate.deprep(otio)
|
178
313
|
raise e
|
179
314
|
end
|
180
315
|
|
181
|
-
|
316
|
+
@@line_editor.finalize
|
317
|
+
Reline::IOGate.deprep(otio)
|
318
|
+
end
|
319
|
+
|
320
|
+
# Keystrokes of GNU Readline will timeout it with the specification of
|
321
|
+
# "keyseq-timeout" when waiting for the 2nd character after the 1st one.
|
322
|
+
# If the 2nd character comes after 1st ESC without timeout it has a
|
323
|
+
# meta-property of meta-key to discriminate modified key with meta-key
|
324
|
+
# from multibyte characters that come with 8th bit on.
|
325
|
+
#
|
326
|
+
# GNU Readline will wait for the 2nd character with "keyseq-timeout"
|
327
|
+
# milli-seconds but wait forever after 3rd characters.
|
328
|
+
def read_io(keyseq_timeout, &block)
|
329
|
+
buffer = []
|
330
|
+
loop do
|
331
|
+
c = Reline::IOGate.getc
|
332
|
+
buffer << c
|
333
|
+
result = @@key_stroke.match_status(buffer)
|
334
|
+
case result
|
335
|
+
when :matched
|
336
|
+
block.(@@key_stroke.expand(buffer).map{ |c| Reline::Key.new(c, c, false) })
|
337
|
+
break
|
338
|
+
when :matching
|
339
|
+
if buffer.size == 1
|
340
|
+
begin
|
341
|
+
succ_c = nil
|
342
|
+
Timeout.timeout(keyseq_timeout / 1000.0) {
|
343
|
+
succ_c = Reline::IOGate.getc
|
344
|
+
}
|
345
|
+
rescue Timeout::Error # cancel matching only when first byte
|
346
|
+
block.([Reline::Key.new(c, c, false)])
|
347
|
+
break
|
348
|
+
else
|
349
|
+
if @@key_stroke.match_status(buffer.dup.push(succ_c)) == :unmatched
|
350
|
+
if c == "\e".ord
|
351
|
+
block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
|
352
|
+
else
|
353
|
+
block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
|
354
|
+
end
|
355
|
+
break
|
356
|
+
else
|
357
|
+
Reline::IOGate.ungetc(succ_c)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
when :unmatched
|
362
|
+
if buffer.size == 1 and c == "\e".ord
|
363
|
+
read_escaped_key(keyseq_timeout, buffer, block)
|
364
|
+
else
|
365
|
+
block.(buffer.map{ |c| Reline::Key.new(c, c, false) })
|
366
|
+
end
|
367
|
+
break
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def read_escaped_key(keyseq_timeout, buffer, block)
|
373
|
+
begin
|
374
|
+
escaped_c = nil
|
375
|
+
Timeout.timeout(keyseq_timeout / 1000.0) {
|
376
|
+
escaped_c = Reline::IOGate.getc
|
377
|
+
}
|
378
|
+
rescue Timeout::Error # independent ESC
|
379
|
+
block.([Reline::Key.new(c, c, false)])
|
380
|
+
else
|
381
|
+
if escaped_c.nil?
|
382
|
+
block.([Reline::Key.new(c, c, false)])
|
383
|
+
elsif escaped_c >= 128 # maybe, first byte of multi byte
|
384
|
+
block.([Reline::Key.new(c, c, false), Reline::Key.new(escaped_c, escaped_c, false)])
|
385
|
+
elsif escaped_c == "\e".ord # escape twice
|
386
|
+
block.([Reline::Key.new(c, c, false), Reline::Key.new(c, c, false)])
|
387
|
+
else
|
388
|
+
block.([Reline::Key.new(escaped_c, escaped_c | 0b10000000, true)])
|
389
|
+
end
|
390
|
+
end
|
182
391
|
end
|
183
392
|
|
184
393
|
def may_req_ambiguous_char_width
|
394
|
+
@@ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
|
185
395
|
return if @@ambiguous_width
|
186
|
-
Reline.move_cursor_column(0)
|
396
|
+
Reline::IOGate.move_cursor_column(0)
|
187
397
|
print "\u{25bd}"
|
188
|
-
@@ambiguous_width = Reline.cursor_pos.x
|
189
|
-
Reline.move_cursor_column(0)
|
190
|
-
Reline.erase_after_cursor
|
398
|
+
@@ambiguous_width = Reline::IOGate.cursor_pos.x
|
399
|
+
Reline::IOGate.move_cursor_column(0)
|
400
|
+
Reline::IOGate.erase_after_cursor
|
191
401
|
end
|
192
402
|
|
193
403
|
def self.ambiguous_width
|
194
404
|
@@ambiguous_width
|
195
405
|
end
|
196
406
|
end
|
407
|
+
|
408
|
+
if Reline::IS_WINDOWS
|
409
|
+
require 'reline/windows'
|
410
|
+
Reline::IOGate = Reline::Windows
|
411
|
+
else
|
412
|
+
require 'reline/ansi'
|
413
|
+
Reline::IOGate = Reline::ANSI
|
414
|
+
end
|
415
|
+
require 'reline/general_io'
|