reline 0.0.7 → 0.1.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 985d3ce68260542cce79b3d3291c29ca30ee82250e50089c2de7bb16f60d755d
4
- data.tar.gz: d3444d9e894a66fd2a0e5be4fddd5d36b9b254f0aaff7ed1d590fec26c13b3f6
3
+ metadata.gz: 7612855f90bdd7b5602c7d9e7b2b4de200d6986c45ff64b5a356380c1211b83e
4
+ data.tar.gz: 58406022787d706f400e828e31fde79b3e90c09a106b82a84218e5b748725db4
5
5
  SHA512:
6
- metadata.gz: 9a5651134aeb6d0eba7e33b4fa6e85c2ac43677432a7b94e8aa24d1c9f05326126c81669aa297f9cb03bea7f92c2ee8600364205c8a8eafa7c6185441472f9d1
7
- data.tar.gz: 63cc8b0b0b936e8ac1a490c9a1c849631b2c4c33c520eb2dd666b9b93b641417f3640542067de4f64ff2e28fd4dd883ed3821c7704bb82a64485ee52d5273c3b
6
+ metadata.gz: dcd02ec0e09c6dafa6cf3e9e1dbbeab9befabe033a1ae581628ff01fb4435fce3bb4cbe4cccb5890761e1a58a6637379a5f154ed67f28edaf46a71ba6a00d5c1
7
+ data.tar.gz: 55a61bcab1c41d35002462c80f11d4406426a815bc84ddb680f73c023139fea95e89bc5cc3d1a9e25f1b77ed0986923e5152cfdd489b9e484bd2e459a6602256
@@ -32,19 +32,17 @@ module Reline
32
32
  dig_perfect_match_proc
33
33
  ).each(&method(:attr_reader))
34
34
 
35
- ATTR_ACCESSOR_NAMES = %i(
36
- completion_case_fold
37
- ).each(&method(:attr_accessor))
38
-
39
35
  attr_accessor :config
40
36
  attr_accessor :key_stroke
41
37
  attr_accessor :line_editor
42
38
  attr_accessor :ambiguous_width
39
+ attr_accessor :last_incremental_search
43
40
  attr_reader :output
44
41
 
45
42
  def initialize
46
43
  self.output = STDOUT
47
44
  yield self
45
+ @completion_quote_character = nil
48
46
  end
49
47
 
50
48
  def completion_append_character=(val)
@@ -83,6 +81,18 @@ module Reline
83
81
  @special_prefixes = v.encode(Encoding::default_external)
84
82
  end
85
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
+
86
96
  def completion_proc=(p)
87
97
  raise ArgumentError unless p.respond_to?(:call)
88
98
  @completion_proc = p
@@ -202,6 +212,7 @@ module Reline
202
212
  end
203
213
  line_editor.output = output
204
214
  line_editor.completion_proc = completion_proc
215
+ line_editor.completion_append_character = completion_append_character
205
216
  line_editor.output_modifier_proc = output_modifier_proc
206
217
  line_editor.prompt_proc = prompt_proc
207
218
  line_editor.auto_indent_proc = auto_indent_proc
@@ -335,12 +346,14 @@ 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
345
358
  private :readline
346
359
 
@@ -367,6 +380,8 @@ module Reline
367
380
  def_single_delegator :line_editor, :rerender, :redisplay
368
381
  def_single_delegators :core, :vi_editing_mode?, :emacs_editing_mode?
369
382
  def_single_delegators :core, :ambiguous_width
383
+ def_single_delegators :core, :last_incremental_search
384
+ def_single_delegators :core, :last_incremental_search=
370
385
 
371
386
  def_single_delegators :core, :readmultiline
372
387
  def_instance_delegators self, :readmultiline
@@ -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, # ↑
@@ -11,6 +13,8 @@ class Reline::ANSI
11
13
  [27, 91, 70] => :ed_move_to_end, # End
12
14
  [27, 32] => :em_set_mark, # M-<space>
13
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+←
14
18
  }
15
19
 
16
20
  @@input = STDIN
@@ -28,7 +32,8 @@ class Reline::ANSI
28
32
  unless @@buf.empty?
29
33
  return @@buf.shift
30
34
  end
