36
37:- module('$history',
38 [ read_history/6,
39 '$save_history_line'/1, 40 '$clean_history'/0,
41 '$load_history'/0,
42 '$save_history_event'/1
43 ]). 44
53
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
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
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
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
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
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
187
188save_event(Dont, Event) :-
189 memberchk(Event, Dont),
190 !.
191save_event(_, Event) :-
192 '$save_history_event'(Event).
193
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
249
250history_depth_(N) :-
251 current_prolog_flag(history, N),
252 integer(N),
253 N > 0,
254 !.
255history_depth_(25).
256
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
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
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
367
368matching_event(String, Event) :-
369 '$history'(_, AtomEvent),
370 atom_chars(AtomEvent, Event),
371 '$append'(String, _, Event),
372 !