View source with raw comments or as raw
    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_param_object/2,         % +Param, -Object
   42	    pldoc_href_object/2,        % +HREF, -Object
   43	    man_object_uri/2,           % +Object, -URI
   44	    man_uri_object/2,           % +URI, -Object
   45	    xpce_object_label/2,        % +Object, -Label
   46
   47	    man_content_tree/2,         % +Dir, -Tree
   48	    man_packages_tree/1         % -Tree
   49	  ]).   50:- use_module(library(xpath),[xpath/3, op(_,_,_)]).   51:- use_module(library(http/html_write)).   52:- use_module(library(debug),[assertion/1,debug/3]).   53
   54:- autoload(doc_html,
   55	    [ object_tree/5, private/2, object_page_header/4, objects/4,
   56	      object_href/2, object_synopsis/4, object_footer/4,
   57	      object_page_footer/4, localise_object/2,
   58	      object_ref/4, object_page/4,
   59	      object_source_button//2
   60	    ]).   61:- autoload(doc_process,[doc_comment/4]).   62:- autoload(doc_search,[search_form/3]).   63:- autoload(doc_util,[atom_to_object/2,atom_pi/2]).   64:- autoload(man_index,[manual_object/5]).   65:- autoload(library(apply),[maplist/2,maplist/3,convlist/3]).   66:- autoload(library(error),[permission_error/3,existence_error/2]).   67:- autoload(library(filesex),
   68	    [directory_file_path/3,relative_file_name/3]).   69:- autoload(library(lists),
   70	    [select/4,append/3,member/2,last/2,selectchk/3]).   71:- autoload(library(option),[merge_options/3,option/2,option/3]).   72:- autoload(library(pairs),[pairs_values/2,pairs_keys/2]).   73:- autoload(library(prolog_xref),[xref_public_list/3]).   74:- autoload(library(sgml),
   75	    [ load_html/3, dtd/2, new_sgml_parser/2, set_sgml_parser/2,
   76	      sgml_parse/2, free_sgml_parser/1
   77	    ]).   78:- autoload(library(uri),
   79	    [uri_encoded/3, uri_components/2, uri_data/3,
   80	     uri_query_components/2]).   81:- autoload(library(www_browser),[expand_url_path/2]).   82:- autoload(library(http/html_head),[html_requires/3]).   83:- if(exists_source(library(http/http_dispatch))).   84:- use_module(library(http/http_dispatch),
   85              [ http_link_to_id/3, http_location_by_id/2,
   86                http_handler/3, http_reply_file/3, http_redirect/3
   87              ]).   88:- endif.   89:- autoload(library(http/http_path),[http_absolute_location/3]).   90:- autoload(library(http/mimetype),[file_mime_type/2]).   91
   92:- include(hooks).

Process SWI-Prolog HTML manuals

   98:- predicate_options(man_page//2, 2,
   99		     [ for(atom),
  100		       links(boolean),
  101		       navtree(boolean),
  102		       link_scheme(atom),
  103		       synopsis(boolean),
  104		       footer(boolean),
  105		       link_source(boolean),
  106		       no_manual(oneof([fail,error])),
  107		       search_in(oneof([all, app, man])),
  108		       search_match(oneof([name, summary])),
  109		       search_options(boolean)
  110		     ]).  111
  112
  113		 /*******************************
  114		 *           HIERARCHY          *
  115		 *******************************/
 man_nav_tree(+Obj, +Options) is semidet
Create a navigation tree consisting of a nested ul list that reflects the location of Obj in the manual.
  122man_nav_tree(Obj, Options) -->
  123    { ensure_man_tree,
  124      man_nav_tree(Obj, Tree, Options),
  125      TreeOptions = [ secref_style(title)
  126		    | Options
  127		    ]
  128    },
  129    html(ul(class(nav),
  130	    \object_tree(Tree, [Obj], TreeOptions))).
 man_nav_tree(+Obj, -Tree, +Options) is semidet
True when Tree is the navigation tree for Obj. By default, this is the tree going from the leaf to the root, unfolding the neighbors of Obj.
  139man_nav_tree(Obj, Tree, _Options) :-
  140    man_child_of(Obj, Parent),
  141    !,
  142    findall(Neighbour, man_child_of(Neighbour, Parent), Neighbours0),
  143    (   findall(Child, man_child_of(Child, Obj), Children),
  144	Children \== []
  145    ->  select(Obj, Neighbours0, node(Obj, Children), Neighbours)
  146    ;   Neighbours = Neighbours0
  147    ),
  148    path_up(node(Parent, Neighbours), Tree).
  149man_nav_tree(Obj, node(Obj, Children), _Options) :-
  150    findall(Child, man_child_of(Child, Obj), Children).
  151
  152
  153path_up(Node, Tree) :-
  154    node_id(Node, Id),
  155    man_child_of(Id, Parent),
  156    !,
  157    (   Parent == root
  158    ->  findall(Neighbour, man_child_of(Neighbour, Parent), Neighbours0),
  159	select(Id, Neighbours0, Node, Neighbours),
  160	Tree = node(root, Neighbours)
  161    ;   path_up(node(Parent, [Node]), Tree)
  162    ).
  163path_up(Tree, Tree).
 man_child_of(?Child, ?Parent) is nondet
Query the manual hierarchy.
  170man_child_of(Child, Parent) :-
  171    term_hash(Child, ChildHash),
  172    term_hash(Parent, ParentHash),
  173    man_child_of(ChildHash, Child, ParentHash, Parent).
  174
  175:- dynamic
  176    man_child_of/4,
  177    man_tree_done/0.
 ensure_man_tree
Materialize the manual tree as a binary relation.
  183ensure_man_tree :-
  184    man_tree_done,
  185    !.
  186ensure_man_tree :-
  187    with_mutex(man_tree,
  188	       make_man_tree).
  189
  190make_man_tree :-
  191    man_tree_done,
  192    !.
  193make_man_tree :-
  194    man_content_tree(swi_man_manual('.'), ManTree),
  195    man_packages_tree(PkgTree),
  196    assert_tree(node(root, [ManTree, PkgTree])),
  197    assertz(man_tree_done).
  198
  199assert_tree(node(Id, Children)) :-
  200    !,
  201    maplist(assert_parent(Id), Children),
  202    maplist(assert_tree, Children).
  203assert_tree(_).
  204
  205assert_parent(Id, Child) :-
  206    node_id(Child, ChildId),
  207    term_hash(Id, ParentHash),
  208    term_hash(ChildId, ChildHash),
  209    assertz(man_child_of(ChildHash, ChildId, ParentHash, Id)).
  210
  211node_id(node(Id, _), Id) :- !.
  212node_id(Id, Id).
 man_content_tree(+Dir, -Tree) is det
