View source with raw comments or as raw
    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)  1985-2025, University of Amsterdam
    7                              VU University Amsterdam
    8                              CWI Amsterdam
    9                              SWI-Prolog Solutions b.v.
   10    All rights reserved.
   11
   12    Redistribution and use in source and binary forms, with or without
   13    modification, are permitted provided that the following conditions
   14    are met:
   15
   16    1. Redistributions of source code must retain the above copyright
   17       notice, this list of conditions and the following disclaimer.
   18
   19    2. Redistributions in binary form must reproduce the above copyright
   20       notice, this list of conditions and the following disclaimer in
   21       the documentation and/or other materials provided with the
   22       distribution.
   23
   24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   25    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   26    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   27    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   28    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   29    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   30    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   31    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   32    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   33    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   34    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   35    POSSIBILITY OF SUCH DAMAGE.
   36*/
   37
   38:- module('$history',
   39          [ read_term_with_history/2,           % -Term, +Line
   40            '$save_history_line'/1,             % +Line
   41            '$clean_history'/0,
   42            '$load_history'/0,
   43            '$save_history_event'/1
   44          ]).
 read_term_with_history(-Term, +Options)
Read a term guide by Options and maintain a history similar to most Unix shells.

When read_history reads a term of the form $silent(Goal), it will call Goal and pretend it has not seen anything. This hook is used by the GNU-Emacs interface to for communication between GNU-EMACS and SWI-Prolog.

   56read_term_with_history(Term, Options) :-
   57    '$option'(prompt(Prompt), Options, '~! ?-'),
   58    '$option'(input(Input), Options, user_input),
   59    repeat,
   60        prompt_history(Prompt),
   61        '$toplevel':read_query_line(Input, Raw),
   62        read_history_(Raw, Term, Options),
   63    !.
   64
   65read_history_(Raw, _Term, Options) :-
   66    '$option'(show(Raw), Options, history),
   67    list_history,
   68    !,
   69    fail.
   70read_history_(Raw, _Term, Options) :-
   71    '$option'(help(Raw), Options, '!help'),
   72    '$option'(show(Show), Options, '!history'),
   73    print_message(help, history(help(Show, Raw))),
   74    !,
   75    fail.
   76read_history_(Raw, Term, Options) :-
   77    expand_history(Raw, Expanded, Changed),
   78    '$save_history_line'(Expanded),
   79    '$option'(module(Module), Options, Var),
   80    (   Module == Var
   81    ->  '$current_typein_module'(Module)
   82    ;   true
   83    ),
   84    '$option'(variable_names(Bindings), Options, Bindings0),
   85    catch(read_term_from_atom(Expanded, Term0,
   86                              [ module(Module),
   87                                variable_names(Bindings0)
   88                              ]),
   89          E,
   90          (   print_message(error, E),
   91              fail
   92          )),
   93    (   var(Term0)
   94    ->  Term = Term0,
   95        Bindings = Bindings0
   96    ;   Term0 = '$silent'(Goal)
   97    ->  user:ignore(Goal),
   98        read_term_with_history(Term, Options)
   99    ;   save_event(Expanded, Options),
  100        (   Changed == true
  101        ->  print_message(query, history(expanded(Expanded)))
  102        ;   true
  103        ),
  104        Term = Term0,
  105        Bindings = Bindings0
  106    ).
 list_history
Write recorded history events using print_message/2.
  112list_history :-
  113    (   '$history'(Last, _)
  114    ->  true
  115    ;   Last = 0
  116    ),
  117    history_depth_(Depth),
  118    plus(First, Depth, Last),
  119    findall(Nr/Event,
  120            (   between(First, Last, Nr),
  121                '$history'(Nr, Event)
  122            ),
  123            Events),
  124    print_message(query, history(history(Events))).
  125
  126'$clean_history' :-
  127    retractall('$history'(_,_)).
 $load_history is det
Load persistent history using a hook
  133'$load_history' :-
  134    '$clean_history',
  135    current_prolog_flag(history, Depth),
  136    Depth > 0,
  137    catch(prolog:history(current_input, load), _, true), !.
  138'$load_history'.
 prompt_history(+Prompt)
