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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34664ed4926dba843c8d0b5c161a8783384afbe37ef601a6ef7130e6acfe614f
4
- data.tar.gz: c006411fd71550186facf348787d964f51f8e7ad03d87c7718ab2689a973b2c0
3
+ metadata.gz: beb24e6d3114b6debe540928f1299fd10d420ff447f90401c9a8249b12804b7d
4
+ data.tar.gz: 046dfcfa706a95f62acb152238cae3680f918f9af2c9a52eee4fa4e5771ebdbc
5
5
  SHA512:
6
- metadata.gz: f8307b181652977ca88bc9858533a41cdb9305b27c5fdd1584b6c9c7931e96fbf4c86c73d84b0d09cb4d27a7061adbf5e5f7c327f65864986eea2fd75c55937f
7
- data.tar.gz: 47453f12ee20b6438751008216abfb04bf8ff06e9e1b5ab9bcb5e73319bc39aeecb1748d2ad54f5293385a04fba877f3fe6fa219d1e36afacb41851106ebf22c
6
+ metadata.gz: a9a1abfe4febd8da98052386b0cca9a4b3c9f333c0a53c439d5980cad64c3b2f9f75c35d2498e42beadd4cea2f6a7d90c005d0112317b8ea90f89fd45b0f8632
7
+ data.tar.gz: 6f482d37f4c85ab5f0a5df08e069f81c5289b7bedbcea105a0601a7174bd3ca314c0a04750e2bba91cb31ad7bdc7f56afa4f3b8398bf2bd80e635e356be12615
@@ -1,18 +1,20 @@
1
1
  require 'io/console'
2
+ require 'timeout'
2
3
  require 'reline/version'
3
4
  require 'reline/config'
4
5
  require 'reline/key_actor'
5
6
  require 'reline/key_stroke'
6
7
  require 'reline/line_editor'
8
+ require 'reline/history'
7
9
 
8
10
  module Reline
11
+ Key = Struct.new('Key', :char, :combined_char, :with_meta)
12
+
9
13
  extend self
10
14
  FILENAME_COMPLETION_PROC = nil
11
15
  USERNAME_COMPLETION_PROC = nil
12
- HISTORY = Array.new
13
16
 
14
17
  if RUBY_PLATFORM =~ /mswin|mingw/
15
- require 'Win32API'
16
18
  IS_WINDOWS = true
17
19
  else
18
20
  IS_WINDOWS = false
@@ -20,44 +22,83 @@ module Reline
20
22
 
21
23
  CursorPos = Struct.new(:x, :y)
22
24
 
23
- class << self
24
- attr_accessor :basic_quote_characters
25
- attr_accessor :completer_quote_characters
26
- attr_accessor :completer_word_break_characters
27
- attr_reader :completion_append_character
28
- attr_accessor :completion_case_fold
29
- attr_accessor :filename_quote_characters
30
- attr_writer :input
31
- attr_writer :output
32
- end
33
-
25
+ @@config = Reline::Config.new
26
+ @@key_stroke = Reline::KeyStroke.new(@@config)
27
+ @@line_editor = Reline::LineEditor.new(@@config)
34
28
  @@ambiguous_width = nil
35
- @@config = nil
36
29
 
37
- @basic_quote_characters = '"\''
38
- @completer_quote_characters
39
- @completer_word_break_characters = @basic_word_break_characters.dup
40
- @completion_append_character
30
+ HISTORY = History.new(@@config)
31
+
32
+ @@completion_append_character = nil
33
+ def self.completion_append_character
34
+ @@completion_append_character
35
+ end
41
36
  def self.completion_append_character=(val)
42
37
  if val.nil?
43
- @completion_append_character = nil
38
+ @@completion_append_character = nil
44
39
  elsif val.size == 1
45
- @completion_append_character = val
40
+ @@completion_append_character = val.encode(Encoding::default_external)
46
41
  elsif val.size > 1
47
- @completion_append_character = val[0]
42
+ @@completion_append_character = val[0].encode(Encoding::default_external)
48
43
  else
49
- @completion_append_character = val
44
+ @@completion_append_character = nil
50
45
  end
51
46
  end
52
- @completion_case_fold
53
- @filename_quote_characters
54
47
 
