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) 2018-2022, CWI Amsterdam 7 SWI-Prolog Solutions b.v. 8 All rights reserved. 9 10 Redistribution and use in source and binary forms, with or without 11 modification, are permitted provided that the following conditions 12 are met: 13 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 17 2. Redistributions in binary form must reproduce the above copyright 18 notice, this list of conditions and the following disclaimer in 19 the documentation and/or other materials provided with the 20 distribution. 21 22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 POSSIBILITY OF SUCH DAMAGE. 34*/ 35 36:- module(prolog_help, 37 [ help/0, 38 help/1, % +Object 39 apropos/1 % +Search 40 ]). 41:- use_module(library(pldoc), []). 42:- autoload(library(apply), [maplist/3]). 43:- autoload(library(error), [must_be/2]). 44:- autoload(library(isub), [isub/4]). 45:- autoload(library(lists), [append/3, sum_list/2]). 46:- autoload(library(pairs), [pairs_values/2]). 47:- autoload(library(porter_stem), [tokenize_atom/2]). 48:- autoload(library(process), [process_create/3]). 49:- autoload(library(sgml), [load_html/3]). 50:- autoload(library(solution_sequences), [distinct/1]). 51:- autoload(library(http/html_write), [html/3, print_html/1]). 52:- autoload(library(lynx/html_text), [html_text/2]). 53:- autoload(pldoc(doc_man), [man_page/4]). 54:- autoload(pldoc(doc_modes), [mode/2]). 55:- autoload(pldoc(doc_words), [doc_related_word/3]). 56:- autoload(pldoc(man_index), [man_object_property/2, doc_object_identifier/2]). 57:- autoload(library(prolog_code), [pi_head/2]). 58:- autoload(library(prolog_xref), [xref_source/2]). 59 60:- use_module(library(lynx/pldoc_style), []).
87:- meta_predicate 88 with_pager( ). 89 90:- multifile 91 show_html_hook/1. 92 93% one of `default`, `false`, an executable or executable(options), e.g. 94% less('-r'). 95:- create_prolog_flag(help_pager, default, 96 [ type(term), 97 keep(true) 98 ]).
topics(s)
to
give help for. Notations for What are:
?- help(append).
If an exact match fails this predicates attempts fuzzy matching and, when successful, display the results headed by a warning that the matches are based on fuzzy matching.
If possible, the results are sent through a pager such as the
less
program. This behaviour is controlled by the Prolog flag
help_pager
. See section level documentation.
134help :- 135 notrace(show_matches([help/1, apropos/1], exact-help)). 136 137help(What) :- 138 notrace(help_no_trace(What)). 139 140help_no_trace(What) :- 141 help_objects_how(What, Matches, How), 142 !, 143 show_matches(Matches, How-What). 144help_no_trace(What) :- 145 print_message(warning, help(not_found(What))). 146 147show_matches(Matches, HowWhat) :- 148 help_html(Matches, HowWhat, HTML), 149 !, 150 show_html(HTML).
158show_html(HTML) :- 159 show_html_hook(HTML), 160 !. 161show_html(HTML) :- 162 setup_call_cleanup( 163 open_string(HTML, In), 164 load_html(stream(In), DOM, []), 165 close(In)), 166 page_width(PageWidth), 167 LineWidth is PageWidth - 4, 168 with_pager(html_text(DOM, [width(LineWidth)])). 169 170help_html(Matches, How, HTML) :- 171 phrase(html(html([ head([]), 172 body([ \match_type(How), 173 dl(\man_pages(Matches, 174 [ no_manual(fail), 175 links(false), 176 link_source(false), 177 navtree(false), 178 server(false) 179 ])) 180 ]) 181 ])), 182 Tokens), 183 !, 184 with_output_to(string(HTML), 185 print_html(Tokens)). 186 187match_type(exact-_) --> 188 []. 189match_type(dwim-For) --> 190 html(p(class(warning), 191 [ 'WARNING: No matches for "', span(class('help-query'), For), 192 '" Showing closely related results' 193 ])). 194 195man_pages([], _) --> 196 []. 197man_pages([H|T], Options) --> 198 man_page(H, Options), 199 man_pages(T, Options). 200 201page_width(Width) :- 202 tty_width(W), 203 Width is min(100,max(50,W)).
210tty_width(W) :- 211 \+ running_under_emacs, 212 catch(tty_size(_, W), _, fail), 213 !. 214tty_width(80). 215 216help_objects_how(Spec, Objects, exact) :- 217 help_objects(Spec, exact, Objects), 218 !. 219help_objects_how(Spec, Objects, dwim) :- 220 help_objects(Spec, dwim, Objects), 221 !. 222 223help_objects(Spec, How, Objects) :- 224 findall(ID-Obj, help_object(Spec, How, Obj, ID), Objects0), 225 Objects0 \== [], 226 sort(1, @>, Objects0, Objects1), 227 pairs_values(Objects1, Objects2), 228 sort(Objects2, Objects). 229 230help_object(Fuzzy/Arity, How, Name/Arity, ID) :- 231 match_name(How, Fuzzy, Name), 232 man_object_property(Name/Arity, id(ID)). 233help_object(Fuzzy//Arity, How, Name//Arity, ID) :- 234 match_name(How, Fuzzy, Name), 235 man_object_property(Name//Arity, id(ID)). 236help_object(Fuzzy/Arity, How, f(Name/Arity), ID) :- 237 match_name(How, Fuzzy, Name), 238 man_object_property(f(Name/Arity), id(ID)). 239help_object(Fuzzy, How, Name/Arity, ID) :- 240 atom(Fuzzy), 241 match_name(How, Fuzzy, Name), 242 man_object_property(Name/Arity, id(ID)). 243help_object(Fuzzy, How, Name//Arity, ID) :- 244 atom(Fuzzy), 245 match_name(How, Fuzzy, Name), 246 man_object_property(Name//Arity, id(ID)). 247help_object(Fuzzy, How, f(Name/Arity), ID) :- 248 atom(Fuzzy), 249 match_name(How, Fuzzy, Name), 250 man_object_property(f(Name/Arity), id(ID)). 251help_object(Fuzzy, How, c(Name), ID) :- 252 atom(Fuzzy), 253 match_name(How, Fuzzy, Name), 254 man_object_property(c(Name), id(ID)). 255help_object(SecID, _How, section(Label), ID) :- 256 atom(SecID), 257 ( atom_concat('sec:', SecID, Label) 258 ; sub_atom(SecID, _, _, 0, '.html'), 259 Label = SecID 260 ), 261 man_object_property(section(_Level,_Num,Label,_File), id(ID)). 262help_object(Func, How, c(Name), ID) :- 263 compound(Func), 264 compound_name_arity(Func, Fuzzy, 0), 265 match_name(How, Fuzzy, Name), 266 man_object_property(c(Name), id(ID)). 267% for currently loaded predicates 268help_object(Module, _How, Name/Arity, _ID) :- 269 atom(Module), 270 current_module(Module), 271 atom_concat('sec:', Module, SecLabel), 272 \+ man_object_property(section(_,_,SecLabel,_), _), % not a section 273 current_predicate_help(Module:Name/Arity). 274help_object(Name/Arity, _How, Name/Arity, _ID) :- 275 atom(Name), 276 current_predicate_help(_:Name/Arity). 277help_object(Fuzzy, How, Name/Arity, _ID) :- 278 atom(Fuzzy), 279 match_name(How, Fuzzy, Name), 280 current_predicate_help(_:Name/Arity).
289current_predicate_help(M:Name/Arity) :- 290 current_predicate(M:Name/Arity), 291 pi_head(Name/Arity,Head), 292 \+ predicate_property(M:Head, imported_from(_)), 293 \+ man_object_property(Name/Arity, _), % must not be indexed already 294 ( mode(M:_, _) % Some predicates are documented 295 -> true 296 ; \+ module_property(M, class(system)), 297 predicate_property(M:Head,file(File)), 298 xref_source(File,[comments(store)]) 299 ), 300 mode(M:Head, _). % Test that our predicate is documented 301 302match_name(exact, Name, Name). 303match_name(dwim, Name, Fuzzy) :- 304 freeze(Fuzzy, dwim_match(Fuzzy, Name)).
312with_pager(Goal) :- 313 pager_ok(Pager, Options), 314 !, 315 Catch = error(io_error(_,_), _), 316 current_output(OldIn), 317 setup_call_cleanup( 318 process_create(Pager, Options, 319 [stdin(pipe(In))]), 320 ( set_stream(In, tty(true)), 321 set_output(In), 322 catch(Goal, Catch, true) 323 ), 324 ( set_output(OldIn), 325 close(In, [force(true)]) 326 )). 327with_pager(Goal) :- 328 call(Goal). 329 330pager_ok(_Path, _Options) :- 331 current_prolog_flag(help_pager, false), 332 !, 333 fail. 334pager_ok(Path, Options) :- 335 current_prolog_flag(help_pager, default), 336 !, 337 stream_property(current_output, tty(true)), 338 \+ running_under_emacs, 339 ( distinct(( getenv('PAGER', Pager) 340 ; Pager = less 341 )), 342 absolute_file_name(path(Pager), Path, 343 [ access(execute), 344 file_errors(fail) 345 ]) 346 -> pager_options(Path, Options) 347 ). 348pager_ok(Path, Options) :- 349 current_prolog_flag(help_pager, Term), 350 callable(Term), 351 compound_name_arguments(Term, Pager, Options), 352 absolute_file_name(path(Pager), Path, 353 [ access(execute), 354 file_errors(fail) 355 ]). 356 357pager_options(Path, Options) :- 358 file_base_name(Path, File), 359 file_name_extension(Base, _, File), 360 downcase_atom(Base, Id), 361 pager_default_options(Id, Options). 362 363pager_default_options(less, ['-r']).
371running_under_emacs :- 372 current_prolog_flag(emacs_inferior_process, true), 373 !. 374running_under_emacs :- 375 getenv('TERM', dumb), 376 !. 377running_under_emacs :- 378 current_prolog_flag(toplevel_prompt, P), 379 sub_atom(P, _, _, _, 'ediprolog'), 380 !.
section
, cfunction
, function
,
iso_predicate
, swi_builtin_predicate
, library_predicate
,
dcg
and aliases chapter
, arithmetic
, c_function
,
predicate
, nonterminal
and non_terminal
. For example:
?- apropos(c:close). ?- apropos(f:min).
405apropos(Query) :- 406 notrace(apropos_no_trace(Query)). 407 408apropos_no_trace(Query) :- 409 findall(Q-(Obj-Summary), apropos(Query, Obj, Summary, Q), Pairs), 410 ( Pairs == [] 411 -> print_message(warning, help(no_apropos_match(Query))) 412 ; sort(1, >=, Pairs, Sorted), 413 length(Sorted, Len), 414 ( Len > 20 415 -> length(Truncated, 20), 416 append(Truncated, _, Sorted) 417 ; Truncated = Sorted 418 ), 419 pairs_values(Truncated, Matches), 420 print_message(information, help(apropos_matches(Matches, Len))) 421 ). 422 423apropos(Query, Obj, Summary, Q) :- 424 parse_query(Query, Type, Words), 425 man_object_property(Obj, summary(Summary)), 426 apropos_match(Type, Words, Obj, Summary, Q). 427 428parse_query(Type:String, Type, Words) :- 429 !, 430 must_be(atom, Type), 431 must_be(text, String), 432 tokenize_atom(String, Words). 433parse_query(String, _Type, Words) :- 434 must_be(text, String), 435 tokenize_atom(String, Words). 436 437apropos_match(Type, Query, Object, Summary, Q) :- 438 maplist(amatch(Object, Summary), Query, Scores), 439 match_object_type(Type, Object), 440 sum_list(Scores, Q). 441 442amatch(Object, Summary, Query, Score) :- 443 ( doc_object_identifier(Object, String) 444 ; String = Summary 445 ), 446 amatch(Query, String, Score), 447 !. 448 449amatch(Query, To, Quality) :- 450 doc_related_word(Query, Related, Distance), 451 sub_atom_icasechk(To, _, Related), 452 isub(Related, To, false, Quality0), 453 Quality is Quality0*Distance. 454 455match_object_type(Type, _Object) :- 456 var(Type), 457 !. 458match_object_type(Type, Object) :- 459 downcase_atom(Type, LType), 460 object_class(Object, Class), 461 match_object_class(LType, Class). 462 463match_object_class(Type, Class) :- 464 ( TheClass = Class 465 ; class_alias(Class, TheClass) 466 ), 467 sub_atom(TheClass, 0, _, _, Type), 468 !. 469 470class_alias(section, chapter). 471class_alias(function, arithmetic). 472class_alias(cfunction, c_function). 473class_alias(iso_predicate, predicate). 474class_alias(swi_builtin_predicate, predicate). 475class_alias(library_predicate, predicate). 476class_alias(dcg, predicate). 477class_alias(dcg, nonterminal). 478class_alias(dcg, non_terminal). 479 480class_tag(section, 'SEC'). 481class_tag(function, ' F'). 482class_tag(iso_predicate, 'ISO'). 483class_tag(swi_builtin_predicate, 'SWI'). 484class_tag(library_predicate, 'LIB'). 485class_tag(dcg, 'DCG'). 486 487object_class(section(_Level, _Num, _Label, _File), section). 488object_class(c(_Name), cfunction). 489object_class(f(_Name/_Arity), function). 490object_class(Name/Arity, Type) :- 491 functor(Term, Name, Arity), 492 ( current_predicate(system:Name/Arity), 493 predicate_property(system:Term, built_in) 494 -> ( predicate_property(system:Term, iso) 495 -> Type = iso_predicate 496 ; Type = swi_builtin_predicate 497 ) 498 ; Type = library_predicate 499 ). 500object_class(_M:_Name/_Arity, library_predicate). 501object_class(_Name//_Arity, dcg). 502object_class(_M:_Name//_Arity, dcg). 503 504 505 /******************************* 506 * MESSAGES * 507 *******************************/ 508 509:- multifile prolog:message//1. 510 511prologmessage(help(not_found(What))) --> 512 [ 'No help for ~p.'-[What], nl, 513 'Use ?- apropos(query). to search for candidates.'-[] 514 ]. 515prologmessage(help(no_apropos_match(Query))) --> 516 [ 'No matches for ~p'-[Query] ]. 517prologmessage(help(apropos_matches(Pairs, Total))) --> 518 { tty_width(W), 519 Width is max(30,W), 520 length(Pairs, Count) 521 }, 522 matches(Pairs, Width), 523 ( {Count =:= Total} 524 -> [] 525 ; [ nl, 526 ansi(fg(red), 'Showing ~D of ~D matches', [Count,Total]), nl, nl, 527 'Use ?- apropos(Type:Query) or multiple words in Query '-[], nl, 528 'to restrict your search. For example:'-[], nl, nl, 529 ' ?- apropos(iso:open).'-[], nl, 530 ' ?- apropos(\'open file\').'-[] 531 ] 532 ). 533 534matches([], _) --> []. 535matches([H|T], Width) --> 536 match(H, Width), 537 ( {T == []} 538 -> [] 539 ; [nl], 540 matches(T, Width) 541 ). 542 543match(Obj-Summary, Width) --> 544 { Left is min(40, max(20, round(Width/3))), 545 Right is Width-Left-2, 546 man_object_summary(Obj, ObjS, Tag), 547 write_length(ObjS, LenObj, [portray(true), quoted(true)]), 548 Spaces0 is Left - LenObj - 4, 549 ( Spaces0 > 0 550 -> Spaces = Spaces0, 551 SummaryLen = Right 552 ; Spaces = 1, 553 SummaryLen is Right + Spaces0 - 1 554 ), 555 truncate(Summary, SummaryLen, SummaryE) 556 }, 557 [ ansi([fg(default)], '~w ~p', [Tag, ObjS]), 558 '~|~*+~w'-[Spaces, SummaryE] 559% '~*|~w'-[Spaces, SummaryE] % Should eventually work 560 ]. 561 562truncate(Summary, Width, SummaryE) :- 563 string_length(Summary, SL), 564 SL > Width, 565 !, 566 Pre is Width-4, 567 sub_string(Summary, 0, Pre, _, S1), 568 string_concat(S1, " ...", SummaryE). 569truncate(Summary, _, Summary). 570 571man_object_summary(section(_Level, _Num, Label, _File), Obj, 'SEC') :- 572 atom_concat('sec:', Obj, Label), 573 !. 574man_object_summary(section(0, _Num, File, _Path), File, 'SEC') :- !. 575man_object_summary(c(Name), Obj, ' C') :- !, 576 compound_name_arguments(Obj, Name, []). 577man_object_summary(f(Name/Arity), Name/Arity, ' F') :- !. 578man_object_summary(Obj, Obj, Tag) :- 579 ( object_class(Obj, Class), 580 class_tag(Class, Tag) 581 -> true 582 ; Tag = ' ?' 583 ). 584 585 /******************************* 586 * SANDBOX * 587 *******************************/ 588 589sandbox:safe_primitive(prolog_help:apropos(_)). 590sandbox:safe_primitive(prolog_help:help(_))
Text based manual
This module provides help/1 and apropos/1 that give help on a topic or searches the manual for relevant topics.
By default the result of help/1 is sent through a pager such as
less
. This behaviour is controlled by the following:help_pager
, which can be set to one of the following values:PAGER
or otherwise tries to find theless
program.program_name(Arg, ...)
. For example,less('-r')
would be the default. Note that the program name can be an absolute path if single quotes are used. */