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) 2006-2020, University of Amsterdam 7 VU University Amsterdam 8 CWI, Amsterdam 9 All rights reserved. 10 11 Redistribution and use in source and binary forms, with or without 12 modification, are permitted provided that the following conditions 13 are met: 14 15 1. Redistributions of source code must retain the above copyright 16 notice, this list of conditions and the following disclaimer. 17 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in 20 the documentation and/or other materials provided with the 21 distribution. 22 23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 POSSIBILITY OF SUCH DAMAGE. 35*/ 36 37:- module(pldoc_man, 38 [ man_page//2, % +Obj, +Options 39 man_overview//1, % +Options 40 41 man_content_tree/2, % +Dir, -Tree 42 man_packages_tree/1 % -Tree 43 ]). 44:- use_module(library(xpath),[xpath/3, op(_,_,_)]). 45:- use_module(library(http/html_write)). 46:- use_module(library(debug),[assertion/1,debug/3]). 47 48:- autoload(doc_html, 49 [ object_tree/5, private/2, object_page_header/4, objects/4, 50 object_href/2, object_synopsis/4, object_footer/4, 51 object_page_footer/4, 52 object_ref/4, object_page/4, 53 object_source_button//2 54 ]). 55:- autoload(doc_process,[doc_comment/4]). 56:- autoload(doc_search,[search_form/3]). 57:- autoload(doc_util,[atom_to_object/2,atom_pi/2]). 58:- autoload(man_index,[manual_object/5]). 59:- autoload(library(apply),[maplist/2,maplist/3,convlist/3]). 60:- autoload(library(error),[permission_error/3,existence_error/2]). 61:- autoload(library(filesex), 62 [directory_file_path/3,relative_file_name/3]). 63:- autoload(library(lists), 64 [select/4,append/3,member/2,last/2,selectchk/3]). 65:- autoload(library(option),[merge_options/3,option/2,option/3]). 66:- autoload(library(pairs),[pairs_values/2,pairs_keys/2]). 67:- autoload(library(prolog_xref),[xref_public_list/3]). 68:- autoload(library(sgml), 69 [ load_html/3, dtd/2, new_sgml_parser/2, set_sgml_parser/2, 70 sgml_parse/2, free_sgml_parser/1 71 ]). 72:- autoload(library(uri),[uri_encoded/3]). 73:- autoload(library(www_browser),[expand_url_path/2]). 74:- autoload(library(http/html_head),[html_requires/3]). 75:- if(exists_source(library(http/http_dispatch))). 76:- use_module(library(http/http_dispatch), 77 [ http_link_to_id/3, http_location_by_id/2, 78 http_handler/3, http_reply_file/3, http_redirect/3 79 ]). 80:- endif. 81:- autoload(library(http/http_path),[http_absolute_location/3]). 82:- autoload(library(http/mimetype),[file_mime_type/2]). 83 84:- include(hooks).
90:- predicate_options(man_page//2, 2, 91 [ for(atom), 92 links(boolean), 93 navtree(boolean), 94 synopsis(boolean), 95 footer(boolean), 96 link_source(boolean), 97 no_manual(oneof([fail,error])), 98 search_in(oneof([all, app, man])), 99 search_match(oneof([name, summary])), 100 search_options(boolean) 101 ]). 102 103 104 /******************************* 105 * HIERARCHY * 106 *******************************/
ul list that
reflects the location of Obj in the manual.
(Obj, Options) -->
114 { ensure_man_tree,
115 man_nav_tree(Obj, Tree, Options),
116 TreeOptions = [ secref_style(title)
117 | Options
118 ]
119 },
120 html(ul(class(nav),
121 \object_tree(Tree, [Obj], TreeOptions))).130man_nav_tree(Obj, Tree, _Options) :- 131 man_child_of(Obj, Parent), 132 !, 133 findall(Neighbour, man_child_of(Neighbour, Parent), Neighbours0), 134 ( findall(Child, man_child_of(Child, Obj), Children), 135 Children \== [] 136 -> select(Obj, Neighbours0, node(Obj, Children), Neighbours) 137 ; Neighbours = Neighbours0 138 ), 139 path_up(node(Parent, Neighbours), Tree). 140man_nav_tree(Obj, node(Obj, Children), _Options) :- 141 findall(Child, man_child_of(Child, Obj), Children). 142 143 144path_up(Node, Tree) :- 145 node_id(Node, Id), 146 man_child_of(Id, Parent), 147 !, 148 ( Parent == root 149 -> findall(Neighbour, man_child_of(Neighbour, Parent), Neighbours0), 150 select(Id, Neighbours0, Node, Neighbours), 151 Tree = node(root, Neighbours) 152 ; path_up(node(Parent, [Node]), Tree) 153 ). 154path_up(Tree, Tree).
161man_child_of(Child, Parent) :- 162 term_hash(Child, ChildHash), 163 term_hash(Parent, ParentHash), 164 man_child_of(ChildHash, Child, ParentHash, Parent). 165 166:- dynamic 167 man_child_of/4, 168 man_tree_done/0.
174ensure_man_tree :- 175 man_tree_done, 176 !. 177ensure_man_tree :- 178 with_mutex(man_tree, 179 make_man_tree). 180 181make_man_tree :- 182 man_tree_done, 183 !. 184make_man_tree :- 185 man_content_tree(swi_man_manual('.'), ManTree), 186 man_packages_tree(PkgTree), 187 assert_tree(node(root, [ManTree, PkgTree])), 188 assertz(man_tree_done). 189 190assert_tree(node(Id, Children)) :- 191 !, 192 maplist(assert_parent(Id), Children), 193 maplist(assert_tree, Children). 194assert_tree(_). 195 196assert_parent(Id, Child) :- 197 node_id(Child, ChildId), 198 term_hash(Id, ParentHash), 199 term_hash(ChildId, ChildHash), 200 assertz(man_child_of(ChildHash, ChildId, ParentHash, Id)). 201 202node_id(node(Id, _), Id) :- !. 203node_id(Id, Id).
Contents.html for making the toplevel tree
that links to the individual files. Then we use
html_content_tree/2 to materialize the trees for the files.213man_content_tree(Spec, node(manual, Chapters)) :- 214 absolute_file_name(Spec, Dir, 215 [ file_type(directory), 216 access(read) 217 ]), 218 directory_file_path(Dir, 'Contents.html', ContentsFile), 219 load_html(ContentsFile, DOM, [cdata(string)]), 220 findall(Level-Path, 221 ( xpath(DOM, //div(@class=Class), DIV), 222 class_level(Class, Level), 223 xpath(DIV, a(@class=sec,@href=File), _), 224 \+ sub_atom(File, _, _, _, #), 225 directory_file_path(Dir, File, Path) 226 ), 227 Pairs), 228 index_chapters(Pairs, Chapters). 229 230class_level('toc-h1', 1). 231class_level('toc-h2', 2). 232class_level('toc-h3', 3). 233class_level('toc-h4', 4). 234 235index_chapters([], []). 236index_chapters([Level-File|T0], [node(Chapter, Children)|T]) :- 237 html_content_tree(File, Node), 238 Node = node(Chapter, Children0), 239 append(Children0, Sections, Children), 240 index_sections(T0, Level, Sections, T1), 241 index_chapters(T1, T). 242 243index_sections([], _, [], []) :- !. 244index_sections([SLevel-File|T0], Level, [Node|T], Rest) :- 245 SLevel > Level, 246 !, 247 html_content_tree(File, Node), 248 index_sections(T0, Level, T, Rest). 249index_sections(Rest, _, [], Rest).
xpce subtree so
xpce classes and their members appear in the navigation tree under
Documentation -> Packages -> XPCE.259man_packages_tree(node(packages, Packages)) :- 260 Section = section(0, _, _, _), 261 findall(File, 262 manual_object(Section, _Title, File, packages, _), 263 Files), 264 maplist(package_node, Files, Packages0), 265 ( man_xpce_tree(XpceTree) 266 -> Packages = [XpceTree|Packages0] 267 ; Packages = Packages0 268 ). 269 270man_xpce_tree(node(xpce, Chapters)) :- 271 catch(man_content_tree(swi_man_xpce('.'), node(_, Chapters)), 272 _, fail). 273 274package_node(File, Tree) :- 275 html_content_tree(File, Tree).
node(Object, ListOfTree).
288html_content_tree(FileIn, Tree) :- 289 absolute_file_name(FileIn, File), 290 findall(Offset-Obj, 291 manual_object(Obj, _Summary, File, _Class, Offset), 292 Pairs), 293 keysort(Pairs, Sorted), 294 pairs_values(Sorted, Objects), 295 make_tree(Objects, Trees), 296 assertion(Trees = [_]), 297 Trees = [Tree]. 298 299make_tree([], []). 300make_tree([Obj|T0], [node(Obj, Children)|T]) :- 301 children(T0, Obj, Children, T1), 302 make_tree(T1, T). 303 304children([], _, [], []) :- !. 305children([Obj|T0], Root, [Node|T], Rest) :- 306 section_level(Obj, ObjLevel), 307 section_level(Root, Level), 308 ObjLevel > Level, 309 !, 310 Node = node(Obj, Children), 311 children(T0, Obj, Children, T1), 312 children(T1, Root, T, Rest). 313children([Obj|T0], Root, [Obj|T], Rest) :- 314 \+ section_level(Obj, _), 315 !, 316 children(T0, Root, T, Rest). 317children(Rest, _, [], Rest). 318 319section_level(section(Level, _Nr, _Id, _File), Level). 320 321 322 /******************************* 323 * RETRIEVE * 324 *******************************/
333load_man_object(Obj, ParentSection, Path, DOM) :- 334 resolve_section(Obj, For), 335 For = section(_,SN,_ID,Path), 336 parent_section(For, ParentSection), 337 findall(Nr-Pos, section_start(Path, Nr, Pos), Pairs), 338 ( ( Pairs = [SN-_|_] 339 ; Pairs == [] 340 ) 341 -> !, 342 load_html(Path, DOM, [cdata(string)]) % Load whole file 343 ; append(_, [SN-Start|Rest], Pairs) 344 -> !, 345 ( member(N-End, Rest), 346 \+ sub_atom(N, 0, _, _, SN), 347 Len is End - Start, 348 Options = [content_length(Len)] 349 -> true 350 ; Options = [] 351 ), 352 open(Path, read, In, [type(binary)]), 353 seek(In, Start, bof, _), 354 dtd(html, DTD), 355 new_sgml_parser(Parser, 356 [ dtd(DTD) 357 ]), 358 set_sgml_parser(Parser, file(Path)), 359 set_sgml_parser(Parser, dialect(sgml)), 360 set_sgml_parser(Parser, encoding('utf-8')), 361 set_sgml_parser(Parser, shorttag(false)), 362 set_sgml_parser(Parser, defaults(false)), 363 call_cleanup(sgml_parse(Parser, 364 [ document(DOM), 365 source(In), 366 syntax_errors(quiet), 367 cdata(string) 368 | Options 369 ]), 370 ( free_sgml_parser(Parser), 371 close(In) 372 )) 373 ). 374load_man_object(For, Parent, Path, DOM) :- 375 object_spec(For, Obj), 376 manual_object(Obj, _, Path, _, Position), 377 ( object_section(Path, Position, Parent) 378 -> true 379 ; Parent = Path 380 ), 381 open(Path, read, In, [type(binary)]), 382 seek(In, Position, bof, _), 383 dtd(html, DTD), 384 new_sgml_parser(Parser, 385 [ dtd(DTD) 386 ]), 387 set_sgml_parser(Parser, file(Path)), 388 set_sgml_parser(Parser, dialect(sgml)), 389 set_sgml_parser(Parser, shorttag(false)), 390 set_sgml_parser(Parser, defaults(false)), 391 call_cleanup(parse_dts_upto_dd(Parser, In, DOM), 392 ( free_sgml_parser(Parser), 393 close(In) 394 )). 395 396parse_dts_upto_dd(Parser, In, Description) :- 397 sgml_parse(Parser, 398 [ document(DOM0), 399 cdata(string), 400 source(In), 401 parse(element), 402 syntax_errors(quiet) 403 ]), 404 ( DOM0 = [Element], 405 Element = element(dt, _, _) 406 -> Description = [Element|More], 407 parse_dts_upto_dd(Parser, In, More) 408 ; Description = DOM0 409 ). 410 411section_start(Path, Nr, Pos) :- 412 manual_object(section(_,Nr,_,_), _, Path, _, Pos).
420resolve_section(section(Level, No, Spec), Section) :- 421 !, 422 resolve_section(section(Level, No, _, Spec), Section). 423resolve_section(section(Level, No, ID, Path), 424 section(Level, No, ID, Path)) :- 425 nonvar(ID), 426 manual_object(section(Level,No,ID,Path), _, _, _, _), 427 !. 428resolve_section(section(Level, No, ID, Spec), 429 section(Level, No, ID, Path)) :- 430 ground(Spec), 431 absolute_file_name(Spec, Path, 432 [ access(read) 433 ]), 434 ( manual_object(section(Level, No, ID, Path), _, _, _, _) 435 -> true 436 ; path_allowed(Path) 437 -> true 438 ; permission_error(read, manual_file, Spec) 439 ). 440 441 442path_allowed(Path) :- % allow all files from swi/doc 443 absolute_file_name(swi(doc), Parent, 444 [ access(read), 445 file_type(directory) 446 ]), 447 sub_atom(Path, 0, _, _, Parent).
457parent_section(section(Level, Nr, _ID, File), Parent) :- 458 integer(Level), 459 Parent = section(PL, PNr, _PID, _PFile), 460 PL is Level - 1, 461 findall(B, sub_atom(Nr, B, _, _, '.'), BL), 462 last(BL, Before), 463 sub_atom(Nr, 0, Before, _, PNr), 464 ( manual_object(Parent, _, File, _, _) 465 -> true 466 ; manual_object(Parent, _, ParentFile, _, _), 467 same_dir(File, ParentFile) 468 -> true 469 ; manual_object(Parent, _, _, _, _) 470 ), 471 !. 472parent_section(section(Level, _, _, File), Parent) :- 473 Parent = section(ParentLevel, _, _, File), 474 manual_object(Parent, _, _, _, _), 475 ParentLevel < Level, 476 !. 477parent_section(section(_, _, _, File), File).
485object_section(Path, Pos, Section) :- 486 Section = section(_,_,_,_), 487 findall(Section, 488 (manual_object(Section, _, Path, _, SecPos), SecPos =< Pos), 489 List), 490 last(List, Section). 491 492same_dir(File1, File2) :- 493 file_directory_name(File1, Dir), 494 file_directory_name(File2, Dir).
501object_spec(Spec, Spec) :- 502 compound(Spec), 503 !. 504object_spec(Atom, Spec) :- 505 catch(atom_to_term(Atom, Spec, _), _, fail), 506 !, 507 Atom \== Spec. 508object_spec(Atom, PI) :- 509 atom_to_object(Atom, PI). 510 511 512 /******************************* 513 * EMIT * 514 *******************************/
f(Name/Arity)
display documentation of an arithmetic functionc(Function)
display documentation of a C API functionsection(Level, Number, Id, File)
Display a section of the manualOptions:
fail, fail instead of displaying a
not-found message.false, omit the synopsis linetrue (default), include links to the parent object;
if false, just emit the manual material.true (default), display the navigation tree, otherwise
suppress it.554man_page(Obj, Options) --> 555 { ground(Obj), 556 special_node(Obj) 557 }, 558 !, 559 html_requires(pldoc), 560 man_links([], Options), 561 man_matches([Obj], Obj, Options). 562man_page(Obj0, Options) --> % Manual stuff 563 { full_page(Obj0, Obj), 564 findall((Parent+Path)-(Obj+DOM), 565 load_man_object(Obj, Parent, Path, DOM), 566 Matches), 567 Matches = [_|_], 568 !, 569 pairs_keys(Matches, ParentPaths), 570 Matches = [Parent+Path-_|_] 571 }, 572 html_requires(pldoc), 573 man_links(ParentPaths, Options), 574 man_matches(Matches, Obj, Options). 575man_page(Obj, Options) --> % PlDoc predicates, etc. 576 { full_object(Obj, Full), 577 findall(Full-File, visible_doc_comment(Full, File, Options), Pairs), 578 Pairs \== [], 579 pairs_keys(Pairs, Objs) 580 }, 581 !, 582 html_requires(pldoc), 583 ( { Pairs = [_-File] } 584 -> object_page_header(File, Options) 585 ; object_page_header(-, Options) 586 ), 587 { merge_options(Options, 588 [ synopsis(true), 589 navtree(true) 590 ], Options2) 591 }, 592 objects(Objs, Options2). 593man_page(Obj, Options) --> % failure 594 { \+ option(no_manual(fail), Options) 595 }, 596 html_requires(pldoc), 597 man_links([], Options), 598 html(p(class(noman), 599 [ 'Sorry, No manual entry for ', 600 b('~w'-[Obj]) 601 ])). 602 603% Show an object if it is not private or it is fully qualified and we 604% want to show all fully qualified objects. Used by help/1. 605 606visible_doc_comment(Obj, File, Options) :- 607 doc_comment(Obj, File:_, _, _), 608 \+ ( private(Obj, Options), 609 \+ ( Obj = _:_, 610 option(qualified(always), Options) 611 ) 612 ). 613 614%special_node(manual). % redirected to the Introduction section 615special_node(root). 616special_node(packages). 617special_node(xpce). 618 619full_page(Obj, _) :- 620 var(Obj), !, fail. 621full_page(Obj, Obj) :- 622 Obj = section(_,_,_,_), 623 !. 624full_page(section(ID), section(_,_,ID,_)) :- !. 625full_page(manual, section(_,_,'sec:intro',_)) :- !. 626full_page(Obj0, Obj) :- 627 ground(Obj0), 628 alt_obj(Obj0, Obj), 629 manual_object(Obj, _, _, _, _), 630 !. 631full_page(Obj, Obj) :- 632 ground(Obj). 633 634alt_obj(Obj, Obj). 635alt_obj(Name/Arity, Name//DCGArity) :- 636 integer(Arity), 637 Arity >= 2, 638 DCGArity is Arity - 2. 639alt_obj(Name//DCGArity, Name/Arity) :- 640 integer(DCGArity), 641 Arity is DCGArity + 2.
647full_object(Object, M:Obj) :- 648 qualify(Object, M:Obj0), 649 alt_obj(Obj0, Obj), 650 doc_comment(M:Obj, _, _, _), 651 !. 652 653qualify(M:O, M:O). 654qualify(O, _:O).
The tricky part is that there are cases where multiple modules export the same predicate. We must find from the title of the manual section which library is documented.
666man_qualified_object(Text, Parent, LibOpt, Object, Section) :- 667 atom(Text), 668 atom_pi(Text, PI), 669 ground(PI), 670 !, 671 man_qualified_object_2(PI, Parent, LibOpt, Object, Section). 672man_qualified_object(Object0, Parent, LibOpt, Object, Section) :- 673 man_qualified_object_2(Object0, Parent, LibOpt, Object, Section). 674 675man_qualified_object_2(Name/Arity, Parent, 676 LibOpt, Module:Name/Arity, Section) :- 677 object_module(Parent, Module, Section, LibOpt), 678 !. 679man_qualified_object_2(Object, Parent, [], Object, Parent).
687:- public 688 man_synopsis//2. % called from man_match//2 689 690man_synopsis(PI, Section) --> 691 man_synopsis(PI, Section, []). 692 693man_synopsis(PI, Section, Options) --> 694 { object_href(Section, HREF) 695 }, 696 object_synopsis(PI, [href(HREF)|Options]).
702object_module(Section0, Module, Section, [source(Term)]) :- 703 parent_section_ndet(Section0, Section), 704 manual_object(Section, Title, _File, _Class, _Offset), 705 ( once(sub_atom(Title, B, _, _, :)), 706 sub_atom(Title, 0, B, _, Atom), 707 catch(term_to_atom(Term, Atom), _, fail), 708 ground(Term), 709 Term = library(_) 710 -> !, 711 absolute_file_name(Term, PlFile, 712 [ file_type(prolog), 713 access(read), 714 file_errors(fail) 715 ]), 716 ( module_property(Module, file(PlFile)) 717 -> true 718 ; xref_public_list(PlFile, -, % module is not loaded 719 [ module(Module) 720 ]) 721 ) 722 ). 723 724parent_section_ndet(Section, Section). 725parent_section_ndet(Section, Parent) :- 726 parent_section(Section, Parent0), 727 parent_section_ndet(Parent0, Parent). 728 729 730man_matches(Matches, Object, Options) --> 731 { option(navtree(false), Options) }, 732 !, 733 man_matches_nt(Matches, Object, Options). 734man_matches(Matches, Object, Options) --> 735 html([ div(class(navtree), 736 div(class(navwindow), 737 \man_nav_tree(Object, Options))), 738 div(class(navcontent), 739 \man_matches_nt(Matches, Object, Options)) 740 ]). 741 742 743man_matches_nt([Match], Object, Options) --> 744 { option(footer(true), Options, true) }, 745 !, 746 man_match(Match, Object, Options), 747 object_page_footer(Object, []). 748man_matches_nt(Matches, Object, Options) --> 749 man_matches_list(Matches, Object, Options). 750 751man_matches_list([], _, _) --> []. 752man_matches_list([H|T], Obj, Options) --> 753 man_match(H, Obj, Options), 754 man_matches_list(T, Obj, Options).
761man_match(packages, packages, _) --> 762 !, 763 html({|html|| 764 <p> 765 Packages are relatively independent add-on libraries that 766 may not be available in all installations. Packages are 767 part of the source code releases of SWI-Prolog and may be 768 enabled or disabled during the build.</p> 769 770 <p> 771 See also <a href="/pack/list">Add-ons</a> for extensions 772 provided by the community that must be installed separately 773 using 774 <a href="/pldoc/doc_for?object=pack_install/1">pack_install/1</a>.</p> 775 |}). 776man_match(xpce, xpce, _) --> 777 !, 778 html({|html|| 779 <p> 780 XPCE is the native graphical toolkit of SWI-Prolog. The 781 reference manual below documents the class hierarchy, global 782 objects, and topical guides.</p> 783 |}). 784man_match(root, root, _) --> 785 !, 786 man_overview([]). 787man_match((Parent+Path)-(Obj+DOM), Obj, Options) --> 788 { \+ option(synopsis(false), Options), 789 DOM = [element(dt,A,C0)|DD], 790 convlist(dt_obj, DOM, Objs), 791 option(link_source(Link), Options, true), 792 man_qualified_object(Obj, Parent, LibOpt, QObj, Section), 793 !, 794 C = [ span(style('float:right;margin-left:5px;'), 795 \object_source_button(QObj, [source_link(Link)])) 796 | C0 797 ] 798 }, 799 dom_list([ element(dt,[],[\man_synopsis(QObj, Section, LibOpt)]), 800 element(dt,A,C) 801 | DD 802 ], Path, Options), 803 object_footer(Objs, Options). 804man_match((_Parent+Path)-(Obj+DOM), Obj, Options) --> 805 dom_list(DOM, Path, Options). 806 807dt_obj(element(dt,_,C), Obj) :- 808 xpath(C, //a(@id=Atom), _), 809 atom_to_object(Atom, Obj). 810 811:- html_meta 812 dom_list(html, +, +, ?, ?). 813 814dom_list(_:[], _, _) --> 815 !, 816 []. 817dom_list(M:[H|T], Path, Options) --> 818 dom(H, Path, Options), 819 dom_list(M:T, Path, Options). 820 821dom(element(E, Atts, Content), Path, Options) --> 822 !, 823 dom_element(E, Atts, Content, Path, Options). 824dom(CDATA, _, _) --> 825 html(CDATA). 826 827dom_element(a, _, [], _, _) --> % Useless back-references 828 !, 829 []. 830dom_element(a, Att, Content, Path, Options) --> 831 { memberchk(href=HREF, Att), 832 ( memberchk(class=Class, Att) 833 -> true 834 ; Class = unknown 835 ), 836 rewrite_ref(Class, HREF, Path, Myref, Options) 837 }, 838 !, 839 html(a(href(Myref), \dom_list(Content, Path, Options))). 840dom_element(span, Att, [CDATA], _, Options) --> 841 { memberchk(class='pred-ext', Att), 842 atom_pi(CDATA, PI), 843 documented(PI), 844 ( option(server(false), Options) 845 -> public_link(predicate(CDATA), HREF) 846 ; http_link_to_id(pldoc_man, [predicate=CDATA], HREF) 847 ) 848 }, 849 !, 850 html(a(href(HREF), CDATA)). 851dom_element(img, Att0, [], Path, _Options) --> 852 { selectchk(src=Src, Att0, Att1), 853 relative_file_name(ImgFile, Path, Src), 854 handler_alias(Handler, DirAlias), 855 absolute_file_name(DirAlias, Dir, 856 [ file_errors(fail), 857 solutions(all), 858 file_type(directory) 859 ]), 860 ensure_slash(Dir, DirS), 861 atom_concat(DirS, NewSrc, ImgFile), 862 !, 863 http_link_to_id(Handler, [], ManRef), 864 directory_file_path(ManRef, NewSrc, NewPath), 865 Begin =.. [img, src(NewPath) | Att1] 866 }, 867 html_begin(Begin), 868 html_end(img). 869dom_element(div, Att, _, _, _) --> 870 { memberchk(class=navigate, Att) }, 871 !. 872dom_element(html, _, Content, Path, Options) --> 873 !, % do not emit a html for the second time 874 dom_list(Content, Path, Options). 875dom_element(head, _, Content, Path, Options) --> 876 !, % do not emit a head for the second time 877 dom_list(Content, Path, Options). 878dom_element(title, _, _, _, _) --> !. 879dom_element(link, _, _, _, _) --> !. 880dom_element(body, _, Content, Path, Options) --> 881 !, % do not emit a body for the second time 882 dom_list(Content, Path, Options). 883dom_element(Name, Attrs, Content, Path, Options) --> 884 { Begin =.. [Name|Attrs] }, 885 html_begin(Begin), 886 dom_list(Content, Path, Options), 887 html_end(Name). 888 889handler_alias(manual_file, swi_man_manual(.)). 890handler_alias(pldoc_package, swi_man_packages(.)). 891 892ensure_slash(Dir, DirS) :- 893 ( sub_atom(Dir, _, _, 0, /) 894 -> DirS = Dir 895 ; atom_concat(Dir, /, DirS) 896 ).
905public_link(predicate(CDATA), HREF) :-
906 uri_encoded(query_value, CDATA, Encoded),
907 atom_concat('https://www.swi-prolog.org/pldoc/doc_for?object=',
908 Encoded, HREF).915documented(PI) :- 916 manual_object(PI, _, _, _, _), 917 !. 918documented(PI) :- 919 full_object(PI, _Obj).
/man?predicate=PI.section(Level, NT, ID, FilePath)section(Level, NT, ID, FilePath)#flag:Namesection(Level, NT, ID, FilePath)#gloss:Name
$ File#Name() Rewrite to /man/CAPI=Name
/doc_for?object=xpce(Class,Kind,Name). Kind is one
of send, get, both, ivar or classvar.sec lookup.963rewrite_ref(_Class, Ref, _Path, Ref, Options) :- 964 option(server(false), Options), 965 !. 966rewrite_ref(Class, Ref0, Path, ManRef, _Options) :- 967 rewrite_ref(Class, Ref0, Path, ManRef). 968 969rewrite_ref(pred, Ref0, _, Ref) :- % Predicate/DCG reference 970 sub_atom(Ref0, _, _, A, '#'), 971 !, 972 sub_atom(Ref0, _, A, 0, Fragment), 973 atom_to_object(Fragment, PI), 974 manual_object(PI, _, _, _, _), 975 uri_encoded(query_value, Fragment, Enc), 976 http_location_by_id(pldoc_man, ManHandler), 977 format(string(Ref), '~w?predicate=~w', [ManHandler, Enc]). 978rewrite_ref(function, Ref0, _, Ref) :- % Arithmetic function reference 979 sub_atom(Ref0, _, _, A, '#'), 980 !, 981 sub_atom(Ref0, _, A, 0, Fragment), 982 atom_to_object(Fragment, PI), 983 manual_object(PI, _, _, _, _), 984 PI=f(Name/Arity), 985 format(atom(PIName), '~w/~w', [Name,Arity]), 986 uri_encoded(query_value, PIName, Enc), 987 http_location_by_id(pldoc_man, ManHandler), 988 format(string(Ref), '~w?function=~w', [ManHandler, Enc]). 989rewrite_ref(func, Ref0, _, Ref) :- % C-API reference 990 sub_atom(Ref0, _, _, A, '#'), 991 !, 992 sub_atom(Ref0, _, A, 0, Fragment), 993 atom_to_object(Fragment, Obj), 994 manual_object(Obj, _, _, _, _), 995 Obj = c(Function), 996 uri_encoded(query_value, Function, Enc), 997 http_location_by_id(pldoc_man, ManHandler), 998 format(string(Ref), '~w?CAPI=~w', [ManHandler, Enc]). 999rewrite_ref(sec, Ref0, Path, Ref) :- % Section inside a file 1000 sub_atom(Ref0, B, _, A, '#'), 1001 !, 1002 sub_atom(Ref0, _, A, 0, Fragment), 1003 sub_atom(Ref0, 0, B, _, File), 1004 referenced_section(Fragment, File, Path, Section), 1005 object_href(Section, Ref). 1006rewrite_ref(sec, File, Path, Ref) :- % Section is a file 1007 file_directory_name(Path, Dir), 1008 atomic_list_concat([Dir, /, File], SecPath), 1009 Obj = section(_, _, _, SecPath), 1010 manual_object(Obj, _, _, _, _), 1011 !, 1012 object_href(Obj, Ref). 1013rewrite_ref(cite, Ref0, Path, Ref) :- % Citation (bit hard-wired) 1014 debug(pldoc(cite), 'Cite ref ~q ~q', [Ref0, Path]), 1015 sub_atom(Ref0, _, _, A, '#'), 1016 !, 1017 sub_atom(Ref0, _, A, 0, Fragment), 1018 uri_encoded(query_value, Fragment, Enc), 1019 http_location_by_id(pldoc_man, ManHandler), 1020 format(string(Ref), '~w?section=bibliography#~w', [ManHandler, Enc]). 1021rewrite_ref(flag, Ref0, Path, Ref) :- 1022 sub_atom(Ref0, B, _, A, '#'), 1023 !, 1024 sub_atom(Ref0, 0, B, _, File), 1025 sub_atom(Ref0, _, A, 0, Fragment), 1026 file_directory_name(Path, Dir), 1027 atomic_list_concat([Dir, /, File], SecPath), 1028 Obj = section(_, _, _, SecPath), 1029 manual_object(Obj, _, _, _, _), 1030 !, 1031 object_href(Obj, Ref1), 1032 format(string(Ref), '~w#~w', [Ref1, Fragment]). 1033rewrite_ref(gloss, Ref0, Path, Ref) :- 1034 sub_atom(Ref0, B, _, A, '#'), 1035 !, 1036 sub_atom(Ref0, 0, B, _, File), 1037 sub_atom(Ref0, _, A, 0, Fragment), 1038 file_directory_name(Path, Dir), 1039 atomic_list_concat([Dir, /, File], SecPath), 1040 Obj = section(_, _, _, SecPath), 1041 manual_object(Obj, _, _, _, _), 1042 !, 1043 object_href(Obj, Ref1), 1044 format(string(Ref), '~w#~w', [Ref1, Fragment]). 1045rewrite_ref(_, Ref0, _, Ref) :- % xpce class member reference 1046 sub_atom(Ref0, _, _, A, '#'), 1047 sub_atom(Ref0, _, A, 0, Fragment), 1048 atomic_list_concat([class, ClassA, KindA, Name], '-', Fragment), 1049 xpce_kind(KindA), 1050 xpce_canonical_object(ClassA, KindA, Name, Obj), 1051 !, 1052 object_href(Obj, Ref). 1053rewrite_ref(_, Ref0, Path, Ref) :- % section ref carrying class="" 1054 sub_atom(Ref0, B, _, A, '#'), % (xpce: class refs, objects) 1055 sub_atom(Ref0, _, A, 0, Fragment), 1056 sub_atom(Fragment, 0, _, _, 'sec:'), 1057 sub_atom(Ref0, 0, B, _, File), 1058 referenced_section(Fragment, File, Path, Section), 1059 !, 1060 object_href(Section, Ref). 1061 1062% The anchor id encodes the access form used at the call site 1063% (=|class-<class>-<kind>-<name>|=) but each method/instance variable 1064% has only one canonical xpce(...) data-obj. An ivar e.g. emits 1065% alias anchors for its send/get/both accessors, and a =both= anchor 1066% is a shorthand that resolves to whichever send/get/ivar form the 1067% class actually defines. Try the parsed kind first, then fall back 1068% to the alternative forms in order of likelihood. 1069 1070xpce_canonical_object(Class, Kind, Name, xpce(Class, Kind, Name)) :- 1071 manual_object(xpce(Class, Kind, Name), _, _, _, _), 1072 !. 1073xpce_canonical_object(Class, _, Name, Obj) :- 1074 member(K, [ivar, send, get, classvar, both]), 1075 Obj = xpce(Class, K, Name), 1076 manual_object(Obj, _, _, _, _), 1077 !. 1078 1079xpce_kind(send). 1080xpce_kind(get). 1081xpce_kind(both). 1082xpce_kind(ivar). 1083xpce_kind(classvar).
1087referenced_section(Fragment, File, Path, section(Level, Nr, ID, SecPath)) :-
1088 atom_concat('sec:', Nr, Fragment),
1089 ( File == ''
1090 -> SecPath = Path
1091 ; file_directory_name(Path, Dir),
1092 atomic_list_concat([Dir, /, File], SecPath)
1093 ),
1094 manual_object(section(Level, Nr, ID, SecPath), _, _, _, _).1101man_links(ParentPaths, Options) --> 1102 prolog:doc_page_header(parents(ParentPaths), Options), 1103 !. 1104man_links(ParentPaths, Options) --> 1105 { option(links(true), Options, true), 1106 option(header(true), Options, true) 1107 }, 1108 !, 1109 html([ div(class(navhdr), 1110 [ div(class(jump), \man_parent(ParentPaths)), 1111 div(class(search), \search_form(Options)), 1112 br(clear(right)) 1113 ]), 1114 p([]) 1115 ]). 1116man_links(_, _) --> 1117 []. 1118 1119man_parent(ParentPaths) --> 1120 { maplist(parent_to_section, ParentPaths, [Section|MoreSections]), 1121 maplist(=(Section), MoreSections) 1122 }, 1123 !, 1124 object_ref(Section, [secref_style(number_title)]). 1125man_parent(_) --> []. 1126 1127parent_to_section(X+_, X) :- 1128 X = section(_,_,_,_), 1129 !. 1130parent_to_section(File+_, Section) :- 1131 atom(File), 1132 manual_object(Section, _Title, File, _Class, _Offset), 1133 !.
number, title or number_title.1142section_link(Section, Options) --> 1143 { option(secref_style(Style), Options, number) 1144 }, 1145 section_link(Style, Section, Options). 1146 1147section_link(number, section(_, Number, _, _), _Options) --> 1148 !, 1149 ( {Number == '0'} % Title. Package? 1150 -> [] 1151 ; html(['Sec. ', Number]) 1152 ). 1153section_link(title, Obj, _Options) --> 1154 !, 1155 { manual_object(Obj, Title, _File, _Class, _Offset) 1156 }, 1157 html(Title). 1158section_link(_, Obj, _Options) --> 1159 !, 1160 { Obj = section(_, Number, _, _), 1161 manual_object(Obj, Title, _File, _Class, _Offset) 1162 }, 1163 ( { Number == '0' } 1164 -> html(Title) 1165 ; html([Number, ' ', Title]) 1166 ).
1172function_link(Function, _) --> 1173 html([Function, '()']). 1174 1175 1176 /******************************* 1177 * INDICES & OVERVIEW * 1178 *******************************/
1185man_overview(Options) --> 1186 { http_absolute_location(pldoc_man(.), RefMan, []) 1187 }, 1188 html([ h1('SWI-Prolog documentation'), 1189 blockquote(class(refman_link), 1190 a(href(RefMan), 1191 'SWI-Prolog reference manual')), 1192 \package_overview(Options), 1193 \paperback(Options) 1194 ]). 1195 1196package_overview(Options) --> 1197 html([ h2(class(package_doc_title), 1198 'SWI-Prolog package documentation'), 1199 blockquote(class(package_overview), 1200 \packages(Options)) 1201 ]). 1202 1203packages(Options) --> 1204 { findall(Pkg, current_package(Pkg), Pkgs) 1205 }, 1206 packages(Pkgs, Options). 1207 1208packages([], _) --> 1209 []. 1210packages([Pkg|T], Options) --> 1211 package(Pkg, Options), 1212 packages(T, Options). 1213 1214package(pkg(Title, HREF, HavePackage), Options) --> 1215 { package_class(HavePackage, Class, Options) 1216 }, 1217 html(div(class(Class), 1218 a([href(HREF)], Title))). 1219 1220package_class(true, pkg_link, _). 1221package_class(false, no_pkg_link, _). 1222 1223current_package(pkg(Title, HREF, HavePackage)) :- 1224 manual_object(section(0, _, _, _), Title, File, packages, _), 1225 file_base_name(File, FileNoDir), 1226 file_name_extension(Base, _, FileNoDir), 1227 ( exists_source(library(Base)) 1228 -> HavePackage = true 1229 ; HavePackage = false 1230 ), 1231 http_absolute_location(pldoc_pkg(FileNoDir), HREF, []). 1232 1233 1234:- if(current_predicate(http_handler/3)). 1235:- http_handler(pldoc(jpl), pldoc_jpl, [prefix]). 1236:- http_handler(pldoc_pkg(.), pldoc_package, [prefix]). 1237:- http_handler(pldoc_man(.), pldoc_refman, [prefix]). 1238:- http_handler(pldoc(packages), pldoc_package_overview, []).
1244pldoc_jpl(Request) :-
1245 memberchk(path_info(JPLFile), Request),
1246 atom_concat('doc/packages/jpl', JPLFile, Path),
1247 http_reply_file(swi(Path), [], Request).1256pldoc_package(Request) :- 1257 ( \+ option(path_info(_), Request) 1258 -> true 1259 ; option(path_info(/), Request) 1260 ), 1261 http_link_to_id(pldoc_object, [object=packages], HREF), 1262 http_redirect(see_other, HREF, Request). 1263pldoc_package(Request) :- 1264 memberchk(path_info(Img), Request), 1265 file_mime_type(Img, image/_), 1266 !, 1267 http_reply_file(swi_man_packages(Img), [], Request). 1268pldoc_package(Request) :- 1269 memberchk(path_info('jpl'), Request), 1270 !, 1271 memberchk(path(Path0), Request), 1272 atom_concat(Path0, /, Path), 1273 http_redirect(moved, Path, Request). 1274pldoc_package(Request) :- 1275 memberchk(path_info(JPLFile), Request), 1276 ( JPLFile == 'jpl/' 1277 -> Path = 'doc/packages/jpl/index.html' 1278 ; sub_atom(JPLFile, 0, _, _, 'jpl/') 1279 -> atom_concat('doc/packages/', JPLFile, Path) 1280 ), 1281 http_reply_file(swi(Path), [], Request). 1282pldoc_package(Request) :- 1283 memberchk(path_info(PkgDoc), Request), 1284 ensure_html_ext(PkgDoc, PkgHtml), 1285 atom_concat('packages/', PkgHtml, Path), 1286 term_to_atom(section(Path), Object), 1287 http_link_to_id(pldoc_object, [object=Object], HREF), 1288 http_redirect(see_other, HREF, Request). 1289 1290ensure_html_ext(Pkg, PkgHtml) :- 1291 file_name_extension(_, html, Pkg), 1292 !, 1293 PkgHtml = Pkg. 1294ensure_html_ext(Pkg, PkgHtml) :- 1295 file_name_extension(Pkg, html, PkgHtml).
1301pldoc_package_overview(_Request) :- 1302 reply_html_page( 1303 pldoc(packages), 1304 title('SWI-Prolog package documentation'), 1305 \package_overview([])). 1306:- endif.
1312paperback(_Options) -->
1313 { expand_url_path(swipl_book(.), HREF)
1314 },
1315 html([ h2('The manual as a book'),
1316 p([ 'A paperback version of the manual is ',
1317 a(href(HREF), 'available'), '.'
1318 ])
1319 ]).1326pldoc_refman(Request) :- 1327 memberchk(path_info(Section), Request), 1328 \+ sub_atom(Section, _, _, _, /), 1329 Obj = section(0,_,_,_), 1330 manual_object(Obj, Title, File, manual, _), 1331 file_base_name(File, Section), 1332 !, 1333 reply_html_page(pldoc(man), 1334 title(Title), 1335 \object_page(Obj, [])). 1336pldoc_refman(Request) :- % server Contents.html 1337 \+ memberchk(path_info(_), Request), 1338 !, 1339 http_link_to_id(pldoc_object, [object(manual)], HREF), 1340 http_redirect(see_other, HREF, Request). 1341pldoc_refman(Request) :- 1342 memberchk(path(Path), Request), 1343 existence_error(http_location, Path). 1344 1345 1346 /******************************* 1347 * HOOK SEARCH * 1348 *******************************/ 1349 1350prologdoc_object_summary(section(ID), Class, File, Summary) :- 1351 nonvar(ID), % when generating, only do full ones 1352 manual_object(section(_Level, _No, ID, _Path), Summary, File, Class, _Offset). 1353prologdoc_object_summary(Obj, Class, File, Summary) :- 1354 manual_object(Obj, Summary, File, Class, _Offset). 1355 1356prologdoc_object_page(Obj, Options) --> 1357 man_page(Obj, [no_manual(fail),footer(false)|Options]).
1363prologdoc_object_link(Obj, Options) --> 1364 { Obj = section(_,_,_,_) }, 1365 !, 1366 section_link(Obj, Options). 1367prologdoc_object_link(Obj0, Options) --> 1368 { Obj0 = section(ID), 1369 Obj = section(_Level, _No, ID, _Path), 1370 manual_object(Obj, _, _, _, _) 1371 }, 1372 !, 1373 section_link(Obj, Options). 1374prologdoc_object_link(Obj, Options) --> 1375 { Obj = c(Function) }, 1376 !, 1377 function_link(Function, Options). 1378prologdoc_object_link(root, _) --> 1379 !, 1380 html('Documentation'). 1381prologdoc_object_link(manual, _Options) --> 1382 !, 1383 html('Reference manual'). 1384prologdoc_object_link(packages, _) --> 1385 html('Packages'). 1386prologdoc_object_link(xpce, _) --> 1387 html('XPCE'). 1388 1389prologdoc_category(manual, 30, 'Reference Manual'). 1390prologdoc_category(packages, 40, 'Packages'). 1391 1392prologdoc_file_index_header(File, Options) --> 1393 { Section = section(_Level, _No, _ID, File), 1394 manual_object(Section, _Summary, File, _Cat, _Offset) 1395 }, 1396 !, 1397 html(tr(th([colspan(3), class(section)], 1398 [ \object_ref(Section, 1399 [ secref_style(number_title) 1400 | Options 1401 ]) 1402 ]))). 1403 1404prologdoc_object_title(Obj, Title) :- 1405 Obj = section(_,_,_,_), 1406 manual_object(Obj, Title, _, _, _), 1407 !. 1408prologdoc_object_title(xpce, 'XPCE'). 1409prologdoc_object_title(xpce(Class, Kind, Name), Title) :- 1410 xpce_member_arrow(Kind, Arrow), 1411 format(atom(Title), '~w~w~w', [Class, Arrow, Name]). 1412 1413% xpce class members appear in the index as xpce(Class, Kind, Name) 1414% (see packages/pldoc/doc_util.pl atom_to_object/2 and packages/xpce/ 1415% man/refmanual). Render link text as the conventional xpce arrow 1416% notation -- Class->name / Class<-name / Class<->name / Class.name -- 1417% so the entries are recognisable next to predicates. 1418 1419prologdoc_object_link(xpce(Class, Kind, Name), _Options) --> 1420 { xpce_member_arrow(Kind, Arrow) }, 1421 html([Class, Arrow, Name]). 1422 1423xpce_member_arrow(send, '->'). 1424xpce_member_arrow(get, '<-'). 1425xpce_member_arrow(both, '<->'). 1426xpce_member_arrow(ivar, '-'). 1427xpce_member_arrow(classvar, '.'). 1428 1429% Predicates such as get_class/4 live in module =pce_principal=, but 1430% the user-facing entry point is =|library(pce)|=, which re-exports 1431% the boot modules. Rewrite the synopsis so it says what users 1432% should actually type. 1433 1434prologpldoc_synopsis_spec(pce(prolog/boot/_), library(pce)). 1435 1436prologdoc_canonical_object(section(_Level, _No, ID, _Path), 1437 section(ID)).
1443prologdoc_object_href(section(ID), HREF) :- 1444 nonvar(ID), 1445 atom_concat('sec:', Sec, ID), 1446 http_link_to_id(pldoc_man, [section(Sec)], HREF). 1447prologdoc_object_href(section(_Level, _No, ID, _Path), HREF) :- 1448 nonvar(ID), 1449 atom_concat('sec:', Sec, ID), 1450 http_link_to_id(pldoc_man, [section(Sec)], HREF). 1451 1452 /******************************* 1453 * NO HTTP * 1454 *******************************/ 1455 1456:- if(\+current_predicate(http_link_to_id/3)). 1457 1458http_link_to_id(Id, Parameters, HREF) :- 1459 must_be(list, Parameters), 1460 format(atom(Location), '/pldoc/~w', [Id]), 1461 ( Parameters == [] 1462 -> HREF = Location 1463 ; uri_data(path, Components, Location), 1464 uri_query_components(String, Parameters), 1465 uri_data(search, Components, String), 1466 uri_components(HREF, Components) 1467 ). 1468 1469:- endif. 1470 1471 /******************************* 1472 * MESSAGES * 1473 *******************************/ 1474 1475:- multifile prolog:message//1. 1476 1477prologmessage(pldoc(no_section_id(File, Title))) --> 1478 [ 'PlDoc: ~w: no id for section "~w"'-[File, Title] ]. 1479prologmessage(pldoc(duplicate_ids(L))) --> 1480 [ 'PlDoc: duplicate manual section IDs:'-[], nl 1481 ], 1482 duplicate_ids(L). 1483 1484duplicate_ids([]) --> []. 1485duplicate_ids([H|T]) --> duplicate_id(H), duplicate_ids(T). 1486 1487duplicate_id(Id) --> 1488 { findall(File, manual_object(section(_,_,Id,File),_,_,_,_), Files) }, 1489 [ ' ~w: ~p'-[Id, Files], nl ]
Process SWI-Prolog HTML manuals