31
- @@input.getbyte
35
+ c = @@input.raw(intr: true, &:getbyte)
36
+ (c == 0x16 && @@input.raw(min: 0, tim: 0, &:getbyte)) || c
32
37
  end
33
38
 
34
39
  def self.ungetc(c)
@@ -60,14 +65,18 @@ class Reline::ANSI
60
65
  def self.cursor_pos
61
66
  begin
62
67
  res = ''
68
+ m = nil
63
69
  @@input.raw do |stdin|
64
70
  @@output << "\e[6n"
65
71
  @@output.flush
66
72
  while (c = stdin.getc) != 'R'
67
73
  res << c if c
68
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
69
79
  end
70
- m = res.match(/(?<row>\d+);(?<column>\d+)/)
71
80
  column = m[:column].to_i - 1
72
81
  row = m[:row].to_i - 1
73
82
  rescue Errno::ENOTTY
@@ -120,24 +129,12 @@ class Reline::ANSI
120
129
  def self.prep
121
130
  retrieve_keybuffer
122
131
  int_handle = Signal.trap('INT', 'IGNORE')
123
- otio = `stty -g`.chomp
124
- setting = ' -echo -icrnl cbreak'
125
- stty = `stty -a`
126
- if /-parenb\b/ =~ stty
127
- setting << ' pass8'
128
- end
129
- if /\bdsusp *=/ =~ stty
130
- setting << ' dsusp undef'
131
- end
132
- setting << ' -ixoff'
133
- `stty #{setting}`
134
132
  Signal.trap('INT', int_handle)
135
- otio
133
+ nil
136
134
  end
137
135
 
138
136
  def self.deprep(otio)
139
137
  int_handle = Signal.trap('INT', 'IGNORE')
140
- system("stty #{otio}", err: File::NULL)
141
138
  Signal.trap('INT', int_handle)
142
139
  Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler
143
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
@@ -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
 
@@ -549,10 +552,14 @@ class Reline::LineEditor
549
552
  private def complete_internal_proc(list, is_menu)
550
553
  preposing, target, postposing = retrieve_completion_block
551
554
  list = list.select { |i|
552
- if i and i.encoding != Encoding::US_ASCII and i.encoding != @encoding
553
- 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)
554
562
  end
555
- i&.start_with?(target)
556
563
  }
557
564
  if is_menu
558
565
  menu(target, list)
@@ -569,10 +576,18 @@ class Reline::LineEditor
569
576
  size = [memo_mbchars.size, item_mbchars.size].min
570
577
  result = ''
571
578
  size.times do |i|
572
- if memo_mbchars[i] == item_mbchars[i]
573
- 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
574
585
  else
575
- break
586
+ if memo_mbchars[i] == item_mbchars[i]
587
+ result << memo_mbchars[i]
588
+ else
589
+ break
590
+ end
576
591
  end
577
592
  end
578
593
  result
@@ -580,27 +595,43 @@ class Reline::LineEditor
580
595
  [target, preposing, completed, postposing]
581
596
  end
582
597
 
583
- private def complete(list)
598
+ private def complete(list, just_show_list = false)
584
599
  case @completion_state
585
600
  when CompletionState::NORMAL, CompletionState::JOURNEY
586
601
  @completion_state = CompletionState::COMPLETION
587
602
  when CompletionState::PERFECT_MATCH
588
603
  @dig_perfect_match_proc&.(@perfect_matched)
589
604
  end
590
- 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
591
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
592
618
  return if result.nil?
593
619
  target, preposing, completed, postposing = result
594
620
  return if completed.nil?
595
- if target <= completed and (@completion_state == CompletionState::COMPLETION or @completion_state == CompletionState::PERFECT_MATCH)
596
- @completion_state = CompletionState::MENU
621
+ if target <= completed and (@completion_state == CompletionState::COMPLETION)
597
622
  if list.include?(completed)
598
- @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
599
628
  @perfect_matched = completed
629
+ else
630
+ @completion_state = CompletionState::MENU
600
631
  end
601
- if target < completed
602
- @line = preposing + completed + postposing
603
- 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
604
635
  @cursor_max = calculate_width(@line)
605
636
  @cursor = calculate_width(line_to_pointer)
606
637
  @byte_pointer = line_to_pointer.bytesize
