reline 0.0.4 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5871c5fe137b41dce9b91a8c3507c871c00440393af055c1d546a17e2613fc1b
4
- data.tar.gz: d6add41c6538d3cd07b813eab3b8998f57759209f821bf7950bf1a69d1f33966
3
+ metadata.gz: bb3a343a202a2c043257a432f50eb05eeb58b5638a88c24eb095c96a40b03fea
4
+ data.tar.gz: e9c8aec5f70fe3897fb8f835d0d5b228a93a5444b0a7ce804290c19ae44c6c55
5
5
  SHA512:
6
- metadata.gz: 105ed33c513bc997c46f6167da553f098f372272fe026b24afd2b472776dd840484afc0f284cd9d973b3db35940d84d99b4110b3d135d16944269c306ba33003
7
- data.tar.gz: d28e6fe88b8a16022febeba31ead5a4e533f2a50fa65ed93328f35e5c0b2634b01b0e0b35efa278f6bbfd96b4fd42adee2d8e15e3f9d3092de5da2b61b1ef550
6
+ metadata.gz: a0e2a92e1e007d23542313783108250c66ad81b90389c306a9f413b8426ce14a39e14731f974aa8fcabcddaeceae529a70388e6e1a3db6ec7a511278b0c568d7
7
+ data.tar.gz: 0f86f497938921154a475f4b63059a4cda560b44b5b6f9fb63b3b4afcae9de3ea351c1226640eb074546a765b2f51ff5a62bd1af20142a6211c8e74a06f16a57
@@ -16,12 +16,6 @@ module Reline
16
16
  CursorPos = Struct.new(:x, :y)
17
17
 
18
18
  class Core
19
- if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
20
- IS_WINDOWS = true
21
- else
22
- IS_WINDOWS = false
23
- end
24
-
25
19
  ATTR_READER_NAMES = %i(
26
20
  completion_append_character
27
21
  basic_word_break_characters
@@ -38,19 +32,17 @@ module Reline
38
32
  dig_perfect_match_proc
39
33
  ).each(&method(:attr_reader))
40
34
 
41
- ATTR_ACCESSOR_NAMES = %i(
42
- completion_case_fold
43
- ).each(&method(:attr_accessor))
44
-
45
35
  attr_accessor :config
46
36
  attr_accessor :key_stroke
47
37
  attr_accessor :line_editor
48
38
  attr_accessor :ambiguous_width
39
+ attr_accessor :last_incremental_search
49
40
  attr_reader :output
50
41
 
51
42
  def initialize
52
43
  self.output = STDOUT
53
44
  yield self
45
+ @completion_quote_character = nil
54
46
  end
55
47
 
56
48
  def completion_append_character=(val)
@@ -89,23 +81,35 @@ module Reline
89
81
  @special_prefixes = v.encode(Encoding::default_external)
90
82
  end
91
83
 
84
+ def completion_case_fold=(v)
85
+ @config.completion_ignore_case = v
86
+ end
87
+
88
+ def completion_case_fold
89
+ @config.completion_ignore_case
90
+ end
91
+
92
+ def completion_quote_character
93
+ @completion_quote_character
94
+ end
95
+
92
96
  def completion_proc=(p)
93
- raise ArgumentError unless p.is_a?(Proc)
97
+ raise ArgumentError unless p.respond_to?(:call)
94
98
  @completion_proc = p
95
99
  end
96
100
 
97
101
  def output_modifier_proc=(p)
98
- raise ArgumentError unless p.is_a?(Proc)
102
+ raise ArgumentError unless p.respond_to?(:call)
99
103
  @output_modifier_proc = p
100
104
  end
101
105
 
102
106
  def prompt_proc=(p)
103
- raise ArgumentError unless p.is_a?(Proc)
107
+ raise ArgumentError unless p.respond_to?(:call)
104
108
  @prompt_proc = p
105
109
  end
106
110
 
107
111
  def auto_indent_proc=(p)
108
- raise ArgumentError unless p.is_a?(Proc)
112
+ raise ArgumentError unless p.respond_to?(:call)
109
113
  @auto_indent_proc = p
110
114
  end
111
115
 
@@ -114,7 +118,7 @@ module Reline
114
118
  end
115
119
 
116
120
  def dig_perfect_match_proc=(p)
117
- raise ArgumentError unless p.is_a?(Proc)
121
+ raise ArgumentError unless p.respond_to?(:call)
118
122
  @dig_perfect_match_proc = p
119
123
  end
120
124
 
@@ -166,7 +170,7 @@ module Reline
166
170
  inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
167
171
 
168
172
  whole_buffer = line_editor.whole_buffer.dup
169
- whole_buffer.taint
173
+ whole_buffer.taint if RUBY_VERSION < '2.7'
170
174
  if add_hist and whole_buffer and whole_buffer.chomp.size > 0
171
175
  Reline::HISTORY << whole_buffer
172
176
  end
@@ -179,7 +183,7 @@ module Reline
179
183
  inner_readline(prompt, add_hist, false)
180
184
 
181
185
  line = line_editor.line.dup
182
- line.taint
186
+ line.taint if RUBY_VERSION < '2.7'
183
187
  if add_hist and line and line.chomp.size > 0
184
188
  Reline::HISTORY << line.chomp
185
189
  end
@@ -208,6 +212,7 @@ module Reline
208
212
  end
209
213
  line_editor.output = output
210
214
  line_editor.completion_proc = completion_proc
215
+ line_editor.completion_append_character = completion_append_character
211
216
  line_editor.output_modifier_proc = output_modifier_proc
212
217
  line_editor.prompt_proc = prompt_proc
213
218
  line_editor.auto_indent_proc = auto_indent_proc
