1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2017-2025, VU University Amsterdam 7 CWI Amsterdam 8 SWI-Prolog Solutions b.v. 9 All rights reserved. 10 11 Redistribution and use in source and binary forms, with or without 12 modification, are permitted provided that the following conditions 13 are met: 14 15 1. Redistributions of source code must retain the above copyright 16 notice, this list of conditions and the following disclaimer. 17 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in 20 the documentation and/or other materials provided with the 21 distribution. 22 23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 POSSIBILITY OF SUCH DAMAGE. 35*/ 36 37:- module(editline, 38 [ el_wrap/0, % wrap user_input, etc. 39 el_wrap/1, % +Options 40 el_wrap/4, % +Prog, +Input, +Output, +Error 41 el_wrap/5, % +Prog, +Input, +Output, +Error, +Options 42 el_wrapped/1, % +Input 43 el_unwrap/1, % +Input 44 45 el_source/2, % +Input, +File 46 el_bind/2, % +Input, +Args 47 el_set/2, % +Input, +Action 48 el_get/2, % +Input, ?Property 49 el_addfn/4, % +Input, +Name, +Help, :Goal 50 el_cursor/2, % +Input, +Move 51 el_line/2, % +Input, -Line 52 el_insertstr/2, % +Input, +Text 53 el_deletestr/2, % +Input, +Count 54 55 el_history/2, % +Input, ?Action 56 el_history_events/2, % +Input, -Events 57 el_add_history/2, % +Input, +Line 58 el_write_history/2, % +Input, +FileName 59 el_read_history/2, % +Input, +FileName 60 61 el_version/1 % -Version:integer 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(,,,). 74 75:- multifile 76 el_setup/1, % +Input 77 el_wcwidth/2, % +Code, -Columns 78 prolog:complete_input/4.
106el_wrap_if_ok :- 107 stream_property(user_input, tty(true)), 108 !, 109 el_wrap. 110el_wrap_if_ok.
user_input
is connected to a terminal. This is the high level predicate used
for most purposes. The remainder of the library interface deals with
low level predicates that allows for applying and programming
libedit in non-standard situations.
The library is registered with ProgName set to swipl (see
el_wrap/4).
Options processed:
history or 100 if this flag is not defined.136el_wrap :- 137 el_wrap([]). 138 139el_wrap(_) :- 140 el_wrapped(user_input), 141 !. 142el_wrap(Options) :- 143 stream_property(user_input, tty(true)), !, 144 findall(Opt, el_default(Opt), Defaults), 145 merge_options(Options, Defaults, Options1), 146 el_wrap(swipl, user_input, user_output, user_error, Options1), 147 add_prolog_commands(user_input), 148 ignore(el_set(user_input, wordchars("_"))), 149 forall(el_setup(user_input), true), 150 enable_bracketed_paste(user_input), 151 enable_word_motion(user_input), 152 enable_windows_eof(user_input). 153el_wrap(_). 154 155el_default(history(Size)) :- 156 current_prolog_flag(history, Value), 157 ( integer(Value), 158 Value >= 0 159 -> Size = Value 160 ; Value == false 161 -> Size = 0 162 ). 163:- if(current_predicate(prolog_alert_signal/2)). 164el_default(alert_signo(SigNo)) :- 165 prolog_alert_signal(SigName, SigName), 166 current_signal(SigName, SigNo, _Handler). 167:- endif. 168 169add_prolog_commands(Input) :- 170 el_addfn(Input, complete, 'Complete atoms and files', complete), 171 el_addfn(Input, show_completions, 'List completions', show_completions), 172 el_addfn(Input, electric, 'Indicate matching bracket', electric), 173 el_addfn(Input, isearch_history, 'Incremental search in history', 174 isearch_history), 175 el_addfn(Input, bracketed_paste, 'Handle bracketed paste', bracketed_paste), 176 el_bind(Input, ["^I", complete]), 177 el_bind(Input, ["^[?", show_completions]), 178 el_bind(Input, ["^R", isearch_history]), 179 bind_electric(Input), 180 add_paste_quoted(Input), 181 el_source(Input, _).
-v or -e.
192enable_bracketed_paste(Input) :-
193 ( el_get(Input, editor(vi))
194 -> el_bind(Input, ['-r', "\e[200~"]),
195 el_set(Input, bracketed_paste(false))
196 ; el_bind(Input, ["\e[200~", bracketed_paste]),
197 el_set(Input, bracketed_paste(true))
198 ).ESC[1;5D and ESC[1;5C
for these key combinations (see packages/xpce/src/txt/terminal.c);
most xterm-compatible terminals use the same sequences. In vi
mode the binding is installed in both the insert keymap and the
command (alternative) keymap. Called after the el_setup/1 hooks
since those may switch editor.
This complements the readline emulation, which already binds these sequences in its initialisation.
213enable_word_motion(Input) :-
214 el_bind(Input, ["\e[1;5D", 'ed-prev-word']),
215 el_bind(Input, ["\e[1;5C", 'em-next-word']),
216 ( el_get(Input, editor(vi))
217 -> el_bind(Input, ['-a', "\e[1;5D", 'vi-prev-word']),
218 el_bind(Input, ['-a', "\e[1;5C", 'vi-next-word'])
219 ; true
220 ).^Z to send end-of-file on Windows, matching the platform
convention (compare with ^D on Unix). Routed through libedit's
built-in em-delete-or-list (emacs) or vi-list-or-eof (vi), both
of which return EOF on an empty line.229enable_windows_eof(Input) :- 230 current_prolog_flag(windows, true), 231 !, 232 ( el_get(Input, editor(vi)) 233 -> el_bind(Input, ["^Z", 'vi-list-or-eof']) 234 ; el_bind(Input, ["^Z", 'em-delete-or-list']) 235 ). 236enable_windows_eof(_).
251el_wrap(ProgName, In, Out, Error) :-
252 el_wrap(ProgName, In, Out, Error, []).forall(el_setup(Input), true) after the
input stream has been wrapped, the default Prolog commands have been
added and the default user setup file has been sourced using
el_source/2. It can be used to define and bind additional commands.$HOME/.editrcbind command with the given arguments. The
example below lists the current key bindings.
?- el_bind(user_input, ['-a']).
The predicate el_bind/2 is typically used to bind commands defined using el_addfn/4. Note that the C proxy function has only the last character of the command as context to find the Prolog binding. This implies we cannot both bind e.g., "^[?" and "?" to a Prolog function.
bind -a (see el_bind/2) and Goal is called of the
associated key-binding is activated. Goal is called as
call(:Goal, +Input, +Char, -Continue)
where Input is the input stream providing access to the editor, Char
the activating character and Continue must be instantated with one
of the known continuation codes as defined by libedit: norm,
newline, eof, arghack, refresh, refresh_beep, cursor,
redisplay, error or fatal. In addition, the following Continue
code is provided.
The registered Goal typically used el_line/2 to fetch the input line and el_cursor/2, el_insertstr/2 and/or el_deletestr/2 to manipulate the input line.
Normally el_bind/2 is used to associate the defined command with a keyboard sequence.
el_set() and el_wset(). Currently provided values for
Action are:
el_wsey() EL_WORDCHARS, which is only provided
in some recent versions of libedit.ESC[200~ /
ESC[201~ before each prompt, which the default bindings
route through bracketed_paste/3. Disabling sends the matching
ESC[?2004l sequence immediately. enable_bracketed_paste/1
manages this based on the current editor; you normally do not
need to set it directly.This predicate fails silently of Action is not implemented. Illegal input raises in an exception.
el_get(). Currently supported Property terms:
emacs or vi, reflecting the current
keymap selected via el_bind/2 with -e / -v.
Any other Property raises a domain_error(editline_property, _).
line(Before,
After), where Before is a string holding the text before the
cursor and After is a string holding the text after the cursor.history() from libedit. Supported actions are:
el_history() using H_GETSIZE,
which returns the number of currently saved events. The number
of saved events may be computed from first and last or
using el_history_events/2.first is the most recent event and
last the oldest.history() API, retrieving a numbered event without changing
the current notion.Num-String, where
Num is the event number and String is the associated string
without terminating newline.LIBEDIT_MAJOR*10000 + LIBEDIT_MINOR*100.
The version is generated from the include file histedit.h,
which implies that the actual version of the shared library may be
different.462:- multifile 463 prolog:history/2. 464 465prologhistory(Input, enabled) :- 466 !, 467 el_wrapped(Input), 468 el_history(Input, getsize(Size)), 469 Size > 0. 470prologhistory(Input, add(Line)) :- 471 !, 472 el_add_history(Input, Line). 473prologhistory(Input, load(File)) :- 474 !, 475 compat_read_history(Input, File). 476prologhistory(Input, save(File)) :- 477 !, 478 el_write_history(Input, File). 479prologhistory(Input, events(Events)) :- 480 !, 481 el_history_events(Input, Events). 482prologhistory(Input, Command) :- 483 public_command(Command), 484 !, 485 el_history(Input, Command). 486 487public_command(first(_Num, _String)). 488public_command(curr(_Num, _String)). 489public_command(event(_Num, _String)). 490public_command(prev_str(_Search, _Num, _String)). 491public_command(clear).
swipl-win.exe before migrating to SDL.498compat_read_history(Input, File) :- 499 catch(el_read_history(Input, File), error(editline(_),_), fail), 500 !. 501compat_read_history(Input, File) :- 502 access_file(File, read), 503 setup_call_cleanup( 504 open(File, read, In, [encoding(utf8)]), 505 read_old_history(Input, In), 506 close(In)), 507 !. 508compat_read_history(_, _). 509 510read_old_history(Input, From) :- 511 catch('$raw_read'(From, Line), error(_,_), fail), 512 ( Line == end_of_file 513 -> true 514 ; string_concat(Line, '.', Event), 515 el_add_history(Input, Event), 516 read_old_history(Input, From) 517 ). 518 519 /******************************* 520 * ELECTRIC CARET * 521 *******************************/
527bind_electric(Input) :- 528 forall(bracket(_Open, Close), bind_code(Input, Close, electric)), 529 forall(quote(Close), bind_code(Input, Close, electric)). 530 531bind_code(Input, Code, Command) :- 532 string_codes(Key, [Code]), 533 el_bind(Input, [Key, Command]).
538electric(Input, Char, Continue) :- 539 string_codes(Str, [Char]), 540 el_insertstr(Input, Str), 541 el_line(Input, line(Before, _)), 542 ( string_codes(Before, Codes), 543 nesting(Codes, 0, Nesting), 544 reverse(Nesting, [Close|RevNesting]) 545 -> ( Close = open(_,_) % open quote 546 -> Continue = refresh 547 ; matching_open(RevNesting, Close, _, Index) 548 -> string_length(Before, Len), % Proper match 549 Move is Index-Len, 550 Continue = electric(Move, 500, refresh) 551 ; Continue = refresh_beep % Not properly nested 552 ) 553 ; Continue = refresh_beep 554 ). 555 556matching_open_index(String, Index) :- 557 string_codes(String, Codes), 558 nesting(Codes, 0, Nesting), 559 reverse(Nesting, [Close|RevNesting]), 560 matching_open(RevNesting, Close, _, Index). 561 562matching_open([Open|Rest], Close, Rest, Index) :- 563 Open = open(Index,_), 564 match(Open, Close), 565 !. 566matching_open([Close1|Rest1], Close, Rest, Index) :- 567 Close1 = close(_,_), 568 matching_open(Rest1, Close1, Rest2, _), 569 matching_open(Rest2, Close, Rest, Index). 570 571match(open(_,Open),close(_,Close)) :- 572 ( bracket(Open, Close) 573 -> true 574 ; Open == Close, 575 quote(Open) 576 ). 577 578bracket(0'(, 0')). 579bracket(0'[, 0']). 580bracket(0'{, 0'}). 581 582quote(0'\'). 583quote(0'\"). 584quote(0'\`). 585 586nesting([], _, []). 587nesting([H|T], I, Nesting) :- 588 ( bracket(H, _Close) 589 -> Nesting = [open(I,H)|Nest] 590 ; bracket(_Open, H) 591 -> Nesting = [close(I,H)|Nest] 592 ), 593 !, 594 I2 is I+1, 595 nesting(T, I2, Nest). 596nesting([0'0, 0'\'|T], I, Nesting) :- 597 !, 598 phrase(skip_code, T, T1), 599 difflist_length(T, T1, Len), 600 I2 is I+Len+2, 601 nesting(T1, I2, Nesting). 602nesting([H|T], I, Nesting) :- 603 quote(H), 604 !, 605 ( phrase(skip_quoted(H), T, T1) 606 -> difflist_length(T, T1, Len), 607 I2 is I+Len+1, 608 Nesting = [open(I,H),close(I2,H)|Nest], 609 nesting(T1, I2, Nest) 610 ; Nesting = [open(I,H)] % Open quote 611 ). 612nesting([_|T], I, Nesting) :- 613 I2 is I+1, 614 nesting(T, I2, Nesting). 615 616difflist_length(List, Tail, Len) :- 617 difflist_length(List, Tail, 0, Len). 618 619difflist_length(List, Tail, Len0, Len) :- 620 List == Tail, 621 !, 622 Len = Len0. 623difflist_length([_|List], Tail, Len0, Len) :- 624 Len1 is Len0+1, 625 difflist_length(List, Tail, Len1, Len). 626 627skip_quoted(H) --> 628 [H], 629 !. 630skip_quoted(H) --> 631 "\\", [H], 632 !, 633 skip_quoted(H). 634skip_quoted(H) --> 635 [_], 636 skip_quoted(H). 637 638skip_code --> 639 "\\", [_], 640 !. 641skip_code --> 642 [_]. 643 644 645 /******************************* 646 * COMPLETION * 647 *******************************/
complete editline function. The
predicate is called with three arguments, the first being the input
stream used to access the libedit functions and the second the
activating character. The last argument tells libedit what to do.
Consult el_set(3), EL_ADDFN for details.658:- dynamic 659 last_complete/2. 660 661complete(Input, _Char, Continue) :- 662 el_line(Input, line(Before, After)), 663 ensure_input_completion, 664 prolog:complete_input(Before, After, Delete, Completions), 665 ( Completions = [One] 666 -> string_length(Delete, Len), 667 el_deletestr(Input, Len), 668 complete_text(One, Text), 669 el_insertstr(Input, Text), 670 Continue = refresh 671 ; Completions == [] 672 -> Continue = refresh_beep 673 ; get_time(Now), 674 retract(last_complete(TLast, Before)), 675 Now - TLast < 2 676 -> plain_style, 677 nl(user_error), 678 list_alternatives(Completions), 679 Continue = redisplay 680 ; retractall(last_complete(_,_)), 681 get_time(Now), 682 asserta(last_complete(Now, Before)), 683 common_competion(Completions, Extend), 684 ( Delete == Extend 685 -> Continue = refresh_beep 686 ; string_length(Delete, Len), 687 el_deletestr(Input, Len), 688 el_insertstr(Input, Extend), 689 Continue = refresh 690 ) 691 ). 692 693:- dynamic 694 input_completion_loaded/0. 695 696ensure_input_completion :- 697 input_completion_loaded, 698 !. 699ensure_input_completion :- 700 predicate_property(prolog:complete_input(_,_,_,_), 701 number_of_clauses(N)), 702 N > 0, 703 !. 704ensure_input_completion :- 705 exists_source(library(console_input)), 706 !, 707 use_module(library(console_input), []), 708 asserta(input_completion_loaded). 709ensure_input_completion.
716show_completions(Input, _Char, Continue) :-
717 el_line(Input, line(Before, After)),
718 prolog:complete_input(Before, After, _Delete, Completions),
719 plain_style,
720 nl(user_error),
721 list_alternatives(Completions),
722 Continue = redisplay.input in effect while the user is typing (see
decorate_prompt/2 in boot/toplevel.pl). Erasing part of the screen
paints it with the current background colour, so libedit must switch
these attributes off before it erases the rows the line no longer
uses, e.g. after unwrapping a long line or moving from a multi-line
history entry to a shorter one. See EL_ATTRS in libedit.
The toplevel calls this each time it builds a prompt, so a theme loaded at run time is picked up. Does nothing if libedit is too old to support this.
739prologline_editor_attributes(Off, On) :-
740 el_wrapped(user_input),
741 ignore(el_set(user_input, attrs(Off, On))).
The toplevel decorates the input line by leaving the attributes of
the colour class input in effect while the user is typing (see
decorate_prompt/2 in boot/toplevel.pl). Without this reset that
decoration also applies to the text below, and if the class has a
background colour it paints whatever we erase with `\e[K` as well.
The commands that write this text end with redisplay, which makes
libedit redraw the line from scratch and thus re-install the style.
758plain_style :- 759 format(user_error, '\e[0m', []). 760 761complete_text(Text-_Comment, Text) :- !. 762complete_text(Text, Text).
768common_competion(Alternatives, Common) :- 769 maplist(atomic, Alternatives), 770 !, 771 common_prefix(Alternatives, Common). 772common_competion(Alternatives, Common) :- 773 maplist(complete_text, Alternatives, AltText), 774 !, 775 common_prefix(AltText, Common).
781common_prefix([A1|T], Common) :- 782 common_prefix_(T, A1, Common). 783 784common_prefix_([], Common, Common). 785common_prefix_([H|T], Common0, Common) :- 786 common_prefix(H, Common0, Common1), 787 common_prefix_(T, Common1, Common).
793common_prefix(A1, A2, Prefix) :- 794 sub_atom(A1, 0, _, _, A2), 795 !, 796 Prefix = A2. 797common_prefix(A1, A2, Prefix) :- 798 sub_atom(A2, 0, _, _, A1), 799 !, 800 Prefix = A1. 801common_prefix(A1, A2, Prefix) :- 802 atom_codes(A1, C1), 803 atom_codes(A2, C2), 804 list_common_prefix(C1, C2, C), 805 string_codes(Prefix, C). 806 807list_common_prefix([H|T0], [H|T1], [H|T]) :- 808 !, 809 list_common_prefix(T0, T1, T). 810list_common_prefix(_, _, []).
820list_alternatives(Alternatives) :- 821 maplist(atomic, Alternatives), 822 !, 823 length(Alternatives, Count), 824 maplist(atom_length, Alternatives, Lengths), 825 max_list(Lengths, Max), 826 tty_size(_, Cols), 827 ColW is Max+2, 828 Columns is max(1, Cols // ColW), 829 RowCount is (Count+Columns-1)//Columns, 830 length(Rows, RowCount), 831 to_matrix(Alternatives, Rows, Rows), 832 ( RowCount > 11 833 -> length(First, 10), 834 Skipped is RowCount - 10, 835 append(First, _, Rows), 836 maplist(write_row(ColW), First), 837 format(user_error, '... skipped ~D rows~n', [Skipped]) 838 ; maplist(write_row(ColW), Rows) 839 ). 840list_alternatives(Alternatives) :- 841 maplist(complete_text, Alternatives, AltText), 842 list_alternatives(AltText). 843 844to_matrix([], _, Rows) :- 845 !, 846 maplist(close_list, Rows). 847to_matrix([H|T], [RH|RT], Rows) :- 848 !, 849 add_list(RH, H), 850 to_matrix(T, RT, Rows). 851to_matrix(List, [], Rows) :- 852 to_matrix(List, Rows, Rows). 853 854add_list(Var, Elem) :- 855 var(Var), !, 856 Var = [Elem|_]. 857add_list([_|T], Elem) :- 858 add_list(T, Elem). 859 860close_list(List) :- 861 append(List, [], _), 862 !. 863 864write_row(ColW, Row) :- 865 length(Row, Columns), 866 make_format(Columns, ColW, Format), 867 format(user_error, Format, Row). 868 869make_format(N, ColW, Format) :- 870 format(string(PerCol), '~~w~~t~~~d+', [ColW]), 871 Front is N - 1, 872 length(LF, Front), 873 maplist(=(PerCol), LF), 874 append(LF, ['~w~n'], Parts), 875 atomics_to_string(Parts, Format). 876 877 878 /******************************* 879 * SEARCH * 880 *******************************/
887isearch_history(Input, _Char, Continue) :- 888 el_line(Input, line(Before, After)), 889 string_concat(Before, After, Current), 890 string_length(Current, Len), 891 search_print('', "", Current), 892 search(Input, "", Current, 1, Line), 893 el_deletestr(Input, Len), 894 el_insertstr(Input, Line), 895 Continue = redisplay. 896 897search(Input, For, Current, Nth, Line) :- 898 el_getc(Input, Next), 899 Next \== -1, 900 !, 901 search(Next, Input, For, Current, Nth, Line). 902search(_Input, _For, _Current, _Nth, ""). 903 904search(7, _Input, _, Current, _, Current) :- % C-g: abort 905 !, 906 clear_line. 907search(18, Input, For, Current, Nth, Line) :- % C-r: search previous 908 !, 909 N2 is Nth+1, 910 search_(Input, For, Current, N2, Line). 911search(19, Input, For, Current, Nth, Line) :- % C-s: search next 912 !, 913 N2 is max(1,Nth-1), 914 search_(Input, For, Current, N2, Line). 915search(127, Input, For, Current, _Nth, Line) :- % DEL/BS: shorten search 916 sub_string(For, 0, _, 1, For1), 917 !, 918 search_(Input, For1, Current, 1, Line). 919search(Char, Input, For, Current, Nth, Line) :- 920 code_type(Char, cntrl), 921 !, 922 search_end(Input, For, Current, Nth, Line), 923 el_push(Input, Char). 924search(Char, Input, For, Current, _Nth, Line) :- 925 format(string(For1), '~w~c', [For,Char]), 926 search_(Input, For1, Current, 1, Line). 927 928search_(Input, For1, Current, Nth, Line) :- 929 ( find_in_history(Input, For1, Current, Nth, Candidate) 930 -> search_print('', For1, Candidate) 931 ; search_print('failed ', For1, Current) 932 ), 933 search(Input, For1, Current, Nth, Line). 934 935search_end(Input, For, Current, Nth, Line) :- 936 ( find_in_history(Input, For, Current, Nth, Line) 937 -> true 938 ; Line = Current 939 ), 940 clear_line. 941 942find_in_history(_, "", Current, _, Current) :- 943 !. 944find_in_history(Input, For, _, Nth, Line) :- 945 el_history_events(Input, History), 946 call_nth(( member(_N-Line, History), 947 sub_string(Line, _, _, _, For) 948 ), 949 Nth), 950 !. 951 952% Note that we reset the attributes (see plain_style/0): the search 953% prompt is not the line being edited and, more importantly, `\e[0K' 954% erases using the current background colour. 955 956search_print(State, Search, Current) :- 957 format(user_error, '\r\e[0m(~wreverse-i-search)`~w\': ~w\e[0K', 958 [State, Search, Current]). 959 960clear_line :- 961 format(user_error, '\r\e[0m\e[0K', []). 962 963 964 /******************************* 965 * PASTE QUOTED * 966 *******************************/ 967 968:- meta_predicate 969 with_quote_flags(,,). 970 971add_paste_quoted(Input) :- 972 current_prolog_flag(gui, true), 973 !, 974 el_addfn(Input, paste_quoted, 'Paste as quoted atom', paste_quoted), 975 el_bind(Input, ["^Y", paste_quoted]). 976add_paste_quoted(_).
984paste_quoted(Input, _Char, Continue) :- 985 clipboard_content(String), 986 quote_text(Input, String, Quoted), 987 el_insertstr(Input, Quoted), 988 Continue = refresh. 989 990quote_text(Input, String, Value) :- 991 el_line(Input, line(Before, _After)), 992 ( sub_string(Before, _, 1, 0, Quote) 993 -> true 994 ; Quote = "'" 995 ), 996 quote_text(Input, Quote, String, Value). 997 998quote_text(Input, "'", Text, Quoted) => 999 format(string(Quoted), '~q', [Text]), 1000 el_deletestr(Input, 1). 1001quote_text(Input, "\"", Text, Quoted) => 1002 atom_string(Text, String), 1003 with_quote_flags( 1004 string, codes, 1005 format(string(Quoted), '~q', [String])), 1006 el_deletestr(Input, 1). 1007quote_text(Input, "`", Text, Quoted) => 1008 atom_string(Text, String), 1009 with_quote_flags( 1010 codes, string, 1011 format(string(Quoted), '~q', [String])), 1012 el_deletestr(Input, 1). 1013quote_text(_, _, Text, Quoted) => 1014 format(string(Quoted), '~q', [Text]). 1015 1016with_quote_flags(Double, Back, Goal) :- 1017 setup_call_cleanup( 1018 ( push_prolog_flag(double_quotes, Double), 1019 push_prolog_flag(back_quotes, Back) ), 1020 Goal, 1021 ( pop_prolog_flag(back_quotes), 1022 pop_prolog_flag(double_quotes) )). 1023 1024clipboard_content(Text) :- 1025 current_prolog_flag(gui, true), 1026 !, 1027 autoload_call(in_pce_thread_sync( 1028 autoload_call( 1029 get(@(display), paste, primary, string(Text))))). 1030clipboard_content(""). 1031 1032 1033 /******************************* 1034 * BRACKETED PASTE * 1035 *******************************/
The terminal is asked to enable bracketed paste mode (ESC[?2004h) from the C layer each time a prompt is issued.
1046bracketed_paste(Input, _Char, Continue) :-
1047 collect_paste(Input, [], RevCodes),
1048 reverse(RevCodes, Codes),
1049 string_codes(Text, Codes),
1050 el_insertstr(Input, Text),
1051 Continue = refresh.
1060collect_paste(Input, RevCodes, Result) :-
1061 el_getc(Input, Char),
1062 ( Char == -1 % EOF / error
1063 -> Result = RevCodes
1064 ; paste_char(Char, Char1),
1065 RevCodes1 = [Char1|RevCodes],
1066 ( RevCodes1 = [0'~,0'1,0'0,0'2,0'[,0'\e|Rest] % ESC[201~ reversed
1067 -> Result = Rest
1068 ; collect_paste(Input, RevCodes1, Result)
1069 )
1070 ).1079paste_char(0'\r, 0'\n) :- !. % CR → LF (INLCR maps \n to \r in edit mode) 1080paste_char(C, C). 1081 1082 1083 /******************************* 1084 * MESSAGE * 1085 *******************************/ 1086 1087:- multifile prolog:error_message//1. 1088 1089prologerror_message(editline(Msg)) --> 1090 [ 'editline: ~s'-[Msg] ]
BSD libedit based command line editing
This library wraps the BSD libedit command line editor. The binding provides a high level API to enable command line editing on the Prolog user streams and low level predicates to apply the library on other streams and program the library.
While reading a line, the binding surrounds the prompt and the input with the OSC 133 semantic prompt marks of FinalTerm:
ESC]133;Abefore the prompt,ESC]133;Bbefore the input,ESC]133;Cafter the line was entered andESC]133;Dwhere its output ends, which is just before the next line is asked for. A term that takes more than one line is read a line at a time; the prompt of each line after the first is markedESC]133;A;k=s, which says it continues an input rather than starting one, so that a terminal can keep the whole term together. A terminal that understands them (Epilog, iTerm2, kitty, WezTerm, VS Code) can tell prompt, input and output apart, which is what lets it jump between prompts, act on the output of one command or, in Epilog, move the caret to a click in the line being edited. A read that asks for a single character, as get_single_char/1 and the debugger do, is not marked: there is no line being edited there. A terminal that does not know the marks ignores them; one that prints them instead can be spared them with el_set/2 usingprompt_marks(false).