reline 0.1.5 → 0.3.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.
data/lib/reline.rb CHANGED
@@ -7,14 +7,33 @@ require 'reline/key_actor'
7
7
  require 'reline/key_stroke'
8
8
  require 'reline/line_editor'
9
9
  require 'reline/history'
10
+ require 'reline/terminfo'
10
11
  require 'rbconfig'
11
12
 
12
13
  module Reline
13
14
  FILENAME_COMPLETION_PROC = nil
14
15
  USERNAME_COMPLETION_PROC = nil
15
16
 
16
- Key = Struct.new('Key', :char, :combined_char, :with_meta)
17
+ class ConfigEncodingConversionError < StandardError; end
18
+
19
+ Key = Struct.new('Key', :char, :combined_char, :with_meta) do
20
+ def match?(other)
21
+ case other
22
+ when Reline::Key
23
+ (other.char.nil? or char.nil? or char == other.char) and
24
+ (other.combined_char.nil? or combined_char.nil? or combined_char == other.combined_char) and
25
+ (other.with_meta.nil? or with_meta.nil? or with_meta == other.with_meta)
26
+ when Integer, Symbol
27
+ (combined_char and combined_char == other) or
28
+ (combined_char.nil? and char and char == other)
29
+ else
30
+ false
31
+ end
32
+ end
33
+ alias_method :==, :match?
34
+ end
17
35
  CursorPos = Struct.new(:x, :y)
36
+ DialogRenderInfo = Struct.new(:pos, :contents, :bg_color, :width, :height, :scrollbar, keyword_init: true)
18
37
 
19
38
  class Core