@@ -260,7 +265,10 @@ module Reline
260
265
  result = key_stroke.match_status(buffer)
261
266
  case result
262
267
  when :matched
263
- block.(key_stroke.expand(buffer).map{ |c| Reline::Key.new(c, c, false) })
268
+ expanded = key_stroke.expand(buffer).map{ |expanded_c|
269
+ Reline::Key.new(expanded_c, expanded_c, false)
270
+ }
271
+ block.(expanded)
264
272
  break
265
273
  when :matching
266
274
  if buffer.size == 1
@@ -289,7 +297,10 @@ module Reline
289
297
  if buffer.size == 1 and c == "\e".ord
290
298
  read_escaped_key(keyseq_timeout, c, block)
291
299
  else
292
- block.(buffer.map{ |c| Reline::Key.new(c, c, false) })
300
+ expanded = buffer.map{ |expanded_c|
301
+ Reline::Key.new(expanded_c, expanded_c, false)
302
+ }
303
+ block.(expanded)
293
304
  end
294
305
  break
295
306
  end
@@ -319,7 +330,7 @@ module Reline
319
330
 
320
331
  private def may_req_ambiguous_char_width
321
332
  @ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
322
- return if @ambiguous_width
333
+ return if ambiguous_width
323
334
  Reline::IOGate.move_cursor_column(0)
324
335
  print "\u{25bd}"
325
336
  @ambiguous_width = Reline::IOGate.cursor_pos.x
@@ -335,13 +346,16 @@ module Reline
335
346
  # Documented API
336
347
  #--------------------------------------------------------
337
348
 
