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
data/lib/reline/ansi.rb
CHANGED
@@ -1,35 +1,75 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
class Reline::ANSI
|
2
|
+
RAW_KEYSTROKE_CONFIG = {
|
3
|
+
[27, 91, 65] => :ed_prev_history, # ↑
|
4
|
+
[27, 91, 66] => :ed_next_history, # ↓
|
5
|
+
[27, 91, 67] => :ed_next_char, # →
|
6
|
+
[27, 91, 68] => :ed_prev_char, # ←
|
7
|
+
[27, 91, 51, 126] => :key_delete, # Del
|
8
|
+
[27, 91, 49, 126] => :ed_move_to_beg, # Home
|
9
|
+
[27, 91, 52, 126] => :ed_move_to_end, # End
|
10
|
+
}.each_key(&:freeze).freeze
|
11
|
+
|
12
|
+
@@input = STDIN
|
13
|
+
def self.input=(val)
|
14
|
+
@@input = val
|
15
|
+
end
|
16
|
+
|
17
|
+
@@output = STDOUT
|
18
|
+
def self.output=(val)
|
19
|
+
@@output = val
|
20
|
+
end
|
21
|
+
|
22
|
+
@@buf = []
|
23
|
+
def self.getc
|
24
|
+
unless @@buf.empty?
|
25
|
+
return @@buf.shift
|
26
|
+
end
|
3
27
|
c = nil
|
4
|
-
|
5
|
-
|
6
|
-
result = select([$stdin], [], [], 0.1)
|
28
|
+
loop do
|
29
|
+
result = select([@@input], [], [], 0.1)
|
7
30
|
next if result.nil?
|
8
|
-
c =
|
31
|
+
c = @@input.read(1)
|
32
|
+
break
|
9
33
|
end
|
10
|
-
c
|
34
|
+
c&.ord
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.ungetc(c)
|
38
|
+
@@buf.unshift(c)
|
11
39
|
end
|
12
40
|
|
13
41
|
def self.get_screen_size
|
14
|
-
|
42
|
+
@@input.winsize
|
43
|
+
rescue Errno::ENOTTY
|
44
|
+
[24, 80]
|
15
45
|
end
|
16
46
|
|
17
47
|
def self.set_screen_size(rows, columns)
|
18
|
-
|
48
|
+
@@input.winsize = [rows, columns]
|
49
|
+
self
|
50
|
+
rescue Errno::ENOTTY
|
19
51
|
self
|
20
52
|
end
|
21
53
|
|
22
54
|
def self.cursor_pos
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
55
|
+
begin
|
56
|
+
res = ''
|
57
|
+
@@input.raw do |stdin|
|
58
|
+
@@output << "\e[6n"
|
59
|
+
@@output.flush
|
60
|
+
while (c = stdin.getc) != 'R'
|
61
|
+
res << c if c
|
62
|
+
end
|
29
63
|
end
|
64
|
+
m = res.match(/(?<row>\d+);(?<column>\d+)/)
|
65
|
+
column = m[:column].to_i - 1
|
66
|
+
row = m[:row].to_i - 1
|
67
|
+
rescue Errno::ENOTTY
|
68
|
+
buf = @@output.pread(@@output.pos, 0)
|
69
|
+
row = buf.count("\n")
|
70
|
+
column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
|
30
71
|
end
|
31
|
-
|
32
|
-
CursorPos.new(m[:column].to_i - 1, m[:row].to_i - 1)
|
72
|
+
Reline::CursorPos.new(column, row)
|
33
73
|
end
|
34
74
|
|
35
75
|
def self.move_cursor_column(x)
|
@@ -66,11 +106,11 @@ module Reline
|
|
66
106
|
print "\e[1;1H"
|
67
107
|
end
|
68
108
|
|
69
|
-
def prep
|
109
|
+
def self.prep
|
70
110
|
int_handle = Signal.trap('INT', 'IGNORE')
|
71
111
|
otio = `stty -g`.chomp
|
72
112
|
setting = ' -echo -icrnl cbreak'
|
73
|
-
if
|
113
|
+
if /-parenb\b/ =~ `stty -a`
|
74
114
|
setting << ' pass8'
|
75
115
|
end
|
76
116
|
setting << ' -ixoff'
|
@@ -79,7 +119,7 @@ module Reline
|
|
79
119
|
otio
|
80
120
|
end
|
81
121
|
|
82
|
-
def deprep(otio)
|
122
|
+
def self.deprep(otio)
|
83
123
|
int_handle = Signal.trap('INT', 'IGNORE')
|
84
124
|
`stty #{otio}`
|
85
125
|
Signal.trap('INT', int_handle)
|
data/lib/reline/config.rb
CHANGED
@@ -1,23 +1,70 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
|
3
3
|
class Reline::Config
|
4
|
-
|
4
|
+
attr_reader :test_mode
|
5
|
+
|
6
|
+
DEFAULT_PATH = '~/.inputrc'
|
7
|
+
|
8
|
+
KEYSEQ_PATTERN = /\\(?:C|Control)-[A-Za-z_]|\\(?:M|Meta)-[0-9A-Za-z_]|\\(?:C|Control)-(?:M|Meta)-[A-Za-z_]|\\(?:M|Meta)-(?:C|Control)-[A-Za-z_]|\\e|\\[\\\"\'abdfnrtv]|\\\d{1,3}|\\x\h{1,2}|./
|
9
|
+
|
10
|
+
class InvalidInputrc < RuntimeError
|
11
|
+
attr_accessor :file, :lineno
|
12
|
+
end
|
13
|
+
|
14
|
+
VARIABLE_NAMES = %w{
|
15
|
+
bind-tty-special-chars
|
16
|
+
blink-matching-paren
|
17
|
+
byte-oriented
|
18
|
+
completion-ignore-case
|
19
|
+
convert-meta
|
20
|
+
disable-completion
|
21
|
+
enable-keypad
|
22
|
+
expand-tilde
|
23
|
+
history-preserve-point
|
24
|
+
history-size
|
25
|
+
horizontal-scroll-mode
|
26
|
+
input-meta
|
27
|
+
keyseq-timeout
|
28
|
+
mark-directories
|
29
|
+
mark-modified-lines
|
30
|
+
mark-symlinked-directories
|
31
|
+
match-hidden-files
|
32
|
+
meta-flag
|
33
|
+
output-meta
|
34
|
+
page-completions
|
35
|
+
prefer-visible-bell
|
36
|
+
print-completions-horizontally
|
37
|
+
show-all-if-ambiguous
|
38
|
+
show-all-if-unmodified
|
39
|
+
visible-stats
|
40
|
+
}
|
41
|
+
VARIABLE_NAME_SYMBOLS = VARIABLE_NAMES.map { |v| :"#{v.tr(?-, ?_)}" }
|
42
|
+
VARIABLE_NAME_SYMBOLS.each do |v|
|
43
|
+
attr_accessor v
|
44
|
+
end
|
5
45
|
|
6
46
|
def initialize
|
47
|
+
@additional_key_bindings = {} # from inputrc
|
48
|
+
@default_key_bindings = {} # environment-dependent
|
7
49
|
@skip_section = nil
|
8
|
-
@if_stack =
|
50
|
+
@if_stack = nil
|
9
51
|
@editing_mode_label = :emacs
|
10
52
|
@keymap_label = :emacs
|
11
53
|
@key_actors = {}
|
12
54
|
@key_actors[:emacs] = Reline::KeyActor::Emacs.new
|
13
55
|
@key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new
|
14
56
|
@key_actors[:vi_command] = Reline::KeyActor::ViCommand.new
|
57
|
+
@history_size = 500
|
58
|
+
@keyseq_timeout = 500
|
59
|
+
@test_mode = false
|
15
60
|
end
|
16
61
|
|
17
62
|
def reset
|
18
63
|
if editing_mode_is?(:vi_command)
|
19
64
|
@editing_mode_label = :vi_insert
|
20
65
|
end
|
66
|
+
@additional_key_bindings = {}
|
67
|
+
@default_key_bindings = {}
|
21
68
|
end
|
22
69
|
|
23
70
|
def editing_mode
|
@@ -36,29 +83,51 @@ class Reline::Config
|
|
36
83
|
@key_actors[@keymap_label]
|
37
84
|
end
|
38
85
|
|
39
|
-
def read(file =
|
86
|
+
def read(file = nil)
|
87
|
+
file ||= File.expand_path(ENV['INPUTRC'] || DEFAULT_PATH)
|
40
88
|
begin
|
41
89
|
if file.respond_to?(:readlines)
|
42
90
|
lines = file.readlines
|
43
91
|
else
|
44
|
-
|
45
|
-
lines = f.readlines
|
46
|
-
end
|
92
|
+
lines = File.readlines(file)
|
47
93
|
end
|
48
94
|
rescue Errno::ENOENT
|
49
|
-
$stderr.puts "no such file #{file}"
|
50
95
|
return nil
|
51
96
|
end
|
52
97
|
|
53
|
-
read_lines(lines)
|
98
|
+
read_lines(lines, file)
|
54
99
|
self
|
100
|
+
rescue InvalidInputrc => e
|
101
|
+
warn e.message
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
def key_bindings
|
106
|
+
# override @default_key_bindings with @additional_key_bindings
|
107
|
+
@default_key_bindings.merge(@additional_key_bindings)
|
55
108
|
end
|
56
109
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
110
|
+
def add_default_key_binding(keystroke, target)
|
111
|
+
@default_key_bindings[keystroke] = target
|
112
|
+
end
|
113
|
+
|
114
|
+
def reset_default_key_bindings
|
115
|
+
@default_key_bindings = {}
|
116
|
+
end
|
117
|
+
|
118
|
+
def read_lines(lines, file = nil)
|
119
|
+
conditions = [@skip_section, @if_stack]
|
120
|
+
@skip_section = nil
|
121
|
+
@if_stack = []
|
122
|
+
|
123
|
+
lines.each_with_index do |line, no|
|
124
|
+
next if line.match(/\A\s*#/)
|
125
|
+
|
126
|
+
no += 1
|
127
|
+
|
128
|
+
line = line.chomp.lstrip
|
60
129
|
if line[0, 1] == '$'
|
61
|
-
handle_directive(line[1..-1])
|
130
|
+
handle_directive(line[1..-1], file, no)
|
62
131
|
next
|
63
132
|
end
|
64
133
|
|
@@ -70,14 +139,21 @@ class Reline::Config
|
|
70
139
|
next
|
71
140
|
end
|
72
141
|
|
73
|
-
if line =~ /\s*(
|
142
|
+
if line =~ /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/
|
74
143
|
key, func_name = $1, $2
|
75
|
-
bind_key(key, func_name)
|
144
|
+
keystroke, func = bind_key(key, func_name)
|
145
|
+
next unless keystroke
|
146
|
+
@additional_key_bindings[keystroke] = func
|
76
147
|
end
|
77
148
|
end
|
149
|
+
unless @if_stack.empty?
|
150
|
+
raise InvalidInputrc, "#{file}:#{@if_stack.last[1]}: unclosed if"
|
151
|
+
end
|
152
|
+
ensure
|
153
|
+
@skip_section, @if_stack = conditions
|
78
154
|
end
|
79
155
|
|
80
|
-
def handle_directive(directive)
|
156
|
+
def handle_directive(directive, file, no)
|
81
157
|
directive, args = directive.split(' ')
|
82
158
|
case directive
|
83
159
|
when 'if'
|
@@ -88,18 +164,20 @@ class Reline::Config
|
|
88
164
|
when 'version'
|
89
165
|
else # application name
|
90
166
|
condition = true if args == 'Ruby'
|
167
|
+
condition = true if args == 'Reline'
|
91
168
|
end
|
92
|
-
|
93
|
-
@if_stack << @skip_section
|
94
|
-
end
|
169
|
+
@if_stack << [file, no, @skip_section]
|
95
170
|
@skip_section = !condition
|
96
171
|
when 'else'
|
172
|
+
if @if_stack.empty?
|
173
|
+
raise InvalidInputrc, "#{file}:#{no}: unmatched else"
|
174
|
+
end
|
97
175
|
@skip_section = !@skip_section
|
98
176
|
when 'endif'
|
99
|
-
@
|
100
|
-
|
101
|
-
@skip_section = @if_stack.pop
|
177
|
+
if @if_stack.empty?
|
178
|
+
raise InvalidInputrc, "#{file}:#{no}: unmatched endif"
|
102
179
|
end
|
180
|
+
@skip_section = @if_stack.pop
|
103
181
|
when 'include'
|
104
182
|
read(args)
|
105
183
|
end
|
@@ -107,31 +185,7 @@ class Reline::Config
|
|
107
185
|
|
108
186
|
def bind_variable(name, value)
|
109
187
|
case name
|
110
|
-
when
|
111
|
-
bind-tty-special-chars
|
112
|
-
blink-matching-paren
|
113
|
-
byte-oriented
|
114
|
-
completion-ignore-case
|
115
|
-
convert-meta
|
116
|
-
disable-completion
|
117
|
-
enable-keypad
|
118
|
-
expand-tilde
|
119
|
-
history-preserve-point
|
120
|
-
horizontal-scroll-mode
|
121
|
-
input-meta
|
122
|
-
mark-directories
|
123
|
-
mark-modified-lines
|
124
|
-
mark-symlinked-directories
|
125
|
-
match-hidden-files
|
126
|
-
meta-flag
|
127
|
-
output-meta
|
128
|
-
page-completions
|
129
|
-
prefer-visible-bell
|
130
|
-
print-completions-horizontally
|
131
|
-
show-all-if-ambiguous
|
132
|
-
show-all-if-unmodified
|
133
|
-
visible-stats
|
134
|
-
} then
|
188
|
+
when VARIABLE_NAMES then
|
135
189
|
variable_name = :"@#{name.tr(?-, ?_)}"
|
136
190
|
instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
|
137
191
|
when 'bell-style'
|
@@ -170,65 +224,63 @@ class Reline::Config
|
|
170
224
|
when 'vi-insert'
|
171
225
|
@keymap_label = :vi_insert
|
172
226
|
end
|
227
|
+
when 'keyseq-timeout'
|
228
|
+
@keyseq_timeout = value.to_i
|
173
229
|
end
|
174
230
|
end
|
175
231
|
|
176
232
|
def bind_key(key, func_name)
|
177
|
-
if key =~
|
178
|
-
keyseq = parse_keyseq($1)
|
233
|
+
if key =~ /\A"(.*)"\z/
|
234
|
+
keyseq = parse_keyseq($1)
|
179
235
|
else
|
180
236
|
keyseq = nil
|
181
237
|
end
|
182
238
|
if func_name =~ /"(.*)"/
|
183
|
-
func = parse_keyseq($1)
|
239
|
+
func = parse_keyseq($1)
|
184
240
|
else
|
185
|
-
func = func_name.to_sym # It must be macro.
|
241
|
+
func = func_name.tr(?-, ?_).to_sym # It must be macro.
|
186
242
|
end
|
187
243
|
[keyseq, func]
|
188
244
|
end
|
189
245
|
|
190
|
-
def
|
246
|
+
def key_notation_to_code(notation)
|
191
247
|
case notation
|
192
|
-
when /\\C-([A-Za-z_])/
|
193
|
-
(1 + $1.downcase.ord - ?a.ord)
|
194
|
-
when /\\M-([0-9A-Za-z_])/
|
248
|
+
when /\\(?:C|Control)-([A-Za-z_])/
|
249
|
+
(1 + $1.downcase.ord - ?a.ord)
|
250
|
+
when /\\(?:M|Meta)-([0-9A-Za-z_])/
|
195
251
|
modified_key = $1
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
code.chr('ASCII-8BIT')
|
206
|
-
when /\\C-M-[A-Za-z_]/, /\\M-C-[A-Za-z_]/
|
252
|
+
case $1
|
253
|
+
when /[0-9]/
|
254
|
+
?\M-0.bytes.first + (modified_key.ord - ?0.ord)
|
255
|
+
when /[A-Z]/
|
256
|
+
?\M-A.bytes.first + (modified_key.ord - ?A.ord)
|
257
|
+
when /[a-z]/
|
258
|
+
?\M-a.bytes.first + (modified_key.ord - ?a.ord)
|
259
|
+
end
|
260
|
+
when /\\(?:C|Control)-(?:M|Meta)-[A-Za-z_]/, /\\(?:M|Meta)-(?:C|Control)-[A-Za-z_]/
|
207
261
|
# 129 M-^A
|
208
|
-
when /\\(\d{1,3})/ then $1.to_i(8)
|
209
|
-
when /\\x(\h{1,2})/ then $1.to_i(16)
|
210
|
-
when "\\e" then ?\e
|
211
|
-
when "\\\\" then
|
212
|
-
when "\\\"" then ?"
|
213
|
-
when "\\'" then ?'
|
214
|
-
when "\\a" then ?\a
|
215
|
-
when "\\b" then ?\b
|
216
|
-
when "\\d" then ?\d
|
217
|
-
when "\\f" then ?\f
|
218
|
-
when "\\n" then ?\n
|
219
|
-
when "\\r" then ?\r
|
220
|
-
when "\\t" then ?\t
|
221
|
-
when "\\v" then ?\v
|
222
|
-
else notation
|
262
|
+
when /\\(\d{1,3})/ then $1.to_i(8) # octal
|
263
|
+
when /\\x(\h{1,2})/ then $1.to_i(16) # hexadecimal
|
264
|
+
when "\\e" then ?\e.ord
|
265
|
+
when "\\\\" then ?\\.ord
|
266
|
+
when "\\\"" then ?".ord
|
267
|
+
when "\\'" then ?'.ord
|
268
|
+
when "\\a" then ?\a.ord
|
269
|
+
when "\\b" then ?\b.ord
|
270
|
+
when "\\d" then ?\d.ord
|
271
|
+
when "\\f" then ?\f.ord
|
272
|
+
when "\\n" then ?\n.ord
|
273
|
+
when "\\r" then ?\r.ord
|
274
|
+
when "\\t" then ?\t.ord
|
275
|
+
when "\\v" then ?\v.ord
|
276
|
+
else notation.ord
|
223
277
|
end
|
224
278
|
end
|
225
279
|
|
226
280
|
def parse_keyseq(str)
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
ret << key_notation_to_char($&)
|
231
|
-
str = $'
|
281
|
+
ret = []
|
282
|
+
str.scan(KEYSEQ_PATTERN) do
|
283
|
+
ret << key_notation_to_code($&)
|
232
284
|
end
|
233
285
|
ret
|
234
286
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
class Reline::GeneralIO
|
4
|
+
RAW_KEYSTROKE_CONFIG = {}.freeze
|
5
|
+
|
6
|
+
@@buf = []
|
7
|
+
|
8
|
+
def self.input=(val)
|
9
|
+
@@input = val
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.getc
|
13
|
+
unless @@buf.empty?
|
14
|
+
return @@buf.shift
|
15
|
+
end
|
16
|
+
c = nil
|
17
|
+
loop do
|
18
|
+
result = select([@@input], [], [], 0.1)
|
19
|
+
next if result.nil?
|
20
|
+
c = @@input.read(1)
|
21
|
+
break
|
22
|
+
end
|
23
|
+
c&.ord
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.ungetc(c)
|
27
|
+
@@buf.unshift(c)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.get_screen_size
|
31
|
+
[1, 1]
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.cursor_pos
|
35
|
+
Reline::CursorPos.new(1, 1)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.move_cursor_column(val)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.move_cursor_up(val)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.move_cursor_down(val)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.erase_after_cursor
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.scroll_down(val)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.clear_screen
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.set_screen_size(rows, columns)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.prep
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.deprep(otio)
|
63
|
+
end
|
64
|
+
end
|