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_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).

Process SWI-Prolog HTML manuals

   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		 *******************************/
 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.
  113man_nav_tree(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))).
 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.
  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).
 man_child_of(?Child, ?Parent) is nondet
Query the manual hierarchy.
  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.
 ensure_man_tree
Materialize the manual tree as a binary relation.
  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).
 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.
  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).
 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.
  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).
 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).
  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		 *******************************/
 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?
  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).
 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.
  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).
 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.
  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).
 object_section(+Path, +Position, -Section) is semidet
Section is the section in which object appears. This is the last section object before position.
  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).
 object_spec(+Atom, -SpecTerm)
Tranform the Name/Arity, etc strings as received from the HTTP into a term. Must return unique results.
  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		 *******************************/
 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.
  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.
 full_object(+Object, -Full) is semidet
Translate to canonical PlDoc object
  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).
 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.

  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).
 man_synopsis(+Object, +Section)//
Give synopsis details for a fully specified predicate indicator and link this to the section.
  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]).
 object_module(+Section0, -Module, -Section, -LibOpt) is semidet
Find the module documented by Section.
  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).
 man_match(+Term, +Object, +Options)// is det
If possible, insert the synopsis into the title of the description.
  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    ).
 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.
  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).
 documented(+PI) is semidet
True if we have documentation about PI
  915documented(PI) :-
  916    manual_object(PI, _, _, _, _),
  917    !.
  918documented(PI) :-
  919    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
  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).
 referenced_section(+Fragment, +File, +Path, -Section)
 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), _, _, _, _).
 man_links(+ParentPaths, +Options)// is det
Create top link structure for manual pages.
 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    !.
 section_link(+Obj, +Options)// is det
Create link to a section. Options recognised:
secref_style(+Style)
One of 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    ).
 function_link(+Function, +Options) is det
Create a link to a C-function
 1172function_link(Function, _) -->
 1173    html([Function, '()']).
 1174
 1175
 1176		 /*******************************
 1177		 *       INDICES & OVERVIEW     *
 1178		 *******************************/
 man_overview(+Options)// is det
Provide a toplevel overview on the manual: the reference manual and the available packages.
 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, []).
 pldoc_jpl(+Request)
Hack to include JPL documentation in server.
 1244pldoc_jpl(Request) :-
 1245    memberchk(path_info(JPLFile), Request),
 1246    atom_concat('doc/packages/jpl', JPLFile, Path),
 1247    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.
 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).
 pldoc_package_overview(+Request)
Provide an overview of the package documentation
 1301pldoc_package_overview(_Request) :-
 1302    reply_html_page(
 1303	pldoc(packages),
 1304	title('SWI-Prolog package documentation'),
 1305	\package_overview([])).
 1306:- endif.
 paperback(+Options)//
Link to the paperback version of the manual.
 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	 ]).
 pldoc_refman(+Request)
HTTP handler for PlDoc Reference Manual access. Accepts /refman/[<package>.html.]
 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
 1350prolog:doc_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).
 1353prolog:doc_object_summary(Obj, Class, File, Summary) :-
 1354    manual_object(Obj, Summary, File, Class, _Offset).
 1355
 1356prolog:doc_object_page(Obj, Options) -->
 1357    man_page(Obj, [no_manual(fail),footer(false)|Options]).
 prolog:doc_object_link(+Obj, +Options)//
Provide the HTML to describe Obj for linking purposes.
 1363prolog:doc_object_link(Obj, Options) -->
 1364    { Obj = section(_,_,_,_) },
 1365    !,
 1366    section_link(Obj, Options).
 1367prolog:doc_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).
 1374prolog:doc_object_link(Obj, Options) -->
 1375    { Obj = c(Function) },
 1376    !,
 1377    function_link(Function, Options).
 1378prolog:doc_object_link(root, _) -->
 1379    !,
 1380    html('Documentation').
 1381prolog:doc_object_link(manual, _Options) -->
 1382    !,
 1383    html('Reference manual').
 1384prolog:doc_object_link(packages, _) -->
 1385    html('Packages').
 1386prolog:doc_object_link(xpce, _) -->
 1387    html('XPCE').
 1388
 1389prolog:doc_category(manual,   30, 'Reference Manual').
 1390prolog:doc_category(packages, 40, 'Packages').
 1391
 1392prolog:doc_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
 1404prolog:doc_object_title(Obj, Title) :-
 1405    Obj = section(_,_,_,_),
 1406    manual_object(Obj, Title, _, _, _),
 1407    !.
 1408prolog:doc_object_title(xpce, 'XPCE').
 1409prolog:doc_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
 1419prolog:doc_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
 1434prolog:pldoc_synopsis_spec(pce(prolog/boot/_), library(pce)).
 1435
 1436prolog:doc_canonical_object(section(_Level, _No, ID, _Path),
 1437			    section(ID)).
 prolog:doc_object_href(+Object, -HREF) is semidet
Produce a HREF for section objects.
 1443prolog:doc_object_href(section(ID), HREF) :-
 1444    nonvar(ID),
 1445    atom_concat('sec:', Sec, ID),
 1446    http_link_to_id(pldoc_man, [section(Sec)], HREF).
 1447prolog:doc_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
 1477prolog:message(pldoc(no_section_id(File, Title))) -->
 1478    [ 'PlDoc: ~w: no id for section "~w"'-[File, Title] ].
 1479prolog:message(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 ]