reline 0.5.3 → 0.5.5

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: 77e916d771c2f7219d8cba4be2e3b5e6102dddcb505b342375b8717e069caab1
4
- data.tar.gz: ec4262c712ed77b4807255d12887542b37e74cf0e1ccf1dfef78d803d886ba17
3
+ metadata.gz: 590a83a7301ffc23869017938e580376b9e3025130ebb0f102fa47d5ac9be28f
4
+ data.tar.gz: 4652d400dba7e688591f1795e1c2a42b51ce4c9ad8381420c56e68c167034535
5
5
  SHA512:
6
- metadata.gz: b0b88eb00ce703b01f691c2fc780a7f9ff43fefdcc9d42d22122bba742ddc03b20db19ecca2c21082081f66d4993f70a3a4cb53ea50ed5a0255728635360c45c
7
- data.tar.gz: eb4b51fb15f94e2c349fe4cf19c2d6426f7fdf0800320b89a14d66e6f18ea72f327d7792a8edb6d6034df708bb7c25fbffb625fe7cc03271efdb6662d9c90deb
6
+ metadata.gz: 9d3272d6b13d84818a895910d2a0201fa1629ea0dfc71037904a9c5f862c277866780df9aad323a5898941c9f2162fa2684892451617b30e7b9ec637ec3c143d
7
+ data.tar.gz: 67004e4bf5b626ebf7d098750ffa7b198539ae8d344ad65b80f101faef0f0f57504e05cd2cd90a167feaea0f6a78c179abd1d698cecb29c290a7d2dbac3923ae
data/lib/reline/ansi.rb CHANGED
@@ -284,7 +284,7 @@ class Reline::ANSI
284
284
  buf = @@output.pread(@@output.pos, 0)
285
285
  row = buf.count("\n")
286
286
  column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0
287
- rescue Errno::ESPIPE
287
+ rescue Errno::ESPIPE, IOError
288
288
  # Just returns column 1 for ambiguous width because this I/O is not
289
289
  # tty and can't seek.
290
290
  row = 0
data/lib/reline/config.rb CHANGED
@@ -53,8 +53,6 @@ class Reline::Config
53
53
  @additional_key_bindings[:vi_insert] = {}
54
54
  @additional_key_bindings[:vi_command] = {}
55
55
  @oneshot_key_bindings = {}
56
- @skip_section = nil
57
- @if_stack = nil
58
56
  @editing_mode_label = :emacs
59
57
  @keymap_label = :emacs
60
58
  @keymap_prefix = []
@@ -71,17 +69,14 @@ class Reline::Config
71
69
  @test_mode = false
72
70
  @autocompletion = false
73
71
  @convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding)
72
+ @loaded = false
74
73
  end
75
74
 
76
75
  def reset
77
76
  if editing_mode_is?(:vi_command)
78
77
  @editing_mode_label = :vi_insert
79
78
  end
80
- @additional_key_bindings.keys.each do |key|
81
- @additional_key_bindings[key].clear
82
- end
83
79
  @oneshot_key_bindings.clear
84
- reset_default_key_bindings
85
80
  end
86
81
 
87
82
  def editing_mode
@@ -100,6 +95,10 @@ class Reline::Config
100
95
  @key_actors[@keymap_label]
101
96
  end
102
97
 
98
+ def loaded?
99
+ @loaded
100
+ end
101
+
103
102
  def inputrc_path
104
103
  case ENV['INPUTRC']
105
104
  when nil, ''
@@ -131,6 +130,7 @@ class Reline::Config
131
130
  end
132
131
 
133
132
  def read(file = nil)
133
+ @loaded = true
134
134
  file ||= default_inputrc_path
135
135
  begin
136
136
  if file.respond_to?(:readlines)
@@ -173,12 +173,6 @@ class Reline::Config
173
173
  @key_actors[@keymap_label].default_key_bindings[keystroke] = target
174
174
  end
175
175
 
176
- def reset_default_key_bindings
177
- @key_actors.values.each do |ka|
178
- ka.reset_default_key_bindings
179
- end
180
- end
181
-
182
176
  def read_lines(lines, file = nil)
183
177
  if not lines.empty? and lines.first.encoding != Reline.encoding_system_needs
184
178
  begin
@@ -190,9 +184,7 @@ class Reline::Config
190
184
  end
191
185
  end
192
186
  end
