reline 0.3.0 → 0.3.6
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 +4 -4
- data/README.md +25 -1
- data/lib/reline/ansi.rb +11 -4
- data/lib/reline/config.rb +17 -11
- data/lib/reline/general_io.rb +10 -0
- data/lib/reline/key_actor/emacs.rb +1 -1
- data/lib/reline/key_stroke.rb +50 -7
- data/lib/reline/line_editor.rb +235 -282
- data/lib/reline/terminfo.rb +44 -18
- data/lib/reline/unicode/east_asian_width.rb +88 -56
- data/lib/reline/unicode.rb +30 -53
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +7 -3
- data/lib/reline.rb +115 -80
- metadata +4 -5
- data/lib/reline/sibori.rb +0 -170
data/lib/reline.rb
CHANGED
@@ -11,12 +11,13 @@ require 'reline/terminfo'
|
|
11
11
|
require 'rbconfig'
|
12
12
|
|
13
13
|
module Reline
|
14
|
+
# NOTE: For making compatible with the rb-readline gem
|
14
15
|
FILENAME_COMPLETION_PROC = nil
|
15
16
|
USERNAME_COMPLETION_PROC = nil
|
16
17
|
|
17
18
|
class ConfigEncodingConversionError < StandardError; end
|
18
19
|
|
19
|
-
Key = Struct.new(
|
20
|
+
Key = Struct.new(:char, :combined_char, :with_meta) do
|
20
21
|
def match?(other)
|
21
22
|
case other
|
22
23
|
when Reline::Key
|
@@ -33,7 +34,18 @@ module Reline
|
|
33
34
|
alias_method :==, :match?
|
34
35
|
end
|
35
36
|
CursorPos = Struct.new(:x, :y)
|
36
|
-
DialogRenderInfo = Struct.new(
|
37
|
+
DialogRenderInfo = Struct.new(
|
38
|
+
:pos,
|
39
|
+
:contents,
|
40
|
+
:bg_color,
|
41
|
+
:pointer_bg_color,
|
42
|
+
:fg_color,
|
43
|
+
:pointer_fg_color,
|
44
|
+
:width,
|
45
|
+
:height,
|
46
|
+
:scrollbar,
|
47
|
+
keyword_init: true
|
48
|
+
)
|
37
49
|
|
38
50
|
class Core
|
39
51
|
ATTR_READER_NAMES = %i(
|
@@ -58,6 +70,11 @@ module Reline
|
|
58
70
|
attr_accessor :last_incremental_search
|
59
71
|
attr_reader :output
|
60
72
|
|
73
|
+
extend Forwardable
|
74
|
+
def_delegators :config,
|
75
|
+
:autocompletion,
|
76
|
+
:autocompletion=
|
77
|
+
|
61
78
|
def initialize
|
62
79
|
self.output = STDOUT
|
63
80
|
@dialog_proc_list = {}
|
@@ -66,44 +83,48 @@ module Reline
|
|
66
83
|
@bracketed_paste_finished = false
|
67
84
|
end
|
68
85
|
|
86
|
+
def io_gate
|
87
|
+
Reline::IOGate
|
88
|
+
end
|
89
|
+
|
69
90
|
def encoding
|
70
|
-
|
91
|
+
io_gate.encoding
|
71
92
|
end
|
72
93
|
|
73
94
|
def completion_append_character=(val)
|
74
95
|
if val.nil?
|
75
96
|
@completion_append_character = nil
|
76
97
|
elsif val.size == 1
|
77
|
-
@completion_append_character = val.encode(
|
98
|
+
@completion_append_character = val.encode(encoding)
|
78
99
|
elsif val.size > 1
|
79
|
-
@completion_append_character = val[0].encode(
|
100
|
+
@completion_append_character = val[0].encode(encoding)
|
80
101
|
else
|
81
102
|
@completion_append_character = nil
|
82
103
|
end
|
83
104
|
end
|
84
105
|
|
85
106
|
def basic_word_break_characters=(v)
|
86
|
-
@basic_word_break_characters = v.encode(
|
107
|
+
@basic_word_break_characters = v.encode(encoding)
|
87
108
|
end
|
88
109
|
|
89
110
|
def completer_word_break_characters=(v)
|
90
|
-
@completer_word_break_characters = v.encode(
|
111
|
+
@completer_word_break_characters = v.encode(encoding)
|
91
112
|
end
|
92
113
|
|
93
114
|
def basic_quote_characters=(v)
|
94
|
-
@basic_quote_characters = v.encode(
|
115
|
+
@basic_quote_characters = v.encode(encoding)
|
95
116
|
end
|
96
117
|
|
97
118
|
def completer_quote_characters=(v)
|
98
|
-
@completer_quote_characters = v.encode(
|
119
|
+
@completer_quote_characters = v.encode(encoding)
|
99
120
|
end
|
100
121
|
|
101
122
|
def filename_quote_characters=(v)
|
102
|
-
@filename_quote_characters = v.encode(
|
123
|
+
@filename_quote_characters = v.encode(encoding)
|
103
124
|
end
|
104
125
|
|
105
126
|
def special_prefixes=(v)
|
106
|
-
@special_prefixes = v.encode(
|
127
|
+
@special_prefixes = v.encode(encoding)
|
107
128
|
end
|
108
129
|
|
109
130
|
def completion_case_fold=(v)
|
@@ -123,14 +144,6 @@ module Reline
|
|
123
144
|
@completion_proc = p
|
124
145
|
end
|
125
146
|
|
126
|
-
def autocompletion
|
127
|
-
@config.autocompletion
|
128
|
-
end
|
129
|
-
|
130
|
-
def autocompletion=(val)
|
131
|
-
@config.autocompletion = val
|
132
|
-
end
|
133
|
-
|
134
147
|
def output_modifier_proc=(p)
|
135
148
|
raise ArgumentError unless p.respond_to?(:call) or p.nil?
|
136
149
|
@output_modifier_proc = p
|
@@ -157,9 +170,13 @@ module Reline
|
|
157
170
|
|
158
171
|
DialogProc = Struct.new(:dialog_proc, :context)
|
159
172
|
def add_dialog_proc(name_sym, p, context = nil)
|
160
|
-
raise ArgumentError unless p.respond_to?(:call) or p.nil?
|
161
173
|
raise ArgumentError unless name_sym.instance_of?(Symbol)
|
162
|
-
|
174
|
+
if p.nil?
|
175
|
+
@dialog_proc_list.delete(name_sym)
|
176
|
+
else
|
177
|
+
raise ArgumentError unless p.respond_to?(:call)
|
178
|
+
@dialog_proc_list[name_sym] = DialogProc.new(p, context)
|
179
|
+
end
|
163
180
|
end
|
164
181
|
|
165
182
|
def dialog_proc(name_sym)
|
@@ -168,20 +185,16 @@ module Reline
|
|
168
185
|
|
169
186
|
def input=(val)
|
170
187
|
raise TypeError unless val.respond_to?(:getc) or val.nil?
|
171
|
-
if val.respond_to?(:getc)
|
172
|
-
|
173
|
-
Reline::ANSI.input = val
|
174
|
-
elsif Reline::IOGate == Reline::GeneralIO
|
175
|
-
Reline::GeneralIO.input = val
|
176
|
-
end
|
188
|
+
if val.respond_to?(:getc) && io_gate.respond_to?(:input=)
|
189
|
+
io_gate.input = val
|
177
190
|
end
|
178
191
|
end
|
179
192
|
|
180
193
|
def output=(val)
|
181
194
|
raise TypeError unless val.respond_to?(:write) or val.nil?
|
182
195
|
@output = val
|
183
|
-
if
|
184
|
-
|
196
|
+
if io_gate.respond_to?(:output=)
|
197
|
+
io_gate.output = val
|
185
198
|
end
|
186
199
|
end
|
187
200
|
|
@@ -204,7 +217,7 @@ module Reline
|
|
204
217
|
end
|
205
218
|
|
206
219
|
def get_screen_size
|
207
|
-
|
220
|
+
io_gate.get_screen_size
|
208
221
|
end
|
209
222
|
|
210
223
|
Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE = ->() {
|
@@ -243,27 +256,40 @@ module Reline
|
|
243
256
|
context.push(cursor_pos_to_render, result, pointer, dialog)
|
244
257
|
end
|
245
258
|
dialog.pointer = pointer
|
246
|
-
DialogRenderInfo.new(
|
259
|
+
DialogRenderInfo.new(
|
260
|
+
pos: cursor_pos_to_render,
|
261
|
+
contents: result,
|
262
|
+
scrollbar: true,
|
263
|
+
height: [15, preferred_dialog_height].min,
|
264
|
+
bg_color: 46,
|
265
|
+
pointer_bg_color: 45,
|
266
|
+
fg_color: 37,
|
267
|
+
pointer_fg_color: 37
|
268
|
+
)
|
247
269
|
}
|
248
270
|
Reline::DEFAULT_DIALOG_CONTEXT = Array.new
|
249
271
|
|
250
272
|
def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
273
|
+
Reline.update_iogate
|
274
|
+
io_gate.with_raw_input do
|
275
|
+
unless confirm_multiline_termination
|
276
|
+
raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
|
277
|
+
end
|
278
|
+
inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
|
255
279
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
280
|
+
whole_buffer = line_editor.whole_buffer.dup
|
281
|
+
whole_buffer.taint if RUBY_VERSION < '2.7'
|
282
|
+
if add_hist and whole_buffer and whole_buffer.chomp("\n").size > 0
|
283
|
+
Reline::HISTORY << whole_buffer
|
284
|
+
end
|
261
285
|
|
262
|
-
|
263
|
-
|
286
|
+
line_editor.reset_line if line_editor.whole_buffer.nil?
|
287
|
+
whole_buffer
|
288
|
+
end
|
264
289
|
end
|
265
290
|
|
266
291
|
def readline(prompt = '', add_hist = false)
|
292
|
+
Reline.update_iogate
|
267
293
|
inner_readline(prompt, add_hist, false)
|
268
294
|
|
269
295
|
line = line_editor.line.dup
|
@@ -278,7 +304,7 @@ module Reline
|
|
278
304
|
|
279
305
|
private def inner_readline(prompt, add_hist, multiline, &confirm_multiline_termination)
|
280
306
|
if ENV['RELINE_STDERR_TTY']
|
281
|
-
if
|
307
|
+
if io_gate.win?
|
282
308
|
$stderr = File.open(ENV['RELINE_STDERR_TTY'], 'a')
|
283
309
|
else
|
284
310
|
$stderr.reopen(ENV['RELINE_STDERR_TTY'], 'w')
|
@@ -286,10 +312,10 @@ module Reline
|
|
286
312
|
$stderr.sync = true
|
287
313
|
$stderr.puts "Reline is used by #{Process.pid}"
|
288
314
|
end
|
289
|
-
otio =
|
315
|
+
otio = io_gate.prep
|
290
316
|
|
291
317
|
may_req_ambiguous_char_width
|
292
|
-
line_editor.reset(prompt, encoding:
|
318
|
+
line_editor.reset(prompt, encoding: encoding)
|
293
319
|
if multiline
|
294
320
|
line_editor.multiline_on
|
295
321
|
if block_given?
|
@@ -313,7 +339,7 @@ module Reline
|
|
313
339
|
unless config.test_mode
|
314
340
|
config.read
|
315
341
|
config.reset_default_key_bindings
|
316
|
-
|
342
|
+
io_gate.set_default_key_bindings(config)
|
317
343
|
end
|
318
344
|
|
319
345
|
line_editor.rerender
|
@@ -322,9 +348,9 @@ module Reline
|
|
322
348
|
line_editor.set_signal_handlers
|
323
349
|
prev_pasting_state = false
|
324
350
|
loop do
|
325
|
-
prev_pasting_state =
|
351
|
+
prev_pasting_state = io_gate.in_pasting?
|
326
352
|
read_io(config.keyseq_timeout) { |inputs|
|
327
|
-
line_editor.set_pasting_state(
|
353
|
+
line_editor.set_pasting_state(io_gate.in_pasting?)
|
328
354
|
inputs.each { |c|
|
329
355
|
line_editor.input_key(c)
|
330
356
|
line_editor.rerender
|
@@ -334,29 +360,29 @@ module Reline
|
|
334
360
|
@bracketed_paste_finished = false
|
335
361
|
end
|
336
362
|
}
|
337
|
-
if prev_pasting_state == true and not
|
363
|
+
if prev_pasting_state == true and not io_gate.in_pasting? and not line_editor.finished?
|
338
364
|
line_editor.set_pasting_state(false)
|
339
365
|
prev_pasting_state = false
|
340
366
|
line_editor.rerender_all
|
341
367
|
end
|
342
368
|
break if line_editor.finished?
|
343
369
|
end
|
344
|
-
|
370
|
+
io_gate.move_cursor_column(0)
|
345
371
|
rescue Errno::EIO
|
346
372
|
# Maybe the I/O has been closed.
|
347
373
|
rescue StandardError => e
|
348
374
|
line_editor.finalize
|
349
|
-
|
375
|
+
io_gate.deprep(otio)
|
350
376
|
raise e
|
351
377
|
rescue Exception
|
352
378
|
# Including Interrupt
|
353
379
|
line_editor.finalize
|
354
|
-
|
380
|
+
io_gate.deprep(otio)
|
355
381
|
raise
|
356
382
|
end
|
357
383
|
|
358
384
|
line_editor.finalize
|
359
|
-
|
385
|
+
io_gate.deprep(otio)
|
360
386
|
end
|
361
387
|
|
362
388
|
# GNU Readline waits for "keyseq-timeout" milliseconds to see if the ESC
|
@@ -371,7 +397,7 @@ module Reline
|
|
371
397
|
private def read_io(keyseq_timeout, &block)
|
372
398
|
buffer = []
|
373
399
|
loop do
|
374
|
-
c =
|
400
|
+
c = io_gate.getc
|
375
401
|
if c == -1
|
376
402
|
result = :unmatched
|
377
403
|
@bracketed_paste_finished = true
|
@@ -411,7 +437,7 @@ module Reline
|
|
411
437
|
begin
|
412
438
|
succ_c = nil
|
413
439
|
Timeout.timeout(keyseq_timeout / 1000.0) {
|
414
|
-
succ_c =
|
440
|
+
succ_c = io_gate.getc
|
415
441
|
}
|
416
442
|
rescue Timeout::Error # cancel matching only when first byte
|
417
443
|
block.([Reline::Key.new(c, c, false)])
|
@@ -426,7 +452,7 @@ module Reline
|
|
426
452
|
end
|
427
453
|
return :break
|
428
454
|
when :matching
|
429
|
-
|
455
|
+
io_gate.ungetc(succ_c)
|
430
456
|
return :next
|
431
457
|
when :matched
|
432
458
|
buffer << succ_c
|
@@ -443,7 +469,7 @@ module Reline
|
|
443
469
|
begin
|
444
470
|
escaped_c = nil
|
445
471
|
Timeout.timeout(keyseq_timeout / 1000.0) {
|
446
|
-
escaped_c =
|
472
|
+
escaped_c = io_gate.getc
|
447
473
|
}
|
448
474
|
rescue Timeout::Error # independent ESC
|
449
475
|
block.([Reline::Key.new(c, c, false)])
|
@@ -466,19 +492,19 @@ module Reline
|
|
466
492
|
end
|
467
493
|
|
468
494
|
private def may_req_ambiguous_char_width
|
469
|
-
@ambiguous_width = 2 if
|
495
|
+
@ambiguous_width = 2 if io_gate == Reline::GeneralIO or !STDOUT.tty?
|
470
496
|
return if defined? @ambiguous_width
|
471
|
-
|
497
|
+
io_gate.move_cursor_column(0)
|
472
498
|
begin
|
473
499
|
output.write "\u{25bd}"
|
474
500
|
rescue Encoding::UndefinedConversionError
|
475
501
|
# LANG=C
|
476
502
|
@ambiguous_width = 1
|
477
503
|
else
|
478
|
-
@ambiguous_width =
|
504
|
+
@ambiguous_width = io_gate.cursor_pos.x
|
479
505
|
end
|
480
|
-
|
481
|
-
|
506
|
+
io_gate.move_cursor_column(0)
|
507
|
+
io_gate.erase_after_cursor
|
482
508
|
end
|
483
509
|
end
|
484
510
|
|
@@ -541,7 +567,7 @@ module Reline
|
|
541
567
|
@core ||= Core.new { |core|
|
542
568
|
core.config = Reline::Config.new
|
543
569
|
core.key_stroke = Reline::KeyStroke.new(core.config)
|
544
|
-
core.line_editor = Reline::LineEditor.new(core.config,
|
570
|
+
core.line_editor = Reline::LineEditor.new(core.config, core.encoding)
|
545
571
|
|
546
572
|
core.basic_word_break_characters = " \t\n`><=;|&{("
|
547
573
|
core.completer_word_break_characters = " \t\n`><=;|&{("
|
@@ -554,33 +580,42 @@ module Reline
|
|
554
580
|
end
|
555
581
|
|
556
582
|
def self.ungetc(c)
|
557
|
-
|
583
|
+
core.io_gate.ungetc(c)
|
558
584
|
end
|
559
585
|
|
560
586
|
def self.line_editor
|
561
587
|
core.line_editor
|
562
588
|
end
|
563
|
-
end
|
564
589
|
|
565
|
-
|
566
|
-
if
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
else
|
590
|
+
def self.update_iogate
|
591
|
+
return if core.config.test_mode
|
592
|
+
|
593
|
+
# Need to change IOGate when `$stdout.tty?` change from false to true by `$stdout.reopen`
|
594
|
+
# Example: rails/spring boot the application in non-tty, then run console in tty.
|
595
|
+
if ENV['TERM'] != 'dumb' && core.io_gate == Reline::GeneralIO && $stdout.tty?
|
572
596
|
require 'reline/ansi'
|
573
|
-
|
597
|
+
remove_const(:IOGate)
|
598
|
+
const_set(:IOGate, Reline::ANSI)
|
574
599
|
end
|
575
|
-
else
|
576
|
-
Reline::IOGate = Reline::Windows
|
577
600
|
end
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
601
|
+
end
|
602
|
+
|
603
|
+
require 'reline/general_io'
|
604
|
+
io = Reline::GeneralIO
|
605
|
+
unless ENV['TERM'] == 'dumb'
|
606
|
+
case RbConfig::CONFIG['host_os']
|
607
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
608
|
+
require 'reline/windows'
|
609
|
+
tty = (io = Reline::Windows).msys_tty?
|
582
610
|
else
|
583
|
-
|
611
|
+
tty = $stdout.tty?
|
584
612
|
end
|
585
613
|
end
|
614
|
+
Reline::IOGate = if tty
|
615
|
+
require 'reline/ansi'
|
616
|
+
Reline::ANSI
|
617
|
+
else
|
618
|
+
io
|
619
|
+
end
|
620
|
+
|
586
621
|
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.
|
4
|
+
version: 0.3.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aycabta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-07-08 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.
|
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.
|
75
|
+
rubygems_version: 3.4.13
|
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
|