Give prompt, substituting '~!' by the event number.
  145prompt_history('') :-
  146    !,
  147    ttyflush.
  148prompt_history(Prompt) :-
  149    (   '$history'(Last, _)
  150    ->  This is Last + 1
  151    ;   This = 1
  152    ),
  153    atom_codes(Prompt, SP),
  154    atom_codes(This, ST),
  155    (   atom_codes('~!', Repl),
  156        substitute(Repl, ST, SP, String)
  157    ->  prompt1(String)
  158    ;   prompt1(Prompt)
  159    ),
  160    ttyflush.
  161
  162%   substitute(+Old, +New, +String, -Substituted)
  163%   substitute first occurence of Old in String by New
  164
  165substitute(Old, New, String, Substituted) :-
  166    '$append'(Head, OldAndTail, String),
  167    '$append'(Old, Tail, OldAndTail),
  168    !,
  169    '$append'(Head, New, HeadAndNew),
  170    '$append'(HeadAndNew, Tail, Substituted),
  171    !.
 $save_history_line(+Line)
Add Line to the command line editing history.
  177:- multifile
  178    prolog:history_line/2.  179
  180'$save_history_line'(end_of_file) :- !.
  181'$save_history_line'(Line) :-
  182    format(string(CompleteLine), '~W~W',
  183           [ Line, [partial(true)],
  184             '.',  [partial(true)]
  185           ]),
  186    catch(prolog:history(user_input, add(CompleteLine)), _, fail),
  187    !.
  188'$save_history_line'(_).
 save_event(+Event, +Options)
Save Event into the history system unless it appears in the option no_save.
  195save_event(Event, Options) :-
  196    '$option'(no_save(Dont), Options),
  197    memberchk(Event, Dont),
  198    !.
  199save_event(Event, _) :-
  200    '$save_history_event'(Event).
 $save_history_event(+Event) is det