@@ -610,7 +641,8 @@ class Reline::LineEditor
610
641
 
611
642
  private def move_completed_list(list, direction)
612
643
  case @completion_state
613
- when CompletionState::NORMAL, CompletionState::COMPLETION, CompletionState::MENU
644
+ when CompletionState::NORMAL, CompletionState::COMPLETION,
645
+ CompletionState::MENU, CompletionState::MENU_WITH_PERFECT_MATCH
614
646
  @completion_state = CompletionState::JOURNEY
615
647
  result = retrieve_completion_block
616
648
  return if result.nil?
@@ -776,20 +808,20 @@ class Reline::LineEditor
776
808
  @first_char = false
777
809
  completion_occurs = false
778
810
  if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
779
- result = retrieve_completion_block
780
- slice = result[1]
781
- result = @completion_proc.(slice) if @completion_proc and slice
782
- if result.is_a?(Array)
783
- completion_occurs = true
784
- 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
785
817
  end
786
- elsif @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
787
- result = retrieve_completion_block
788
- slice = result[1]
789
- result = @completion_proc.(slice) if @completion_proc and slice
790
- if result.is_a?(Array)
791
- completion_occurs = true
792
- 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
793
825
  end
794
826
  elsif Symbol === key.char and respond_to?(key.char, true)
795
827
  process_key(key.char, key.char)
@@ -804,6 +836,14 @@ class Reline::LineEditor
804
836
  end
805
837
  end
806
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
+
807
847
  private def process_auto_indent
808
848
  return if not @check_new_auto_indent and @previous_line_index # move cursor up or down
809
849
  if @check_new_auto_indent and @previous_line_index and @previous_line_index > 0 and @line_index > @previous_line_index
@@ -815,12 +855,9 @@ class Reline::LineEditor
815
855
  @line = ' ' * new_indent + @line.lstrip
816
856
 
817
857
  new_indent = nil
818
- (new_lines[-2].size + 1).times do |n|
819
- result = @auto_indent_proc.(new_lines[0..-2], @line_index - 1, n, false)
820
- if result
821
- new_indent = result
822
- break
823
- end
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
824
861
  end
825
862
  if new_indent&.>= 0
826
863
  @line = ' ' * new_indent + @line.lstrip
@@ -848,7 +885,7 @@ class Reline::LineEditor
848
885
  @check_new_auto_indent = false
849
886
  end
850
887
 
851
- def retrieve_completion_block
888
+ def retrieve_completion_block(set_completion_quote_character = false)
852
889
  word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
853
890
  quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
854
891
  before = @line.byteslice(0, @byte_pointer)
@@ -867,31 +904,42 @@ class Reline::LineEditor
867
904
  if quote and slice.start_with?(closing_quote)
868
905
  quote = nil
869
906
  i += 1
907
+ rest = nil
908
+ break_pointer = nil
870
909
  elsif quote and slice.start_with?(escaped_quote)
871
910
  # skip
872
911
  i += 2
873
912
  elsif slice =~ quote_characters_regexp # find new "
913
+ rest = $'
874
914
  quote = $&
875
915
  closing_quote = /(?!\\)#{Regexp.escape(quote)}/
876
916
  escaped_quote = /\\#{Regexp.escape(quote)}/
877
917
  i += 1
918
+ break_pointer = i
878
919
  elsif not quote and slice =~ word_break_regexp
879
920
  rest = $'
880
921
  i += 1
922
+ before = @line.byteslice(i, @byte_pointer - i)
881
923
  break_pointer = i
882
924
  else
883
925
  i += 1
884
926
  end
885
927
  end
928
+ postposing = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
886
929
  if rest
887
930
  preposing = @line.byteslice(0, break_pointer)
888
931
  target = rest
932
+ if set_completion_quote_character and quote
933
+ Reline.core.instance_variable_set(:@completion_quote_character, quote)
934
+ if postposing !~ /(?!\\)#{Regexp.escape(quote)}/ # closing quote
935
+ insert_text(quote)
936
+ end
937
+ end
889
938
  else
890
939
  preposing = ''
891
940
  target = before
892
941
  end