55
48
  @@basic_word_break_characters = " \t\n`><=;|&{("
56
49
  def self.basic_word_break_characters
57
50
  @@basic_word_break_characters
58
51
  end
59
52
  def self.basic_word_break_characters=(v)
60
- @@basic_word_break_characters = v
53
+ @@basic_word_break_characters = v.encode(Encoding::default_external)
54
+ end
55
+
56
+ @@completer_word_break_characters = @@basic_word_break_characters.dup
57
+ def self.completer_word_break_characters
58
+ @@completer_word_break_characters
59
+ end
60
+ def self.completer_word_break_characters=(v)
61
+ @@completer_word_break_characters = v.encode(Encoding::default_external)
62
+ end
63
+
64
+ @@basic_quote_characters = '"\''
65
+ def self.basic_quote_characters
66
+ @@basic_quote_characters
67
+ end
68
+ def self.basic_quote_characters=(v)
69
+ @@basic_quote_characters = v.encode(Encoding::default_external)
70
+ end
71
+
72
+ @@completer_quote_characters = '"\''
73
+ def self.completer_quote_characters
74
+ @@completer_quote_characters
75
+ end
76
+ def self.completer_quote_characters=(v)
77
+ @@completer_quote_characters = v.encode(Encoding::default_external)
78
+ end
79
+
80
+ @@filename_quote_characters = ''
81
+ def self.filename_quote_characters
82
+ @@filename_quote_characters
83
+ end
84
+ def self.filename_quote_characters=(v)
85
+ @@filename_quote_characters = v.encode(Encoding::default_external)
86
+ end
87
+
88
+ @@special_prefixes = ''
89
+ def self.special_prefixes
90
+ @@special_prefixes
91
+ end
92
+ def self.special_prefixes=(v)
93
+ @@special_prefixes = v.encode(Encoding::default_external)
94
+ end
95
+
96
+ @@completion_case_fold = nil
97
+ def self.completion_case_fold
98
+ @@completion_case_fold
99
+ end
100
+ def self.completion_case_fold=(v)
101
+ @@completion_case_fold = v
61
102
  end
62
103
 
63
104
  @@completion_proc = nil
@@ -65,132 +106,310 @@ module Reline
65
106
  @@completion_proc
66
107
  end
67
108
  def self.completion_proc=(p)
109
+ raise ArgumentError unless p.is_a?(Proc)
68
110
  @@completion_proc = p
69
111
  end
70
112
 
113
+ @@output_modifier_proc = nil
114
+ def self.output_modifier_proc
115
+ @@output_modifier_proc
116
+ end
117
+ def self.output_modifier_proc=(p)
118
+ raise ArgumentError unless p.is_a?(Proc)
119
+ @@output_modifier_proc = p
120
+ end
121
+
122
+ @@prompt_proc = nil
123
+ def self.prompt_proc
124
+ @@prompt_proc
125
+ end
126
+ def self.prompt_proc=(p)
127
+ raise ArgumentError unless p.is_a?(Proc)
128
+ @@prompt_proc = p
129
+ end
130
+
131
+ @@auto_indent_proc = nil
132
+ def self.auto_indent_proc
133
+ @@auto_indent_proc
134
+ end
135
+ def self.auto_indent_proc=(p)
136
+ raise ArgumentError unless p.is_a?(Proc)
137
+ @@auto_indent_proc = p
138
+ end
139
+
140
+ @@pre_input_hook = nil
141
+ def self.pre_input_hook
142
+ @@pre_input_hook
143
+ end
144
+ def self.pre_input_hook=(p)
145
+ @@pre_input_hook = p
146
+ end
147
+
71
148
  @@dig_perfect_match_proc = nil
72
149
  def self.dig_perfect_match_proc
73
150
  @@dig_perfect_match_proc
74
151
  end
75
152
  def self.dig_perfect_match_proc=(p)
153
+ raise ArgumentError unless p.is_a?(Proc)
76
154
  @@dig_perfect_match_proc = p
77
155
  end
78
156
 
79
- if IS_WINDOWS
80
- require 'reline/windows'
81
- else
82
- require 'reline/ansi'
157
+ def self.insert_text(text)
158
+ @@line_editor&.insert_text(text)
159
+ self
83
160
  end
