reline 0.0.7 → 0.1.0

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: d28ea6c28099d6ee05888e139f26785b7f524b2d43b97169a262d3039fe4b12d
4
+ data.tar.gz: c929cedf8040597cdaf6d19ab14aa6724cf4b47dbf4c78b6a1fc2e74637bfc09
5
5
  SHA512:
6
- metadata.gz: 9a5651134aeb6d0eba7e33b4fa6e85c2ac43677432a7b94e8aa24d1c9f05326126c81669aa297f9cb03bea7f92c2ee8600364205c8a8eafa7c6185441472f9d1
7
- data.tar.gz: 63cc8b0b0b936e8ac1a490c9a1c849631b2c4c33c520eb2dd666b9b93b641417f3640542067de4f64ff2e28fd4dd883ed3821c7704bb82a64485ee52d5273c3b
6
+ metadata.gz: f9c4c8c540077105e8c326dd7ee0de350dbf8cd7a0aba14ad467f28b2dbb9c52eade5601e56ada651d2ea6f713e2a47891545bccdd9ea11c43d01dfe315fdd27
7
+ data.tar.gz: 7e8a2c186e0b4c84e3a7ddc62ab08dce768d1ea768ffc91bcf880e0227bb003c2b8fa288e45044cf1ab73144bd7ec46e9ac0faa582721acaf55726fc111706d4
@@ -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
 
@@ -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
@@ -587,20 +602,28 @@ class Reline::LineEditor
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
+ is_menu = (@completion_state == CompletionState::MENU or @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH)
591
606
  result = complete_internal_proc(list, is_menu)
607
+ if @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
608
+ @completion_state = CompletionState::PERFECT_MATCH
609
+ end
592
610
  return if result.nil?
593
611
  target, preposing, completed, postposing = result
594
612
  return if completed.nil?
595
- if target <= completed and (@completion_state == CompletionState::COMPLETION or @completion_state == CompletionState::PERFECT_MATCH)
596
- @completion_state = CompletionState::MENU
613
+ if target <= completed and (@completion_state == CompletionState::COMPLETION)
597
614
  if list.include?(completed)
598
- @completion_state = CompletionState::PERFECT_MATCH
615
+ if list.one?
616
+ @completion_state = CompletionState::PERFECT_MATCH
617
+ else
618
+ @completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
619
+ end
599
620
  @perfect_matched = completed
621
+ else
622
+ @completion_state = CompletionState::MENU
600
623
  end
601
624
  if target < completed
602
- @line = preposing + completed + postposing
603
- line_to_pointer = preposing + completed
625
+ @line = preposing + completed + completion_append_character.to_s + postposing
626
+ line_to_pointer = preposing + completed + completion_append_character.to_s
604
627
  @cursor_max = calculate_width(@line)
605
628
  @cursor = calculate_width(line_to_pointer)
606
629
  @byte_pointer = line_to_pointer.bytesize
@@ -610,7 +633,8 @@ class Reline::LineEditor
610
633
 
611
634
  private def move_completed_list(list, direction)
612
635
  case @completion_state
613
- when CompletionState::NORMAL, CompletionState::COMPLETION, CompletionState::MENU
636
+ when CompletionState::NORMAL, CompletionState::COMPLETION,
637
+ CompletionState::MENU, CompletionState::MENU_WITH_PERFECT_MATCH
614
638
  @completion_state = CompletionState::JOURNEY
615
639
  result = retrieve_completion_block
616
640
  return if result.nil?
@@ -776,20 +800,20 @@ class Reline::LineEditor
776
800
  @first_char = false
777
801
  completion_occurs = false
778
802
  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)
803
+ unless @config.disable_completion
804
+ result = call_completion_proc
805
+ if result.is_a?(Array)
806
+ completion_occurs = true
807
+ complete(result)
808
+ end
785
809
  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)
810
+ elsif not @config.disable_completion and @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
811
+ unless @config.disable_completion
812
+ result = call_completion_proc
813
+ if result.is_a?(Array)
814
+ completion_occurs = true
815
+ move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
816
+ end
793
817
  end
794
818
  elsif Symbol === key.char and respond_to?(key.char, true)
795
819
  process_key(key.char, key.char)
@@ -804,6 +828,14 @@ class Reline::LineEditor
804
828
  end
805
829
  end