Compute the content tree for a multi-file HTML document. We do this by processing 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.
  222man_content_tree(Spec, node(manual, Chapters)) :-
  223    absolute_file_name(Spec, Dir,
  224		       [ file_type(directory),
  225			 access(read)
  226		       ]),
  227    directory_file_path(Dir, 'Contents.html', ContentsFile),
  228    load_html(ContentsFile, DOM, [cdata(string)]),
  229    findall(Level-Path,
  230	    ( xpath(DOM, //div(@class=Class), DIV),
  231	      class_level(Class, Level),
  232	      xpath(DIV, a(@class=sec,@href=File), _),
  233	      \+ sub_atom(File, _, _, _, #),
  234	      directory_file_path(Dir, File, Path)
  235	    ),
  236	    Pairs),
  237    index_chapters(Pairs, Chapters).
  238
  239class_level('toc-h1', 1).
  240class_level('toc-h2', 2).
  241class_level('toc-h3', 3).
  242class_level('toc-h4', 4).
  243
  244index_chapters([], []).
  245index_chapters([Level-File|T0], [node(Chapter, Children)|T]) :-
  246    html_content_tree(File, Node),
  247    Node = node(Chapter, Children0),
  248    append(Children0, Sections, Children),
  249    index_sections(T0, Level, Sections, T1),
  250    index_chapters(T1, T).
  251
  252index_sections([], _, [], []) :- !.
  253index_sections([SLevel-File|T0], Level, [Node|T], Rest) :-
  254    SLevel > Level,
  255    !,
  256    html_content_tree(File, Node),
  257    index_sections(T0, Level, T, Rest).
  258index_sections(Rest, _, [], Rest).
 man_packages_tree(-Tree) is det
Tree is the content tree of all packages. When the xpce reference manual is installed it is prepended as a single xpce subtree so xpce classes and their members appear in the navigation tree under Documentation -> Packages -> XPCE.
  268man_packages_tree(node(packages, Packages)) :-
  269    Section = section(0, _, _, _),
  270    findall(File,
  271	    manual_object(Section, _Title, File, packages, _),
  272	    Files),
  273    maplist(package_node, Files, Packages0),
  274    (   man_xpce_tree(XpceTree)
  275    ->  Packages = [XpceTree|Packages0]
  276    ;   Packages = Packages0
  277    ).
  278
  279man_xpce_tree(node(xpce, Chapters)) :-
  280    catch(man_content_tree(swi_man_xpce('.'), node(_, Chapters)),
  281          _, fail).
  282
  283package_node(File, Tree) :-
  284    html_content_tree(File, Tree).
 html_content_tree(+ManualFile, -Tree) is det
True when Tree represents the hierarchical structure of objects documented in the HTML file ManualFile. Tree is a term where of the form below. Object is a documentation object (typically a section or predicate indicator) that may be handed to object_link//1 and similar predicates to make a table of contents.
node(Object, ListOfTree).
  297html_content_tree(FileIn, Tree) :-
  298    absolute_file_name(FileIn, File),
  299    findall(Offset-Obj,
  300	    manual_object(Obj, _Summary, File, _Class, Offset),
  301	    Pairs),
  302    keysort(Pairs, Sorted),
  303    pairs_values(Sorted, Objects),
  304    make_tree(Objects, Trees),
  305    assertion(Trees = [_]),
  306    Trees = [Tree].
  307
  308make_tree([], []).
  309make_tree([Obj|T0], [node(Obj, Children)|T]) :-
  310    children(T0, Obj, Children, T1),
  311    make_tree(T1, T).
  312
  313children([], _, [], []) :- !.
  314children([Obj|T0], Root, [Node|T], Rest) :-
  315    section_level(Obj, ObjLevel),
  316    section_level(Root, Level),
  317    ObjLevel > Level,
  318    !,
  319    Node = node(Obj, Children),
  320    children(T0, Obj, Children, T1),
  321    children(T1, Root, T, Rest).
  322children([Obj|T0], Root, [Obj|T], Rest) :-
  323    \+ section_level(Obj, _),
  324    !,
  325    children(T0, Root, T, Rest).
  326children(Rest, _, [], Rest).
  327
  328section_level(section(Level, _Nr, _Id, _File), Level).
  329
  330
  331		 /*******************************
  332		 *            RETRIEVE          *
  333		 *******************************/
 load_man_object(+Obj, -Parent, -Path, -DOM) is nondet
load the desription of the object matching Obj from the HTML sources and return the DT/DD pair in DOM.
To be done
- Nondet?
  342load_man_object(Obj, ParentSection, Path, DOM) :-
  343    resolve_section(Obj, For),
  344    For = section(_,SN,_ID,Path),
  345    parent_section(For, ParentSection),
  346    findall(Nr-Pos, section_start(Path, Nr, Pos), Pairs),
  347    (   (   Pairs = [SN-_|_]
  348	;   Pairs == []
  349	)
  350    ->  !,
  351	load_html(Path, DOM, [cdata(string)])           % Load whole file
  352    ;   append(_, [SN-Start|Rest], Pairs)
  353    ->  !,
  354	(   member(N-End, Rest),
  355	    \+ sub_atom(N, 0, _, _, SN),
  356	    Len is End - Start,
  357	    Options = [content_length(Len)]
  358	->  true
  359	;   Options = []
  360	),
  361	open(Path, read, In, [type(binary)]),
  362	seek(In, Start, bof, _),
  363	dtd(html, DTD),
  364	new_sgml_parser(Parser,
  365			[ dtd(DTD)
  366			]),
  367	set_sgml_parser(Parser, file(Path)),
  368	set_sgml_parser(Parser, dialect(sgml)),
  369	set_sgml_parser(Parser, encoding('utf-8')),
  370	set_sgml_parser(Parser, shorttag(false)),
  371	set_sgml_parser(Parser, defaults(false)),
  372	call_cleanup(sgml_parse(Parser,
  373				[ document(DOM),
  374				  source(In),
  375				  syntax_errors(quiet),
  376				  cdata(string)
  377				| Options
  378				]),
  379		     ( free_sgml_parser(Parser),
  380		       close(In)
  381		     ))
  382    ).
  383load_man_object(For, Parent, Path, DOM) :-
  384    object_spec(For, Obj),
  385    manual_object(Obj, _, Path, _, Position),
  386    (   object_section(Path, Position, Parent)
  387    ->  true
  388    ;   Parent = Path
  389    ),
  390    open(Path, read, In, [type(binary)]),
  391    seek(In, Position, bof, _),
  392    dtd(html, DTD),
  393    new_sgml_parser(Parser,
  394		    [ dtd(DTD)
  395		    ]),
  396    set_sgml_parser(Parser, file(Path)),
  397    set_sgml_parser(Parser, dialect(sgml)),
  398    set_sgml_parser(Parser, shorttag(false)),
  399    set_sgml_parser(Parser, defaults(false)),
  400    call_cleanup(parse_dts_upto_dd(Parser, In, DOM),
  401		 ( free_sgml_parser(Parser),
  402		   close(In)
  403		 )).
  404
  405parse_dts_upto_dd(Parser, In, Description) :-
  406    sgml_parse(Parser,
  407	       [ document(DOM0),
  408		 cdata(string),
  409		 source(In),
  410		 parse(element),
  411		 syntax_errors(quiet)
  412	       ]),
  413    (   DOM0 = [Element],
  414	Element = element(dt, _, _)
  415    ->  Description = [Element|More],
  416	parse_dts_upto_dd(Parser, In, More)
  417    ;   Description = DOM0
  418    ).
  419
  420section_start(Path, Nr, Pos) :-
  421    manual_object(section(_,Nr,_,_), _, Path, _, Pos).
 resolve_section(+SecIn, -SecOut) is det
Resolve symbolic path reference and fill in level and section number if this information is missing. The latter allows us to refer to files of the manual.
  429resolve_section(section(Level, No, Spec), Section) :-
  430    !,
  431    resolve_section(section(Level, No, _, Spec), Section).
  432resolve_section(section(Level, No, ID, Path),
  433		section(Level, No, ID, Path)) :-
  434    nonvar(ID),
  435    manual_object(section(Level,No,ID,Path), _, _, _, _),
  436    !.
  437resolve_section(section(Level, No, ID, Spec),
  438		section(Level, No, ID, Path)) :-
  439    ground(Spec),
  440    absolute_file_name(Spec, Path,
  441		       [ access(read)
  442		       ]),
  443    (   manual_object(section(Level, No, ID, Path), _, _, _, _)
  444    ->  true
  445    ;   path_allowed(Path)
  446    ->  true
  447    ;   permission_error(read, manual_file, Spec)
  448    ).
  449
  450
  451path_allowed(Path) :-                   % allow all files from swi/doc
  452    absolute_file_name(swi(doc), Parent,
  453		       [ access(read),
  454			 file_type(directory)
  455		       ]),
  456    sub_atom(Path, 0, _, _, Parent).
 parent_section(+Section, -Parent) is det
Parent is the parent-section of Section. First computes the section number and than finds the required number in the same file or same directory. If this doesn't exist, get the file as a whole.
  466parent_section(section(Level, Nr, _ID, File), Parent) :-
  467    integer(Level),
  468    Parent = section(PL, PNr, _PID, _PFile),
  469    PL is Level - 1,
  470    findall(B, sub_atom(Nr, B, _, _, '.'), BL),
  471    last(BL, Before),
  472    sub_atom(Nr, 0, Before, _, PNr),
  473    (   manual_object(Parent, _, File, _, _)
  474    ->  true
  475    ;   manual_object(Parent, _, ParentFile, _, _),
  476	same_dir(File, ParentFile)
  477    ->  true
  478    ;   manual_object(Parent, _, _, _, _)
  479    ),
  480    !.
  481parent_section(section(Level, _, _, File), Parent) :-
  482    Parent = section(ParentLevel, _, _, File),
  483    manual_object(Parent, _, _, _, _),
  484    ParentLevel < Level,
  485    !.
  486parent_section(section(_, _, _, File), File).
 object_section(+Path, +Position, -Section) is semidet
Section is the section in which object appears. This is the last section object before position.
  494object_section(Path, Pos, Section) :-
  495    Section = section(_,_,_,_),
  496    findall(Section,
  497	   (manual_object(Section, _, Path, _, SecPos), SecPos =< Pos),
  498	    List),
  499    last(List, Section).
  500
  501same_dir(File1, File2) :-
  502    file_directory_name(File1, Dir),
  503    file_directory_name(File2, Dir).
 object_spec(+Atom, -SpecTerm)
Tranform the Name/Arity, etc strings as received from the HTTP into a term. Must return unique results.
  510object_spec(Spec, Spec) :-
  511    compound(Spec),
  512    !.
  513object_spec(Atom, Spec) :-
  514    catch(atom_to_term(Atom, Spec, _), _, fail),
  515    !,
  516    Atom \== Spec.
  517object_spec(Atom, PI) :-
  518    atom_to_object(Atom, PI).
  519
  520
  521		 /*******************************
  522		 *            EMIT              *
  523		 *******************************/
 man_page(+Obj, +Options)// is semidet
Produce a Prolog manual page for Obj. The page consists of a link to the section-file and a search field, followed by the predicate description. Obj is one of:

Options:

no_manual(Action)
If Action = fail, fail instead of displaying a not-found message.
synopsis(Bool)
If false, omit the synopsis line
links(Bool)
If true (default), include links to the parent object; if false, just emit the manual material.
navtree(Bool)
If true (default), display the navigation tree, otherwise suppress it.
link_scheme(Scheme)
Write the manual references as Scheme:Object IRIs rather than as links into the PlDoc server. References that do not resolve to a documentation object are removed. Used by help/1 to create clickable terminal output. See man_object_uri/2.
  570man_page(Obj, Options) -->
  571    { ground(Obj),
  572      special_node(Obj)
  573    },
  574    !,
  575    html_requires(pldoc),
  576    man_links([], Options),
  577    man_matches([Obj], Obj, Options).
  578man_page(Obj0, Options) -->                     % Manual stuff
  579    { full_page(Obj0, Obj),
  580      findall((Parent+Path)-(Obj+DOM),
  581	      load_man_object(Obj, Parent, Path, DOM),
  582	      Matches),
  583      Matches = [_|_],
  584      !,
  585      pairs_keys(Matches, ParentPaths),
  586      Matches = [Parent+Path-_|_]
  587    },
  588    html_requires(pldoc),
  589    man_links(ParentPaths, Options),
  590    man_matches(Matches, Obj, Options).
  591man_page(Obj, Options) -->                      % PlDoc predicates, etc.
  592    { full_object(Obj, Full),
  593      findall(Full-File, visible_doc_comment(Full, File, Options), Pairs),
  594      Pairs \== [],
  595      pairs_keys(Pairs, Objs)
  596    },
  597    !,
  598    html_requires(pldoc),
  599    (   { Pairs = [_-File] }
  600    ->  object_page_header(File, Options)
  601    ;   object_page_header(-, Options)
  602    ),
  603    { merge_options(Options,
  604		    [ synopsis(true),
  605		      navtree(true)
  606		    ], Options2)
  607    },
  608    objects(Objs, Options2).
  609man_page(Obj, Options) -->                      % failure
  610    { \+ option(no_manual(fail), Options)
  611    },
  612    html_requires(pldoc),
  613    man_links([], Options),
  614    html(p(class(noman),
  615	   [ 'Sorry, No manual entry for ',
  616	     b('~w'-[Obj])
  617	   ])).
  618
  619% Show an object if it is not private   or  it is fully qualified and we
  620% want to show all fully qualified objects. Used by help/1.
  621
  622visible_doc_comment(Obj, File, Options) :-
  623    doc_comment(Obj, File:_, _, _),
  624    \+ ( private(Obj, Options),
  625         \+ ( Obj = _:_,
  626              option(qualified(always), Options)
  627            )
  628       ).
  629
  630%special_node(manual).          % redirected to the Introduction section
  631special_node(root).
  632special_node(packages).
  633special_node(xpce).
  634
  635full_page(Obj, _) :-
  636    var(Obj), !, fail.
  637full_page(Obj, Obj) :-
  638    Obj = section(_,_,_,_),
  639    !.
  640full_page(section(ID), section(_,_,ID,_)) :- !.
  641full_page(manual, section(_,_,'sec:intro',_)) :- !.
  642full_page(Obj0, Obj) :-
  643    ground(Obj0),
  644    alt_obj(Obj0, Obj),
  645    manual_object(Obj, _, _, _, _),
  646    !.
  647full_page(Obj, Obj) :-
  648    ground(Obj).
  649
  650alt_obj(Obj, Obj).
  651alt_obj(Name/Arity, Name//DCGArity) :-
  652    integer(Arity),
  653    Arity >= 2,
  654    DCGArity is Arity - 2.
  655alt_obj(Name//DCGArity, Name/Arity) :-
  656    integer(DCGArity),
  657    Arity is DCGArity + 2.
 full_object(+Object, -Full) is semidet
Translate to canonical PlDoc object
  663full_object(Object, M:Obj) :-
  664    qualify(Object, M:Obj0),
  665    alt_obj(Obj0, Obj),
  666    doc_comment(M:Obj, _, _, _),
  667    !.
  668
  669qualify(M:O, M:O).
  670qualify(O, _:O).
 man_qualified_object(+Object, +Parent, -LibraryOpt, -QObject, -Section) is semidet
Get a qualified predicate description from Text that appears in the section Parent.

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.

  682man_qualified_object(Text, Parent, LibOpt, Object, Section) :-
  683    atom(Text),
  684    atom_pi(Text, PI),
  685    ground(PI),
  686    !,
  687    man_qualified_object_2(PI, Parent, LibOpt, Object, Section).
  688man_qualified_object(Object0, Parent, LibOpt, Object, Section) :-
  689    man_qualified_object_2(Object0, Parent, LibOpt, Object, Section).
  690
  691man_qualified_object_2(Name/Arity, Parent,
  692		       LibOpt, Module:Name/Arity, Section) :-
  693    object_module(Parent, Module, Section, LibOpt),
  694    !.
  695man_qualified_object_2(Object, Parent, [], Object, Parent).
 man_synopsis(+Object, +Section)//
Give synopsis details for a fully specified predicate indicator and link this to the section.
  703:- public
  704    man_synopsis//2.                % called from man_match//2
  705
  706man_synopsis(PI, Section) -->
  707    man_synopsis(PI, Section, []).
  708
  709man_synopsis(PI, Section, Options) -->
  710    { object_href(Section, HREF)
  711    },
  712    object_synopsis(PI, [href(HREF)|Options]).
 object_module(+Section0, -Module, -Section, -LibOpt) is semidet
Find the module documented by Section.
  718object_module(Section0, Module, Section, [source(Term)]) :-
  719    parent_section_ndet(Section0, Section),
  720    manual_object(Section, Title, _File, _Class, _Offset),
  721    (   once(sub_atom(Title, B, _, _, :)),
  722	sub_atom(Title, 0, B, _, Atom),
  723	catch(term_to_atom(Term, Atom), _, fail),
  724	ground(Term),
  725	Term = library(_)
  726    ->  !,
  727	absolute_file_name(Term, PlFile,
  728			   [ file_type(prolog),
  729			     access(read),
  730			     file_errors(fail)
  731			   ]),
  732	(   module_property(Module, file(PlFile))
  733	->  true
  734	;   xref_public_list(PlFile, -,         % module is not loaded
  735			     [ module(Module)
  736			     ])
  737	)
  738    ).
  739
  740parent_section_ndet(Section, Section).
  741parent_section_ndet(Section, Parent) :-
  742    parent_section(Section, Parent0),
  743    parent_section_ndet(Parent0, Parent).
  744
  745
  746man_matches(Matches, Object, Options) -->
  747    { option(navtree(false), Options) },
  748    !,
  749    man_matches_nt(Matches, Object, Options).
  750man_matches(Matches, Object, Options) -->
  751    html([ div(class(navtree),
  752	       div(class(navwindow),
  753		   \man_nav_tree(Object, Options))),
  754	   div(class(navcontent),
  755	       \man_matches_nt(Matches, Object, Options))
  756	 ]).
  757
  758
  759man_matches_nt([Match], Object, Options) -->
  760    { option(footer(true), Options, true) },
  761    !,
  762    man_match(Match, Object, Options),
  763    object_page_footer(Object, []).
  764man_matches_nt(Matches, Object, Options) -->
  765    man_matches_list(Matches, Object, Options).
  766
  767man_matches_list([], _, _) --> [].
  768man_matches_list([H|T], Obj, Options) -->
  769    man_match(H, Obj, Options),
  770    man_matches_list(T, Obj, Options).
 man_match(+Term, +Object, +Options)// is det
If possible, insert the synopsis into the title of the description.
  777man_match(packages, packages, _) -->
  778    !,
  779    html({|html||
  780	  <p>
  781	  Packages are relatively independent add-on libraries that
  782	  may not be available in all installations.  Packages are
  783	  part of the source code releases of SWI-Prolog and may be
  784	  enabled or disabled during the build.</p>
  785
  786	  <p>
  787	  See also <a href="/pack/list">Add-ons</a> for extensions
  788	  provided by the community that must be installed separately
  789	  using
  790	  <a href="/pldoc/doc_for?object=pack_install/1">pack_install/1</a>.</p>
  791	 |}).
  792man_match(xpce, xpce, _) -->
  793    !,
  794    html({|html||
  795	  <p>
  796	  XPCE is the native graphical toolkit of SWI-Prolog.  The
  797	  reference manual below documents the class hierarchy, global
  798	  objects, and topical guides.</p>
  799	 |}).
  800man_match(root, root, _) -->
  801    !,
  802    man_overview([]).
  803man_match((Parent+Path)-(Obj+DOM), Obj, Options) -->
  804    { \+ option(synopsis(false), Options),
  805      DOM = [element(dt,A,C0)|DD],
  806      convlist(dt_obj, DOM, Objs),
  807      option(link_source(Link), Options, true),
  808      man_qualified_object(Obj, Parent, LibOpt, QObj, Section),
  809      !,
  810      C = [ span(style('float:right;margin-left:5px;'),
  811		 \object_source_button(QObj, [source_link(Link)]))
  812	  | C0
  813	  ]
  814    },
  815    dom_list([ element(dt,[],[\man_synopsis(QObj, Section, LibOpt)]),
  816	       element(dt,A,C)
  817	     | DD
  818	     ], Path, Options),
  819    object_footer(Objs, Options).
  820man_match((_Parent+Path)-(Obj+DOM), Obj, Options) -->
  821    dom_list(DOM, Path, Options).
  822
  823dt_obj(element(dt,_,C), Obj) :-
  824    xpath(C, //a(@id=Atom), _),
  825    atom_to_object(Atom, Obj).
  826
  827:- html_meta
  828    dom_list(html, +, +, ?, ?).  829
  830dom_list(_:[], _, _) -->
  831    !,
  832    [].
  833dom_list(M:[H|T], Path, Options) -->
  834    dom(H, Path, Options),
  835    dom_list(M:T, Path, Options).
  836
  837dom(element(E, Atts, Content), Path, Options) -->
  838    !,
  839    dom_element(E, Atts, Content, Path, Options).
  840dom(CDATA, _, _) -->
  841    html(CDATA).
  842
  843dom_element(a, _, [], _, _) -->                % Useless back-references
  844    !,
  845    [].
  846dom_element(a, Att, Content, Path, Options) -->
  847    { memberchk(href=HREF, Att),
  848      (   memberchk(class=Class, Att)
  849      ->  true
  850      ;   Class = unknown
  851      ),
  852      rewrite_ref(Class, HREF, Path, Myref, Options)
  853    },
  854    !,
  855    html(a(href(Myref), \dom_list(Content, Path, Options))).
  856dom_element(a, _Att, Content, Path, Options) -->  % unresolvable manual link
  857    { option(link_scheme(_), Options)             % there is nothing to click
  858    },
  859    !,
  860    dom_list(Content, Path, Options).
  861dom_element(span, Att, [CDATA], _, Options) -->
  862    { memberchk(class='pred-ext', Att),
  863      atom_pi(CDATA, PI),
  864      documented(PI),
  865      (   option(link_scheme(Scheme), Options)
  866      ->  object_scheme_uri(Scheme, PI, HREF)
  867      ;   option(server(false), Options)
  868      ->  public_link(predicate(CDATA), HREF)
  869      ;   http_link_to_id(pldoc_man, [predicate=CDATA], HREF)
  870      )
  871    },
  872    !,
  873    html(a(href(HREF), CDATA)).
  874dom_element(img, Att0, [], Path, _Options) -->
  875    { selectchk(src=Src, Att0, Att1),
  876      relative_file_name(ImgFile, Path, Src),
  877      handler_alias(Handler, DirAlias),
  878      absolute_file_name(DirAlias, Dir,
  879			 [ file_errors(fail),
  880			   solutions(all),
  881			   file_type(directory)
  882			 ]),
  883      ensure_slash(Dir, DirS),
  884      atom_concat(DirS, NewSrc, ImgFile),
  885      !,
  886      http_link_to_id(Handler, [], ManRef),
  887      directory_file_path(ManRef, NewSrc, NewPath),
  888      Begin =.. [img, src(NewPath) | Att1]
  889    },
  890    html_begin(Begin),
  891    html_end(img).
  892dom_element(div, Att, _, _, _) -->
  893    { memberchk(class=navigate, Att) },
  894    !.
  895dom_element(html, _, Content, Path, Options) -->
  896    !,                              % do not emit a html for the second time
  897    dom_list(Content, Path, Options).
  898dom_element(head, _, Content, Path, Options) -->
  899    !,                              % do not emit a head for the second time
  900    dom_list(Content, Path, Options).
  901dom_element(title, _, _, _, _) --> !.
  902dom_element(link, _, _, _, _) --> !.
  903dom_element(body, _, Content, Path, Options) -->
  904    !,                              % do not emit a body for the second time
  905    dom_list(Content, Path, Options).
  906dom_element(Name, Attrs, Content, Path, Options) -->
  907    { Begin =.. [Name|Attrs] },
  908    html_begin(Begin),
  909    dom_list(Content, Path, Options),
  910    html_end(Name).
  911
  912handler_alias(manual_file,   swi_man_manual(.)).
  913handler_alias(pldoc_package, swi_man_packages(.)).
  914
  915ensure_slash(Dir, DirS) :-
  916    (   sub_atom(Dir, _, _, 0, /)
  917    ->  DirS = Dir
  918    ;   atom_concat(Dir, /, DirS)
  919    ).
 public_link(+Spec, -HREF)
We do not have a web server. Create a link to the public server instead.
bug
- The predicate may not be there.
  928public_link(predicate(CDATA), HREF) :-
  929    uri_encoded(query_value, CDATA, Encoded),
  930    atom_concat('https://www.swi-prolog.org/pldoc/doc_for?object=',
  931		Encoded, HREF).
 documented(+PI) is semidet
True if we have documentation about PI
  938documented(PI) :-
  939    manual_object(PI, _, _, _, _),
  940    !.
  941documented(PI) :-
  942    full_object(PI, _Obj).
 rewrite_ref(+Class, +Ref0, +Path, -ManRef, +Options) is semidet
Rewrite Ref0 from the HTML reference manual format to the server format. Reformatted:
File#Name/Arity
Local reference using the manual presentation /man?predicate=PI.
File#sec:NR
Rewrite to section(Level, NT, ID, FilePath)
File#flag:Name
Rewrite to section(Level, NT, ID, FilePath)#flag:Name
File#gloss:Name
Rewrite to section(Level, NT, ID, FilePath)#gloss:Name

$ File#Name() Rewrite to /man/CAPI=Name

File#class-<Class>-<Kind>-<Name>
In-page link in the xpce reference manual. Rewrite to /doc_for?object=xpce(Class,Kind,Name). Kind is one of send, get, both, ivar or classvar.
File#sec:NR
Section reference whose <A> tag carries no class attribute, as produced by the xpce manual for class and object refs. Rewritten via the same sec lookup.
Arguments:
Class- Class of the <A>. Supported classes are
secLink to a section
predLink to a predicate
flagLink to a Prolog flag
glossLink to a glossary
Ref0- Initial reference from the a element
Path- Currently loaded file
ManRef- PlDoc server reference
  986rewrite_ref(Class, Ref0, Path, ManRef, Options) :-
  987    option(link_scheme(Scheme), Options),
  988    !,
  989    ref_object(Class, Ref0, Path, Object),
  990    object_scheme_uri(Scheme, Object, ManRef).
  991rewrite_ref(_Class, Ref, _Path, Ref, Options) :-
  992    option(server(false), Options),
  993    !.
  994rewrite_ref(Class, Ref0, Path, ManRef, _Options) :-
  995    rewrite_ref(Class, Ref0, Path, ManRef).
  996
  997rewrite_ref(pred, Ref0, _, Ref) :-              % Predicate/DCG reference
  998    ref_fragment(Ref0, _File, Fragment),
  999    !,
 1000    atom_to_object(Fragment, PI),
 1001    manual_object(PI, _, _, _, _),
 1002    uri_encoded(query_value, Fragment, Enc),
 1003    http_location_by_id(pldoc_man, ManHandler),
 1004    format(string(Ref), '~w?predicate=~w', [ManHandler, Enc]).
 1005rewrite_ref(function, Ref0, _, Ref) :-          % Arithmetic function reference
 1006    ref_fragment(Ref0, _File, Fragment),
 1007    !,
 1008    atom_to_object(Fragment, PI),
 1009    manual_object(PI, _, _, _, _),
 1010    PI=f(Name/Arity),
 1011    format(atom(PIName), '~w/~w', [Name,Arity]),
 1012    uri_encoded(query_value, PIName, Enc),
 1013    http_location_by_id(pldoc_man, ManHandler),
 1014    format(string(Ref), '~w?function=~w', [ManHandler, Enc]).
 1015rewrite_ref(func, Ref0, _, Ref) :-              % C-API reference
 1016    ref_fragment(Ref0, _File, Fragment),
 1017    !,
 1018    atom_to_object(Fragment, Obj),
 1019    manual_object(Obj, _, _, _, _),
 1020    Obj = c(Function),
 1021    uri_encoded(query_value, Function, Enc),
 1022    http_location_by_id(pldoc_man, ManHandler),
 1023    format(string(Ref), '~w?CAPI=~w', [ManHandler, Enc]).
 1024rewrite_ref(sec, Ref0, Path, Ref) :-            % Section inside a file
 1025    ref_fragment(Ref0, File, Fragment),
 1026    !,
 1027    referenced_section(Fragment, File, Path, Section),
 1028    object_href(Section, Ref).
 1029rewrite_ref(sec, File, Path, Ref) :-            % Section is a file
 1030    file_section(File, Path, Obj),
 1031    !,
 1032    object_href(Obj, Ref).
 1033rewrite_ref(cite, Ref0, Path, Ref) :-           % Citation (bit hard-wired)
 1034    debug(pldoc(cite), 'Cite ref ~q ~q', [Ref0, Path]),
 1035    ref_fragment(Ref0, _File, Fragment),
 1036    !,
 1037    uri_encoded(query_value, Fragment, Enc),
 1038    http_location_by_id(pldoc_man, ManHandler),
 1039    format(string(Ref), '~w?section=bibliography#~w', [ManHandler, Enc]).
 1040rewrite_ref(flag, Ref0, Path, Ref) :-
 1041    ref_fragment(Ref0, File, Fragment),
 1042    !,
 1043    file_section(File, Path, Obj),
 1044    !,
 1045    object_href(Obj, Ref1),
 1046    format(string(Ref), '~w#~w', [Ref1, Fragment]).
 1047rewrite_ref(gloss, Ref0, Path, Ref) :-
 1048    ref_fragment(Ref0, File, Fragment),
 1049    !,
 1050    file_section(File, Path, Obj),
 1051    !,
 1052    object_href(Obj, Ref1),
 1053    format(string(Ref), '~w#~w', [Ref1, Fragment]).
 1054rewrite_ref(_, Ref0, _, Ref) :-                 % xpce class member reference
 1055    xpce_member_ref(Ref0, Obj),
 1056    !,
 1057    object_href(Obj, Ref).
 1058rewrite_ref(_, Ref0, Path, Ref) :-              % section ref carrying class=""
 1059    ref_fragment(Ref0, File, Fragment),         % (xpce: class refs, objects)
 1060    sub_atom(Fragment, 0, _, _, 'sec:'),
 1061    referenced_section(Fragment, File, Path, Section),
 1062    !,
 1063    object_href(Section, Ref).
 ref_fragment(+Ref, -File, -Fragment) is semidet
Split a manual reference into the file it points at and the anchor in that file. File is '' if Ref only holds an anchor.
 1070ref_fragment(Ref, File, Fragment) :-
 1071    sub_atom(Ref, B, _, A, '#'),
 1072    !,
 1073    sub_atom(Ref, 0, B, _, File),
 1074    sub_atom(Ref, _, A, 0, Fragment).
 file_section(+File, +Path, -Section) is semidet
Section is the manual section that is held by File, a manual file referenced from the manual file Path.
 1081file_section(File, Path, Section) :-
 1082    file_directory_name(Path, Dir),
 1083    atomic_list_concat([Dir, /, File], SecPath),
 1084    Section = section(_, _, _, SecPath),
 1085    manual_object(Section, _, _, _, _).
 xpce_member_ref(+Ref, -Object) is semidet
Object is the XPCE class member addressed by the in-page reference Ref, e.g. class-point-get-x.
 1092xpce_member_ref(Ref, Obj) :-
 1093    ref_fragment(Ref, _File, Fragment),
 1094    atomic_list_concat([class, ClassA, KindA, Name], '-', Fragment),
 1095    xpce_kind(KindA),
 1096    xpce_canonical_object(ClassA, KindA, Name, Obj).
 ref_object(+Class, +Ref, +Path, -Object) is semidet
True when Ref, a reference from the HTML reference manual file Path, addresses documentation Object. This is the resolving half of rewrite_ref/4 and, unlike the latter, needs no PlDoc server. It drives the link_scheme option of man_page//2. Flag and glossary references resolve to the section that holds them.
 1106ref_object(pred, Ref, _, Obj) :-
 1107    !,
 1108    ref_fragment(Ref, _File, Fragment),
 1109    atom_to_object(Fragment, Obj),
 1110    manual_object(Obj, _, _, _, _).
 1111ref_object(function, Ref, _, Obj) :-
 1112    !,
 1113    ref_fragment(Ref, _File, Fragment),
 1114    atom_to_object(Fragment, Obj),
 1115    Obj = f(_),
 1116    manual_object(Obj, _, _, _, _).
 1117ref_object(func, Ref, _, Obj) :-
 1118    !,
 1119    ref_fragment(Ref, _File, Fragment),
 1120    atom_to_object(Fragment, Obj),
 1121    Obj = c(_),
 1122    manual_object(Obj, _, _, _, _).
 1123ref_object(sec, Ref, Path, Obj) :-
 1124    !,
 1125    (   ref_fragment(Ref, File, Fragment)
 1126    ->  referenced_section(Fragment, File, Path, Obj)
 1127    ;   file_section(Ref, Path, Obj)
 1128    ).
 1129ref_object(Class, Ref, Path, Obj) :-
 1130    section_ref_class(Class),
 1131    !,
 1132    ref_fragment(Ref, File, _Fragment),
 1133    file_section(File, Path, Obj).
 1134ref_object(_, Ref, _, Obj) :-                   % xpce class member reference
 1135    xpce_member_ref(Ref, Obj),
 1136    !.
 1137ref_object(_, Ref, Path, Obj) :-                % section ref carrying class=""
 1138    ref_fragment(Ref, File, Fragment),
 1139    sub_atom(Fragment, 0, _, _, 'sec:'),
 1140    referenced_section(Fragment, File, Path, Obj).
 1141
 1142section_ref_class(flag).
 1143section_ref_class(gloss).
 1144
 1145%   The anchor id encodes the access form used at the call site
 1146%   (=|class-<class>-<kind>-<name>|=) but each method/instance variable
 1147%   has only one canonical xpce(...) data-obj.  An ivar e.g. emits
 1148%   alias anchors for its send/get/both accessors, and a =both= anchor
 1149%   is a shorthand that resolves to whichever send/get/ivar form the
 1150%   class actually defines.  Try the parsed kind first, then fall back
 1151%   to the alternative forms in order of likelihood.
 1152
 1153xpce_canonical_object(Class, Kind, Name, xpce(Class, Kind, Name)) :-
 1154    manual_object(xpce(Class, Kind, Name), _, _, _, _),
 1155    !.
 1156xpce_canonical_object(Class, _, Name, Obj) :-
 1157    member(K, [ivar, send, get, classvar, both]),
 1158    Obj = xpce(Class, K, Name),
 1159    manual_object(Obj, _, _, _, _),
 1160    !.
 1161
 1162xpce_kind(send).
 1163xpce_kind(get).
 1164xpce_kind(both).
 1165xpce_kind(ivar).
 1166xpce_kind(classvar).
 referenced_section(+Fragment, +File, +Path, -Section)
 1170referenced_section(Fragment, File, Path, section(Level, Nr, ID, SecPath)) :-
 1171    atom_concat('sec:', Nr, Fragment),
 1172    (   File == ''
 1173    ->  SecPath = Path
 1174    ;   file_directory_name(Path, Dir),
 1175	atomic_list_concat([Dir, /, File], SecPath)
 1176    ),
 1177    manual_object(section(Level, Nr, ID, SecPath), _, _, _, _).
 man_links(+ParentPaths, +Options)// is det
Create top link structure for manual pages.
 1184man_links(ParentPaths, Options) -->
 1185    prolog:doc_page_header(parents(ParentPaths), Options),
 1186    !.
 1187man_links(ParentPaths, Options) -->
 1188    { option(links(true), Options, true),
 1189      option(header(true), Options, true)
 1190    },
 1191    !,
 1192    html([ div(class(navhdr),
 1193	       [ div(class(jump), \man_parent(ParentPaths)),
 1194		 div(class(search), \search_form(Options)),
 1195		 br(clear(right))
 1196	       ]),
 1197	   p([])
 1198	 ]).
 1199man_links(_, _) -->
 1200    [].
 1201
 1202man_parent(ParentPaths) -->
 1203    { maplist(parent_to_section, ParentPaths, [Section|MoreSections]),
 1204      maplist(=(Section), MoreSections)
 1205    },
 1206    !,
 1207    object_ref(Section, [secref_style(number_title)]).
 1208man_parent(_) --> [].
 1209
 1210parent_to_section(X+_, X) :-
 1211    X = section(_,_,_,_),
 1212    !.
 1213parent_to_section(File+_, Section) :-
 1214    atom(File),
 1215    manual_object(Section, _Title, File, _Class, _Offset),
 1216    !.
 section_link(+Obj, +Options)// is det
Create link to a section. Options recognised:
secref_style(+Style)
One of number, title or number_title.
 1225section_link(Section, Options) -->
 1226    { option(secref_style(Style), Options, number)
 1227    },
 1228    section_link(Style, Section, Options).
 1229
 1230section_link(number, section(_, Number, _, _), _Options) -->
 1231    !,
 1232    (   {Number == '0'}             % Title.  Package?
 1233    ->  []
 1234    ;   html(['Sec. ', Number])
 1235    ).
 1236section_link(title, Obj, _Options) -->
 1237    !,
 1238    { manual_object(Obj, Title, _File, _Class, _Offset)
 1239    },
 1240    html(Title).
 1241section_link(_, Obj, _Options) -->
 1242    !,
 1243    { Obj = section(_, Number, _, _),
 1244      manual_object(Obj, Title, _File, _Class, _Offset)
 1245    },
 1246    (   { Number == '0' }
 1247    ->  html(Title)
 1248    ;   html([Number, ' ', Title])
 1249    ).
 function_link(+Function, +Options) is det
Create a link to a C-function
 1255function_link(Function, _) -->
 1256    html([Function, '()']).
 1257
 1258
 1259		 /*******************************
 1260		 *       INDICES & OVERVIEW     *
 1261		 *******************************/
 man_overview(+Options)// is det
Provide a toplevel overview on the manual: the reference manual and the available packages.
 1268man_overview(Options) -->
 1269    { http_absolute_location(pldoc_man(.), RefMan, [])
 1270    },
 1271    html([ h1('SWI-Prolog documentation'),
 1272	   blockquote(class(refman_link),
 1273		      a(href(RefMan),
 1274			'SWI-Prolog reference manual')),
 1275	   \package_overview(Options),
 1276	   \paperback(Options)
 1277	 ]).
 1278
 1279package_overview(Options) -->
 1280    html([ h2(class(package_doc_title),
 1281	      'SWI-Prolog package documentation'),
 1282	   blockquote(class(package_overview),
 1283		      \packages(Options))
 1284	 ]).
 1285
 1286packages(Options) -->
 1287    { findall(Pkg, current_package(Pkg), Pkgs)
 1288    },
 1289    packages(Pkgs, Options).
 1290
 1291packages([], _) -->
 1292    [].
 1293packages([Pkg|T], Options) -->
 1294    package(Pkg, Options),
 1295    packages(T, Options).
 1296
 1297package(pkg(Title, HREF, HavePackage), Options) -->
 1298    { package_class(HavePackage, Class, Options)
 1299    },
 1300    html(div(class(Class),
 1301	     a([href(HREF)], Title))).
 1302
 1303package_class(true,  pkg_link, _).
 1304package_class(false, no_pkg_link, _).
 1305
 1306current_package(pkg(Title, HREF, HavePackage)) :-
 1307    manual_object(section(0, _, _, _), Title, File, packages, _),
 1308    file_base_name(File, FileNoDir),
 1309    file_name_extension(Base, _, FileNoDir),
 1310    (   exists_source(library(Base))
 1311    ->  HavePackage = true
 1312    ;   HavePackage = false
 1313    ),
 1314    http_absolute_location(pldoc_pkg(FileNoDir), HREF, []).
 1315
 1316
 1317:- if(current_predicate(http_handler/3)). 1318:- http_handler(pldoc(jpl),      pldoc_jpl,              [prefix]). 1319:- http_handler(pldoc_pkg(.),    pldoc_package,          [prefix]). 1320:- http_handler(pldoc_man(.),    pldoc_refman,           [prefix]). 1321:- http_handler(pldoc(packages), pldoc_package_overview, []).
 pldoc_jpl(+Request)
Hack to include JPL documentation in server.
 1327pldoc_jpl(Request) :-
 1328    memberchk(path_info(JPLFile), Request),
 1329    atom_concat('doc/packages/jpl', JPLFile, Path),
 1330    http_reply_file(swi(Path), [], Request).
 pldoc_package(+Request)
HTTP handler for PlDoc package documentation. Accepts /pldoc/package/<package>.{html,gif}. The path =/pldoc/package/<package>= is redirected to the canonical object version.
 1339pldoc_package(Request) :-
 1340    (   \+ option(path_info(_), Request)
 1341    ->  true
 1342    ;   option(path_info(/), Request)
 1343    ),
 1344    http_link_to_id(pldoc_object, [object=packages], HREF),
 1345    http_redirect(see_other, HREF, Request).
 1346pldoc_package(Request) :-
 1347    memberchk(path_info(Img), Request),
 1348    file_mime_type(Img, image/_),
 1349    !,
 1350    http_reply_file(swi_man_packages(Img), [], Request).
 1351pldoc_package(Request) :-
 1352    memberchk(path_info('jpl'), Request),
 1353    !,
 1354    memberchk(path(Path0), Request),
 1355    atom_concat(Path0, /, Path),
 1356    http_redirect(moved, Path, Request).
 1357pldoc_package(Request) :-
 1358    memberchk(path_info(JPLFile), Request),
 1359    (   JPLFile == 'jpl/'
 1360    ->  Path = 'doc/packages/jpl/index.html'
 1361    ;   sub_atom(JPLFile, 0, _, _, 'jpl/')
 1362    ->  atom_concat('doc/packages/', JPLFile, Path)
 1363    ),
 1364    http_reply_file(swi(Path), [], Request).
 1365pldoc_package(Request) :-
 1366    memberchk(path_info(PkgDoc), Request),
 1367    ensure_html_ext(PkgDoc, PkgHtml),
 1368    atom_concat('packages/', PkgHtml, Path),
 1369    term_to_atom(section(Path), Object),
 1370    http_link_to_id(pldoc_object, [object=Object], HREF),
 1371    http_redirect(see_other, HREF, Request).
 1372
 1373ensure_html_ext(Pkg, PkgHtml) :-
 1374    file_name_extension(_, html, Pkg),
 1375    !,
 1376    PkgHtml = Pkg.
 1377ensure_html_ext(Pkg, PkgHtml) :-
 1378    file_name_extension(Pkg, html, PkgHtml).
 pldoc_package_overview(+Request)
Provide an overview of the package documentation
 1384pldoc_package_overview(_Request) :-
 1385    reply_html_page(
 1386	pldoc(packages),
 1387	title('SWI-Prolog package documentation'),
 1388	\package_overview([])).
 1389:- endif.
 paperback(+Options)//
Link to the paperback version of the manual.
 1395paperback(_Options) -->
 1396    { expand_url_path(swipl_book(.), HREF)
 1397    },
 1398    html([ h2('The manual as a book'),
 1399	   p([ 'A paperback version of the manual is ',
 1400	       a(href(HREF), 'available'), '.'
 1401	     ])
 1402	 ]).
 pldoc_refman(+Request)
HTTP handler for PlDoc Reference Manual access. Accepts /refman/[<package>.html.]
 1409pldoc_refman(Request) :-
 1410    memberchk(path_info(Section), Request),
 1411    \+ sub_atom(Section, _, _, _, /),
 1412    Obj = section(0,_,_,_),
 1413    manual_object(Obj, Title, File, manual, _),
 1414    file_base_name(File, Section),
 1415    !,
 1416    reply_html_page(pldoc(man),
 1417		    title(Title),
 1418		    \object_page(Obj, [])).
 1419pldoc_refman(Request) :-                % server Contents.html
 1420    \+ memberchk(path_info(_), Request),
 1421    !,
 1422    http_link_to_id(pldoc_object, [object(manual)], HREF),
 1423    http_redirect(see_other, HREF, Request).
 1424pldoc_refman(Request) :-
 1425    memberchk(path(Path), Request),
 1426    existence_error(http_location, Path).
 1427
 1428
 1429		 /*******************************
 1430		 *          HOOK SEARCH         *
 1431		 *******************************/
 1432
 1433prolog:doc_object_summary(section(ID), Class, File, Summary) :-
 1434    nonvar(ID),                     % when generating, only do full ones
 1435    manual_object(section(_Level, _No, ID, _Path), Summary, File, Class, _Offset).
 1436prolog:doc_object_summary(Obj, Class, File, Summary) :-
 1437    manual_object(Obj, Summary, File, Class, _Offset).
 1438
 1439prolog:doc_object_page(Obj, Options) -->
 1440    man_page(Obj, [no_manual(fail),footer(false)|Options]).
 prolog:doc_object_link(+Obj, +Options)//
Provide the HTML to describe Obj for linking purposes.
 1446prolog:doc_object_link(Obj, Options) -->
 1447    { Obj = section(_,_,_,_) },
 1448    !,
 1449    section_link(Obj, Options).
 1450prolog:doc_object_link(Obj0, Options) -->
 1451    { Obj0 = section(ID),
 1452      Obj = section(_Level, _No, ID, _Path),
 1453      manual_object(Obj, _, _, _, _)
 1454    },
 1455    !,
 1456    section_link(Obj, Options).
 1457prolog:doc_object_link(Obj, Options) -->
 1458    { Obj = c(Function) },
 1459    !,
 1460    function_link(Function, Options).
 1461prolog:doc_object_link(root, _) -->
 1462    !,
 1463    html('Documentation').
 1464prolog:doc_object_link(manual, _Options) -->
 1465    !,
 1466    html('Reference manual').
 1467prolog:doc_object_link(packages, _) -->
 1468    html('Packages').
 1469prolog:doc_object_link(xpce, _) -->
 1470    html('XPCE').
 1471
 1472prolog:doc_category(manual,   30, 'Reference Manual').
 1473prolog:doc_category(packages, 40, 'Packages').
 1474
 1475prolog:doc_file_index_header(File, Options) -->
 1476    { Section = section(_Level, _No, _ID, File),
 1477      manual_object(Section, _Summary, File, _Cat, _Offset)
 1478    },
 1479    !,
 1480    html(tr(th([colspan(3), class(section)],
 1481	       [ \object_ref(Section,
 1482			     [ secref_style(number_title)
 1483			     | Options
 1484			     ])
 1485	       ]))).
 1486
 1487prolog:doc_object_title(Obj, Title) :-
 1488    Obj = section(_,_,_,_),
 1489    manual_object(Obj, Title, _, _, _),
 1490    !.
 1491prolog:doc_object_title(xpce, 'XPCE').
 1492prolog:doc_object_title(xpce(Class, Kind, Name), Title) :-
 1493    xpce_object_label(xpce(Class, Kind, Name), Title).
 1494
 1495%   xpce class members appear in the index as xpce(Class, Kind, Name)
 1496%   (see packages/pldoc/doc_util.pl atom_to_object/2 and packages/xpce/
 1497%   man/refmanual). Render link text as the conventional xpce arrow
 1498%   notation -- Class->name / Class<-name / Class<->name / Class.name --
 1499%   so the entries are recognisable next to predicates.
 1500
 1501prolog:doc_object_link(xpce(Class, Kind, Name), _Options) -->
 1502    { xpce_object_label(xpce(Class, Kind, Name), Label) },
 1503    html(Label).
 xpce_object_label(+Object, -Label) is semidet
Label is the conventional xpce arrow notation for an xpce/3 class member, e.g. `point<-x`.
 1510xpce_object_label(xpce(Class, Kind, Name), Label) :-
 1511    xpce_member_arrow(Kind, Arrow),
 1512    atomic_list_concat([Class, Arrow, Name], Label).
 1513
 1514xpce_member_arrow(send,     '->').
 1515xpce_member_arrow(get,      '<-').
 1516xpce_member_arrow(both,     '<->').
 1517xpce_member_arrow(ivar,     '-').
 1518xpce_member_arrow(classvar, '.').
 1519
 1520%   Predicates such as get_class/4 live in module =pce_principal=, but
 1521%   the user-facing entry point is =|library(pce)|=, which re-exports
 1522%   the boot modules.  Rewrite the synopsis so it says what users
 1523%   should actually type.
 1524
 1525prolog:pldoc_synopsis_spec(pce(prolog/boot/_), library(pce)).
 1526
 1527prolog:doc_canonical_object(section(_Level, _No, ID, _Path),
 1528			    section(ID)).
 prolog:doc_object_href(+Object, -HREF) is semidet
Produce a HREF for section objects.
 1534prolog:doc_object_href(section(ID), HREF) :-
 1535    nonvar(ID),
 1536    atom_concat('sec:', Sec, ID),
 1537    http_link_to_id(pldoc_man, [section(Sec)], HREF).
 1538prolog:doc_object_href(section(_Level, _No, ID, _Path), HREF) :-
 1539    nonvar(ID),
 1540    atom_concat('sec:', Sec, ID),
 1541    http_link_to_id(pldoc_man, [section(Sec)], HREF).
 1542
 1543
 1544		 /*******************************
 1545		 *         RESOLVE LINKS	*
 1546		 *******************************/
 man_param_object(+Param, -Object) is semidet
True when Param, a parameter of the PlDoc HTTP interface, refers to documentation Object. Param is one of
See also
- pldoc_href_object/2 does the same for a complete HREF.
 1561man_param_object(predicate(PI), Obj) :-
 1562    atom_pi(PI, Obj).
 1563man_param_object(function(PI), f(Name/Arity)) :-
 1564    atom_pi(PI, Name/Arity),
 1565    integer(Arity).
 1566man_param_object('CAPI'(Name), c(Name)).
 1567man_param_object(section(Sec), section(ID)) :-
 1568    atom_concat('sec:', Sec, ID).
 1569man_param_object(object(Text), Obj) :-
 1570    (   catch(atom_to_term(Text, Obj0, _), error(_,_), fail)
 1571    ->  Obj = Obj0
 1572    ;   atom_to_object(Text, Obj)
 1573    ).
 pldoc_href_object(+HREF, -Object) is semidet
True when HREF, a link as generated for the PlDoc web server, points at documentation Object. Fails if HREF does not address a specific documented object, e.g., a link into a source file.
 1581pldoc_href_object(HREF, Object) :-
 1582    uri_components(HREF, Components),
 1583    uri_data(path, Components, Path),
 1584    atom(Path),
 1585    man_handler_location(Path),
 1586    uri_data(search, Components, Search),
 1587    atom(Search),
 1588    uri_query_components(Search, Query),
 1589    member(Name=Value, Query),
 1590    Param =.. [Name,Value],
 1591    man_param_object(Param, Object),
 1592    !.
 1593
 1594%  Note that the handlers are only registered  if the PlDoc server has
 1595%  been loaded.  If it is not, we cannot resolve the link.
 1596
 1597man_handler_location(Path) :-
 1598    man_handler_id(Id),
 1599    catch(http_location_by_id(Id, Path), error(_,_), fail),
 1600    !.
 1601
 1602man_handler_id(pldoc_man).
 1603man_handler_id(pldoc_object).
 man_object_uri(+Object, -URI) is semidet
 man_uri_object(+URI, -Object) is semidet
Convert between a documentation object and a `man:` IRI. Such IRIs embed manual hyperlinks into terminal output using OSC8 hyperlinks. man_object_uri/2 fails if Object is not fully specified.
See also
- help/1 and ansi_hyperlink/3.
 1614man_object_uri(Object, URI) :-
 1615    object_scheme_uri(man, Object, URI).
 object_scheme_uri(+Scheme, +Object, -URI) is semidet
Write Object as an IRI using the pseudo scheme Scheme. See the link_scheme option of man_page//2.
 1622object_scheme_uri(Scheme, Object0, URI) :-
 1623    localise_object(Object0, Object),
 1624    ground(Object),
 1625    format(atom(URI), '~w:~q', [Scheme, Object]).
 1626
 1627man_uri_object(URI, Object) :-
 1628    atom_concat('man:', Text, URI),
 1629    catch(term_to_atom(Object, Text), _, fail),
 1630    ground(Object).
 1631
 1632		 /*******************************
 1633		 *           NO HTTP		*
 1634		 *******************************/
 1635
 1636:- if(\+current_predicate(http_location_by_id/2)). 1637
 1638http_location_by_id(Id, Location) :-
 1639    format(atom(Location), '/pldoc/~w', [Id]).
 1640
 1641:- endif. 1642
 1643:- if(\+current_predicate(http_link_to_id/3)). 1644
 1645http_link_to_id(Id, Parameters, HREF) :-
 1646    must_be(list, Parameters),
 1647    format(atom(Location), '/pldoc/~w', [Id]),
 1648    (   Parameters == []
 1649    ->  HREF = Location
 1650    ;   uri_data(path, Components, Location),
 1651        uri_query_components(String, Parameters),
 1652        uri_data(search, Components, String),
 1653        uri_components(HREF, Components)
 1654    ).
 1655
 1656:- endif. 1657
 1658		 /*******************************
 1659		 *           MESSAGES           *
 1660		 *******************************/
 1661
 1662:- multifile prolog:message//1. 1663
 1664prolog:message(pldoc(no_section_id(File, Title))) -->
 1665    [ 'PlDoc: ~w: no id for section "~w"'-[File, Title] ].
 1666prolog:message(pldoc(duplicate_ids(L))) -->
 1667    [ 'PlDoc: duplicate manual section IDs:'-[], nl
 1668    ],
 1669    duplicate_ids(L).
 1670
 1671duplicate_ids([]) --> [].
 1672duplicate_ids([H|T]) --> duplicate_id(H), duplicate_ids(T).
 1673
 1674duplicate_id(Id) -->
 1675    { findall(File, manual_object(section(_,_,Id,File),_,_,_,_), Files) },
 1676    [ '    ~w: ~p'-[Id, Files], nl ]