reline 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b325efda8753fa1ecea3b1dabfd685416016ee334876673bf0d965c3a02095d
4
- data.tar.gz: 4b22612ebce12cc96593f98535bfb9223eebbded388931ccbd8bbdb4d13ce1d1
3
+ metadata.gz: 47d262bd9b8ab9b3fb314863af068783c217d53177f947372eb979df2ceaf3cf
4
+ data.tar.gz: 8112b4441c7946a697adf3d16ec79531fc744fac132ea3cca89ef16b9b6518d3
5
5
  SHA512:
6
- metadata.gz: 29f19adce6163f38399da6119c3605039593d244c7119d601b413279a678cd87a5ad5eba5679f2ea7ccc1bdf7b4743987b2840cf4c0f69fa316c0d2fa84b5f45
7
- data.tar.gz: be0b1ad84d01936ff721b3a18f196d0ee19817da6baf9200e4e6a96681e4b87594c55d1aa71071fcda63f35c4c8d0aefa7aa7a016c62c538d165e19f82e7d3b8
6
+ metadata.gz: 22570f23bfa74602ef627bf977982304b0e5849cbc52abb33f321a32b3860348eabb3efd88bfcfa6b46032433a012813a0fa9aa70422438d0f0694a724ca3055
7
+ data.tar.gz: 6a30e3c69c1a64149a5cd0517e3c46c6dfb9d724532ded4ab4475c6b595bcf5cdf6b6ffd1b9d32156b8a8a447885bb918d35f78be90934a2e2513f87fdf40bb0
data/lib/reline/ansi.rb CHANGED
@@ -151,7 +151,11 @@ class Reline::ANSI
151
151
  end
152
152
 
153
153
  def self.with_raw_input
154
- @@input.raw { yield }
154
+ if @@input.tty?
155
+ @@input.raw(intr: true) { yield }
156
+ else
157
+ yield
158
+ end
155
159
  end
156
160
 
157
161
  @@buf = []
@@ -159,11 +163,13 @@ class Reline::ANSI
159
163
  unless @@buf.empty?
160
164
  return @@buf.shift
161
165
  end
162
- until c = @@input.raw(intr: true) { @@input.wait_readable(0.1) && @@input.getbyte }
163
- timeout_second -= 0.1
166
+ until @@input.wait_readable(0.01)
167
+ timeout_second -= 0.01
164
168
  return nil if timeout_second <= 0
165
- Reline.core.line_editor.resize
169
+
170
+ Reline.core.line_editor.handle_signal
166
171
  end
172
+ c = @@input.getbyte
167
173
  (c == 0x16 && @@input.raw(min: 0, time: 0, &:getbyte)) || c
168
174
  rescue Errno::EIO
169
175
  # Maybe the I/O has been closed.
@@ -309,7 +315,7 @@ class Reline::ANSI
309
315
  end
310
316
 
311
317
  def self.hide_cursor
312
- if Reline::Terminfo.enabled?
318
+ if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
313
319
  begin
314
320
  @@output.write Reline::Terminfo.tigetstr('civis')
315
321
  rescue Reline::Terminfo::TerminfoError
@@ -321,7 +327,7 @@ class Reline::ANSI
321
327
  end
322
328
 
323
329
  def self.show_cursor
324
- if Reline::Terminfo.enabled?
330
+ if Reline::Terminfo.enabled? && Reline::Terminfo.term_supported?
325
331
  begin
326
332
  @@output.write Reline::Terminfo.tigetstr('cnorm')
327
333
  rescue Reline::Terminfo::TerminfoError
@@ -46,6 +46,7 @@ class Reline::GeneralIO
46
46
  end
47
47
  c = nil
48
48
  loop do
49
+ Reline.core.line_editor.handle_signal
49
50
  result = @@input.wait_readable(0.1)
50
51
  next if result.nil?
51
52
  c = @@input.read(1)
@@ -41,15 +41,36 @@ class Reline::LineEditor
41
41
  NORMAL = :normal
