37
38:- module('$history',
39 [ read_term_with_history/2, 40 '$save_history_line'/1, 41 '$clean_history'/0,
42 '$load_history'/0,
43 '$save_history_event'/1
44 ]). 45
55
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 ).
107
111
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'(_,_)).
128
132
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'.
139
140
144
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
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 !.
172
176
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'(_).
189
194
195save_event(Event, Options) :-
196 '$option'(no_save(Dont), Options),
197 memberchk(Event, Dont),
198 !.
199save_event(Event, _) :-
200 '$save_history_event'(Event).
201
209
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
257
258history_depth_(N) :-
259 current_prolog_flag(history, N),
260 integer(N),
261 N > 0,
262 !.
263history_depth_(25).
264
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
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
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
375
376matching_event(String, Event) :-
377 '$history'(_, AtomEvent),
378 atom_chars(AtomEvent, Event),
379 '$append'(String, _, Event),
380 !