193
- conditions = [@skip_section, @if_stack]
194
- @skip_section = nil
195
- @if_stack = []
187
+ if_stack = []
196
188
 
197
189
  lines.each_with_index do |line, no|
198
190
  next if line.match(/\A\s*#/)
@@ -201,11 +193,11 @@ class Reline::Config
201
193
 
202
194
  line = line.chomp.lstrip
203
195
  if line.start_with?('$')
204
- handle_directive(line[1..-1], file, no)
196
+ handle_directive(line[1..-1], file, no, if_stack)
205
197
  next
206
198
  end
207
199
 
208
- next if @skip_section
200
+ next if if_stack.any? { |_no, skip| skip }
209
201
 
210
202
  case line
211
203
  when /^set +([^ ]+) +([^ ]+)/i
@@ -214,43 +206,47 @@ class Reline::Config
214
206
  next
215
207
  when /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o
216
208
  key, func_name = $1, $2
209
+ func_name = func_name.split.first
217
210
  keystroke, func = bind_key(key, func_name)
218
211
  next unless keystroke
219
212
  @additional_key_bindings[@keymap_label][@keymap_prefix + keystroke] = func
220
213
  end
221
214
  end
222
- unless @if_stack.empty?
223
- raise InvalidInputrc, "#{file}:#{@if_stack.last[1]}: unclosed if"
215
+ unless if_stack.empty?
216
+ raise InvalidInputrc, "#{file}:#{if_stack.last[0]}: unclosed if"
224
217
  end
225
- ensure
226
- @skip_section, @if_stack = conditions
227
218
  end
228
219
 
229
- def handle_directive(directive, file, no)
220
+ def handle_directive(directive, file, no, if_stack)
230
221
  directive, args = directive.split(' ')
231
222
  case directive
232
223
  when 'if'
233
224
  condition = false
234
225
  case args
235
- when 'mode'
226
+ when /^mode=(vi|emacs)$/i
227
+ mode = $1.downcase
228
+ # NOTE: mode=vi means vi-insert mode
229
+ mode = 'vi_insert' if mode == 'vi'
230
+ if @editing_mode_label == mode.to_sym
231
+ condition = true
232
+ end
236
233
  when 'term'
237
234
  when 'version'
238
235
  else # application name
239
236
  condition = true if args == 'Ruby'
240
237
  condition = true if args == 'Reline'
241
238
  end
242
- @if_stack << [file, no, @skip_section]
243
- @skip_section = !condition
239
+ if_stack << [no, !condition]
244
240
  when 'else'
245
- if @if_stack.empty?
241
+ if if_stack.empty?
246
242
  raise InvalidInputrc, "#{file}:#{no}: unmatched else"
247
243
  end
248
- @skip_section = !@skip_section
244
+ if_stack.last[1] = !if_stack.last[1]
249
245
  when 'endif'
250
- if @if_stack.empty?
246
+ if if_stack.empty?
251
247
  raise InvalidInputrc, "#{file}:#{no}: unmatched endif"
252
248
  end
253
- @skip_section = @if_stack.pop
249
+ if_stack.pop
254
250
  when 'include'
255
251
  read(File.expand_path(args))
256
252
  end
@@ -12,8 +12,4 @@ class Reline::KeyActor::Base
12
12
  def default_key_bindings
13
13
  @default_key_bindings
14
14
  end
15
-
16
- def reset_default_key_bindings
17
- @default_key_bindings.clear
18
- end
19
15
  end
@@ -387,7 +387,7 @@ class Reline::LineEditor
387
387
  next cached
388
388
  end
389
389
  *wrapped_prompts, code_line_prompt = split_by_width(prompt, width).first.compact
390
- wrapped_lines = split_by_width(line, width, offset: calculate_width(code_line_prompt)).first.compact
390
+ wrapped_lines = split_by_width(line, width, offset: calculate_width(code_line_prompt, true)).first.compact
391
391
  wrapped_prompts.map { |p| [p, ''] } + [[code_line_prompt, wrapped_lines.first]] + wrapped_lines.drop(1).map { |c| ['', c] }
392
392
  end
393
393
  end
@@ -414,8 +414,13 @@ class Reline::LineEditor
414
414
  @output.write "#{Reline::IOGate::RESET_COLOR}#{' ' * width}"
415
415
  else
416
416
  x, w, content = new_items[level]
417
- content = Reline::Unicode.take_range(content, base_x - x, width) unless x == base_x && w == width
418
- Reline::IOGate.move_cursor_column base_x
417
+ cover_begin = base_x != 0 && new_levels[base_x - 1] == level
418
+ cover_end = new_levels[base_x + width] == level
419
+ pos = 0
420
+ unless x == base_x && w == width
421
+ content, pos = Reline::Unicode.take_mbchar_range(content, base_x - x, width, cover_begin: cover_begin, cover_end: cover_end, padding: true)
422
+ end
423
+ Reline::IOGate.move_cursor_column x + pos
419
424
  @output.write "#{Reline::IOGate::RESET_COLOR}#{content}#{Reline::IOGate::RESET_COLOR}"
420
425
  end
421
426
  base_x += width
@@ -699,13 +704,6 @@ class Reline::LineEditor
699
704
 
700
705
  DIALOG_DEFAULT_HEIGHT = 20
701
706
 
702
- private def padding_space_with_escape_sequences(str, width)
703
- padding_width = width - calculate_width(str, true)
704
- # padding_width should be only positive value. But macOS and Alacritty returns negative value.
705
- padding_width = 0 if padding_width < 0
706
- str + (' ' * padding_width)
707
- end
708
-
709
707
  private def dialog_range(dialog, dialog_y)
710
708
  x_range = dialog.column...dialog.column + dialog.width
711
709
  y_range = dialog_y + dialog.vertical_offset...dialog_y + dialog.vertical_offset + dialog.contents.size
@@ -778,7 +776,7 @@ class Reline::LineEditor
778
776
  dialog.contents = contents.map.with_index do |item, i|
779
777
  line_sgr = i == pointer ? enhanced_sgr : default_sgr
780
778
  str_width = dialog.width - (scrollbar_pos.nil? ? 0 : @block_elem_width)
781
- str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, str_width), str_width)
779
+ str, = Reline::Unicode.take_mbchar_range(item, 0, str_width, padding: true)
782
780
  colored_content = "#{line_sgr}#{str}"
