36
37:- module(editline,
38 [ el_wrap/0, 39 el_wrap/1, 40 el_wrap/4, 41 el_wrap/5, 42 el_wrapped/1, 43 el_unwrap/1, 44
45 el_source/2, 46 el_bind/2, 47 el_set/2, 48 el_get/2, 49 el_addfn/4, 50 el_cursor/2, 51 el_line/2, 52 el_insertstr/2, 53 el_deletestr/2, 54
55 el_history/2, 56 el_history_events/2, 57 el_add_history/2, 58 el_write_history/2, 59 el_read_history/2, 60
61 el_version/1 62 ]). 63:- autoload(library(apply),[maplist/2,maplist/3]). 64:- autoload(library(lists),[reverse/2,max_list/2,append/3,member/2]). 65:- autoload(library(solution_sequences),[call_nth/2]). 66:- autoload(library(option), [merge_options/3]). 67
68:- use_foreign_library(foreign(libedit4pl)). 69
70:- initialization el_wrap_if_ok. 71
72:- meta_predicate
73 el_addfn(+,+,+,3). 74
75:- multifile
76 el_setup/1, 77 el_wcwidth/2, 78 prolog:complete_input/4. 79
80
88
89el_wrap_if_ok :-
90 \+ current_prolog_flag(readline, readline),
91 stream_property(user_input, tty(true)),
92 !,
93 el_wrap.
94el_wrap_if_ok.
95
119
120el_wrap :-
121 el_wrap([]).
122
123el_wrap(_) :-
124 el_wrapped(user_input),
125 !.
126el_wrap(Options) :-
127 stream_property(user_input, tty(true)), !,
128 findall(Opt, el_default(Opt), Defaults),
129 merge_options(Options, Defaults, Options1),
130 el_wrap(swipl, user_input, user_output, user_error, Options1),
131 add_prolog_commands(user_input),
132 ignore(el_set(user_input, wordchars("_"))),
133 forall(el_setup(user_input), true),
134 enable_bracketed_paste(user_input),
135 enable_word_motion(user_input),
136 enable_windows_eof(user_input).
137el_wrap(_).
138
139el_default(history(Size)) :-
140 current_prolog_flag(history, Value),
141 ( integer(Value),
142 Value >= 0
143 -> Size = Value
144 ; Value == false
145 -> Size = 0
146 ).
147:- if(current_predicate(prolog_alert_signal/2)). 148el_default(alert_signo(SigNo)) :-
149 prolog_alert_signal(SigName, SigName),
150 current_signal(SigName, SigNo, _Handler).
151:- endif. 152
153add_prolog_commands(Input) :-
154 el_addfn(Input, complete, 'Complete atoms and files', complete),
155 el_addfn(Input, show_completions, 'List completions', show_completions),
156 el_addfn(Input, electric, 'Indicate matching bracket', electric),
157 el_addfn(Input, isearch_history, 'Incremental search in history',
158 isearch_history),
159 el_addfn(Input, bracketed_paste, 'Handle bracketed paste', bracketed_paste),
160 el_bind(Input, ["^I", complete]),
161 el_bind(Input, ["^[?", show_completions]),
162 el_bind(Input, ["^R", isearch_history]),
163 bind_electric(Input),
164 add_paste_quoted(Input),
165 el_source(Input, _).
166
175
176enable_bracketed_paste(Input) :-
177 ( el_get(Input, editor(vi))
178 -> el_bind(Input, ['-r', "\e[200~"]),
179 el_set(Input, bracketed_paste(false))
180 ; el_bind(Input, ["\e[200~", bracketed_paste]),
181 el_set(Input, bracketed_paste(true))
182 ).
183
196
197enable_word_motion(Input) :-
198 el_bind(Input, ["\e[1;5D", 'ed-prev-word']),
199 el_bind(Input, ["\e[1;5C", 'em-next-word']),
200 ( el_get(Input, editor(vi))
201 -> el_bind(Input, ['-a', "\e[1;5D", 'vi-prev-word']),
202 el_bind(Input, ['-a', "\e[1;5C", 'vi-next-word'])
203 ; true
204 ).
205
212
213enable_windows_eof(Input) :-
214 current_prolog_flag(windows, true),
215 !,
216 ( el_get(Input, editor(vi))
217 -> el_bind(Input, ["^Z", 'vi-list-or-eof'])
218 ; el_bind(Input, ["^Z", 'em-delete-or-list'])
219 ).
220enable_windows_eof(_).
221
234
235el_wrap(ProgName, In, Out, Error) :-
236 el_wrap(ProgName, In, Out, Error, []).
237
244
248
256
261
262
279
309
330
343
349
354
358
362
399
405
409
415
422
429
434
435:- multifile
436 prolog:history/2. 437
438prolog:history(Input, enabled) :-
439 !,
440 el_wrapped(Input),
441 el_history(Input, getsize(Size)),
442 Size > 0.
443prolog:history(Input, add(Line)) :-
444 !,
445 el_add_history(Input, Line).
446prolog:history(Input, load(File)) :-
447 !,
448 compat_read_history(Input, File).
449prolog:history(Input, save(File)) :-
450 !,
451 el_write_history(Input, File).
452prolog:history(Input, events(Events)) :-
453 !,
454 el_history_events(Input, Events).
455prolog:history(Input, Command) :-
456 public_command(Command),
457 !,
458 el_history(Input, Command).
459
460public_command(first(_Num, _String)).
461public_command(curr(_Num, _String)).
462public_command(event(_Num, _String)).
463public_command(prev_str(_Search, _Num, _String)).
464public_command(clear).
465
470
471compat_read_history(Input, File) :-
472 catch(el_read_history(Input, File), error(editline(_),_), fail),
473 !.
474compat_read_history(Input, File) :-
475 access_file(File, read),
476 setup_call_cleanup(
477 open(File, read, In, [encoding(utf8)]),
478 read_old_history(Input, In),
479 close(In)),
480 !.
481compat_read_history(_, _).
482
483read_old_history(Input, From) :-
484 catch('$raw_read'(From, Line), error(_,_), fail),
485 ( Line == end_of_file
486 -> true
487 ; string_concat(Line, '.', Event),
488 el_add_history(Input, Event),
489 read_old_history(Input, From)
490 ).
491
492 495
499
500bind_electric(Input) :-
501 forall(bracket(_Open, Close), bind_code(Input, Close, electric)),
502 forall(quote(Close), bind_code(Input, Close, electric)).
503
504bind_code(Input, Code, Command) :-
505 string_codes(Key, [Code]),
506 el_bind(Input, [Key, Command]).
507
508
510
511electric(Input, Char, Continue) :-
512 string_codes(Str, [Char]),
513 el_insertstr(Input, Str),
514 el_line(Input, line(Before, _)),
515 ( string_codes(Before, Codes),
516 nesting(Codes, 0, Nesting),
517 reverse(Nesting, [Close|RevNesting])
518 -> ( Close = open(_,_) 519 -> Continue = refresh
520 ; matching_open(RevNesting, Close, _, Index)
521 -> string_length(Before, Len), 522 Move is Index-Len,
523 Continue = electric(Move, 500, refresh)
524 ; Continue = refresh_beep 525 )
526 ; Continue = refresh_beep
527 ).
528
529matching_open_index(String, Index) :-
530 string_codes(String, Codes),
531 nesting(Codes, 0, Nesting),
532 reverse(Nesting, [Close|RevNesting]),
533 matching_open(RevNesting, Close, _, Index).
534
535matching_open([Open|Rest], Close, Rest, Index) :-
536 Open = open(Index,_),
537 match(Open, Close),
538 !.
539matching_open([Close1|Rest1], Close, Rest, Index) :-
540 Close1 = close(_,_),
541 matching_open(Rest1, Close1, Rest2, _),
542 matching_open(Rest2, Close, Rest, Index).
543
544match(open(_,Open),close(_,Close)) :-
545 ( bracket(Open, Close)
546 -> true
547 ; Open == Close,
548 quote(Open)
549 ).
550
551bracket(0'(, 0')).
552bracket(0'[, 0']).
553bracket(0'{, 0'}).
554
555quote(0'\').
556quote(0'\").
557quote(0'\`).
558
559nesting([], _, []).
560nesting([H|T], I, Nesting) :-
561 ( bracket(H, _Close)
562 -> Nesting = [open(I,H)|Nest]
563 ; bracket(_Open, H)
564 -> Nesting = [close(I,H)|Nest]
565 ),
566 !,
567 I2 is I+1,
568 nesting(T, I2, Nest).
569nesting([0'0, 0'\'|T], I, Nesting) :-
570 !,
571 phrase(skip_code, T, T1),
572 difflist_length(T, T1, Len),
573 I2 is I+Len+2,
574 nesting(T1, I2, Nesting).
575nesting([H|T], I, Nesting) :-
576 quote(H),
577 !,
578 ( phrase(skip_quoted(H), T, T1)
579 -> difflist_length(T, T1, Len),
580 I2 is I+Len+1,
581 Nesting = [open(I,H),close(I2,H)|Nest],
582 nesting(T1, I2, Nest)
583 ; Nesting = [open(I,H)] 584 ).
585nesting([_|T], I, Nesting) :-
586 I2 is I+1,
587 nesting(T, I2, Nesting).
588
589difflist_length(List, Tail, Len) :-
590 difflist_length(List, Tail, 0, Len).
591
592difflist_length(List, Tail, Len0, Len) :-
593 List == Tail,
594 !,
595 Len = Len0.
596difflist_length([_|List], Tail, Len0, Len) :-
597 Len1 is Len0+1,
598 difflist_length(List, Tail, Len1, Len).
599
600skip_quoted(H) -->
601 [H],
602 !.
603skip_quoted(H) -->
604 "\\", [H],
605 !,
606 skip_quoted(H).
607skip_quoted(H) -->
608 [_],
609 skip_quoted(H).
610
611skip_code -->
612 "\\", [_],
613 !.
614skip_code -->
615 [_].
616
617
618 621
629
630
631:- dynamic
632 last_complete/2. 633
634complete(Input, _Char, Continue) :-
635 el_line(Input, line(Before, After)),
636 ensure_input_completion,
637 prolog:complete_input(Before, After, Delete, Completions),
638 ( Completions = [One]
639 -> string_length(Delete, Len),
640 el_deletestr(Input, Len),
641 complete_text(One, Text),
642 el_insertstr(Input, Text),
643 Continue = refresh
644 ; Completions == []
645 -> Continue = refresh_beep
646 ; get_time(Now),
647 retract(last_complete(TLast, Before)),
648 Now - TLast < 2
649 -> nl(user_error),
650 list_alternatives(Completions),
651 Continue = redisplay
652 ; retractall(last_complete(_,_)),
653 get_time(Now),
654 asserta(last_complete(Now, Before)),
655 common_competion(Completions, Extend),
656 ( Delete == Extend
657 -> Continue = refresh_beep
658 ; string_length(Delete, Len),
659 el_deletestr(Input, Len),
660 el_insertstr(Input, Extend),
661 Continue = refresh
662 )
663 ).
664
665:- dynamic
666 input_completion_loaded/0. 667
668ensure_input_completion :-
669 input_completion_loaded,
670 !.
671ensure_input_completion :-
672 predicate_property(prolog:complete_input(_,_,_,_),
673 number_of_clauses(N)),
674 N > 0,
675 !.
676ensure_input_completion :-
677 exists_source(library(console_input)),
678 !,
679 use_module(library(console_input), []),
680 asserta(input_completion_loaded).
681ensure_input_completion.
682
683
687
688show_completions(Input, _Char, Continue) :-
689 el_line(Input, line(Before, After)),
690 prolog:complete_input(Before, After, _Delete, Completions),
691 nl(user_error),
692 list_alternatives(Completions),
693 Continue = redisplay.
694
695complete_text(Text-_Comment, Text) :- !.
696complete_text(Text, Text).
697
701
702common_competion(Alternatives, Common) :-
703 maplist(atomic, Alternatives),
704 !,
705 common_prefix(Alternatives, Common).
706common_competion(Alternatives, Common) :-
707 maplist(complete_text, Alternatives, AltText),
708 !,
709 common_prefix(AltText, Common).
710
714
715common_prefix([A1|T], Common) :-
716 common_prefix_(T, A1, Common).
717
718common_prefix_([], Common, Common).
719common_prefix_([H|T], Common0, Common) :-
720 common_prefix(H, Common0, Common1),
721 common_prefix_(T, Common1, Common).
722
726
727common_prefix(A1, A2, Prefix) :-
728 sub_atom(A1, 0, _, _, A2),
729 !,
730 Prefix = A2.
731common_prefix(A1, A2, Prefix) :-
732 sub_atom(A2, 0, _, _, A1),
733 !,
734 Prefix = A1.
735common_prefix(A1, A2, Prefix) :-
736 atom_codes(A1, C1),
737 atom_codes(A2, C2),
738 list_common_prefix(C1, C2, C),
739 string_codes(Prefix, C).
740
741list_common_prefix([H|T0], [H|T1], [H|T]) :-
742 !,
743 list_common_prefix(T0, T1, T).
744list_common_prefix(_, _, []).
745
746
747
753
754list_alternatives(Alternatives) :-
755 maplist(atomic, Alternatives),
756 !,
757 length(Alternatives, Count),
758 maplist(atom_length, Alternatives, Lengths),
759 max_list(Lengths, Max),
760 tty_size(_, Cols),
761 ColW is Max+2,
762 Columns is max(1, Cols // ColW),
763 RowCount is (Count+Columns-1)//Columns,
764 length(Rows, RowCount),
765 to_matrix(Alternatives, Rows, Rows),
766 ( RowCount > 11
767 -> length(First, 10),
768 Skipped is RowCount - 10,
769 append(First, _, Rows),
770 maplist(write_row(ColW), First),
771 format(user_error, '... skipped ~D rows~n', [Skipped])
772 ; maplist(write_row(ColW), Rows)
773 ).
774list_alternatives(Alternatives) :-
775 maplist(complete_text, Alternatives, AltText),
776 list_alternatives(AltText).
777
778to_matrix([], _, Rows) :-
779 !,
780 maplist(close_list, Rows).
781to_matrix([H|T], [RH|RT], Rows) :-
782 !,
783 add_list(RH, H),
784 to_matrix(T, RT, Rows).
785to_matrix(List, [], Rows) :-
786 to_matrix(List, Rows, Rows).
787
788add_list(Var, Elem) :-
789 var(Var), !,
790 Var = [Elem|_].
791add_list([_|T], Elem) :-
792 add_list(T, Elem).
793
794close_list(List) :-
795 append(List, [], _),
796 !.
797
798write_row(ColW, Row) :-
799 length(Row, Columns),
800 make_format(Columns, ColW, Format),
801 format(user_error, Format, Row).
802
803make_format(N, ColW, Format) :-
804 format(string(PerCol), '~~w~~t~~~d+', [ColW]),
805 Front is N - 1,
806 length(LF, Front),
807 maplist(=(PerCol), LF),
808 append(LF, ['~w~n'], Parts),
809 atomics_to_string(Parts, Format).
810
811
812 815
820
821isearch_history(Input, _Char, Continue) :-
822 el_line(Input, line(Before, After)),
823 string_concat(Before, After, Current),
824 string_length(Current, Len),
825 search_print('', "", Current),
826 search(Input, "", Current, 1, Line),
827 el_deletestr(Input, Len),
828 el_insertstr(Input, Line),
829 Continue = redisplay.
830
831search(Input, For, Current, Nth, Line) :-
832 el_getc(Input, Next),
833 Next \== -1,
834 !,
835 search(Next, Input, For, Current, Nth, Line).
836search(_Input, _For, _Current, _Nth, "").
837
838search(7, _Input, _, Current, _, Current) :- 839 !,
840 clear_line.
841search(18, Input, For, Current, Nth, Line) :- 842 !,
843 N2 is Nth+1,
844 search_(Input, For, Current, N2, Line).
845search(19, Input, For, Current, Nth, Line) :- 846 !,
847 N2 is max(1,Nth-1),
848 search_(Input, For, Current, N2, Line).
849search(127, Input, For, Current, _Nth, Line) :- 850 sub_string(For, 0, _, 1, For1),
851 !,
852 search_(Input, For1, Current, 1, Line).
853search(Char, Input, For, Current, Nth, Line) :-
854 code_type(Char, cntrl),
855 !,
856 search_end(Input, For, Current, Nth, Line),
857 el_push(Input, Char).
858search(Char, Input, For, Current, _Nth, Line) :-
859 format(string(For1), '~w~c', [For,Char]),
860 search_(Input, For1, Current, 1, Line).
861
862search_(Input, For1, Current, Nth, Line) :-
863 ( find_in_history(Input, For1, Current, Nth, Candidate)
864 -> search_print('', For1, Candidate)
865 ; search_print('failed ', For1, Current)
866 ),
867 search(Input, For1, Current, Nth, Line).
868
869search_end(Input, For, Current, Nth, Line) :-
870 ( find_in_history(Input, For, Current, Nth, Line)
871 -> true
872 ; Line = Current
873 ),
874 clear_line.
875
876find_in_history(_, "", Current, _, Current) :-
877 !.
878find_in_history(Input, For, _, Nth, Line) :-
879 el_history_events(Input, History),
880 call_nth(( member(_N-Line, History),
881 sub_string(Line, _, _, _, For)
882 ),
883 Nth),
884 !.
885
886search_print(State, Search, Current) :-
887 format(user_error, '\r(~wreverse-i-search)`~w\': ~w\e[0K',
888 [State, Search, Current]).
889
890clear_line :-
891 format(user_error, '\r\e[0K', []).
892
893
894 897
898:- meta_predicate
899 with_quote_flags(+,+,0). 900
901add_paste_quoted(Input) :-
902 current_prolog_flag(gui, true),
903 !,
904 el_addfn(Input, paste_quoted, 'Paste as quoted atom', paste_quoted),
905 el_bind(Input, ["^Y", paste_quoted]).
906add_paste_quoted(_).
907
913
914paste_quoted(Input, _Char, Continue) :-
915 clipboard_content(String),
916 quote_text(Input, String, Quoted),
917 el_insertstr(Input, Quoted),
918 Continue = refresh.
919
920quote_text(Input, String, Value) :-
921 el_line(Input, line(Before, _After)),
922 ( sub_string(Before, _, 1, 0, Quote)
923 -> true
924 ; Quote = "'"
925 ),
926 quote_text(Input, Quote, String, Value).
927
928quote_text(Input, "'", Text, Quoted) =>
929 format(string(Quoted), '~q', [Text]),
930 el_deletestr(Input, 1).
931quote_text(Input, "\"", Text, Quoted) =>
932 atom_string(Text, String),
933 with_quote_flags(
934 string, codes,
935 format(string(Quoted), '~q', [String])),
936 el_deletestr(Input, 1).
937quote_text(Input, "`", Text, Quoted) =>
938 atom_string(Text, String),
939 with_quote_flags(
940 codes, string,
941 format(string(Quoted), '~q', [String])),
942 el_deletestr(Input, 1).
943quote_text(_, _, Text, Quoted) =>
944 format(string(Quoted), '~q', [Text]).
945
946with_quote_flags(Double, Back, Goal) :-
947 setup_call_cleanup(
948 ( push_prolog_flag(double_quotes, Double),
949 push_prolog_flag(back_quotes, Back) ),
950 Goal,
951 ( pop_prolog_flag(back_quotes),
952 pop_prolog_flag(double_quotes) )).
953
954clipboard_content(Text) :-
955 current_prolog_flag(gui, true),
956 !,
957 autoload_call(in_pce_thread_sync(
958 autoload_call(
959 get(@(display), paste, primary, string(Text))))).
960clipboard_content("").
961
962
963 966
975
976bracketed_paste(Input, _Char, Continue) :-
977 collect_paste(Input, [], RevCodes),
978 reverse(RevCodes, Codes),
979 string_codes(Text, Codes),
980 el_insertstr(Input, Text),
981 Continue = refresh.
982
989
990collect_paste(Input, RevCodes, Result) :-
991 el_getc(Input, Char),
992 ( Char == -1 993 -> Result = RevCodes
994 ; paste_char(Char, Char1),
995 RevCodes1 = [Char1|RevCodes],
996 ( RevCodes1 = [0'~,0'1,0'0,0'2,0'[,0'\e|Rest] 997 -> Result = Rest
998 ; collect_paste(Input, RevCodes1, Result)
999 )
1000 ).
1001
1008
1009paste_char(0'\r, 0'\n) :- !. 1010paste_char(C, C).
1011
1012
1013 1016
1017:- multifile prolog:error_message//1. 1018
1019prolog:error_message(editline(Msg)) -->
1020 [ 'editline: ~s'-[Msg] ]