42
42
  COMPLETION = :completion
43
43
  MENU = :menu
44
- JOURNEY = :journey
45
44
  MENU_WITH_PERFECT_MATCH = :menu_with_perfect_match
46
45
  PERFECT_MATCH = :perfect_match
47
46
  end
48
47
 
49
48
  RenderedScreen = Struct.new(:base_y, :lines, :cursor_y, keyword_init: true)
50
49
 
51
- CompletionJourneyData = Struct.new(:preposing, :postposing, :list, :pointer)
52
- MenuInfo = Struct.new(:target, :list)
50
+ CompletionJourneyState = Struct.new(:line_index, :pre, :target, :post, :list, :pointer)
51
+
52
+ class MenuInfo
53
+ attr_reader :list
54
+
55
+ def initialize(list)
56
+ @list = list
57
+ end
58
+
59
+ def lines(screen_width)
60
+ return [] if @list.empty?
61
+
62
+ list = @list.sort
63
+ sizes = list.map { |item| Reline::Unicode.calculate_width(item) }
64
+ item_width = sizes.max + 2
65
+ num_cols = [screen_width / item_width, 1].max
66
+ num_rows = list.size.fdiv(num_cols).ceil
67
+ list_with_padding = list.zip(sizes).map { |item, size| item + ' ' * (item_width - size) }
68
+ aligned = (list_with_padding + [nil] * (num_rows * num_cols - list_with_padding.size)).each_slice(num_rows).to_a.transpose
69
+ aligned.map do |row|
70
+ row.join.rstrip
71
+ end
72
+ end
73
+ end
53
74
 
54
75
  MINIMUM_SCROLLBAR_HEIGHT = 1
55
76
 
@@ -117,9 +138,6 @@ class Reline::LineEditor
117
138
  @screen_size = Reline::IOGate.get_screen_size
118
139
  reset_variables(prompt, encoding: encoding)
119
140
  @rendered_screen.base_y = Reline::IOGate.cursor_pos.y
120
- Reline::IOGate.set_winch_handler do
121
- @resized = true
122
- end
123
141
  if ENV.key?('RELINE_ALT_SCROLLBAR')
124
142
  @full_block = '::'
125
143
  @upper_half_block = "''"
@@ -143,7 +161,12 @@ class Reline::LineEditor
143
161
  end
144
162
  end
145
163
 
146
- def resize
164
+ def handle_signal
165
+ handle_interrupted
166
+ handle_resized
167
+ end
168
+
169
+ private def handle_resized
147
170
  return unless @resized
148
171
 
149
172
  @screen_size = Reline::IOGate.get_screen_size
@@ -156,25 +179,35 @@ class Reline::LineEditor
156
179
  render_differential
157
180
  end
158
181
 
182
+ private def handle_interrupted
183
+ return unless @interrupted
184
+
185
+ @interrupted = false
186
+ clear_dialogs
187
+ scrolldown = render_differential
188
+ Reline::IOGate.scroll_down scrolldown
189
+ Reline::IOGate.move_cursor_column 0
190
+ @rendered_screen.lines = []
191
+ @rendered_screen.cursor_y = 0
192
+ case @old_trap
193
+ when 'DEFAULT', 'SYSTEM_DEFAULT'
194
+ raise Interrupt
195
+ when 'IGNORE'
196
+ # Do nothing
197
+ when 'EXIT'
198
+ exit
199
+ else
200
+ @old_trap.call if @old_trap.respond_to?(:call)
201
+ end
202
+ end
203
+
159
204
  def set_signal_handlers
160
- @old_trap = Signal.trap('INT') {
161
- clear_dialogs
162
- scrolldown = render_differential
163
- Reline::IOGate.scroll_down scrolldown
164
- Reline::IOGate.move_cursor_column 0
165
- @rendered_screen.lines = []
166
- @rendered_screen.cursor_y = 0
167
- case @old_trap
168
- when 'DEFAULT', 'SYSTEM_DEFAULT'
169
- raise Interrupt
170
- when 'IGNORE'
171
- # Do nothing
172
- when 'EXIT'
173
- exit
174
- else
175
- @old_trap.call if @old_trap.respond_to?(:call)
176
- end
177
- }
205
+ Reline::IOGate.set_winch_handler do
206
+ @resized = true
207
+ end
208
+ @old_trap = Signal.trap('INT') do
209
+ @interrupted = true
210
+ end
178
211
  end