20
39
  ATTR_READER_NAMES = %i(
@@ -36,14 +55,15 @@ module Reline
36
55
  attr_accessor :config
37
56
  attr_accessor :key_stroke
38
57
  attr_accessor :line_editor
39
- attr_accessor :ambiguous_width
40
58
  attr_accessor :last_incremental_search
41
59
  attr_reader :output
42
60
 
43
61
  def initialize
44
62
  self.output = STDOUT
63
+ @dialog_proc_list = {}
45
64
  yield self
46
65
  @completion_quote_character = nil
66
+ @bracketed_paste_finished = false
47
67
  end
48
68
 
49
69
  def encoding
@@ -103,6 +123,14 @@ module Reline
103
123
  @completion_proc = p
104
124
  end
105
125
 
126
+ def autocompletion
127
+ @config.autocompletion
128
+ end
129
+
130
+ def autocompletion=(val)
131
+ @config.autocompletion = val
132
+ end
133
+
106
134
  def output_modifier_proc=(p)
107
135
  raise ArgumentError unless p.respond_to?(:call) or p.nil?
108
136
  @output_modifier_proc = p
@@ -127,6 +155,17 @@ module Reline
127
155
  @dig_perfect_match_proc = p
128
156
  end
129
157
 
158
+ DialogProc = Struct.new(:dialog_proc, :context)
159
+ def add_dialog_proc(name_sym, p, context = nil)
160
+ raise ArgumentError unless p.respond_to?(:call) or p.nil?
161
+ raise ArgumentError unless name_sym.instance_of?(Symbol)
162
+ @dialog_proc_list[name_sym] = DialogProc.new(p, context)
163
+ end
164
+
165
+ def dialog_proc(name_sym)
166
+ @dialog_proc_list[name_sym]
167
+ end
168
+
130
169
  def input=(val)
131
170
  raise TypeError unless val.respond_to?(:getc) or val.nil?
132
171
  if val.respond_to?(:getc)
@@ -168,6 +207,46 @@ module Reline
168
207
  Reline::IOGate.get_screen_size
169
208
  end
170
209
 
210
+ Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE = ->() {
211
+ # autocomplete
212
+ return nil unless config.autocompletion
213
+ if just_cursor_moving and completion_journey_data.nil?
214
+ # Auto complete starts only when edited
215
+ return nil
216
+ end
217
+ pre, target, post = retrieve_completion_block(true)
218
+ if target.nil? or target.empty? or (completion_journey_data&.pointer == -1 and target.size <= 3)
219
+ return nil
220
+ end
221
+ if completion_journey_data and completion_journey_data.list
222
+ result = completion_journey_data.list.dup
223
+ result.shift
224
+ pointer = completion_journey_data.pointer - 1
225
+ else
226
+ result = call_completion_proc_with_checking_args(pre, target, post)
227
+ pointer = nil
228
+ end
229
+ if result and result.size == 1 and result[0] == target and pointer != 0
230
+ result = nil
231
+ end
232
+ target_width = Reline::Unicode.calculate_width(target)
233
+ x = cursor_pos.x - target_width
234
+ if x < 0
235
+ x = screen_width + x
236
+ y = -1
237
+ else
238
+ y = 0
239
+ end
240
+ cursor_pos_to_render = Reline::CursorPos.new(x, y)
241
+ if context and context.is_a?(Array)
242
+ context.clear
243
+ context.push(cursor_pos_to_render, result, pointer, dialog)
244
+ end
245
+ dialog.pointer = pointer
246
+ DialogRenderInfo.new(pos: cursor_pos_to_render, contents: result, scrollbar: true, height: 15)
247
+ }
248
+ Reline::DEFAULT_DIALOG_CONTEXT = Array.new
249
+
171
250
  def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
172
251
  unless confirm_multiline_termination
173
252
  raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
@@ -199,7 +278,11 @@ module Reline
199
278
 
200
279
  private def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
201
280
  if ENV['RELINE_STDERR_TTY']
202
- $stderr.reopen(ENV['RELINE_STDERR_TTY'], 'w')
281
+ if Reline::IOGate.win?
282
+ $stderr = File.open(ENV['RELINE_STDERR_TTY'], 'a')
283
+ else
284
+ $stderr.reopen(ENV['RELINE_STDERR_TTY'], 'w')
285
+ end
203
286
  $stderr.sync = true
204
287
  $stderr.puts "Reline is used by #{Process.pid}"
205
288
  end
@@ -223,25 +306,39 @@ module Reline
223
306
  line_editor.auto_indent_proc = auto_indent_proc
224
307
  line_editor.dig_perfect_match_proc = dig_perfect_match_proc
225
308
  line_editor.pre_input_hook = pre_input_hook
309
+ @dialog_proc_list.each_pair do |name_sym, d|
310
+ line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
311
+ end
226
312
 
227
313
  unless config.test_mode
228
314
  config.read
229
315
  config.reset_default_key_bindings
230
- Reline::IOGate::RAW_KEYSTROKE_CONFIG.each_pair do |key, func|
231
- config.add_default_key_binding(key, func)
232
- end
316
+ Reline::IOGate.set_default_key_bindings(config)
233
317
  end
234
318
 
235
319
  line_editor.rerender
236
320
 
237
321
  begin
322
+ line_editor.set_signal_handlers
323
+ prev_pasting_state = false
238
324
  loop do
325
+ prev_pasting_state = Reline::IOGate.in_pasting?
239
326
  read_io(config.keyseq_timeout) { |inputs|
327
+ line_editor.set_pasting_state(Reline::IOGate.in_pasting?)
240
328
  inputs.each { |c|
241
329
  line_editor.input_key(c)
242
330
  line_editor.rerender
243
331
  }
332
+ if @bracketed_paste_finished
333
+ line_editor.rerender_all
334
+ @bracketed_paste_finished = false
335
+ end
244
336
  }
337
+ if prev_pasting_state == true and not Reline::IOGate.in_pasting? and not line_editor.finished?
338
+ line_editor.set_pasting_state(false)
339
+ prev_pasting_state = false
340
+ line_editor.rerender_all
341
+ end
245
342
  break if line_editor.finished?
246
343
  end
247
344
  Reline::IOGate.move_cursor_column(0)
@@ -251,17 +348,23 @@ module Reline
251
348
  line_editor.finalize
252
349
  Reline::IOGate.deprep(otio)
253
350
  raise e
351
+ rescue Exception
352
+ # Including Interrupt
353
+ line_editor.finalize
354
+ Reline::IOGate.deprep(otio)
355
+ raise
254
356
  end
255
357
 
256
358
  line_editor.finalize
257
359
  Reline::IOGate.deprep(otio)
258
360
  end
259
361
 
260
- # Keystrokes of GNU Readline will timeout it with the specification of
261
- # "keyseq-timeout" when waiting for the 2nd character after the 1st one.
262
- # If the 2nd character comes after 1st ESC without timeout it has a
263
- # meta-property of meta-key to discriminate modified key with meta-key
264
- # from multibyte characters that come with 8th bit on.
362
+ # GNU Readline waits for "keyseq-timeout" milliseconds to see if the ESC
363
+ # is followed by a character, and times out and treats it as a standalone
364
+ # ESC if the second character does not arrive. If the second character
365
+ # comes before timed out, it is treated as a modifier key with the
366
+ # meta-property of meta-key, so that it can be distinguished from
367
+ # multibyte characters with the 8th bit turned on.
265
368
  #
266
369
  # GNU Readline will wait for the 2nd character with "keyseq-timeout"
267
370
  # milli-seconds but wait forever after 3rd characters.
@@ -269,8 +372,13 @@ module Reline
269
372
  buffer = []
270
373
  loop do
271
374
  c = Reline::IOGate.getc
272
- buffer << c
273
- result = key_stroke.match_status(buffer)
375
+ if c == -1
376
+ result = :unmatched
377
+ @bracketed_paste_finished = true
378
+ else
379
+ buffer << c
380
+ result = key_stroke.match_status(buffer)
381
+ end
274
382
  case result
275
383
  when :matched
276
384
  expanded = key_stroke.expand(buffer).map{ |expanded_c|
@@ -280,25 +388,9 @@ module Reline
280
388
  break
281
389
  when :matching
282
390
  if buffer.size == 1
283
- begin
284
- succ_c = nil
285
- Timeout.timeout(keyseq_timeout / 1000.0) {
286
- succ_c = Reline::IOGate.getc
287
- }
288
- rescue Timeout::Error # cancel matching only when first byte
289
- block.([Reline::Key.new(c, c, false)])
290
- break
291
- else
292
- if key_stroke.match_status(buffer.dup.push(succ_c)) == :unmatched
293
- if c == "\e".ord
294
- block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
295
- else
296
- block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
297
- end
298
- break
299
- else
300
- Reline::IOGate.ungetc(succ_c)
301
- end
391
+ case read_2nd_character_of_key_sequence(keyseq_timeout, buffer, c, block)
392
+ when :break then break
393
+ when :next then next
302
394
  end
303
395
  end
304
396
  when :unmatched
@@ -315,6 +407,38 @@ module Reline
315
407
  end
316
408
  end
317
409
 
410
+ private def read_2nd_character_of_key_sequence(keyseq_timeout, buffer, c, block)
411
+ begin
412
+ succ_c = nil
413
+ Timeout.timeout(keyseq_timeout / 1000.0) {
414
+ succ_c = Reline::IOGate.getc
415
+ }
416
+ rescue Timeout::Error # cancel matching only when first byte
417
+ block.([Reline::Key.new(c, c, false)])
418
+ return :break
419
+ else
420
+ case key_stroke.match_status(buffer.dup.push(succ_c))
421
+ when :unmatched
422
+ if c == "\e".ord
423
+ block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
424
+ else
425
+ block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
426
+ end
427
+ return :break
428
+ when :matching
429
+ Reline::IOGate.ungetc(succ_c)
430
+ return :next
431
+ when :matched
432
+ buffer << succ_c
433
+ expanded = key_stroke.expand(buffer).map{ |expanded_c|
434
+ Reline::Key.new(expanded_c, expanded_c, false)
435
+ }
436
+ block.(expanded)
437
+ return :break
438
+ end
439
+ end
440
+ end
441
+
318
442
  private def read_escaped_key(keyseq_timeout, c, block)
319
443
  begin
320
444
  escaped_c = nil
@@ -336,9 +460,14 @@ module Reline
336
460
  end
337
461
  end
338
462
 
463
+ def ambiguous_width
464
+ may_req_ambiguous_char_width unless defined? @ambiguous_width
465
+ @ambiguous_width
466
+ end
467
+
339
468
  private def may_req_ambiguous_char_width
340
469
  @ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
341
- return if ambiguous_width
470
+ return if defined? @ambiguous_width
342
471
  Reline::IOGate.move_cursor_column(0)
343
472
  begin
344
473
  output.write "\u{25bd}"
@@ -361,7 +490,7 @@ module Reline
361
490
  #--------------------------------------------------------
362
491
 
363
492
  (Core::ATTR_READER_NAMES).each { |name|
364
- def_single_delegators :core, "#{name}", "#{name}="
493
+ def_single_delegators :core, :"#{name}", :"#{name}="
365
494
  }
366
495
  def_single_delegators :core, :input=, :output=
367
496
  def_single_delegators :core, :vi_editing_mode, :emacs_editing_mode
@@ -396,6 +525,9 @@ module Reline
396
525
  def_single_delegators :core, :ambiguous_width
397
526
  def_single_delegators :core, :last_incremental_search
398
527
  def_single_delegators :core, :last_incremental_search=
528
+ def_single_delegators :core, :add_dialog_proc
529
+ def_single_delegators :core, :dialog_proc
530
+ def_single_delegators :core, :autocompletion, :autocompletion=
399
531
 
400
532
  def_single_delegators :core, :readmultiline
401
533
  def_instance_delegators self, :readmultiline
@@ -417,25 +549,38 @@ module Reline
417
549
  core.completer_quote_characters = '"\''
418
550
  core.filename_quote_characters = ""
419
551
  core.special_prefixes = ""
552
+ core.add_dialog_proc(:autocomplete, Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE, Reline::DEFAULT_DIALOG_CONTEXT)
420
553
  }
421
554
  end
422
555
 
556
+ def self.ungetc(c)
557
+ Reline::IOGate.ungetc(c)
558
+ end
559
+
423
560
  def self.line_editor
424
561
  core.line_editor
425
562
  end
426
563
  end
427
564
 
565
+ require 'reline/general_io'
428
566
  if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
429
567
  require 'reline/windows'
430
568
  if Reline::Windows.msys_tty?
431
- require 'reline/ansi'
432
- Reline::IOGate = Reline::ANSI
569
+ Reline::IOGate = if ENV['TERM'] == 'dumb'
570
+ Reline::GeneralIO
571
+ else
572
+ require 'reline/ansi'
573
+ Reline::ANSI
574
+ end
433
575
  else
434
576
  Reline::IOGate = Reline::Windows
435
577
  end
436
578
  else
437
- require 'reline/ansi'
438
- Reline::IOGate = Reline::ANSI
579
+ Reline::IOGate = if $stdout.isatty
580
+ require 'reline/ansi'
581
+ Reline::ANSI
582
+ else
583
+ Reline::GeneralIO
584
+ end
439
585
  end
440
586
  Reline::HISTORY = Reline::History.new(Reline.core.config)
441
- require 'reline/general_io'
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2009, Park Heesob
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ * Neither the name of Park Heesob nor the names of its contributors
13
+ may be used to endorse or promote products derived from this software
14
+ without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-13 00:00:00.000000000 Z
11
+ date: 2022-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -24,48 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.5'
27
- - !ruby/object:Gem::Dependency
28
- name: bundler
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: rake
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: test-unit
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
27
  description: Alternative GNU Readline or Editline implementation by pure Ruby.
70
28
  email:
71
29
  - aycabta@gmail.com
@@ -89,10 +47,13 @@ files:
89
47
  - lib/reline/key_stroke.rb
90
48
  - lib/reline/kill_ring.rb
91
49
  - lib/reline/line_editor.rb
50
+ - lib/reline/sibori.rb
51
+ - lib/reline/terminfo.rb
92
52
  - lib/reline/unicode.rb
93
53
  - lib/reline/unicode/east_asian_width.rb
94
54
  - lib/reline/version.rb
95
55
  - lib/reline/windows.rb
56
+ - license_of_rb-readline
96
57
  homepage: https://github.com/ruby/reline
97
58
  licenses:
98
59
  - Ruby
@@ -112,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
73
  - !ruby/object:Gem::Version
113
74
  version: '0'
114
75
  requirements: []
115
- rubygems_version: 3.1.2
76
+ rubygems_version: 3.0.3.1
116
77
  signing_key:
117
78
  specification_version: 4
118
79
  summary: Alternative GNU Readline or Editline implementation by pure Ruby.