35
36:- module(prolog_help,
37 [ help/0,
38 help/1, 39 apropos/1 40 ]). 41:- use_module(library(pldoc), []). 42:- use_module(library(isub), [isub/4]). 43
44:- autoload(library(apply), [maplist/3]). 45:- autoload(library(error), [must_be/2]). 46:- autoload(library(lists), [append/3, sum_list/2]). 47:- autoload(library(pairs), [pairs_values/2]). 48:- autoload(library(porter_stem), [tokenize_atom/2]). 49:- autoload(library(process), [process_create/3]). 50:- autoload(library(sgml), [load_html/3]). 51:- autoload(library(solution_sequences), [distinct/1]). 52:- autoload(library(http/html_write), [html/3, print_html/1]). 53:- autoload(library(lynx/html_text), [html_text/2]). 54:- autoload(pldoc(doc_man), [man_page/4]). 55:- autoload(pldoc(doc_modes), [mode/2]). 56:- autoload(pldoc(doc_words), [doc_related_word/3]). 57:- autoload(pldoc(man_index), [man_object_property/2, doc_object_identifier/2]). 58:- autoload(library(prolog_code), [pi_head/2]). 59:- autoload(library(prolog_xref), [xref_source/2]). 60
61:- use_module(library(lynx/pldoc_style), []). 62
87
88:- meta_predicate
89 with_pager(0). 90
91:- multifile
92 show_html_hook/1. 93
96:- create_prolog_flag(help_pager, default,
97 [ type(term),
98 keep(true)
99 ]). 100
134
135help :-
136 notrace(show_matches([help/1, apropos/1], exact-help)).
137
138help(What) :-
139 notrace(help_no_trace(What)).
140
141help_no_trace(What) :-
142 help_objects_how(What, Matches, How),
143 !,
144 show_matches(Matches, How-What).
145help_no_trace(What) :-
146 print_message(warning, help(not_found(What))).
147
148show_matches(Matches, HowWhat) :-
149 help_html(Matches, HowWhat, HTML),
150 !,
151 show_html(HTML).
152
158
159show_html(HTML) :-
160 show_html_hook(HTML),
161 !.
162show_html(HTML) :-
163 setup_call_cleanup(
164 open_string(HTML, In),
165 load_html(stream(In), DOM, []),
166 close(In)),
167 page_width(PageWidth),
168 LineWidth is PageWidth - 4,
169 with_pager(html_text(DOM, [width(LineWidth)])).
170
171help_html(Matches, How, HTML) :-
172 phrase(html(html([ head([]),
173 body([ \match_type(How),
174 dl(\man_pages(Matches,
175 [ no_manual(fail),
176 links(false),
177 link_source(false),
178 navtree(false),
179 server(false),
180 qualified(always)
181 ]))
182 ])
183 ])),
184 Tokens),
185 !,
186 with_output_to(string(HTML),
187 print_html(Tokens)).
188
189match_type(exact-_) -->
190 [].
191match_type(dwim-For) -->
192 html(p(class(warning),
193 [ 'WARNING: No matches for "', span(class('help-query'), For),
194 '" Showing closely related results'
195 ])).
196
197man_pages([], _) -->
198 [].
199man_pages([H|T], Options) -->
200 ( man_page(H, Options)
201 -> []
202 ; html(p(class(warning),
203 [ 'WARNING: No help for ~p'-[H]
204 ]))
205 ),
206 man_pages(T, Options).
207
208page_width(Width) :-
209 tty_width(W),
210 Width is min(100,max(50,W)).
211
216
217tty_width(W) :-
218 \+ running_under_emacs,
219 catch(tty_size(_, W), _, fail),
220 !.
221tty_width(80).
222
223help_objects_how(Spec, Objects, exact) :-
224 help_objects(Spec, exact, Objects),
225 !.
226help_objects_how(Spec, Objects, dwim) :-
227 help_objects(Spec, dwim, Objects),
228 !.
229
230help_objects(Spec, How, Objects) :-
231 findall(ID-Obj, help_object(Spec, How, Obj, ID), Objects0),
232 Objects0 \== [],
233 sort(1, @>, Objects0, Objects1),
234 pairs_values(Objects1, Objects2),
235 sort(Objects2, Objects).
236
237help_object(Fuzzy/Arity, How, Name/Arity, ID) :-
238 match_name(How, Fuzzy, Name),
239 man_object_property(Name/Arity, id(ID)).
240help_object(Fuzzy//Arity, How, Name//Arity, ID) :-
241 match_name(How, Fuzzy, Name),
242 man_object_property(Name//Arity, id(ID)).
243help_object(Fuzzy/Arity, How, f(Name/Arity), ID) :-
244 match_name(How, Fuzzy, Name),
245 man_object_property(f(Name/Arity), id(ID)).
246help_object(Fuzzy, How, Name/Arity, ID) :-
247 atom(Fuzzy),
248 match_name(How, Fuzzy, Name),
249 man_object_property(Name/Arity, id(ID)).
250help_object(Fuzzy, How, Name//Arity, ID) :-
251 atom(Fuzzy),
252 match_name(How, Fuzzy, Name),
253 man_object_property(Name//Arity, id(ID)).
254help_object(Fuzzy, How, f(Name/Arity), ID) :-
255 atom(Fuzzy),
256 match_name(How, Fuzzy, Name),
257 man_object_property(f(Name/Arity), id(ID)).
258help_object(Fuzzy, How, c(Name), ID) :-
259 atom(Fuzzy),
260 match_name(How, Fuzzy, Name),
261 man_object_property(c(Name), id(ID)).
262help_object(SecID, _How, section(Label), ID) :-
263 atom(SecID),
264 ( atom_concat('sec:', SecID, Label)
265 ; sub_atom(SecID, _, _, 0, '.html'),
266 Label = SecID
267 ),
268 man_object_property(section(_Level,_Num,Label,_File), id(ID)).
269help_object(Func, How, c(Name), ID) :-
270 compound(Func),
271 compound_name_arity(Func, Fuzzy, 0),
272 match_name(How, Fuzzy, Name),
273 man_object_property(c(Name), id(ID)).
275help_object(Module, _How, Module:Name/Arity, _ID) :-
276 atom(Module),
277 current_module(Module),
278 atom_concat('sec:', Module, SecLabel),
279 \+ man_object_property(section(_,_,SecLabel,_), _), 280 current_predicate_help(Module:Name/Arity).
281help_object(Name/Arity, _How, Name/Arity, _ID) :-
282 atom(Name),
283 current_predicate_help(_:Name/Arity).
284help_object(Fuzzy, How, Module:Name/Arity, _ID) :-
285 atom(Fuzzy),
286 match_name(How, Fuzzy, Name),
287 current_predicate_help(Module:Name/Arity).
288
295
296current_predicate_help(M:Name/Arity) :-
297 current_predicate(M:Name/Arity),
298 pi_head(Name/Arity,Head),
299 \+ predicate_property(M:Head, imported_from(_)),
300 \+ man_object_property(Name/Arity, _), 301 ( mode(M:_, _) 302 -> true
303 ; \+ module_property(M, class(system)),
304 predicate_property(M:Head,file(File)),
305 xref_source(File,[comments(store)])
306 ),
307 mode(M:Head, _). 308
309match_name(exact, Name, Name).
310match_name(dwim, Name, Fuzzy) :-
311 freeze(Fuzzy, dwim_match(Fuzzy, Name)).
312
313
318
(Goal) :-
320 pager_ok(Pager, Options),
321 !,
322 Catch = error(io_error(_,_), _),
323 current_output(OldIn),
324 setup_call_cleanup(
325 process_create(Pager, Options,
326 [stdin(pipe(In))]),
327 ( set_stream(In, tty(true)),
328 set_output(In),
329 catch(Goal, Catch, true)
330 ),
331 ( set_output(OldIn),
332 close(In, [force(true)])
333 )).
334with_pager(Goal) :-
335 call(Goal).
336
(_Path, _Options) :-
338 current_prolog_flag(help_pager, false),
339 !,
340 fail.
341pager_ok(Path, Options) :-
342 current_prolog_flag(help_pager, default),
343 !,
344 stream_property(current_output, tty(true)),
345 \+ running_under_emacs,
346 ( distinct(( getenv('PAGER', Pager)
347 ; Pager = less
348 )),
349 absolute_file_name(path(Pager), Path,
350 [ access(execute),
351 file_errors(fail)
352 ])
353 -> pager_options(Path, Options)
354 ).
355pager_ok(Path, Options) :-
356 current_prolog_flag(help_pager, Term),
357 callable(Term),
358 compound_name_arguments(Term, Pager, Options),
359 absolute_file_name(path(Pager), Path,
360 [ access(execute),
361 file_errors(fail)
362 ]).
363
(Path, Options) :-
365 file_base_name(Path, File),
366 file_name_extension(Base, _, File),
367 downcase_atom(Base, Id),
368 pager_default_options(Id, Options).
369
(less, ['-r']).
371
372
377
378running_under_emacs :-
379 current_prolog_flag(emacs_inferior_process, true),
380 !.
381running_under_emacs :-
382 getenv('TERM', dumb),
383 !.
384running_under_emacs :-
385 current_prolog_flag(toplevel_prompt, P),
386 sub_atom(P, _, _, _, 'ediprolog'),
387 !.
388
389
411
412apropos(Query) :-
413 notrace(apropos_no_trace(Query)).
414
415apropos_no_trace(Query) :-
416 findall(Q-(Obj-Summary), apropos(Query, Obj, Summary, Q), Pairs),
417 ( Pairs == []
418 -> print_message(warning, help(no_apropos_match(Query)))
419 ; sort(1, >=, Pairs, Sorted),
420 length(Sorted, Len),
421 ( Len > 20
422 -> length(Truncated, 20),
423 append(Truncated, _, Sorted)
424 ; Truncated = Sorted
425 ),
426 pairs_values(Truncated, Matches),
427 print_message(information, help(apropos_matches(Matches, Len)))
428 ).
429
430apropos(Query, Obj, Summary, Q) :-
431 parse_query(Query, Type, Words),
432 man_object_property(Obj, summary(Summary)),
433 apropos_match(Type, Words, Obj, Summary, Q).
434
435parse_query(Type:String, Type, Words) :-
436 !,
437 must_be(atom, Type),
438 must_be(text, String),
439 tokenize_atom(String, Words).
440parse_query(String, _Type, Words) :-
441 must_be(text, String),
442 tokenize_atom(String, Words).
443
444apropos_match(Type, Query, Object, Summary, Q) :-
445 maplist(amatch(Object, Summary), Query, Scores),
446 match_object_type(Type, Object),
447 sum_list(Scores, Q).
448
449amatch(Object, Summary, Query, Score) :-
450 ( doc_object_identifier(Object, String)
451 ; String = Summary
452 ),
453 amatch(Query, String, Score),
454 !.
455
456amatch(Query, To, Quality) :-
457 doc_related_word(Query, Related, Distance),
458 sub_atom_icasechk(To, _, Related),
459 isub(Related, To, false, Quality0),
460 Quality is Quality0*Distance.
461
462match_object_type(Type, _Object) :-
463 var(Type),
464 !.
465match_object_type(Type, Object) :-
466 downcase_atom(Type, LType),
467 object_class(Object, Class),
468 match_object_class(LType, Class).
469
470match_object_class(Type, Class) :-
471 ( TheClass = Class
472 ; class_alias(Class, TheClass)
473 ),
474 sub_atom(TheClass, 0, _, _, Type),
475 !.
476
477class_alias(section, chapter).
478class_alias(function, arithmetic).
479class_alias(cfunction, c_function).
480class_alias(iso_predicate, predicate).
481class_alias(swi_builtin_predicate, predicate).
482class_alias(library_predicate, predicate).
483class_alias(dcg, predicate).
484class_alias(dcg, nonterminal).
485class_alias(dcg, non_terminal).
486
487class_tag(section, 'SEC').
488class_tag(function, ' F').
489class_tag(iso_predicate, 'ISO').
490class_tag(swi_builtin_predicate, 'SWI').
491class_tag(library_predicate, 'LIB').
492class_tag(dcg, 'DCG').
493
494object_class(section(_Level, _Num, _Label, _File), section).
495object_class(c(_Name), cfunction).
496object_class(f(_Name/_Arity), function).
497object_class(Name/Arity, Type) :-
498 functor(Term, Name, Arity),
499 ( current_predicate(system:Name/Arity),
500 predicate_property(system:Term, built_in)
501 -> ( predicate_property(system:Term, iso)
502 -> Type = iso_predicate
503 ; Type = swi_builtin_predicate
504 )
505 ; Type = library_predicate
506 ).
507object_class(_M:_Name/_Arity, library_predicate).
508object_class(_Name//_Arity, dcg).
509object_class(_M:_Name//_Arity, dcg).
510
511
512 515
516:- multifile prolog:message//1. 517
518prolog:message(help(not_found(What))) -->
519 [ 'No help for ~p.'-[What], nl,
520 'Use ?- apropos(query). to search for candidates.'-[]
521 ].
522prolog:message(help(no_apropos_match(Query))) -->
523 [ 'No matches for ~p'-[Query] ].
524prolog:message(help(apropos_matches(Pairs, Total))) -->
525 { tty_width(W),
526 Width is max(30,W),
527 length(Pairs, Count)
528 },
529 matches(Pairs, Width),
530 ( {Count =:= Total}
531 -> []
532 ; [ nl,
533 ansi(fg(red), 'Showing ~D of ~D matches', [Count,Total]), nl, nl,
534 'Use ?- apropos(Type:Query) or multiple words in Query '-[], nl,
535 'to restrict your search. For example:'-[], nl, nl,
536 ' ?- apropos(iso:open).'-[], nl,
537 ' ?- apropos(\'open file\').'-[]
538 ]
539 ).
540
541matches([], _) --> [].
542matches([H|T], Width) -->
543 match(H, Width),
544 ( {T == []}
545 -> []
546 ; [nl],
547 matches(T, Width)
548 ).
549
550match(Obj-Summary, Width) -->
551 { Left is min(40, max(20, round(Width/3))),
552 Right is Width-Left-2,
553 man_object_summary(Obj, ObjS, Tag),
554 write_length(ObjS, LenObj, [portray(true), quoted(true)]),
555 Spaces0 is Left - LenObj - 4,
556 ( Spaces0 > 0
557 -> Spaces = Spaces0,
558 SummaryLen = Right
559 ; Spaces = 1,
560 SummaryLen is Right + Spaces0 - 1
561 ),
562 truncate(Summary, SummaryLen, SummaryE)
563 },
564 [ ansi([fg(default)], '~w ~p', [Tag, ObjS]),
565 '~|~*+~w'-[Spaces, SummaryE]
567 ].
568
569truncate(Summary, Width, SummaryE) :-
570 string_length(Summary, SL),
571 SL > Width,
572 !,
573 Pre is Width-4,
574 sub_string(Summary, 0, Pre, _, S1),
575 string_concat(S1, " ...", SummaryE).
576truncate(Summary, _, Summary).
577
578man_object_summary(section(_Level, _Num, Label, _File), Obj, 'SEC') :-
579 atom_concat('sec:', Obj, Label),
580 !.
581man_object_summary(section(0, _Num, File, _Path), File, 'SEC') :- !.
582man_object_summary(c(Name), Obj, ' C') :- !,
583 compound_name_arguments(Obj, Name, []).
584man_object_summary(f(Name/Arity), Name/Arity, ' F') :- !.
585man_object_summary(Obj, Obj, Tag) :-
586 ( object_class(Obj, Class),
587 class_tag(Class, Tag)
588 -> true
589 ; Tag = ' ?'
590 ).
591
592 595
596sandbox:safe_primitive(prolog_help:apropos(_)).
597sandbox:safe_primitive(prolog_help:help(_))