Save an input line as text into the !- based history. Event is one of
  210:- thread_local
  211    '$history'/2.  212
  213'$save_history_event'(Num-String) :-
  214    integer(Num), string(String),
  215    !,
  216    asserta('$history'(Num, String)),
  217    truncate_history(Num).
  218'$save_history_event'(Event) :-
  219    to_string(Event, Event1),
  220    !,
  221    last_event(Num, String),
  222    (   Event1 == String
  223    ->  true
  224    ;   New is Num + 1,
  225        asserta('$history'(New, Event1)),
  226        truncate_history(New)
  227    ).
  228'$save_history_event'(Event) :-
  229    '$type_error'(history_event, Event).
  230
  231last_event(Num, String) :-
  232    '$history'(Num, String),
  233    !.
  234last_event(0, "").
  235
  236to_string(String, String) :-
  237    string(String),
  238    !.
  239to_string(Atom, String) :-
  240    atom_string(Atom, String).
  241
  242truncate_history(New) :-
  243    history_depth_(Depth),
  244    remove_history(New, Depth).
  245
  246remove_history(New, Depth) :-
  247    New - Depth =< 0,
  248    !.
  249remove_history(New, Depth) :-
  250    Remove is New - Depth,
  251    retract('$history'(Remove, _)),
  252    !.
  253remove_history(_, _).
  254
  255%    history_depth_(-Depth)
  256%    Define the depth to which to keep the history.
  257
  258history_depth_(N) :-
  259    current_prolog_flag(history, N),
  260    integer(N),
  261    N > 0,
  262    !.
  263history_depth_(25).
  264
  265%    expand_history(+Raw, -Expanded)
  266%    Expand Raw using the available history list. Expandations performed
  267%    are:
  268%
  269%       !match          % Last event starting <match>
  270%       !n              % Event nr. <n>
  271%       !!              % last event
  272%
  273%    Note: the first character after a '!' should be a letter or number to
  274%    avoid problems with the cut.
  275
  276expand_history(Raw, Expanded, Changed) :-
  277    atom_chars(Raw, RawString),
  278    expand_history2(RawString, ExpandedString, Changed),
  279    atom_chars(Expanded, ExpandedString),
  280    !.
  281
  282expand_history2([!], [!], false) :- !.
  283expand_history2([!, C|Rest], [!|Expanded], Changed) :-
  284    not_event_char(C),
  285    !,
  286    expand_history2([C|Rest], Expanded, Changed).
  287expand_history2([!|Rest], Expanded, true) :-
  288    !,
  289    match_event(Rest, Event, NewRest),
  290    '$append'(Event, RestExpanded, Expanded),
  291    !,
  292    expand_history2(NewRest, RestExpanded, _).
  293expand_history2(['\''|In], ['\''|Out], Changed) :-
  294    !,
  295    skip_quoted(In, '\'', Out, Tin, Tout),
  296    expand_history2(Tin, Tout, Changed).
  297expand_history2(['"'|In], ['"'|Out], Changed) :-
  298    !,
  299    skip_quoted(In, '"', Out, Tin, Tout),
  300    expand_history2(Tin, Tout, Changed).
  301expand_history2([H|T], [H|R], Changed) :-
  302    !,
  303    expand_history2(T, R, Changed).
  304expand_history2([], [], false).
  305
  306skip_quoted([Q|T],Q,[Q|R], T, R) :- !.
  307skip_quoted([\,Q|T0],Q,[\,Q|T], In, Out) :-
  308    !,
  309    skip_quoted(T0, Q, T, In, Out).
  310skip_quoted([Q,Q|T0],Q,[Q,Q|T], In, Out) :-
  311    !,
  312    skip_quoted(T0, Q, T, In, Out).
  313skip_quoted([C|T0],Q,[C|T], In, Out) :-
  314    !,
  315    skip_quoted(T0, Q, T, In, Out).
  316skip_quoted([], _, [], [], []).
  317
  318%   get_last_event(-String)
  319%   return last event typed as a string
  320
  321get_last_event(Event) :-
  322    '$history'(_, Atom),
  323    atom_chars(Atom, Event),
  324    !.
  325get_last_event(_) :-
  326    print_message(query, history(no_event)),
  327    fail.
  328
  329%   match_event(+Spec, -Event, -Rest)
  330%   Use Spec as a specification of and event and return the event as Event
  331%   and what is left of Spec as Rest.
  332
  333match_event(Spec, Event, Rest) :-
  334    find_event(Spec, Event, Rest),
  335    !.
  336match_event(_, _, _) :-
  337    print_message(query, history(no_event)),
  338    fail.
  339
  340not_event_char(C) :- code_type(C, csym), !, fail.
  341not_event_char(!) :- !, fail.
  342not_event_char(_).
  343
  344find_event([!|Left], Event, Left) :-
  345    !,
  346    get_last_event(Event).
  347find_event([N|Rest], Event, Left) :-
  348    code_type(N, digit),
  349    !,
  350    take_number([N|Rest], String, Left),
  351    number_codes(Number, String),
  352    '$history'(Number, Atom),
  353    atom_chars(Atom, Event).
  354find_event(Spec, Event, Left) :-
  355    take_string(Spec, String, Left),
  356    matching_event(String, Event).
  357
  358take_string([C|Rest], [C|String], Left) :-
  359    code_type(C, csym),
  360    !,
  361    take_string(Rest, String, Left).
  362take_string([C|Rest], [], [C|Rest]) :- !.
  363take_string([], [], []).
  364
  365take_number([C|Rest], [C|String], Left) :-
  366    code_type(C, digit),
  367    !,
  368    take_string(Rest, String, Left).
  369take_number([C|Rest], [], [C|Rest]) :- !.
  370take_number([], [], []).
  371
  372%   matching_event(+String, -Event)
  373%
  374%   Return first event with prefix String as a Prolog string.
  375
  376matching_event(String, Event) :-
  377    '$history'(_, AtomEvent),
  378    atom_chars(AtomEvent, Event),
  379    '$append'(String, _, Event),
  380    !