reline 0.3.0 → 0.3.2

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: 2e8c2181e5eb21934546d6b768715cb47eab6e1ea1edb2fd2300f43af62dc2b1
4
- data.tar.gz: 99a61e2729d4b5d150f6f59d99d6de235ea4f1aab6ac262c92fff6a77101b85f
3
+ metadata.gz: 8198104fe3ad27c73acaaffc9c8b1c8b9a5c50069a26a71cd940dd2b5dc13afc
4
+ data.tar.gz: 171ff8cdf2ae4aebaf4cfb6cea131e2d90e5eb56a2c09dbe19acfa8765522ffc
5
5
  SHA512:
6
- metadata.gz: 96ceefe0bf71e7e44ccd2a5970b35fc44019af9d7da4d2e04ac1d827eb6bc6a201e679a5e40c4db976554159286f16e39cdeac68bcf69c2c5abeb00a2045e563
7
- data.tar.gz: 251c2029df6b3073010ad6a591fb216990124ed4f9e00f533e79461be66c6a2383ea56c1554dd273e4af3cfc9358dee37b76a1b4bff25a1867972201bfebace3
6
+ metadata.gz: e3411f0086a0445d2d805fc46528a86b947c58b8e61a664dc08b437596702ef1f360cd7e85a1dab64bf159f6f39691b9d5110f951bd5376f4fce0439e9557532
7
+ data.tar.gz: 968ac4a376ba350590fd37d05525a5a2e5e26c44e159b5fd9b0c7400e260850e91088357c007651373d44b7b7d37c699852014d07258428be86081581baf0229
data/lib/reline/ansi.rb CHANGED
@@ -150,7 +150,7 @@ class Reline::ANSI
150
150
  until c = @@input.raw(intr: true) { @@input.wait_readable(0.1) && @@input.getbyte }
151
151
  Reline.core.line_editor.resize
152
152
  end
153
- (c == 0x16 && @@input.raw(min: 0, tim: 0, &:getbyte)) || c
153
+ (c == 0x16 && @@input.raw(min: 0, time: 0, &:getbyte)) || c
154
154
  rescue Errno::EIO
155
155
  # Maybe the I/O has been closed.
156
156
  nil
data/lib/reline/config.rb CHANGED
@@ -45,6 +45,8 @@ class Reline::Config
45
45
  attr_accessor v
46
46
  end
47
47
 
48
+ attr_accessor :autocompletion
49
+
48
50
  def initialize
49
51
  @additional_key_bindings = {} # from inputrc
50
52
  @additional_key_bindings[:emacs] = {}
@@ -55,6 +57,7 @@ class Reline::Config
55
57
  @if_stack = nil
56
58
  @editing_mode_label = :emacs
57
59
  @keymap_label = :emacs
60
+ @keymap_prefix = []
58
61
  @key_actors = {}
59
62
  @key_actors[:emacs] = Reline::KeyActor::Emacs.new
60
63
  @key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new
@@ -93,14 +96,6 @@ class Reline::Config
93
96
  (val.respond_to?(:any?) ? val : [val]).any?(@editing_mode_label)
94
97
  end
95
98
 
96
- def autocompletion=(val)
97
- @autocompletion = val
98
- end
99
-
100
- def autocompletion
101
- @autocompletion
102
- end
103
-
104
99
  def keymap
105
100
  @key_actors[@keymap_label]
106
101
  end
@@ -221,7 +216,7 @@ class Reline::Config
221
216
  key, func_name = $1, $2
222
217
  keystroke, func = bind_key(key, func_name)
223
218
  next unless keystroke
224
- @additional_key_bindings[@keymap_label][keystroke] = func
219
+ @additional_key_bindings[@keymap_label][@keymap_prefix + keystroke] = func
225
220
  end
226
221
  end
227
222
  unless @if_stack.empty?
@@ -292,18 +287,29 @@ class Reline::Config
292
287
  when 'emacs'
293
288
  @editing_mode_label = :emacs
294
289
  @keymap_label = :emacs
