1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: jan@swi-prolog.org 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2018-2026, 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 apropos/2, % +Search, +Options 41 help_apropos/4, 42 help_text/2 % :PI, -Text:string 43 ]). 44:- use_module(library(pldoc), []). 45:- use_module(library(isub), [isub/4]). 46:- autoload(library(apply), [maplist/3]). 47:- autoload(library(error), [must_be/2]). 48:- autoload(library(lists), [append/3, sum_list/2, select/3]). 49:- autoload(library(option), [option/3]). 50:- autoload(library(pairs), [pairs_values/2]). 51:- autoload(library(porter_stem), [tokenize_atom/2]). 52:- autoload(library(process), 53 [process_create/3, process_which/2, process_wait/2]). 54:- autoload(library(sgml), [load_html/3]). 55:- autoload(library(solution_sequences), [distinct/1]). 56:- autoload(library(http/html_write), [html/3, print_html/1]). 57:- autoload(library(lynx/html_text), [html_text/2]). 58:- autoload(pldoc(doc_man), 59 [ man_page/4, pldoc_href_object/2, 60 man_object_uri/2, man_uri_object/2, 61 xpce_object_label/2 62 ]). 63:- autoload(library(pce), [send/3, get/3]). 64:- autoload(pldoc(doc_modes), [(mode)/2]). 65:- autoload(pldoc(doc_words), [doc_related_word/3]). 66:- autoload(pldoc(man_index), [man_object_property/2, doc_object_identifier/2]). 67:- autoload(library(prolog_code), [pi_head/2]). 68:- autoload(library(prolog_xref), [xref_source/2]). 69:- use_module(library(lynx/pldoc_style), []). 70:- autoload(library(terms), [mapsubterms/3]).
97:- meta_predicate 98 with_pager(). 99 100:- multifile 101 show_html_hook/1. 102 103% one of `default`, `false`, an executable or executable(options), e.g. 104% less('-r'). 105:- create_prolog_flag(help_pager, default, 106 [ type(term), 107 keep(true) 108 ]).
topics(s) to
give help for. Notations for What are:
?- help(append).
help/1 shows documentation from the manual as well as from loaded user code if the code is documented using PlDoc. To show only the documentatoion of the loaded predicate we may prefix predicate indicator with the module in which it is defined.
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.
If the terminal supports hyperlinks (see the Prolog flag
hyperlink_term), the manual references in the page are clickable.
In an Epilog window, clicking one quits the pager and runs help/1 on
the linked object.
162help :- 163 notrace(show_matches([help/1, apropos/1], exact-help)). 164 165help(What) :- 166 notrace(help_no_trace(What)). 167 168help_no_trace(What) :- 169 help_objects_how(What, Matches, How), 170 !, 171 show_matches(Matches, How-What). 172help_no_trace(What) :- 173 print_message(warning, help(not_found(What))). 174 175show_matches(Matches, HowWhat) :- 176 help_html(Matches, HowWhat, HTML), 177 !, 178 show_html(HTML).
186show_html(HTML) :- 187 show_html_hook(HTML), 188 !. 189show_html(HTML) :- 190 load_html(string(HTML), DOM0, []), 191 mapsubterms(man_link, DOM0, DOM), 192 page_width(PageWidth), 193 LineWidth is PageWidth - 4, 194 with_pager(html_text(DOM, [width(LineWidth)])). 195 196help_html(Matches, How, HTML) :- 197 ( current_prolog_flag(epilog, true) 198 -> Extra = [link_scheme(man)] 199 ; Extra = [] 200 ), 201 phrase(html(html([ head([]), 202 body([ \match_type(How), 203 dl(\man_pages(Matches, 204 [ no_manual(fail), 205 links(false), 206 link_source(false), 207 navtree(false), 208 server(false), 209 qualified(always) 210 | Extra 211 ])) 212 ]) 213 ])), 214 Tokens), 215 !, 216 with_output_to(string(HTML), 217 print_html(Tokens)). 218 219match_type(exact-_) --> 220 []. 221match_type(dwim-For) --> 222 html(p(class(warning), 223 [ 'WARNING: No matches for "', span(class('help-query'), For), 224 '" Showing closely related results' 225 ])). 226 227man_pages([], _) --> 228 []. 229man_pages([H|T], Options) --> 230 ( man_page(H, Options) 231 -> [] 232 ; html(p(class(warning), 233 [ 'WARNING: No help for ~p'-[H] 234 ])) 235 ), 236 man_pages(T, Options). 237 238page_width(Width) :- 239 tty_width(W), 240 Width is min(100,max(50,W)).
247tty_width(W) :- 248 \+ running_under_emacs, 249 catch(tty_size(_, W), _, fail), 250 !. 251tty_width(80). 252 253help_objects_how(Spec, Objects, exact) :- 254 help_objects(Spec, exact, Objects), 255 !. 256help_objects_how(Spec, Objects, dwim) :- 257 help_objects(Spec, dwim, Objects), 258 !. 259 260help_objects(Spec, How, Objects) :- 261 findall(ID-Obj, help_object(Spec, How, Obj, ID), Objects0), 262 Objects0 \== [], 263 sort(1, @>, Objects0, Objects1), 264 pairs_values(Objects1, Objects2), 265 sort(Objects2, Objects). 266 267help_object(Fuzzy/Arity, How, Name/Arity, ID) :- 268 match_name(How, Fuzzy, Name), 269 man_object_property(Name/Arity, id(ID)). 270help_object(Fuzzy//Arity, How, Name//Arity, ID) :- 271 match_name(How, Fuzzy, Name), 272 man_object_property(Name//Arity, id(ID)). 273help_object(Fuzzy/Arity, How, f(Name/Arity), ID) :- 274 match_name(How, Fuzzy, Name), 275 man_object_property(f(Name/Arity), id(ID)). 276help_object(Fuzzy, How, Name/Arity, ID) :- 277 atom(Fuzzy), 278 match_name(How, Fuzzy, Name), 279 man_object_property(Name/Arity, id(ID)). 280help_object(Fuzzy, How, Name//Arity, ID) :- 281 atom(Fuzzy), 282 match_name(How, Fuzzy, Name), 283 man_object_property(Name//Arity, id(ID)). 284help_object(Fuzzy, How, f(Name/Arity), ID) :- 285 atom(Fuzzy), 286 match_name(How, Fuzzy, Name), 287 man_object_property(f(Name/Arity), id(ID)). 288help_object(Fuzzy, How, c(Name), ID) :- 289 atom(Fuzzy), 290 match_name(How, Fuzzy, Name), 291 man_object_property(c(Name), id(ID)). 292help_object(SecID, _How, section(Label), ID) :- 293 atom(SecID), 294 ( atom_concat('sec:', SecID, Label) 295 ; sub_atom(SecID, _, _, 0, '.html'), 296 Label = SecID 297 ), 298 man_object_property(section(_Level,_Num,Label,_File), id(ID)). 299help_object(Func, How, c(Name), ID) :- 300 compound(Func), 301 compound_name_arity(Func, Fuzzy, 0), 302 match_name(How, Fuzzy, Name), 303 man_object_property(c(Name), id(ID)). 304% resolved manual objects, e.g. from a clicked hyperlink. See man_link/2. 305help_object(Obj, _How, Obj, ID) :- 306 man_object_id(Obj, ID). 307% for currently loaded predicates 308help_object(Module, _How, Module:Name/Arity, _ID) :- 309 atom(Module), 310 current_module(Module), 311 atom_concat('sec:', Module, SecLabel), 312 \+ man_object_property(section(_,_,SecLabel,_), _), % not a section 313 current_predicate_help(Module:Name/Arity). 314help_object(Module:Name, _How, Module:Name/Arity, _ID) :- 315 atom(Name), 316 current_predicate_help(Module:Name/Arity). 317help_object(Module:Name/Arity, _How, Module:Name/Arity, _ID) :- 318 atom(Name), 319 current_predicate_help(Module:Name/Arity). 320help_object(Name/Arity, _How, Module:Name/Arity, _ID) :- 321 atom(Name), 322 current_predicate_help(Module:Name/Arity). 323help_object(Fuzzy, How, Module:Name/Arity, _ID) :- 324 atom(Fuzzy), 325 match_name(How, Fuzzy, Name), 326 current_predicate_help(Module:Name/Arity).
334man_object_id(Module:Name/Arity, ID) :- 335 atom(Module), 336 atom(Name), 337 integer(Arity), 338 man_object_property(Module:Name/Arity, id(ID)). 339man_object_id(Module:Name//Arity, ID) :- 340 atom(Module), 341 atom(Name), 342 integer(Arity), 343 man_object_property(Module:Name//Arity, id(ID)). 344man_object_id(section(Label), ID) :- 345 atom(Label), 346 man_object_property(section(_Level,_Num,Label,_File), id(ID)). 347man_object_id(f(Name/Arity), ID) :- 348 atom(Name), 349 integer(Arity), 350 man_object_property(f(Name/Arity), id(ID)). 351man_object_id(c(Name), ID) :- 352 atom(Name), 353 man_object_property(c(Name), id(ID)). 354man_object_id(xpce(Class,Kind,Name), ID) :- 355 atom(Class), 356 atom(Kind), 357 atom(Name), 358 man_object_property(xpce(Class,Kind,Name), id(ID)).
367current_predicate_help(M:Name/Arity) :- 368 current_predicate(M:Name/Arity), 369 pi_head(Name/Arity,Head), 370 \+ predicate_property(M:Head, imported_from(_)), 371 module_property(M, class(user)), 372 ( mode(M:_, _) % Some predicates are documented 373 -> true 374 ; \+ module_property(M, class(system)), 375 main_source_file(M:Head, File), 376 xref_source(File,[comments(store)]) 377 ), 378 mode(M:Head, _). % Test that our predicate is documented 379 380match_name(exact, Name, Name). 381match_name(dwim, Name, Fuzzy) :- 382 freeze(Fuzzy, dwim_match(Fuzzy, Name)).
388main_source_file(Pred, File) :- 389 predicate_property(Pred, file(File0)), 390 main_source(File0, File). 391 392main_source(File, Main) :- 393 source_file(File), 394 !, 395 Main = File. 396main_source(File, Main) :- 397 source_file_property(File, included_in(Parent, _Time)), 398 main_source(Parent, Main).
408with_pager(Goal) :- 409 pager_ok(Pager, Options), 410 !, 411 current_output(Screen), 412 setup_call_cleanup( 413 pager_screen(Screen, enter), 414 paged(Pager, Goal, Options), 415 pager_screen(Screen, leave)). 416with_pager(Goal) :- 417 call(Goal).
425:- dynamic 426 pager/2. % Thread, PID 427 428paged(Pager, Goal, Options) :- 429 Catch = error(io_error(_,_), _), 430 current_output(OldIn), 431 thread_self(Me), 432 setup_call_cleanup( 433 ( process_create(Pager, Options, 434 [stdin(pipe(In)), process(PID)]), 435 assertz(pager(Me, PID), Ref) 436 ), 437 ( set_stream(In, tty(true)), 438 set_output(In), 439 catch(Goal, Catch, true) 440 ), 441 call_cleanup(( set_output(OldIn), 442 close(In, [force(true)]), 443 process_wait(PID, _Status) 444 ), 445 erase(Ref))).
Elsewhere the pager does this itself, from its terminal description,
and a pager that does not (cat) is one whose output should stay.
460pager_screen(_Screen, _Which) :- 461 \+ current_prolog_flag(windows, true), 462 !. 463pager_screen(Screen, _Which) :- 464 \+ stream_property(Screen, tty(true)), 465 !. 466pager_screen(Screen, enter) :- 467 !, 468 format(Screen, '\e[?1049h', []), 469 flush_output(Screen). 470pager_screen(Screen, leave) :- 471 format(Screen, '\e[?1049l', []), 472 flush_output(Screen). 473 474pager_ok(_Path, _Options) :- 475 current_prolog_flag(help_pager, false), 476 !, 477 fail. 478pager_ok(Path, Options) :- 479 current_prolog_flag(help_pager, default), 480 !, 481 stream_property(current_output, tty(true)), 482 \+ running_under_emacs, 483 ( distinct(( getenv('PAGER', Pager) 484 ; Pager = less 485 )), 486 absolute_file_name(path(Pager), Path, 487 [ access(execute), 488 file_errors(fail) 489 ]) 490 -> pager_options(Path, Options) 491 ). 492pager_ok(Path, Options) :- 493 current_prolog_flag(help_pager, Term), 494 callable(Term), 495 compound_name_arguments(Term, Pager, Options), 496 ( is_absolute_file_name(Pager) 497 -> Prog = Pager 498 ; Prog = path(Pager) 499 ), 500 process_which(Prog, Path). 501 502pager_options(Path, Options) :- 503 file_base_name(Path, File), 504 file_name_extension(Base, _, File), 505 downcase_atom(Base, Id), 506 pager_default_options(Id, Options), 507 !. 508pager_options(_, []). 509 510pager_default_options(less, ['-r']).
518running_under_emacs :- 519 current_prolog_flag(emacs_inferior_process, true), 520 !. 521running_under_emacs :- 522 getenv('TERM', dumb), 523 !. 524running_under_emacs :- 525 current_prolog_flag(toplevel_prompt, P), 526 sub_atom(P, _, _, _, 'ediprolog'), 527 !.
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).
Only the best limit matches are shown. Options:
If the terminal supports hyperlinks (see the Prolog flag
hyperlink_term), the matches are clickable and so is the line that
reports there are more matches. In an Epilog window, clicking these
runs help/1 on the match or apropos/2 on the next page.
564apropos(Query) :- 565 apropos(Query, []). 566 567apropos(Query, Options) :- 568 notrace(apropos_no_trace(Query, Options)). 569 570apropos_no_trace(Query, Options) :- 571 option(limit(Limit), Options, 20), 572 option(offset(From), Options, 0), 573 must_be(positive_integer, Limit), 574 must_be(nonneg, From), 575 findall(Q-(Obj-Summary), help_apropos(Query, Obj, Summary, Q), Pairs), 576 ( Pairs == [] 577 -> print_message(warning, help(no_apropos_match(Query))) 578 ; sort(1, >=, Pairs, Sorted), 579 length(Sorted, Total), 580 page(Sorted, From, Limit, Page), 581 pairs_values(Page, Matches), 582 print_message(information, 583 help(apropos_matches(Query, Matches, From, Total))) 584 ).
591page(List, From, Limit, Page) :-
592 length(List, Len),
593 Skip is min(From, Len),
594 length(Prefix, Skip),
595 append(Prefix, Rest, List),
596 length(Rest, RestLen),
597 Take is min(Limit, RestLen),
598 length(Page, Take),
599 append(Page, _, Rest).607help_apropos(Query, Obj, Summary, Q) :- 608 parse_query(Query, Type, Words), 609 man_object_property(Obj, summary(Summary)), 610 apropos_match(Type, Words, Obj, Summary, Q). 611 612parse_query(Type:String, Type, Words) :- 613 !, 614 must_be(atom, Type), 615 must_be(text, String), 616 tokenize_atom(String, Words). 617parse_query(String, _Type, Words) :- 618 must_be(text, String), 619 tokenize_atom(String, Words). 620 621apropos_match(Type, Query, Object, Summary, Q) :- 622 maplist(amatch(Object, Summary), Query, Scores), 623 match_object_type(Type, Object), 624 sum_list(Scores, Q). 625 626amatch(Object, Summary, Query, Score) :- 627 ( doc_object_identifier(Object, String) 628 ; String = Summary 629 ), 630 amatch(Query, String, Score), 631 !. 632 633amatch(Query, To, Quality) :- 634 doc_related_word(Query, Related, Distance), 635 sub_atom_icasechk(To, _, Related), 636 isub(Related, To, false, Quality0), 637 Quality is Quality0*Distance. 638 639match_object_type(Type, _Object) :- 640 var(Type), 641 !. 642match_object_type(Type, Object) :- 643 downcase_atom(Type, LType), 644 object_class(Object, Class), 645 match_object_class(LType, Class). 646 647match_object_class(Type, Class) :- 648 ( TheClass = Class 649 ; class_alias(Class, TheClass) 650 ), 651 sub_atom(TheClass, 0, _, _, Type), 652 !. 653 654class_alias(section, chapter). 655class_alias(function, arithmetic). 656class_alias(cfunction, c_function). 657class_alias(iso_predicate, predicate). 658class_alias(swi_builtin_predicate, predicate). 659class_alias(library_predicate, predicate). 660class_alias(dcg, predicate). 661class_alias(dcg, nonterminal). 662class_alias(dcg, non_terminal). 663 664class_tag(section, 'SEC'). 665class_tag(function, 'F'). 666class_tag(cfunction, 'C'). 667class_tag(iso_predicate, 'ISO'). 668class_tag(swi_builtin_predicate, 'SWI'). 669class_tag(library_predicate, 'LIB'). 670class_tag(dcg, 'DCG'). 671class_tag(xpce, 'XPCE'). 672 673object_class(section(_Level, _Num, _Label, _File), section). 674object_class(c(_Name), cfunction). 675object_class(f(_Name/_Arity), function). 676object_class(xpce(_Class, _Kind, _Name), xpce). 677object_class(Name/Arity, Type) :- 678 functor(Term, Name, Arity), 679 ( current_predicate(system:Name/Arity), 680 predicate_property(system:Term, built_in) 681 -> ( predicate_property(system:Term, iso) 682 -> Type = iso_predicate 683 ; Type = swi_builtin_predicate 684 ) 685 ; Type = library_predicate 686 ). 687object_class(_M:_Name/_Arity, library_predicate). 688object_class(_Name//_Arity, dcg). 689object_class(_M:_Name//_Arity, dcg).
Name/Arity for which
documentation exists, HelpText is the documentation in textual format
(parsed from the HTML help).697help_text(Pred, HelpText) :- 698 help_objects(Pred, exact, Matches), !, 699 catch(help_html(Matches, exact-exact, HtmlDoc), _, fail), 700 setup_call_cleanup(open_string(HtmlDoc, In), 701 load_html(stream(In), Dom, []), 702 close(In)), 703 with_output_to(string(HelpText), html_text(Dom, [])). 704 705 706 /******************************* 707 * LINKS * 708 *******************************/
link_scheme(man) option of man_page//2 already wrote the manual
references as man: IRIs, which a terminal emits as OSC8 hyperlinks
(see ansi_hyperlink/3) and tty_link_hook/2 below resolves when clicked.
This maps the remaining links, which address the PlDoc server, onto
the same IRIs. Links we cannot resolve are removed.
718man_link(element(a, Attrs0, Content), Element) :-
719 select(href=HREF0, Attrs0, Attrs1),
720 \+ sub_atom(HREF0, 0, _, _, 'man:'),
721 ( current_prolog_flag(epilog, true),
722 pldoc_href_object(HREF0, Object),
723 man_object_uri(Object, HREF)
724 -> Element = element(a, [href=HREF|Attrs1], Content)
725 ; Element = element(b, Attrs1, Content)
726 ).apropos: IRI and the apropos/2 goal that
continues the search at Offset. Used to make the line telling
there are more matches clickable.735apropos_uri(Query, Offset, URI) :- 736 format(atom(URI), 'apropos:~q', [Query+Offset]). 737 738apropos_uri_goal(URI, apropos(Query, [offset(Offset)])) :- 739 atom_concat('apropos:', Text, URI), 740 catch(term_to_atom(Query+Offset, Text), error(_,_), fail), 741 integer(Offset).
man: or apropos: link that was clicked in an Epilog
Terminal. We quit the pager if it is still showing the page the link
was clicked in and let the terminal run help/1 on the linked object or
continue the apropos/2 search.750:- multifile epilog:tty_link_hook/2. 751 752epilogtty_link_hook(Terminal, URL) :- 753 link_goal(URL, Goal), 754 !, % the link is ours, do not let 755 quit_pager(Terminal), % Epilog pass it to a browser 756 ignore(send(Terminal, inject, Goal)). 757 758link_goal(URL, help(Object)) :- 759 man_uri_object(URL, Object). 760link_goal(URL, Goal) :- 761 apropos_uri_goal(URL, Goal).
q.768quit_pager(Terminal) :- 769 get(Terminal, thread, Thread), 770 pager(Thread, _PID), 771 !, 772 send(Terminal, send, "q"). 773quit_pager(_). 774 775 /******************************* 776 * MESSAGES * 777 *******************************/ 778 779:- multifile prolog:message//1. 780 781prologmessage(help(not_found(What))) --> 782 [ 'No help for ~p.'-[What], nl, 783 'Use ?- apropos(query). to search for candidates.'-[] 784 ]. 785prologmessage(help(no_apropos_match(Query))) --> 786 [ 'No matches for ~p'-[Query] ]. 787prologmessage(help(apropos_matches(Query, Pairs, From, Total))) --> 788 { tty_width(W), 789 Width is max(30,W), 790 length(Pairs, Count), 791 End is From+Count 792 }, 793 matches(Pairs, Width), 794 ( {End =:= Total, From =:= 0} 795 -> [] 796 ; [nl], 797 showing(Query, From, End, Total), 798 ( {End =:= Total} 799 -> [] 800 ; [ nl, nl, 801 'Use ?- apropos(Type:Query) or multiple words in Query '-[], nl, 802 'to restrict your search. For example:'-[], nl, nl, 803 ' ?- apropos(iso:open).'-[], nl, 804 ' ?- apropos(\'open file\').'-[] 805 ] 806 ) 807 ).
814showing(Query, From, End, Total) --> 815 { End < Total, 816 apropos_uri(Query, End, URI), 817 Start is From+1 818 }, 819 !, 820 [ ansi([fg(red), href(URI)], 'Showing ~D..~D of ~D matches', 821 [Start,End,Total]) 822 ]. 823showing(_Query, From, End, Total) --> 824 { Start is From+1 }, 825 [ ansi(fg(red), 'Showing ~D..~D of ~D matches', [Start,End,Total]) ]. 826 827matches([], _) --> []. 828matches([H|T], Width) --> 829 match(H, Width), 830 ( {T == []} 831 -> [] 832 ; [nl], 833 matches(T, Width) 834 ). 835 836match(Obj-Summary, Width) --> 837 { Left is min(40, max(20, round(Width/3))), 838 Right is Width-Left-2, 839 man_object_summary(Obj, ObjS, Tag), 840 format(string(TagS), '~t~w~4|', [Tag]), 841 string_length(ObjS, LenObj), 842 Spaces0 is Left - LenObj - 5, 843 ( Spaces0 > 0 844 -> Spaces = Spaces0, 845 SummaryLen = Right 846 ; Spaces = 1, 847 SummaryLen is Right + Spaces0 - 1 848 ), 849 truncate(Summary, SummaryLen, SummaryE), 850 match_attributes(Obj, Attrs) 851 }, 852 [ ansi([fg(default)], '~w ', [TagS]), 853 ansi(Attrs, '~w', [ObjS]), 854 '~|~*+~w'-[Spaces, SummaryE] 855% '~*|~w'-[Spaces, SummaryE] % Should eventually work 856 ].
863match_attributes(Obj, [fg(default), href(URI)]) :- 864 current_prolog_flag(hyperlink_term, true), 865 man_object_uri(Obj, URI), 866 !. 867match_attributes(_Obj, [fg(default)]). 868 869truncate(Summary, Width, SummaryE) :- 870 string_length(Summary, SL), 871 SL > Width, 872 !, 873 ellipsis(Ellipsis, Len), 874 Pre is max(0, Width-Len), 875 sub_string(Summary, 0, Pre, _, S1), 876 string_concat(S1, Ellipsis, SummaryE). 877truncate(Summary, _, Summary).
885ellipsis(" \u2026", 2) :- 886 stream_property(user_error, encoding(Enc)), 887 unicode_encoding(Enc), 888 !. 889ellipsis(" ...", 4). 890 891unicode_encoding(utf8). 892unicode_encoding(unicode_be). 893unicode_encoding(unicode_le). 894unicode_encoding(wchar_t).
901man_object_summary(section(_Level, _Num, Label, _File), Text, 'SEC') :- 902 atom_concat('sec:', Name, Label), 903 !, 904 format(string(Text), '~w', [Name]). 905man_object_summary(section(0, _Num, File, _Path), Text, 'SEC') :- !, 906 format(string(Text), '~w', [File]). 907man_object_summary(c(Name), Text, 'C') :- !, 908 format(string(Text), '~w()', [Name]). 909man_object_summary(xpce(Class, Kind, Name), Text, 'XPCE') :- !, 910 xpce_object_label(xpce(Class, Kind, Name), Label), 911 format(string(Text), '~w', [Label]). 912man_object_summary(f(Name/Arity), Text, 'F') :- !, 913 format(string(Text), '~p', [Name/Arity]). 914man_object_summary(Obj, Text, Tag) :- 915 ( object_class(Obj, Class), 916 class_tag(Class, Tag) 917 -> true 918 ; Tag = '?' 919 ), 920 format(string(Text), '~p', [Obj]). 921 922 /******************************* 923 * SANDBOX * 924 *******************************/ 925 926sandbox:safe_primitive(prolog_help:apropos(_)). 927sandbox:safe_primitive(prolog_help:apropos(_,_)). 928sandbox: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:PAGERor otherwise tries to find thelessprogram.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.