reline 0.2.7 → 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
@@ -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.