290
+ @keymap_prefix = []
295
291
  when 'vi'
296
292
  @editing_mode_label = :vi_insert
297
293
  @keymap_label = :vi_insert
294
+ @keymap_prefix = []
298
295
  end
299
296
  when 'keymap'
300
297
  case value
301
- when 'emacs', 'emacs-standard', 'emacs-meta', 'emacs-ctlx'
298
+ when 'emacs', 'emacs-standard'
299
+ @keymap_label = :emacs
300
+ @keymap_prefix = []
301
+ when 'emacs-ctlx'
302
+ @keymap_label = :emacs
303
+ @keymap_prefix = [?\C-x.ord]
304
+ when 'emacs-meta'
302
305
  @keymap_label = :emacs
306
+ @keymap_prefix = [?\e.ord]
303
307
  when 'vi', 'vi-move', 'vi-command'
304
308
  @keymap_label = :vi_command
309
+ @keymap_prefix = []
305
310
  when 'vi-insert'
306
311
  @keymap_label = :vi_insert
312
+ @keymap_prefix = []
307
313
  end
308
314
  when 'keyseq-timeout'
309
315
  @keyseq_timeout = value.to_i
@@ -57,6 +57,12 @@ class Reline::GeneralIO
57
57
  Reline::CursorPos.new(1, 1)
58
58
  end
59
59
 
60
+ def self.hide_cursor
61
+ end
62
+
63
+ def self.show_cursor
64
+ end
65
+
60
66
  def self.move_cursor_column(val)
61
67
  end
62
68
 
@@ -43,7 +43,7 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
43
43
  # 20 ^T
44
44
  :ed_transpose_chars,
45
45
  # 21 ^U
46
- :ed_kill_line,
46
+ :unix_line_discard,
47
47
  # 22 ^V
48
48
  :ed_quoted_insert,
49
49
  # 23 ^W
@@ -466,15 +466,17 @@ class Reline::LineEditor
466
466
  new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
467
467
  rendered = false
468
468
  if @add_newline_to_end_of_buffer
469
+ clear_dialog_with_content
469
470
  rerender_added_newline(prompt, prompt_width)
470
471
  @add_newline_to_end_of_buffer = false
471
472
  else
472
473
  if @just_cursor_moving and not @rerender_all
474
+ clear_dialog_with_content
473
475
  rendered = just_move_cursor
474
- render_dialog((prompt_width + @cursor) % @screen_size.last)
475
476
  @just_cursor_moving = false
476
477
  return
477
478
  elsif @previous_line_index or new_highest_in_this != @highest_in_this
479
+ clear_dialog_with_content
478
480
  rerender_changed_current_line
479
481
  @previous_line_index = nil
480
482
  rendered = true
@@ -653,7 +655,10 @@ class Reline::LineEditor
653
655
  end
654
656
 
655
657
  private def padding_space_with_escape_sequences(str, width)
656
- str + (' ' * (width - calculate_width(str, true)))
658
+ padding_width = width - calculate_width(str, true)
659
+ # padding_width should be only positive value. But macOS and Alacritty returns negative value.
660
+ padding_width = 0 if padding_width < 0
661
+ str + (' ' * padding_width)
657
662
  end
658
663
 
659
664
  private def render_each_dialog(dialog, cursor_column)
@@ -741,24 +746,21 @@ class Reline::LineEditor
741
746
  Reline::IOGate.move_cursor_column(dialog.column)
742
747
  dialog.contents.each_with_index do |item, i|
743
748
  if i == pointer
744
- bg_color = '45'
749
+ fg_color = dialog_render_info.pointer_fg_color
750
+ bg_color = dialog_render_info.pointer_bg_color
745
751
  else
746
- if dialog_render_info.bg_color
747
- bg_color = dialog_render_info.bg_color
748
- else
749
- bg_color = '46'
750
- end
752
+ fg_color = dialog_render_info.fg_color
753
+ bg_color = dialog_render_info.bg_color
751
754
  end
