reline 0.2.5 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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.