reline 0.0.1 → 0.0.2
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 +2 -0
- data/lib/reline.rb +318 -331
- data/lib/reline/ansi.rb +6 -0
- data/lib/reline/config.rb +4 -5
- data/lib/reline/general_io.rb +3 -0
- data/lib/reline/line_editor.rb +78 -10
- data/lib/reline/unicode/east_asian_width.rb +1 -1
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +23 -3
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f47cc0525de297744dff41dc87b9a4ac5c9ff1cad21da7f60a41aa4b17bd1b0e
|
4
|
+
data.tar.gz: '04681f2a2dc431f65fa9f2df68c8b7aa1955a7a4032b8d6d5483984e236d4c9b'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad38bf76659bc7553ebf084fd4aeedf6aba7cba1a2860fc78af0683222109216d59bdb3b4bf66962ce9a640b9033d21f707442151834e9ccc21c6fc8d115347b
|
7
|
+
data.tar.gz: 95d9d943870e64897076cdc97750f6110817bc4ad14e958755164aa862785d7e48399465f7997f900547f797e6304cf48d6761f78612651ebe7aa6dca505fab3
|
data/README.md
CHANGED
data/lib/reline.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'io/console'
|
2
2
|
require 'timeout'
|
3
|
+
require 'forwardable'
|
3
4
|
require 'reline/version'
|
4
5
|
require 'reline/config'
|
5
6
|
require 'reline/key_actor'
|
@@ -8,404 +9,390 @@ require 'reline/line_editor'
|
|
8
9
|
require 'reline/history'
|
9
10
|
|
10
11
|
module Reline
|
11
|
-
Key = Struct.new('Key', :char, :combined_char, :with_meta)
|
12
|
-
|
13
|
-
extend self
|
14
12
|
FILENAME_COMPLETION_PROC = nil
|
15
13
|
USERNAME_COMPLETION_PROC = nil
|
16
14
|
|
17
|
-
|
18
|
-
IS_WINDOWS = true
|
19
|
-
else
|
20
|
-
IS_WINDOWS = false
|
21
|
-
end
|
22
|
-
|
15
|
+
Key = Struct.new('Key', :char, :combined_char, :with_meta)
|
23
16
|
CursorPos = Struct.new(:x, :y)
|
24
17
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
@@ambiguous_width = nil
|
29
|
-
|
30
|
-
HISTORY = History.new(@@config)
|
31
|
-
|
32
|
-
@@completion_append_character = nil
|
33
|
-
def self.completion_append_character
|
34
|
-
@@completion_append_character
|
35
|
-
end
|
36
|
-
def self.completion_append_character=(val)
|
37
|
-
if val.nil?
|
38
|
-
@@completion_append_character = nil
|
39
|
-
elsif val.size == 1
|
40
|
-
@@completion_append_character = val.encode(Encoding::default_external)
|
41
|
-
elsif val.size > 1
|
42
|
-
@@completion_append_character = val[0].encode(Encoding::default_external)
|
18
|
+
class Core
|
19
|
+
if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
20
|
+
IS_WINDOWS = true
|
43
21
|
else
|
44
|
-
|
22
|
+
IS_WINDOWS = false
|
45
23
|
end
|
46
|
-
end
|
47
|
-
|
48
|
-
@@basic_word_break_characters = " \t\n`><=;|&{("
|
49
|
-
def self.basic_word_break_characters
|
50
|
-
@@basic_word_break_characters
|
51
|
-
end
|
52
|
-
def self.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
24
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
25
|
+
ATTR_READER_NAMES = %i(
|
26
|
+
completion_append_character
|
27
|
+
basic_word_break_characters
|
28
|
+
completer_word_break_characters
|
29
|
+
basic_quote_characters
|
30
|
+
completer_quote_characters
|
31
|
+
filename_quote_characters
|
32
|
+
special_prefixes
|
33
|
+
completion_proc
|
34
|
+
output_modifier_proc
|
35
|
+
prompt_proc
|
36
|
+
auto_indent_proc
|
37
|
+
pre_input_hook
|
38
|
+
dig_perfect_match_proc
|
39
|
+
).each(&method(:attr_reader))
|
40
|
+
|
41
|
+
ATTR_ACCESSOR_NAMES = %i(
|
42
|
+
completion_case_fold
|
43
|
+
).each(&method(:attr_accessor))
|
44
|
+
|
45
|
+
attr_accessor :config
|
46
|
+
attr_accessor :key_stroke
|
47
|
+
attr_accessor :line_editor
|
48
|
+
attr_accessor :ambiguous_width
|
49
|
+
attr_reader :output
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
self.output = STDOUT
|
53
|
+
yield self
|
54
|
+
end
|
103
55
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
56
|
+
def completion_append_character=(val)
|
57
|
+
if val.nil?
|
58
|
+
@completion_append_character = nil
|
59
|
+
elsif val.size == 1
|
60
|
+
@completion_append_character = val.encode(Encoding::default_external)
|
61
|
+
elsif val.size > 1
|
62
|
+
@completion_append_character = val[0].encode(Encoding::default_external)
|
63
|
+
else
|
64
|
+
@completion_append_character = nil
|
65
|
+
end
|
66
|
+
end
|
112
67
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
end
|
117
|
-
def self.output_modifier_proc=(p)
|
118
|
-
raise ArgumentError unless p.is_a?(Proc)
|
119
|
-
@@output_modifier_proc = p
|
120
|
-
end
|
68
|
+
def basic_word_break_characters=(v)
|
69
|
+
@basic_word_break_characters = v.encode(Encoding::default_external)
|
70
|
+
end
|
121
71
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
end
|
126
|
-
def self.prompt_proc=(p)
|
127
|
-
raise ArgumentError unless p.is_a?(Proc)
|
128
|
-
@@prompt_proc = p
|
129
|
-
end
|
72
|
+
def completer_word_break_characters=(v)
|
73
|
+
@completer_word_break_characters = v.encode(Encoding::default_external)
|
74
|
+
end
|
130
75
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
end
|
135
|
-
def self.auto_indent_proc=(p)
|
136
|
-
raise ArgumentError unless p.is_a?(Proc)
|
137
|
-
@@auto_indent_proc = p
|
138
|
-
end
|
76
|
+
def basic_quote_characters=(v)
|
77
|
+
@basic_quote_characters = v.encode(Encoding::default_external)
|
78
|
+
end
|
139
79
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
end
|
144
|
-
def self.pre_input_hook=(p)
|
145
|
-
@@pre_input_hook = p
|
146
|
-
end
|
80
|
+
def completer_quote_characters=(v)
|
81
|
+
@completer_quote_characters = v.encode(Encoding::default_external)
|
82
|
+
end
|
147
83
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
end
|
152
|
-
def self.dig_perfect_match_proc=(p)
|
153
|
-
raise ArgumentError unless p.is_a?(Proc)
|
154
|
-
@@dig_perfect_match_proc = p
|
155
|
-
end
|
84
|
+
def filename_quote_characters=(v)
|
85
|
+
@filename_quote_characters = v.encode(Encoding::default_external)
|
86
|
+
end
|
156
87
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
end
|
88
|
+
def special_prefixes=(v)
|
89
|
+
@special_prefixes = v.encode(Encoding::default_external)
|
90
|
+
end
|
161
91
|
|
162
|
-
|
163
|
-
|
164
|
-
|
92
|
+
def completion_proc=(p)
|
93
|
+
raise ArgumentError unless p.is_a?(Proc)
|
94
|
+
@completion_proc = p
|
95
|
+
end
|
165
96
|
|
166
|
-
|
167
|
-
|
168
|
-
|
97
|
+
def output_modifier_proc=(p)
|
98
|
+
raise ArgumentError unless p.is_a?(Proc)
|
99
|
+
@output_modifier_proc = p
|
100
|
+
end
|
169
101
|
|
170
|
-
|
171
|
-
|
172
|
-
|
102
|
+
def prompt_proc=(p)
|
103
|
+
raise ArgumentError unless p.is_a?(Proc)
|
104
|
+
@prompt_proc = p
|
105
|
+
end
|
173
106
|
|
174
|
-
|
175
|
-
|
176
|
-
|
107
|
+
def auto_indent_proc=(p)
|
108
|
+
raise ArgumentError unless p.is_a?(Proc)
|
109
|
+
@auto_indent_proc = p
|
110
|
+
end
|
177
111
|
|
178
|
-
|
179
|
-
|
180
|
-
|
112
|
+
def pre_input_hook=(p)
|
113
|
+
@pre_input_hook = p
|
114
|
+
end
|
181
115
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
@@config.reset
|
187
|
-
end
|
116
|
+
def dig_perfect_match_proc=(p)
|
117
|
+
raise ArgumentError unless p.is_a?(Proc)
|
118
|
+
@dig_perfect_match_proc = p
|
119
|
+
end
|
188
120
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
121
|
+
def input=(val)
|
122
|
+
raise TypeError unless val.respond_to?(:getc) or val.nil?
|
123
|
+
if val.respond_to?(:getc)
|
124
|
+
if defined?(Reline::ANSI) and Reline::IOGate == Reline::ANSI
|
125
|
+
Reline::ANSI.input = val
|
126
|
+
elsif Reline::IOGate == Reline::GeneralIO
|
127
|
+
Reline::GeneralIO.input = val
|
128
|
+
end
|
196
129
|
end
|
197
130
|
end
|
198
|
-
end
|
199
131
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
132
|
+
def output=(val)
|
133
|
+
raise TypeError unless val.respond_to?(:write) or val.nil?
|
134
|
+
@output = val
|
135
|
+
if defined?(Reline::ANSI) and Reline::IOGate == Reline::ANSI
|
136
|
+
Reline::ANSI.output = val
|
137
|
+
end
|
206
138
|
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
139
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
def self.emacs_editing_mode?
|
224
|
-
@@config.editing_mode_is?(:emacs)
|
225
|
-
end
|
140
|
+
def vi_editing_mode
|
141
|
+
config.editing_mode = :vi_insert
|
142
|
+
nil
|
143
|
+
end
|
226
144
|
|
227
|
-
|
228
|
-
|
229
|
-
|
145
|
+
def emacs_editing_mode
|
146
|
+
config.editing_mode = :emacs
|
147
|
+
nil
|
148
|
+
end
|
230
149
|
|
231
|
-
|
232
|
-
|
233
|
-
|
150
|
+
def vi_editing_mode?
|
151
|
+
config.editing_mode_is?(:vi_insert, :vi_command)
|
152
|
+
end
|
234
153
|
|
235
|
-
|
236
|
-
|
237
|
-
raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
|
154
|
+
def emacs_editing_mode?
|
155
|
+
config.editing_mode_is?(:emacs)
|
238
156
|
end
|
239
|
-
inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
|
240
157
|
|
241
|
-
|
242
|
-
|
243
|
-
if add_hist and whole_buffer and whole_buffer.chomp.size > 0
|
244
|
-
Reline::HISTORY << whole_buffer
|
158
|
+
def get_screen_size
|
159
|
+
Reline::IOGate.get_screen_size
|
245
160
|
end
|
246
161
|
|
247
|
-
|
248
|
-
|
249
|
-
|
162
|
+
def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
|
163
|
+
unless confirm_multiline_termination
|
164
|
+
raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
|
165
|
+
end
|
166
|
+
inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
|
250
167
|
|
251
|
-
|
252
|
-
|
168
|
+
whole_buffer = line_editor.whole_buffer.dup
|
169
|
+
whole_buffer.taint
|
170
|
+
if add_hist and whole_buffer and whole_buffer.chomp.size > 0
|
171
|
+
Reline::HISTORY << whole_buffer
|
172
|
+
end
|
253
173
|
|
254
|
-
|
255
|
-
|
256
|
-
if add_hist and line and line.chomp.size > 0
|
257
|
-
Reline::HISTORY << line.chomp
|
174
|
+
line_editor.reset_line if line_editor.whole_buffer.nil?
|
175
|
+
whole_buffer
|
258
176
|
end
|
259
177
|
|
260
|
-
|
261
|
-
|
262
|
-
end
|
178
|
+
def readline(prompt = '', add_hist = false)
|
179
|
+
inner_readline(prompt, add_hist, false)
|
263
180
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
$stderr.puts "Reline is used by #{Process.pid}"
|
269
|
-
end
|
270
|
-
otio = Reline::IOGate.prep
|
271
|
-
|
272
|
-
may_req_ambiguous_char_width
|
273
|
-
@@line_editor.reset(prompt)
|
274
|
-
if multiline
|
275
|
-
@@line_editor.multiline_on
|
276
|
-
if block_given?
|
277
|
-
@@line_editor.confirm_multiline_termination_proc = confirm_multiline_termination
|
181
|
+
line = line_editor.line.dup
|
182
|
+
line.taint
|
183
|
+
if add_hist and line and line.chomp.size > 0
|
184
|
+
Reline::HISTORY << line.chomp
|
278
185
|
end
|
279
|
-
|
280
|
-
|
186
|
+
|
187
|
+
line_editor.reset_line if line_editor.line.nil?
|
188
|
+
line
|
281
189
|
end
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
190
|
+
|
191
|
+
private def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
|
192
|
+
if ENV['RELINE_STDERR_TTY']
|
193
|
+
$stderr.reopen(ENV['RELINE_STDERR_TTY'], 'w')
|
194
|
+
$stderr.sync = true
|
195
|
+
$stderr.puts "Reline is used by #{Process.pid}"
|
196
|
+
end
|
197
|
+
otio = Reline::IOGate.prep
|
198
|
+
|
199
|
+
may_req_ambiguous_char_width
|
200
|
+
line_editor.reset(prompt)
|
201
|
+
if multiline
|
202
|
+
line_editor.multiline_on
|
203
|
+
if block_given?
|
204
|
+
line_editor.confirm_multiline_termination_proc = confirm_multiline_termination
|
205
|
+
end
|
206
|
+
else
|
207
|
+
line_editor.multiline_off
|
208
|
+
end
|
209
|
+
line_editor.output = output
|
210
|
+
line_editor.completion_proc = completion_proc
|
211
|
+
line_editor.output_modifier_proc = output_modifier_proc
|
212
|
+
line_editor.prompt_proc = prompt_proc
|
213
|
+
line_editor.auto_indent_proc = auto_indent_proc
|
214
|
+
line_editor.dig_perfect_match_proc = dig_perfect_match_proc
|
215
|
+
line_editor.pre_input_hook = pre_input_hook
|
216
|
+
line_editor.rerender
|
217
|
+
|
218
|
+
unless config.test_mode
|
219
|
+
config.read
|
220
|
+
config.reset_default_key_bindings
|
221
|
+
Reline::IOGate::RAW_KEYSTROKE_CONFIG.each_pair do |key, func|
|
222
|
+
config.add_default_key_binding(key, func)
|
223
|
+
end
|
296
224
|
end
|
297
|
-
end
|
298
225
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
226
|
+
begin
|
227
|
+
loop do
|
228
|
+
read_io(config.keyseq_timeout) { |inputs|
|
229
|
+
inputs.each { |c|
|
230
|
+
line_editor.input_key(c)
|
231
|
+
line_editor.rerender
|
232
|
+
}
|
305
233
|
}
|
306
|
-
|
307
|
-
|
234
|
+
break if line_editor.finished?
|
235
|
+
end
|
236
|
+
Reline::IOGate.move_cursor_column(0)
|
237
|
+
rescue StandardError => e
|
238
|
+
line_editor.finalize
|
239
|
+
Reline::IOGate.deprep(otio)
|
240
|
+
raise e
|
308
241
|
end
|
309
|
-
|
310
|
-
|
311
|
-
@@line_editor.finalize
|
242
|
+
|
243
|
+
line_editor.finalize
|
312
244
|
Reline::IOGate.deprep(otio)
|
313
|
-
raise e
|
314
245
|
end
|
315
246
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
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
|
247
|
+
# Keystrokes of GNU Readline will timeout it with the specification of
|
248
|
+
# "keyseq-timeout" when waiting for the 2nd character after the 1st one.
|
249
|
+
# If the 2nd character comes after 1st ESC without timeout it has a
|
250
|
+
# meta-property of meta-key to discriminate modified key with meta-key
|
251
|
+
# from multibyte characters that come with 8th bit on.
|
252
|
+
#
|
253
|
+
# GNU Readline will wait for the 2nd character with "keyseq-timeout"
|
254
|
+
# milli-seconds but wait forever after 3rd characters.
|
255
|
+
private def read_io(keyseq_timeout, &block)
|
256
|
+
buffer = []
|
257
|
+
loop do
|
258
|
+
c = Reline::IOGate.getc
|
259
|
+
buffer << c
|
260
|
+
result = key_stroke.match_status(buffer)
|
261
|
+
case result
|
262
|
+
when :matched
|
263
|
+
block.(key_stroke.expand(buffer).map{ |c| Reline::Key.new(c, c, false) })
|
264
|
+
break
|
265
|
+
when :matching
|
266
|
+
if buffer.size == 1
|
267
|
+
begin
|
268
|
+
succ_c = nil
|
269
|
+
Timeout.timeout(keyseq_timeout / 1000.0) {
|
270
|
+
succ_c = Reline::IOGate.getc
|
271
|
+
}
|
272
|
+
rescue Timeout::Error # cancel matching only when first byte
|
273
|
+
block.([Reline::Key.new(c, c, false)])
|
355
274
|
break
|
356
275
|
else
|
357
|
-
|
276
|
+
if key_stroke.match_status(buffer.dup.push(succ_c)) == :unmatched
|
277
|
+
if c == "\e".ord
|
278
|
+
block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
|
279
|
+
else
|
280
|
+
block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
|
281
|
+
end
|
282
|
+
break
|
283
|
+
else
|
284
|
+
Reline::IOGate.ungetc(succ_c)
|
285
|
+
end
|
358
286
|
end
|
359
287
|
end
|
288
|
+
when :unmatched
|
289
|
+
if buffer.size == 1 and c == "\e".ord
|
290
|
+
read_escaped_key(keyseq_timeout, buffer, block)
|
291
|
+
else
|
292
|
+
block.(buffer.map{ |c| Reline::Key.new(c, c, false) })
|
293
|
+
end
|
294
|
+
break
|
360
295
|
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
296
|
end
|
369
297
|
end
|
370
|
-
end
|
371
298
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
block.([Reline::Key.new(c, c, false)])
|
380
|
-
else
|
381
|
-
if escaped_c.nil?
|
299
|
+
private def read_escaped_key(keyseq_timeout, buffer, block)
|
300
|
+
begin
|
301
|
+
escaped_c = nil
|
302
|
+
Timeout.timeout(keyseq_timeout / 1000.0) {
|
303
|
+
escaped_c = Reline::IOGate.getc
|
304
|
+
}
|
305
|
+
rescue Timeout::Error # independent ESC
|
382
306
|
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
307
|
else
|
388
|
-
|
308
|
+
if escaped_c.nil?
|
309
|
+
block.([Reline::Key.new(c, c, false)])
|
310
|
+
elsif escaped_c >= 128 # maybe, first byte of multi byte
|
311
|
+
block.([Reline::Key.new(c, c, false), Reline::Key.new(escaped_c, escaped_c, false)])
|
312
|
+
elsif escaped_c == "\e".ord # escape twice
|
313
|
+
block.([Reline::Key.new(c, c, false), Reline::Key.new(c, c, false)])
|
314
|
+
else
|
315
|
+
block.([Reline::Key.new(escaped_c, escaped_c | 0b10000000, true)])
|
316
|
+
end
|
389
317
|
end
|
390
318
|
end
|
319
|
+
|
320
|
+
private def may_req_ambiguous_char_width
|
321
|
+
@ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
|
322
|
+
return if @ambiguous_width
|
323
|
+
Reline::IOGate.move_cursor_column(0)
|
324
|
+
print "\u{25bd}"
|
325
|
+
@ambiguous_width = Reline::IOGate.cursor_pos.x
|
326
|
+
Reline::IOGate.move_cursor_column(0)
|
327
|
+
Reline::IOGate.erase_after_cursor
|
328
|
+
end
|
391
329
|
end
|
392
330
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
331
|
+
extend Forwardable
|
332
|
+
extend SingleForwardable
|
333
|
+
|
334
|
+
#--------------------------------------------------------
|
335
|
+
# Documented API
|
336
|
+
#--------------------------------------------------------
|
337
|
+
|
338
|
+
(Core::ATTR_READER_NAMES + Core::ATTR_ACCESSOR_NAMES).each { |name|
|
339
|
+
def_single_delegators :core, "#{name}", "#{name}="
|
340
|
+
}
|
341
|
+
def_single_delegators :core, :input=, :output=
|
342
|
+
def_single_delegators :core, :vi_editing_mode, :emacs_editing_mode
|
343
|
+
def_single_delegators :core, :readline
|
344
|
+
def_instance_delegators self, :readline
|
345
|
+
|
346
|
+
|
347
|
+
#--------------------------------------------------------
|
348
|
+
# Undocumented API
|
349
|
+
#--------------------------------------------------------
|
350
|
+
|
351
|
+
# Testable in original
|
352
|
+
def_single_delegators :core, :get_screen_size
|
353
|
+
def_single_delegators :line_editor, :eof?
|
354
|
+
def_instance_delegators self, :eof?
|
355
|
+
def_single_delegators :line_editor, :delete_text
|
356
|
+
def_single_delegator :line_editor, :line, :line_buffer
|
357
|
+
def_single_delegator :line_editor, :byte_pointer, :point
|
358
|
+
def_single_delegator :line_editor, :byte_pointer=, :point=
|
359
|
+
|
360
|
+
def self.insert_text(*args, &block)
|
361
|
+
line_editor.insert_text(*args, &block)
|
362
|
+
self
|
363
|
+
end
|
364
|
+
|
365
|
+
# Untestable in original
|
366
|
+
def_single_delegator :line_editor, :rerender, :redisplay
|
367
|
+
def_single_delegators :core, :vi_editing_mode?, :emacs_editing_mode?
|
368
|
+
def_single_delegators :core, :ambiguous_width
|
369
|
+
|
370
|
+
def_single_delegators :core, :readmultiline
|
371
|
+
def_instance_delegators self, :readmultiline
|
372
|
+
|
373
|
+
def self.core
|
374
|
+
@core ||= Core.new { |core|
|
375
|
+
core.config = Reline::Config.new
|
376
|
+
core.key_stroke = Reline::KeyStroke.new(core.config)
|
377
|
+
core.line_editor = Reline::LineEditor.new(core.config)
|
378
|
+
|
379
|
+
core.basic_word_break_characters = " \t\n`><=;|&{("
|
380
|
+
core.completer_word_break_characters = " \t\n`><=;|&{("
|
381
|
+
core.basic_quote_characters = '"\''
|
382
|
+
core.completer_quote_characters = '"\''
|
383
|
+
core.filename_quote_characters = ""
|
384
|
+
core.special_prefixes = ""
|
385
|
+
}
|
401
386
|
end
|
402
387
|
|
403
|
-
def self.
|
404
|
-
|
388
|
+
def self.line_editor
|
389
|
+
core.line_editor
|
405
390
|
end
|
391
|
+
|
392
|
+
HISTORY = History.new(core.config)
|
406
393
|
end
|
407
394
|
|
408
|
-
if Reline::IS_WINDOWS
|
395
|
+
if Reline::Core::IS_WINDOWS
|
409
396
|
require 'reline/windows'
|
410
397
|
Reline::IOGate = Reline::Windows
|
411
398
|
else
|
data/lib/reline/ansi.rb
CHANGED
@@ -106,6 +106,11 @@ class Reline::ANSI
|
|
106
106
|
print "\e[1;1H"
|
107
107
|
end
|
108
108
|
|
109
|
+
@@old_winch_handler = nil
|
110
|
+
def self.set_winch_handler(&handler)
|
111
|
+
@@old_winch_handler = Signal.trap('WINCH', &handler)
|
112
|
+
end
|
113
|
+
|
109
114
|
def self.prep
|
110
115
|
int_handle = Signal.trap('INT', 'IGNORE')
|
111
116
|
otio = `stty -g`.chomp
|
@@ -123,5 +128,6 @@ class Reline::ANSI
|
|
123
128
|
int_handle = Signal.trap('INT', 'IGNORE')
|
124
129
|
`stty #{otio}`
|
125
130
|
Signal.trap('INT', int_handle)
|
131
|
+
Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
|
126
132
|
end
|
127
133
|
end
|
data/lib/reline/config.rb
CHANGED
@@ -126,20 +126,19 @@ class Reline::Config
|
|
126
126
|
no += 1
|
127
127
|
|
128
128
|
line = line.chomp.lstrip
|
129
|
-
if line
|
129
|
+
if line.start_with?('$')
|
130
130
|
handle_directive(line[1..-1], file, no)
|
131
131
|
next
|
132
132
|
end
|
133
133
|
|
134
134
|
next if @skip_section
|
135
135
|
|
136
|
-
|
136
|
+
case line
|
137
|
+
when /^set +([^ ]+) +([^ ]+)/i
|
137
138
|
var, value = $1.downcase, $2.downcase
|
138
139
|
bind_variable(var, value)
|
139
140
|
next
|
140
|
-
|
141
|
-
|
142
|
-
if line =~ /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/
|
141
|
+
when /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
|
143
142
|
key, func_name = $1, $2
|
144
143
|
keystroke, func = bind_key(key, func_name)
|
145
144
|
next unless keystroke
|
data/lib/reline/general_io.rb
CHANGED
data/lib/reline/line_editor.rb
CHANGED
@@ -69,6 +69,63 @@ class Reline::LineEditor
|
|
69
69
|
Reline::IOGate.move_cursor_column(0)
|
70
70
|
@old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
|
71
71
|
}
|
72
|
+
Reline::IOGate.set_winch_handler do
|
73
|
+
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
74
|
+
old_screen_size = @screen_size
|
75
|
+
@screen_size = Reline::IOGate.get_screen_size
|
76
|
+
if old_screen_size.last < @screen_size.last # columns increase
|
77
|
+
@rerender_all = true
|
78
|
+
rerender
|
79
|
+
else
|
80
|
+
special_prompt = nil
|
81
|
+
if @vi_arg
|
82
|
+
prompt = "(arg: #{@vi_arg}) "
|
83
|
+
prompt_width = calculate_width(prompt)
|
84
|
+
special_prompt = prompt
|
85
|
+
elsif @searching_prompt
|
86
|
+
prompt = @searching_prompt
|
87
|
+
prompt_width = calculate_width(prompt)
|
88
|
+
special_prompt = prompt
|
89
|
+
else
|
90
|
+
prompt = @prompt
|
91
|
+
prompt_width = calculate_width(prompt, true)
|
92
|
+
end
|
93
|
+
back = 0
|
94
|
+
new_buffer = whole_lines
|
95
|
+
prompt_list = nil
|
96
|
+
if @prompt_proc
|
97
|
+
prompt_list = @prompt_proc.(new_buffer)
|
98
|
+
prompt_list[@line_index] = special_prompt if special_prompt
|
99
|
+
prompt = prompt_list[@line_index]
|
100
|
+
prompt_width = calculate_width(prompt, true)
|
101
|
+
end
|
102
|
+
new_buffer.each_with_index do |line, index|
|
103
|
+
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
104
|
+
width = prompt_width + calculate_width(line)
|
105
|
+
height = calculate_height_by_width(width)
|
106
|
+
back += height
|
107
|
+
end
|
108
|
+
@highest_in_all = back
|
109
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
110
|
+
@first_line_started_from =
|
111
|
+
if @line_index.zero?
|
112
|
+
0
|
113
|
+
else
|
114
|
+
@buffer_of_lines[0..(@line_index - 1)].inject(0) { |result, line|
|
115
|
+
result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
|
116
|
+
}
|
117
|
+
end
|
118
|
+
if @prompt_proc
|
119
|
+
prompt = prompt_list[@line_index]
|
120
|
+
prompt_width = calculate_width(prompt, true)
|
121
|
+
end
|
122
|
+
calculate_nearest_cursor
|
123
|
+
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
124
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
125
|
+
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
126
|
+
@rerender_all = true
|
127
|
+
end
|
128
|
+
end
|
72
129
|
end
|
73
130
|
|
74
131
|
def finalize
|
@@ -241,7 +298,7 @@ class Reline::LineEditor
|
|
241
298
|
@byte_pointer = new_byte_pointer
|
242
299
|
end
|
243
300
|
|
244
|
-
def rerender
|
301
|
+
def rerender
|
245
302
|
return if @line.nil?
|
246
303
|
if @menu_info
|
247
304
|
scroll_down(@highest_in_all - @first_line_started_from)
|
@@ -255,12 +312,15 @@ class Reline::LineEditor
|
|
255
312
|
move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
|
256
313
|
@menu_info = nil
|
257
314
|
end
|
315
|
+
special_prompt = nil
|
258
316
|
if @vi_arg
|
259
317
|
prompt = "(arg: #{@vi_arg}) "
|
260
318
|
prompt_width = calculate_width(prompt)
|
319
|
+
special_prompt = prompt
|
261
320
|
elsif @searching_prompt
|
262
321
|
prompt = @searching_prompt
|
263
322
|
prompt_width = calculate_width(prompt)
|
323
|
+
special_prompt = prompt
|
264
324
|
else
|
265
325
|
prompt = @prompt
|
266
326
|
prompt_width = calculate_width(prompt, true)
|
@@ -272,6 +332,7 @@ class Reline::LineEditor
|
|
272
332
|
prompt_list = nil
|
273
333
|
if @prompt_proc
|
274
334
|
prompt_list = @prompt_proc.(whole_lines)
|
335
|
+
prompt_list[@line_index] = special_prompt if special_prompt
|
275
336
|
prompt = prompt_list[@line_index]
|
276
337
|
prompt_width = calculate_width(prompt, true)
|
277
338
|
end
|
@@ -303,6 +364,7 @@ class Reline::LineEditor
|
|
303
364
|
prompt_list = nil
|
304
365
|
if @prompt_proc
|
305
366
|
prompt_list = @prompt_proc.(new_lines)
|
367
|
+
prompt_list[@line_index] = special_prompt if special_prompt
|
306
368
|
prompt = prompt_list[@line_index]
|
307
369
|
prompt_width = calculate_width(prompt, true)
|
308
370
|
end
|
@@ -372,6 +434,7 @@ class Reline::LineEditor
|
|
372
434
|
prompt_list = nil
|
373
435
|
if @prompt_proc
|
374
436
|
prompt_list = @prompt_proc.(new_buffer)
|
437
|
+
prompt_list[@line_index] = special_prompt if special_prompt
|
375
438
|
prompt = prompt_list[@line_index]
|
376
439
|
prompt_width = calculate_width(prompt, true)
|
377
440
|
end
|
@@ -429,6 +492,7 @@ class Reline::LineEditor
|
|
429
492
|
prompt_list = nil
|
430
493
|
if @prompt_proc
|
431
494
|
prompt_list = @prompt_proc.(whole_lines)
|
495
|
+
prompt_list[@line_index] = special_prompt if special_prompt
|
432
496
|
prompt = prompt_list[@line_index]
|
433
497
|
prompt_width = calculate_width(prompt, true)
|
434
498
|
end
|
@@ -801,17 +865,25 @@ class Reline::LineEditor
|
|
801
865
|
rest = nil
|
802
866
|
break_pointer = nil
|
803
867
|
quote = nil
|
868
|
+
closing_quote = nil
|
869
|
+
escaped_quote = nil
|
804
870
|
i = 0
|
805
871
|
while i < @byte_pointer do
|
806
872
|
slice = @line.byteslice(i, @byte_pointer - i)
|
807
|
-
|
873
|
+
unless slice.valid_encoding?
|
874
|
+
i += 1
|
875
|
+
next
|
876
|
+
end
|
877
|
+
if quote and slice.start_with?(closing_quote)
|
808
878
|
quote = nil
|
809
879
|
i += 1
|
810
|
-
elsif quote and slice.start_with?(
|
880
|
+
elsif quote and slice.start_with?(escaped_quote)
|
811
881
|
# skip
|
812
882
|
i += 2
|
813
883
|
elsif slice =~ quote_characters_regexp # find new "
|
814
884
|
quote = $&
|
885
|
+
closing_quote = /(?!\\)#{Regexp.escape(quote)}/
|
886
|
+
escaped_quote = /\\#{Regexp.escape(quote)}/
|
815
887
|
i += 1
|
816
888
|
elsif not quote and slice =~ word_break_regexp
|
817
889
|
rest = $'
|
@@ -839,11 +911,7 @@ class Reline::LineEditor
|
|
839
911
|
else
|
840
912
|
temp_buffer[@line_index] = @line
|
841
913
|
end
|
842
|
-
|
843
|
-
@confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n")
|
844
|
-
else
|
845
|
-
false
|
846
|
-
end
|
914
|
+
@confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n")
|
847
915
|
end
|
848
916
|
|
849
917
|
def insert_text(text)
|
@@ -1697,8 +1765,8 @@ class Reline::LineEditor
|
|
1697
1765
|
end
|
1698
1766
|
|
1699
1767
|
private def ed_delete_next_char(key, arg: 1)
|
1700
|
-
|
1701
|
-
|
1768
|
+
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
1769
|
+
unless @line.empty? || byte_size == 0
|
1702
1770
|
@line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
|
1703
1771
|
copy_for_vi(mbchar)
|
1704
1772
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
data/lib/reline/version.rb
CHANGED
data/lib/reline/windows.rb
CHANGED
@@ -51,7 +51,9 @@ class Reline::Windows
|
|
51
51
|
|
52
52
|
VK_MENU = 0x12
|
53
53
|
VK_SHIFT = 0x10
|
54
|
+
STD_INPUT_HANDLE = -10
|
54
55
|
STD_OUTPUT_HANDLE = -11
|
56
|
+
WINDOW_BUFFER_SIZE_EVENT = 0x04
|
55
57
|
@@getwch = Win32API.new('msvcrt', '_getwch', [], 'I')
|
56
58
|
@@kbhit = Win32API.new('msvcrt', '_kbhit', [], 'I')
|
57
59
|
@@GetKeyState = Win32API.new('user32', 'GetKeyState', ['L'], 'L')
|
@@ -61,6 +63,9 @@ class Reline::Windows
|
|
61
63
|
@@FillConsoleOutputCharacter = Win32API.new('kernel32', 'FillConsoleOutputCharacter', ['L', 'L', 'L', 'L', 'P'], 'L')
|
62
64
|
@@ScrollConsoleScreenBuffer = Win32API.new('kernel32', 'ScrollConsoleScreenBuffer', ['L', 'P', 'P', 'L', 'P'], 'L')
|
63
65
|
@@hConsoleHandle = @@GetStdHandle.call(STD_OUTPUT_HANDLE)
|
66
|
+
@@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE)
|
67
|
+
@@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L')
|
68
|
+
@@ReadConsoleInput = Win32API.new('kernel32', 'ReadConsoleInput', ['L', 'P', 'L', 'P'], 'L')
|
64
69
|
@@buf = []
|
65
70
|
|
66
71
|
def self.getwch
|
@@ -81,6 +86,17 @@ class Reline::Windows
|
|
81
86
|
end
|
82
87
|
|
83
88
|
def self.getc
|
89
|
+
num_of_events = 0.chr * 8
|
90
|
+
while @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) != 0 and num_of_events.unpack('L').first > 0
|
91
|
+
input_record = 0.chr * 18
|
92
|
+
read_event = 0.chr * 4
|
93
|
+
if @@ReadConsoleInput.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
|
94
|
+
event = input_record[0, 2].unpack('s*').first
|
95
|
+
if event == WINDOW_BUFFER_SIZE_EVENT
|
96
|
+
@@winch_handler.()
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
84
100
|
unless @@buf.empty?
|
85
101
|
return @@buf.shift
|
86
102
|
end
|
@@ -122,16 +138,16 @@ class Reline::Windows
|
|
122
138
|
end
|
123
139
|
|
124
140
|
def self.get_screen_size
|
125
|
-
csbi = 0.chr *
|
141
|
+
csbi = 0.chr * 22
|
126
142
|
@@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)
|
127
143
|
csbi[0, 4].unpack('SS').reverse
|
128
144
|
end
|
129
145
|
|
130
146
|
def self.cursor_pos
|
131
|
-
csbi = 0.chr *
|
147
|
+
csbi = 0.chr * 22
|
132
148
|
@@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)
|
133
149
|
x = csbi[4, 2].unpack('s*').first
|
134
|
-
y = csbi[6,
|
150
|
+
y = csbi[6, 2].unpack('s*').first
|
135
151
|
Reline::CursorPos.new(x, y)
|
136
152
|
end
|
137
153
|
|
@@ -181,6 +197,10 @@ class Reline::Windows
|
|
181
197
|
raise NotImplementedError
|
182
198
|
end
|
183
199
|
|
200
|
+
def self.set_winch_handler(&handler)
|
201
|
+
@@winch_handler = handler
|
202
|
+
end
|
203
|
+
|
184
204
|
def self.prep
|
185
205
|
# do nothing
|
186
206
|
nil
|
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.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aycabta
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-09-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -98,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
98
|
- !ruby/object:Gem::Version
|
99
99
|
version: '0'
|
100
100
|
requirements: []
|
101
|
-
rubygems_version: 3.0.
|
101
|
+
rubygems_version: 3.0.6
|
102
102
|
signing_key:
|
103
103
|
specification_version: 4
|
104
104
|
summary: Alternative GNU Readline or Editline implementation by pure Ruby.
|