View source with formatted 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-2016, University of Amsterdam
   7                              VU University Amsterdam
   8                              CWI Amsterdam
   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('$history',
  38          [ read_history/6,
  39            '$save_history_line'/1,             % +Line
  40            '$clean_history'/0,
  41            '$load_history'/0,
  42            '$save_history_event'/1
  43          ]).
  44
  45%%  read_history(+History, +Help, +DontStore, +Prompt, -Term, -Bindings)
  46%
  47%   Give a prompt using Prompt. The   sequence  '%w' is substituted with
  48%   the current event number. Then read a term from the input stream and
  49%   perform the history expansion. Return  the   expanded  term  and the
  50%   bindings of the variables as with  read/2. entering the term History
  51%   makes read_history/5 print the  history.   Help  specifies  the help
  52%   command. DontStore is a list of events that need not be stored.
  53
  54%   When read_history reads a term of   the  form $silent(Goal), it will
  55%   call Goal and pretend it has not seen anything. This hook is used by
  56%   the GNU-Emacs interface to for   communication between GNU-EMACS and
  57%   SWI-Prolog.
  58
  59read_history(History, Help, DontStore, Prompt, Term, Bindings) :-
  60    repeat,
  61        prompt_history(Prompt),
  62        '$toplevel':read_query_line(user_input, Raw),
  63        read_history_(History, Help, DontStore, Raw, Term, Bindings),
  64    !.
  65
  66read_history_(History, _, _, History, _, _) :-
  67    list_history,
  68    !,
  69    fail.
  70read_history_(Show, Help, _, Help, _, _) :-
  71    print_message(help, history(help(Show, Help))),
  72    !,
  73    fail.
  74read_history_(History, Help, DontStore, Raw, Term, Bindings) :-
  75    expand_history(Raw, Expanded, Changed),
  76    '$save_history_line'(Expanded),
  77    '$current_typein_module'(TypeIn),
  78    catch(read_term_from_atom(Expanded, Term0,
  79                              [ variable_names(Bindings0),
  80                                module(TypeIn)
  81                              ]),
  82          E,
  83          (   print_message(error, E),
  84              fail
  85          )),
  86    (   var(Term0)
  87    ->  Term = Term0,
  88        Bindings = Bindings0
  89    ;   Term0 = '$silent'(Goal)
  90    ->  user:ignore(Goal),
  91        read_history(History, Help, DontStore, '', Term, Bindings)
  92    ;   save_event(DontStore, Expanded),
  93        (   Changed == true
  94        ->  print_message(query, history(expanded(Expanded)))
  95        ;   true
  96        ),
  97        Term = Term0,
  98        Bindings = Bindings0
  99    ).
 100
 101
 102%   list_history
 103%   Write history events to the current output stream.
 104
 105list_history :-
 106    (   '$history'(Last, _)
 107    ->  true
 108    ;   Last = 0
 109    ),
 110    history_depth_(Depth),
 111    plus(First, Depth, Last),
 112    findall(Nr/Event,
 113            (   between(First, Last, Nr),
 114                '$history'(Nr, Event)
 115            ),
 116            Events),
 117    print_message(query, history(history(Events))).
 118
 119'$clean_history' :-
 120    retractall('$history'(_,_)).
 121
 122%!  '$load_history' is det.
 123%
 124%   Load persistent history using a hook
 125
 126'$load_history' :-
 127    '$clean_history',
 128    current_prolog_flag(history, Depth),
 129    Depth > 0,
 130    catch(prolog:history(current_input, load), _, true), !.
 131'$load_history'.
 132
 133
 134%%   prompt_history(+Prompt)
 135%
 136%    Give prompt, substituting '~!' by the event number.
 137
 138prompt_history('') :-
 139    !,
 140    ttyflush.
 141prompt_history(Prompt) :-
 142    (   '$history'(Last, _)
 143    ->  This is Last + 1
 144    ;   This = 1
 145    ),
 146    atom_codes(Prompt, SP),
 147    atom_codes(This, ST),
 148    (   atom_codes('~!', Repl),
 149        substitute(Repl, ST, SP, String)
 150    ->  prompt1(String)
 151    ;   prompt1(Prompt)
 152    ),
 153    ttyflush.
 154
 155%   substitute(+Old, +New, +String, -Substituted)
 156%   substitute first occurence of Old in String by New
 157
 158substitute(Old, New, String, Substituted) :-
 159    '$append'(Head, OldAndTail, String),
 160    '$append'(Old, Tail, OldAndTail),
 161    !,
 162    '$append'(Head, New, HeadAndNew),
 163    '$append'(HeadAndNew, Tail, Substituted),
 164    !.
 165
 166%!  '$save_history_line'(+Line)
 167%
 168%   Add Line to the command line editing history.
 169
 170:- multifile
 171    prolog:history_line/2.
 172
 173'$save_history_line'(end_of_file) :- !.
 174'$save_history_line'(Line) :-
 175    format(string(CompleteLine), '~W~W',
 176           [ Line, [partial(true)],
 177             '.',  [partial(true)]
 178           ]),
 179    catch(prolog:history(user_input, add(CompleteLine)), _, fail),
 180    !.
 181'$save_history_line'(_).
 182
 183%!  save_event(+DoNotSave, +Event)
 184%
 185%   Save Event into the  history  system  if   it  is  not  a  member of
 186%   DoNotSave.
 187
 188save_event(Dont, Event) :-
 189    memberchk(Event, Dont),
 190    !.
 191save_event(_, Event) :-
 192    '$save_history_event'(Event).
 193
 194%!  '$save_history_event'(+Event) is det.
 195%
 196%   Save an input line as text into the !- based history. Event is one
 197%   of
 198%
 199%     * a *string*.  The event is added with a next number at the end.
 200%     * a *pair*.  The event is added with the given sequence number.
 201
 202:- thread_local
 203    '$history'/2.
 204
 205'$save_history_event'(Num-String) :-
 206    integer(Num), string(String),
 207    !,
 208    asserta('$history'(Num, String)),
 209    truncate_history(Num).
 210'$save_history_event'(Event) :-
 211    to_string(Event, Event1),
 212    !,
 213    last_event(Num, String),
 214    (   Event1 == String
 215    ->  true
 216    ;   New is Num + 1,
 217        asserta('$history'(New, Event1)),
 218        truncate_history(New)
 219    ).
 220'$save_history_event'(Event) :-
 221    '$type_error'(history_event, Event).
 222
 223last_event(Num, String) :-
 224    '$history'(Num, String),
 225    !.
 226last_event(0, "").
 227
 228to_string(String, String) :-
 229    string(String),
 230    !.
 231to_string(Atom, String) :-
 232    atom_string(Atom, String).
 233
 234truncate_history(New) :-
 235    history_depth_(Depth),
 236    remove_history(New, Depth).
 237
 238remove_history(New, Depth) :-
 239    New - Depth =< 0,
 240    !.
 241remove_history(New, Depth) :-
 242    Remove is New - Depth,
 243    retract('$history'(Remove, _)),
 244    !.
 245remove_history(_, _).
 246
 247%    history_depth_(-Depth)
 248%    Define the depth to which to keep the history.
 249
 250history_depth_(N) :-
 251    current_prolog_flag(history, N),
 252    integer(N),
 253    N > 0,
 254    !.
 255history_depth_(25).
 256
 257%    expand_history(+Raw, -Expanded)
 258%    Expand Raw using the available history list. Expandations performed
 259%    are:
 260%
 261%       !match          % Last event starting <match>
 262%       !n              % Event nr. <n>
 263%       !!              % last event
 264%
 265%    Note: the first character after a '!' should be a letter or number to
 266%    avoid problems with the cut.
 267
 268expand_history(Raw, Expanded, Changed) :-
 269    atom_chars(Raw, RawString),
 270    expand_history2(RawString, ExpandedString, Changed),
 271    atom_chars(Expanded, ExpandedString),
 272    !.
 273
 274expand_history2([!], [!], false) :- !.
 275expand_history2([!, C|Rest], [!|Expanded], Changed) :-
 276    not_event_char(C),
 277    !,
 278    expand_history2([C|Rest], Expanded, Changed).
 279expand_history2([!|Rest], Expanded, true) :-
 280    !,
 281    match_event(Rest, Event, NewRest),
 282    '$append'(Event, RestExpanded, Expanded),
 283    !,
 284    expand_history2(NewRest, RestExpanded, _).
 285expand_history2(['\''|In], ['\''|Out], Changed) :-
 286    !,
 287    skip_quoted(In, '\'', Out, Tin, Tout),
 288    expand_history2(Tin, Tout, Changed).
 289expand_history2(['"'|In], ['"'|Out], Changed) :-
 290    !,
 291    skip_quoted(In, '"', Out, Tin, Tout),
 292    expand_history2(Tin, Tout, Changed).
 293expand_history2([H|T], [H|R], Changed) :-
 294    !,
 295    expand_history2(T, R, Changed).
 296expand_history2([], [], false).
 297
 298skip_quoted([Q|T],Q,[Q|R], T, R) :- !.
 299skip_quoted([\,Q|T0],Q,[\,Q|T], In, Out) :-
 300    !,
 301    skip_quoted(T0, Q, T, In, Out).
 302skip_quoted([Q,Q|T0],Q,[Q,Q|T], In, Out) :-
 303    !,
 304    skip_quoted(T0, Q, T, In, Out).
 305skip_quoted([C|T0],Q,[C|T], In, Out) :-
 306    !,
 307    skip_quoted(T0, Q, T, In, Out).
 308skip_quoted([], _, [], [], []).
 309
 310%   get_last_event(-String)
 311%   return last event typed as a string
 312
 313get_last_event(Event) :-
 314    '$history'(_, Atom),
 315    atom_chars(Atom, Event),
 316    !.
 317get_last_event(_) :-
 318    print_message(query, history(no_event)),
 319    fail.
 320
 321%   match_event(+Spec, -Event, -Rest)
 322%   Use Spec as a specification of and event and return the event as Event
 323%   and what is left of Spec as Rest.
 324
 325match_event(Spec, Event, Rest) :-
 326    find_event(Spec, Event, Rest),
 327    !.
 328match_event(_, _, _) :-
 329    print_message(query, history(no_event)),
 330    fail.
 331
 332not_event_char(C) :- code_type(C, csym), !, fail.
 333not_event_char(!) :- !, fail.
 334not_event_char(_).
 335
 336find_event([!|Left], Event, Left) :-
 337    !,
 338    get_last_event(Event).
 339find_event([N|Rest], Event, Left) :-
 340    code_type(N, digit),
 341    !,
 342    take_number([N|Rest], String, Left),
 343    number_codes(Number, String),
 344    '$history'(Number, Atom),
 345    atom_chars(Atom, Event).
 346find_event(Spec, Event, Left) :-
 347    take_string(Spec, String, Left),
 348    matching_event(String, Event).
 349
 350take_string([C|Rest], [C|String], Left) :-
 351    code_type(C, csym),
 352    !,
 353    take_string(Rest, String, Left).
 354take_string([C|Rest], [], [C|Rest]) :- !.
 355take_string([], [], []).
 356
 357take_number([C|Rest], [C|String], Left) :-
 358    code_type(C, digit),
 359    !,
 360    take_string(Rest, String, Left).
 361take_number([C|Rest], [], [C|Rest]) :- !.
 362take_number([], [], []).
 363
 364%   matching_event(+String, -Event)
 365%
 366%   Return first event with prefix String as a Prolog string.
 367
 368matching_event(String, Event) :-
 369    '$history'(_, AtomEvent),
 370    atom_chars(AtomEvent, Event),
 371    '$append'(String, _, Event),
 372    !.