reline 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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'
|