reline 0.3.0 → 0.3.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|