179
212
 
180
213
  def finalize
@@ -191,7 +224,6 @@ class Reline::LineEditor
191
224
  @encoding = encoding
192
225
  @is_multiline = false
193
226
  @finished = false
194
- @cleared = false
195
227
  @history_pointer = nil
196
228
  @kill_ring ||= Reline::KillRing.new
197
229
  @vi_clipboard = ''
@@ -199,7 +231,7 @@ class Reline::LineEditor
199
231
  @waiting_proc = nil
200
232
  @waiting_operator_proc = nil
201
233
  @waiting_operator_vi_arg = nil
202
- @completion_journey_data = nil
234
+ @completion_journey_state = nil
203
235
  @completion_state = CompletionState::NORMAL
204
236
  @perfect_matched = nil
205
237
  @menu_info = nil
@@ -213,6 +245,7 @@ class Reline::LineEditor
213
245
  @in_pasting = false
214
246
  @auto_indent_proc = nil
215
247
  @dialogs = []
248
+ @interrupted = false
216
249
  @resized = false
217
250
  @cache = {}
218
251
  @rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0)
@@ -458,7 +491,7 @@ class Reline::LineEditor
458
491
  [[0, Reline::Unicode.calculate_width(l, true), l]]
459
492
  end
460
493
  if @menu_info
461
- @menu_info.list.sort!.each do |item|
494
+ @menu_info.lines(screen_width).each do |item|
462
495
  new_lines << [[0, Reline::Unicode.calculate_width(item), item]]
463
496
  end
464
497
  @menu_info = nil # TODO: do not change state here
@@ -520,23 +553,13 @@ class Reline::LineEditor
520
553
  screen_height - wrapped_cursor_y + screen_scroll_top - @rendered_screen.base_y - 1
521
554
  end
522
555
 
523
- def handle_cleared
524
- return unless @cleared
525
-
526
- @cleared = false
527
- Reline::IOGate.clear_screen
528
- @screen_size = Reline::IOGate.get_screen_size
529
- @rendered_screen.lines = []
530
- @rendered_screen.base_y = 0
531
- @rendered_screen.cursor_y = 0
532
- end
533
-
534
556
  def rerender
535
- handle_cleared
536
557
  render_differential unless @in_pasting
537
558
  end
538
559
 
539
560
  class DialogProcScope
561
+ CompletionJourneyData = Struct.new(:preposing, :postposing, :list, :pointer)
562
+
540
563
  def initialize(line_editor, config, proc_to_exec, context)
541
564
  @line_editor = line_editor
542
565
  @config = config
@@ -600,7 +623,7 @@ class Reline::LineEditor
600
623
  end
601
624
 
602
625
  def completion_journey_data
603
- @line_editor.instance_variable_get(:@completion_journey_data)
626
+ @line_editor.dialog_proc_scope_completion_journey_data
604
627
  end
605
628
 
606
629
  def config
@@ -771,7 +794,7 @@ class Reline::LineEditor
771
794
  if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: complete)
772
795
  after.lines("\n").map { |l| l.chomp('') }
773
796
  else
774
- before
797
+ before.map { |l| Reline::Unicode.escape_for_print(l) }
775
798
  end
776
799
  end
777
800
 
@@ -779,8 +802,8 @@ class Reline::LineEditor
779
802
  @config.editing_mode
780
803
  end
781
804
 
782
- private def menu(target, list)
783
- @menu_info = MenuInfo.new(target, list)
805
+ private def menu(_target, list)
806
+ @menu_info = MenuInfo.new(list)
784
807
  end
