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 prolog:complete_input/4. 78
79
87
88el_wrap_if_ok :-
89 \+ current_prolog_flag(readline, readline),
90 stream_property(user_input, tty(true)),
91 !,
92 el_wrap.
93el_wrap_if_ok.
94
118
119el_wrap :-
120 el_wrap([]).
121
122el_wrap(_) :-
123 el_wrapped(user_input),
124 !.
125el_wrap(Options) :-
126 stream_property(user_input, tty(true)), !,
127 findall(Opt, el_default(Opt), Defaults),
128 merge_options(Options, Defaults, Options1),
129 el_wrap(swipl, user_input, user_output, user_error, Options1),
130 add_prolog_commands(user_input),
131 ignore(el_set(user_input, wordchars("_"))),
132 forall(el_setup(user_input), true),
133 enable_bracketed_paste(user_input),
134 enable_windows_eof(user_input).
135el_wrap(_).
136
137el_default(history(Size)) :-
138 current_prolog_flag(history, Value),
139 ( integer(Value),
140 Value >= 0
141 -> Size = Value
142 ; Value == false
143 -> Size = 0
144 ).
145:- if(current_predicate(prolog_alert_signal/2)). 146el_default(alert_signo(SigNo)) :-
147 prolog_alert_signal(SigName, SigName),
148 current_signal(SigName, SigNo, _Handler).
149:- endif. 150
151add_prolog_commands(Input) :-
152 el_addfn(Input, complete, 'Complete atoms and files', complete),
153 el_addfn(Input, show_completions, 'List completions', show_completions),
154 el_addfn(Input, electric, 'Indicate matching bracket', electric),
155 el_addfn(Input, isearch_history, 'Incremental search in history',
156 isearch_history),
157 el_addfn(Input, bracketed_paste, 'Handle bracketed paste', bracketed_paste),
158 el_bind(Input, ["^I", complete]),
159 el_bind(Input, ["^[?", show_completions]),
160 el_bind(Input, ["^R", isearch_history]),
161 bind_electric(Input),
162 add_paste_quoted(Input),
163 el_source(Input, _).
164
173
174enable_bracketed_paste(Input) :-
175 ( el_get(Input, editor(vi))
176 -> el_bind(Input, ['-r', "\e[200~"]),
177 el_set(Input, bracketed_paste(false))
178 ; el_bind(Input, ["\e[200~", bracketed_paste]),
179 el_set(Input, bracketed_paste(true))
180 ).
181
188
189enable_windows_eof(Input) :-
190 current_prolog_flag(windows, true),
191 !,
192 ( el_get(Input, editor(vi))
193 -> el_bind(Input, ["^Z", 'vi-list-or-eof'])
194 ; el_bind(Input, ["^Z", 'em-delete-or-list'])
195 ).
196enable_windows_eof(_).
197
210
211el_wrap(ProgName, In, Out, Error) :-
212 el_wrap(ProgName, In, Out, Error, []).
213
220
224
232
237
238
255
285
306
319
325
330
334
338
375
381
385
391
398
405
410
411:- multifile
412 prolog:history/2. 413
414prolog:history(Input, enabled) :-
415 !,
416 el_wrapped(Input),
417 el_history(Input, getsize(Size)),
418 Size > 0.
419prolog:history(Input, add(Line)) :-
420 !,
421 el_add_history(Input, Line).
422prolog:history(Input, load(File)) :-
423 !,
424 compat_read_history(Input, File).
425prolog:history(Input, save(File)) :-
426 !,
427 el_write_history(Input, File).
428prolog:history(Input, events(Events)) :-
429 !,
430 el_history_events(Input, Events).
431prolog:history(Input, Command) :-
432 public_command(Command),
433 !,
434 el_history(Input, Command).
435
436public_command(first(_Num, _String)).
437public_command(curr(_Num, _String)).
438public_command(event(_Num, _String)).
439public_command(prev_str(_Search, _Num, _String)).
440public_command(clear).
441
446
447compat_read_history(Input, File) :-
448 catch(el_read_history(Input, File), error(editline(_),_), fail),
449 !.
450compat_read_history(Input, File) :-
451 access_file(File, read),
452 setup_call_cleanup(
453 open(File, read, In, [encoding(utf8)]),
454 read_old_history(Input, In),
455 close(In)),
456 !.
457compat_read_history(_, _).
458
459read_old_history(Input, From) :-
460 catch('$raw_read'(From, Line), error(_,_), fail),
461 ( Line == end_of_file
462 -> true
463 ; string_concat(Line, '.', Event),
464 el_add_history(Input, Event),
465 read_old_history(Input, From)
466 ).
467
468 471
475
476bind_electric(Input) :-
477 forall(bracket(_Open, Close), bind_code(Input, Close, electric)),
478 forall(quote(Close), bind_code(Input, Close, electric)).
479
480bind_code(Input, Code, Command) :-
481 string_codes(Key, [Code]),
482 el_bind(Input, [Key, Command]).
483
484
486
487electric(Input, Char, Continue) :-
488 string_codes(Str, [Char]),
489 el_insertstr(Input, Str),
490 el_line(Input, line(Before, _)),
491 ( string_codes(Before, Codes),
492 nesting(Codes, 0, Nesting),
493 reverse(Nesting, [Close|RevNesting])
494 -> ( Close = open(_,_) 495 -> Continue = refresh
496 ; matching_open(RevNesting, Close, _, Index)
497 -> string_length(Before, Len), 498 Move is Index-Len,
499 Continue = electric(Move, 500, refresh)
500 ; Continue = refresh_beep 501 )
502 ; Continue = refresh_beep
503 ).
504
505matching_open_index(String, Index) :-
506 string_codes(String, Codes),
507 nesting(Codes, 0, Nesting),
508 reverse(Nesting, [Close|RevNesting]),
509 matching_open(RevNesting, Close, _, Index).
510
511matching_open([Open|Rest], Close, Rest, Index) :-
512 Open = open(Index,_),
513 match(Open, Close),
514 !.
515matching_open([Close1|Rest1], Close, Rest, Index) :-
516 Close1 = close(_,_),
517 matching_open(Rest1, Close1, Rest2, _),
518 matching_open(Rest2, Close, Rest, Index).
519
520match(open(_,Open),close(_,Close)) :-
521 ( bracket(Open, Close)
522 -> true
523 ; Open == Close,
524 quote(Open)
525 ).
526
527bracket(0'(, 0')).
528bracket(0'[, 0']).
529bracket(0'{, 0'}).
530
531quote(0'\').
532quote(0'\").
533quote(0'\`).
534
535nesting([], _, []).
536nesting([H|T], I, Nesting) :-
537 ( bracket(H, _Close)
538 -> Nesting = [open(I,H)|Nest]
539 ; bracket(_Open, H)
540 -> Nesting = [close(I,H)|Nest]
541 ),
542 !,
543 I2 is I+1,
544 nesting(T, I2, Nest).
545nesting([0'0, 0'\'|T], I, Nesting) :-
546 !,
547 phrase(skip_code, T, T1),
548 difflist_length(T, T1, Len),
549 I2 is I+Len+2,
550 nesting(T1, I2, Nesting).
551nesting([H|T], I, Nesting) :-
552 quote(H),
553 !,
554 ( phrase(skip_quoted(H), T, T1)
555 -> difflist_length(T, T1, Len),
556 I2 is I+Len+1,
557 Nesting = [open(I,H),close(I2,H)|Nest],
558 nesting(T1, I2, Nest)
559 ; Nesting = [open(I,H)] 560 ).
561nesting([_|T], I, Nesting) :-
562 I2 is I+1,
563 nesting(T, I2, Nesting).
564
565difflist_length(List, Tail, Len) :-
566 difflist_length(List, Tail, 0, Len).
567
568difflist_length(List, Tail, Len0, Len) :-
569 List == Tail,
570 !,
571 Len = Len0.
572difflist_length([_|List], Tail, Len0, Len) :-
573 Len1 is Len0+1,
574 difflist_length(List, Tail, Len1, Len).
575
576skip_quoted(H) -->
577 [H],
578 !.
579skip_quoted(H) -->
580 "\\", [H],
581 !,
582 skip_quoted(H).
583skip_quoted(H) -->
584 [_],
585 skip_quoted(H).
586
587skip_code -->
588 "\\", [_],
589 !.
590skip_code -->
591 [_].
592
593
594 597
605
606
607:- dynamic
608 last_complete/2. 609
610complete(Input, _Char, Continue) :-
611 el_line(Input, line(Before, After)),
612 ensure_input_completion,
613 prolog:complete_input(Before, After, Delete, Completions),
614 ( Completions = [One]
615 -> string_length(Delete, Len),
616 el_deletestr(Input, Len),
617 complete_text(One, Text),
618 el_insertstr(Input, Text),
619 Continue = refresh
620 ; Completions == []
621 -> Continue = refresh_beep
622 ; get_time(Now),
623 retract(last_complete(TLast, Before)),
624 Now - TLast < 2
625 -> nl(user_error),
626 list_alternatives(Completions),
627 Continue = redisplay
628 ; retractall(last_complete(_,_)),
629 get_time(Now),
630 asserta(last_complete(Now, Before)),
631 common_competion(Completions, Extend),
632 ( Delete == Extend
633 -> Continue = refresh_beep
634 ; string_length(Delete, Len),
635 el_deletestr(Input, Len),
636 el_insertstr(Input, Extend),
637 Continue = refresh
638 )
639 ).
640
641:- dynamic
642 input_completion_loaded/0. 643
644ensure_input_completion :-
645 input_completion_loaded,
646 !.
647ensure_input_completion :-
648 predicate_property(prolog:complete_input(_,_,_,_),
649 number_of_clauses(N)),
650 N > 0,
651 !.
652ensure_input_completion :-
653 exists_source(library(console_input)),
654 !,
655 use_module(library(console_input), []),
656 asserta(input_completion_loaded).
657ensure_input_completion.
658
659
663
664show_completions(Input, _Char, Continue) :-
665 el_line(Input, line(Before, After)),
666 prolog:complete_input(Before, After, _Delete, Completions),
667 nl(user_error),
668 list_alternatives(Completions),
669 Continue = redisplay.
670
671complete_text(Text-_Comment, Text) :- !.
672complete_text(Text, Text).
673
677
678common_competion(Alternatives, Common) :-
679 maplist(atomic, Alternatives),
680 !,
681 common_prefix(Alternatives, Common).
682common_competion(Alternatives, Common) :-
683 maplist(complete_text, Alternatives, AltText),
684 !,
685 common_prefix(AltText, Common).
686
690
691common_prefix([A1|T], Common) :-
692 common_prefix_(T, A1, Common).
693
694common_prefix_([], Common, Common).
695common_prefix_([H|T], Common0, Common) :-
696 common_prefix(H, Common0, Common1),
697 common_prefix_(T, Common1, Common).
698
702
703common_prefix(A1, A2, Prefix) :-
704 sub_atom(A1, 0, _, _, A2),
705 !,
706 Prefix = A2.
707common_prefix(A1, A2, Prefix) :-
708 sub_atom(A2, 0, _, _, A1),
709 !,
710 Prefix = A1.
711common_prefix(A1, A2, Prefix) :-
712 atom_codes(A1, C1),
713 atom_codes(A2, C2),
714 list_common_prefix(C1, C2, C),
715 string_codes(Prefix, C).
716
717list_common_prefix([H|T0], [H|T1], [H|T]) :-
718 !,
719 list_common_prefix(T0, T1, T).
720list_common_prefix(_, _, []).
721
722
723
729
730list_alternatives(Alternatives) :-
731 maplist(atomic, Alternatives),
732 !,
733 length(Alternatives, Count),
734 maplist(atom_length, Alternatives, Lengths),
735 max_list(Lengths, Max),
736 tty_size(_, Cols),
737 ColW is Max+2,
738 Columns is max(1, Cols // ColW),
739 RowCount is (Count+Columns-1)//Columns,
740 length(Rows, RowCount),
741 to_matrix(Alternatives, Rows, Rows),
742 ( RowCount > 11
743 -> length(First, 10),
744 Skipped is RowCount - 10,
745 append(First, _, Rows),
746 maplist(write_row(ColW), First),
747 format(user_error, '... skipped ~D rows~n', [Skipped])
748 ; maplist(write_row(ColW), Rows)
749 ).
750list_alternatives(Alternatives) :-
751 maplist(complete_text, Alternatives, AltText),
752 list_alternatives(AltText).
753
754to_matrix([], _, Rows) :-
755 !,
756 maplist(close_list, Rows).
757to_matrix([H|T], [RH|RT], Rows) :-
758 !,
759 add_list(RH, H),
760 to_matrix(T, RT, Rows).
761to_matrix(List, [], Rows) :-
762 to_matrix(List, Rows, Rows).
763
764add_list(Var, Elem) :-
765 var(Var), !,
766 Var = [Elem|_].
767add_list([_|T], Elem) :-
768 add_list(T, Elem).
769
770close_list(List) :-
771 append(List, [], _),
772 !.
773
774write_row(ColW, Row) :-
775 length(Row, Columns),
776 make_format(Columns, ColW, Format),
777 format(user_error, Format, Row).
778
779make_format(N, ColW, Format) :-
780 format(string(PerCol), '~~w~~t~~~d+', [ColW]),
781 Front is N - 1,
782 length(LF, Front),
783 maplist(=(PerCol), LF),
784 append(LF, ['~w~n'], Parts),
785 atomics_to_string(Parts, Format).
786
787
788 791
796
797isearch_history(Input, _Char, Continue) :-
798 el_line(Input, line(Before, After)),
799 string_concat(Before, After, Current),
800 string_length(Current, Len),
801 search_print('', "", Current),
802 search(Input, "", Current, 1, Line),
803 el_deletestr(Input, Len),
804 el_insertstr(Input, Line),
805 Continue = redisplay.
806
807search(Input, For, Current, Nth, Line) :-
808 el_getc(Input, Next),
809 Next \== -1,
810 !,
811 search(Next, Input, For, Current, Nth, Line).
812search(_Input, _For, _Current, _Nth, "").
813
814search(7, _Input, _, Current, _, Current) :- 815 !,
816 clear_line.
817search(18, Input, For, Current, Nth, Line) :- 818 !,
819 N2 is Nth+1,
820 search_(Input, For, Current, N2, Line).
821search(19, Input, For, Current, Nth, Line) :- 822 !,
823 N2 is max(1,Nth-1),
824 search_(Input, For, Current, N2, Line).
825search(127, Input, For, Current, _Nth, Line) :- 826 sub_string(For, 0, _, 1, For1),
827 !,
828 search_(Input, For1, Current, 1, Line).
829search(Char, Input, For, Current, Nth, Line) :-
830 code_type(Char, cntrl),
831 !,
832 search_end(Input, For, Current, Nth, Line),
833 el_push(Input, Char).
834search(Char, Input, For, Current, _Nth, Line) :-
835 format(string(For1), '~w~c', [For,Char]),
836 search_(Input, For1, Current, 1, Line).
837
838search_(Input, For1, Current, Nth, Line) :-
839 ( find_in_history(Input, For1, Current, Nth, Candidate)
840 -> search_print('', For1, Candidate)
841 ; search_print('failed ', For1, Current)
842 ),
843 search(Input, For1, Current, Nth, Line).
844
845search_end(Input, For, Current, Nth, Line) :-
846 ( find_in_history(Input, For, Current, Nth, Line)
847 -> true
848 ; Line = Current
849 ),
850 clear_line.
851
852find_in_history(_, "", Current, _, Current) :-
853 !.
854find_in_history(Input, For, _, Nth, Line) :-
855 el_history_events(Input, History),
856 call_nth(( member(_N-Line, History),
857 sub_string(Line, _, _, _, For)
858 ),
859 Nth),
860 !.
861
862search_print(State, Search, Current) :-
863 format(user_error, '\r(~wreverse-i-search)`~w\': ~w\e[0K',
864 [State, Search, Current]).
865
866clear_line :-
867 format(user_error, '\r\e[0K', []).
868
869
870 873
874:- meta_predicate
875 with_quote_flags(+,+,0). 876
877add_paste_quoted(Input) :-
878 current_prolog_flag(gui, true),
879 !,
880 el_addfn(Input, paste_quoted, 'Paste as quoted atom', paste_quoted),
881 el_bind(Input, ["^Y", paste_quoted]).
882add_paste_quoted(_).
883
889
890paste_quoted(Input, _Char, Continue) :-
891 clipboard_content(String),
892 quote_text(Input, String, Quoted),
893 el_insertstr(Input, Quoted),
894 Continue = refresh.
895
896quote_text(Input, String, Value) :-
897 el_line(Input, line(Before, _After)),
898 ( sub_string(Before, _, 1, 0, Quote)
899 -> true
900 ; Quote = "'"
901 ),
902 quote_text(Input, Quote, String, Value).
903
904quote_text(Input, "'", Text, Quoted) =>
905 format(string(Quoted), '~q', [Text]),
906 el_deletestr(Input, 1).
907quote_text(Input, "\"", Text, Quoted) =>
908 atom_string(Text, String),
909 with_quote_flags(
910 string, codes,
911 format(string(Quoted), '~q', [String])),
912 el_deletestr(Input, 1).
913quote_text(Input, "`", Text, Quoted) =>
914 atom_string(Text, String),
915 with_quote_flags(
916 codes, string,
917 format(string(Quoted), '~q', [String])),
918 el_deletestr(Input, 1).
919quote_text(_, _, Text, Quoted) =>
920 format(string(Quoted), '~q', [Text]).
921
922with_quote_flags(Double, Back, Goal) :-
923 current_prolog_flag(double_quotes, ODouble),
924 current_prolog_flag(back_quotes, OBack),
925 setup_call_cleanup(
926 ( set_prolog_flag(double_quotes, Double),
927 set_prolog_flag(back_quotes, Back) ),
928 Goal,
929 ( set_prolog_flag(double_quotes, ODouble),
930 set_prolog_flag(back_quotes, OBack) )).
931
932clipboard_content(Text) :-
933 current_prolog_flag(gui, true),
934 !,
935 autoload_call(in_pce_thread_sync(
936 autoload_call(
937 get(@(display), paste, primary, string(Text))))).
938clipboard_content("").
939
940
941 944
953
954bracketed_paste(Input, _Char, Continue) :-
955 collect_paste(Input, [], RevCodes),
956 reverse(RevCodes, Codes),
957 string_codes(Text, Codes),
958 el_insertstr(Input, Text),
959 Continue = refresh.
960
967
968collect_paste(Input, RevCodes, Result) :-
969 el_getc(Input, Char),
970 ( Char == -1 971 -> Result = RevCodes
972 ; paste_char(Char, Char1),
973 RevCodes1 = [Char1|RevCodes],
974 ( RevCodes1 = [0'~,0'1,0'0,0'2,0'[,0'\e|Rest] 975 -> Result = Rest
976 ; collect_paste(Input, RevCodes1, Result)
977 )
978 ).
979
986
987paste_char(0'\r, 0'\n) :- !. 988paste_char(C, C).
989
990
991 994
995:- multifile prolog:error_message//1. 996
997prolog:error_message(editline(Msg)) -->
998 [ 'editline: ~s'-[Msg] ]