reline 0.0.2 → 0.0.7
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 +28 -20
- data/lib/reline/ansi.rb +22 -11
- data/lib/reline/general_io.rb +1 -1
- data/lib/reline/key_actor/vi_command.rb +1 -1
- data/lib/reline/key_actor/vi_insert.rb +1 -1
- data/lib/reline/key_stroke.rb +1 -1
- data/lib/reline/line_editor.rb +133 -96
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +86 -62
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 985d3ce68260542cce79b3d3291c29ca30ee82250e50089c2de7bb16f60d755d
|
4
|
+
data.tar.gz: d3444d9e894a66fd2a0e5be4fddd5d36b9b254f0aaff7ed1d590fec26c13b3f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9a5651134aeb6d0eba7e33b4fa6e85c2ac43677432a7b94e8aa24d1c9f05326126c81669aa297f9cb03bea7f92c2ee8600364205c8a8eafa7c6185441472f9d1
|
7
|
+
data.tar.gz: 63cc8b0b0b936e8ac1a490c9a1c849631b2c4c33c520eb2dd666b9b93b641417f3640542067de4f64ff2e28fd4dd883ed3821c7704bb82a64485ee52d5273c3b
|
data/lib/reline.rb
CHANGED
@@ -16,12 +16,6 @@ module Reline
|
|
16
16
|
CursorPos = Struct.new(:x, :y)
|
17
17
|
|
18
18
|
class Core
|
19
|
-
if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
20
|
-
IS_WINDOWS = true
|
21
|
-
else
|
22
|
-
IS_WINDOWS = false
|
23
|
-
end
|
24
|
-
|
25
19
|
ATTR_READER_NAMES = %i(
|
26
20
|
completion_append_character
|
27
21
|
basic_word_break_characters
|
@@ -90,22 +84,22 @@ module Reline
|
|
90
84
|
end
|
91
85
|
|
92
86
|
def completion_proc=(p)
|
93
|
-
raise ArgumentError unless p.
|
87
|
+
raise ArgumentError unless p.respond_to?(:call)
|
94
88
|
@completion_proc = p
|
95
89
|
end
|
96
90
|
|
97
91
|
def output_modifier_proc=(p)
|
98
|
-
raise ArgumentError unless p.
|
92
|
+
raise ArgumentError unless p.respond_to?(:call)
|
99
93
|
@output_modifier_proc = p
|
100
94
|
end
|
101
95
|
|
102
96
|
def prompt_proc=(p)
|
103
|
-
raise ArgumentError unless p.
|
97
|
+
raise ArgumentError unless p.respond_to?(:call)
|
104
98
|
@prompt_proc = p
|
105
99
|
end
|
106
100
|
|
107
101
|
def auto_indent_proc=(p)
|
108
|
-
raise ArgumentError unless p.
|
102
|
+
raise ArgumentError unless p.respond_to?(:call)
|
109
103
|
@auto_indent_proc = p
|
110
104
|
end
|
111
105
|
|
@@ -114,7 +108,7 @@ module Reline
|
|
114
108
|
end
|
115
109
|
|
116
110
|
def dig_perfect_match_proc=(p)
|
117
|
-
raise ArgumentError unless p.
|
111
|
+
raise ArgumentError unless p.respond_to?(:call)
|
118
112
|
@dig_perfect_match_proc = p
|
119
113
|
end
|
120
114
|
|
@@ -166,7 +160,7 @@ module Reline
|
|
166
160
|
inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
|
167
161
|
|
168
162
|
whole_buffer = line_editor.whole_buffer.dup
|
169
|
-
whole_buffer.taint
|
163
|
+
whole_buffer.taint if RUBY_VERSION < '2.7'
|
170
164
|
if add_hist and whole_buffer and whole_buffer.chomp.size > 0
|
171
165
|
Reline::HISTORY << whole_buffer
|
172
166
|
end
|
@@ -179,7 +173,7 @@ module Reline
|
|
179
173
|
inner_readline(prompt, add_hist, false)
|
180
174
|
|
181
175
|
line = line_editor.line.dup
|
182
|
-
line.taint
|
176
|
+
line.taint if RUBY_VERSION < '2.7'
|
183
177
|
if add_hist and line and line.chomp.size > 0
|
184
178
|
Reline::HISTORY << line.chomp
|
185
179
|
end
|
@@ -260,7 +254,10 @@ module Reline
|
|
260
254
|
result = key_stroke.match_status(buffer)
|
261
255
|
case result
|
262
256
|
when :matched
|
263
|
-
|
257
|
+
expanded = key_stroke.expand(buffer).map{ |expanded_c|
|
258
|
+
Reline::Key.new(expanded_c, expanded_c, false)
|
259
|
+
}
|
260
|
+
block.(expanded)
|
264
261
|
break
|
265
262
|
when :matching
|
266
263
|
if buffer.size == 1
|
@@ -287,16 +284,19 @@ module Reline
|
|
287
284
|
end
|
288
285
|
when :unmatched
|
289
286
|
if buffer.size == 1 and c == "\e".ord
|
290
|
-
read_escaped_key(keyseq_timeout,
|
287
|
+
read_escaped_key(keyseq_timeout, c, block)
|
291
288
|
else
|
292
|
-
|
289
|
+
expanded = buffer.map{ |expanded_c|
|
290
|
+
Reline::Key.new(expanded_c, expanded_c, false)
|
291
|
+
}
|
292
|
+
block.(expanded)
|
293
293
|
end
|
294
294
|
break
|
295
295
|
end
|
296
296
|
end
|
297
297
|
end
|
298
298
|
|
299
|
-
private def read_escaped_key(keyseq_timeout,
|
299
|
+
private def read_escaped_key(keyseq_timeout, c, block)
|
300
300
|
begin
|
301
301
|
escaped_c = nil
|
302
302
|
Timeout.timeout(keyseq_timeout / 1000.0) {
|
@@ -319,7 +319,7 @@ module Reline
|
|
319
319
|
|
320
320
|
private def may_req_ambiguous_char_width
|
321
321
|
@ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
|
322
|
-
return if
|
322
|
+
return if ambiguous_width
|
323
323
|
Reline::IOGate.move_cursor_column(0)
|
324
324
|
print "\u{25bd}"
|
325
325
|
@ambiguous_width = Reline::IOGate.cursor_pos.x
|
@@ -342,6 +342,7 @@ module Reline
|
|
342
342
|
def_single_delegators :core, :vi_editing_mode, :emacs_editing_mode
|
343
343
|
def_single_delegators :core, :readline
|
344
344
|
def_instance_delegators self, :readline
|
345
|
+
private :readline
|
345
346
|
|
346
347
|
|
347
348
|
#--------------------------------------------------------
|
@@ -369,6 +370,7 @@ module Reline
|
|
369
370
|
|
370
371
|
def_single_delegators :core, :readmultiline
|
371
372
|
def_instance_delegators self, :readmultiline
|
373
|
+
private :readmultiline
|
372
374
|
|
373
375
|
def self.core
|
374
376
|
@core ||= Core.new { |core|
|
@@ -392,9 +394,15 @@ module Reline
|
|
392
394
|
HISTORY = History.new(core.config)
|
393
395
|
end
|
394
396
|
|
395
|
-
if
|
397
|
+
if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
396
398
|
require 'reline/windows'
|
397
|
-
Reline::
|
399
|
+
if Reline::Windows.get_screen_size == [0, 0]
|
400
|
+
# Maybe Mintty on Cygwin
|
401
|
+
require 'reline/ansi'
|
402
|
+
Reline::IOGate = Reline::ANSI
|
403
|
+
else
|
404
|
+
Reline::IOGate = Reline::Windows
|
405
|
+
end
|
398
406
|
else
|
399
407
|
require 'reline/ansi'
|
400
408
|
Reline::IOGate = Reline::ANSI
|
data/lib/reline/ansi.rb
CHANGED
@@ -7,7 +7,11 @@ class Reline::ANSI
|
|
7
7
|
[27, 91, 51, 126] => :key_delete, # Del
|
8
8
|
[27, 91, 49, 126] => :ed_move_to_beg, # Home
|
9
9
|
[27, 91, 52, 126] => :ed_move_to_end, # End
|
10
|
-
|
10
|
+
[27, 91, 72] => :ed_move_to_beg, # Home
|
11
|
+
[27, 91, 70] => :ed_move_to_end, # End
|
12
|
+
[27, 32] => :em_set_mark, # M-<space>
|
13
|
+
[24, 24] => :em_exchange_mark, # C-x C-x TODO also add Windows
|
14
|
+
}
|
11
15
|
|
12
16
|
@@input = STDIN
|
13
17
|
def self.input=(val)
|
@@ -24,20 +28,22 @@ class Reline::ANSI
|
|
24
28
|
unless @@buf.empty?
|
25
29
|
return @@buf.shift
|
26
30
|
end
|
27
|
-
|
28
|
-
loop do
|
29
|
-
result = select([@@input], [], [], 0.1)
|
30
|
-
next if result.nil?
|
31
|
-
c = @@input.read(1)
|
32
|
-
break
|
33
|
-
end
|
34
|
-
c&.ord
|
31
|
+
@@input.getbyte
|
35
32
|
end
|
36
33
|
|
37
34
|
def self.ungetc(c)
|
38
35
|
@@buf.unshift(c)
|
39
36
|
end
|
40
37
|
|
38
|
+
def self.retrieve_keybuffer
|
39
|
+
result = select([@@input], [], [], 0.001)
|
40
|
+
return if result.nil?
|
41
|
+
str = @@input.read_nonblock(1024)
|
42
|
+
str.bytes.each do |c|
|
43
|
+
@@buf.push(c)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
41
47
|
def self.get_screen_size
|
42
48
|
@@input.winsize
|
43
49
|
rescue Errno::ENOTTY
|
@@ -112,12 +118,17 @@ class Reline::ANSI
|
|
112
118
|
end
|
113
119
|
|
114
120
|
def self.prep
|
121
|
+
retrieve_keybuffer
|
115
122
|
int_handle = Signal.trap('INT', 'IGNORE')
|
116
123
|
otio = `stty -g`.chomp
|
117
124
|
setting = ' -echo -icrnl cbreak'
|
118
|
-
|
125
|
+
stty = `stty -a`
|
126
|
+
if /-parenb\b/ =~ stty
|
119
127
|
setting << ' pass8'
|
120
128
|
end
|
129
|
+
if /\bdsusp *=/ =~ stty
|
130
|
+
setting << ' dsusp undef'
|
131
|
+
end
|
121
132
|
setting << ' -ixoff'
|
122
133
|
`stty #{setting}`
|
123
134
|
Signal.trap('INT', int_handle)
|
@@ -126,7 +137,7 @@ class Reline::ANSI
|
|
126
137
|
|
127
138
|
def self.deprep(otio)
|
128
139
|
int_handle = Signal.trap('INT', 'IGNORE')
|
129
|
-
|
140
|
+
system("stty #{otio}", err: File::NULL)
|
130
141
|
Signal.trap('INT', int_handle)
|
131
142
|
Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
|
132
143
|
end
|
data/lib/reline/general_io.rb
CHANGED
data/lib/reline/key_stroke.rb
CHANGED
@@ -32,7 +32,7 @@ class Reline::KeyStroke
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def expand(input)
|
35
|
-
lhs = key_mapping.keys.select { |
|
35
|
+
lhs = key_mapping.keys.select { |item| input.start_with? item }.sort_by(&:size).reverse.first
|
36
36
|
return input unless lhs
|
37
37
|
rhs = key_mapping[lhs]
|
38
38
|
|
data/lib/reline/line_editor.rb
CHANGED
@@ -60,14 +60,35 @@ class Reline::LineEditor
|
|
60
60
|
reset_variables
|
61
61
|
end
|
62
62
|
|
63
|
+
private def check_multiline_prompt(buffer, prompt)
|
64
|
+
if @vi_arg
|
65
|
+
prompt = "(arg: #{@vi_arg}) "
|
66
|
+
@rerender_all = true
|
67
|
+
elsif @searching_prompt
|
68
|
+
prompt = @searching_prompt
|
69
|
+
@rerender_all = true
|
70
|
+
else
|
71
|
+
prompt = @prompt
|
72
|
+
end
|
73
|
+
if @prompt_proc
|
74
|
+
prompt_list = @prompt_proc.(buffer)
|
75
|
+
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
76
|
+
prompt = prompt_list[@line_index]
|
77
|
+
prompt_width = calculate_width(prompt, true)
|
78
|
+
[prompt, prompt_width, prompt_list]
|
79
|
+
else
|
80
|
+
prompt_width = calculate_width(prompt, true)
|
81
|
+
[prompt, prompt_width, nil]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
63
85
|
def reset(prompt = '', encoding = Encoding.default_external)
|
64
86
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
65
87
|
@screen_size = Reline::IOGate.get_screen_size
|
66
88
|
reset_variables(prompt, encoding)
|
67
89
|
@old_trap = Signal.trap('SIGINT') {
|
68
|
-
scroll_down(@highest_in_all - @first_line_started_from)
|
69
|
-
Reline::IOGate.move_cursor_column(0)
|
70
90
|
@old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
|
91
|
+
raise Interrupt
|
71
92
|
}
|
72
93
|
Reline::IOGate.set_winch_handler do
|
73
94
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
@@ -77,28 +98,9 @@ class Reline::LineEditor
|
|
77
98
|
@rerender_all = true
|
78
99
|
rerender
|
79
100
|
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
101
|
back = 0
|
94
102
|
new_buffer = whole_lines
|
95
|
-
prompt_list =
|
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
|
103
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
102
104
|
new_buffer.each_with_index do |line, index|
|
103
105
|
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
104
106
|
width = prompt_width + calculate_width(line)
|
@@ -111,9 +113,7 @@ class Reline::LineEditor
|
|
111
113
|
if @line_index.zero?
|
112
114
|
0
|
113
115
|
else
|
114
|
-
@buffer_of_lines[0..(@line_index - 1)]
|
115
|
-
result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
|
116
|
-
}
|
116
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
|
117
117
|
end
|
118
118
|
if @prompt_proc
|
119
119
|
prompt = prompt_list[@line_index]
|
@@ -138,6 +138,7 @@ class Reline::LineEditor
|
|
138
138
|
|
139
139
|
def reset_variables(prompt = '', encoding = Encoding.default_external)
|
140
140
|
@prompt = prompt
|
141
|
+
@mark_pointer = nil
|
141
142
|
@encoding = encoding
|
142
143
|
@is_multiline = false
|
143
144
|
@finished = false
|
@@ -186,6 +187,16 @@ class Reline::LineEditor
|
|
186
187
|
@is_multiline = false
|
187
188
|
end
|
188
189
|
|
190
|
+
private def calculate_height_by_lines(lines, prompt_list)
|
191
|
+
result = 0
|
192
|
+
lines.each_with_index { |line, i|
|
193
|
+
prompt = ''
|
194
|
+
prompt = prompt_list[i] if prompt_list and prompt_list[i]
|
195
|
+
result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
|
196
|
+
}
|
197
|
+
result
|
198
|
+
end
|
199
|
+
|
189
200
|
private def insert_new_line(cursor_line, next_line)
|
190
201
|
@line = cursor_line
|
191
202
|
@buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding))
|
@@ -306,36 +317,18 @@ class Reline::LineEditor
|
|
306
317
|
@menu_info.list.each do |item|
|
307
318
|
Reline::IOGate.move_cursor_column(0)
|
308
319
|
@output.print item
|
320
|
+
@output.flush
|
309
321
|
scroll_down(1)
|
310
322
|
end
|
311
323
|
scroll_down(@highest_in_all - 1)
|
312
324
|
move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
|
313
325
|
@menu_info = nil
|
314
326
|
end
|
315
|
-
|
316
|
-
if @vi_arg
|
317
|
-
prompt = "(arg: #{@vi_arg}) "
|
318
|
-
prompt_width = calculate_width(prompt)
|
319
|
-
special_prompt = prompt
|
320
|
-
elsif @searching_prompt
|
321
|
-
prompt = @searching_prompt
|
322
|
-
prompt_width = calculate_width(prompt)
|
323
|
-
special_prompt = prompt
|
324
|
-
else
|
325
|
-
prompt = @prompt
|
326
|
-
prompt_width = calculate_width(prompt, true)
|
327
|
-
end
|
327
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
328
328
|
if @cleared
|
329
329
|
Reline::IOGate.clear_screen
|
330
330
|
@cleared = false
|
331
331
|
back = 0
|
332
|
-
prompt_list = nil
|
333
|
-
if @prompt_proc
|
334
|
-
prompt_list = @prompt_proc.(whole_lines)
|
335
|
-
prompt_list[@line_index] = special_prompt if special_prompt
|
336
|
-
prompt = prompt_list[@line_index]
|
337
|
-
prompt_width = calculate_width(prompt, true)
|
338
|
-
end
|
339
332
|
modify_lines(whole_lines).each_with_index do |line, index|
|
340
333
|
if @prompt_proc
|
341
334
|
pr = prompt_list[index]
|
@@ -361,16 +354,8 @@ class Reline::LineEditor
|
|
361
354
|
else
|
362
355
|
new_lines = whole_lines
|
363
356
|
end
|
364
|
-
prompt_list =
|
365
|
-
|
366
|
-
prompt_list = @prompt_proc.(new_lines)
|
367
|
-
prompt_list[@line_index] = special_prompt if special_prompt
|
368
|
-
prompt = prompt_list[@line_index]
|
369
|
-
prompt_width = calculate_width(prompt, true)
|
370
|
-
end
|
371
|
-
all_height = new_lines.inject(0) { |result, line|
|
372
|
-
result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
|
373
|
-
}
|
357
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
358
|
+
all_height = calculate_height_by_lines(new_lines, prompt_list)
|
374
359
|
diff = all_height - @highest_in_all
|
375
360
|
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
376
361
|
if diff > 0
|
@@ -410,9 +395,7 @@ class Reline::LineEditor
|
|
410
395
|
if @line_index.zero?
|
411
396
|
0
|
412
397
|
else
|
413
|
-
@buffer_of_lines[0..(@line_index - 1)]
|
414
|
-
result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
|
415
|
-
}
|
398
|
+
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
|
416
399
|
end
|
417
400
|
if @prompt_proc
|
418
401
|
prompt = prompt_list[@line_index]
|
@@ -431,13 +414,7 @@ class Reline::LineEditor
|
|
431
414
|
Reline::IOGate.move_cursor_column(0)
|
432
415
|
back = 0
|
433
416
|
new_buffer = whole_lines
|
434
|
-
prompt_list =
|
435
|
-
if @prompt_proc
|
436
|
-
prompt_list = @prompt_proc.(new_buffer)
|
437
|
-
prompt_list[@line_index] = special_prompt if special_prompt
|
438
|
-
prompt = prompt_list[@line_index]
|
439
|
-
prompt_width = calculate_width(prompt, true)
|
440
|
-
end
|
417
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
441
418
|
new_buffer.each_with_index do |line, index|
|
442
419
|
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
443
420
|
width = prompt_width + calculate_width(line)
|
@@ -477,9 +454,7 @@ class Reline::LineEditor
|
|
477
454
|
if @line_index.zero?
|
478
455
|
0
|
479
456
|
else
|
480
|
-
new_buffer[0..(@line_index - 1)]
|
481
|
-
result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
|
482
|
-
}
|
457
|
+
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list)
|
483
458
|
end
|
484
459
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
485
460
|
move_cursor_down(@first_line_started_from + @started_from)
|
@@ -489,13 +464,7 @@ class Reline::LineEditor
|
|
489
464
|
end
|
490
465
|
line = modify_lines(whole_lines)[@line_index]
|
491
466
|
if @is_multiline
|
492
|
-
prompt_list =
|
493
|
-
if @prompt_proc
|
494
|
-
prompt_list = @prompt_proc.(whole_lines)
|
495
|
-
prompt_list[@line_index] = special_prompt if special_prompt
|
496
|
-
prompt = prompt_list[@line_index]
|
497
|
-
prompt_width = calculate_width(prompt, true)
|
498
|
-
end
|
467
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
499
468
|
if finished?
|
500
469
|
# Always rerender on finish because output_modifier_proc may return a different output.
|
501
470
|
render_partial(prompt, prompt_width, line)
|
@@ -541,6 +510,7 @@ class Reline::LineEditor
|
|
541
510
|
next
|
542
511
|
end
|
543
512
|
@output.print line
|
513
|
+
@output.flush
|
544
514
|
if @first_prompt
|
545
515
|
@first_prompt = false
|
546
516
|
@pre_input_hook&.call
|
@@ -683,9 +653,9 @@ class Reline::LineEditor
|
|
683
653
|
else
|
684
654
|
old_waiting_proc = @waiting_proc
|
685
655
|
old_waiting_operator_proc = @waiting_operator_proc
|
686
|
-
@waiting_proc = proc { |
|
656
|
+
@waiting_proc = proc { |k|
|
687
657
|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
|
688
|
-
old_waiting_proc.(
|
658
|
+
old_waiting_proc.(k)
|
689
659
|
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
|
690
660
|
@cursor, @byte_pointer = old_cursor, old_byte_pointer
|
691
661
|
@waiting_operator_proc.(cursor_diff, byte_pointer_diff)
|
@@ -796,7 +766,7 @@ class Reline::LineEditor
|
|
796
766
|
end
|
797
767
|
|
798
768
|
def input_key(key)
|
799
|
-
if key.
|
769
|
+
if key.char.nil?
|
800
770
|
if @first_char
|
801
771
|
@line = nil
|
802
772
|
end
|
@@ -836,6 +806,26 @@ class Reline::LineEditor
|
|
836
806
|
|
837
807
|
private def process_auto_indent
|
838
808
|
return if not @check_new_auto_indent and @previous_line_index # move cursor up or down
|
809
|
+
if @check_new_auto_indent and @previous_line_index and @previous_line_index > 0 and @line_index > @previous_line_index
|
810
|
+
# Fix indent of a line when a newline is inserted to the next
|
811
|
+
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
812
|
+
new_indent = @auto_indent_proc.(new_lines[0..-3].push(''), @line_index - 1, 0, true)
|
813
|
+
md = @line.match(/\A */)
|
814
|
+
prev_indent = md[0].count(' ')
|
815
|
+
@line = ' ' * new_indent + @line.lstrip
|
816
|
+
|
817
|
+
new_indent = nil
|
818
|
+
(new_lines[-2].size + 1).times do |n|
|
819
|
+
result = @auto_indent_proc.(new_lines[0..-2], @line_index - 1, n, false)
|
820
|
+
if result
|
821
|
+
new_indent = result
|
822
|
+
break
|
823
|
+
end
|
824
|
+
end
|
825
|
+
if new_indent&.>= 0
|
826
|
+
@line = ' ' * new_indent + @line.lstrip
|
827
|
+
end
|
828
|
+
end
|
839
829
|
if @previous_line_index
|
840
830
|
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
841
831
|
else
|
@@ -1027,8 +1017,8 @@ class Reline::LineEditor
|
|
1027
1017
|
end
|
1028
1018
|
width
|
1029
1019
|
else
|
1030
|
-
str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |
|
1031
|
-
|
1020
|
+
str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |w, gc|
|
1021
|
+
w + Reline::Unicode.get_mbchar_width(gc)
|
1032
1022
|
}
|
1033
1023
|
end
|
1034
1024
|
end
|
@@ -1049,6 +1039,8 @@ class Reline::LineEditor
|
|
1049
1039
|
end
|
1050
1040
|
end
|
1051
1041
|
|
1042
|
+
private def ed_unassigned(key) end # do nothing
|
1043
|
+
|
1052
1044
|
private def ed_insert(key)
|
1053
1045
|
if key.instance_of?(String)
|
1054
1046
|
width = Reline::Unicode.get_mbchar_width(key)
|
@@ -1153,7 +1145,11 @@ class Reline::LineEditor
|
|
1153
1145
|
alias_method :end_of_line, :ed_move_to_end
|
1154
1146
|
|
1155
1147
|
private def ed_search_prev_history(key)
|
1156
|
-
|
1148
|
+
if @is_multiline
|
1149
|
+
@line_backup_in_history = whole_buffer
|
1150
|
+
else
|
1151
|
+
@line_backup_in_history = @line
|
1152
|
+
end
|
1157
1153
|
searcher = Fiber.new do
|
1158
1154
|
search_word = String.new(encoding: @encoding)
|
1159
1155
|
multibyte_buf = String.new(encoding: 'ASCII-8BIT')
|
@@ -1188,18 +1184,32 @@ class Reline::LineEditor
|
|
1188
1184
|
end
|
1189
1185
|
end
|
1190
1186
|
if hit
|
1191
|
-
@
|
1192
|
-
|
1187
|
+
if @is_multiline
|
1188
|
+
@buffer_of_lines = hit.split("\n")
|
1189
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1190
|
+
@line_index = @buffer_of_lines.size - 1
|
1191
|
+
@line = @buffer_of_lines.last
|
1192
|
+
@rerender_all = true
|
1193
|
+
@searching_prompt = "(reverse-i-search)`%s'" % [search_word]
|
1194
|
+
else
|
1195
|
+
@line = hit
|
1196
|
+
@searching_prompt = "(reverse-i-search)`%s': %s" % [search_word, hit]
|
1197
|
+
end
|
1193
1198
|
last_hit = hit
|
1194
1199
|
else
|
1195
|
-
@
|
1200
|
+
if @is_multiline
|
1201
|
+
@rerender_all = true
|
1202
|
+
@searching_prompt = "(failed reverse-i-search)`%s'" % [search_word]
|
1203
|
+
else
|
1204
|
+
@searching_prompt = "(failed reverse-i-search)`%s': %s" % [search_word, last_hit]
|
1205
|
+
end
|
1196
1206
|
end
|
1197
1207
|
end
|
1198
1208
|
end
|
1199
1209
|
searcher.resume
|
1200
1210
|
@searching_prompt = "(reverse-i-search)`': "
|
1201
|
-
@waiting_proc = ->(
|
1202
|
-
case
|
1211
|
+
@waiting_proc = ->(k) {
|
1212
|
+
case k
|
1203
1213
|
when "\C-j".ord, "\C-?".ord
|
1204
1214
|
if @history_pointer
|
1205
1215
|
@line = Reline::HISTORY[@history_pointer]
|
@@ -1219,14 +1229,25 @@ class Reline::LineEditor
|
|
1219
1229
|
@cursor_max = calculate_width(@line)
|
1220
1230
|
@cursor = @byte_pointer = 0
|
1221
1231
|
else
|
1222
|
-
chr =
|
1223
|
-
if chr.match?(/[[:print:]]/)
|
1224
|
-
searcher.resume(
|
1232
|
+
chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
|
1233
|
+
if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == 127
|
1234
|
+
searcher.resume(k)
|
1225
1235
|
else
|
1226
1236
|
if @history_pointer
|
1227
|
-
|
1237
|
+
line = Reline::HISTORY[@history_pointer]
|
1228
1238
|
else
|
1229
|
-
|
1239
|
+
line = @line_backup_in_history
|
1240
|
+
end
|
1241
|
+
if @is_multiline
|
1242
|
+
@line_backup_in_history = whole_buffer
|
1243
|
+
@buffer_of_lines = line.split("\n")
|
1244
|
+
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
1245
|
+
@line_index = @buffer_of_lines.size - 1
|
1246
|
+
@line = @buffer_of_lines.last
|
1247
|
+
@rerender_all = true
|
1248
|
+
else
|
1249
|
+
@line_backup_in_history = @line
|
1250
|
+
@line = line
|
1230
1251
|
end
|
1231
1252
|
@searching_prompt = nil
|
1232
1253
|
@waiting_proc = nil
|
@@ -1279,7 +1300,7 @@ class Reline::LineEditor
|
|
1279
1300
|
@line = Reline::HISTORY[@history_pointer]
|
1280
1301
|
end
|
1281
1302
|
end
|
1282
|
-
if @config.editing_mode_is?(:emacs)
|
1303
|
+
if @config.editing_mode_is?(:emacs, :vi_insert)
|
1283
1304
|
@cursor_max = @cursor = calculate_width(@line)
|
1284
1305
|
@byte_pointer = @line.bytesize
|
1285
1306
|
elsif @config.editing_mode_is?(:vi_command)
|
@@ -1326,7 +1347,7 @@ class Reline::LineEditor
|
|
1326
1347
|
end
|
1327
1348
|
end
|
1328
1349
|
@line = '' unless @line
|
1329
|
-
if @config.editing_mode_is?(:emacs)
|
1350
|
+
if @config.editing_mode_is?(:emacs, :vi_insert)
|
1330
1351
|
@cursor_max = @cursor = calculate_width(@line)
|
1331
1352
|
@byte_pointer = @line.bytesize
|
1332
1353
|
elsif @config.editing_mode_is?(:vi_command)
|
@@ -1863,13 +1884,13 @@ class Reline::LineEditor
|
|
1863
1884
|
end
|
1864
1885
|
|
1865
1886
|
private def vi_replace_char(key, arg: 1)
|
1866
|
-
@waiting_proc = ->(
|
1887
|
+
@waiting_proc = ->(k) {
|
1867
1888
|
if arg == 1
|
1868
1889
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
1869
1890
|
before = @line.byteslice(0, @byte_pointer)
|
1870
1891
|
remaining_point = @byte_pointer + byte_size
|
1871
1892
|
after = @line.byteslice(remaining_point, @line.size - remaining_point)
|
1872
|
-
@line = before +
|
1893
|
+
@line = before + k.chr + after
|
1873
1894
|
@cursor_max = calculate_width(@line)
|
1874
1895
|
@waiting_proc = nil
|
1875
1896
|
elsif arg > 1
|
@@ -1880,7 +1901,7 @@ class Reline::LineEditor
|
|
1880
1901
|
before = @line.byteslice(0, @byte_pointer)
|
1881
1902
|
remaining_point = @byte_pointer + byte_size
|
1882
1903
|
after = @line.byteslice(remaining_point, @line.size - remaining_point)
|
1883
|
-
replaced =
|
1904
|
+
replaced = k.chr * arg
|
1884
1905
|
@line = before + replaced + after
|
1885
1906
|
@byte_pointer += replaced.bytesize
|
1886
1907
|
@cursor += calculate_width(replaced)
|
@@ -1941,4 +1962,20 @@ class Reline::LineEditor
|
|
1941
1962
|
arg -= 1
|
1942
1963
|
vi_join_lines(key, arg: arg) if arg > 0
|
1943
1964
|
end
|
1965
|
+
|
1966
|
+
private def em_set_mark(key)
|
1967
|
+
@mark_pointer = [@byte_pointer, @line_index]
|
1968
|
+
end
|
1969
|
+
alias_method :set_mark, :em_set_mark
|
1970
|
+
|
1971
|
+
private def em_exchange_mark(key)
|
1972
|
+
new_pointer = [@byte_pointer, @line_index]
|
1973
|
+
@previous_line_index = @line_index
|
1974
|
+
@byte_pointer, @line_index = @mark_pointer
|
1975
|
+
@byte_pointer, @line_index = @mark_pointer
|
1976
|
+
@cursor = calculate_width(@line.byteslice(0, @byte_pointer))
|
1977
|
+
@cursor_max = calculate_width(@line)
|
1978
|
+
@mark_pointer = new_pointer
|
1979
|
+
end
|
1980
|
+
alias_method :exchange_point_and_mark, :em_exchange_mark
|
1944
1981
|
end
|
data/lib/reline/version.rb
CHANGED
data/lib/reline/windows.rb
CHANGED
@@ -9,47 +9,61 @@ class Reline::Windows
|
|
9
9
|
[224, 83] => :key_delete, # Del
|
10
10
|
[224, 71] => :ed_move_to_beg, # Home
|
11
11
|
[224, 79] => :ed_move_to_end, # End
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
12
|
+
[ 0, 41] => :ed_unassigned, # input method on/off
|
13
|
+
[ 0, 72] => :ed_prev_history, # ↑
|
14
|
+
[ 0, 80] => :ed_next_history, # ↓
|
15
|
+
[ 0, 77] => :ed_next_char, # →
|
16
|
+
[ 0, 75] => :ed_prev_char, # ←
|
17
|
+
[ 0, 83] => :key_delete, # Del
|
18
|
+
[ 0, 71] => :ed_move_to_beg, # Home
|
19
|
+
[ 0, 79] => :ed_move_to_end # End
|
20
|
+
}
|
21
|
+
|
22
|
+
if defined? JRUBY_VERSION
|
23
|
+
require 'win32api'
|
24
|
+
else
|
25
|
+
class Win32API
|
26
|
+
DLL = {}
|
27
|
+
TYPEMAP = {"0" => Fiddle::TYPE_VOID, "S" => Fiddle::TYPE_VOIDP, "I" => Fiddle::TYPE_LONG}
|
28
|
+
POINTER_TYPE = Fiddle::SIZEOF_VOIDP == Fiddle::SIZEOF_LONG_LONG ? 'q*' : 'l!*'
|
29
|
+
|
30
|
+
WIN32_TYPES = "VPpNnLlIi"
|
31
|
+
DL_TYPES = "0SSI"
|
32
|
+
|
33
|
+
def initialize(dllname, func, import, export = "0", calltype = :stdcall)
|
34
|
+
@proto = [import].join.tr(WIN32_TYPES, DL_TYPES).sub(/^(.)0*$/, '\1')
|
35
|
+
import = @proto.chars.map {|win_type| TYPEMAP[win_type.tr(WIN32_TYPES, DL_TYPES)]}
|
36
|
+
export = TYPEMAP[export.tr(WIN32_TYPES, DL_TYPES)]
|
37
|
+
calltype = Fiddle::Importer.const_get(:CALL_TYPE_TO_ABI)[calltype]
|
38
|
+
|
39
|
+
handle = DLL[dllname] ||=
|
40
|
+
begin
|
41
|
+
Fiddle.dlopen(dllname)
|
42
|
+
rescue Fiddle::DLError
|
43
|
+
raise unless File.extname(dllname).empty?
|
44
|
+
Fiddle.dlopen(dllname + ".dll")
|
45
|
+
end
|
46
|
+
|
47
|
+
@func = Fiddle::Function.new(handle[func], import, export, calltype)
|
48
|
+
rescue Fiddle::DLError => e
|
49
|
+
raise LoadError, e.message, e.backtrace
|
50
|
+
end
|
40
51
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
52
|
+
def call(*args)
|
53
|
+
import = @proto.split("")
|
54
|
+
args.each_with_index do |x, i|
|
55
|
+
args[i], = [x == 0 ? nil : x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
|
56
|
+
args[i], = [x].pack("I").unpack("i") if import[i] == "I"
|
57
|
+
end
|
58
|
+
ret, = @func.call(*args)
|
59
|
+
return ret || 0
|
46
60
|
end
|
47
|
-
ret, = @func.call(*args)
|
48
|
-
return ret || 0
|
49
61
|
end
|
50
62
|
end
|
51
63
|
|
52
64
|
VK_MENU = 0x12
|
65
|
+
VK_LMENU = 0xA4
|
66
|
+
VK_CONTROL = 0x11
|
53
67
|
VK_SHIFT = 0x10
|
54
68
|
STD_INPUT_HANDLE = -10
|
55
69
|
STD_OUTPUT_HANDLE = -11
|
@@ -66,23 +80,33 @@ class Reline::Windows
|
|
66
80
|
@@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE)
|
67
81
|
@@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L')
|
68
82
|
@@ReadConsoleInput = Win32API.new('kernel32', 'ReadConsoleInput', ['L', 'P', 'L', 'P'], 'L')
|
69
|
-
@@
|
83
|
+
@@input_buf = []
|
84
|
+
@@output_buf = []
|
70
85
|
|
71
86
|
def self.getwch
|
87
|
+
unless @@input_buf.empty?
|
88
|
+
return @@input_buf.shift
|
89
|
+
end
|
72
90
|
while @@kbhit.call == 0
|
73
91
|
sleep(0.001)
|
74
92
|
end
|
75
|
-
result = []
|
76
93
|
until @@kbhit.call == 0
|
77
94
|
ret = @@getwch.call
|
95
|
+
if ret == 0 or ret == 0xE0
|
96
|
+
@@input_buf << ret
|
97
|
+
ret = @@getwch.call
|
98
|
+
@@input_buf << ret
|
99
|
+
return @@input_buf.shift
|
100
|
+
end
|
78
101
|
begin
|
79
|
-
|
102
|
+
bytes = ret.chr(Encoding::UTF_8).encode(Encoding.default_external).bytes
|
103
|
+
@@input_buf.push(*bytes)
|
80
104
|
rescue Encoding::UndefinedConversionError
|
81
|
-
|
82
|
-
|
105
|
+
@@input_buf << ret
|
106
|
+
@@input_buf << @@getwch.call if ret == 224
|
83
107
|
end
|
84
108
|
end
|
85
|
-
|
109
|
+
@@input_buf.shift
|
86
110
|
end
|
87
111
|
|
88
112
|
def self.getc
|
@@ -97,44 +121,44 @@ class Reline::Windows
|
|
97
121
|
end
|
98
122
|
end
|
99
123
|
end
|
100
|
-
unless @@
|
101
|
-
return @@
|
124
|
+
unless @@output_buf.empty?
|
125
|
+
return @@output_buf.shift
|
102
126
|
end
|
103
127
|
input = getwch
|
104
|
-
|
105
|
-
|
106
|
-
|
128
|
+
meta = (@@GetKeyState.call(VK_LMENU) & 0x80) != 0
|
129
|
+
control = (@@GetKeyState.call(VK_CONTROL) & 0x80) != 0
|
130
|
+
shift = (@@GetKeyState.call(VK_SHIFT) & 0x80) != 0
|
131
|
+
force_enter = !input.instance_of?(Array) && (control or shift) && input == 0x0D
|
132
|
+
if force_enter
|
107
133
|
# It's treated as Meta+Enter on Windows
|
108
|
-
@@
|
109
|
-
@@
|
110
|
-
|
111
|
-
|
112
|
-
else # single byte
|
113
|
-
case input[0]
|
134
|
+
@@output_buf.push("\e".ord)
|
135
|
+
@@output_buf.push(input)
|
136
|
+
else
|
137
|
+
case input
|
114
138
|
when 0x00
|
115
|
-
|
116
|
-
|
139
|
+
meta = false
|
140
|
+
@@output_buf.push(input)
|
117
141
|
input = getwch
|
118
|
-
@@
|
142
|
+
@@output_buf.push(*input)
|
119
143
|
when 0xE0
|
120
|
-
@@
|
144
|
+
@@output_buf.push(input)
|
121
145
|
input = getwch
|
122
|
-
@@
|
146
|
+
@@output_buf.push(*input)
|
123
147
|
when 0x03
|
124
|
-
@@
|
148
|
+
@@output_buf.push(input)
|
125
149
|
else
|
126
|
-
@@
|
150
|
+
@@output_buf.push(input)
|
127
151
|
end
|
128
152
|
end
|
129
|
-
if
|
153
|
+
if meta
|
130
154
|
"\e".ord
|
131
155
|
else
|
132
|
-
@@
|
156
|
+
@@output_buf.shift
|
133
157
|
end
|
134
158
|
end
|
135
159
|
|
136
160
|
def self.ungetc(c)
|
137
|
-
@@
|
161
|
+
@@output_buf.unshift(c)
|
138
162
|
end
|
139
163
|
|
140
164
|
def self.get_screen_size
|
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.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aycabta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|