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.
@@ -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