806
830
 
831
+ def call_completion_proc
832
+ result = retrieve_completion_block(true)
833
+ slice = result[1]
834
+ result = @completion_proc.(slice) if @completion_proc and slice
835
+ Reline.core.instance_variable_set(:@completion_quote_character, nil)
836
+ result
837
+ end
838
+
807
839
  private def process_auto_indent
808
840
  return if not @check_new_auto_indent and @previous_line_index # move cursor up or down
809
841
  if @check_new_auto_indent and @previous_line_index and @previous_line_index > 0 and @line_index > @previous_line_index
@@ -815,12 +847,9 @@ class Reline::LineEditor
815
847
  @line = ' ' * new_indent + @line.lstrip
816
848
 
817
849
  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
850
+ result = @auto_indent_proc.(new_lines[0..-2], @line_index - 1, (new_lines[-2].size + 1), false)
851
+ if result
852
+ new_indent = result
824
853
  end
825
854
  if new_indent&.>= 0
826
855
  @line = ' ' * new_indent + @line.lstrip
@@ -848,7 +877,7 @@ class Reline::LineEditor
848
877
  @check_new_auto_indent = false
849
878
  end
850
879
 
851
- def retrieve_completion_block
880
+ def retrieve_completion_block(set_completion_quote_character = false)
852
881
  word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
853
882
  quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
854
883
  before = @line.byteslice(0, @byte_pointer)
@@ -867,14 +896,18 @@ class Reline::LineEditor
867
896
  if quote and slice.start_with?(closing_quote)
868
897
  quote = nil
869
898
  i += 1
899
+ rest = nil
900
+ break_pointer = nil
870
901
  elsif quote and slice.start_with?(escaped_quote)
871
902
  # skip
872
903
  i += 2
873
904
  elsif slice =~ quote_characters_regexp # find new "
905
+ rest = $'
874
906
  quote = $&
875
907
  closing_quote = /(?!\\)#{Regexp.escape(quote)}/
876
908
  escaped_quote = /\\#{Regexp.escape(quote)}/
877
909
  i += 1
910
+ break_pointer = i
878
911
  elsif not quote and slice =~ word_break_regexp
879
912
  rest = $'
880
913
  i += 1
@@ -883,15 +916,21 @@ class Reline::LineEditor
883
916
  i += 1
884
917
  end
885
918
  end
919
+ postposing = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
886
920
  if rest
887
921
  preposing = @line.byteslice(0, break_pointer)
888
922
  target = rest
923
+ if set_completion_quote_character and quote
924
+ Reline.core.instance_variable_set(:@completion_quote_character, quote)
925
+ if postposing !~ /(?!\\)#{Regexp.escape(quote)}/ # closing quote
926
+ insert_text(quote)
927
+ end
928
+ end
889
929
  else
890
930
  preposing = ''
891
931
  target = before
892
932
  end