785
808
 
786
809
  private def complete_internal_proc(list, is_menu)
@@ -829,9 +852,9 @@ class Reline::LineEditor
829
852
  [target, preposing, completed, postposing]
830
853
  end
831
854
 
832
- private def complete(list, just_show_list = false)
855
+ private def complete(list, just_show_list)
833
856
  case @completion_state
834
- when CompletionState::NORMAL, CompletionState::JOURNEY
857
+ when CompletionState::NORMAL
835
858
  @completion_state = CompletionState::COMPLETION
836
859
  when CompletionState::PERFECT_MATCH
837
860
  @dig_perfect_match_proc&.(@perfect_matched)
@@ -871,46 +894,44 @@ class Reline::LineEditor
871
894
  end
872
895
  end
873
896
 
874
- private def move_completed_list(list, direction)
875
- case @completion_state
876
- when CompletionState::NORMAL, CompletionState::COMPLETION,
877
- CompletionState::MENU, CompletionState::MENU_WITH_PERFECT_MATCH
878
- @completion_state = CompletionState::JOURNEY
879
- result = retrieve_completion_block
880
- return if result.nil?
881
- preposing, target, postposing = result
882
- @completion_journey_data = CompletionJourneyData.new(
883
- preposing, postposing,
884
- [target] + list.select{ |item| item.start_with?(target) }, 0)
885
- if @completion_journey_data.list.size == 1
886
- @completion_journey_data.pointer = 0
887
- else
888
- case direction
889
- when :up
890
- @completion_journey_data.pointer = @completion_journey_data.list.size - 1
891
- when :down
892
- @completion_journey_data.pointer = 1
893
- end
894
- end
895
- @completion_state = CompletionState::JOURNEY
896
- else
897
- case direction
898
- when :up
899
- @completion_journey_data.pointer -= 1
900
- if @completion_journey_data.pointer < 0
901
- @completion_journey_data.pointer = @completion_journey_data.list.size - 1
902
- end
903
- when :down
904
- @completion_journey_data.pointer += 1
905
- if @completion_journey_data.pointer >= @completion_journey_data.list.size
906
- @completion_journey_data.pointer = 0
907
- end
908
- end
897
+ def dialog_proc_scope_completion_journey_data
898
+ return nil unless @completion_journey_state
899
+ line_index = @completion_journey_state.line_index
900
+ pre_lines = @buffer_of_lines[0...line_index].map { |line| line + "\n" }
901
+ post_lines = @buffer_of_lines[(line_index + 1)..-1].map { |line| line + "\n" }
902
+ DialogProcScope::CompletionJourneyData.new(
903
+ pre_lines.join + @completion_journey_state.pre,
904
+ @completion_journey_state.post + post_lines.join,
905
+ @completion_journey_state.list,
906
+ @completion_journey_state.pointer
907
+ )
908
+ end
909
+
910
+ private def move_completed_list(direction)
911
+ @completion_journey_state ||= retrieve_completion_journey_state
912
+ return false unless @completion_journey_state
913
+
914
+ if (delta = { up: -1, down: +1 }[direction])
915
+ @completion_journey_state.pointer = (@completion_journey_state.pointer + delta) % @completion_journey_state.list.size
909
916
  end
910
- completed = @completion_journey_data.list[@completion_journey_data.pointer]
911
- line_to_pointer = (@completion_journey_data.preposing + completed).split("\n")[@line_index] || String.new(encoding: @encoding)
912
- new_line = line_to_pointer + (@completion_journey_data.postposing.split("\n").first || '')
913
- set_current_line(new_line, line_to_pointer.bytesize)
917
+ completed = @completion_journey_state.list[@completion_journey_state.pointer]
918
+ set_current_line(@completion_journey_state.pre + completed + @completion_journey_state.post, @completion_journey_state.pre.bytesize + completed.bytesize)
919
+ true
920
+ end
921
+
922
+ private def retrieve_completion_journey_state
923
+ preposing, target, postposing = retrieve_completion_block
924
+ list = call_completion_proc
925
+ return unless list.is_a?(Array)
926
+
927
+ candidates = list.select{ |item| item.start_with?(target) }
928
+ return if candidates.empty?
929
+
930
+ pre = preposing.split("\n", -1).last || ''
931
+ post = postposing.split("\n", -1).first || ''
932
+ CompletionJourneyState.new(
933
+ @line_index, pre, target, post, [target] + candidates, 0
934
+ )
914
935
  end
