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