reline 0.2.7 → 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
@@ -16,8 +16,24 @@ module Reline
16
16
 
17
17
  class ConfigEncodingConversionError < StandardError; end
18
18
 
19
- Key = Struct.new('Key', :char, :combined_char, :with_meta)
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
20
35
  CursorPos = Struct.new(:x, :y)
36
+ DialogRenderInfo = Struct.new(:pos, :contents, :bg_color, :width, :height, :scrollbar, keyword_init: true)
21
37
 
22
38
  class Core
23
39
  ATTR_READER_NAMES = %i(
@@ -44,6 +60,7 @@ module Reline
44
60
 
45
61
  def initialize
46
62
  self.output = STDOUT
63
+ @dialog_proc_list = {}
47
64
  yield self
48
65
  @completion_quote_character = nil
49
66
  @bracketed_paste_finished = false
@@ -106,6 +123,14 @@ module Reline
106
123
  @completion_proc = p
107
124
  end
108
125
 
126
+ def autocompletion
127
+ @config.autocompletion
128
+ end
129
+
130
+ def autocompletion=(val)
131
+ @config.autocompletion = val
132
+ end
133
+
109
134
  def output_modifier_proc=(p)
110
135
  raise ArgumentError unless p.respond_to?(:call) or p.nil?
111
136
  @output_modifier_proc = p
@@ -130,6 +155,17 @@ module Reline
130
155
  @dig_perfect_match_proc = p
131
156
  end
132
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
+
133
169
  def input=(val)
134
170
  raise TypeError unless val.respond_to?(:getc) or val.nil?
135
171
  if val.respond_to?(:getc)
@@ -171,6 +207,46 @@ module Reline
171
207
  Reline::IOGate.get_screen_size
172
208
  end
173
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
+
174
250
  def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
175
251
  unless confirm_multiline_termination
176
252
  raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
@@ -230,6 +306,9 @@ module Reline
230
306
  line_editor.auto_indent_proc = auto_indent_proc
231
307
  line_editor.dig_perfect_match_proc = dig_perfect_match_proc
232
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
233
312
 
234
313
  unless config.test_mode
235
314
  config.read
@@ -240,6 +319,7 @@ module Reline
240
319
  line_editor.rerender
241
320
 
242
321
  begin
322
+ line_editor.set_signal_handlers
243
323
  prev_pasting_state = false
244
324
  loop do
245
325
  prev_pasting_state = Reline::IOGate.in_pasting?
@@ -268,6 +348,11 @@ module Reline
268
348
  line_editor.finalize
269
349
  Reline::IOGate.deprep(otio)
270
350
  raise e
351
+ rescue Exception
352
+ # Including Interrupt
353
+ line_editor.finalize
354
+ Reline::IOGate.deprep(otio)
355
+ raise
271
356
  end
272
357
 
273
358
  line_editor.finalize
@@ -303,25 +388,9 @@ module Reline
303
388
  break
304
389
  when :matching
305
390
  if buffer.size == 1
306
- begin
307
- succ_c = nil
308
- Timeout.timeout(keyseq_timeout / 1000.0) {
309
- succ_c = Reline::IOGate.getc
310
- }
311
- rescue Timeout::Error # cancel matching only when first byte
312
- block.([Reline::Key.new(c, c, false)])
313
- break
314
- else
315
- if key_stroke.match_status(buffer.dup.push(succ_c)) == :unmatched
316
- if c == "\e".ord
317
- block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
318
- else
319
- block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
320
- end
321
- break
322
- else
323
- Reline::IOGate.ungetc(succ_c)
324
- end
391
+ case read_2nd_character_of_key_sequence(keyseq_timeout, buffer, c, block)
392
+ when :break then break
393
+ when :next then next
325
394
  end
326
395
  end
327
396
  when :unmatched
@@ -338,6 +407,38 @@ module Reline
338
407
  end
339
408
  end
340
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
+
341
442
  private def read_escaped_key(keyseq_timeout, c, block)
342
443
  begin
343
444
  escaped_c = nil
@@ -366,7 +467,7 @@ module Reline
366
467
 
367
468
  private def may_req_ambiguous_char_width
368
469
  @ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
369
- return if @ambiguous_width
470
+ return if defined? @ambiguous_width
370
471
  Reline::IOGate.move_cursor_column(0)
371
472
  begin
372
473
  output.write "\u{25bd}"
@@ -389,7 +490,7 @@ module Reline
389
490
  #--------------------------------------------------------
390
491
 
391
492
  (Core::ATTR_READER_NAMES).each { |name|
392
- def_single_delegators :core, "#{name}", "#{name}="
493
+ def_single_delegators :core, :"#{name}", :"#{name}="
393
494
  }
394
495
  def_single_delegators :core, :input=, :output=
395
496
  def_single_delegators :core, :vi_editing_mode, :emacs_editing_mode
@@ -424,6 +525,9 @@ module Reline
424
525
  def_single_delegators :core, :ambiguous_width
425
526
  def_single_delegators :core, :last_incremental_search
426
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=
427
531
 
428
532
  def_single_delegators :core, :readmultiline
429
533
  def_instance_delegators self, :readmultiline
@@ -445,6 +549,7 @@ module Reline
445
549
  core.completer_quote_characters = '"\''
446
550
  core.filename_quote_characters = ""
447
551
  core.special_prefixes = ""
552
+ core.add_dialog_proc(:autocomplete, Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE, Reline::DEFAULT_DIALOG_CONTEXT)
448
553
  }
449
554
  end
450
555
 
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.7
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-08-12 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
@@ -73,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
73
  - !ruby/object:Gem::Version
74
74
  version: '0'
75
75
  requirements: []
76
- rubygems_version: 3.2.22
76
+ rubygems_version: 3.0.3.1
77
77
  signing_key:
78
78
  specification_version: 4
79
79
  summary: Alternative GNU Readline or Editline implementation by pure Ruby.