reline 0.5.3 → 0.5.5

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