783
781
  if scrollbar_pos
784
782
  if scrollbar_pos <= (i * 2) and (i * 2 + 1) < (scrollbar_pos + bar_height)
@@ -1120,6 +1118,7 @@ class Reline::LineEditor
1120
1118
  end
1121
1119
  end
1122
1120
  if key.char.nil?
1121
+ process_insert(force: true)
1123
1122
  if @first_char
1124
1123
  @eof = true
1125
1124
  end
@@ -1542,11 +1541,7 @@ class Reline::LineEditor
1542
1541
  alias_method :vi_zero, :ed_move_to_beg
1543
1542
 
1544
1543
  private def ed_move_to_end(key)
1545
- @byte_pointer = 0
1546
- while @byte_pointer < current_line.bytesize
1547
- byte_size = Reline::Unicode.get_next_mbchar_size(current_line, @byte_pointer)
1548
- @byte_pointer += byte_size
1549
- end
1544
+ @byte_pointer = current_line.bytesize
1550
1545
  end
1551
1546
  alias_method :end_of_line, :ed_move_to_end
1552
1547
 
@@ -145,7 +145,13 @@ class Reline::Unicode
145
145
  lines.last << NON_PRINTING_END
146
146
  when csi
147
147
  lines.last << csi
148
- seq << csi
148
+ unless in_zero_width
149
+ if csi == -"\e[m" || csi == -"\e[0m"
150
+ seq.clear
151
+ else
152
+ seq << csi
153
+ end
154
+ end
149
155
  when osc
150
156
  lines.last << osc
151
157
  seq << osc
@@ -173,32 +179,78 @@ class Reline::Unicode
173
179
 
174
180
  # Take a chunk of a String cut by width with escape sequences.
175
181
  def self.take_range(str, start_col, max_width)
182
+ take_mbchar_range(str, start_col, max_width).first
183
+ end
184
+
185
+ def self.take_mbchar_range(str, start_col, width, cover_begin: false, cover_end: false, padding: false)
176
186
  chunk = String.new(encoding: str.encoding)
187
+
188
+ end_col = start_col + width
177
189
  total_width = 0
178
190
  rest = str.encode(Encoding::UTF_8)
179
191
  in_zero_width = false
192
+ chunk_start_col = nil
193
+ chunk_end_col = nil
194
+ has_csi = false
180
195
  rest.scan(WIDTH_SCANNER) do |non_printing_start, non_printing_end, csi, osc, gc|
181
196
  case
182
197
  when non_printing_start