893
- postposing = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
894
- [preposing, target, postposing]
942
+ [preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
895
943
  end
896
944
 
897
945
  def confirm_multiline_termination
@@ -1144,25 +1192,34 @@ class Reline::LineEditor
1144
1192
  end
1145
1193
  alias_method :end_of_line, :ed_move_to_end
1146
1194
 
1147
- private def ed_search_prev_history(key)
1148
- if @is_multiline
1149
- @line_backup_in_history = whole_buffer
1150
- else
1151
- @line_backup_in_history = @line
1152
- end
1153
- searcher = Fiber.new do
1195
+ private def generate_searcher
1196
+ Fiber.new do |first_key|
1197
+ prev_search_key = first_key
1154
1198
  search_word = String.new(encoding: @encoding)
1155
1199
  multibyte_buf = String.new(encoding: 'ASCII-8BIT')
1156
1200
  last_hit = nil
1201
+ case first_key
1202
+ when "\C-r".ord
1203
+ prompt_name = 'reverse-i-search'
1204
+ when "\C-s".ord
1205
+ prompt_name = 'i-search'
1206
+ end
1157
1207
  loop do
1158
1208
  key = Fiber.yield(search_word)
1209
+ search_again = false
1159
1210
  case key
1160
- when "\C-h".ord, 127
1211
+ when -1 # determined
1212
+ Reline.last_incremental_search = search_word
1213
+ break
1214
+ when "\C-h".ord, "\C-?".ord
1161
1215
  grapheme_clusters = search_word.grapheme_clusters
1162
1216
  if grapheme_clusters.size > 0
1163
1217
  grapheme_clusters.pop
1164
1218
  search_word = grapheme_clusters.join
1165
1219
  end
1220
+ when "\C-r".ord, "\C-s".ord
1221
+ search_again = true if prev_search_key == key
1222
+ prev_search_key = key
1166
1223
  else
1167
1224
  multibyte_buf << key
1168
1225
  if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
@@ -1171,18 +1228,61 @@ class Reline::LineEditor
1171
1228
  end
1172
1229
  end
1173
1230
  hit = nil
1174
- if @line_backup_in_history.include?(search_word)
1231
+ if not search_word.empty? and @line_backup_in_history&.include?(search_word)
1175
1232
  @history_pointer = nil
1176
1233
  hit = @line_backup_in_history
1177
1234
  else
1178
- hit_index = Reline::HISTORY.rindex { |item|
1179
- item.include?(search_word)
1180
- }
1235
+ if search_again
1236
+ if search_word.empty? and Reline.last_incremental_search
1237
+ search_word = Reline.last_incremental_search
1238
+ end
1239
+ if @history_pointer # TODO
1240
+ case prev_search_key
1241
+ when "\C-r".ord
1242
+ history_pointer_base = 0
1243
+ history = Reline::HISTORY[0..(@history_pointer - 1)]
1244
+ when "\C-s".ord
1245
+ history_pointer_base = @history_pointer + 1
1246
+ history = Reline::HISTORY[(@history_pointer + 1)..-1]
1247
+ end
1248
+ else
1249
+ history_pointer_base = 0
1250
+ history = Reline::HISTORY
1251
+ end
1252
+ elsif @history_pointer
1253
+ case prev_search_key
1254
+ when "\C-r".ord
1255
+ history_pointer_base = 0
1256
+ history = Reline::HISTORY[0..@history_pointer]
1257
+ when "\C-s".ord
1258
+ history_pointer_base = @history_pointer
1259
+ history = Reline::HISTORY[@history_pointer..-1]
1260
+ end
1261
+ else
1262
+ history_pointer_base = 0
1263
+ history = Reline::HISTORY
1264
+ end
1265
+ case prev_search_key
1266
+ when "\C-r".ord
1267
+ hit_index = history.rindex { |item|
1268
+ item.include?(search_word)
1269
+ }
1270
+ when "\C-s".ord
1271
+ hit_index = history.index { |item|
1272
+ item.include?(search_word)
1273
+ }
1274
+ end
1181
1275
  if hit_index
1182
- @history_pointer = hit_index
1276
+ @history_pointer = history_pointer_base + hit_index
1183
1277
  hit = Reline::HISTORY[@history_pointer]
1184
1278
  end
1185
1279
  end
1280
+ case prev_search_key
1281
+ when "\C-r".ord
1282
+ prompt_name = 'reverse-i-search'
1283
+ when "\C-s".ord
1284
+ prompt_name = 'i-search'
1285
+ end
1186
1286
  if hit
1187
1287
  if @is_multiline
1188
1288
  @buffer_of_lines = hit.split("\n")
@@ -1190,47 +1290,77 @@ class Reline::LineEditor
1190
1290
  @line_index = @buffer_of_lines.size - 1
1191
1291
  @line = @buffer_of_lines.last
1192
1292
  @rerender_all = true
1193
- @searching_prompt = "(reverse-i-search)`%s'" % [search_word]
1293
+ @searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
1194
1294
  else
1195
1295
  @line = hit
1196
- @searching_prompt = "(reverse-i-search)`%s': %s" % [search_word, hit]
1296
+ @searching_prompt = "(%s)`%s': %s" % [prompt_name, search_word, hit]
1197
1297
  end
1198
1298
  last_hit = hit
1199
1299
  else
1200
1300
  if @is_multiline
1201
1301
  @rerender_all = true
1202
- @searching_prompt = "(failed reverse-i-search)`%s'" % [search_word]
1302
+ @searching_prompt = "(failed %s)`%s'" % [prompt_name, search_word]
1203
1303
  else
1204
- @searching_prompt = "(failed reverse-i-search)`%s': %s" % [search_word, last_hit]
1304
+ @searching_prompt = "(failed %s)`%s': %s" % [prompt_name, search_word, last_hit]
1205
1305
  end
1206
1306
  end
1207
1307
  end
1208
1308
  end
1209
- searcher.resume
1309
+ end
1310
+
1311
+ private def search_history(key)
1312
+ unless @history_pointer
1313
+ if @is_multiline
1314
+ @line_backup_in_history = whole_buffer
1315
+ else
1316
+ @line_backup_in_history = @line
1317
+ end
1318
+ end
1319
+ searcher = generate_searcher
1320
+ searcher.resume(key)
1210
1321
  @searching_prompt = "(reverse-i-search)`': "
1211
1322
  @waiting_proc = ->(k) {
1212
1323
  case k
1213
- when "\C-j".ord, "\C-?".ord
1324
+ when "\C-j".ord
1214
1325
  if @history_pointer
1215
- @line = Reline::HISTORY[@history_pointer]
1326
+ buffer = Reline::HISTORY[@history_pointer]
1216
1327
  else
1217
- @line = @line_backup_in_history
1328
+ buffer = @line_backup_in_history
1329
+ end
1330
+ if @is_multiline
1331
+ @buffer_of_lines = buffer.split("\n")
1332
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1333
+ @line_index = @buffer_of_lines.size - 1
1334
+ @line = @buffer_of_lines.last
1335
+ @rerender_all = true
1336
+ else
1337
+ @line = buffer
1218
1338
  end
1219
1339
  @searching_prompt = nil
1220
1340
  @waiting_proc = nil
1221
1341
  @cursor_max = calculate_width(@line)
1222
1342
  @cursor = @byte_pointer = 0
1343
+ searcher.resume(-1)
1223
1344
  when "\C-g".ord
1224
- @line = @line_backup_in_history
1345
+ if @is_multiline
1346
+ @buffer_of_lines = @line_backup_in_history.split("\n")
1347
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1348
+ @line_index = @buffer_of_lines.size - 1
1349
+ @line = @buffer_of_lines.last
1350
+ @rerender_all = true
1351
+ else
1352
+ @line = @line_backup_in_history
1353
+ end
1225
1354
  @history_pointer = nil
1226
1355
  @searching_prompt = nil
1227
1356
  @waiting_proc = nil
1228
1357
  @line_backup_in_history = nil
1229
1358
  @cursor_max = calculate_width(@line)
1230
1359
  @cursor = @byte_pointer = 0
1360
+ @rerender_all = true
1231
1361
  else
1232
1362
  chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
1233
- if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == 127
1363
+ if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == "\C-?".ord or k == "\C-r".ord or k == "\C-s".ord
1234
1364
  searcher.resume(k)
1235
1365
  else
1236
1366
  if @history_pointer
@@ -1253,13 +1383,21 @@ class Reline::LineEditor
1253
1383
  @waiting_proc = nil
1254
1384
  @cursor_max = calculate_width(@line)
1255
1385
  @cursor = @byte_pointer = 0
1386
+ searcher.resume(-1)
1256
1387
  end
1257
1388
  end
1258
1389
  }
1259
1390
  end
1260
1391
 
1392
+ private def ed_search_prev_history(key)
1393
+ search_history(key)
1394
+ end
1395
+ alias_method :reverse_search_history, :ed_search_prev_history
1396
+
1261
1397
  private def ed_search_next_history(key)
1398
+ search_history(key)
1262
1399
  end
1400
+ alias_method :forward_search_history, :ed_search_next_history
1263
1401
 
1264
1402
  private def ed_prev_history(key, arg: 1)
1265
1403
  if @is_multiline and @line_index > 0
@@ -1417,6 +1555,14 @@ class Reline::LineEditor
1417
1555
  @byte_pointer = @line.bytesize
1418
1556
  @cursor = @cursor_max = calculate_width(@line)
1419
1557
  @kill_ring.append(deleted)
1558
+ elsif @is_multiline and @byte_pointer == @line.bytesize and @buffer_of_lines.size > @line_index + 1
1559
+ @cursor = calculate_width(@line)
1560
+ @byte_pointer = @line.bytesize
1561
+ @line += @buffer_of_lines.delete_at(@line_index + 1)
1562
+ @cursor_max = calculate_width(@line)
1563
+ @buffer_of_lines[@line_index] = @line
1564
+ @rerender_all = true
1565
+ @rest_height += 1
1420
1566
  end
1421
1567
  end
1422
1568
 
@@ -1430,7 +1576,7 @@ class Reline::LineEditor
1430
1576
  end
1431
1577
  end
1432
1578
 
1433
- private def em_delete_or_list(key)
1579
+ private def em_delete(key)
1434
1580
  if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
1435
1581
  @line = nil
1436
1582
  if @buffer_of_lines.size > 1
@@ -1455,7 +1601,19 @@ class Reline::LineEditor
1455
1601
  @rest_height += 1
1456
1602
  end
1457
1603
  end
1458
- alias_method :delete_char, :em_delete_or_list
1604
+ alias_method :delete_char, :em_delete
1605
+
1606
+ private def em_delete_or_list(key)
1607
+ if @line.empty? or @byte_pointer < @line.bytesize
1608
+ em_delete(key)
1609
+ else # show completed list
1610
+ result = call_completion_proc
1611
+ if result.is_a?(Array)
1612
+ complete(result, true)
1613
+ end
1614
+ end
1615
+ end
1616
+ alias_method :delete_char_or_list, :em_delete_or_list
1459
1617
 
1460
1618
  private def em_yank(key)
1461
1619
  yanked = @kill_ring.yank
@@ -1759,18 +1917,6 @@ class Reline::LineEditor
1759
1917
  private def vi_yank(key)
1760
1918
  end
1761
1919
 
1762
- private def vi_end_of_transmission(key)
1763
- if @line.empty?
1764
- @line = nil
1765
- if @buffer_of_lines.size > 1
1766
- scroll_down(@highest_in_all - @first_line_started_from)
1767
- end
1768
- Reline::IOGate.move_cursor_column(0)
1769
- @eof = true
1770
- finish
1771
- end
1772
- end
1773
-
1774
1920
  private def vi_list_or_eof(key)
1775
1921
  if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
1776
1922
  @line = nil
@@ -1781,9 +1927,11 @@ class Reline::LineEditor
1781
1927
  @eof = true
1782
1928
  finish
1783
1929
  else
1784
- # TODO: list
1930
+ ed_newline(key)
1785
1931
  end
1786
1932
  end
1933
+ alias_method :vi_end_of_transmission, :vi_list_or_eof
1934
+ alias_method :vi_eof_maybe, :vi_list_or_eof
1787
1935
 
1788
1936
  private def ed_delete_next_char(key, arg: 1)
1789
1937
  byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.0.7'
2
+ VERSION = '0.1.2'
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.7
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-20 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.2
102
116
  signing_key:
103
117
  specification_version: 4
104
118
  summary: Alternative GNU Readline or Editline implementation by pure Ruby.