752
755
  str_width = dialog.width - (dialog.scrollbar_pos.nil? ? 0 : @block_elem_width)
753
756
  str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, str_width), str_width)
754
- @output.write "\e[#{bg_color}m#{str}"
757
+ @output.write "\e[#{bg_color}m\e[#{fg_color}m#{str}"
755
758
  if dialog.scrollbar_pos and (dialog.scrollbar_pos != old_dialog.scrollbar_pos or dialog.column != old_dialog.column)
756
759
  @output.write "\e[37m"
757
760
  if dialog.scrollbar_pos <= (i * 2) and (i * 2 + 1) < (dialog.scrollbar_pos + bar_height)
758
761
  @output.write @full_block
759
762
  elsif dialog.scrollbar_pos <= (i * 2) and (i * 2) < (dialog.scrollbar_pos + bar_height)
760
763
  @output.write @upper_half_block
761
- str += ''
762
764
  elsif dialog.scrollbar_pos <= (i * 2 + 1) and (i * 2) < (dialog.scrollbar_pos + bar_height)
763
765
  @output.write @lower_half_block
764
766
  else
@@ -883,6 +885,14 @@ class Reline::LineEditor
883
885
  end
884
886
  end
885
887
 
888
+ private def clear_dialog_with_content
889
+ @dialogs.each do |dialog|
890
+ clear_each_dialog(dialog)
891
+ dialog.contents = nil
892
+ dialog.trap_key = nil
893
+ end
894
+ end
895
+
886
896
  private def clear_each_dialog(dialog)
887
897
  dialog.trap_key = nil
888
898
  return unless dialog.contents
@@ -1420,7 +1430,7 @@ class Reline::LineEditor
1420
1430
  if @waiting_operator_proc
1421
1431
  if VI_MOTIONS.include?(method_symbol)
1422
1432
  old_cursor, old_byte_pointer = @cursor, @byte_pointer
1423
- @vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg > 1
1433
+ @vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg&.> 1
1424
1434
  block.(true)
1425
1435
  unless @waiting_proc
1426
1436
  cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
@@ -2223,6 +2233,8 @@ class Reline::LineEditor
2223
2233
  @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
2224
2234
  @line_index = @buffer_of_lines.size - 1
2225
2235
  @line = @buffer_of_lines.last
2236
+ @byte_pointer = @line.bytesize
2237
+ @cursor = @cursor_max = calculate_width(@line)
2226
2238
  @rerender_all = true
2227
2239
  @searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
2228
2240
  else
@@ -35,7 +35,7 @@ module Reline::Terminfo
35
35
  # Gem module isn't defined in test-all of the Ruby repository, and
36
36
  # Fiddle in Ruby 3.0.0 or later supports Fiddle::TYPE_VARIADIC.
37
37
  fiddle_supports_variadic = true
38
- elsif Fiddle.const_defined?(:VERSION) and Gem::Version.create(Fiddle::VERSION) >= Gem::Version.create('1.0.1')
38
+ elsif Fiddle.const_defined?(:VERSION,false) and Gem::Version.create(Fiddle::VERSION) >= Gem::Version.create('1.0.1')
39
39
  # Fiddle::TYPE_VARIADIC is supported from Fiddle 1.0.1.
40
40
  fiddle_supports_variadic = true
41
41
  else
@@ -74,12 +74,27 @@ module Reline::Terminfo
74
74
  #extern 'char *tparm(const char *str, ...)'
