reline 0.2.7 → 0.2.8.pre.1
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/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:
|