reline 0.0.7 → 0.1.2

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: 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.