reline 0.3.9 → 0.5.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -43,11 +43,13 @@ class Reline::Unicode
43
43
 
44
44
  def self.escape_for_print(str)
45
45
  str.chars.map! { |gr|
46
- escaped = EscapedPairs[gr.ord]
47
- if escaped && gr != -"\n" && gr != -"\t"
48
- escaped
49
- else
46
+ case gr
47
+ when -"\n"
50
48
  gr
49
+ when -"\t"
50
+ -' '
51
+ else
52
+ EscapedPairs[gr.ord] || gr
51
53
  end
52
54
  }.join
53
55
  end
@@ -128,10 +130,10 @@ class Reline::Unicode
128
130
  end
129
131
  end
130
132
 
131
- def self.split_by_width(str, max_width, encoding = str.encoding)
133
+ def self.split_by_width(str, max_width, encoding = str.encoding, offset: 0)
132
134
  lines = [String.new(encoding: encoding)]
133
135
  height = 1
134
- width = 0
136
+ width = offset
135
137
  rest = str.encode(Encoding::UTF_8)
136
138
  in_zero_width = false
137
139
  seq = String.new(encoding: encoding)
@@ -145,7 +147,13 @@ class Reline::Unicode
145
147
  lines.last << NON_PRINTING_END
146
148
  when csi
147
149
  lines.last << csi
148
- seq << csi
150
+ unless in_zero_width
151
+ if csi == -"\e[m" || csi == -"\e[0m"
152
+ seq.clear
153
+ else
154
+ seq << csi
155
+ end
156
+ end
149
157
  when osc
150
158
  lines.last << osc
151
159
  seq << osc
@@ -173,32 +181,78 @@ class Reline::Unicode
173
181
 
174
182
  # Take a chunk of a String cut by width with escape sequences.
175
183
  def self.take_range(str, start_col, max_width)
184
+ take_mbchar_range(str, start_col, max_width).first
185
+ end
186
+
187
+ def self.take_mbchar_range(str, start_col, width, cover_begin: false, cover_end: false, padding: false)
176
188
  chunk = String.new(encoding: str.encoding)
189
+
190
+ end_col = start_col + width
177
191
  total_width = 0
178
192
  rest = str.encode(Encoding::UTF_8)
179
193
  in_zero_width = false
194
+ chunk_start_col = nil
195
+ chunk_end_col = nil
196
+ has_csi = false
180
197
  rest.scan(WIDTH_SCANNER) do |non_printing_start, non_printing_end, csi, osc, gc|
181
198
  case
182
199
  when non_printing_start
183
200
  in_zero_width = true
201
+ chunk << NON_PRINTING_START
184
202
  when non_printing_end
185
203
  in_zero_width = false
204
+ chunk << NON_PRINTING_END
186
205
  when csi
206
+ has_csi = true
187
207
  chunk << csi
188
208
  when osc
189
209
  chunk << osc
190
210
  when gc
191
211
  if in_zero_width
192
212
  chunk << gc
213
+ next
214
+ end
215
+
216
+ mbchar_width = get_mbchar_width(gc)
217
+ prev_width = total_width
218
+ total_width += mbchar_width
219
+
220
+ if (cover_begin || padding ? total_width <= start_col : prev_width < start_col)
221
+ # Current character haven't reached start_col yet
222
+ next
223
+ elsif padding && !cover_begin && prev_width < start_col && start_col < total_width
224
+ # Add preceding padding. This padding might have background color.
225
+ chunk << ' '
226
+ chunk_start_col ||= start_col
227
+ chunk_end_col = total_width
228
+ next
229
+ elsif (cover_end ? prev_width < end_col : total_width <= end_col)
230
+ # Current character is in the range
231
+ chunk << gc
232
+ chunk_start_col ||= prev_width
233
+ chunk_end_col = total_width
234
+ break if total_width >= end_col
193
235
  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
236
+ # Current character exceeds end_col
237
+ if padding && end_col < total_width
238
+ # Add succeeding padding. This padding might have background color.
239
+ chunk << ' '
240
+ chunk_start_col ||= prev_width
241
+ chunk_end_col = end_col
242
+ end
243
+ break
198
244
  end
