reline 0.2.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(
@@ -41,6 +60,7 @@ module Reline
41
60
 
42
61
  def initialize
43
62
  self.output = STDOUT
63
+ @dialog_proc_list = {}
44
64
  yield self
45
65
  @completion_quote_character = nil
46
66
  @bracketed_paste_finished = false
@@ -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')
@@ -227,18 +306,20 @@ module Reline
227
306
  line_editor.auto_indent_proc = auto_indent_proc
228
307
  line_editor.dig_perfect_match_proc = dig_perfect_match_proc
229
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
230
312
 
231
313
  unless config.test_mode
232
314
  config.read
233
315
  config.reset_default_key_bindings
234
- Reline::IOGate::RAW_KEYSTROKE_CONFIG.each_pair do |key, func|
235
- config.add_default_key_binding(key, func)
236
- end
316
+ Reline::IOGate.set_default_key_bindings(config)
237
317
  end
238
318
 
239
319
  line_editor.rerender
240
320
 
241
321
  begin
322
+ line_editor.set_signal_handlers
242
323
  prev_pasting_state = false
243
324
  loop do
244
325
  prev_pasting_state = Reline::IOGate.in_pasting?
@@ -267,17 +348,23 @@ module Reline
267
348
  line_editor.finalize
268
349
  Reline::IOGate.deprep(otio)
269
350
  raise e
351
+ rescue Exception
352
+ # Including Interrupt
353
+ line_editor.finalize
354
+ Reline::IOGate.deprep(otio)
355
+ raise
270
356
  end
271
357
 
272
358
  line_editor.finalize
273
359
  Reline::IOGate.deprep(otio)
274
360
  end
275
361
 
276
- # Keystrokes of GNU Readline will timeout it with the specification of
277
- # "keyseq-timeout" when waiting for the 2nd character after the 1st one.
278
- # If the 2nd character comes after 1st ESC without timeout it has a
279
- # meta-property of meta-key to discriminate modified key with meta-key
280
- # 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.
281
368
  #
282
369
  # GNU Readline will wait for the 2nd character with "keyseq-timeout"
283
370
  # milli-seconds but wait forever after 3rd characters.
@@ -301,25 +388,9 @@ module Reline
301
388
  break
302
389
  when :matching
303
390
  if buffer.size == 1
304
- begin
305
- succ_c = nil
306
- Timeout.timeout(keyseq_timeout / 1000.0) {
307
- succ_c = Reline::IOGate.getc
308
- }
309
- rescue Timeout::Error # cancel matching only when first byte
310
- block.([Reline::Key.new(c, c, false)])
311
- break
312
- else
313
- if key_stroke.match_status(buffer.dup.push(succ_c)) == :unmatched
314
- if c == "\e".ord
315
- block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
316
- else
317
- block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
318
- end
319
- break
320
- else
321
- Reline::IOGate.ungetc(succ_c)
322
- end
391
+ case read_2nd_character_of_key_sequence(keyseq_timeout, buffer, c, block)
392
+ when :break then break
393
+ when :next then next
323
394
  end
324
395
  end
325
396
  when :unmatched
@@ -336,6 +407,38 @@ module Reline
336
407
  end
337
408
  end
338
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
+
339
442
  private def read_escaped_key(keyseq_timeout, c, block)
340
443
  begin
341
444
  escaped_c = nil
@@ -364,7 +467,7 @@ module Reline
364
467
 
365
468
  private def may_req_ambiguous_char_width
366
469
  @ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
367
- return if @ambiguous_width
470
+ return if defined? @ambiguous_width
368
471
  Reline::IOGate.move_cursor_column(0)
369
472
  begin
370
473
  output.write "\u{25bd}"
@@ -387,7 +490,7 @@ module Reline
387
490
  #--------------------------------------------------------
388
491
 
389
492
  (Core::ATTR_READER_NAMES).each { |name|
390
- def_single_delegators :core, "#{name}", "#{name}="
493
+ def_single_delegators :core, :"#{name}", :"#{name}="
391
494
  }
392
495
  def_single_delegators :core, :input=, :output=
393
496
  def_single_delegators :core, :vi_editing_mode, :emacs_editing_mode
@@ -422,6 +525,9 @@ module Reline
422
525
  def_single_delegators :core, :ambiguous_width
423
526
  def_single_delegators :core, :last_incremental_search
424
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=
425
531
 
426
532
  def_single_delegators :core, :readmultiline
427
533
  def_instance_delegators self, :readmultiline
@@ -443,6 +549,7 @@ module Reline
443
549
  core.completer_quote_characters = '"\''
444
550
  core.filename_quote_characters = ""
445
551
  core.special_prefixes = ""
552
+ core.add_dialog_proc(:autocomplete, Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE, Reline::DEFAULT_DIALOG_CONTEXT)
446
553
  }
447
554
  end
448
555
 
@@ -455,17 +562,25 @@ module Reline
455
562
  end
456
563
  end
457
564
 
565
+ require 'reline/general_io'
458
566
  if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
459
567
  require 'reline/windows'
460
568
  if Reline::Windows.msys_tty?
461
- require 'reline/ansi'
462
- 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
463
575
  else
464
576
  Reline::IOGate = Reline::Windows
465
577
  end
466
578
  else
467
- require 'reline/ansi'
468
- Reline::IOGate = Reline::ANSI
579
+ Reline::IOGate = if $stdout.isatty
580
+ require 'reline/ansi'
581
+ Reline::ANSI
582
+ else
583
+ Reline::GeneralIO
584
+ end
469
585
  end
470
586
  Reline::HISTORY = Reline::History.new(Reline.core.config)
471
- require 'reline/general_io'
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.2.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: 2021-04-02 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,62 +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
- - !ruby/object:Gem::Dependency
70
- name: yamatanooroti
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: 0.0.6
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: 0.0.6
83
27
  description: Alternative GNU Readline or Editline implementation by pure Ruby.
84
28
  email:
85
29
  - aycabta@gmail.com
@@ -104,6 +48,7 @@ files:
104
48
  - lib/reline/kill_ring.rb
105
49
  - lib/reline/line_editor.rb
106
50
  - lib/reline/sibori.rb
51
+ - lib/reline/terminfo.rb
107
52
  - lib/reline/unicode.rb
108
53
  - lib/reline/unicode/east_asian_width.rb
109
54
  - lib/reline/version.rb
@@ -128,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
128
73
  - !ruby/object:Gem::Version
129
74
  version: '0'
130
75
  requirements: []
131
- rubygems_version: 3.2.0.rc.1
76
+ rubygems_version: 3.0.3.1
132
77
  signing_key:
133
78
  specification_version: 4
134
79
  summary: Alternative GNU Readline or Editline implementation by pure Ruby.