reline 0.0.7 → 0.1.0

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