338
- (Core::ATTR_READER_NAMES + Core::ATTR_ACCESSOR_NAMES).each { |name|
349
+ (Core::ATTR_READER_NAMES).each { |name|
339
350
  def_single_delegators :core, "#{name}", "#{name}="
340
351
  }
341
352
  def_single_delegators :core, :input=, :output=
342
353
  def_single_delegators :core, :vi_editing_mode, :emacs_editing_mode
343
354
  def_single_delegators :core, :readline
355
+ def_single_delegators :core, :completion_case_fold, :completion_case_fold=
356
+ def_single_delegators :core, :completion_quote_character
344
357
  def_instance_delegators self, :readline
358
+ private :readline
345
359
 
346
360
 
347
361
  #--------------------------------------------------------
@@ -366,9 +380,12 @@ module Reline
366
380
  def_single_delegator :line_editor, :rerender, :redisplay
367
381
  def_single_delegators :core, :vi_editing_mode?, :emacs_editing_mode?
368
382
  def_single_delegators :core, :ambiguous_width
383
+ def_single_delegators :core, :last_incremental_search
384
+ def_single_delegators :core, :last_incremental_search=
369
385
 
370
386
  def_single_delegators :core, :readmultiline
371
387
  def_instance_delegators self, :readmultiline
388
+ private :readmultiline
372
389
 
373
390
  def self.core
374
391
  @core ||= Core.new { |core|
@@ -392,9 +409,15 @@ module Reline
392
409
  HISTORY = History.new(core.config)
393
410
  end
394
411
 
395
- if Reline::Core::IS_WINDOWS
412
+ if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
396
413
  require 'reline/windows'
397
- Reline::IOGate = Reline::Windows
414
+ if Reline::Windows.get_screen_size == [0, 0]
415
+ # Maybe Mintty on Cygwin
416
+ require 'reline/ansi'
417
+ Reline::IOGate = Reline::ANSI
418
+ else
419
+ Reline::IOGate = Reline::Windows
420
+ end
398
421
  else
399
422
  require 'reline/ansi'
400
423
  Reline::IOGate = Reline::ANSI
@@ -1,3 +1,5 @@
1
+ require 'io/console'
2
+
1
3
  class Reline::ANSI
2
4
  RAW_KEYSTROKE_CONFIG = {
3
5
  [27, 91, 65] => :ed_prev_history, # ↑
@@ -7,6 +9,12 @@ class Reline::ANSI
7
9
  [27, 91, 51, 126] => :key_delete, # Del
8
10
  [27, 91, 49, 126] => :ed_move_to_beg, # Home
9
11
  [27, 91, 52, 126] => :ed_move_to_end, # End
12
+ [27, 91, 72] => :ed_move_to_beg, # Home
13
+ [27, 91, 70] => :ed_move_to_end, # End
14
+ [27, 32] => :em_set_mark, # M-<space>
15
+ [24, 24] => :em_exchange_mark, # C-x C-x TODO also add Windows
16
+ [27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→
17
+ [27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+←
10
18
  }
11
19
 
12
20
  @@input = STDIN
@@ -24,7 +32,8 @@ class Reline::ANSI
24
32
  unless @@buf.empty?
25
33
  return @@buf.shift
26
34
  end
27
- @@input.getbyte
35
+ c = @@input.raw(intr: true, &:getbyte)
36
+ (c == 0x16 && @@input.raw(min: 0, tim: 0, &:getbyte)) || c
28
37
  end
29
38
 
30
39
  def self.ungetc(c)
@@ -56,14 +65,18 @@ class Reline::ANSI
56
65
  def self.cursor_pos
57
66
  begin
58
67
  res = ''
68
+ m = nil
59
69
  @@input.raw do |stdin|
60
70
  @@output << "\e[6n"
61
71
  @@output.flush
62
72
  while (c = stdin.getc) != 'R'
63
73
  res << c if c
64
74
  end
75
+ m = res.match(/\e\[(?<row>\d+);(?<column>\d+)/)
76
+ (m.pre_match + m.post_match).chars.reverse_each do |ch|
77
+ stdin.ungetc ch
78
+ end
65
79
  end
66
- m = res.match(/(?<row>\d+);(?<column>\d+)/)
67
80
  column = m[:column].to_i - 1
68
81
  row = m[:row].to_i - 1
69
82
  rescue Errno::ENOTTY
@@ -116,24 +129,12 @@ class Reline::ANSI
116
129
  def self.prep
117
130
  retrieve_keybuffer
118
131
  int_handle = Signal.trap('INT', 'IGNORE')
119
- otio = `stty -g`.chomp
120
- setting = ' -echo -icrnl cbreak'
121
- stty = `stty -a`
122
- if /-parenb\b/ =~ stty
123
- setting << ' pass8'
124
- end
125
- if /\bdsusp *=/ =~ stty
126
- setting << ' dsusp undef'
127
- end
128
- setting << ' -ixoff'
129
- `stty #{setting}`
130
132
  Signal.trap('INT', int_handle)
131
- otio
133
+ nil
132
134
  end
133
135
 
134
136
  def self.deprep(otio)
135
137
  int_handle = Signal.trap('INT', 'IGNORE')
136
- `stty #{otio}`
137
138
  Signal.trap('INT', int_handle)
138
139
  Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
139
140
  end
@@ -157,7 +157,7 @@ class Reline::Config
157
157
  case directive
158
158
  when 'if'
159
159
  condition = false
160
- case args # TODO: variables
160
+ case args
161
161
  when 'mode'
162
162
  when 'term'
163
163
  when 'version'
@@ -184,7 +184,7 @@ class Reline::Config
184
184
 
185
185
  def bind_variable(name, value)
186
186
  case name
187
- when VARIABLE_NAMES then
187
+ when *VARIABLE_NAMES then
188
188
  variable_name = :"@#{name.tr(?-, ?_)}"
189
189
  instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on')
190
190
  when 'bell-style'
@@ -13,7 +13,7 @@ class Reline::History < Array
13
13
  end
14
14
 
15
15
  def [](index)
16
- index = check_index(index)
16
+ index = check_index(index) unless index.is_a?(Range)
17
17
  super(index)
18
18
  end
19
19
 
@@ -9,7 +9,7 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
9
9
  # 3 ^C
10
10
  :ed_ignore,
11
11
  # 4 ^D
12
- :em_delete_or_list,
12
+ :em_delete,
13
13
  # 5 ^E
14
14
  :ed_move_to_end,
15
15
  # 6 ^F
@@ -39,7 +39,7 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
39
39
  # 18 ^R
40
40
  :ed_search_prev_history,
41
41
  # 19 ^S
42
- :ed_ignore,
42
+ :ed_search_next_history,
43
43
  # 20 ^T
44
44
  :ed_transpose_chars,
45
45
  # 21 ^U
@@ -37,7 +37,7 @@ class Reline::KeyActor::ViCommand < Reline::KeyActor::Base
37
37
  # 17 ^Q
38
38
  :ed_ignore,
39
39
  # 18 ^R
40
- :ed_redisplay,
40
+ :ed_search_prev_history,
41
41
  # 19 ^S
42
42
  :ed_ignore,
43
43
  # 20 ^T
@@ -39,7 +39,7 @@ class Reline::KeyActor::ViInsert < Reline::KeyActor::Base
39
39
  # 18 ^R
40
40
  :ed_search_prev_history,
41
41
  # 19 ^S
42
- :ed_ignore,
42
+ :ed_search_next_history,
43
43
  # 20 ^T
44
44
  :ed_insert,
45
45
  # 21 ^U
@@ -32,7 +32,7 @@ class Reline::KeyStroke
32
32
  end
33
33
 
34
34
  def expand(input)
35
- lhs = key_mapping.keys.select { |lhs| input.start_with? lhs }.sort_by(&:size).reverse.first
35
+ lhs = key_mapping.keys.select { |item| input.start_with? item }.sort_by(&:size).reverse.first
36
36
  return input unless lhs
37
37
  rhs = key_mapping[lhs]
38
38
 
@@ -10,6 +10,7 @@ class Reline::LineEditor
10
10
  attr_reader :byte_pointer
11
11
  attr_accessor :confirm_multiline_termination_proc
12
12
  attr_accessor :completion_proc
13
+ attr_accessor :completion_append_character
13
14
  attr_accessor :output_modifier_proc
14
15
  attr_accessor :prompt_proc
15
16
  attr_accessor :auto_indent_proc
@@ -43,6 +44,7 @@ class Reline::LineEditor
43
44
  COMPLETION = :completion
44
45
  MENU = :menu
45
46
  JOURNEY = :journey
47
+ MENU_WITH_PERFECT_MATCH = :menu_with_perfect_match
46
48
  PERFECT_MATCH = :perfect_match
47
49
  end
48
50
 
@@ -57,6 +59,7 @@ class Reline::LineEditor
57
59
 
58
60
  def initialize(config)
59
61
  @config = config
62
+ @completion_append_character = ''
60
63
  reset_variables
61
64
  end
62
65
 
@@ -113,9 +116,7 @@ class Reline::LineEditor
113
116
  if @line_index.zero?
114
117
  0
115
118
  else
116
- @buffer_of_lines[0..(@line_index - 1)].inject(0) { |result, line|
117
- result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
118
- }
119
+ calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
119
120
  end
120
121
  if @prompt_proc
121
122
  prompt = prompt_list[@line_index]
@@ -140,6 +141,7 @@ class Reline::LineEditor
140
141
 
141
142
  def reset_variables(prompt = '', encoding = Encoding.default_external)
142
143
  @prompt = prompt
144
+ @mark_pointer = nil
143
145
  @encoding = encoding
144
146
  @is_multiline = false
145
147
  @finished = false
@@ -188,6 +190,16 @@ class Reline::LineEditor
188
190
  @is_multiline = false
189
191
  end
190
192
 
193
+ private def calculate_height_by_lines(lines, prompt_list)
194
+ result = 0
195
+ lines.each_with_index { |line, i|
196
+ prompt = ''
197
+ prompt = prompt_list[i] if prompt_list and prompt_list[i]
198
+ result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
199
+ }
200
+ result
201
+ end
202
+
191
203
  private def insert_new_line(cursor_line, next_line)
192
204
  @line = cursor_line
193
205
  @buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding))
@@ -346,9 +358,7 @@ class Reline::LineEditor
346
358
  new_lines = whole_lines
347
359
  end
348
360
  prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
349
- all_height = new_lines.inject(0) { |result, line|
350
- result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
351
- }
361
+ all_height = calculate_height_by_lines(new_lines, prompt_list)
352
362
  diff = all_height - @highest_in_all
353
363
  move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
354
364
  if diff > 0
@@ -388,9 +398,7 @@ class Reline::LineEditor
388
398
  if @line_index.zero?
389
399
  0
390
400
  else
391
- @buffer_of_lines[0..(@line_index - 1)].inject(0) { |result, line|
392
- result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
393
- }
401
+ calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list)
394
402
  end
395
403
  if @prompt_proc
396
404
  prompt = prompt_list[@line_index]
@@ -449,9 +457,7 @@ class Reline::LineEditor
449
457
  if @line_index.zero?
450
458
  0
451
459
  else
452
- new_buffer[0..(@line_index - 1)].inject(0) { |result, line|
453
- result + calculate_height_by_width(prompt_width + calculate_width(line)) # TODO prompt_list
454
- }
460
+ calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list)
455
461
  end
456
462
  @started_from = calculate_height_by_width(prompt_width + @cursor) - 1
457
463
  move_cursor_down(@first_line_started_from + @started_from)
@@ -546,10 +552,14 @@ class Reline::LineEditor
546
552
  private def complete_internal_proc(list, is_menu)
547
553
  preposing, target, postposing = retrieve_completion_block
548
554
  list = list.select { |i|
549
- if i and i.encoding != Encoding::US_ASCII and i.encoding != @encoding
550
- raise Encoding::CompatibilityError
555
+ if i and not Encoding.compatible?(target.encoding, i.encoding)
556
+ raise Encoding::CompatibilityError, "#{target.encoding.name} is not compatible with #{i.encoding.name}"
557
+ end
558
+ if @config.completion_ignore_case
559
+ i&.downcase&.start_with?(target.downcase)
560
+ else
561
+ i&.start_with?(target)
551
562
  end
552
- i&.start_with?(target)
553
563
  }
554
564
  if is_menu
555
565
  menu(target, list)
@@ -566,10 +576,18 @@ class Reline::LineEditor
566
576
  size = [memo_mbchars.size, item_mbchars.size].min
567
577
  result = ''
568
578
  size.times do |i|
569
- if memo_mbchars[i] == item_mbchars[i]
570
- result << memo_mbchars[i]
579
+ if @config.completion_ignore_case
580
+ if memo_mbchars[i].casecmp?(item_mbchars[i])
581
+ result << memo_mbchars[i]
582
+ else
583
+ break
584
+ end
571
585
  else
572
- break
586
+ if memo_mbchars[i] == item_mbchars[i]
587
+ result << memo_mbchars[i]
588
+ else
589
+ break
590
+ end
573
591
  end
574
592
  end
575
593
  result
@@ -577,27 +595,43 @@ class Reline::LineEditor
577
595
  [target, preposing, completed, postposing]
578
596
  end
579
597
 
580
- private def complete(list)
598
+ private def complete(list, just_show_list = false)
581
599
  case @completion_state
582
600
  when CompletionState::NORMAL, CompletionState::JOURNEY
583
601
  @completion_state = CompletionState::COMPLETION
584
602
  when CompletionState::PERFECT_MATCH
585
603
  @dig_perfect_match_proc&.(@perfect_matched)
586
604
  end
587
- is_menu = (@completion_state == CompletionState::MENU)
605
+ if just_show_list
606
+ is_menu = true
607
+ elsif @completion_state == CompletionState::MENU
608
+ is_menu = true
609
+ elsif @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
610
+ is_menu = true
611
+ else
612
+ is_menu = false
613
+ end
588
614
  result = complete_internal_proc(list, is_menu)
615
+ if @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
616
+ @completion_state = CompletionState::PERFECT_MATCH
617
+ end
589
618
  return if result.nil?
590
619
  target, preposing, completed, postposing = result
591
620
  return if completed.nil?
592
- if target <= completed and (@completion_state == CompletionState::COMPLETION or @completion_state == CompletionState::PERFECT_MATCH)
593
- @completion_state = CompletionState::MENU
621
+ if target <= completed and (@completion_state == CompletionState::COMPLETION)
594
622
  if list.include?(completed)
595
- @completion_state = CompletionState::PERFECT_MATCH
623
+ if list.one?
624
+ @completion_state = CompletionState::PERFECT_MATCH
625
+ else
626
+ @completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
627
+ end
596
628
  @perfect_matched = completed
629
+ else
630
+ @completion_state = CompletionState::MENU
597
631
  end
598
- if target < completed
599
- @line = preposing + completed + postposing
600
- line_to_pointer = preposing + completed
632
+ if not just_show_list and target < completed
633
+ @line = preposing + completed + completion_append_character.to_s + postposing
634
+ line_to_pointer = preposing + completed + completion_append_character.to_s
601
635
  @cursor_max = calculate_width(@line)
602
636
  @cursor = calculate_width(line_to_pointer)
603
637
  @byte_pointer = line_to_pointer.bytesize
@@ -607,7 +641,8 @@ class Reline::LineEditor
607
641
 
608
642
  private def move_completed_list(list, direction)
609
643
  case @completion_state
610
- when CompletionState::NORMAL, CompletionState::COMPLETION, CompletionState::MENU
644
+ when CompletionState::NORMAL, CompletionState::COMPLETION,
645
+ CompletionState::MENU, CompletionState::MENU_WITH_PERFECT_MATCH
611
646
  @completion_state = CompletionState::JOURNEY
612
647
  result = retrieve_completion_block
613
648
  return if result.nil?
@@ -650,9 +685,9 @@ class Reline::LineEditor
650
685
  else
651
686
  old_waiting_proc = @waiting_proc
652
687
  old_waiting_operator_proc = @waiting_operator_proc
653
- @waiting_proc = proc { |key|
688
+ @waiting_proc = proc { |k|
654
689
  old_cursor, old_byte_pointer = @cursor, @byte_pointer
655
- old_waiting_proc.(key)
690
+ old_waiting_proc.(k)
656
691
  cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
657
692
  @cursor, @byte_pointer = old_cursor, old_byte_pointer
658
693
  @waiting_operator_proc.(cursor_diff, byte_pointer_diff)
@@ -763,7 +798,7 @@ class Reline::LineEditor
763
798
  end
764
799
 
765
800
  def input_key(key)
766
- if key.nil? or key.char.nil?
801
+ if key.char.nil?
767
802
  if @first_char
768
803
  @line = nil
769
804
  end
@@ -773,20 +808,20 @@ class Reline::LineEditor
773
808
  @first_char = false
774
809
  completion_occurs = false
775
810
  if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
776
- result = retrieve_completion_block
777
- slice = result[1]
778
- result = @completion_proc.(slice) if @completion_proc and slice
779
- if result.is_a?(Array)
780
- completion_occurs = true
781
- complete(result)
811
+ unless @config.disable_completion
812
+ result = call_completion_proc
813
+ if result.is_a?(Array)
814
+ completion_occurs = true
815
+ complete(result)
816
+ end
782
817
  end
783
- elsif @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
784
- result = retrieve_completion_block
785
- slice = result[1]
786
- result = @completion_proc.(slice) if @completion_proc and slice
787
- if result.is_a?(Array)
788
- completion_occurs = true
789
- move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
818
+ elsif not @config.disable_completion and @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
819
+ unless @config.disable_completion
820
+ result = call_completion_proc
821
+ if result.is_a?(Array)
822
+ completion_occurs = true
823
+ move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
824
+ end
790
825
  end
791
826
  elsif Symbol === key.char and respond_to?(key.char, true)
792
827
  process_key(key.char, key.char)
@@ -801,8 +836,33 @@ class Reline::LineEditor
801
836
  end
802
837
  end
803
838
 
839
+ def call_completion_proc
840
+ result = retrieve_completion_block(true)
841
+ slice = result[1]
842
+ result = @completion_proc.(slice) if @completion_proc and slice
843
+ Reline.core.instance_variable_set(:@completion_quote_character, nil)
844
+ result
845
+ end
846
+
804
847
  private def process_auto_indent
805
848
  return if not @check_new_auto_indent and @previous_line_index # move cursor up or down
849
+ if @check_new_auto_indent and @previous_line_index and @previous_line_index > 0 and @line_index > @previous_line_index
850
+ # Fix indent of a line when a newline is inserted to the next
851
+ new_lines = whole_lines(index: @previous_line_index, line: @line)
852
+ new_indent = @auto_indent_proc.(new_lines[0..-3].push(''), @line_index - 1, 0, true)
853
+ md = @line.match(/\A */)
854
+ prev_indent = md[0].count(' ')
855
+ @line = ' ' * new_indent + @line.lstrip
856
+
857
+ new_indent = nil
858
+ result = @auto_indent_proc.(new_lines[0..-2], @line_index - 1, (new_lines[-2].size + 1), false)
859
+ if result
860
+ new_indent = result
861
+ end
862
+ if new_indent&.>= 0
863
+ @line = ' ' * new_indent + @line.lstrip
864
+ end
865
+ end
806
866
  if @previous_line_index
807
867
  new_lines = whole_lines(index: @previous_line_index, line: @line)
808
868
  else
@@ -825,7 +885,7 @@ class Reline::LineEditor
825
885
  @check_new_auto_indent = false
826
886
  end
827
887
 
828
- def retrieve_completion_block
888
+ def retrieve_completion_block(set_completion_quote_character = false)
829
889
  word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
830
890
  quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
831
891
  before = @line.byteslice(0, @byte_pointer)
@@ -844,14 +904,18 @@ class Reline::LineEditor
844
904
  if quote and slice.start_with?(closing_quote)
845
905
  quote = nil
846
906
  i += 1
907
+ rest = nil
908
+ break_pointer = nil
847
909
  elsif quote and slice.start_with?(escaped_quote)
848
910
  # skip
849
911
  i += 2
850
912
  elsif slice =~ quote_characters_regexp # find new "
913
+ rest = $'
851
914
  quote = $&
852
915
  closing_quote = /(?!\\)#{Regexp.escape(quote)}/
853
916
  escaped_quote = /\\#{Regexp.escape(quote)}/
854
917
  i += 1
918
+ break_pointer = i
855
919
  elsif not quote and slice =~ word_break_regexp
856
920
  rest = $'
857
921
  i += 1
@@ -860,15 +924,21 @@ class Reline::LineEditor
860
924
  i += 1
861
925
  end
862
926
  end
927
+ postposing = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
863
928
  if rest
864
929
  preposing = @line.byteslice(0, break_pointer)
865
930
  target = rest
931
+ if set_completion_quote_character and quote
932
+ Reline.core.instance_variable_set(:@completion_quote_character, quote)
933
+ if postposing !~ /(?!\\)#{Regexp.escape(quote)}/ # closing quote
934
+ insert_text(quote)
935
+ end
936
+ end
866
937
  else
867
938
  preposing = ''
868
939
  target = before
869
940
  end
870
- postposing = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
871
- [preposing, target, postposing]
941
+ [preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
872
942
  end
873
943
 
874
944
  def confirm_multiline_termination
@@ -994,8 +1064,8 @@ class Reline::LineEditor
994
1064
  end
995
1065
  width
996
1066
  else
997
- str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |width, gc|
998
- width + Reline::Unicode.get_mbchar_width(gc)
1067
+ str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |w, gc|
1068
+ w + Reline::Unicode.get_mbchar_width(gc)
999
1069
  }
1000
1070
  end
1001
1071
  end
@@ -1121,25 +1191,34 @@ class Reline::LineEditor
1121
1191
  end
1122
1192
  alias_method :end_of_line, :ed_move_to_end
1123
1193
 
1124
- private def ed_search_prev_history(key)
1125
- if @is_multiline
1126
- @line_backup_in_history = whole_buffer
1127
- else
1128
- @line_backup_in_history = @line
1129
- end
1130
- searcher = Fiber.new do
1194
+ private def generate_searcher
1195
+ Fiber.new do |first_key|
1196
+ prev_search_key = first_key
1131
1197
  search_word = String.new(encoding: @encoding)
1132
1198
  multibyte_buf = String.new(encoding: 'ASCII-8BIT')
1133
1199
  last_hit = nil
1200
+ case first_key
1201
+ when "\C-r".ord
1202
+ prompt_name = 'reverse-i-search'
1203
+ when "\C-s".ord
1204
+ prompt_name = 'i-search'
1205
+ end
1134
1206
  loop do
1135
1207
  key = Fiber.yield(search_word)
1208
+ search_again = false
1136
1209
  case key
1137
- when "\C-h".ord, 127
1210
+ when -1 # determined
1211
+ Reline.last_incremental_search = search_word
1212
+ break
1213
+ when "\C-h".ord, "\C-?".ord
1138
1214
  grapheme_clusters = search_word.grapheme_clusters
1139
1215
  if grapheme_clusters.size > 0
1140
1216
  grapheme_clusters.pop
1141
1217
  search_word = grapheme_clusters.join
1142
1218
  end
1219
+ when "\C-r".ord, "\C-s".ord
1220
+ search_again = true if prev_search_key == key
1221
+ prev_search_key = key
1143
1222
  else
1144
1223
  multibyte_buf << key
1145
1224
  if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
@@ -1148,18 +1227,61 @@ class Reline::LineEditor
1148
1227
  end
1149
1228
  end
1150
1229
  hit = nil
1151
- if @line_backup_in_history.include?(search_word)
1230
+ if not search_word.empty? and @line_backup_in_history&.include?(search_word)
1152
1231
  @history_pointer = nil
1153
1232
  hit = @line_backup_in_history
1154
1233
  else
1155
- hit_index = Reline::HISTORY.rindex { |item|
1156
- item.include?(search_word)
1157
- }
1234
+ if search_again
1235
+ if search_word.empty? and Reline.last_incremental_search
1236
+ search_word = Reline.last_incremental_search
1237
+ end
1238
+ if @history_pointer # TODO
1239
+ case prev_search_key
1240
+ when "\C-r".ord
1241
+ history_pointer_base = 0
1242
+ history = Reline::HISTORY[0..(@history_pointer - 1)]
1243
+ when "\C-s".ord
1244
+ history_pointer_base = @history_pointer + 1
1245
+ history = Reline::HISTORY[(@history_pointer + 1)..-1]
1246
+ end
1247
+ else
1248
+ history_pointer_base = 0
1249
+ history = Reline::HISTORY
1250
+ end
1251
+ elsif @history_pointer
1252
+ case prev_search_key
1253
+ when "\C-r".ord
1254
+ history_pointer_base = 0
1255
+ history = Reline::HISTORY[0..@history_pointer]
1256
+ when "\C-s".ord
1257
+ history_pointer_base = @history_pointer
1258
+ history = Reline::HISTORY[@history_pointer..-1]
1259
+ end
1260
+ else
1261
+ history_pointer_base = 0
1262
+ history = Reline::HISTORY
1263
+ end
1264
+ case prev_search_key
1265
+ when "\C-r".ord
1266
+ hit_index = history.rindex { |item|
1267
+ item.include?(search_word)
1268
+ }
1269
+ when "\C-s".ord
1270
+ hit_index = history.index { |item|
1271
+ item.include?(search_word)
1272
+ }
1273
+ end
1158
1274
  if hit_index
1159
- @history_pointer = hit_index
1275
+ @history_pointer = history_pointer_base + hit_index
1160
1276
  hit = Reline::HISTORY[@history_pointer]
1161
1277
  end
1162
1278
  end
1279
+ case prev_search_key
1280
+ when "\C-r".ord
1281
+ prompt_name = 'reverse-i-search'
1282
+ when "\C-s".ord
1283
+ prompt_name = 'i-search'
1284
+ end
1163
1285
  if hit
1164
1286
  if @is_multiline
1165
1287
  @buffer_of_lines = hit.split("\n")
@@ -1167,48 +1289,78 @@ class Reline::LineEditor
1167
1289
  @line_index = @buffer_of_lines.size - 1
1168
1290
  @line = @buffer_of_lines.last
1169
1291
  @rerender_all = true
1170
- @searching_prompt = "(reverse-i-search)`%s'" % [search_word]
1292
+ @searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
1171
1293
  else
1172
1294
  @line = hit
1173
- @searching_prompt = "(reverse-i-search)`%s': %s" % [search_word, hit]
1295
+ @searching_prompt = "(%s)`%s': %s" % [prompt_name, search_word, hit]
1174
1296
  end
1175
1297
  last_hit = hit
1176
1298
  else
1177
1299
  if @is_multiline
1178
1300
  @rerender_all = true
1179
- @searching_prompt = "(failed reverse-i-search)`%s'" % [search_word]
1301
+ @searching_prompt = "(failed %s)`%s'" % [prompt_name, search_word]
1180
1302
  else
1181
- @searching_prompt = "(failed reverse-i-search)`%s': %s" % [search_word, last_hit]
1303
+ @searching_prompt = "(failed %s)`%s': %s" % [prompt_name, search_word, last_hit]
1182
1304
  end
1183
1305
  end
1184
1306
  end
1185
1307
  end
1186
- searcher.resume
1308
+ end
1309
+
1310
+ private def search_history(key)
1311
+ unless @history_pointer
1312
+ if @is_multiline
1313
+ @line_backup_in_history = whole_buffer
1314
+ else
1315
+ @line_backup_in_history = @line
1316
+ end
1317
+ end
1318
+ searcher = generate_searcher
1319
+ searcher.resume(key)
1187
1320
  @searching_prompt = "(reverse-i-search)`': "
1188
- @waiting_proc = ->(key) {
1189
- case key
1190
- when "\C-j".ord, "\C-?".ord
1321
+ @waiting_proc = ->(k) {
1322
+ case k
1323
+ when "\C-j".ord
1191
1324
  if @history_pointer
1192
- @line = Reline::HISTORY[@history_pointer]
1325
+ buffer = Reline::HISTORY[@history_pointer]
1193
1326
  else
1194
- @line = @line_backup_in_history
1327
+ buffer = @line_backup_in_history
1328
+ end
1329
+ if @is_multiline
1330
+ @buffer_of_lines = buffer.split("\n")
1331
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1332
+ @line_index = @buffer_of_lines.size - 1
1333
+ @line = @buffer_of_lines.last
1334
+ @rerender_all = true
1335
+ else
1336
+ @line = buffer
1195
1337
  end
1196
1338
  @searching_prompt = nil
1197
1339
  @waiting_proc = nil
1198
1340
  @cursor_max = calculate_width(@line)
1199
1341
  @cursor = @byte_pointer = 0
1342
+ searcher.resume(-1)
1200
1343
  when "\C-g".ord
1201
- @line = @line_backup_in_history
1344
+ if @is_multiline
1345
+ @buffer_of_lines = @line_backup_in_history.split("\n")
1346
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1347
+ @line_index = @buffer_of_lines.size - 1
1348
+ @line = @buffer_of_lines.last
1349
+ @rerender_all = true
1350
+ else
1351
+ @line = @line_backup_in_history
1352
+ end
1202
1353
  @history_pointer = nil
1203
1354
  @searching_prompt = nil
1204
1355
  @waiting_proc = nil
1205
1356
  @line_backup_in_history = nil
1206
1357
  @cursor_max = calculate_width(@line)
1207
1358
  @cursor = @byte_pointer = 0
1359
+ @rerender_all = true
1208
1360
  else
1209
- chr = key.is_a?(String) ? key : key.chr(Encoding::ASCII_8BIT)
1210
- if chr.match?(/[[:print:]]/) or key == "\C-h".ord or key == 127
1211
- searcher.resume(key)
1361
+ chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
1362
+ if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == "\C-?".ord or k == "\C-r".ord or k == "\C-s".ord
1363
+ searcher.resume(k)
1212
1364
  else
1213
1365
  if @history_pointer
1214
1366
  line = Reline::HISTORY[@history_pointer]
@@ -1230,13 +1382,21 @@ class Reline::LineEditor
1230
1382
  @waiting_proc = nil
1231
1383
  @cursor_max = calculate_width(@line)
1232
1384
  @cursor = @byte_pointer = 0
1385
+ searcher.resume(-1)
1233
1386
  end
1234
1387
  end
1235
1388
  }
1236
1389
  end
1237
1390
 
1391
+ private def ed_search_prev_history(key)
1392
+ search_history(key)
1393
+ end
1394
+ alias_method :reverse_search_history, :ed_search_prev_history
1395
+
1238
1396
  private def ed_search_next_history(key)
1397
+ search_history(key)
1239
1398
  end
1399
+ alias_method :forward_search_history, :ed_search_next_history
1240
1400
 
1241
1401
  private def ed_prev_history(key, arg: 1)
1242
1402
  if @is_multiline and @line_index > 0
@@ -1394,6 +1554,14 @@ class Reline::LineEditor
1394
1554
  @byte_pointer = @line.bytesize
1395
1555
  @cursor = @cursor_max = calculate_width(@line)
1396
1556
  @kill_ring.append(deleted)
1557
+ elsif @is_multiline and @byte_pointer == @line.bytesize and @buffer_of_lines.size > @line_index + 1
1558
+ @cursor = calculate_width(@line)
1559
+ @byte_pointer = @line.bytesize
1560
+ @line += @buffer_of_lines.delete_at(@line_index + 1)
1561
+ @cursor_max = calculate_width(@line)
1562
+ @buffer_of_lines[@line_index] = @line
1563
+ @rerender_all = true
1564
+ @rest_height += 1
1397
1565
  end
1398
1566
  end
1399
1567
 
@@ -1407,7 +1575,7 @@ class Reline::LineEditor
1407
1575
  end
1408
1576
  end
1409
1577
 
1410
- private def em_delete_or_list(key)
1578
+ private def em_delete(key)
1411
1579
  if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
1412
1580
  @line = nil
1413
1581
  if @buffer_of_lines.size > 1
@@ -1432,7 +1600,19 @@ class Reline::LineEditor
1432
1600
  @rest_height += 1
1433
1601
  end
1434
1602
  end
1435
- alias_method :delete_char, :em_delete_or_list
1603
+ alias_method :delete_char, :em_delete
1604
+
1605
+ private def em_delete_or_list(key)
1606
+ if @line.empty? or @byte_pointer < @line.bytesize
1607
+ em_delete(key)
1608
+ else # show completed list
1609
+ result = call_completion_proc
1610
+ if result.is_a?(Array)
1611
+ complete(result, true)
1612
+ end
1613
+ end
1614
+ end
1615
+ alias_method :delete_char_or_list, :em_delete_or_list
1436
1616
 
1437
1617
  private def em_yank(key)
1438
1618
  yanked = @kill_ring.yank
@@ -1736,18 +1916,6 @@ class Reline::LineEditor
1736
1916
  private def vi_yank(key)
1737
1917
  end
1738
1918
 
1739
- private def vi_end_of_transmission(key)
1740
- if @line.empty?
1741
- @line = nil
1742
- if @buffer_of_lines.size > 1
1743
- scroll_down(@highest_in_all - @first_line_started_from)
1744
- end
1745
- Reline::IOGate.move_cursor_column(0)
1746
- @eof = true
1747
- finish
1748
- end
1749
- end
1750
-
1751
1919
  private def vi_list_or_eof(key)
1752
1920
  if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
1753
1921
  @line = nil
@@ -1758,9 +1926,11 @@ class Reline::LineEditor
1758
1926
  @eof = true
1759
1927
  finish
1760
1928
  else
1761
- # TODO: list
1929
+ ed_newline(key)
1762
1930
  end
1763
1931
  end
1932
+ alias_method :vi_end_of_transmission, :vi_list_or_eof
1933
+ alias_method :vi_eof_maybe, :vi_list_or_eof
1764
1934
 
1765
1935
  private def ed_delete_next_char(key, arg: 1)
1766
1936
  byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
@@ -1861,13 +2031,13 @@ class Reline::LineEditor
1861
2031
  end
1862
2032
 
1863
2033
  private def vi_replace_char(key, arg: 1)
1864
- @waiting_proc = ->(key) {
2034
+ @waiting_proc = ->(k) {
1865
2035
  if arg == 1
1866
2036
  byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
1867
2037
  before = @line.byteslice(0, @byte_pointer)
1868
2038
  remaining_point = @byte_pointer + byte_size
1869
2039
  after = @line.byteslice(remaining_point, @line.size - remaining_point)
1870
- @line = before + key.chr + after
2040
+ @line = before + k.chr + after
1871
2041
  @cursor_max = calculate_width(@line)
1872
2042
  @waiting_proc = nil
1873
2043
  elsif arg > 1
@@ -1878,7 +2048,7 @@ class Reline::LineEditor
1878
2048
  before = @line.byteslice(0, @byte_pointer)
1879
2049
  remaining_point = @byte_pointer + byte_size
1880
2050
  after = @line.byteslice(remaining_point, @line.size - remaining_point)
1881
- replaced = key.chr * arg
2051
+ replaced = k.chr * arg
1882
2052
  @line = before + replaced + after
1883
2053
  @byte_pointer += replaced.bytesize
1884
2054
  @cursor += calculate_width(replaced)
@@ -1939,4 +2109,20 @@ class Reline::LineEditor
1939
2109
  arg -= 1
1940
2110
  vi_join_lines(key, arg: arg) if arg > 0
1941
2111
  end
2112
+
2113
+ private def em_set_mark(key)
2114
+ @mark_pointer = [@byte_pointer, @line_index]
2115
+ end
2116
+ alias_method :set_mark, :em_set_mark
2117
+
2118
+ private def em_exchange_mark(key)
2119
+ new_pointer = [@byte_pointer, @line_index]
2120
+ @previous_line_index = @line_index
2121
+ @byte_pointer, @line_index = @mark_pointer
2122
+ @byte_pointer, @line_index = @mark_pointer
2123
+ @cursor = calculate_width(@line.byteslice(0, @byte_pointer))
2124
+ @cursor_max = calculate_width(@line)
2125
+ @mark_pointer = new_pointer
2126
+ end
2127
+ alias_method :exchange_point_and_mark, :em_exchange_mark
1942
2128
  end
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.0.4'
2
+ VERSION = '0.1.1'
3
3
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-29 00:00:00.000000000 Z
11
+ date: 2019-12-25 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: io-console
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.5'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -91,14 +105,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
91
105
  requirements:
92
106
  - - ">="
93
107
  - !ruby/object:Gem::Version
94
- version: '0'
108
+ version: '2.5'
95
109
  required_rubygems_version: !ruby/object:Gem::Requirement
96
110
  requirements:
97
111
  - - ">="
98
112
  - !ruby/object:Gem::Version
99
113
  version: '0'
100
114
  requirements: []
101
- rubygems_version: 3.0.6
115
+ rubygems_version: 3.1.0.pre3
102
116
  signing_key:
103
117
  specification_version: 4
104
118
  summary: Alternative GNU Readline or Editline implementation by pure Ruby.