915
936
 
916
937
  private def run_for_operators(key, method_symbol, &block)
@@ -1099,50 +1120,56 @@ class Reline::LineEditor
1099
1120
  @first_char = false
1100
1121
  completion_occurs = false
1101
1122
  if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
1102
- unless @config.disable_completion
1103
- result = call_completion_proc
1104
- if result.is_a?(Array)
1105
- completion_occurs = true
1106
- process_insert
1107
- if @config.autocompletion
1108
- move_completed_list(result, :down)
1109
- else
1110
- complete(result)
1123
+ if !@config.disable_completion
1124
+ process_insert(force: true)
1125
+ if @config.autocompletion
1126
+ @completion_state = CompletionState::NORMAL
1127
+ completion_occurs = move_completed_list(:down)
1128
+ else
1129
+ @completion_journey_state = nil
1130
+ result = call_completion_proc
1131
+ if result.is_a?(Array)
1132
+ completion_occurs = true
1133
+ complete(result, false)
1111
1134
  end
1112
1135
  end
1113
1136
  end
1114
1137
  elsif @config.editing_mode_is?(:emacs, :vi_insert) and key.char == :completion_journey_up
1115
1138
  if not @config.disable_completion and @config.autocompletion
1116
- result = call_completion_proc
1117
- if result.is_a?(Array)
1118
- completion_occurs = true
1119
- process_insert
1120
- move_completed_list(result, :up)
1121
- end
1139
+ process_insert(force: true)
1140
+ @completion_state = CompletionState::NORMAL
1141
+ completion_occurs = move_completed_list(:up)
1122
1142
  end
1123
- elsif not @config.disable_completion and @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
1124
- unless @config.disable_completion
1125
- result = call_completion_proc
1126
- if result.is_a?(Array)
1127
- completion_occurs = true
1128
- process_insert
1129
- move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
1130
- end
1143
+ elsif @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
1144
+ # In vi mode, move completed list even if autocompletion is off
1145
+ if not @config.disable_completion
1146
+ process_insert(force: true)
1147
+ @completion_state = CompletionState::NORMAL
1148
+ completion_occurs = move_completed_list("\C-p".ord == key.char ? :up : :down)
1131
1149
  end
1132
1150
  elsif Symbol === key.char and respond_to?(key.char, true)
1133
1151
  process_key(key.char, key.char)
1134
1152
  else
1135
1153
  normal_char(key)
1136
1154
  end
1155
+
1137
1156
  unless completion_occurs
1138
1157
  @completion_state = CompletionState::NORMAL
1139
- @completion_journey_data = nil
1158
+ @completion_journey_state = nil
1140
1159
  end
1160
+
1141
1161
  if @in_pasting
1142
1162
  clear_dialogs
1143
- else
1144
- return old_lines != @buffer_of_lines
1163
+ return
1164
+ end
1165
+
1166
+ modified = old_lines != @buffer_of_lines
1167
+ if !completion_occurs && modified && !@config.disable_completion && @config.autocompletion
1168
+ # Auto complete starts only when edited
1169
+ process_insert(force: true)
1170
+ @completion_journey_state = retrieve_completion_journey_state
1145
1171
  end
1172
+ modified
1146
1173
  end
1147
1174
 
1148
1175
  def scroll_into_view
@@ -1194,10 +1221,11 @@ class Reline::LineEditor
1194
1221
  new_indent = @auto_indent_proc.(@buffer_of_lines.take(line_index + 1).push(''), line_index, byte_pointer, add_newline)
