reline 0.5.0 → 0.5.1

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