84
161
 
85
- def retrieve_completion_block(line, byte_pointer)
86
- break_regexp = /[#{Regexp.escape(@@basic_word_break_characters)}]/
87
- before_pointer = line.byteslice(0, byte_pointer)
88
- break_point = before_pointer.rindex(break_regexp)
89
- if break_point
90
- preposing = before_pointer[0..(break_point)]
91
- block = before_pointer[(break_point + 1)..-1]
92
- else
93
- preposing = ''
94
- block = before_pointer
162
+ def self.redisplay
163
+ @@line_editor&.rerender
164
+ end
165
+
166
+ def self.line_buffer
167
+ @@line_editor&.line
168
+ end
169
+
170
+ def self.point
171
+ @@line_editor ? @@line_editor.byte_pointer : 0
172
+ end
173
+
174
+ def self.point=(val)
175
+ @@line_editor.byte_pointer = val
176
+ end
177
+
178
+ def self.delete_text(start = nil, length = nil)
179
+ @@line_editor&.delete_text(start, length)
180
+ end
181
+
182
+ private_class_method def self.test_mode
183
+ remove_const('IOGate') if const_defined?('IOGate')
184
+ const_set('IOGate', Reline::GeneralIO)
185
+ @@config.instance_variable_set(:@test_mode, true)
186
+ @@config.reset
187
+ end
188
+
189
+ def self.input=(val)
190
+ raise TypeError unless val.respond_to?(:getc) or val.nil?
191
+ if val.respond_to?(:getc)
192
+ if defined?(Reline::ANSI) and IOGate == Reline::ANSI
193
+ Reline::ANSI.input = val
194
+ elsif IOGate == Reline::GeneralIO
195
+ Reline::GeneralIO.input = val
196
+ end
95
197
  end
96
- postposing = line.byteslice(byte_pointer, line.bytesize)
97
- [preposing, block, postposing]
198
+ end
199
+
200
+ @@output = STDOUT
201
+ def self.output=(val)
202
+ raise TypeError unless val.respond_to?(:write) or val.nil?
203
+ @@output = val
204
+ if defined?(Reline::ANSI) and IOGate == Reline::ANSI
205
+ Reline::ANSI.output = val
206
+ end
207
+ end
208
+
209
+ def self.vi_editing_mode
210
+ @@config.editing_mode = :vi_insert
211
+ nil
212
+ end
213
+
214
+ def self.emacs_editing_mode
215
+ @@config.editing_mode = :emacs
216
+ nil
217
+ end
218
+
219
+ def self.vi_editing_mode?
220
+ @@config.editing_mode_is?(:vi_insert, :vi_command)
221
+ end
222
+
223
+ def self.emacs_editing_mode?
224
+ @@config.editing_mode_is?(:emacs)
225
+ end
226
+
227
+ def self.get_screen_size
228
+ Reline::IOGate.get_screen_size
229
+ end
230
+
231
+ def eof?
232
+ @@line_editor.eof?
98
233
  end
99
234
 
100
235
  def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
101
- if block_given?
102
- inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
103
- else
104
- inner_readline(prompt, add_hist, true)
236
+ unless confirm_multiline_termination
237
+ raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
105
238
  end
239
+ inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
106
240
 
107
- if add_hist and @line_editor.whole_buffer and @line_editor.whole_buffer.chomp.size > 0
108
- Reline::HISTORY << @line_editor.whole_buffer
241
+ whole_buffer = @@line_editor.whole_buffer.dup
242
+ whole_buffer.taint
243
+ if add_hist and whole_buffer and whole_buffer.chomp.size > 0
244
+ Reline::HISTORY << whole_buffer
109
245
  end
110
246
 
111
- @line_editor.whole_buffer
247
+ @@line_editor.reset_line if @@line_editor.whole_buffer.nil?
248
+ whole_buffer
112
249
  end
113
250
 
114
251
  def readline(prompt = '', add_hist = false)
115
252
  inner_readline(prompt, add_hist, false)
116
253
 
117
- if add_hist and @line_editor.line and @line_editor.line.chomp.size > 0
118
- Reline::HISTORY << @line_editor.line.chomp
254
+ line = @@line_editor.line.dup
255
+ line.taint
256
+ if add_hist and line and line.chomp.size > 0
257
+ Reline::HISTORY << line.chomp
119
258
  end
120
259
 
121
- @line_editor.line
260
+ @@line_editor.reset_line if @@line_editor.line.nil?
261
+ line
122
262
  end
123
263
 
124
264
  def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
125
- if @@config.nil?
126
- @@config = Reline::Config.new
127
- @@config.read
265
+ if ENV['RELINE_STDERR_TTY']
266
+ $stderr.reopen(ENV['RELINE_STDERR_TTY'], 'w')
267
+ $stderr.sync = true
268
+ $stderr.puts "Reline is used by #{Process.pid}"
128
269
  end
129
- otio = prep
270
+ otio = Reline::IOGate.prep
130
271
 
131
272
  may_req_ambiguous_char_width
132
- @line_editor = Reline::LineEditor.new(@@config, prompt)
273
+ @@line_editor.reset(prompt)
133
274
  if multiline
134
- @line_editor.multiline_on
275
+ @@line_editor.multiline_on
135
276
  if block_given?
136
- @line_editor.confirm_multiline_termination_proc = confirm_multiline_termination
277
+ @@line_editor.confirm_multiline_termination_proc = confirm_multiline_termination
137
278
  end
138
- end
139
- @line_editor.completion_proc = @@completion_proc
140
- @line_editor.dig_perfect_match_proc = @@dig_perfect_match_proc
141
- @line_editor.retrieve_completion_block = method(:retrieve_completion_block)
142
- @line_editor.rerender
143
-
144
- if IS_WINDOWS
145
- config = {
146
- key_mapping: {
147
- [224, 72] => :ed_prev_history, # ↑
148
- [224, 80] => :ed_next_history, # ↓
149
- [224, 77] => :ed_next_char, # →
150
- [224, 75] => :ed_prev_char # ←
151
- }
152
- }
153
279
  else
154
- config = {
155
- key_mapping: {
156
- [27, 91, 65] => :ed_prev_history, # ↑
157
- [27, 91, 66] => :ed_next_history, # ↓
158
- [27, 91, 67] => :ed_next_char, # →
159
- [27, 91, 68] => :ed_prev_char # ←
160
- }
161
- }
280
+ @@line_editor.multiline_off
281
+ end
282
+ @@line_editor.output = @@output
283
+ @@line_editor.completion_proc = @@completion_proc
284
+ @@line_editor.output_modifier_proc = @@output_modifier_proc
285
+ @@line_editor.prompt_proc = @@prompt_proc
286
+ @@line_editor.auto_indent_proc = @@auto_indent_proc
287
+ @@line_editor.dig_perfect_match_proc = @@dig_perfect_match_proc
288
+ @@line_editor.pre_input_hook = @@pre_input_hook
289
+ @@line_editor.rerender
290
+
291
+ unless @@config.test_mode
292
+ @@config.read
293
+ @@config.reset_default_key_bindings
294
+ Reline::IOGate::RAW_KEYSTROKE_CONFIG.each_pair do |key, func|
295
+ @@config.add_default_key_binding(key, func)
296
+ end
162
297
  end
163
298
 
164
- key_stroke = Reline::KeyStroke.new(config)
165
299
  begin
166
- while c = getc
167
- key_stroke.input_to!(c)&.then { |inputs|
300
+ loop do
301
+ read_io(@@config.keyseq_timeout) { |inputs|
168
302
  inputs.each { |c|
169
- @line_editor.input_key(c)
170
- @line_editor.rerender
303
+ @@line_editor.input_key(c)
304
+ @@line_editor.rerender
171
305
  }
172
306
  }
173
- break if @line_editor.finished?
307
+ break if @@line_editor.finished?
174
308
  end
175
- Reline.move_cursor_column(0)
309
+ Reline::IOGate.move_cursor_column(0)
176
310
  rescue StandardError => e
177
- deprep(otio)
311
+ @@line_editor.finalize
312
+ Reline::IOGate.deprep(otio)
178
313
  raise e
179
314
  end
180
315
 
181
- deprep(otio)
316
+ @@line_editor.finalize
317
+ Reline::IOGate.deprep(otio)
318
+ end
319
+
320
+ # Keystrokes of GNU Readline will timeout it with the specification of
321
+ # "keyseq-timeout" when waiting for the 2nd character after the 1st one.
322
+ # If the 2nd character comes after 1st ESC without timeout it has a
323
+ # meta-property of meta-key to discriminate modified key with meta-key
324
+ # from multibyte characters that come with 8th bit on.
325
+ #
326
+ # GNU Readline will wait for the 2nd character with "keyseq-timeout"
327
+ # milli-seconds but wait forever after 3rd characters.
328
+ def read_io(keyseq_timeout, &block)
329
+ buffer = []
330
+ loop do
331
+ c = Reline::IOGate.getc
332
+ buffer << c
333
+ result = @@key_stroke.match_status(buffer)
334
+ case result
335
+ when :matched
336
+ block.(@@key_stroke.expand(buffer).map{ |c| Reline::Key.new(c, c, false) })
337
+ break
338
+ when :matching
339
+ if buffer.size == 1
340
+ begin
341
+ succ_c = nil
342
+ Timeout.timeout(keyseq_timeout / 1000.0) {
343
+ succ_c = Reline::IOGate.getc
344
+ }
345
+ rescue Timeout::Error # cancel matching only when first byte
346
+ block.([Reline::Key.new(c, c, false)])
347
+ break
348
+ else
349
+ if @@key_stroke.match_status(buffer.dup.push(succ_c)) == :unmatched
350
+ if c == "\e".ord
351
+ block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
352
+ else
353
+ block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
354
+ end
355
+ break
356
+ else
357
+ Reline::IOGate.ungetc(succ_c)
358
+ end
359
+ end
360
+ end
361
+ when :unmatched
362
+ if buffer.size == 1 and c == "\e".ord
363
+ read_escaped_key(keyseq_timeout, buffer, block)
364
+ else
365
+ block.(buffer.map{ |c| Reline::Key.new(c, c, false) })
366
+ end
367
+ break
368
+ end
369
+ end
370
+ end
371
+
372
+ def read_escaped_key(keyseq_timeout, buffer, block)
373
+ begin
374
+ escaped_c = nil
375
+ Timeout.timeout(keyseq_timeout / 1000.0) {
376
+ escaped_c = Reline::IOGate.getc
377
+ }
378
+ rescue Timeout::Error # independent ESC
379
+ block.([Reline::Key.new(c, c, false)])
380
+ else
381
+ if escaped_c.nil?
382
+ block.([Reline::Key.new(c, c, false)])
383
+ elsif escaped_c >= 128 # maybe, first byte of multi byte
384
+ block.([Reline::Key.new(c, c, false), Reline::Key.new(escaped_c, escaped_c, false)])
385
+ elsif escaped_c == "\e".ord # escape twice
386
+ block.([Reline::Key.new(c, c, false), Reline::Key.new(c, c, false)])
387
+ else
388
+ block.([Reline::Key.new(escaped_c, escaped_c | 0b10000000, true)])
389
+ end
390
+ end
182
391
  end
183
392
 
184
393
  def may_req_ambiguous_char_width
394
+ @@ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
185
395
  return if @@ambiguous_width
186
- Reline.move_cursor_column(0)
396
+ Reline::IOGate.move_cursor_column(0)
187
397
  print "\u{25bd}"
188
- @@ambiguous_width = Reline.cursor_pos.x
189
- Reline.move_cursor_column(0)
190
- Reline.erase_after_cursor
398
+ @@ambiguous_width = Reline::IOGate.cursor_pos.x
399
+ Reline::IOGate.move_cursor_column(0)
400
+ Reline::IOGate.erase_after_cursor
191
401
  end
192
402
 
193
403
  def self.ambiguous_width
194
404
  @@ambiguous_width
195
405
  end
196
406
  end
407
+
408
+ if Reline::IS_WINDOWS
409
+ require 'reline/windows'
410
+ Reline::IOGate = Reline::Windows
411
+ else
412
+ require 'reline/ansi'
413
+ Reline::IOGate = Reline::ANSI
414
+ end
415
+ require 'reline/general_io'