1195
1222
  return unless new_indent
1196
1223
 
1197
- @buffer_of_lines[line_index] = ' ' * new_indent + line.lstrip
1224
+ new_line = ' ' * new_indent + line.lstrip
1225
+ @buffer_of_lines[line_index] = new_line
1198
1226
  if @line_index == line_index
1199
- old_indent = line[/\A */].size
1200
- @byte_pointer = [@byte_pointer + new_indent - old_indent, 0].max
1227
+ indent_diff = new_line.bytesize - line.bytesize
1228
+ @byte_pointer = [@byte_pointer + indent_diff, 0].max
1201
1229
  end
1202
1230
  end
1203
1231
 
@@ -2020,7 +2048,7 @@ class Reline::LineEditor
2020
2048
  private def em_delete_or_list(key)
2021
2049
  if current_line.empty? or @byte_pointer < current_line.bytesize
2022
2050
  em_delete(key)
2023
- else # show completed list
2051
+ elsif !@config.autocompletion # show completed list
2024
2052
  result = call_completion_proc
2025
2053
  if result.is_a?(Array)
2026
2054
  complete(result, true)
@@ -2046,7 +2074,11 @@ class Reline::LineEditor
2046
2074
  alias_method :yank_pop, :em_yank_pop
2047
2075
 
2048
2076
  private def ed_clear_screen(key)
2049
- @cleared = true
2077
+ Reline::IOGate.clear_screen
2078
+ @screen_size = Reline::IOGate.get_screen_size
2079
+ @rendered_screen.lines = []
2080
+ @rendered_screen.base_y = 0
2081
+ @rendered_screen.cursor_y = 0
2050
2082
  end
2051
2083
  alias_method :clear_screen, :ed_clear_screen
2052
2084
 
@@ -80,23 +80,11 @@ module Reline::Terminfo
80
80
  def self.setupterm(term, fildes)
81
81
  errret_int = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT)
82
82
  ret = @setupterm.(term, fildes, errret_int)
83
- errret = errret_int[0, Fiddle::SIZEOF_INT].unpack1('i')
84
83
  case ret
85
84
  when 0 # OK
86
- 0
85
+ @term_supported = true
87
86
  when -1 # ERR
88
- case errret
89
- when 1
90
- raise TerminfoError.new('The terminal is hardcopy, cannot be used for curses applications.')
91
- when 0
92
- raise TerminfoError.new('The terminal could not be found, or that it is a generic type, having too little information for curses applications to run.')
93
- when -1
94
- raise TerminfoError.new('The terminfo database could not be found.')
95
- else # unknown
96
- -1
97
- end
98
- else # unknown
99
- -2
87
+ @term_supported = false
100
88
  end
101
89
  end
102
90
 
@@ -148,9 +136,14 @@ module Reline::Terminfo
148
136
  num
149
137
  end
150
138
 
139
+ # NOTE: This means Fiddle and curses are enabled.
151
140
  def self.enabled?
152
141
  true
153
142
  end
143
+
144
+ def self.term_supported?
145
+ @term_supported
146
+ end
154
147
  end if Reline::Terminfo.curses_dl
155
148
 
156
149
  module Reline::Terminfo
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.5.0'
2
+ VERSION = '0.5.1'
3
3
  end
@@ -259,7 +259,7 @@ class Reline::Windows
259
259
  def self.check_input_event
260
260
  num_of_events = 0.chr * 8
261
261
  while @@output_buf.empty?
262
- Reline.core.line_editor.resize
262
+ Reline.core.line_editor.handle_signal
263
263
  if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
264
264
  # prevent for background consolemode change
265
265
  @@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
data/lib/reline.rb CHANGED
@@ -75,6 +75,7 @@ module Reline
75
75
 
76
76
  def initialize
77
77
  self.output = STDOUT
78
+ @mutex = Mutex.new
78
79
  @dialog_proc_list = {}
79
80
  yield self
80
81
  @completion_quote_character = nil
@@ -219,26 +220,16 @@ module Reline
219
220
 