75
75
  @tiparm = Fiddle::Function.new(curses_dl['tparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
76
76
  end
77
- # TODO: add int tigetflag(char *capname) and int tigetnum(char *capname)
77
+ begin
78
+ #extern 'int tigetflag(char *str)'
79
+ @tigetflag = Fiddle::Function.new(curses_dl['tigetflag'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
80
+ rescue Fiddle::DLError
81
+ # OpenBSD lacks tigetflag
82
+ #extern 'int tgetflag(char *str)'
83
+ @tigetflag = Fiddle::Function.new(curses_dl['tgetflag'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
84
+ end
85
+ begin
86
+ #extern 'int tigetnum(char *str)'
87
+ @tigetnum = Fiddle::Function.new(curses_dl['tigetnum'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
88
+ rescue Fiddle::DLError
89
+ # OpenBSD lacks tigetnum
90
+ #extern 'int tgetnum(char *str)'
91
+ @tigetnum = Fiddle::Function.new(curses_dl['tgetnum'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
92
+ end
78
93
 
79
94
  def self.setupterm(term, fildes)
80
- errret_int = String.new("\x00" * 8, encoding: 'ASCII-8BIT')
95
+ errret_int = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT)
81
96
  ret = @setupterm.(term, fildes, errret_int)
82
- errret = errret_int.unpack1('i')
97
+ errret = errret_int[0, Fiddle::SIZEOF_INT].unpack1('i')
83
98
  case ret
84
99
  when 0 # OK
85
100
  0
@@ -106,6 +121,7 @@ module Reline::Terminfo
106
121
  end
107
122
 
108
123
  def self.tigetstr(capname)
124
+ raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String)
109
125
  capability = @tigetstr.(capname)
110
126
  case capability.to_i
111
127
  when 0, -1
@@ -122,6 +138,30 @@ module Reline::Terminfo
122
138
  @tiparm.(str, *new_args).to_s
123
139
  end
124
140
 
141
+ def self.tigetflag(capname)
142
+ raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String)
143
+ flag = @tigetflag.(capname).to_i
144
+ case flag
145
+ when -1
146
+ raise TerminfoError, "not boolean capability: #{capname}"
147
+ when 0
148
+ raise TerminfoError, "can't find capability: #{capname}"
149
+ end
150
+ flag
151
+ end
152
+
153
+ def self.tigetnum(capname)
154
+ raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String)
155
+ num = @tigetnum.(capname).to_i
156
+ case num
157
+ when -2
158
+ raise TerminfoError, "not numeric capability: #{capname}"
159
+ when -1
160
+ raise TerminfoError, "can't find capability: #{capname}"
161
+ end
162
+ num
163
+ end
164
+
125
165
  def self.enabled?
126
166
  true
127
167
  end
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.3.0'
2
+ VERSION = '0.3.2'
3
3
  end
@@ -95,7 +95,7 @@ class Reline::Windows
95
95
  end
96
96
 
97
97
  VK_RETURN = 0x0D
98
- VK_MENU = 0x12
98
+ VK_MENU = 0x12 # ALT key
99
99
  VK_LMENU = 0xA4
100
100
  VK_CONTROL = 0x11
101
101
  VK_SHIFT = 0x10
@@ -249,7 +249,7 @@ class Reline::Windows
249
249
  # no char, only control keys
250
250
  return if key.char_code == 0 and key.control_keys.any?
251
251
 
252
- @@output_buf.push("\e".ord) if key.control_keys.include?(:ALT)
252
+ @@output_buf.push("\e".ord) if key.control_keys.include?(:ALT) and !key.control_keys.include?(:CTRL)
253
253
 
254
254
  @@output_buf.concat(key.char.bytes)
255
255
  end
@@ -386,7 +386,7 @@ class Reline::Windows
386
386
  def self.scroll_down(val)
387
387
  return if val < 0
388
388
  return unless csbi = get_console_screen_buffer_info
389
- buffer_width, x, y, buffer_lines, attributes, window_left, window_top, window_bottom = csbi.unpack('ssssSssx2s')
389
+ buffer_width, buffer_lines, x, y, attributes, window_left, window_top, window_bottom = csbi.unpack('ssssSssx2s')
390
390
  screen_height = window_bottom - window_top + 1
391
391
  val = screen_height if val > screen_height
392
392
 
data/lib/reline.rb CHANGED
@@ -33,7 +33,18 @@ module Reline
33
33
  alias_method :==, :match?
34
34
  end
35
35
  CursorPos = Struct.new(:x, :y)
36
- DialogRenderInfo = Struct.new(:pos, :contents, :bg_color, :width, :height, :scrollbar, keyword_init: true)
36
+ DialogRenderInfo = Struct.new(
37
+ :pos,
38
+ :contents,
39
+ :bg_color,
40
+ :pointer_bg_color,
41
+ :fg_color,
42
+ :pointer_fg_color,
43
+ :width,
44
+ :height,
45
+ :scrollbar,
46
+ keyword_init: true
47
+ )
37
48
 
38
49
  class Core
39
50
  ATTR_READER_NAMES = %i(
@@ -58,6 +69,11 @@ module Reline
58
69
  attr_accessor :last_incremental_search
59
70
  attr_reader :output
60
71
 
72
+ extend Forwardable
73
+ def_delegators :config,
74
+ :autocompletion,
75
+ :autocompletion=
76
+
61
77
  def initialize
62
78
  self.output = STDOUT
63
79
  @dialog_proc_list = {}
@@ -123,14 +139,6 @@ module Reline
123
139
  @completion_proc = p
124
140
  end
125
141
 
126
- def autocompletion
127
- @config.autocompletion
128
- end
129
-
130
- def autocompletion=(val)
131
- @config.autocompletion = val
132
- end
133
-
134
142
  def output_modifier_proc=(p)
135
143
  raise ArgumentError unless p.respond_to?(:call) or p.nil?
136
144
  @output_modifier_proc = p
@@ -243,7 +251,16 @@ module Reline
243
251
  context.push(cursor_pos_to_render, result, pointer, dialog)
244
252
  end
245
253
  dialog.pointer = pointer
246
- DialogRenderInfo.new(pos: cursor_pos_to_render, contents: result, scrollbar: true, height: 15)
254
+ DialogRenderInfo.new(
255
+ pos: cursor_pos_to_render,
256
+ contents: result,
257
+ scrollbar: true,
258
+ height: 15,
259
+ bg_color: 46,
260
+ pointer_bg_color: 45,
261
+ fg_color: 37,
262
+ pointer_fg_color: 37
263
+ )
247
264
  }
248
265
  Reline::DEFAULT_DIALOG_CONTEXT = Array.new
249
266
 
@@ -466,7 +483,7 @@ module Reline
466
483
  end
467
484
 
468
485
  private def may_req_ambiguous_char_width
469
- @ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or STDOUT.is_a?(File)
486
+ @ambiguous_width = 2 if Reline::IOGate == Reline::GeneralIO or !STDOUT.tty?
470
487
  return if defined? @ambiguous_width
471
488
  Reline::IOGate.move_cursor_column(0)
472
489
  begin
@@ -563,24 +580,21 @@ module Reline
563
580
  end
564
581
 
565
582
  require 'reline/general_io'
566
- if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
567
- require 'reline/windows'
568
- if Reline::Windows.msys_tty?
569
- Reline::IOGate = if ENV['TERM'] == 'dumb'
570
- Reline::GeneralIO
571
- else
572
- require 'reline/ansi'
573
- Reline::ANSI
574
- end
583
+ io = Reline::GeneralIO
584
+ unless ENV['TERM'] == 'dumb'
585
+ case RbConfig::CONFIG['host_os']
586
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
587
+ require 'reline/windows'
588
+ tty = (io = Reline::Windows).msys_tty?
575
589
  else
576
- Reline::IOGate = Reline::Windows
590
+ tty = $stdout.tty?
577
591
  end
592
+ end
593
+ Reline::IOGate = if tty
594
+ require 'reline/ansi'
595
+ Reline::ANSI
578
596
  else
579
- Reline::IOGate = if $stdout.isatty
580
- require 'reline/ansi'
581
- Reline::ANSI
582
- else
583
- Reline::GeneralIO
584
- end
597
+ io
585
598
  end
599
+
586
600
  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.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-24 00:00:00.000000000 Z
11
+ date: 2022-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -47,7 +47,6 @@ files:
47
47
  - lib/reline/key_stroke.rb
48
48
  - lib/reline/kill_ring.rb
49
49
  - lib/reline/line_editor.rb
50
- - lib/reline/sibori.rb
51
50
  - lib/reline/terminfo.rb
52
51
  - lib/reline/unicode.rb
53
52
  - lib/reline/unicode/east_asian_width.rb
@@ -66,14 +65,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
66
65
  requirements:
67
66
  - - ">="
68
67
  - !ruby/object:Gem::Version
69
- version: '2.5'
68
+ version: '2.6'
70
69
  required_rubygems_version: !ruby/object:Gem::Requirement
71
70
  requirements:
72
71
  - - ">="
73
72
  - !ruby/object:Gem::Version
74
73
  version: '0'
75
74
  requirements: []
76
- rubygems_version: 3.2.22
75
+ rubygems_version: 3.4.0.dev
77
76
  signing_key:
78
77
  specification_version: 4
79
78
  summary: Alternative GNU Readline or Editline implementation by pure Ruby.
data/lib/reline/sibori.rb DELETED
@@ -1,170 +0,0 @@
1
- require 'reline/unicode'
2
-
3
- =begin
4
-
5
- \ |
6
- \ | <--- whipped cream
7
- \ |
8
- \ |
9
- \-~~|
10
- \ | <--- shibori kutigane (piping nozzle in Japanese)
11
- \Ml
12
- (\ __ __
13
- ( \--( ) )
14
- (__(__)__) <--- compressed whipped cream
15
- =end
16
-
17
- class Sibori
18
- attr_writer :output
19
-
20
- def initialize(width, height, cursor_pos)
21
- @width = width
22
- @height = height
23
- @cursor_pos = cursor_pos
24
- @screen = [String.new]
25
- @line_index = 0
26
- @byte_pointer_in_line = 0
27
- @cleared = false
28
- clone_screen
29
- end
30
-
31
- def clone_screen
32
- @prev_screen = @screen.map { |line|
33
- line.dup
34
- }
35
- @prev_cursor_pos = @cursor_pos.dup
36
- @prev_line_index = @line_index
37
- end
38
-
39
- def print(str)
40
- #$stderr.puts "print #{str.inspect}"
41
- line = @screen[@line_index]
42
- before = line.byteslice(0, @byte_pointer_in_line)
43
- str_width = Reline::Unicode.calculate_width(str, true)
44
- after_cursor = line.byteslice(@byte_pointer_in_line..-1)
45
- after_cursor_width = Reline::Unicode.calculate_width(after_cursor, true)
46
- rest = ''
47
- if after_cursor_width > str_width
48
- rest_byte_pointer = @byte_pointer_in_line + width_to_bytesize(after_cursor, str_width)
49
- rest = line.byteslice(rest_byte_pointer..-1)
50
- end
51
- @screen[@line_index] = before + str + rest
52
- @byte_pointer_in_line += str.bytesize
53
- @cursor_pos.x += Reline::Unicode.calculate_width(str, true)
54
- end
55
-
56
- def move_cursor_column(col)
57
- #$stderr.puts "move_cursor_column(#{col})"
58
- @byte_pointer_in_line = width_to_bytesize(@screen[@line_index], col)
59
- @cursor_pos.x = col
60
- end
61
-
62
- def move_cursor_up(val)
63
- #$stderr.puts "move_cursor_up(#{val})"
64
- if @line_index.positive?
65
- @line_index -= val
66
- @byte_pointer_in_line = width_to_bytesize(@screen[@line_index], @cursor_pos.x)
67
- @cursor_pos.y -= val
68
- end
69
- end
70
-
71
- def move_cursor_down(val)
72
- #$stderr.puts "move_cursor_down(#{val})"
73
- if @line_index < @height - 1
74
- #$stderr.puts "@line_index #{@line_index} @screen.size #{@screen.size} @height #{@height}"
75
- #$stderr.puts @screen.inspect
76
- @line_index += val
77
- @screen[@line_index] = String.new if @line_index == @screen.size
78
- @byte_pointer_in_line = width_to_bytesize(@screen[@line_index], @cursor_pos.x)
79
- @cursor_pos.y += val
80
- end
81
- end
82
-
83
- def scroll_down(val)
84
- #$stderr.puts "scroll_down(#{val})"
85
- if val >= @height
86
- clear_screen
87
- @line_index = @screen.size - 1
88
- return
89
- end
90
- @screen.size.times do |n|
91
- if n < @screen.size - val
92
- #$stderr.puts "A @screen[#{val} + #{n}] (#{@screen[val + n].inspect}) to @screen[#{n}]"
93
- @screen[n] = @screen[val + n]
94
- else
95
- #$stderr.puts "B String.new to @screen[#{n}]"
96
- @screen[n] = String.new
97
- end
98
- end
99
- @line_index += val
100
- end
101
-
102
- def erase_after_cursor
103
- #$stderr.puts "erase_after_cursor"
104
- @screen[@line_index] = @screen[@line_index].byteslice(0, @byte_pointer_in_line)
105
- end
106
-
107
- def clear_screen
108
- #$stderr.puts "clear_screen"
109
- @screen = [String.new]
110
- @line_index = 0
111
- @byte_pointer_in_line = 0
112
- @cursor_pos.x = @cursor_pos.y = 0
113
- @cleared = true
114
- Reline::IOGate.clear_screen
115
- end
116
-
117
- private def width_to_bytesize(str, width)
118
- lines, _ = Reline::Unicode.split_by_width(str, width)
119
- lines.first.bytesize
120
- end
121
-
122
- def render
123
- #$stderr.puts ?* * 100
124
- Reline::IOGate.move_cursor_up(@prev_line_index) if @prev_line_index.positive?
125
- #$stderr.puts "! move_cursor_up(#{@prev_line_index})" if @prev_line_index.positive?
126
- #$stderr.puts "@prev_line_index #{@prev_line_index} @line_index #{@line_index}"
127
- if @screen.size > @prev_screen.size
128
- #$stderr.puts ?a * 100
129
- down = @screen.size - @prev_screen.size
130
- #$stderr.puts "#{@prev_cursor_pos.y} #{down} #{@height}"
131
- if @prev_cursor_pos.y + down > (@height - 1)
132
- #$stderr.puts ?b * 100
133
- scroll = (@prev_cursor_pos.y + down) - (@height - 1)
134
- Reline::IOGate.scroll_down(scroll)
135
- #$stderr.puts "! scroll_down(#{scroll})"
136
- #$stderr.puts "down #{down}"
137
- Reline::IOGate.move_cursor_up(@screen.size - 1 - scroll)
138
- #$stderr.puts "! move_cursor_up(#{@screen.size - 1})"
139
- else
140
- #$stderr.puts ?c * 100
141
- end
142
- end
143
- @screen.size.times do |n|
144
- Reline::IOGate.move_cursor_column(0)
145
- #$stderr.puts "! move_cursor_column(0)"
146
- @output.write @screen[n]
147
- #$stderr.puts "! print #{@screen[n].inspect}"
148
- Reline::IOGate.erase_after_cursor
149
- #$stderr.puts "! erase_after_cursor"
150
- Reline::IOGate.move_cursor_down(1) if n != (@screen.size - 1)
151
- #$stderr.puts "! move_cursor_down(1)" if n != (@screen.size - 1)
152
- end
153
- up = @screen.size - 1 - @line_index
154
- Reline::IOGate.move_cursor_up(up) if up.positive?
155
- #$stderr.puts "! move_cursor_up(#{up})" if up.positive?
156
- column = Reline::Unicode.calculate_width(@screen[@line_index].byteslice(0, @byte_pointer_in_line), true)
157
- Reline::IOGate.move_cursor_column(column)
158
- #$stderr.puts "! move_cursor_column(#{column}) #{@byte_pointer_in_line}"
159
- clone_screen
160
- #$stderr.puts ?- * 10
161
- end
162
-
163
- def prep
164
- Reline::IOGate.prep
165
- end
166
-
167
- def deprep
168
- Reline::IOGate.deprep
169
- end
170
- end