reline 0.2.7 → 0.2.8.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/reline/ansi.rb +16 -2
- data/lib/reline/line_editor.rb +317 -12
- data/lib/reline/unicode.rb +30 -0
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +17 -0
- data/lib/reline.rb +51 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c1dd7202ef6de3ebf89e2ed39c83af1e48bcd46d5f967037edc0097301b8286
|
4
|
+
data.tar.gz: 4d2047341862d83b785903974e4766b5a3d481a033208bfcc1d8891a0b116e65
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a9109bb68de735bd5dba6de8dffecfd23fecd8f09aa0b1f92d0bca6b008ef7c3d3785c16292db8ea90c9baf4d275ef382cd6d0d15487fbe6e491c74a670004bd
|
7
|
+
data.tar.gz: af8f8b99ceb34416253857caf4d87a07bde1fddf72d80ff14caeb2a5940178566bd63fc4a6884eddd60959ac3f352c056bb73bbe646915582b5b3b4d091beeed
|
data/lib/reline/ansi.rb
CHANGED
@@ -274,6 +274,22 @@ class Reline::ANSI
|
|
274
274
|
end
|
275
275
|
end
|
276
276
|
|
277
|
+
def self.hide_cursor
|
278
|
+
if Reline::Terminfo.enabled?
|
279
|
+
@@output.write Reline::Terminfo.tigetstr('civis')
|
280
|
+
else
|
281
|
+
# ignored
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def self.show_cursor
|
286
|
+
if Reline::Terminfo.enabled?
|
287
|
+
@@output.write Reline::Terminfo.tigetstr('cnorm')
|
288
|
+
else
|
289
|
+
# ignored
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
277
293
|
def self.erase_after_cursor
|
278
294
|
@@output.write "\e[K"
|
279
295
|
end
|
@@ -295,8 +311,6 @@ class Reline::ANSI
|
|
295
311
|
|
296
312
|
def self.prep
|
297
313
|
retrieve_keybuffer
|
298
|
-
int_handle = Signal.trap('INT', 'IGNORE')
|
299
|
-
Signal.trap('INT', int_handle)
|
300
314
|
nil
|
301
315
|
end
|
302
316
|
|
data/lib/reline/line_editor.rb
CHANGED
@@ -151,6 +151,7 @@ class Reline::LineEditor
|
|
151
151
|
@screen_height = @screen_size.first
|
152
152
|
reset_variables(prompt, encoding: encoding)
|
153
153
|
@old_trap = Signal.trap(:INT) {
|
154
|
+
clear_dialog
|
154
155
|
if @scroll_partial_screen
|
155
156
|
move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1)
|
156
157
|
else
|
@@ -249,6 +250,7 @@ class Reline::LineEditor
|
|
249
250
|
@drop_terminate_spaces = false
|
250
251
|
@in_pasting = false
|
251
252
|
@auto_indent_proc = nil
|
253
|
+
@dialogs = []
|
252
254
|
reset_line
|
253
255
|
end
|
254
256
|
|
@@ -414,6 +416,7 @@ class Reline::LineEditor
|
|
414
416
|
Reline::IOGate.erase_after_cursor
|
415
417
|
end
|
416
418
|
@output.flush
|
419
|
+
clear_dialog
|
417
420
|
return
|
418
421
|
end
|
419
422
|
new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
|
@@ -424,6 +427,7 @@ class Reline::LineEditor
|
|
424
427
|
else
|
425
428
|
if @just_cursor_moving and not @rerender_all
|
426
429
|
rendered = just_move_cursor
|
430
|
+
render_dialog((prompt_width + @cursor) % @screen_size.last)
|
427
431
|
@just_cursor_moving = false
|
428
432
|
return
|
429
433
|
elsif @previous_line_index or new_highest_in_this != @highest_in_this
|
@@ -446,18 +450,20 @@ class Reline::LineEditor
|
|
446
450
|
new_lines = whole_lines
|
447
451
|
end
|
448
452
|
line = modify_lines(new_lines)[@line_index]
|
453
|
+
clear_dialog
|
449
454
|
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
450
455
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
451
456
|
move_cursor_down(@highest_in_all - (@first_line_started_from + @highest_in_this - 1) - 1)
|
452
457
|
scroll_down(1)
|
453
458
|
Reline::IOGate.move_cursor_column(0)
|
454
459
|
Reline::IOGate.erase_after_cursor
|
455
|
-
|
456
|
-
|
460
|
+
else
|
461
|
+
if not rendered and not @in_pasting
|
457
462
|
line = modify_lines(whole_lines)[@line_index]
|
458
463
|
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
459
464
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
460
465
|
end
|
466
|
+
render_dialog((prompt_width + @cursor) % @screen_size.last)
|
461
467
|
end
|
462
468
|
@buffer_of_lines[@line_index] = @line
|
463
469
|
@rest_height = 0 if @scroll_partial_screen
|
@@ -472,6 +478,289 @@ class Reline::LineEditor
|
|
472
478
|
end
|
473
479
|
end
|
474
480
|
|
481
|
+
class DialogProcScope
|
482
|
+
def initialize(line_editor, proc_to_exec, context)
|
483
|
+
@line_editor = line_editor
|
484
|
+
@proc_to_exec = proc_to_exec
|
485
|
+
@context = context
|
486
|
+
@cursor_pos = Reline::CursorPos.new
|
487
|
+
end
|
488
|
+
|
489
|
+
def context
|
490
|
+
@context
|
491
|
+
end
|
492
|
+
|
493
|
+
def retrieve_completion_block(set_completion_quote_character = false)
|
494
|
+
@line_editor.retrieve_completion_block(set_completion_quote_character)
|
495
|
+
end
|
496
|
+
|
497
|
+
def call_completion_proc_with_checking_args(pre, target, post)
|
498
|
+
@line_editor.call_completion_proc_with_checking_args(pre, target, post)
|
499
|
+
end
|
500
|
+
|
501
|
+
def set_cursor_pos(col, row)
|
502
|
+
@cursor_pos.x = col
|
503
|
+
@cursor_pos.y = row
|
504
|
+
end
|
505
|
+
|
506
|
+
def cursor_pos
|
507
|
+
@cursor_pos
|
508
|
+
end
|
509
|
+
|
510
|
+
def just_cursor_moving
|
511
|
+
@line_editor.instance_variable_get(:@just_cursor_moving)
|
512
|
+
end
|
513
|
+
|
514
|
+
def screen_width
|
515
|
+
@line_editor.instance_variable_get(:@screen_size).last
|
516
|
+
end
|
517
|
+
|
518
|
+
def completion_journey_data
|
519
|
+
@line_editor.instance_variable_get(:@completion_journey_data)
|
520
|
+
end
|
521
|
+
|
522
|
+
def call
|
523
|
+
instance_exec(&@proc_to_exec)
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
class Dialog
|
528
|
+
attr_reader :name
|
529
|
+
attr_accessor :column, :vertical_offset, :contents, :lines_backup
|
530
|
+
|
531
|
+
def initialize(name, proc_scope)
|
532
|
+
@name = name
|
533
|
+
@proc_scope = proc_scope
|
534
|
+
end
|
535
|
+
|
536
|
+
def set_cursor_pos(col, row)
|
537
|
+
@proc_scope.set_cursor_pos(col, row)
|
538
|
+
end
|
539
|
+
|
540
|
+
def call
|
541
|
+
@proc_scope.call
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
def add_dialog_proc(name, p, context = nil)
|
546
|
+
return if @dialogs.any? { |d| d.name == name }
|
547
|
+
@dialogs << Dialog.new(name, DialogProcScope.new(self, p, context))
|
548
|
+
end
|
549
|
+
|
550
|
+
DIALOG_HEIGHT = 20
|
551
|
+
DIALOG_WIDTH = 40
|
552
|
+
private def render_dialog(cursor_column)
|
553
|
+
@dialogs.each do |dialog|
|
554
|
+
render_each_dialog(dialog, cursor_column)
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
private def render_each_dialog(dialog, cursor_column)
|
559
|
+
if @in_pasting
|
560
|
+
dialog.contents = nil
|
561
|
+
return
|
562
|
+
end
|
563
|
+
dialog.set_cursor_pos(cursor_column, @first_line_started_from + @started_from)
|
564
|
+
pos, result, pointer, bg = dialog.call
|
565
|
+
old_dialog_contents = dialog.contents
|
566
|
+
old_dialog_column = dialog.column
|
567
|
+
old_dialog_vertical_offset = dialog.vertical_offset
|
568
|
+
if result and not result.empty?
|
569
|
+
dialog.contents = result
|
570
|
+
dialog.contents = dialog.contents[0...DIALOG_HEIGHT] if dialog.contents.size > DIALOG_HEIGHT
|
571
|
+
else
|
572
|
+
dialog.lines_backup = {
|
573
|
+
lines: modify_lines(whole_lines),
|
574
|
+
line_index: @line_index,
|
575
|
+
first_line_started_from: @first_line_started_from,
|
576
|
+
started_from: @started_from,
|
577
|
+
byte_pointer: @byte_pointer
|
578
|
+
}
|
579
|
+
clear_each_dialog(dialog)
|
580
|
+
dialog.contents = nil
|
581
|
+
return
|
582
|
+
end
|
583
|
+
upper_space = @first_line_started_from - @started_from
|
584
|
+
lower_space = @highest_in_all - @first_line_started_from - @started_from - 1
|
585
|
+
dialog.column = pos.x
|
586
|
+
diff = (dialog.column + DIALOG_WIDTH) - (@screen_size.last - 1)
|
587
|
+
if diff > 0
|
588
|
+
dialog.column -= diff
|
589
|
+
end
|
590
|
+
if (lower_space + @rest_height) >= DIALOG_HEIGHT
|
591
|
+
dialog.vertical_offset = pos.y + 1
|
592
|
+
elsif upper_space >= DIALOG_HEIGHT
|
593
|
+
dialog.vertical_offset = pos.y + -(DIALOG_HEIGHT + 1)
|
594
|
+
else
|
595
|
+
if (lower_space + @rest_height) < DIALOG_HEIGHT
|
596
|
+
scroll_down(DIALOG_HEIGHT)
|
597
|
+
move_cursor_up(DIALOG_HEIGHT)
|
598
|
+
end
|
599
|
+
dialog.vertical_offset = pos.y + 1
|
600
|
+
end
|
601
|
+
Reline::IOGate.hide_cursor
|
602
|
+
reset_dialog(dialog, old_dialog_contents, old_dialog_column, old_dialog_vertical_offset)
|
603
|
+
move_cursor_down(dialog.vertical_offset)
|
604
|
+
Reline::IOGate.move_cursor_column(dialog.column)
|
605
|
+
dialog.contents.each_with_index do |item, i|
|
606
|
+
if i == pointer
|
607
|
+
bg_color = '45'
|
608
|
+
else
|
609
|
+
if bg
|
610
|
+
bg_color = bg
|
611
|
+
else
|
612
|
+
bg_color = '46'
|
613
|
+
end
|
614
|
+
end
|
615
|
+
@output.write "\e[#{bg_color}m%-#{DIALOG_WIDTH}s\e[49m" % item.slice(0, DIALOG_WIDTH)
|
616
|
+
Reline::IOGate.move_cursor_column(dialog.column)
|
617
|
+
move_cursor_down(1) if i < (dialog.contents.size - 1)
|
618
|
+
end
|
619
|
+
Reline::IOGate.move_cursor_column(cursor_column)
|
620
|
+
move_cursor_up(dialog.vertical_offset + dialog.contents.size - 1)
|
621
|
+
Reline::IOGate.show_cursor
|
622
|
+
dialog.lines_backup = {
|
623
|
+
lines: modify_lines(whole_lines),
|
624
|
+
line_index: @line_index,
|
625
|
+
first_line_started_from: @first_line_started_from,
|
626
|
+
started_from: @started_from,
|
627
|
+
byte_pointer: @byte_pointer
|
628
|
+
}
|
629
|
+
end
|
630
|
+
|
631
|
+
private def reset_dialog(dialog, old_dialog_contents, old_dialog_column, old_dialog_vertical_offset)
|
632
|
+
return if dialog.lines_backup.nil? or old_dialog_contents.nil?
|
633
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines], prompt)
|
634
|
+
visual_lines = []
|
635
|
+
visual_start = nil
|
636
|
+
dialog.lines_backup[:lines].each_with_index { |l, i|
|
637
|
+
pr = prompt_list ? prompt_list[i] : prompt
|
638
|
+
vl, _ = split_by_width(pr + l, @screen_size.last)
|
639
|
+
vl.compact!
|
640
|
+
if i == dialog.lines_backup[:line_index]
|
641
|
+
visual_start = visual_lines.size + dialog.lines_backup[:started_from]
|
642
|
+
end
|
643
|
+
visual_lines.concat(vl)
|
644
|
+
}
|
645
|
+
old_y = dialog.lines_backup[:first_line_started_from] + dialog.lines_backup[:started_from]
|
646
|
+
y = @first_line_started_from + @started_from
|
647
|
+
y_diff = y - old_y
|
648
|
+
if (old_y + old_dialog_vertical_offset) < (y + dialog.vertical_offset)
|
649
|
+
# rerender top
|
650
|
+
move_cursor_down(old_dialog_vertical_offset - y_diff)
|
651
|
+
start = visual_start + old_dialog_vertical_offset
|
652
|
+
line_num = dialog.vertical_offset - old_dialog_vertical_offset
|
653
|
+
line_num.times do |i|
|
654
|
+
Reline::IOGate.move_cursor_column(old_dialog_column)
|
655
|
+
if visual_lines[start + i].nil?
|
656
|
+
s = ' ' * DIALOG_WIDTH
|
657
|
+
else
|
658
|
+
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, DIALOG_WIDTH)
|
659
|
+
end
|
660
|
+
@output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % s
|
661
|
+
move_cursor_down(1) if i < (line_num - 1)
|
662
|
+
end
|
663
|
+
move_cursor_up(old_dialog_vertical_offset + line_num - 1 - y_diff)
|
664
|
+
end
|
665
|
+
if (old_y + old_dialog_vertical_offset + old_dialog_contents.size) > (y + dialog.vertical_offset + dialog.contents.size)
|
666
|
+
# rerender bottom
|
667
|
+
move_cursor_down(dialog.vertical_offset + dialog.contents.size - y_diff)
|
668
|
+
start = visual_start + dialog.vertical_offset + dialog.contents.size
|
669
|
+
line_num = (old_dialog_vertical_offset + old_dialog_contents.size) - (dialog.vertical_offset + dialog.contents.size)
|
670
|
+
line_num.times do |i|
|
671
|
+
Reline::IOGate.move_cursor_column(old_dialog_column)
|
672
|
+
if visual_lines[start + i].nil?
|
673
|
+
s = ' ' * DIALOG_WIDTH
|
674
|
+
else
|
675
|
+
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, DIALOG_WIDTH)
|
676
|
+
end
|
677
|
+
@output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % s
|
678
|
+
move_cursor_down(1) if i < (line_num - 1)
|
679
|
+
end
|
680
|
+
move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff)
|
681
|
+
end
|
682
|
+
if old_dialog_column < dialog.column
|
683
|
+
# rerender left
|
684
|
+
move_cursor_down(old_dialog_vertical_offset - y_diff)
|
685
|
+
width = dialog.column - old_dialog_column
|
686
|
+
start = visual_start + old_dialog_vertical_offset
|
687
|
+
line_num = old_dialog_contents.size
|
688
|
+
line_num.times do |i|
|
689
|
+
Reline::IOGate.move_cursor_column(old_dialog_column)
|
690
|
+
if visual_lines[start + i].nil?
|
691
|
+
s = ' ' * width
|
692
|
+
else
|
693
|
+
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, width)
|
694
|
+
end
|
695
|
+
@output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s
|
696
|
+
move_cursor_down(1) if i < (line_num - 1)
|
697
|
+
end
|
698
|
+
move_cursor_up(old_dialog_vertical_offset + line_num - 1 - y_diff)
|
699
|
+
end
|
700
|
+
if (old_dialog_column + DIALOG_WIDTH) > (dialog.column + DIALOG_WIDTH)
|
701
|
+
# rerender right
|
702
|
+
move_cursor_down(old_dialog_vertical_offset + y_diff)
|
703
|
+
width = (old_dialog_column + DIALOG_WIDTH) - (dialog.column + DIALOG_WIDTH)
|
704
|
+
start = visual_start + old_dialog_vertical_offset
|
705
|
+
line_num = old_dialog_contents.size
|
706
|
+
line_num.times do |i|
|
707
|
+
Reline::IOGate.move_cursor_column(old_dialog_column + DIALOG_WIDTH)
|
708
|
+
if visual_lines[start + i].nil?
|
709
|
+
s = ' ' * width
|
710
|
+
else
|
711
|
+
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column + DIALOG_WIDTH, width)
|
712
|
+
end
|
713
|
+
Reline::IOGate.move_cursor_column(dialog.column + DIALOG_WIDTH)
|
714
|
+
@output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s
|
715
|
+
move_cursor_down(1) if i < (line_num - 1)
|
716
|
+
end
|
717
|
+
move_cursor_up(old_dialog_vertical_offset + line_num - 1 + y_diff)
|
718
|
+
end
|
719
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
720
|
+
end
|
721
|
+
|
722
|
+
private def clear_dialog
|
723
|
+
@dialogs.each do |dialog|
|
724
|
+
clear_each_dialog(dialog)
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
private def clear_each_dialog(dialog)
|
729
|
+
return unless dialog.contents
|
730
|
+
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines], prompt)
|
731
|
+
visual_lines = []
|
732
|
+
visual_lines_under_dialog = []
|
733
|
+
visual_start = nil
|
734
|
+
dialog.lines_backup[:lines].each_with_index { |l, i|
|
735
|
+
pr = prompt_list ? prompt_list[i] : prompt
|
736
|
+
vl, _ = split_by_width(pr + l, @screen_size.last)
|
737
|
+
vl.compact!
|
738
|
+
if i == dialog.lines_backup[:line_index]
|
739
|
+
visual_start = visual_lines.size + dialog.lines_backup[:started_from] + dialog.vertical_offset
|
740
|
+
end
|
741
|
+
visual_lines.concat(vl)
|
742
|
+
}
|
743
|
+
visual_lines_under_dialog = visual_lines[visual_start, dialog.contents.size]
|
744
|
+
visual_lines_under_dialog = [] if visual_lines_under_dialog.nil?
|
745
|
+
Reline::IOGate.hide_cursor
|
746
|
+
move_cursor_down(dialog.vertical_offset)
|
747
|
+
dialog_vertical_size = dialog.contents.size
|
748
|
+
dialog_vertical_size.times do |i|
|
749
|
+
if i < visual_lines_under_dialog.size
|
750
|
+
Reline::IOGate.move_cursor_column(0)
|
751
|
+
@output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % visual_lines_under_dialog[i]
|
752
|
+
else
|
753
|
+
Reline::IOGate.move_cursor_column(dialog.column)
|
754
|
+
@output.write "\e[39m\e[49m#{' ' * DIALOG_WIDTH}\e[39m\e[49m"
|
755
|
+
end
|
756
|
+
Reline::IOGate.erase_after_cursor
|
757
|
+
move_cursor_down(1) if i < (dialog_vertical_size - 1)
|
758
|
+
end
|
759
|
+
move_cursor_up(dialog_vertical_size - 1 + dialog.vertical_offset)
|
760
|
+
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
761
|
+
Reline::IOGate.show_cursor
|
762
|
+
end
|
763
|
+
|
475
764
|
private def calculate_scroll_partial_screen(highest_in_all, cursor_y)
|
476
765
|
if @screen_height < highest_in_all
|
477
766
|
old_scroll_partial_screen = @scroll_partial_screen
|
@@ -932,6 +1221,16 @@ class Reline::LineEditor
|
|
932
1221
|
@completion_journey_data = CompletionJourneyData.new(
|
933
1222
|
preposing, postposing,
|
934
1223
|
[target] + list.select{ |item| item.start_with?(target) }, 0)
|
1224
|
+
if @completion_journey_data.list.size == 1
|
1225
|
+
@completion_journey_data.pointer = 0
|
1226
|
+
else
|
1227
|
+
case direction
|
1228
|
+
when :up
|
1229
|
+
@completion_journey_data.pointer = @completion_journey_data.list.size - 1
|
1230
|
+
when :down
|
1231
|
+
@completion_journey_data.pointer = 1
|
1232
|
+
end
|
1233
|
+
end
|
935
1234
|
@completion_state = CompletionState::JOURNEY
|
936
1235
|
else
|
937
1236
|
case direction
|
@@ -946,13 +1245,13 @@ class Reline::LineEditor
|
|
946
1245
|
@completion_journey_data.pointer = 0
|
947
1246
|
end
|
948
1247
|
end
|
949
|
-
completed = @completion_journey_data.list[@completion_journey_data.pointer]
|
950
|
-
@line = @completion_journey_data.preposing + completed + @completion_journey_data.postposing
|
951
|
-
line_to_pointer = @completion_journey_data.preposing + completed
|
952
|
-
@cursor_max = calculate_width(@line)
|
953
|
-
@cursor = calculate_width(line_to_pointer)
|
954
|
-
@byte_pointer = line_to_pointer.bytesize
|
955
1248
|
end
|
1249
|
+
completed = @completion_journey_data.list[@completion_journey_data.pointer]
|
1250
|
+
@line = @completion_journey_data.preposing + completed + @completion_journey_data.postposing
|
1251
|
+
line_to_pointer = @completion_journey_data.preposing + completed
|
1252
|
+
@cursor_max = calculate_width(@line)
|
1253
|
+
@cursor = calculate_width(line_to_pointer)
|
1254
|
+
@byte_pointer = line_to_pointer.bytesize
|
956
1255
|
end
|
957
1256
|
|
958
1257
|
private def run_for_operators(key, method_symbol, &block)
|
@@ -1146,6 +1445,7 @@ class Reline::LineEditor
|
|
1146
1445
|
end
|
1147
1446
|
unless completion_occurs
|
1148
1447
|
@completion_state = CompletionState::NORMAL
|
1448
|
+
@completion_journey_data = nil
|
1149
1449
|
end
|
1150
1450
|
if not @in_pasting and @just_cursor_moving.nil?
|
1151
1451
|
if @previous_line_index and @buffer_of_lines[@previous_line_index] == @line
|
@@ -1165,7 +1465,13 @@ class Reline::LineEditor
|
|
1165
1465
|
|
1166
1466
|
def call_completion_proc
|
1167
1467
|
result = retrieve_completion_block(true)
|
1168
|
-
|
1468
|
+
pre, target, post = result
|
1469
|
+
result = call_completion_proc_with_checking_args(pre, target, post)
|
1470
|
+
Reline.core.instance_variable_set(:@completion_quote_character, nil)
|
1471
|
+
result
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
def call_completion_proc_with_checking_args(pre, target, post)
|
1169
1475
|
if @completion_proc and target
|
1170
1476
|
argnum = @completion_proc.parameters.inject(0) { |result, item|
|
1171
1477
|
case item.first
|
@@ -1179,12 +1485,11 @@ class Reline::LineEditor
|
|
1179
1485
|
when 1
|
1180
1486
|
result = @completion_proc.(target)
|
1181
1487
|
when 2
|
1182
|
-
result = @completion_proc.(target,
|
1488
|
+
result = @completion_proc.(target, pre)
|
1183
1489
|
when 3..Float::INFINITY
|
1184
|
-
result = @completion_proc.(target,
|
1490
|
+
result = @completion_proc.(target, pre, post)
|
1185
1491
|
end
|
1186
1492
|
end
|
1187
|
-
Reline.core.instance_variable_set(:@completion_quote_character, nil)
|
1188
1493
|
result
|
1189
1494
|
end
|
1190
1495
|
|
data/lib/reline/unicode.rb
CHANGED
@@ -185,6 +185,36 @@ class Reline::Unicode
|
|
185
185
|
[lines, height]
|
186
186
|
end
|
187
187
|
|
188
|
+
# Take a chunk of a String with escape sequences.
|
189
|
+
def self.take_range(str, col, length, encoding = str.encoding)
|
190
|
+
chunk = String.new(encoding: encoding)
|
191
|
+
width = 0
|
192
|
+
rest = str.encode(Encoding::UTF_8)
|
193
|
+
in_zero_width = false
|
194
|
+
rest.scan(WIDTH_SCANNER) do |gc|
|
195
|
+
case
|
196
|
+
when gc[NON_PRINTING_START_INDEX]
|
197
|
+
in_zero_width = true
|
198
|
+
when gc[NON_PRINTING_END_INDEX]
|
199
|
+
in_zero_width = false
|
200
|
+
when gc[CSI_REGEXP_INDEX]
|
201
|
+
chunk << gc[CSI_REGEXP_INDEX]
|
202
|
+
when gc[OSC_REGEXP_INDEX]
|
203
|
+
chunk << gc[OSC_REGEXP_INDEX]
|
204
|
+
when gc[GRAPHEME_CLUSTER_INDEX]
|
205
|
+
gc = gc[GRAPHEME_CLUSTER_INDEX]
|
206
|
+
if in_zero_width
|
207
|
+
chunk << gc
|
208
|
+
else
|
209
|
+
width = get_mbchar_width(gc)
|
210
|
+
break if (width + length) <= col
|
211
|
+
chunk << gc if col <= width
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
chunk
|
216
|
+
end
|
217
|
+
|
188
218
|
def self.get_next_mbchar_size(line, byte_pointer)
|
189
219
|
grapheme = line.byteslice(byte_pointer..-1).grapheme_clusters.first
|
190
220
|
grapheme ? grapheme.bytesize : 0
|
data/lib/reline/version.rb
CHANGED
data/lib/reline/windows.rb
CHANGED
@@ -133,9 +133,11 @@ class Reline::Windows
|
|
133
133
|
@@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
|
134
134
|
@@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
135
135
|
@@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
|
136
|
+
@@SetConsoleCursorInfo = Win32API.new('kernel32', 'SetConsoleCursorInfo', ['L', 'P'], 'L')
|
136
137
|
|
137
138
|
@@GetConsoleMode = Win32API.new('kernel32', 'GetConsoleMode', ['L', 'P'], 'L')
|
138
139
|
@@SetConsoleMode = Win32API.new('kernel32', 'SetConsoleMode', ['L', 'L'], 'L')
|
140
|
+
@@WaitForSingleObject = Win32API.new('kernel32', 'WaitForSingleObject', ['L', 'L'], 'L')
|
139
141
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
140
142
|
|
141
143
|
private_class_method def self.getconsolemode
|
@@ -218,6 +220,7 @@ class Reline::Windows
|
|
218
220
|
def self.check_input_event
|
219
221
|
num_of_events = 0.chr * 8
|
220
222
|
while @@output_buf.empty? #or true
|
223
|
+
next if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
|
221
224
|
next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack('L').first == 0
|
222
225
|
input_record = 0.chr * 18
|
223
226
|
read_event = 0.chr * 4
|
@@ -341,6 +344,20 @@ class Reline::Windows
|
|
341
344
|
raise NotImplementedError
|
342
345
|
end
|
343
346
|
|
347
|
+
def self.hide_cursor
|
348
|
+
size = 100
|
349
|
+
visible = 0 # 0 means false
|
350
|
+
cursor_info = [size, visible].pack('Li')
|
351
|
+
@@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
|
352
|
+
end
|
353
|
+
|
354
|
+
def self.show_cursor
|
355
|
+
size = 100
|
356
|
+
visible = 1 # 1 means true
|
357
|
+
cursor_info = [size, visible].pack('Li')
|
358
|
+
@@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
|
359
|
+
end
|
360
|
+
|
344
361
|
def self.set_winch_handler(&handler)
|
345
362
|
@@winch_handler = handler
|
346
363
|
end
|
data/lib/reline.rb
CHANGED
@@ -44,6 +44,7 @@ module Reline
|
|
44
44
|
|
45
45
|
def initialize
|
46
46
|
self.output = STDOUT
|
47
|
+
@dialog_proc_list = []
|
47
48
|
yield self
|
48
49
|
@completion_quote_character = nil
|
49
50
|
@bracketed_paste_finished = false
|
@@ -130,6 +131,12 @@ module Reline
|
|
130
131
|
@dig_perfect_match_proc = p
|
131
132
|
end
|
132
133
|
|
134
|
+
def add_dialog_proc(name_sym, p, context = nil)
|
135
|
+
raise ArgumentError unless p.respond_to?(:call) or p.nil?
|
136
|
+
raise ArgumentError unless name_sym.instance_of?(Symbol)
|
137
|
+
@dialog_proc_list << [name_sym, p, context]
|
138
|
+
end
|
139
|
+
|
133
140
|
def input=(val)
|
134
141
|
raise TypeError unless val.respond_to?(:getc) or val.nil?
|
135
142
|
if val.respond_to?(:getc)
|
@@ -171,6 +178,44 @@ module Reline
|
|
171
178
|
Reline::IOGate.get_screen_size
|
172
179
|
end
|
173
180
|
|
181
|
+
Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE = ->() {
|
182
|
+
# autocomplete
|
183
|
+
if just_cursor_moving and completion_journey_data.nil?
|
184
|
+
# Auto complete starts only when edited
|
185
|
+
return nil
|
186
|
+
end
|
187
|
+
pre, target, post= retrieve_completion_block(true)
|
188
|
+
if target.nil? or target.empty?# or target.size <= 3
|
189
|
+
return nil
|
190
|
+
end
|
191
|
+
if completion_journey_data and completion_journey_data.list
|
192
|
+
result = completion_journey_data.list.dup
|
193
|
+
result.shift
|
194
|
+
pointer = completion_journey_data.pointer - 1
|
195
|
+
else
|
196
|
+
result = call_completion_proc_with_checking_args(pre, target, post)
|
197
|
+
pointer = nil
|
198
|
+
end
|
199
|
+
if result and result.size == 1 and result[0] == target
|
200
|
+
result = nil
|
201
|
+
end
|
202
|
+
target_width = Reline::Unicode.calculate_width(target)
|
203
|
+
x = cursor_pos.x - target_width
|
204
|
+
if x < 0
|
205
|
+
x = screen_width + x
|
206
|
+
y = -1
|
207
|
+
else
|
208
|
+
y = 0
|
209
|
+
end
|
210
|
+
cursor_pos_to_render = Reline::CursorPos.new(x, y)
|
211
|
+
if context and context.is_a?(Array)
|
212
|
+
context.clear
|
213
|
+
context.push(cursor_pos_to_render, result, pointer)
|
214
|
+
end
|
215
|
+
[cursor_pos_to_render, result, pointer, nil]
|
216
|
+
}
|
217
|
+
Reline::DEFAULT_DIALOG_CONTEXT = Array.new
|
218
|
+
|
174
219
|
def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
|
175
220
|
unless confirm_multiline_termination
|
176
221
|
raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
|
@@ -230,6 +275,10 @@ module Reline
|
|
230
275
|
line_editor.auto_indent_proc = auto_indent_proc
|
231
276
|
line_editor.dig_perfect_match_proc = dig_perfect_match_proc
|
232
277
|
line_editor.pre_input_hook = pre_input_hook
|
278
|
+
@dialog_proc_list.each do |d|
|
279
|
+
name_sym, dialog_proc, context = d
|
280
|
+
line_editor.add_dialog_proc(name_sym, dialog_proc, context)
|
281
|
+
end
|
233
282
|
|
234
283
|
unless config.test_mode
|
235
284
|
config.read
|
@@ -424,6 +473,7 @@ module Reline
|
|
424
473
|
def_single_delegators :core, :ambiguous_width
|
425
474
|
def_single_delegators :core, :last_incremental_search
|
426
475
|
def_single_delegators :core, :last_incremental_search=
|
476
|
+
def_single_delegators :core, :add_dialog_proc
|
427
477
|
|
428
478
|
def_single_delegators :core, :readmultiline
|
429
479
|
def_instance_delegators self, :readmultiline
|
@@ -445,6 +495,7 @@ module Reline
|
|
445
495
|
core.completer_quote_characters = '"\''
|
446
496
|
core.filename_quote_characters = ""
|
447
497
|
core.special_prefixes = ""
|
498
|
+
core.add_dialog_proc(:autocomplete, Reline::DEFAULT_DIALOG_PROC_AUTOCOMPLETE, Reline::DEFAULT_DIALOG_CONTEXT)
|
448
499
|
}
|
449
500
|
end
|
450
501
|
|
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.2.
|
4
|
+
version: 0.2.8.pre.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aycabta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-08-
|
11
|
+
date: 2021-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: io-console
|
@@ -69,9 +69,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
69
|
version: '2.5'
|
70
70
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
|
-
- - "
|
72
|
+
- - ">"
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version:
|
74
|
+
version: 1.3.1
|
75
75
|
requirements: []
|
76
76
|
rubygems_version: 3.2.22
|
77
77
|
signing_key:
|