220
221
  Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE = ->() {
221
222
  # autocomplete
222
- return nil unless config.autocompletion
223
- if just_cursor_moving and completion_journey_data.nil?
224
- # Auto complete starts only when edited
225
- return nil
226
- end
227
- pre, target, post = retrieve_completion_block(true)
228
- if target.nil? or target.empty? or (completion_journey_data&.pointer == -1 and target.size <= 3)
229
- return nil
230
- end
231
- if completion_journey_data and completion_journey_data.list
232
- result = completion_journey_data.list.dup
233
- result.shift
234
- pointer = completion_journey_data.pointer - 1
235
- else
236
- result = call_completion_proc_with_checking_args(pre, target, post)
237
- pointer = nil
238
- end
239
- if result and result.size == 1 and result[0] == target and pointer != 0
240
- result = nil
241
- end
223
+ return unless config.autocompletion
224
+
225
+ journey_data = completion_journey_data
226
+ return unless journey_data
227
+
228
+ target = journey_data.list[journey_data.pointer]
229
+ result = journey_data.list.drop(1)
230
+ pointer = journey_data.pointer - 1
231
+ return if target.empty? || (result == [target] && pointer < 0)
232
+
242
233
  target_width = Reline::Unicode.calculate_width(target)
243
234
  x = cursor_pos.x - target_width
244
235
  if x < 0
@@ -264,12 +255,15 @@ module Reline
264
255
  Reline::DEFAULT_DIALOG_CONTEXT = Array.new
265
256
 
266
257
  def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
267
- Reline.update_iogate
268
- io_gate.with_raw_input do
258
+ @mutex.synchronize do
269
259
  unless confirm_multiline_termination
270
260
  raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
271
261
  end
272
- inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
262
+
263
+ Reline.update_iogate
264
+ io_gate.with_raw_input do
265
+ inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
266
+ end
273
267
 
274
268
  whole_buffer = line_editor.whole_buffer.dup
275
269
  whole_buffer.taint if RUBY_VERSION < '2.7'
@@ -288,17 +282,21 @@ module Reline
288
282
  end
289
283
 
290
284
  def readline(prompt = '', add_hist = false)
291
- Reline.update_iogate
292
- inner_readline(prompt, add_hist, false)
285
+ @mutex.synchronize do
286
+ Reline.update_iogate
287
+ io_gate.with_raw_input do
288
+ inner_readline(prompt, add_hist, false)
289
+ end
293
290
 
294
- line = line_editor.line.dup
295
- line.taint if RUBY_VERSION < '2.7'
296
- if add_hist and line and line.chomp("\n").size > 0
297
- Reline::HISTORY << line.chomp("\n")
298
- end
291
+ line = line_editor.line.dup
292
+ line.taint if RUBY_VERSION < '2.7'
293
+ if add_hist and line and line.chomp("\n").size > 0
294
+ Reline::HISTORY << line.chomp("\n")
295
+ end
299
296
 
300
- line_editor.reset_line if line_editor.line.nil?
301
- line
297
+ line_editor.reset_line if line_editor.line.nil?
298
+ line
299
+ end
302
300
  end
303
301
 
304
302
  private def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
@@ -365,19 +363,10 @@ module Reline
365
363
  io_gate.move_cursor_column(0)
366
364
  rescue Errno::EIO
367
365
  # Maybe the I/O has been closed.
368
- rescue StandardError => e
369
- line_editor.finalize
370
- io_gate.deprep(otio)
371
- raise e
372
- rescue Exception
373
- # Including Interrupt
366
+ ensure
374
367
  line_editor.finalize
375
368
  io_gate.deprep(otio)
376
- raise
377
369
  end
378
-
379
- line_editor.finalize
380
- io_gate.deprep(otio)
381
370
  end
382
371
 
383
372
  # GNU Readline waits for "keyseq-timeout" milliseconds to see if the ESC
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-25 00:00:00.000000000 Z
11
+ date: 2024-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console