893
- postposing = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
894
- [preposing, target, postposing]
933
+ [preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
895
934
  end
896
935
 
897
936
  def confirm_multiline_termination
@@ -1144,25 +1183,34 @@ class Reline::LineEditor
1144
1183
  end
1145
1184
  alias_method :end_of_line, :ed_move_to_end
1146
1185
 
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
1186
+ private def generate_searcher
1187
+ Fiber.new do |first_key|
1188
+ prev_search_key = first_key
1154
1189
  search_word = String.new(encoding: @encoding)
1155
1190
  multibyte_buf = String.new(encoding: 'ASCII-8BIT')
1156
1191
  last_hit = nil
1192
+ case first_key
1193
+ when "\C-r".ord
1194
+ prompt_name = 'reverse-i-search'
1195
+ when "\C-s".ord
1196
+ prompt_name = 'i-search'
1197
+ end
1157
1198
  loop do
1158
1199
  key = Fiber.yield(search_word)
1200
+ search_again = false
1159
1201
  case key
1160
- when "\C-h".ord, 127
1202
+ when -1 # determined
1203
+ Reline.last_incremental_search = search_word
1204
+ break
1205
+ when "\C-h".ord, "\C-?".ord
1161
1206
  grapheme_clusters = search_word.grapheme_clusters
1162
1207
  if grapheme_clusters.size > 0
1163
1208
  grapheme_clusters.pop
1164
1209
  search_word = grapheme_clusters.join
1165
1210
  end
1211
+ when "\C-r".ord, "\C-s".ord
1212
+ search_again = true if prev_search_key == key
1213
+ prev_search_key = key
1166
1214
  else
1167
1215
  multibyte_buf << key
1168
1216
  if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
@@ -1171,18 +1219,61 @@ class Reline::LineEditor
1171
1219
  end
1172
1220
  end
1173
1221
  hit = nil
1174
- if @line_backup_in_history.include?(search_word)
1222
+ if not search_word.empty? and @line_backup_in_history&.include?(search_word)
1175
1223
  @history_pointer = nil
1176
1224
  hit = @line_backup_in_history
1177
1225
  else
1178
- hit_index = Reline::HISTORY.rindex { |item|
1179
- item.include?(search_word)
1180
- }
1226
+ if search_again
1227
+ if search_word.empty? and Reline.last_incremental_search
1228
+ search_word = Reline.last_incremental_search
1229
+ end
1230
+ if @history_pointer # TODO
1231
+ case prev_search_key
1232
+ when "\C-r".ord
1233
+ history_pointer_base = 0
1234
+ history = Reline::HISTORY[0..(@history_pointer - 1)]
1235
+ when "\C-s".ord
1236
+ history_pointer_base = @history_pointer + 1
1237
+ history = Reline::HISTORY[(@history_pointer + 1)..-1]
1238
+ end
1239
+ else
1240
+ history_pointer_base = 0
1241
+ history = Reline::HISTORY
1242
+ end
1243
+ elsif @history_pointer
1244
+ case prev_search_key
1245
+ when "\C-r".ord
1246
+ history_pointer_base = 0
1247
+ history = Reline::HISTORY[0..@history_pointer]
1248
+ when "\C-s".ord
1249
+ history_pointer_base = @history_pointer
1250
+ history = Reline::HISTORY[@history_pointer..-1]
1251
+ end
1252
+ else
1253
+ history_pointer_base = 0
1254
+ history = Reline::HISTORY
1255
+ end
1256
+ case prev_search_key
1257
+ when "\C-r".ord
1258
+ hit_index = history.rindex { |item|
1259
+ item.include?(search_word)
1260
+ }
1261
+ when "\C-s".ord
1262
+ hit_index = history.index { |item|
1263
+ item.include?(search_word)
1264
+ }
1265
+ end
1181
1266
  if hit_index
1182
- @history_pointer = hit_index
1267
+ @history_pointer = history_pointer_base + hit_index
1183
1268
  hit = Reline::HISTORY[@history_pointer]
1184
1269
  end
1185
1270
  end
1271
+ case prev_search_key
1272
+ when "\C-r".ord
1273
+ prompt_name = 'reverse-i-search'
1274
+ when "\C-s".ord
1275
+ prompt_name = 'i-search'
1276
+ end
1186
1277
  if hit
1187
1278
  if @is_multiline
1188
1279
  @buffer_of_lines = hit.split("\n")
@@ -1190,47 +1281,77 @@ class Reline::LineEditor
1190
1281
  @line_index = @buffer_of_lines.size - 1
1191
1282
  @line = @buffer_of_lines.last
1192
1283
  @rerender_all = true
1193
- @searching_prompt = "(reverse-i-search)`%s'" % [search_word]
1284
+ @searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
1194
1285
  else
1195
1286
  @line = hit
1196
- @searching_prompt = "(reverse-i-search)`%s': %s" % [search_word, hit]
1287
+ @searching_prompt = "(%s)`%s': %s" % [prompt_name, search_word, hit]
1197
1288
  end
1198
1289
  last_hit = hit
1199
1290
  else
1200
1291
  if @is_multiline
1201
1292
  @rerender_all = true
1202
- @searching_prompt = "(failed reverse-i-search)`%s'" % [search_word]
1293
+ @searching_prompt = "(failed %s)`%s'" % [prompt_name, search_word]
1203
1294
  else
1204
- @searching_prompt = "(failed reverse-i-search)`%s': %s" % [search_word, last_hit]
1295
+ @searching_prompt = "(failed %s)`%s': %s" % [prompt_name, search_word, last_hit]
1205
1296
  end
1206
1297
  end
1207
1298
  end
1208
1299
  end
1209
- searcher.resume
1300
+ end
1301
+
1302
+ private def search_history(key)
1303
+ unless @history_pointer
1304
+ if @is_multiline
1305
+ @line_backup_in_history = whole_buffer
1306
+ else
1307
+ @line_backup_in_history = @line
1308
+ end
1309
+ end
1310
+ searcher = generate_searcher
1311
+ searcher.resume(key)
1210
1312
  @searching_prompt = "(reverse-i-search)`': "
1211
1313
  @waiting_proc = ->(k) {
1212
1314
  case k
1213
- when "\C-j".ord, "\C-?".ord
1315
+ when "\C-j".ord
1214
1316
  if @history_pointer
1215
- @line = Reline::HISTORY[@history_pointer]
1317
+ buffer = Reline::HISTORY[@history_pointer]
1216
1318
  else
1217
- @line = @line_backup_in_history
1319
+ buffer = @line_backup_in_history
1320
+ end
1321
+ if @is_multiline
1322
+ @buffer_of_lines = buffer.split("\n")
1323
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1324
+ @line_index = @buffer_of_lines.size - 1
1325
+ @line = @buffer_of_lines.last
1326
+ @rerender_all = true
1327
+ else
1328
+ @line = buffer
1218
1329
  end
1219
1330
  @searching_prompt = nil
1220
1331
  @waiting_proc = nil
1221
1332
  @cursor_max = calculate_width(@line)
1222
1333
  @cursor = @byte_pointer = 0
1334
+ searcher.resume(-1)
1223
1335
  when "\C-g".ord
1224
- @line = @line_backup_in_history
1336
+ if @is_multiline
1337
+ @buffer_of_lines = @line_backup_in_history.split("\n")
1338
+ @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
1339
+ @line_index = @buffer_of_lines.size - 1
1340
+ @line = @buffer_of_lines.last
1341
+ @rerender_all = true
1342
+ else
1343
+ @line = @line_backup_in_history
1344
+ end
1225
1345
  @history_pointer = nil
1226
1346
  @searching_prompt = nil
1227
1347
  @waiting_proc = nil
1228
1348
  @line_backup_in_history = nil
1229
1349
  @cursor_max = calculate_width(@line)
1230
1350
  @cursor = @byte_pointer = 0
1351
+ @rerender_all = true
1231
1352
  else
1232
1353
  chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
1233
- if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == 127
1354
+ if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == "\C-?".ord or k == "\C-r".ord or k == "\C-s".ord
1234
1355
  searcher.resume(k)
1235
1356
  else
1236
1357
  if @history_pointer
@@ -1253,13 +1374,21 @@ class Reline::LineEditor
1253
1374
  @waiting_proc = nil
1254
1375
  @cursor_max = calculate_width(@line)
1255
1376
  @cursor = @byte_pointer = 0
1377
+ searcher.resume(-1)
1256
1378
  end
1257
1379
  end
1258
1380
  }
1259
1381
  end
1260
1382
 
1383
+ private def ed_search_prev_history(key)
1384
+ search_history(key)
1385
+ end
1386
+ alias_method :reverse_search_history, :ed_search_prev_history
1387
+
1261
1388
  private def ed_search_next_history(key)
1389
+ search_history(key)
1262
1390
  end
1391
+ alias_method :forward_search_history, :ed_search_next_history
1263
1392
 
1264
1393
  private def ed_prev_history(key, arg: 1)
1265
1394
  if @is_multiline and @line_index > 0
@@ -1417,6 +1546,14 @@ class Reline::LineEditor
1417
1546
  @byte_pointer = @line.bytesize
1418
1547
  @cursor = @cursor_max = calculate_width(@line)
1419
1548
  @kill_ring.append(deleted)
1549
+ elsif @is_multiline and @byte_pointer == @line.bytesize and @buffer_of_lines.size > @line_index + 1
1550
+ @cursor = calculate_width(@line)
1551
+ @byte_pointer = @line.bytesize
1552
+ @line += @buffer_of_lines.delete_at(@line_index + 1)
1553
+ @cursor_max = calculate_width(@line)
1554
+ @buffer_of_lines[@line_index] = @line
1555
+ @rerender_all = true
1556
+ @rest_height += 1
1420
1557
  end
1421
1558
  end
1422
1559
 
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.0.7'
2
+ VERSION = '0.1.0'
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.0
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-24 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.