199
245
  end
200
246
  end
201
- chunk
247
+ chunk_start_col ||= start_col
248
+ chunk_end_col ||= start_col
249
+ if padding && chunk_end_col < end_col
250
+ # Append padding. This padding should not include background color.
251
+ chunk << "\e[0m" if has_csi
252
+ chunk << ' ' * (end_col - chunk_end_col)
253
+ chunk_end_col = end_col
254
+ end
255
+ [chunk, chunk_start_col, chunk_end_col - chunk_start_col]
202
256
  end
203
257
 
204
258
  def self.get_next_mbchar_size(line, byte_pointer)
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.3.9'
2
+ VERSION = '0.5.8'
3
3
  end
@@ -1,6 +1,8 @@
1
1
  require 'fiddle/import'
2
2
 
3
3
  class Reline::Windows
4
+ RESET_COLOR = "\e[0m"
5
+
4
6
  def self.encoding
5
7
  Encoding::UTF_8
6
8
  end
@@ -85,7 +87,7 @@ class Reline::Windows
85
87
  def call(*args)
86
88
  import = @proto.split("")
87
89
  args.each_with_index do |x, i|
88
- args[i], = [x == 0 ? nil : x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
90
+ args[i], = [x == 0 ? nil : +x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
89
91
  args[i], = [x].pack("I").unpack("i") if import[i] == "I"
90
92
  end
91
93
  ret, = @func.call(*args)
@@ -257,7 +259,7 @@ class Reline::Windows
257
259
  def self.check_input_event
258
260
  num_of_events = 0.chr * 8
259
261
  while @@output_buf.empty?
260
- Reline.core.line_editor.resize
262
+ Reline.core.line_editor.handle_signal
261
263
  if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
262
264
  # prevent for background consolemode change
263
265
  @@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
data/lib/reline.rb CHANGED
@@ -7,6 +7,7 @@ require 'reline/key_stroke'
7
7
  require 'reline/line_editor'
8
8
  require 'reline/history'
9
9
  require 'reline/terminfo'
10
+ require 'reline/face'
10
11
  require 'rbconfig'
11
12
 
12
13
  module Reline
@@ -36,10 +37,8 @@ module Reline
36
37
  DialogRenderInfo = Struct.new(
37
38
  :pos,
38
39
  :contents,
39
- :bg_color,
40
- :pointer_bg_color,
41
- :fg_color,
42
- :pointer_fg_color,
40
+ :face,
41
+ :bg_color, # For the time being, this line should stay here for the compatibility with IRB.
43
42
  :width,
44
43
  :height,
45
44
  :scrollbar,
@@ -76,10 +75,10 @@ module Reline
76
75
 
77
76
  def initialize
78
77
  self.output = STDOUT
78
+ @mutex = Mutex.new
79
79
  @dialog_proc_list = {}
80
80
  yield self
81
81
  @completion_quote_character = nil
82
- @bracketed_paste_finished = false
83
82
  end
84
83
 
85
84
  def io_gate
@@ -221,32 +220,25 @@ module Reline
221
220
 
222
221
  Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE = ->() {
223
222
  # autocomplete
224
- return nil unless config.autocompletion
225
- if just_cursor_moving and completion_journey_data.nil?
226
- # Auto complete starts only when edited
227
- return nil
228
- end
229
- pre, target, post = retrieve_completion_block(true)
230
- if target.nil? or target.empty? or (completion_journey_data&.pointer == -1 and target.size <= 3)
231
- return nil
232
- end
233
- if completion_journey_data and completion_journey_data.list
234
- result = completion_journey_data.list.dup
235
- result.shift
236
- pointer = completion_journey_data.pointer - 1
237
- else
238
- result = call_completion_proc_with_checking_args(pre, target, post)
239
- pointer = nil
240
- end
241
- if result and result.size == 1 and result[0] == target and pointer != 0
242
- result = nil
243
- end
223
+ return unless config.autocompletion
224
+
225
+ journey_data = completion_journey_data
226
+ return unless journey_data
227
+
228
+ target = journey_data.list.first
229
+ completed = journey_data.list[journey_data.pointer]
230
+ result = journey_data.list.drop(1)
231
+ pointer = journey_data.pointer - 1
232
+ return if completed.empty? || (result == [completed] && pointer < 0)
233
+
244
234
  target_width = Reline::Unicode.calculate_width(target)
245
- x = cursor_pos.x - target_width
246
- if x < 0
247
- 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
248
239
  y = -1
249
240
  else
241
+ x = [cursor_pos.x - completed_width, 0].max
250
242
  y = 0
251
243
  end
252
244
  cursor_pos_to_render = Reline::CursorPos.new(x, y)
@@ -260,21 +252,21 @@ module Reline
260
252
  contents: result,
261
253
  scrollbar: true,
262
254
  height: [15, preferred_dialog_height].min,
263
- bg_color: 46,
264
- pointer_bg_color: 45,
265
- fg_color: 37,
266
- pointer_fg_color: 37
255
+ face: :completion_dialog
267
256
  )
268
257
  }
269
258
  Reline::DEFAULT_DIALOG_CONTEXT = Array.new
270
259
 
271
260
  def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
272
- Reline.update_iogate
273
- io_gate.with_raw_input do
261
+ @mutex.synchronize do
274
262
  unless confirm_multiline_termination
275
263
  raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
276
264
  end
277
- inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
265
+
266
+ Reline.update_iogate
267
+ io_gate.with_raw_input do
268
+ inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
269
+ end
278
270
 
279
271
  whole_buffer = line_editor.whole_buffer.dup
280
272
  whole_buffer.taint if RUBY_VERSION < '2.7'
@@ -282,23 +274,32 @@ module Reline
282
274
  Reline::HISTORY << whole_buffer
283
275
  end
284
276
 
285
- line_editor.reset_line if line_editor.whole_buffer.nil?
286
- whole_buffer
277
+ if line_editor.eof?
278
+ line_editor.reset_line
279
+ # Return nil if the input is aborted by C-d.
280
+ nil
281
+ else
282
+ whole_buffer
283
+ end
287
284
  end
288
285
  end
289
286
 
290
287
  def readline(prompt = '', add_hist = false)
291
- Reline.update_iogate
292
- inner_readline(prompt, add_hist, false)
288
+ @mutex.synchronize do
289
+ Reline.update_iogate
290
+ io_gate.with_raw_input do
291
+ inner_readline(prompt, add_hist, false)
292
+ end
293
293
 
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
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
299
299
 
300
- line_editor.reset_line if line_editor.line.nil?
301
- line
300
+ line_editor.reset_line if line_editor.line.nil?
301
+ line
302
+ end
302
303
  end
303
304
 
304
305
  private def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
@@ -311,6 +312,10 @@ module Reline
311
312
  $stderr.sync = true
312
313
  $stderr.puts "Reline is used by #{Process.pid}"
313
314
  end
315
+ unless config.test_mode or config.loaded?
316
+ config.read
317
+ io_gate.set_default_key_bindings(config)
318
+ end
314
319
  otio = io_gate.prep
315
320
 
316
321
  may_req_ambiguous_char_width
@@ -330,58 +335,47 @@ module Reline
330
335
  line_editor.prompt_proc = prompt_proc
331
336
  line_editor.auto_indent_proc = auto_indent_proc
332
337
  line_editor.dig_perfect_match_proc = dig_perfect_match_proc
333
- line_editor.pre_input_hook = pre_input_hook
334
- @dialog_proc_list.each_pair do |name_sym, d|
335
- line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
336
- end
337
-
338
- unless config.test_mode
339
- config.read
340
- config.reset_default_key_bindings
341
- io_gate.set_default_key_bindings(config)
338
+ pre_input_hook&.call
339
+ unless Reline::IOGate == Reline::GeneralIO
340
+ @dialog_proc_list.each_pair do |name_sym, d|
341
+ line_editor.add_dialog_proc(name_sym, d.dialog_proc, d.context)
342
+ end
342
343
  end
343
344
 
345
+ line_editor.print_nomultiline_prompt(prompt)
346
+ line_editor.update_dialogs
344
347
  line_editor.rerender
345
348
 
346
349
  begin
347
350
  line_editor.set_signal_handlers
348
- prev_pasting_state = false
349
351
  loop do
350
- prev_pasting_state = io_gate.in_pasting?
351
352
  read_io(config.keyseq_timeout) { |inputs|
352
353
  line_editor.set_pasting_state(io_gate.in_pasting?)
353
- inputs.each { |c|
354
- line_editor.input_key(c)
355
- line_editor.rerender
356
- }
357
- if @bracketed_paste_finished
358
- line_editor.rerender_all
359
- @bracketed_paste_finished = false
354
+ inputs.each do |key|
355
+ if key.char == :bracketed_paste_start
356
+ text = io_gate.read_bracketed_paste
357
+ line_editor.insert_pasted_text(text)
358
+ line_editor.scroll_into_view
359
+ else
360
+ line_editor.update(key)
361
+ end
360
362
  end
361
363
  }
362
- if prev_pasting_state == true and not io_gate.in_pasting? and not line_editor.finished?
363
- line_editor.set_pasting_state(false)
364
- prev_pasting_state = false
365
- line_editor.rerender_all
364
+ if line_editor.finished?
365
+ line_editor.render_finished
366
+ break
367
+ else
368
+ line_editor.set_pasting_state(io_gate.in_pasting?)
369
+ line_editor.rerender
366
370
  end
367
- break if line_editor.finished?
368
371
  end
369
372
  io_gate.move_cursor_column(0)
370
373
  rescue Errno::EIO
371
374
  # Maybe the I/O has been closed.
372
- rescue StandardError => e
375
+ ensure
373
376
  line_editor.finalize
374
377
  io_gate.deprep(otio)
375
- raise e
376
- rescue Exception
377
- # Including Interrupt
378
- line_editor.finalize
379
- io_gate.deprep(otio)
380
- raise
381
378
  end
382
-
383
- line_editor.finalize
384
- io_gate.deprep(otio)
385
379
  end
386
380
 
387
381
  # GNU Readline waits for "keyseq-timeout" milliseconds to see if the ESC
@@ -399,7 +393,6 @@ module Reline
399
393
  c = io_gate.getc(Float::INFINITY)
400
394
  if c == -1
401
395
  result = :unmatched
402
- @bracketed_paste_finished = true
403
396
  else
404
397
  buffer << c
405
398
  result = key_stroke.match_status(buffer)
@@ -606,4 +599,6 @@ else
606
599
  io
607
600
  end
608
601
 
602
+ Reline::Face.load_initial_configs
603
+
609
604
  Reline::HISTORY = Reline::History.new(Reline.core.config)
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.3.9
4
+ version: 0.5.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-03 00:00:00.000000000 Z
11
+ date: 2024-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -37,6 +37,7 @@ files:
37
37
  - lib/reline.rb
38
38
  - lib/reline/ansi.rb
39
39
  - lib/reline/config.rb
40
+ - lib/reline/face.rb
40
41
  - lib/reline/general_io.rb
41
42
  - lib/reline/history.rb
42
43
  - lib/reline/key_actor.rb
@@ -56,7 +57,10 @@ files:
56
57
  homepage: https://github.com/ruby/reline
57
58
  licenses:
58
59
  - Ruby
59
- metadata: {}
60
+ metadata:
61
+ bug_tracker_uri: https://github.com/ruby/reline/issues
62
+ changelog_uri: https://github.com/ruby/reline/releases
63
+ source_code_uri: https://github.com/ruby/reline
60
64
  post_install_message:
61
65
  rdoc_options: []
62
66
  require_paths:
@@ -72,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
76
  - !ruby/object:Gem::Version
73
77
  version: '0'
74
78
  requirements: []
75
- rubygems_version: 3.4.13
79
+ rubygems_version: 3.5.9
76
80
  signing_key:
77
81
  specification_version: 4
78
82
  summary: Alternative GNU Readline or Editline implementation by pure Ruby.