reline 0.1.3

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,177 @@
1
+ require 'io/console'
2
+
3
+ class Reline::ANSI
4
+ def self.encoding
5
+ Encoding.default_external
6
+ end
7
+
8
+ def self.win?
9
+ false
10
+ end
11
+
12
+ RAW_KEYSTROKE_CONFIG = {
13
+ # Console (80x25)
14
+ [27, 91, 49, 126] => :ed_move_to_beg, # Home
15
+ [27, 91, 52, 126] => :ed_move_to_end, # End
16
+ [27, 91, 51, 126] => :key_delete, # Del
17
+ [27, 91, 65] => :ed_prev_history, # ↑
18
+ [27, 91, 66] => :ed_next_history, # ↓
19
+ [27, 91, 67] => :ed_next_char, # →
20
+ [27, 91, 68] => :ed_prev_char, # ←
21
+
22
+ # KDE
23
+ [27, 91, 72] => :ed_move_to_beg, # Home
24
+ [27, 91, 70] => :ed_move_to_end, # End
25
+ # Del is 0x08
26
+ [27, 71, 65] => :ed_prev_history, # ↑
27
+ [27, 71, 66] => :ed_next_history, # ↓
28
+ [27, 71, 67] => :ed_next_char, # →
29
+ [27, 71, 68] => :ed_prev_char, # ←
30
+
31
+ # GNOME
32
+ [27, 79, 72] => :ed_move_to_beg, # Home
33
+ [27, 79, 70] => :ed_move_to_end, # End
34
+ # Del is 0x08
35
+ # Arrow keys are the same of KDE
36
+
37
+ # others
38
+ [27, 32] => :em_set_mark, # M-<space>
39
+ [24, 24] => :em_exchange_mark, # C-x C-x TODO also add Windows
40
+ [27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→
41
+ [27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+←
42
+
43
+ [27, 79, 65] => :ed_prev_history, # ↑
44
+ [27, 79, 66] => :ed_next_history, # ↓
45
+ [27, 79, 67] => :ed_next_char, # →
46
+ [27, 79, 68] => :ed_prev_char, # ←
47
+ }
48
+
49
+ @@input = STDIN
50
+ def self.input=(val)
51
+ @@input = val
52
+ end
53
+
54
+ @@output = STDOUT
55
+ def self.output=(val)
56
+ @@output = val
57
+ end
58
+
59
+ @@buf = []
60
+ def self.getc
61
+ unless @@buf.empty?
62
+ return @@buf.shift
63
+ end
64
+ c = @@input.raw(intr: true, &:getbyte)
65
+ (c == 0x16 && @@input.raw(min: 0, tim: 0, &:getbyte)) || c
66
+ end
67
+
68
+ def self.ungetc(c)
69
+ @@buf.unshift(c)
70
+ end
71
+
72
+ def self.retrieve_keybuffer
73
+ begin
74
+ result = select([@@input], [], [], 0.001)
75
+ return if result.nil?
76
+ str = @@input.read_nonblock(1024)
77
+ str.bytes.each do |c|
78
+ @@buf.push(c)
79
+ end
80
+ rescue EOFError
81
+ end
82
+ end
83
+
84
+ def self.get_screen_size
85
+ s = @@input.winsize
86
+ return s if s[0] > 0 && s[1] > 0
87
+ s = [ENV["LINES"].to_i, ENV["COLUMNS"].to_i]
88
+ return s if s[0] > 0 && s[1] > 0
89
+ [24, 80]
90
+ rescue Errno::ENOTTY
91
+ [24, 80]
92
+ end
93
+
94
+ def self.set_screen_size(rows, columns)
95
+ @@input.winsize = [rows, columns]
96
+ self
97
+ rescue Errno::ENOTTY
98
+ self
99
+ end
100
+
101
+ def self.cursor_pos
102
+ begin
103
+ res = ''
104
+ m = nil
105
+ @@input.raw do |stdin|
106
+ @@output << "\e[6n"
107
+ @@output.flush
108
+ while (c = stdin.getc) != 'R'
109
+ res << c if c
110
+ end
111
+ m = res.match(/\e\[(?<row>\d+);(?<column>\d+)/)
112
+ (m.pre_match + m.post_match).chars.reverse_each do |ch|
113
+ stdin.ungetc ch
114
+ end
115
+ end
116
+ column = m[:column].to_i - 1
117
+ row = m[:row].to_i - 1
118
+ rescue Errno::ENOTTY
119
+ buf = @@output.pread(@@output.pos, 0)
120
+ row = buf.count("\n")
121
+ column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
122
+ end
123
+ Reline::CursorPos.new(column, row)
124
+ end
125
+
126
+ def self.move_cursor_column(x)
127
+ @@output.write "\e[#{x + 1}G"
128
+ end
129
+
130
+ def self.move_cursor_up(x)
131
+ if x > 0
132
+ @@output.write "\e[#{x}A" if x > 0
133
+ elsif x < 0
134
+ move_cursor_down(-x)
135
+ end
136
+ end
137
+
138
+ def self.move_cursor_down(x)
139
+ if x > 0
140
+ @@output.write "\e[#{x}B" if x > 0
141
+ elsif x < 0
142
+ move_cursor_up(-x)
143
+ end
144
+ end
145
+
146
+ def self.erase_after_cursor
147
+ @@output.write "\e[K"
148
+ end
149
+
150
+ def self.scroll_down(x)
151
+ return if x.zero?
152
+ @@output.write "\e[#{x}S"
153
+ end
154
+
155
+ def self.clear_screen
156
+ @@output.write "\e[2J"
157
+ @@output.write "\e[1;1H"
158
+ end
159
+
160
+ @@old_winch_handler = nil
161
+ def self.set_winch_handler(&handler)
162
+ @@old_winch_handler = Signal.trap('WINCH', &handler)
163
+ end
164
+
165
+ def self.prep
166
+ retrieve_keybuffer
167
+ int_handle = Signal.trap('INT', 'IGNORE')
168
+ Signal.trap('INT', int_handle)
169
+ nil
170
+ end
171
+
172
+ def self.deprep(otio)
173
+ int_handle = Signal.trap('INT', 'IGNORE')
174
+ Signal.trap('INT', int_handle)
175
+ Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
176
+ end
177
+ end
@@ -0,0 +1,288 @@
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
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 'history-size'
188
+ @history_size = value.to_i
189
+ when 'bell-style'
190
+ @bell_style =
191
+ case value
192
+ when 'none', 'off'
193
+ :none
194
+ when 'audible', 'on'
195
+ :audible
196
+ when 'visible'
197
+ :visible
198
+ else
199
+ :audible
200
+ end
201
+ when 'comment-begin'
202
+ @comment_begin = value.dup
203
+ when 'completion-query-items'
204
+ @completion_query_items = value.to_i
205
+ when 'isearch-terminators'
206
+ @isearch_terminators = instance_eval(value)
207
+ when 'editing-mode'
208
+ case value
209
+ when 'emacs'
210
+ @editing_mode_label = :emacs
211
+ @keymap_label = :emacs
212
+ when 'vi'
213
+ @editing_mode_label = :vi_insert
214
+ @keymap_label = :vi_insert
215
+ end
216
+ when 'keymap'
217
+ case value
218
+ when 'emacs', 'emacs-standard', 'emacs-meta', 'emacs-ctlx'
219
+ @keymap_label = :emacs
220
+ when 'vi', 'vi-move', 'vi-command'
221
+ @keymap_label = :vi_command
222
+ when 'vi-insert'
223
+ @keymap_label = :vi_insert
224
+ end
225
+ when 'keyseq-timeout'
226
+ @keyseq_timeout = value.to_i
227
+ when *VARIABLE_NAMES then
228
+ variable_name = :"@#{name.tr(?-, ?_)}"
229
+ instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
230
+ end
231
+ end
232
+
233
+ def bind_key(key, func_name)
234
+ if key =~ /\A"(.*)"\z/
235
+ keyseq = parse_keyseq($1)
236
+ else
237
+ keyseq = nil
238
+ end
239
+ if func_name =~ /"(.*)"/
240
+ func = parse_keyseq($1)
241
+ else
242
+ func = func_name.tr(?-, ?_).to_sym # It must be macro.
243
+ end
244
+ [keyseq, func]
245
+ end
246
+
247
+ def key_notation_to_code(notation)
248
+ case notation
249
+ when /\\(?:C|Control)-([A-Za-z_])/
250
+ (1 + $1.downcase.ord - ?a.ord)
251
+ when /\\(?:M|Meta)-([0-9A-Za-z_])/
252
+ modified_key = $1
253
+ case $1
254
+ when /[0-9]/
255
+ ?\M-0.bytes.first + (modified_key.ord - ?0.ord)
256
+ when /[A-Z]/
257
+ ?\M-A.bytes.first + (modified_key.ord - ?A.ord)
258
+ when /[a-z]/
259
+ ?\M-a.bytes.first + (modified_key.ord - ?a.ord)
260
+ end
261
+ when /\\(?:C|Control)-(?:M|Meta)-[A-Za-z_]/, /\\(?:M|Meta)-(?:C|Control)-[A-Za-z_]/
262
+ # 129 M-^A
263
+ when /\\(\d{1,3})/ then $1.to_i(8) # octal
264
+ when /\\x(\h{1,2})/ then $1.to_i(16) # hexadecimal
265
+ when "\\e" then ?\e.ord
266
+ when "\\\\" then ?\\.ord
267
+ when "\\\"" then ?".ord
268
+ when "\\'" then ?'.ord
269
+ when "\\a" then ?\a.ord
270
+ when "\\b" then ?\b.ord
271
+ when "\\d" then ?\d.ord
272
+ when "\\f" then ?\f.ord
273
+ when "\\n" then ?\n.ord
274
+ when "\\r" then ?\r.ord
275
+ when "\\t" then ?\t.ord
276
+ when "\\v" then ?\v.ord
277
+ else notation.ord
278
+ end
279
+ end
280
+
281
+ def parse_keyseq(str)
282
+ ret = []
283
+ str.scan(KEYSEQ_PATTERN) do
284
+ ret << key_notation_to_code($&)
285
+ end
286
+ ret
287
+ end
288
+ end