183
198
  in_zero_width = true
199
+ chunk << NON_PRINTING_START
184
200
  when non_printing_end
185
201
  in_zero_width = false
202
+ chunk << NON_PRINTING_END
186
203
  when csi
204
+ has_csi = true
187
205
  chunk << csi
188
206
  when osc
189
207
  chunk << osc
190
208
  when gc
191
209
  if in_zero_width
192
210
  chunk << gc
211
+ next
212
+ end
213
+
214
+ mbchar_width = get_mbchar_width(gc)
215
+ prev_width = total_width
216
+ total_width += mbchar_width
217
+
218
+ if (cover_begin || padding ? total_width <= start_col : prev_width < start_col)
219
+ # Current character haven't reached start_col yet
220
+ next
221
+ elsif padding && !cover_begin && prev_width < start_col && start_col < total_width
222
+ # Add preceding padding. This padding might have background color.
223
+ chunk << ' '
224
+ chunk_start_col ||= start_col
225
+ chunk_end_col = total_width
226
+ next
227
+ elsif (cover_end ? prev_width < end_col : total_width <= end_col)
228
+ # Current character is in the range
229
+ chunk << gc
230
+ chunk_start_col ||= prev_width
231
+ chunk_end_col = total_width
232
+ break if total_width >= end_col
193
233
  else
194
- mbchar_width = get_mbchar_width(gc)
195
- total_width += mbchar_width
196
- break if (start_col + max_width) < total_width
197
- chunk << gc if start_col < total_width
234
+ # Current character exceeds end_col
235
+ if padding && end_col < total_width
236
+ # Add succeeding padding. This padding might have background color.
237
+ chunk << ' '
238
+ chunk_start_col ||= prev_width
239
+ chunk_end_col = end_col
240
+ end
241
+ break
198
242
  end
199
243
  end
200
244
  end
201
- chunk
245
+ chunk_start_col ||= start_col
246
+ chunk_end_col ||= start_col
247
+ if padding && chunk_end_col < end_col
248
+ # Append padding. This padding should not include background color.
249
+ chunk << "\e[0m" if has_csi
250
+ chunk << ' ' * (end_col - chunk_end_col)
251
+ chunk_end_col = end_col
252
+ end
253
+ [chunk, chunk_start_col, chunk_end_col - chunk_start_col]
202
254
  end
203
255
 
204
256
  def self.get_next_mbchar_size(line, byte_pointer)
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.5.3'
2
+ VERSION = '0.5.5'
3
3
  end
data/lib/reline.rb CHANGED
@@ -225,17 +225,20 @@ module Reline
225
225
  journey_data = completion_journey_data
226
226
  return unless journey_data
227
227
 
228
- target = journey_data.list[journey_data.pointer]
228
+ target = journey_data.list.first
229
+ completed = journey_data.list[journey_data.pointer]
229
230
  result = journey_data.list.drop(1)
230
231
  pointer = journey_data.pointer - 1
231
- return if target.empty? || (result == [target] && pointer < 0)
232
+ return if completed.empty? || (result == [completed] && pointer < 0)
232
233
 
233
234
  target_width = Reline::Unicode.calculate_width(target)
234
- x = cursor_pos.x - target_width
235
- if x < 0
236
- x = screen_width + x
235
+ completed_width = Reline::Unicode.calculate_width(completed)
236
+ if cursor_pos.x <= completed_width - target_width
237
+ # When target is rendered on the line above cursor position
238
+ x = screen_width - completed_width
237
239
  y = -1
238
240
  else
241
+ x = [cursor_pos.x - completed_width, 0].max
239
242
  y = 0
240
243
  end
241
244
  cursor_pos_to_render = Reline::CursorPos.new(x, y)
@@ -335,9 +338,8 @@ module Reline
335
338
  end
336
339
  end
337
340
 
338
- unless config.test_mode
341
+ unless config.test_mode or config.loaded?
339
342
  config.read
340
- config.reset_default_key_bindings
341
343
  io_gate.set_default_key_bindings(config)
342
344
  end
343
345
 
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.3
4
+ version: 0.5.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-04-23 00:00:00.000000000 Z
11
+ date: 2024-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -76,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
78
  requirements: []
79
- rubygems_version: 3.5.3
79
+ rubygems_version: 3.5.9
80
80
  signing_key:
81
81
  specification_version: 4
82
82
  summary: Alternative GNU Readline or Editline implementation by pure Ruby.