reline 0.0.4
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 +7 -0
- data/BSDL +22 -0
- data/COPYING +56 -0
- data/README.md +9 -0
- data/lib/reline.rb +402 -0
- data/lib/reline/ansi.rb +140 -0
- data/lib/reline/config.rb +286 -0
- data/lib/reline/general_io.rb +67 -0
- data/lib/reline/history.rb +56 -0
- data/lib/reline/key_actor.rb +7 -0
- data/lib/reline/key_actor/base.rb +7 -0
- data/lib/reline/key_actor/emacs.rb +517 -0
- data/lib/reline/key_actor/vi_command.rb +518 -0
- data/lib/reline/key_actor/vi_insert.rb +517 -0
- data/lib/reline/key_stroke.rb +53 -0
- data/lib/reline/kill_ring.rb +113 -0
- data/lib/reline/line_editor.rb +1942 -0
- data/lib/reline/unicode.rb +527 -0
- data/lib/reline/unicode/east_asian_width.rb +1145 -0
- data/lib/reline/version.rb +3 -0
- data/lib/reline/windows.rb +236 -0
- metadata +105 -0
data/lib/reline/ansi.rb
ADDED
@@ -0,0 +1,140 @@
|
|
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
|
+
}
|
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
|
27
|
+
@@input.getbyte
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.ungetc(c)
|
31
|
+
@@buf.unshift(c)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.retrieve_keybuffer
|
35
|
+
result = select([@@input], [], [], 0.001)
|
36
|
+
return if result.nil?
|
37
|
+
str = @@input.read_nonblock(1024)
|
38
|
+
str.bytes.each do |c|
|
39
|
+
@@buf.push(c)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.get_screen_size
|
44
|
+
@@input.winsize
|
45
|
+
rescue Errno::ENOTTY
|
46
|
+
[24, 80]
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.set_screen_size(rows, columns)
|
50
|
+
@@input.winsize = [rows, columns]
|
51
|
+
self
|
52
|
+
rescue Errno::ENOTTY
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.cursor_pos
|
57
|
+
begin
|
58
|
+
res = ''
|
59
|
+
@@input.raw do |stdin|
|
60
|
+
@@output << "\e[6n"
|
61
|
+
@@output.flush
|
62
|
+
while (c = stdin.getc) != 'R'
|
63
|
+
res << c if c
|
64
|
+
end
|
65
|
+
end
|
66
|
+
m = res.match(/(?<row>\d+);(?<column>\d+)/)
|
67
|
+
column = m[:column].to_i - 1
|
68
|
+
row = m[:row].to_i - 1
|
69
|
+
rescue Errno::ENOTTY
|
70
|
+
buf = @@output.pread(@@output.pos, 0)
|
71
|
+
row = buf.count("\n")
|
72
|
+
column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
|
73
|
+
end
|
74
|
+
Reline::CursorPos.new(column, row)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.move_cursor_column(x)
|
78
|
+
print "\e[#{x + 1}G"
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.move_cursor_up(x)
|
82
|
+
if x > 0
|
83
|
+
print "\e[#{x}A" if x > 0
|
84
|
+
elsif x < 0
|
85
|
+
move_cursor_down(-x)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.move_cursor_down(x)
|
90
|
+
if x > 0
|
91
|
+
print "\e[#{x}B" if x > 0
|
92
|
+
elsif x < 0
|
93
|
+
move_cursor_up(-x)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.erase_after_cursor
|
98
|
+
print "\e[K"
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.scroll_down(x)
|
102
|
+
return if x.zero?
|
103
|
+
print "\e[#{x}S"
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.clear_screen
|
107
|
+
print "\e[2J"
|
108
|
+
print "\e[1;1H"
|
109
|
+
end
|
110
|
+
|
111
|
+
@@old_winch_handler = nil
|
112
|
+
def self.set_winch_handler(&handler)
|
113
|
+
@@old_winch_handler = Signal.trap('WINCH', &handler)
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.prep
|
117
|
+
retrieve_keybuffer
|
118
|
+
int_handle = Signal.trap('INT', 'IGNORE')
|
119
|
+
otio = `stty -g`.chomp
|
120
|
+
setting = ' -echo -icrnl cbreak'
|
121
|
+
stty = `stty -a`
|
122
|
+
if /-parenb\b/ =~ stty
|
123
|
+
setting << ' pass8'
|
124
|
+
end
|
125
|
+
if /\bdsusp *=/ =~ stty
|
126
|
+
setting << ' dsusp undef'
|
127
|
+
end
|
128
|
+
setting << ' -ixoff'
|
129
|
+
`stty #{setting}`
|
130
|
+
Signal.trap('INT', int_handle)
|
131
|
+
otio
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.deprep(otio)
|
135
|
+
int_handle = Signal.trap('INT', 'IGNORE')
|
136
|
+
`stty #{otio}`
|
137
|
+
Signal.trap('INT', int_handle)
|
138
|
+
Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,286 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
class Reline::Config
|
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
|
45
|
+
|
46
|
+
def initialize
|
47
|
+
@additional_key_bindings = {} # from inputrc
|
48
|
+
@default_key_bindings = {} # environment-dependent
|
49
|
+
@skip_section = nil
|
50
|
+
@if_stack = nil
|
51
|
+
@editing_mode_label = :emacs
|
52
|
+
@keymap_label = :emacs
|
53
|
+
@key_actors = {}
|
54
|
+
@key_actors[:emacs] = Reline::KeyActor::Emacs.new
|
55
|
+
@key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new
|
56
|
+
@key_actors[:vi_command] = Reline::KeyActor::ViCommand.new
|
57
|
+
@history_size = 500
|
58
|
+
@keyseq_timeout = 500
|
59
|
+
@test_mode = false
|
60
|
+
end
|
61
|
+
|
62
|
+
def reset
|
63
|
+
if editing_mode_is?(:vi_command)
|
64
|
+
@editing_mode_label = :vi_insert
|
65
|
+
end
|
66
|
+
@additional_key_bindings = {}
|
67
|
+
@default_key_bindings = {}
|
68
|
+
end
|
69
|
+
|
70
|
+
def editing_mode
|
71
|
+
@key_actors[@editing_mode_label]
|
72
|
+
end
|
73
|
+
|
74
|
+
def editing_mode=(val)
|
75
|
+
@editing_mode_label = val
|
76
|
+
end
|
77
|
+
|
78
|
+
def editing_mode_is?(*val)
|
79
|
+
(val.respond_to?(:any?) ? val : [val]).any?(@editing_mode_label)
|
80
|
+
end
|
81
|
+
|
82
|
+
def keymap
|
83
|
+
@key_actors[@keymap_label]
|
84
|
+
end
|
85
|
+
|
86
|
+
def read(file = nil)
|
87
|
+
file ||= File.expand_path(ENV['INPUTRC'] || DEFAULT_PATH)
|
88
|
+
begin
|
89
|
+
if file.respond_to?(:readlines)
|
90
|
+
lines = file.readlines
|
91
|
+
else
|
92
|
+
lines = File.readlines(file)
|
93
|
+
end
|
94
|
+
rescue Errno::ENOENT
|
95
|
+
return nil
|
96
|
+
end
|
97
|
+
|
98
|
+
read_lines(lines, file)
|
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)
|
108
|
+
end
|
109
|
+
|
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
|
129
|
+
if line.start_with?('$')
|
130
|
+
handle_directive(line[1..-1], file, no)
|
131
|
+
next
|
132
|
+
end
|
133
|
+
|
134
|
+
next if @skip_section
|
135
|
+
|
136
|
+
case line
|
137
|
+
when /^set +([^ ]+) +([^ ]+)/i
|
138
|
+
var, value = $1.downcase, $2.downcase
|
139
|
+
bind_variable(var, value)
|
140
|
+
next
|
141
|
+
when /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
|
142
|
+
key, func_name = $1, $2
|
143
|
+
keystroke, func = bind_key(key, func_name)
|
144
|
+
next unless keystroke
|
145
|
+
@additional_key_bindings[keystroke] = func
|
146
|
+
end
|
147
|
+
end
|
148
|
+
unless @if_stack.empty?
|
149
|
+
raise InvalidInputrc, "#{file}:#{@if_stack.last[1]}: unclosed if"
|
150
|
+
end
|
151
|
+
ensure
|
152
|
+
@skip_section, @if_stack = conditions
|
153
|
+
end
|
154
|
+
|
155
|
+
def handle_directive(directive, file, no)
|
156
|
+
directive, args = directive.split(' ')
|
157
|
+
case directive
|
158
|
+
when 'if'
|
159
|
+
condition = false
|
160
|
+
case args # TODO: variables
|
161
|
+
when 'mode'
|
162
|
+
when 'term'
|
163
|
+
when 'version'
|
164
|
+
else # application name
|
165
|
+
condition = true if args == 'Ruby'
|
166
|
+
condition = true if args == 'Reline'
|
167
|
+
end
|
168
|
+
@if_stack << [file, no, @skip_section]
|
169
|
+
@skip_section = !condition
|
170
|
+
when 'else'
|
171
|
+
if @if_stack.empty?
|
172
|
+
raise InvalidInputrc, "#{file}:#{no}: unmatched else"
|
173
|
+
end
|
174
|
+
@skip_section = !@skip_section
|
175
|
+
when 'endif'
|
176
|
+
if @if_stack.empty?
|
177
|
+
raise InvalidInputrc, "#{file}:#{no}: unmatched endif"
|
178
|
+
end
|
179
|
+
@skip_section = @if_stack.pop
|
180
|
+
when 'include'
|
181
|
+
read(args)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def bind_variable(name, value)
|
186
|
+
case name
|
187
|
+
when VARIABLE_NAMES then
|
188
|
+
variable_name = :"@#{name.tr(?-, ?_)}"
|
189
|
+
instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
|
190
|
+
when 'bell-style'
|
191
|
+
@bell_style =
|
192
|
+
case value
|
193
|
+
when 'none', 'off'
|
194
|
+
:none
|
195
|
+
when 'audible', 'on'
|
196
|
+
:audible
|
197
|
+
when 'visible'
|
198
|
+
:visible
|
199
|
+
else
|
200
|
+
:audible
|
201
|
+
end
|
202
|
+
when 'comment-begin'
|
203
|
+
@comment_begin = value.dup
|
204
|
+
when 'completion-query-items'
|
205
|
+
@completion_query_items = value.to_i
|
206
|
+
when 'isearch-terminators'
|
207
|
+
@isearch_terminators = instance_eval(value)
|
208
|
+
when 'editing-mode'
|
209
|
+
case value
|
210
|
+
when 'emacs'
|
211
|
+
@editing_mode_label = :emacs
|
212
|
+
@keymap_label = :emacs
|
213
|
+
when 'vi'
|
214
|
+
@editing_mode_label = :vi_insert
|
215
|
+
@keymap_label = :vi_insert
|
216
|
+
end
|
217
|
+
when 'keymap'
|
218
|
+
case value
|
219
|
+
when 'emacs', 'emacs-standard', 'emacs-meta', 'emacs-ctlx'
|
220
|
+
@keymap_label = :emacs
|
221
|
+
when 'vi', 'vi-move', 'vi-command'
|
222
|
+
@keymap_label = :vi_command
|
223
|
+
when 'vi-insert'
|
224
|
+
@keymap_label = :vi_insert
|
225
|
+
end
|
226
|
+
when 'keyseq-timeout'
|
227
|
+
@keyseq_timeout = value.to_i
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def bind_key(key, func_name)
|
232
|
+
if key =~ /\A"(.*)"\z/
|
233
|
+
keyseq = parse_keyseq($1)
|
234
|
+
else
|
235
|
+
keyseq = nil
|
236
|
+
end
|
237
|
+
if func_name =~ /"(.*)"/
|
238
|
+
func = parse_keyseq($1)
|
239
|
+
else
|
240
|
+
func = func_name.tr(?-, ?_).to_sym # It must be macro.
|
241
|
+
end
|
242
|
+
[keyseq, func]
|
243
|
+
end
|
244
|
+
|
245
|
+
def key_notation_to_code(notation)
|
246
|
+
case notation
|
247
|
+
when /\\(?:C|Control)-([A-Za-z_])/
|
248
|
+
(1 + $1.downcase.ord - ?a.ord)
|
249
|
+
when /\\(?:M|Meta)-([0-9A-Za-z_])/
|
250
|
+
modified_key = $1
|
251
|
+
case $1
|
252
|
+
when /[0-9]/
|
253
|
+
?\M-0.bytes.first + (modified_key.ord - ?0.ord)
|
254
|
+
when /[A-Z]/
|
255
|
+
?\M-A.bytes.first + (modified_key.ord - ?A.ord)
|
256
|
+
when /[a-z]/
|
257
|
+
?\M-a.bytes.first + (modified_key.ord - ?a.ord)
|
258
|
+
end
|
259
|
+
when /\\(?:C|Control)-(?:M|Meta)-[A-Za-z_]/, /\\(?:M|Meta)-(?:C|Control)-[A-Za-z_]/
|
260
|
+
# 129 M-^A
|
261
|
+
when /\\(\d{1,3})/ then $1.to_i(8) # octal
|
262
|
+
when /\\x(\h{1,2})/ then $1.to_i(16) # hexadecimal
|
263
|
+
when "\\e" then ?\e.ord
|
264
|
+
when "\\\\" then ?\\.ord
|
265
|
+
when "\\\"" then ?".ord
|
266
|
+
when "\\'" then ?'.ord
|
267
|
+
when "\\a" then ?\a.ord
|
268
|
+
when "\\b" then ?\b.ord
|
269
|
+
when "\\d" then ?\d.ord
|
270
|
+
when "\\f" then ?\f.ord
|
271
|
+
when "\\n" then ?\n.ord
|
272
|
+
when "\\r" then ?\r.ord
|
273
|
+
when "\\t" then ?\t.ord
|
274
|
+
when "\\v" then ?\v.ord
|
275
|
+
else notation.ord
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def parse_keyseq(str)
|
280
|
+
ret = []
|
281
|
+
str.scan(KEYSEQ_PATTERN) do
|
282
|
+
ret << key_notation_to_code($&)
|
283
|
+
end
|
284
|
+
ret
|
285
|
+
end
|
286
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
class Reline::GeneralIO
|
4
|
+
RAW_KEYSTROKE_CONFIG = {}
|
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.set_winch_handler(&handler)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.prep
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.deprep(otio)
|
66
|
+
end
|
67
|
+
end
|