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(+,+,+,3). 74 75:- multifile 76 el_setup/1, % +Input 77 prolog:complete_input/4.
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.
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.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). 134el_wrap(_). 135 136el_default(history(Size)) :- 137 current_prolog_flag(history, Value), 138 ( integer(Value), 139 Value >= 0 140 -> Size = Value 141 ; Value == false 142 -> Size = 0 143 ). 144:- if(current_predicate(prolog_alert_signal/2)). 145el_default(alert_signo(SigNo)) :- 146 prolog_alert_signal(SigName, SigName), 147 current_signal(SigName, SigNo, _Handler). 148:- endif. 149 150add_prolog_commands(Input) :- 151 el_addfn(Input, complete, 'Complete atoms and files', complete), 152 el_addfn(Input, show_completions, 'List completions', show_completions), 153 el_addfn(Input, electric, 'Indicate matching bracket', electric), 154 el_addfn(Input, isearch_history, 'Incremental search in history', 155 isearch_history), 156 el_addfn(Input, bracketed_paste, 'Handle bracketed paste', bracketed_paste), 157 el_bind(Input, ["^I", complete]), 158 el_bind(Input, ["^[?", show_completions]), 159 el_bind(Input, ["^R", isearch_history]), 160 bind_electric(Input), 161 add_paste_quoted(Input), 162 el_source(Input, _).
-v or -e.
173enable_bracketed_paste(Input) :-
174 ( el_get(Input, editor(vi))
175 -> el_bind(Input, ['-r', "\e[200~"]),
176 el_set(Input, bracketed_paste(false))
177 ; el_bind(Input, ["\e[200~", bracketed_paste]),
178 el_set(Input, bracketed_paste(true))
179 ).
194el_wrap(ProgName, In, Out, Error) :-
195 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.394:- multifile 395 prolog:history/2. 396 397prologhistory(Input, enabled) :- 398 !, 399 el_wrapped(Input), 400 el_history(Input, getsize(Size)), 401 Size > 0. 402prologhistory(Input, add(Line)) :- 403 !, 404 el_add_history(Input, Line). 405prologhistory(Input, load(File)) :- 406 !, 407 compat_read_history(Input, File). 408prologhistory(Input, save(File)) :- 409 !, 410 el_write_history(Input, File). 411prologhistory(Input, events(Events)) :- 412 !, 413 el_history_events(Input, Events). 414prologhistory(Input, Command) :- 415 public_command(Command), 416 !, 417 el_history(Input, Command). 418 419public_command(first(_Num, _String)). 420public_command(curr(_Num, _String)). 421public_command(event(_Num, _String)). 422public_command(prev_str(_Search, _Num, _String)). 423public_command(clear).
swipl-win.exe before migrating to SDL.430compat_read_history(Input, File) :- 431 catch(el_read_history(Input, File), error(editline(_),_), fail), 432 !. 433compat_read_history(Input, File) :- 434 access_file(File, read), 435 setup_call_cleanup( 436 open(File, read, In, [encoding(utf8)]), 437 read_old_history(Input, In), 438 close(In)), 439 !. 440compat_read_history(_, _). 441 442read_old_history(Input, From) :- 443 catch('$raw_read'(From, Line), error(_,_), fail), 444 ( Line == end_of_file 445 -> true 446 ; string_concat(Line, '.', Event), 447 el_add_history(Input, Event), 448 read_old_history(Input, From) 449 ). 450 451 /******************************* 452 * ELECTRIC CARET * 453 *******************************/
459bind_electric(Input) :- 460 forall(bracket(_Open, Close), bind_code(Input, Close, electric)), 461 forall(quote(Close), bind_code(Input, Close, electric)). 462 463bind_code(Input, Code, Command) :- 464 string_codes(Key, [Code]), 465 el_bind(Input, [Key, Command]).
470electric(Input, Char, Continue) :- 471 string_codes(Str, [Char]), 472 el_insertstr(Input, Str), 473 el_line(Input, line(Before, _)), 474 ( string_codes(Before, Codes), 475 nesting(Codes, 0, Nesting), 476 reverse(Nesting, [Close|RevNesting]) 477 -> ( Close = open(_,_) % open quote 478 -> Continue = refresh 479 ; matching_open(RevNesting, Close, _, Index) 480 -> string_length(Before, Len), % Proper match 481 Move is Index-Len, 482 Continue = electric(Move, 500, refresh) 483 ; Continue = refresh_beep % Not properly nested 484 ) 485 ; Continue = refresh_beep 486 ). 487 488matching_open_index(String, Index) :- 489 string_codes(String, Codes), 490 nesting(Codes, 0, Nesting), 491 reverse(Nesting, [Close|RevNesting]), 492 matching_open(RevNesting, Close, _, Index). 493 494matching_open([Open|Rest], Close, Rest, Index) :- 495 Open = open(Index,_), 496 match(Open, Close), 497 !. 498matching_open([Close1|Rest1], Close, Rest, Index) :- 499 Close1 = close(_,_), 500 matching_open(Rest1, Close1, Rest2, _), 501 matching_open(Rest2, Close, Rest, Index). 502 503match(open(_,Open),close(_,Close)) :- 504 ( bracket(Open, Close) 505 -> true 506 ; Open == Close, 507 quote(Open) 508 ). 509 510bracket(0'(, 0')). 511bracket(0'[, 0']). 512bracket(0'{, 0'}). 513 514quote(0'\'). 515quote(0'\"). 516quote(0'\`). 517 518nesting([], _, []). 519nesting([H|T], I, Nesting) :- 520 ( bracket(H, _Close) 521 -> Nesting = [open(I,H)|Nest] 522 ; bracket(_Open, H) 523 -> Nesting = [close(I,H)|Nest] 524 ), 525 !, 526 I2 is I+1, 527 nesting(T, I2, Nest). 528nesting([0'0, 0'\'|T], I, Nesting) :- 529 !, 530 phrase(skip_code, T, T1), 531 difflist_length(T, T1, Len), 532 I2 is I+Len+2, 533 nesting(T1, I2, Nesting). 534nesting([H|T], I, Nesting) :- 535 quote(H), 536 !, 537 ( phrase(skip_quoted(H), T, T1) 538 -> difflist_length(T, T1, Len), 539 I2 is I+Len+1, 540 Nesting = [open(I,H),close(I2,H)|Nest], 541 nesting(T1, I2, Nest) 542 ; Nesting = [open(I,H)] % Open quote 543 ). 544nesting([_|T], I, Nesting) :- 545 I2 is I+1, 546 nesting(T, I2, Nesting). 547 548difflist_length(List, Tail, Len) :- 549 difflist_length(List, Tail, 0, Len). 550 551difflist_length(List, Tail, Len0, Len) :- 552 List == Tail, 553 !, 554 Len = Len0. 555difflist_length([_|List], Tail, Len0, Len) :- 556 Len1 is Len0+1, 557 difflist_length(List, Tail, Len1, Len). 558 559skip_quoted(H) --> 560 [H], 561 !. 562skip_quoted(H) --> 563 "\\", [H], 564 !, 565 skip_quoted(H). 566skip_quoted(H) --> 567 [_], 568 skip_quoted(H). 569 570skip_code --> 571 "\\", [_], 572 !. 573skip_code --> 574 [_]. 575 576 577 /******************************* 578 * COMPLETION * 579 *******************************/
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.590:- dynamic 591 last_complete/2. 592 593complete(Input, _Char, Continue) :- 594 el_line(Input, line(Before, After)), 595 ensure_input_completion, 596 prolog:complete_input(Before, After, Delete, Completions), 597 ( Completions = [One] 598 -> string_length(Delete, Len), 599 el_deletestr(Input, Len), 600 complete_text(One, Text), 601 el_insertstr(Input, Text), 602 Continue = refresh 603 ; Completions == [] 604 -> Continue = refresh_beep 605 ; get_time(Now), 606 retract(last_complete(TLast, Before)), 607 Now - TLast < 2 608 -> nl(user_error), 609 list_alternatives(Completions), 610 Continue = redisplay 611 ; retractall(last_complete(_,_)), 612 get_time(Now), 613 asserta(last_complete(Now, Before)), 614 common_competion(Completions, Extend), 615 ( Delete == Extend 616 -> Continue = refresh_beep 617 ; string_length(Delete, Len), 618 el_deletestr(Input, Len), 619 el_insertstr(Input, Extend), 620 Continue = refresh 621 ) 622 ). 623 624:- dynamic 625 input_completion_loaded/0. 626 627ensure_input_completion :- 628 input_completion_loaded, 629 !. 630ensure_input_completion :- 631 predicate_property(prolog:complete_input(_,_,_,_), 632 number_of_clauses(N)), 633 N > 0, 634 !. 635ensure_input_completion :- 636 exists_source(library(console_input)), 637 !, 638 use_module(library(console_input), []), 639 asserta(input_completion_loaded). 640ensure_input_completion.
647show_completions(Input, _Char, Continue) :- 648 el_line(Input, line(Before, After)), 649 prolog:complete_input(Before, After, _Delete, Completions), 650 nl(user_error), 651 list_alternatives(Completions), 652 Continue = redisplay. 653 654complete_text(Text-_Comment, Text) :- !. 655complete_text(Text, Text).
661common_competion(Alternatives, Common) :- 662 maplist(atomic, Alternatives), 663 !, 664 common_prefix(Alternatives, Common). 665common_competion(Alternatives, Common) :- 666 maplist(complete_text, Alternatives, AltText), 667 !, 668 common_prefix(AltText, Common).
674common_prefix([A1|T], Common) :- 675 common_prefix_(T, A1, Common). 676 677common_prefix_([], Common, Common). 678common_prefix_([H|T], Common0, Common) :- 679 common_prefix(H, Common0, Common1), 680 common_prefix_(T, Common1, Common).
686common_prefix(A1, A2, Prefix) :- 687 sub_atom(A1, 0, _, _, A2), 688 !, 689 Prefix = A2. 690common_prefix(A1, A2, Prefix) :- 691 sub_atom(A2, 0, _, _, A1), 692 !, 693 Prefix = A1. 694common_prefix(A1, A2, Prefix) :- 695 atom_codes(A1, C1), 696 atom_codes(A2, C2), 697 list_common_prefix(C1, C2, C), 698 string_codes(Prefix, C). 699 700list_common_prefix([H|T0], [H|T1], [H|T]) :- 701 !, 702 list_common_prefix(T0, T1, T). 703list_common_prefix(_, _, []).
713list_alternatives(Alternatives) :- 714 maplist(atomic, Alternatives), 715 !, 716 length(Alternatives, Count), 717 maplist(atom_length, Alternatives, Lengths), 718 max_list(Lengths, Max), 719 tty_size(_, Cols), 720 ColW is Max+2, 721 Columns is max(1, Cols // ColW), 722 RowCount is (Count+Columns-1)//Columns, 723 length(Rows, RowCount), 724 to_matrix(Alternatives, Rows, Rows), 725 ( RowCount > 11 726 -> length(First, 10), 727 Skipped is RowCount - 10, 728 append(First, _, Rows), 729 maplist(write_row(ColW), First), 730 format(user_error, '... skipped ~D rows~n', [Skipped]) 731 ; maplist(write_row(ColW), Rows) 732 ). 733list_alternatives(Alternatives) :- 734 maplist(complete_text, Alternatives, AltText), 735 list_alternatives(AltText). 736 737to_matrix([], _, Rows) :- 738 !, 739 maplist(close_list, Rows). 740to_matrix([H|T], [RH|RT], Rows) :- 741 !, 742 add_list(RH, H), 743 to_matrix(T, RT, Rows). 744to_matrix(List, [], Rows) :- 745 to_matrix(List, Rows, Rows). 746 747add_list(Var, Elem) :- 748 var(Var), !, 749 Var = [Elem|_]. 750add_list([_|T], Elem) :- 751 add_list(T, Elem). 752 753close_list(List) :- 754 append(List, [], _), 755 !. 756 757write_row(ColW, Row) :- 758 length(Row, Columns), 759 make_format(Columns, ColW, Format), 760 format(user_error, Format, Row). 761 762make_format(N, ColW, Format) :- 763 format(string(PerCol), '~~w~~t~~~d+', [ColW]), 764 Front is N - 1, 765 length(LF, Front), 766 maplist(=(PerCol), LF), 767 append(LF, ['~w~n'], Parts), 768 atomics_to_string(Parts, Format). 769 770 771 /******************************* 772 * SEARCH * 773 *******************************/
780isearch_history(Input, _Char, Continue) :- 781 el_line(Input, line(Before, After)), 782 string_concat(Before, After, Current), 783 string_length(Current, Len), 784 search_print('', "", Current), 785 search(Input, "", Current, 1, Line), 786 el_deletestr(Input, Len), 787 el_insertstr(Input, Line), 788 Continue = redisplay. 789 790search(Input, For, Current, Nth, Line) :- 791 el_getc(Input, Next), 792 Next \== -1, 793 !, 794 search(Next, Input, For, Current, Nth, Line). 795search(_Input, _For, _Current, _Nth, ""). 796 797search(7, _Input, _, Current, _, Current) :- % C-g: abort 798 !, 799 clear_line. 800search(18, Input, For, Current, Nth, Line) :- % C-r: search previous 801 !, 802 N2 is Nth+1, 803 search_(Input, For, Current, N2, Line). 804search(19, Input, For, Current, Nth, Line) :- % C-s: search next 805 !, 806 N2 is max(1,Nth-1), 807 search_(Input, For, Current, N2, Line). 808search(127, Input, For, Current, _Nth, Line) :- % DEL/BS: shorten search 809 sub_string(For, 0, _, 1, For1), 810 !, 811 search_(Input, For1, Current, 1, Line). 812search(Char, Input, For, Current, Nth, Line) :- 813 code_type(Char, cntrl), 814 !, 815 search_end(Input, For, Current, Nth, Line), 816 el_push(Input, Char). 817search(Char, Input, For, Current, _Nth, Line) :- 818 format(string(For1), '~w~c', [For,Char]), 819 search_(Input, For1, Current, 1, Line). 820 821search_(Input, For1, Current, Nth, Line) :- 822 ( find_in_history(Input, For1, Current, Nth, Candidate) 823 -> search_print('', For1, Candidate) 824 ; search_print('failed ', For1, Current) 825 ), 826 search(Input, For1, Current, Nth, Line). 827 828search_end(Input, For, Current, Nth, Line) :- 829 ( find_in_history(Input, For, Current, Nth, Line) 830 -> true 831 ; Line = Current 832 ), 833 clear_line. 834 835find_in_history(_, "", Current, _, Current) :- 836 !. 837find_in_history(Input, For, _, Nth, Line) :- 838 el_history_events(Input, History), 839 call_nth(( member(_N-Line, History), 840 sub_string(Line, _, _, _, For) 841 ), 842 Nth), 843 !. 844 845search_print(State, Search, Current) :- 846 format(user_error, '\r(~wreverse-i-search)`~w\': ~w\e[0K', 847 [State, Search, Current]). 848 849clear_line :- 850 format(user_error, '\r\e[0K', []). 851 852 853 /******************************* 854 * PASTE QUOTED * 855 *******************************/ 856 857:- meta_predicate 858 with_quote_flags(+,+,0). 859 860add_paste_quoted(Input) :- 861 current_prolog_flag(gui, true), 862 !, 863 el_addfn(Input, paste_quoted, 'Paste as quoted atom', paste_quoted), 864 el_bind(Input, ["^Y", paste_quoted]). 865add_paste_quoted(_).
873paste_quoted(Input, _Char, Continue) :- 874 clipboard_content(String), 875 quote_text(Input, String, Quoted), 876 el_insertstr(Input, Quoted), 877 Continue = refresh. 878 879quote_text(Input, String, Value) :- 880 el_line(Input, line(Before, _After)), 881 ( sub_string(Before, _, 1, 0, Quote) 882 -> true 883 ; Quote = "'" 884 ), 885 quote_text(Input, Quote, String, Value). 886 887quote_text(Input, "'", Text, Quoted) => 888 format(string(Quoted), '~q', [Text]), 889 el_deletestr(Input, 1). 890quote_text(Input, "\"", Text, Quoted) => 891 atom_string(Text, String), 892 with_quote_flags( 893 string, codes, 894 format(string(Quoted), '~q', [String])), 895 el_deletestr(Input, 1). 896quote_text(Input, "`", Text, Quoted) => 897 atom_string(Text, String), 898 with_quote_flags( 899 codes, string, 900 format(string(Quoted), '~q', [String])), 901 el_deletestr(Input, 1). 902quote_text(_, _, Text, Quoted) => 903 format(string(Quoted), '~q', [Text]). 904 905with_quote_flags(Double, Back, Goal) :- 906 current_prolog_flag(double_quotes, ODouble), 907 current_prolog_flag(back_quotes, OBack), 908 setup_call_cleanup( 909 ( set_prolog_flag(double_quotes, Double), 910 set_prolog_flag(back_quotes, Back) ), 911 Goal, 912 ( set_prolog_flag(double_quotes, ODouble), 913 set_prolog_flag(back_quotes, OBack) )). 914 915clipboard_content(Text) :- 916 current_prolog_flag(gui, true), 917 !, 918 autoload_call(in_pce_thread_sync( 919 autoload_call( 920 get(@(display), paste, primary, string(Text))))). 921clipboard_content(""). 922 923 924 /******************************* 925 * BRACKETED PASTE * 926 *******************************/
The terminal is asked to enable bracketed paste mode (ESC[?2004h) from the C layer each time a prompt is issued.
937bracketed_paste(Input, _Char, Continue) :-
938 collect_paste(Input, [], RevCodes),
939 reverse(RevCodes, Codes),
940 string_codes(Text, Codes),
941 el_insertstr(Input, Text),
942 Continue = refresh.
951collect_paste(Input, RevCodes, Result) :-
952 el_getc(Input, Char),
953 ( Char == -1 % EOF / error
954 -> Result = RevCodes
955 ; paste_char(Char, Char1),
956 RevCodes1 = [Char1|RevCodes],
957 ( RevCodes1 = [0'~,0'1,0'0,0'2,0'[,0'\e|Rest] % ESC[201~ reversed
958 -> Result = Rest
959 ; collect_paste(Input, RevCodes1, Result)
960 )
961 ).970paste_char(0'\r, 0'\n) :- !. % CR → LF (INLCR maps \n to \r in edit mode) 971paste_char(C, C). 972 973 974 /******************************* 975 * MESSAGE * 976 *******************************/ 977 978:- multifile prolog:error_message//1. 979 980prologerror_message(editline(Msg)) --> 981 [ '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. */