View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        jan@swi-prolog.org
    5    WWW:           https://www.swi-prolog.org
    6    Copyright (c)  2006-2026, University of Amsterdam
    7			      VU University Amsterdam
    8			      CWI, Amsterdam
    9                              SWI-Prolog Solutions b.v.
   10    All rights reserved.
   11
   12    Redistribution and use in source and binary forms, with or without
   13    modification, are permitted provided that the following conditions
   14    are met:
   15
   16    1. Redistributions of source code must retain the above copyright
   17       notice, this list of conditions and the following disclaimer.
   18
   19    2. Redistributions in binary form must reproduce the above copyright
   20       notice, this list of conditions and the following disclaimer in
   21       the documentation and/or other materials provided with the
   22       distribution.
   23
   24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   25    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   26    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   27    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   28    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   29    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   30    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   31    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   32    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   33    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   34    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   35    POSSIBILITY OF SUCH DAMAGE.
   36*/
   37
   38:- module(pldoc_html,
   39	  [ doc_for_file/2,             % +FileSpec, +Options
   40	    doc_write_html/3,           % +Stream, +Title, +Term
   41	    doc_for_wiki_file/2,        % +FileSpec, +Options
   42					% Support doc_index
   43	    doc_page_dom/3,             % +Title, +Body, -DOM
   44	    print_html_head/1,          % +Stream
   45	    predref//1,                 % +PI //
   46	    predref//2,                 % +PI, Options //
   47	    nopredref//1,               % +PI //
   48	    module_info/3,              % +File, +Options0, -Options
   49	    doc_hide_private/3,         % +Doc0, -Doc, +Options
   50	    edit_button//2,             % +File, +Options, //
   51	    source_button//2,           % +File, +Options, //
   52	    zoom_button//2,             % +File, +Options, //
   53            pred_edit_button//2,        % +PredInd, +Options, //
   54	    object_edit_button//2,      % +Obj, +Options, //
   55	    object_source_button//2,    % +Obj, +Options, //
   56	    doc_resources//1,           % +Options
   57	    ensure_doc_objects/1,       % +File
   58					% Support other backends
   59	    doc_file_objects/5,         % +FSpec, -File, -Objs, -FileOpts, +Opts
   60	    existing_linked_file/2,     % +FileSpec, -Path
   61	    unquote_filespec/2,         % +FileSpec, -Unquoted
   62	    doc_tag_title/2,            % +Tag, -Title
   63	    mode_anchor_name/2,         % +Mode, -Anchor
   64	    pred_anchor_name/3,         % +Head, -PI, -Anchor
   65	    private/2,                  % +Obj, +Options
   66	    (multifile)/2,              % +Obj, +Options
   67	    is_pi/1,                    % @Term
   68	    is_op_type/2,               % +Atom, ?Type
   69					% Output routines
   70	    file//1,                    % +File, //
   71	    file//2,                    % +File, +Options, //
   72	    include//3,                 % +File, +Type, +Options //
   73	    tags//1,                    % +Tags, //
   74	    term//3,                    % +Text, +Term, +Bindings, //
   75	    file_header//2,             % +File, +Options, //
   76	    flagref//1,                 % +Flag
   77	    objects//2,                 % +Objects, +Options, //
   78	    object_ref//2,              % +Object, +Options, //
   79	    object_name//2,             % +Object, +Object
   80	    object_href/2,              % +Object, -URL
   81	    object_tree//3,             % +Tree, +Current, +Options
   82	    object_page//2,             % +Object, +Options, //
   83	    object_page_header//2,      % +File, +Options, //
   84	    object_synopsis//2,         % +Object, +Options, //
   85	    object_footer//2,           % +Object, +Options, //
   86	    object_page_footer//2,      % +Object, +Options, //
   87	    cite//1                     % +Citations
   88	  ]).   89% Get HTTP server infrastructure when available
   90:- if(exists_source(library(http/http_dispatch))).   91:- use_module(library(http/http_dispatch)).   92:- use_module(library(http/http_wrapper)).   93:- use_module(library(http/jquery)).   94:- use_module(library(apply)).   95:- use_module(library(debug)).   96:- use_module(library(error)).   97:- use_module(library(filesex)).   98:- use_module(library(lists)).   99:- use_module(library(occurs)).  100:- use_module(library(option)).  101:- use_module(library(pairs)).  102:- use_module(library(prolog_source)).  103:- use_module(library(prolog_xref)).  104:- use_module(library(readutil)).  105:- use_module(library(solution_sequences)).  106:- use_module(library(uri)).  107:- use_module(library(http/html_head)).  108:- use_module(library(http/html_write)).  109:- use_module(library(http/http_path)).  110:- use_module(library(http/term_html)).  111:- use_module(pldoc(doc_index)).  112:- use_module(pldoc(doc_man)).  113:- use_module(pldoc(doc_modes)).  114:- use_module(pldoc(doc_process)).  115:- use_module(pldoc(doc_search)).  116:- use_module(pldoc(doc_util)).  117:- use_module(pldoc(doc_wiki)).  118:- use_module(library(dcg/high_order)).  119:- use_module(library(dcg/basics)).  120
  121pldoc_server(true).
  122:- else.  123
  124:- multifile
  125    http:location/3.  126
  127http:location(pldoc_resource, '/pldoc/res', []).
  128
  129pldoc_server(false).
  130:- endif.  131
  132:- use_module(library(lists)).  133:- use_module(library(option)).  134:- use_module(library(uri)).  135:- use_module(library(readutil)).  136:- use_module(library(http/html_write)).  137:- use_module(library(http/http_path)).  138:- use_module(library(http/html_head)).  139:- use_module(library(http/term_html)).  140:- use_module(library(debug)).  141:- use_module(library(apply)).  142:- use_module(library(pairs)).  143:- use_module(library(filesex)).  144:- use_module(doc_process).  145:- use_module(doc_man).  146:- use_module(doc_modes).  147:- use_module(doc_wiki).  148:- use_module(doc_search).  149:- use_module(doc_index).  150:- use_module(doc_util).  151:- use_module(library(solution_sequences)).  152:- use_module(library(error)).  153:- use_module(library(occurs)).  154:- use_module(library(prolog_source)).  155:- use_module(library(prolog_xref)).  156
  157:- include(hooks).

PlDoc HTML backend

This module translates the Herbrand term from the documentation extracting module doc_wiki.pl into HTML+CSS.

To be done
- Split put generation from computation as computation is reusable in other backends.
  169:- public
  170    args//1,                        % Called from \Term output created
  171    pred_dt//3,                     % by the wiki renderer
  172    section//2,
  173    tag//2.  174
  175
  176:- predicate_options(doc_for_wiki_file/2, 2,
  177		     [ edit(boolean)
  178		     ]).  179:- predicate_options(doc_hide_private/3, 3,
  180		     [module(atom), public(list), public_only(boolean)]).  181:- predicate_options(edit_button//2, 2,
  182		     [ edit(boolean)
  183		     ]).  184:- predicate_options(file//2, 2,
  185		     [ label(any),
  186		       absolute_path(atom),
  187		       href(atom),
  188		       map_extension(list),
  189		       files(list),
  190		       edit_handler(atom)
  191		     ]).  192:- predicate_options(file_header//2, 2,
  193		     [ edit(boolean),
  194		       files(list),
  195		       public_only(boolean)
  196		     ]).  197:- predicate_options(include//3, 3,
  198		     [ absolute_path(atom),
  199		       class(atom),
  200		       files(list),
  201		       href(atom),
  202		       label(any),
  203		       map_extension(list)
  204		     ]).  205:- predicate_options(object_edit_button//2, 2,
  206		     [ edit(boolean),
  207		       pass_to(pred_edit_button//2, 2)
  208		     ]).  209:- predicate_options(object_page//2, 2,
  210		     [ for(any),
  211		       header(boolean),
  212		       links(boolean),
  213		       no_manual(boolean),
  214		       try_manual(boolean),
  215		       search_in(oneof([all,app,man])),
  216		       search_match(oneof([name,summary])),
  217		       search_options(boolean)
  218		     ]).  219:- predicate_options(object_ref//2, 2,
  220		     [ files(list),
  221		       qualify(boolean),
  222		       style(oneof([number,title,number_title])),
  223		       secref_style(oneof([number,title,number_title]))
  224		     ]).  225:- predicate_options(object_synopsis//2, 2,
  226		     [ href(atom)
  227		     ]).  228:- predicate_options(pred_dt//3, 3,
  229		     [ edit(boolean)
  230		     ]).  231:- predicate_options(pred_edit_button//2, 2,
  232		     [ edit(boolean)
  233		     ]).  234:- predicate_options(predref//2, 2,
  235		     [ files(list),
  236		       prefer(oneof([manual,app])),
  237		       pass_to(object_ref/4, 2)
  238		     ]).  239:- predicate_options(private/2, 2,
  240		     [ module(atom),
  241		       public(list)
  242		     ]).  243:- predicate_options(source_button//2, 2,
  244		     [ files(list)
  245		     ]).  246
  247
  248		 /*******************************
  249		 *           RESOURCES          *
  250		 *******************************/
  251
  252:- if(pldoc_server(true)).  253:- html_resource(pldoc_css,
  254		 [ virtual(true),
  255		   requires([ pldoc_resource('pldoc.css')
  256			    ])
  257		 ]).  258:- html_resource(pldoc_resource('pldoc.js'),
  259		 [ requires([ jquery
  260			    ])
  261		 ]).  262:- html_resource(pldoc_js,
  263		 [ virtual(true),
  264		   requires([ pldoc_resource('pldoc.js')
  265			    ])
  266		 ]).  267:- html_resource(pldoc,
  268		 [ virtual(true),
  269		   requires([ pldoc_css,
  270			      pldoc_js
  271			    ])
  272		 ]).  273:- else.  274:- html_resource(pldoc_css, [virtual(true)]).  275:- html_resource(pldoc_resource('pldoc.js'), [virtual(true)]).  276:- html_resource(pldoc_js, [virtual(true)]).  277:- html_resource(pldoc, [virtual(true)]).  278:- endif.  279
  280
  281		 /*******************************
  282		 *       FILE PROCESSING        *
  283		 *******************************/
 doc_for_file(+File, +Options) is det
HTTP handler that writes documentation for File as HTML. Options:
public_only(+Bool)
If true (default), only emit documentation for exported predicates.
edit(Bool)
If true, provide edit buttons. Default, these buttons are suppressed.
title(+Title)
Specify the page title. Default is the base name of the file.
Arguments:
File- Prolog file specification or xref source id.
  304doc_for_file(FileSpec, Options) :-
  305    doc_file_objects(FileSpec, File, Objects, FileOptions, Options),
  306    doc_file_title(File, Title, FileOptions, Options),
  307    doc_write_page(
  308	pldoc(file(File, Title)),
  309	title(Title),
  310	\prolog_file(File, Objects, FileOptions, Options),
  311	Options).
  312
  313doc_file_title(_, Title, _, Options) :-
  314    option(title(Title), Options),
  315    !.
  316doc_file_title(File, Title, FileOptions, _) :-
  317    memberchk(file(Title0, _Comment), FileOptions),
  318    !,
  319    file_base_name(File, Base),
  320    atomic_list_concat([Base, ' -- ', Title0], Title).
  321doc_file_title(File, Title, _, _) :-
  322    file_base_name(File, Title).
  323
  324:- html_meta doc_write_page(+, html, html, +).  325
  326doc_write_page(Style, Head, Body, Options) :-
  327    option(files(_), Options),
  328    !,
  329    phrase(page(Style, Head, Body), HTML),
  330    print_html(HTML).
  331doc_write_page(Style, Head, Body, _) :-
  332    reply_html_page(Style, Head, Body).
  333
  334
  335prolog_file(File, Objects, FileOptions, Options) -->
  336    { b_setval(pldoc_file, File),   % TBD: delete?
  337      file_directory_name(File, Dir)
  338    },
  339    html([ \doc_resources(Options),
  340	   \doc_links(Dir, FileOptions),
  341	   \file_header(File, FileOptions)
  342	 | \objects(Objects, FileOptions)
  343	 ]),
  344    undocumented(File, Objects, FileOptions).
 doc_resources(+Options)// is det
Include required resources (CSS, JS) into the output. The first clause supports doc_files.pl. A bit hacky ...
  351doc_resources(Options) -->
  352    { option(resource_directory(ResDir), Options),
  353      nb_current(pldoc_output, OutputFile),
  354      !,
  355      directory_file_path(ResDir, 'pldoc.css', Res),
  356      relative_file_name(Res, OutputFile, Ref)
  357    },
  358    html_requires(Ref).
  359doc_resources(Options) -->
  360    { option(html_resources(Resoures), Options, pldoc)
  361    },
  362    html_requires(Resoures).
 doc_file_objects(+FileSpec, -File, -Objects, -FileOptions, +Options) is det
Extracts relevant information for FileSpec from the PlDoc database. FileOptions contains:

Objects contains

We distinguish three different states for FileSpec:

  1. File was cross-referenced with collection enabled. All information is in the xref database.
  2. File was loaded. If comments are not loaded, cross-reference the file, while storing the comments as the compiler would do.
  3. Neither of the above. In this case we cross-reference the file.
Arguments:
FileSpec- File specification as used for load_files/2.
File- Prolog canonical filename
  391doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  392    xref_current_source(FileSpec),
  393    xref_option(FileSpec, comments(collect)),
  394    !,
  395    File = FileSpec,
  396    findall(Object, xref_doc_object(File, Object), Objects0),
  397    reply_file_objects(File, Objects0, Objects, FileOptions, Options).
  398doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  399    absolute_file_name(FileSpec, File,
  400		       [ file_type(prolog),
  401			 access(read)
  402		       ]),
  403    source_file(File),
  404    !,
  405    ensure_doc_objects(File),
  406    Pos = File:Line,
  407    findall(Line-doc(Obj,Pos,Comment),
  408	    doc_comment(Obj, Pos, _, Comment), Pairs),
  409    sort(Pairs, Pairs1),            % remove duplicates
  410    keysort(Pairs1, ByLine),
  411    pairs_values(ByLine, Objs0),
  412    reply_file_objects(File, Objs0, Objects, FileOptions, Options).
  413doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  414    absolute_file_name(FileSpec, File,
  415		       [ file_type(prolog),
  416			 access(read)
  417		       ]),
  418    xref_source(File, [silent(true)]),
  419    findall(Object, xref_doc_object(File, Object), Objects0),
  420    reply_file_objects(File, Objects0, Objects, FileOptions, Options).
  421
  422
  423reply_file_objects(File, Objs0, Objects, FileOptions, Options) :-
  424    module_info(File, ModuleOptions, Options),
  425    file_info(Objs0, Objs1, FileOptions, ModuleOptions),
  426    doc_hide_private(Objs1, ObjectsSelf, ModuleOptions),
  427    include_reexported(ObjectsSelf, Objects1, File, FileOptions),
  428    remove_doc_duplicates(Objects1, Objects, []).
  429
  430remove_doc_duplicates([], [], _).
  431remove_doc_duplicates([H|T0], [H|T], Seen) :-
  432    H = doc(_, _, Comment),
  433    \+ memberchk(Comment, Seen),
  434    !,
  435    remove_doc_duplicates(T0, T, [Comment|Seen]).
  436remove_doc_duplicates([_|T0], T, Seen) :-
  437    remove_doc_duplicates(T0, T, Seen).
  438
  439include_reexported(SelfObjects, Objects, File, Options) :-
  440    option(include_reexported(true), Options),
  441    option(module(Module), Options),
  442    option(public(Exports), Options),
  443    select_undocumented(Exports, Module, SelfObjects, Undoc),
  444    re_exported_doc(Undoc, File, Module, REObjs, _),
  445    REObjs \== [],
  446    !,
  447    append(SelfObjects, REObjs, Objects).
  448include_reexported(Objects, Objects, _, _).
 xref_doc_object(File, DocObject) is nondet
  453xref_doc_object(File, doc(M:module(Title),File:0,Comment)) :-
  454    xref_comment(File, Title, Comment),
  455    xref_module(File, M).
  456xref_doc_object(File, doc(M:Name/Arity,File:0,Comment)) :-
  457    xref_comment(File, Head, _Summary, Comment),
  458    xref_module(File, Module),
  459    strip_module(Module:Head, M, Plain),
  460    functor(Plain, Name, Arity).
 ensure_doc_objects(+File) is det
Ensure we have documentation about File. If we have no comments for the file because it was loaded before comment collection was enabled, run the cross-referencer on it to collect the comments and meta-information.
Arguments:
File- is a canonical filename that is loaded.
  471:- dynamic
  472    no_comments/2.  473
  474ensure_doc_objects(File) :-
  475    source_file(File),
  476    !,
  477    (   doc_file_has_comments(File)
  478    ->  true
  479    ;   no_comments(File, TimeChecked),
  480	time_file(File, TimeChecked)
  481    ->  true
  482    ;   xref_source(File, [silent(true), comments(store)]),
  483	retractall(no_comments(File, _)),
  484	(   doc_file_has_comments(File)
  485	->  true
  486	;   time_file(File, TimeChecked),
  487	    assertz(no_comments(File, TimeChecked))
  488	)
  489    ).
  490ensure_doc_objects(File) :-
  491    xref_source(File, [silent(true)]).
 module_info(+File, -ModuleOptions, +OtherOptions) is det
Add options module(Name), public(Exports) to OtherOptions if File is a module file.
  498module_info(File, [module(Module), public(Exports)|Options], Options) :-
  499    module_property(Module, file(File)),
  500    !,
  501    module_property(Module, exports(Exports)).
  502module_info(File, [module(Module), public(Exports)|Options], Options) :-
  503    xref_module(File, Module),
  504    !,
  505    findall(PI, xref_exported_pi(File, PI), Exports).
  506module_info(_, Options, Options).
  507
  508xref_exported_pi(Src, Name/Arity) :-
  509    xref_exported(Src, Head),
  510    functor(Head, Name, Arity).
 doc_hide_private(+Objs, +Public, +Options)
Remove the private objects from Objs according to Options.
  516doc_hide_private(Objs, Objs, Options) :-
  517    option(public_only(false), Options, true),
  518    !.
  519doc_hide_private(Objs0, Objs, Options) :-
  520    hide_private(Objs0, Objs, Options).
  521
  522hide_private([], [], _).
  523hide_private([H|T0], T, Options) :-
  524    obj(H, Obj),
  525    private(Obj, Options),
  526    !,
  527    hide_private(T0, T, Options).
  528hide_private([H|T0], [H|T], Options) :-
  529    hide_private(T0, T, Options).
 obj(+Term, -Object) is det
Extract the documented object from its environment. It is assumed to be the first term. Note that if multiple objects are described by the same comment Term is a list.
  537obj(doc(Obj0, _Pos, _Summary), Obj) :-
  538    !,
  539    (   Obj0 = [Obj|_]
  540    ->  true
  541    ;   Obj = Obj0
  542    ).
  543obj(Obj0, Obj) :-
  544    (   Obj0 = [Obj|_]
  545    ->  true
  546    ;   Obj = Obj0
  547    ).
 private(+Obj, +Options) is semidet
True if Obj is not exported from Options. This means Options defined a module and Obj is not member of the exports of the module.
  556:- multifile
  557    prolog:doc_is_public_object/1.  558
  559private(Object, _Options) :-
  560    prolog:doc_is_public_object(Object), !, fail.
  561private(Module:PI, Options) :-
  562    multifile(Module:PI, Options), !, fail.
  563private(Module:PI, Options) :-
  564    public(Module:PI, Options), !, fail.
  565private(Module:PI, Options) :-
  566    option(module(Module), Options),
  567    option(public(Public), Options),
  568    !,
  569    \+ ( member(PI2, Public),
  570	 eq_pi(PI, PI2)
  571       ).
  572private(Module:PI, _Options) :-
  573    module_property(Module, file(_)),      % A loaded module
  574    !,
  575    module_property(Module, exports(Exports)),
  576    \+ ( member(PI2, Exports),
  577	 eq_pi(PI, PI2)
  578       ).
  579private(Module:PI, _Options) :-
  580    \+ (pi_to_head(PI, Head),
  581	xref_exported(Source, Head),
  582	xref_module(Source, Module)).
 prolog:doc_is_public_object(+Object) is semidet
Hook that allows objects to be displayed with the default public-only view.
 multifile(+Obj, +Options) is semidet
True if Obj is a multifile predicate.
  593multifile(Obj, _Options) :-
  594    strip_module(user:Obj, Module, PI),
  595    pi_to_head(PI, Head),
  596    (   predicate_property(Module:Head, multifile)
  597    ;   xref_module(Source, Module),
  598	xref_defined(Source, Head, multifile(_Line))
  599    ),
  600    !.
 public(+Options, +Options)
True if Obj is declared using public/1.
  606public(Obj, _Options) :-
  607    strip_module(user:Obj, Module, PI),
  608    pi_to_head(PI, Head),
  609    (   predicate_property(Module:Head, public)
  610    ;   xref_module(Source, Module),
  611	xref_defined(Source, Head, public(_Line))
  612    ),
  613    !.
  614
  615pi_to_head(Var, _) :-
  616    var(Var), !, fail.
  617pi_to_head(Name/Arity, Term) :-
  618    functor(Term, Name, Arity).
  619pi_to_head(Name//DCGArity, Term) :-
  620    Arity is DCGArity+2,
  621    functor(Term, Name, Arity).
 file_info(+Comments, -RestComment, -FileOptions, +OtherOptions) is det
Add options file(Title, Comment) to OtherOptions if available.
  627file_info(Comments, RestComments, [file(Title, Comment)|Opts], Opts) :-
  628    select(doc(_:module(Title),_,Comment), Comments, RestComments),
  629    !.
  630file_info(Comments, Comments, Opts, Opts).
 file_header(+File, +Options)// is det
Create the file header.
  637file_header(File, Options) -->
  638    { memberchk(file(Title, Comment), Options),
  639      !,
  640      file_base_name(File, Base)
  641    },
  642    file_title([Base, ' -- ', Title], File, Options),
  643    { is_structured_comment(Comment, Prefixes),
  644      string_codes(Comment, Codes),
  645      indented_lines(Codes, Prefixes, Lines),
  646      section_comment_header(Lines, _Header, Lines1),
  647      wiki_lines_to_dom(Lines1, [], DOM)
  648    },
  649    html(DOM).
  650file_header(File, Options) -->
  651    { file_base_name(File, Base)
  652    },
  653    file_title([Base], File, Options).
 file_title(+Title:list, +File, +Options)// is det
Emit the file-header and manipulation buttons.
  660file_title(Title, File, Options) -->
  661    prolog:doc_file_title(Title, File, Options),
  662    !.
  663file_title(Title, File, Options) -->
  664    { file_base_name(File, Base)
  665    },
  666    html(h1(class(file),
  667	    [ span(style('float:right'),
  668		   [ \reload_button(File, Base, Options),
  669		     \zoom_button(Base, Options),
  670		     \source_button(Base, Options),
  671		     \edit_button(File, Options)
  672		   ])
  673	    | Title
  674	    ])).
 reload_button(+File, +Base, +Options)// is det
Create a button for reloading the sources and updating the documentation page. Note that the button is not shown if the file is not loaded because we do not want to load files through the documentation system.
  684reload_button(File, _Base, Options) -->
  685    { \+ source_file(File),
  686      \+ option(files(_), Options)
  687    },
  688    !,
  689    html(span(class(file_anot), '[not loaded]')).
  690reload_button(_File, Base, Options) -->
  691    { option(edit(true), Options),
  692      !,
  693      option(public_only(Public), Options, true)
  694    },
  695    html(a(href(Base+[reload(true), public_only(Public)]),
  696	   img([ class(action),
  697		 alt('Reload'),
  698		 title('Make & Reload'),
  699		 src(location_by_id(pldoc_resource)+'reload.png')
  700	       ]))).
  701reload_button(_, _, _) --> [].
 edit_button(+File, +Options)// is det
Create an edit button for File. If the button is clicked, JavaScript sends a message to the server without modifying the current page. JavaScript code is in the file pldoc.js.
  709edit_button(File, Options) -->
  710    { option(edit(true), Options)
  711    },
  712    !,
  713    html(a([ onClick('HTTPrequest(\'' +
  714		     location_by_id(pldoc_edit) + [file(File)] +
  715		     '\')')
  716	   ],
  717	   img([ class(action),
  718		 alt(edit),
  719		 title('Edit file'),
  720		 src(location_by_id(pldoc_resource)+'edit.png')
  721	     ]))).
  722edit_button(_, _) -->
  723    [].
 zoom_button(BaseName, +Options)// is det
Add zoom in/out button to show/hide the private documentation.
  730zoom_button(_, Options) -->
  731    { option(files(_Map), Options) },
  732    !.    % generating files
  733zoom_button(Base, Options) -->
  734    {   (   option(public_only(true), Options, true)
  735	->  Zoom = 'public.png',
  736	    Alt = 'Public',
  737	    Title = 'Click to include private',
  738	    PublicOnly = false
  739	;   Zoom = 'private.png',
  740	    Alt = 'All predicates',
  741	    Title = 'Click to show exports only',
  742	    PublicOnly = true
  743	)
  744    },
  745    html(a(href(Base+[public_only(PublicOnly)]),
  746	   img([ class(action),
  747		 alt(Alt),
  748		 title(Title),
  749		 src(location_by_id(pldoc_resource)+Zoom)
  750	       ]))).
 source_button(+File, +Options)// is det
Add show-source button.
  757source_button(_File, Options) -->
  758    { option(files(_Map), Options) },
  759    !.    % generating files
  760source_button(File, _Options) -->
  761    { (   is_absolute_file_name(File)
  762      ->  doc_file_href(File, HREF0)
  763      ;   HREF0 = File
  764      )
  765    },
  766    html(a(href(HREF0+[show(src)]),
  767	   img([ class(action),
  768		 alt('Show source'),
  769		 title('Show source'),
  770		 src(location_by_id(pldoc_resource)+'source.png')
  771	       ]))).
 objects(+Objects:list, +Options)// is det
Emit the documentation body. Options includes:
navtree(+Boolean)
If true, provide a navitation tree.
  781objects(Objects, Options) -->
  782    { option(navtree(true), Options),
  783      !,
  784      objects_nav_tree(Objects, Tree)
  785    },
  786    html([ div(class(navtree),
  787	       div(class(navwindow),
  788		   \nav_tree(Tree, Objects, Options))),
  789	   div(class(navcontent),
  790	       \objects_nt(Objects, Options))
  791	 ]).
  792objects(Objects, Options) -->
  793    objects_nt(Objects, Options).
  794
  795objects_nt(Objects, Options) -->
  796    objects(Objects, [body], Options).
  797
  798objects([], Mode, _) -->
  799    pop_mode(body, Mode, _).
  800objects([Obj|T], Mode, Options) -->
  801    object(Obj, Mode, Mode1, Options),
  802    objects(T, Mode1, Options).
 object(+Spec, +ModeIn, -ModeOut, +Options)// is det
Emit the documentation of a single object.
Arguments:
Spec- is one of doc(Obj,Pos,Comment), which is used to list the objects documented in a file or a plain Obj, used for documenting the object regardless of its location.
  813object(doc(Obj,Pos,Comment), Mode0, Mode, Options) -->
  814    !,
  815    object(Obj, [Pos-Comment], Mode0, Mode, [scope(file)|Options]).
  816object(Obj, Mode0, Mode, Options) -->
  817    { findall(Pos-Comment,
  818	      doc_comment(Obj, Pos, _Summary, Comment),
  819	      Pairs)
  820    },
  821    !,
  822    { b_setval(pldoc_object, Obj) },
  823    object(Obj, Pairs, Mode0, Mode, Options).
  824
  825object(Obj, Pairs, Mode0, Mode, Options) -->
  826    { is_pi(Obj),
  827      !,
  828      maplist(pred_dom(Obj, Options), Pairs, DOMS),
  829      append(DOMS, DOM)
  830    },
  831    need_mode(dl, Mode0, Mode),
  832    html(DOM).
  833object([Obj|_Same], Pairs, Mode0, Mode, Options) -->
  834    !,
  835    object(Obj, Pairs, Mode0, Mode, Options).
  836object(Obj, _Pairs, Mode, Mode, _Options) -->
  837    { debug(pldoc, 'Skipped ~p', [Obj]) },
  838    [].
  839
  840pred_dom(Obj, Options, Pos-Comment, DOM) :-
  841    is_structured_comment(Comment, Prefixes),
  842    string_codes(Comment, Codes),
  843    indented_lines(Codes, Prefixes, Lines),
  844    strip_module(user:Obj, Module, _),
  845    process_modes(Lines, Module, Pos, Modes, Args, Lines1),
  846    (   private(Obj, Options)
  847    ->  Class = privdef             % private definition
  848    ;   multifile(Obj, Options)
  849    ->  (   option(scope(file), Options)
  850	->  (   more_doc(Obj, Pos)
  851	    ->  Class = multidef(object(Obj))
  852	    ;   Class = multidef
  853	    )
  854	;   Class = multidef(file((Pos)))
  855	)
  856    ;   public(Obj, Options)
  857    ->  Class = publicdef           % :- public definition
  858    ;   Class = pubdef              % exported definition
  859    ),
  860    (   Obj = Module:_
  861    ->  POptions = [module(Module)|Options]
  862    ;   POptions = Options
  863    ),
  864    Pos = File:Line,
  865    DTOptions = [file(File),line(Line)|POptions],
  866    DOM = [\pred_dt(Modes, Class, DTOptions), dd(class=defbody, DOM1)],
  867    wiki_lines_to_dom(Lines1, Args, DOM0),
  868    strip_leading_par(DOM0, DOM1).
  869
  870more_doc(Obj, File:_) :-
  871    doc_comment(Obj, File2:_, _, _),
  872    File2 \== File,
  873    !.
 need_mode(+Mode:atom, +Stack:list, -NewStack:list)// is det
While predicates are part of a description list, sections are not and we therefore need to insert <dl>...</dl> into the output. We do so by demanding an outer environment and push/pop the required elements.
  882need_mode(Mode, Stack, Stack) -->
  883    { Stack = [Mode|_] },
  884    !,
  885    [].
  886need_mode(Mode, Stack, Rest) -->
  887    { memberchk(Mode, Stack)
  888    },
  889    !,
  890    pop_mode(Mode, Stack, Rest).
  891need_mode(Mode, Stack, [Mode|Stack]) -->
  892    !,
  893    html_begin(Mode).
  894
  895pop_mode(Mode, Stack, Stack) -->
  896    { Stack = [Mode|_] },
  897    !,
  898    [].
  899pop_mode(Mode, [H|Rest0], Rest) -->
  900    html_end(H),
  901    pop_mode(Mode, Rest0, Rest).
 undocumented(+File, +Objects, +Options)// is det
Describe undocumented predicates if the file is a module file.
  907undocumented(File, Objs, Options) -->
  908    { option(module(Module), Options),
  909      option(public(Exports), Options),
  910      select_undocumented(Exports, Module, Objs, Undoc),
  911      re_exported_doc(Undoc, File, Module, UREObjs, ReallyUnDoc),
  912      sort(2, @=<, UREObjs, REObjs) % UREObjs = doc(PI,Pos,Comment)
  913                                    % i.e., sort on Pos
  914    },
  915    !,
  916    re_exported_doc(REObjs, Options),
  917    undocumented(ReallyUnDoc, Options).
  918undocumented(_, _, _) -->
  919    [].
  920
  921re_exported_doc([], _) --> !.
  922re_exported_doc(Objs, Options) -->
  923    reexport_header(Objs, Options),
  924    objects(Objs, Options).
  925
  926reexport_header(_, Options) -->
  927    { option(reexport_header(true), Options, true)
  928    },
  929    !,
  930    html([ h2(class(wiki), 'Re-exported predicates'),
  931	   p([ "The following predicates are exported from this file \c
  932                while their implementation is defined in imported modules \c
  933                or non-module files loaded by this module."
  934	     ])
  935	 ]).
  936reexport_header(_, _) -->
  937    [].
  938
  939undocumented([], _) --> !.
  940undocumented(UnDoc, Options) -->
  941    html([ h2(class(undoc), 'Undocumented predicates'),
  942	   p(['The following predicates are exported, but not ',
  943	      'or incorrectly documented.'
  944	     ]),
  945	   dl(class(undoc),
  946	      \undocumented_predicates(UnDoc, Options))
  947	 ]).
  948
  949
  950undocumented_predicates([], _) -->
  951    [].
  952undocumented_predicates([H|T], Options) -->
  953    undocumented_pred(H, Options),
  954    undocumented_predicates(T, Options).
  955
  956undocumented_pred(Name/Arity, Options) -->
  957    { functor(Head, Name, Arity) },
  958    html(dt(class=undoc, \pred_mode(Head, [], _, Options))).
  959
  960select_undocumented([], _, _, []).
  961select_undocumented([PI|T0], M, Objs, [PI|T]) :-
  962    is_pi(PI),
  963    \+ in_doc(M:PI, Objs),
  964    !,
  965    select_undocumented(T0, M, Objs, T).
  966select_undocumented([_|T0], M, Objs, T) :-
  967    select_undocumented(T0, M, Objs, T).
  968
  969in_doc(PI, Objs) :-
  970    member(doc(O,_,_), Objs),
  971    (   is_list(O)
  972    ->  member(O2, O),
  973	eq_pi(PI, O2)
  974    ;   eq_pi(PI, O)
  975    ).
 eq_pi(PI1, PI2) is semidet
True if PI1 and PI2 refer to the same predicate.
  982eq_pi(PI, PI) :- !.
  983eq_pi(M:PI1, M:PI2) :-
  984    atom(M),
  985    !,
  986    eq_pi(PI1, PI2).
  987eq_pi(Name/A, Name//DCGA) :-
  988    A =:= DCGA+2,
  989    !.
  990eq_pi(Name//DCGA, Name/A) :-
  991    A =:= DCGA+2.
 is_pi(@Term) is semidet
True if Term is a predicate indicator.
  997is_pi(Var) :-
  998    var(Var),
  999    !,
 1000    fail.
 1001is_pi(_:PI) :-
 1002    !,
 1003    is_pi(PI).
 1004is_pi(_/_).
 1005is_pi(_//_).
 re_exported_doc(+Undoc:list(pi), +File:atom, +Module:atom, -ImportedDoc, -ReallyUnDoc:list(pi))
 1011re_exported_doc([], _, _, [], []).
 1012re_exported_doc([PI|T0], File, Module, [doc(Orig:PI,Pos,Comment)|ObjT], UnDoc) :-
 1013    pi_to_head(PI, Head),
 1014    (   predicate_property(Module:Head, imported_from(Orig))
 1015    ->  true
 1016    ;   predicate_property(Module:Head, exported)
 1017    ->  Orig = Module
 1018    ;   xref_defined(File, Head, imported(File2)),
 1019	ensure_doc_objects(File2),
 1020	xref_module(File2, Orig)
 1021    ),
 1022    doc_comment(Orig:PI, Pos, _, Comment),
 1023    !,
 1024    re_exported_doc(T0, File, Module, ObjT, UnDoc).
 1025re_exported_doc([PI|T0], File, Module, REObj, [PI|UnDoc]) :-
 1026    re_exported_doc(T0, File, Module, REObj, UnDoc).
 1027
 1028
 1029		 /*******************************
 1030		 *      SINGLE OBJECT PAGE      *
 1031		 *******************************/
 object_page(+Obj, +Options)// is semidet
Generate an HTML page describing Obj. The top presents the file the object is documented in and a search-form. Options:
header(+Boolean)
Show the navigation and search header.
 1041object_page(Obj, Options) -->
 1042    prolog:doc_object_page(Obj, Options),
 1043    !,
 1044    object_page_footer(Obj, Options).
 1045object_page(Obj, Options) -->
 1046    { doc_comment(Obj, File:_Line, _Summary, _Comment)
 1047    },
 1048    !,
 1049    (   { \+ ( doc_comment(Obj, File2:_, _, _),
 1050	       File2 \== File )
 1051	}
 1052    ->  html([ \html_requires(pldoc),
 1053	       \object_page_header(File, Options),
 1054	       \object_synopsis(Obj, []),
 1055	       \objects([Obj], Options)
 1056	     ])
 1057    ;   html([ \html_requires(pldoc),
 1058	       \object_page_header(-, Options),
 1059	       \objects([Obj], [synopsis(true)|Options])
 1060	     ])
 1061    ),
 1062    object_page_footer(Obj, Options).
 1063object_page(M:Name/Arity, Options) -->          % specified module, but public
 1064    { functor(Head, Name, Arity),
 1065      (   predicate_property(M:Head, exported)
 1066      ->  module_property(M, class(library))
 1067      ;   \+ predicate_property(M:Head, defined)
 1068      )
 1069    },
 1070    prolog:doc_object_page(Name/Arity, Options),
 1071    !,
 1072    object_page_footer(Name/Arity, Options).
 1073
 1074object_page_header(File, Options) -->
 1075    prolog:doc_page_header(file(File), Options),
 1076    !.
 1077object_page_header(File, Options) -->
 1078    { option(header(true), Options, true) },
 1079    !,
 1080    html(div(class(navhdr),
 1081	     [ div(class(jump), \file_link(File)),
 1082	       div(class(search), \search_form(Options)),
 1083	       br(clear(right))
 1084	     ])).
 1085object_page_header(_, _) --> [].
 1086
 1087file_link(-) -->
 1088    !,
 1089    places_menu(-).
 1090file_link(File) -->
 1091    { file_directory_name(File, Dir)
 1092    },
 1093    places_menu(Dir),
 1094    html([ div(a(href(location_by_id(pldoc_doc)+File), File))
 1095	 ]).
 object_footer(+Obj, +Options)// is det
Call the hook doc_object_footer//2. This hook is used by the examples machinery. When the changelog support is installed (cmake -DCHANGELOG=ON), a small block listing introduction and last change is appended after the hook output — see object_changelog//2.
 1105object_footer(Obj, Options) -->
 1106    object_changelog(Obj, Options),
 1107    (   prolog:doc_object_footer(Obj, Options)
 1108    ->  []
 1109    ;   []
 1110    ).
 object_changelog(+Obj, +Options)// is det
Emit a small footer noting when Obj (a Name/Arity, possibly module- or DCG-qualified, or a list of such, or f(...)) was introduced and when it was last changed. Empty when no events are known or when the changelog support was not built (CHANGELOG cmake option off). Each version is rendered as Maj.Min.Patch and linked to the underlying commit on GitHub.
 1121:- if(exists_source(library(pldoc/doc_changes))). 1122:- use_module(library(pldoc/doc_changes),
 1123              [doc_introduced/3, doc_last_changed/4]). 1124
 1125object_changelog(Objs, Options) -->
 1126    { is_list(Objs), !,
 1127      objs_pis(Objs, PIs)
 1128    },
 1129    object_changelog_pis(PIs, Options).
 1130object_changelog(Obj, Options) -->
 1131    { objs_pis([Obj], PIs)
 1132    },
 1133    object_changelog_pis(PIs, Options).
 1134
 1135object_changelog_pis(PIs, _Options) -->
 1136    { changelog_union(PIs, Events),
 1137      Events \== []
 1138    },
 1139    !,
 1140    html(div(class('pldoc-changes'),
 1141             [ div(class('pldoc-changes-header'), "History"),
 1142               ul(\changelog_events(Events)),
 1143               div(class('pldoc-changes-footer'),
 1144                   "Disclaimer: heuristically mined from GIT")
 1145             ])).
 1146object_changelog_pis(_, _) --> [].
 objs_pis(+Objs, -PIs) is det
Filter Objs (a list of documentation objects) to the predicate indicators stored in changelog_event/7. Module qualifiers are stripped; DCG arity is folded to the underlying predicate arity (the extractor does not yet emit // events separately). Other object shapes (xpce(...), c(...), f(...), library(...), section(...)) are dropped so the footer is suppressed for them.
 1157objs_pis(Objs, PIs) :-
 1158    convlist(obj_pi, Objs, PIs).
 1159
 1160obj_pi(_:O, PI) :- !, obj_pi(O, PI).
 1161obj_pi(Name/Arity,  Name/Arity)  :- atom(Name), integer(Arity).
 1162obj_pi(Name//Arity, Name/PArity) :-
 1163    atom(Name), integer(Arity),
 1164    PArity is Arity + 2.
 1165
 1166changelog_events(Events) -->
 1167    sequence(changelog_event, Events).
 1168
 1169changelog_event(Event) -->
 1170    html(li(\changelog_event_(Event))).
 1171
 1172changelog_event_(event(Type, Version, Hash, Subject, Repo)) -->
 1173    { clean_subject(Subject, Subject1)
 1174    },
 1175    html([ \event_type(Type), " in ",
 1176           \version_commit_link(Version, Hash, Repo),
 1177           " ", em(Subject1)
 1178         ]).
 1179
 1180clean_subject(Subject0, Subject) :-
 1181    string_codes(Subject0, Codes),
 1182    phrase((tag, string(Msg)), Codes),
 1183    !,
 1184    string_codes(Subject, Msg).
 1185clean_subject(Subject, Subject).
 1186
 1187tag -->
 1188    capitals, ":", whites.
 1189
 1190capitals -->
 1191    [C], { between(0'A, 0'Z, C) }, !,
 1192    capitals.
 1193capitals -->
 1194    [].
 1195
 1196event_type(introduced) ==> html(b(title("The predicate was added"),
 1197                                  "Introduced")).
 1198event_type(added)      ==> html(b(title("New functinality was added"),
 1199                                  "Extended")).
 1200event_type(enhanced)   ==> html(b(title("Implementation has been improved"),
 1201                                  "Enhanced")).
 1202event_type(fixed)      ==> html(b(title("A bug was fixed"),
 1203                                  "Fixed")).
 1204event_type(modified)   ==> html(b(title("The modification may break compatibility"),
 1205                                  "Modified")).
 1206event_type(Id)         ==> html(b(title("Unknow tag"),
 1207                                  Id)).
 changelog_union_line(+PIs, -Line) is nondet
Emit one paragraph (Line) for the introduction event (only if every PI in the block has an introduction event in the tracked range; otherwise the line is suppressed to avoid implying the whole family is newer than the older members really are), and one paragraph for the most recent change across any family member.
 1217changelog_union(PIs, [Event]) :-
 1218    all_introduced(PIs, Event).
 1219changelog_union(PIs, Events) :-
 1220    union_last_changed(PIs, Events).
 all_introduced(+PIs, -Event) is semidet
Succeeds when every PI has an introduction event in the tracked range. Version is the highest such introduction version across the family — i.e. the version at which the family reached its currently-documented shape. Hash and Repo identify a representative commit (the one at that highest version).
 1230all_introduced(PIs, Event) :-
 1231    maplist(any_introduction, PIs, Events),
 1232    E0 = event(introduced, V0, _, _, _),
 1233    aggregate_all(max(V0, E0),
 1234                  member(E0, Events),
 1235                  max(_, Event)).
 1236
 1237any_introduction(PI, Event) :-
 1238    doc_introduced(PI, _, Event), !.
 union_last_changed(+PIs, -Events:list) is det
Most recent change event across PIs, ranked by version. Fails if no PI has a change event in the tracked range.
 1245union_last_changed(PIs, Events) :-
 1246    Ev = event(T0, V0, _H0, _S0, _R0),
 1247    findall(Ev,
 1248            ( member(PI, PIs),
 1249              doc_last_changed(PI, V0, T0, Ev)
 1250            ),
 1251            Events0),
 1252    sort(2, >=, Events0, Events).
 1253
 1254version_commit_link(V, Hash, Repo) -->
 1255    { version_string(V, VStr),
 1256      short_hash(Hash, Short),
 1257      commit_url(Repo, Hash, URL)
 1258    },
 1259    html([ b(VStr), ' (👉',
 1260           a([href(URL), target('_blank')], Short),
 1261           ')'
 1262         ]).
 1263
 1264version_string(N, VStr) :-
 1265    Major is N // 10000,
 1266    Minor is (N // 100) mod 100,
 1267    Patch is N mod 100,
 1268    format(string(VStr), "~d.~d.~d", [Major, Minor, Patch]).
 1269
 1270short_hash(Hash, Short) :-
 1271    string(Hash),
 1272    !,
 1273    sub_string(Hash, 0, 7, _, Short).
 1274short_hash(Hash, Short) :-
 1275    sub_atom(Hash, 0, 7, _, Short),
 1276    !.
 1277short_hash(Hash, Hash).
 1278
 1279commit_url(Repo, Hash, URL) :-
 1280    atom(Repo), Repo \== unknown,
 1281    !,
 1282    format(atom(URL), "https://github.com/~w/commit/~w", [Repo, Hash]).
 1283commit_url(_, Hash, URL) :-
 1284    format(atom(URL), "#~w", [Hash]).
 1285:- else.	% library(pldoc/doc_changes) is missing
 1286object_changelog(_, _) --> [].
 1287:- endif.
 object_page_footer(+Obj, +Options)// is det
Call the hook doc_object_page_footer//2. This hook will be used to deal with annotations.
 1295object_page_footer(Obj, Options) -->
 1296    prolog:doc_object_page_footer(Obj, Options),
 1297    !.
 1298object_page_footer(_, _) --> [].
 object_synopsis(Obj, Options)// is det
Provide additional information about Obj. Note that due to reexport facilities, predicates may be available from multiple modules.
To be done
- Currently we provide a synopsis for the one where the definition resides. This is not always correct. Notably there are cases where multiple implementation modules are bundled in a larger interface that is the `preferred' module.
 1312object_synopsis(Name/Arity, _) -->
 1313    { functor(Head, Name, Arity),
 1314      predicate_property(system:Head, built_in)
 1315    },
 1316    synopsis([span(class(builtin), 'built-in')]).
 1317object_synopsis(Name/Arity, Options) -->
 1318    !,
 1319    object_synopsis(_:Name/Arity, Options).
 1320object_synopsis(M:Name/Arity, Options) -->
 1321    { functor(Head, Name, Arity),
 1322      (   option(source(Spec), Options)
 1323      ->  absolute_file_name(Spec, File,
 1324			     [ access(read),
 1325			       file_type(prolog),
 1326			       file_errors(fail)
 1327			     ])
 1328      ;   predicate_property(M:Head, exported),
 1329	  \+ predicate_property(M:Head, imported_from(_)),
 1330	  module_property(M, file(File)),
 1331	  file_name_on_path(File, Spec)
 1332      ),
 1333      !,
 1334      unquote_filespec(Spec, Unquoted0),
 1335      (   prolog:pldoc_synopsis_spec(Unquoted0, Unquoted)
 1336      ->  true
 1337      ;   Unquoted = Unquoted0
 1338      )
 1339    },
 1340    use_module_synopsis(Head, File, Unquoted, Options).
 1341object_synopsis(Name//Arity, Options) -->
 1342    !,
 1343    { DCGArity is Arity+2 },
 1344    object_synopsis(Name/DCGArity, Options).
 1345object_synopsis(Module:Name//Arity, Options) -->
 1346    !,
 1347    { DCGArity is Arity+2 },
 1348    object_synopsis(Module:Name/DCGArity, Options).
 1349object_synopsis(f(_/_), _) -->
 1350    synopsis(span(class(function),
 1351		  [ 'Arithmetic function (see ',
 1352		    \object_ref(is/2, []),
 1353		    ')'
 1354		  ])).
 1355object_synopsis(c(Func), _) -->
 1356    {   sub_atom(Func, 0, _, _, 'PL_')
 1357    ;   sub_atom(Func, 0, _, _, 'S')
 1358    },
 1359    !,
 1360    synopsis([span(class(cfunc), 'C-language interface function')]).
 1361object_synopsis(_, _) --> [].
 1362
 1363:- html_meta(synopsis(html,?,?)). 1364
 1365use_module_synopsis(Head, File, Unquoted, Options) -->
 1366    { Args = [class(copy), title('Click to copy')] },
 1367    (   { option(href(HREF), Options) }
 1368    ->  synopsis([ code(Args, [':- use_module(',a(href(HREF), '~q'-[Unquoted]),').'])
 1369                 | \can_autoload(Head, File)
 1370                 ])
 1371    ;   synopsis([ code(Args, ':- use_module(~q).'-[Unquoted])
 1372                 | \can_autoload(Head, File)
 1373                 ])
 1374    ).
 1375
 1376synopsis(HTML) -->
 1377    html(div(class(synopsis),
 1378	     [ span(class('synopsis-hdr'), 'Availability:')
 1379	     | HTML
 1380	     ])).
 1381
 1382can_autoload(Head, File) -->
 1383    { predicate_property(Head, autoload(FileBase)),
 1384      file_name_extension(FileBase, _Ext, File)
 1385    },
 1386    !,
 1387    html(span(class(autoload), \can_be_autoloaded)).
 1388can_autoload(_, _) -->
 1389    [].
 1390
 1391can_be_autoloaded -->
 1392    { catch(http_link_to_id(pldoc_man, [section(autoload)], HREF),
 1393            error(_,_), fail)
 1394    },
 1395    html(['(can be ', a(href(HREF), autoloaded), ')']).
 1396can_be_autoloaded -->
 1397    html('(can be autoloaded)').
 unquote_filespec(+Spec, -Unquoted) is det
Translate e.g. library('semweb/rdf_db') into library(semweb/rdf_db).
 1405unquote_filespec(Spec, Unquoted) :-
 1406    compound(Spec),
 1407    Spec =.. [Alias,Path],
 1408    atom(Path),
 1409    atomic_list_concat(Parts, /, Path),
 1410    maplist(need_no_quotes, Parts),
 1411    !,
 1412    parts_to_path(Parts, UnquotedPath),
 1413    Unquoted =.. [Alias, UnquotedPath].
 1414unquote_filespec(Spec, Spec).
 1415
 1416need_no_quotes(Atom) :-
 1417    format(atom(A), '~q', [Atom]),
 1418    \+ sub_atom(A, 0, _, _, '\'').
 1419
 1420parts_to_path([One], One) :- !.
 1421parts_to_path(List, More/T) :-
 1422    (   append(H, [T], List)
 1423    ->  parts_to_path(H, More)
 1424    ).
 1425
 1426
 1427		 /*******************************
 1428		 *             PRINT            *
 1429		 *******************************/
 doc_write_html(+Out:stream, +Title:atomic, +DOM) is det
Write HTML for the documentation page DOM using Title to Out.
 1435doc_write_html(Out, Title, Doc) :-
 1436    doc_page_dom(Title, Doc, DOM),
 1437    phrase(html(DOM), Tokens),
 1438    print_html_head(Out),
 1439    print_html(Out, Tokens).
 doc_page_dom(+Title, +Body, -DOM) is det
Create the complete HTML DOM from the Title and Body. It adds links to the style-sheet and javaScript files.
 1446doc_page_dom(Title, Body, DOM) :-
 1447    DOM = html([ head([ title(Title),
 1448			link([ rel(stylesheet),
 1449			       type('text/css'),
 1450			       href(location_by_id(pldoc_resource)+'pldoc.css')
 1451			     ]),
 1452			script([ src(location_by_id(pldoc_resource)+'pldoc.js'),
 1453				 type('text/javascript')
 1454			       ], [])
 1455		      ]),
 1456		 body(Body)
 1457	       ]).
 print_html_head(+Out:stream) is det
Print the DOCTYPE line.
 1463print_html_head(Out) :-
 1464    format(Out,
 1465	   '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" \c
 1466	       "http://www.w3.org/TR/html4/strict.dtd">~n', []).
 1467
 1468% Rendering rules
 1469%
 1470% These rules translate \-terms produced by wiki.pl
 tags(+Tags)// is det
Emit the @tag tags of a description. Tags is produced by tags/3.
See also
- combine_tags/2.
 1478tags(Tags) -->
 1479    html(dl(class=tags, Tags)).
 tag(+Tag, +Values:list)// is det
Called from \tag(Name, Values) terms produced by doc_wiki.pl.
 1485tag(Tag, Values) -->
 1486    {   doc_tag_title(Tag, Title),
 1487	atom_concat('keyword-', Tag, Class)
 1488    },
 1489    html([ dt(class=Class, Title),
 1490	   \tag_values(Values, Class)
 1491	 ]).
 1492
 1493tag_values([], _) -->
 1494    [].
 1495tag_values([H|T], Class) -->
 1496    html(dd(class=Class, ['- '|H])),
 1497    tag_values(T, Class).
 doc_tag_title(+Tag, -Title) is det
Title is the name to use for Tag in the generated documentation.
 1504doc_tag_title(Tag, Title) :-
 1505    tag_title(Tag, Title),
 1506    !.
 1507doc_tag_title(Tag, Tag).
 1508
 1509tag_title(compat, 'Compatibility').
 1510tag_title(tbd,    'To be done').
 1511tag_title(see,    'See also').
 1512tag_title(error,  'Errors').
 1513tag_title(since,  'Since').
 args(+Params:list) is det
Called from \args(List) created by doc_wiki.pl. Params is a list of arg(Name, Descr).
 1520args(Params) -->
 1521    html([ dt(class=tag, 'Arguments:'),
 1522	   dd(table(class=arglist,
 1523		    \arg_list(Params)))
 1524	 ]).
 1525
 1526arg_list([]) -->
 1527    [].
 1528arg_list([H|T]) -->
 1529    argument(H),
 1530    arg_list(T).
 1531
 1532argument(arg(Name,Descr)) -->
 1533    html(tr([td(var(Name)), td(class=argdescr, ['- '|Descr])])).
 1534
 1535
 1536		 /*******************************
 1537		 *         NAVIGATION TREE      *
 1538		 *******************************/
 objects_nav_tree(+Objects, -Tree) is det
Provide a navigation tree showing the context of Object. Tree is of the form node(Object, Children).
 1545objects_nav_tree(Objects, Tree) :-
 1546    maplist(object_nav_tree, Objects, Trees),
 1547    union_trees(Trees, Tree0),
 1548    remove_unique_root(Tree0, Tree).
 1549
 1550object_nav_tree(Obj, Tree) :-
 1551    Node = node(directory(Dir), FileNodes),
 1552    FileNode = node(file(File), Siblings),
 1553    doc_comment(Obj, File:_Line, _Summary, _Comment),
 1554    !,
 1555    file_directory_name(File, Dir),
 1556    sibling_file_nodes(Dir, FileNodes0),
 1557    selectchk(node(file(File),[]), FileNodes0, FileNode, FileNodes),
 1558    findall(Sibling, doc_comment(Sibling, File:_, _, _), Siblings0),
 1559    delete(Siblings0, _:module(_), Siblings1),
 1560    doc_hide_private(Siblings1, Siblings2, []),
 1561    flatten(Siblings2, Siblings),   % a comment may describe objects
 1562    embed_directories(Node, Tree).
 1563
 1564sibling_file_nodes(Dir, Nodes) :-
 1565    findall(node(file(File), []),
 1566	    (   source_file(File),
 1567		file_directory_name(File, Dir)
 1568	    ),
 1569	    Nodes).
 1570
 1571embed_directories(Node, Tree) :-
 1572    Node = node(file(File), _),
 1573    !,
 1574    file_directory_name(File, Dir),
 1575    Super = node(directory(Dir), [Node]),
 1576    embed_directories(Super, Tree).
 1577embed_directories(Node, Tree) :-
 1578    Node = node(directory(Dir), _),
 1579    file_directory_name(Dir, SuperDir),
 1580    SuperDir \== Dir,
 1581    !,
 1582    Super = node(directory(SuperDir), [Node]),
 1583    embed_directories(Super, Tree).
 1584embed_directories(Tree, Tree).
 1585
 1586
 1587union_trees([Tree], Tree) :- !.
 1588union_trees([T1,T2|Trees], Tree) :-
 1589    merge_trees(T1, T2, M1),
 1590    union_trees([M1|Trees], Tree).
 1591
 1592merge_trees(node(R, Ch1), node(R, Ch2), node(R, Ch)) :-
 1593    merge_nodes(Ch1, Ch2, Ch).
 1594
 1595merge_nodes([], Ch, Ch) :- !.
 1596merge_nodes(Ch, [], Ch) :- !.
 1597merge_nodes([node(Root, Ch1)|T1], N1, [T1|Nodes]) :-
 1598    selectchk(node(Root, Ch2), N1, N2),
 1599    !,
 1600    merge_trees(node(Root, Ch1), node(Root, Ch2), T1),
 1601    merge_nodes(T1, N2, Nodes).
 1602merge_nodes([Node|T1], N1, [Node|Nodes]) :-
 1603    merge_nodes(T1, N1, Nodes).
 remove_unique_root(+TreeIn, -Tree)
Remove the root part that does not branch
 1609remove_unique_root(node(_, [node(R1, [R2])]), Tree) :-
 1610    !,
 1611    remove_unique_root(node(R1, [R2]), Tree).
 1612remove_unique_root(Tree, Tree).
 nav_tree(+Tree, +Current, +Options)// is det
Render the navigation tree
 1618nav_tree(Tree, Current, Options) -->
 1619    html(ul(class(nav),
 1620	    \object_tree(Tree, Current, Options))).
 object_tree(+Tree, +Current, +Options)// is det
Render a tree of objects used for navigation.
 1626object_tree(node(Id, []), Target, Options) -->
 1627    !,
 1628    { node_class(Id, Target, Class) },
 1629    html(li(class(Class),
 1630	    \node(Id, Options))).
 1631object_tree(node(Id, Children), Target, Options) -->
 1632    !,
 1633    { node_class(Id, Target, Class) },
 1634    html(li(class(Class),
 1635	    [ \node(Id, Options),
 1636	      ul(class(nav),
 1637		 \object_trees(Children, Target, Options))
 1638	    ])).
 1639object_tree(Id, Target, Options) -->
 1640    !,
 1641    { node_class(Id, Target, Class) },
 1642    html(li(class([obj|Class]), \node(Id, Options))).
 1643
 1644object_trees([], _, _) --> [].
 1645object_trees([H|T], Target, Options) -->
 1646    object_tree(H, Target, Options),
 1647    object_trees(T, Target, Options).
 1648
 1649node_class(Ids, Current, Class) :-
 1650    is_list(Ids),
 1651    !,
 1652    (   member(Id, Ids), memberchk(Id, Current)
 1653    ->  Class = [nav,current]
 1654    ;   Class = [nav]
 1655    ).
 1656node_class(Id, Current, Class) :-
 1657    (   memberchk(Id, Current)
 1658    ->  Class = [nav,current]
 1659    ;   Class = [nav]
 1660    ).
 1661
 1662node(file(File), Options) -->
 1663    !,
 1664    object_ref(file(File), [style(title)|Options]).
 1665node(Id, Options) -->
 1666    object_ref(Id, Options).
 1667
 1668
 1669		 /*******************************
 1670		 *            SECTIONS          *
 1671		 *******************************/
 1672
 1673section(Type, Title) -->
 1674    { string_codes(Title, Codes),
 1675      wiki_codes_to_dom(Codes, [], Content0),
 1676      strip_leading_par(Content0, Content),
 1677      make_section(Type, Content, HTML)
 1678    },
 1679    html(HTML).
 1680
 1681make_section(module,  Title, h1(class=module,  Title)).
 1682make_section(section, Title, h1(class=section, Title)).
 1683
 1684
 1685		 /*******************************
 1686		 *       PRED MODE HEADER       *
 1687		 *******************************/
 pred_dt(+Modes, +Class, Options)// is det
Emit the predicate header.
Arguments:
Modes- List as returned by process_modes/5.
 1695pred_dt(Modes, Class, Options) -->
 1696    pred_dt(Modes, Class, [], _Done, Options).
 1697
 1698pred_dt([], _, Done, Done, _) -->
 1699    [].
 1700pred_dt([H|T], Class, Done0, Done, Options) -->
 1701    { functor(Class, CSSClass, _) },
 1702    html(dt(class=CSSClass,
 1703	    [ \pred_mode(H, Done0, Done1, Options),
 1704	      \mode_anot(Class)
 1705	    ])),
 1706    pred_dt(T, Class, Done1, Done, Options).
 1707
 1708mode_anot(privdef) -->
 1709    !,
 1710    html(span([class(anot), style('float:right')],
 1711	      '[private]')).
 1712mode_anot(multidef(object(Obj))) -->
 1713    !,
 1714    { object_href(Obj, HREF) },
 1715    html(span([class(anot), style('float:right')],
 1716	      ['[', a(href(HREF), multifile), ']'
 1717	      ])).
 1718mode_anot(multidef(file(File:_))) -->
 1719    !,
 1720    { file_name_on_path(File, Spec),
 1721      unquote_filespec(Spec, Unquoted),
 1722      doc_file_href(File, HREF)
 1723    },
 1724    html(span([class(anot), style('float:right')],
 1725	      ['[multifile, ', a(href(HREF), '~q'-[Unquoted]), ']'
 1726	      ])).
 1727mode_anot(multidef) -->
 1728    !,
 1729    html(span([class(anot), style('float:right')],
 1730	      '[multifile]')).
 1731mode_anot(_) -->
 1732    [].
 1733
 1734pred_mode(mode(Head,Vars), Done0, Done, Options) -->
 1735    !,
 1736    { bind_vars(Head, Vars) },
 1737    pred_mode(Head, Done0, Done, Options).
 1738pred_mode(Head is Det, Done0, Done, Options) -->
 1739    !,
 1740    anchored_pred_head(Head, Done0, Done, Options),
 1741    pred_det(Det).
 1742pred_mode(Head, Done0, Done, Options) -->
 1743    anchored_pred_head(Head, Done0, Done, Options).
 1744
 1745bind_vars(Term, Bindings) :-
 1746    bind_vars(Bindings),
 1747    anon_vars(Term).
 1748
 1749bind_vars([]).
 1750bind_vars([Name=Var|T]) :-
 1751    Var = '$VAR'(Name),
 1752    bind_vars(T).
 anon_vars(+Term) is det
Bind remaining variables in Term to '$VAR'('_'), so they are printed as '_'.
 1759anon_vars(Var) :-
 1760    var(Var),
 1761    !,
 1762    Var = '$VAR'('_').
 1763anon_vars(Term) :-
 1764    compound(Term),
 1765    !,
 1766    Term =.. [_|Args],
 1767    maplist(anon_vars, Args).
 1768anon_vars(_).
 1769
 1770
 1771anchored_pred_head(Head, Done0, Done, Options) -->
 1772    { pred_anchor_name(Head, PI, Name) },
 1773    (   { memberchk(PI, Done0) }
 1774    ->  { Done = Done0 },
 1775	pred_head(Head)
 1776    ;   html([ span(style('float:right'),
 1777		    [ \pred_edit_or_source_button(Head, Options),
 1778		      &(nbsp)
 1779		    ]),
 1780	       a(name=Name, \pred_head(Head))
 1781	     ]),
 1782	{ Done = [PI|Done0] }
 1783    ).
 1784
 1785
 1786pred_edit_or_source_button(Head, Options) -->
 1787    { option(edit(true), Options) },
 1788    !,
 1789    pred_edit_button(Head, Options).
 1790pred_edit_or_source_button(Head, Options) -->
 1791    { option(source_link(true), Options) },
 1792    !,
 1793    pred_source_button(Head, Options).
 1794pred_edit_or_source_button(_, _) --> [].
 pred_edit_button(+PredIndicator, +Options)// is det
Create a button for editing the given predicate. Options processed:
module(M)
Resolve to module M
file(F)
For multi-file predicates: link to version in file.
line(L)
Line to edit (in file)
 1808pred_edit_button(_, Options) -->
 1809    { \+ option(edit(true), Options) },
 1810    !.
 1811pred_edit_button(PI0, Options0) -->
 1812    { canonicalise_predref(PI0, PI, Options0, Options) },
 1813    pred_edit_button2(PI, Options).
 1814
 1815pred_edit_button2(Name/Arity, Options) -->
 1816    { \+ ( memberchk(file(_), Options), % always edit if file and line
 1817	   memberchk(line(_), Options)  % are given.
 1818	 ),
 1819      functor(Head, Name, Arity),
 1820      option(module(M), Options, _),
 1821      \+ ( current_module(M),
 1822	   source_file(M:Head, _File)
 1823	 )
 1824    },
 1825    !.
 1826pred_edit_button2(Name/Arity, Options) -->
 1827    { include(edit_param, Options, Extra),
 1828      http_link_to_id(pldoc_edit,
 1829		      [name(Name),arity(Arity)|Extra],
 1830		      EditHREF)
 1831    },
 1832    html(a(onClick('HTTPrequest(\'' + EditHREF + '\')'),
 1833	   img([ class(action),
 1834		 alt('Edit predicate'),
 1835		 title('Edit predicate'),
 1836		 src(location_by_id(pldoc_resource)+'editpred.png')
 1837	       ]))).
 1838pred_edit_button2(_, _) -->
 1839    !,
 1840    [].
 1841
 1842edit_param(module(_)).
 1843edit_param(file(_)).
 1844edit_param(line(_)).
 object_edit_button(+Object, +Options)// is det
Create a button for editing Object.
 1851object_edit_button(_, Options) -->
 1852    { \+ option(edit(true), Options) },
 1853    !.
 1854object_edit_button(PI, Options) -->
 1855    { is_pi(PI) },
 1856    !,
 1857    pred_edit_button(PI, Options).
 1858object_edit_button(_, _) -->
 1859    [].
 pred_source_button(+PredIndicator, +Options)// is det
Create a button for viewing the source of a predicate.
 1866pred_source_button(PI0, Options0) -->
 1867    { canonicalise_predref(PI0, PI, Options0, Options),
 1868      option(module(M), Options, _),
 1869      pred_source_href(PI, M, HREF), !
 1870    },
 1871    html(a([ href(HREF),
 1872             class(source)
 1873	   ],
 1874	   img([ class(action),
 1875		 alt('Source'),
 1876		 title('Show source'),
 1877		 src(location_by_id(pldoc_resource)+'source.png')
 1878	       ]))).
 1879pred_source_button(_, _) -->
 1880    [].
 object_source_button(+Object, +Options)// is det
Create a button for showing the source of Object.
 1887object_source_button(PI, Options) -->
 1888    { is_pi(PI),
 1889      option(source_link(true), Options, true)
 1890    },
 1891    !,
 1892    pred_source_button(PI, Options).
 1893object_source_button(_, _) -->
 1894    [].
 canonicalise_predref(+PredRef, -PI:Name/Arity, +Options0, -Options) is det
Canonicalise a predicate reference. A possible module qualifier is added as module(M) to Options.
 1902canonicalise_predref(M:PI0, PI, Options0, [module(M)|Options]) :-
 1903    !,
 1904    canonicalise_predref(PI0, PI, Options0, Options).
 1905canonicalise_predref(//(Head), PI, Options0, Options) :-
 1906    !,
 1907    functor(Head, Name, Arity),
 1908    PredArity is Arity + 2,
 1909    canonicalise_predref(Name/PredArity, PI, Options0, Options).
 1910canonicalise_predref(Name//Arity, PI, Options0, Options) :-
 1911    integer(Arity), Arity >= 0,
 1912    !,
 1913    PredArity is Arity + 2,
 1914    canonicalise_predref(Name/PredArity, PI, Options0, Options).
 1915canonicalise_predref(PI, PI, Options, Options) :-
 1916    PI = Name/Arity,
 1917    atom(Name), integer(Arity), Arity >= 0,
 1918    !.
 1919canonicalise_predref(Head, PI, Options0, Options) :-
 1920    functor(Head, Name, Arity),
 1921    canonicalise_predref(Name/Arity, PI, Options0, Options).
 pred_head(+Term) is det
Emit a predicate head. The functor is typeset as a span using class pred and the arguments and var using class arglist.
 1929pred_head(Var) -->
 1930    { var(Var),
 1931      !,
 1932      instantiation_error(Var)
 1933    }.
 1934pred_head(//(Head)) -->
 1935    !,
 1936    pred_head(Head),
 1937    html(//).
 1938pred_head(M:Head) -->
 1939    html([span(class=module, M), :]),
 1940    pred_head(Head).
 1941pred_head(Head) -->
 1942    { atom(Head) },
 1943    !,
 1944    html(b(class=pred, Head)).
 1945pred_head(Head) -->                     % Infix operators
 1946    { Head =.. [Functor,Left,Right],
 1947      is_op_type(Functor, infix)
 1948    },
 1949    !,
 1950    html([ var(class=arglist, \pred_arg(Left, 1)),
 1951	   ' ', b(class=pred, Functor), ' ',
 1952	   var(class=arglist, \pred_arg(Right, 2))
 1953	 ]).
 1954pred_head(Head) -->                     % Prefix operators
 1955    { Head =.. [Functor,Arg],
 1956      is_op_type(Functor, prefix)
 1957    },
 1958    !,
 1959    html([ b(class=pred, Functor), ' ',
 1960	   var(class=arglist, \pred_arg(Arg, 1))
 1961	 ]).
 1962pred_head(Head) -->                     % Postfix operators
 1963    { Head =.. [Functor,Arg],
 1964      is_op_type(Functor, postfix)
 1965    },
 1966    !,
 1967    html([ var(class=arglist, \pred_arg(Arg, 1)),
 1968	   ' ', b(class=pred, Functor)
 1969	 ]).
 1970pred_head({Head}) -->
 1971    !,
 1972    html([ b(class=pred, '{'),
 1973	   var(class=arglist,
 1974	       \pred_args([Head], 1)),
 1975	   b(class=pred, '}')
 1976	 ]).
 1977pred_head(Head) -->                     % Plain terms
 1978    { Head =.. [Functor|Args] },
 1979    html([ b(class=pred, Functor),
 1980	   var(class=arglist,
 1981	       [ '(', \pred_args(Args, 1), ')' ])
 1982	 ]).
 is_op_type(+Atom, ?Type)
True if Atom is an operator of Type. Type is one of prefix, infix or postfix.
 1989is_op_type(Functor, Type) :-
 1990    current_op(_Pri, F, Functor),
 1991    op_type(F, Type).
 1992
 1993op_type(fx,  prefix).
 1994op_type(fy,  prefix).
 1995op_type(xf,  postfix).
 1996op_type(yf,  postfix).
 1997op_type(xfx, infix).
 1998op_type(xfy, infix).
 1999op_type(yfx, infix).
 2000op_type(yfy, infix).
 2001
 2002
 2003pred_args([], _) -->
 2004    [].
 2005pred_args([H|T], I) -->
 2006    pred_arg(H, I),
 2007    (   {T==[]}
 2008    ->  []
 2009    ;   html(', '),
 2010	{ I2 is I + 1 },
 2011	pred_args(T, I2)
 2012    ).
 2013
 2014pred_arg(Var, I) -->
 2015    { var(Var) },
 2016    !,
 2017    html(['Arg', I]).
 2018pred_arg(...(Term), I) -->
 2019    !,
 2020    pred_arg(Term, I),
 2021    html('...').
 2022pred_arg(Term, I) -->
 2023    { Term =.. [Ind,Arg],
 2024      mode_indicator(Ind)
 2025    },
 2026    !,
 2027    html([Ind, \pred_arg(Arg, I)]).
 2028pred_arg(Arg:Type, _) -->
 2029    !,
 2030    html([\argname(Arg), :, \argtype(Type)]).
 2031pred_arg(Arg, _) -->
 2032    argname(Arg).
 2033
 2034argname('$VAR'(Name)) -->
 2035    !,
 2036    html(Name).
 2037argname(Name) -->
 2038    !,
 2039    html(Name).
 2040
 2041argtype(Term) -->
 2042    { format(string(S), '~W',
 2043	     [ Term,
 2044	       [ quoted(true),
 2045		 numbervars(true)
 2046	       ]
 2047	     ]) },
 2048    html(S).
 2049
 2050pred_det(unknown) -->
 2051    [].
 2052pred_det(Det) -->
 2053    html([' is ', b(class=det, Det)]).
 term(+Text, +Term, +Bindings)// is det
Process the \term element as produced by doc_wiki.pl.
To be done
- Properly merge with pred_head//1
 2062term(_, Atom, []) -->
 2063    { atomic(Atom),
 2064      !,
 2065      format(string(S), '~W', [Atom,[quoted(true)]])
 2066    },
 2067    html(span(class=functor, S)).
 2068term(_, Key:Type, [TypeName=Type]) -->
 2069    { atomic(Key)
 2070    },
 2071    !,
 2072    html([span(class='pl-key', Key), :, span(class('pl-var'), TypeName)]).
 2073term(_, Term, Bindings) -->
 2074    { is_mode(Term is det),         % HACK. Bit too strict?
 2075      bind_vars(Bindings)
 2076    },
 2077    !,
 2078    pred_head(Term).
 2079term(_, Term, Bindings) -->
 2080    term(Term,
 2081	 [ variable_names(Bindings),
 2082	   quoued(true)
 2083	 ]).
 2084
 2085
 2086		 /*******************************
 2087		 *             PREDREF          *
 2088		 *******************************/
 predref(+PI)// is det
 predref(+PI, +Options)// is det
Create a reference to a predicate. The reference consists of the relative path to the file using the predicate indicator as anchor.

Current file must be available through the global variable pldoc_file. If this variable not set it creates a link to /doc/<file>#anchor. Such links only work in the online browser.

 2101predref(Term) -->
 2102    { catch(nb_getval(pldoc_options, Options), _, Options = []) },
 2103    predref(Term, Options).
 2104
 2105predref(Obj, Options) -->
 2106    { Obj = _:_,
 2107      doc_comment(Obj, File:_Line, _, _),
 2108      (   (   option(files(Map), Options)
 2109	  ->  memberchk(file(File,_), Map)
 2110	  ;   true
 2111	  )
 2112      ->  object_href(Obj, HREF, Options)
 2113      ;   manref(Obj, HREF, Options)
 2114      )
 2115    },
 2116    !,
 2117    html(a(href(HREF), \object_name(Obj, [qualify(true)|Options]))).
 2118predref(M:Term, Options) -->
 2119    !,
 2120    predref(Term, M, Options).
 2121predref(Term, Options) -->
 2122    predref(Term, _, Options).
 2123
 2124predref(Name/Arity, _, Options) -->             % Builtin; cannot be overruled
 2125    { prolog:doc_object_summary(Name/Arity, manual, _, _),
 2126      !,
 2127      manref(Name/Arity, HREF, Options)
 2128    },
 2129    html(a([class=builtin, href=HREF], [Name, /, Arity])).
 2130predref(Name/Arity, _, Options) -->             % From packages
 2131    { option(prefer(manual), Options),
 2132      prolog:doc_object_summary(Name/Arity, Category, _, _),
 2133      !,
 2134      manref(Name/Arity, HREF, Options)
 2135    },
 2136    html(a([class=Category, href=HREF], [Name, /, Arity])).
 2137predref(Obj, Module, Options) -->               % Local
 2138    { doc_comment(Module:Obj, File:_Line, _, _),
 2139      (   option(files(Map), Options)
 2140      ->  memberchk(file(File,_), Map)
 2141      ;   true
 2142      )
 2143    },
 2144    !,
 2145    object_ref(Module:Obj, Options).
 2146predref(Name/Arity, Module, Options) -->
 2147    { \+ option(files(_), Options),
 2148      pred_href(Name/Arity, Module, HREF)
 2149    },
 2150    !,
 2151    html(a(href=HREF, [Name, /, Arity])).
 2152predref(Name//Arity, Module, Options) -->
 2153    { \+ option(files(_), Options),
 2154      PredArity is Arity + 2,
 2155      pred_href(Name/PredArity, Module, HREF)
 2156    },
 2157    !,
 2158    html(a(href=HREF, [Name, //, Arity])).
 2159predref(PI, _, Options) -->             % From packages
 2160    { canonical_pi(PI, CPI, HTML),
 2161      (   option(files(_), Options)
 2162      ->  Category = extmanual
 2163      ;   prolog:doc_object_summary(CPI, Category, _, _)
 2164      ),
 2165      manref(CPI, HREF, Options)
 2166    },
 2167    html(a([class=Category, href=HREF], HTML)).
 2168predref(PI, _, _Options) -->
 2169    { canonical_pi(PI, _CPI, HTML)
 2170    },
 2171    !,
 2172    html(span(class=undef, HTML)).
 2173predref(Callable, Module, Options) -->
 2174    { callable(Callable),
 2175      functor(Callable, Name, Arity)
 2176    },
 2177    predref(Name/Arity, Module, Options).
 2178
 2179canonical_pi(Name/Arity, Name/Arity, [Name, /, Arity]) :-
 2180    atom(Name), integer(Arity),
 2181    !.
 2182canonical_pi(Name//Arity, Name/Arity2, [Name, //, Arity]) :-
 2183    atom(Name), integer(Arity),
 2184    !,
 2185    Arity2 is Arity+2.
 nopredref(+PI)//
Result of name/arity, non-linking predicate indicator.
 2191nopredref(PI) -->
 2192    { canonical_pi(PI, _CPI, HTML)
 2193    },
 2194    !,
 2195    html(span(class=nopredref, HTML)).
 flagref(+Flag)//
Reference to a Prolog flag.
To be done
- generate a link to the Prolog website?
 2203flagref(Flag) -->
 2204    html(code(Flag)).
 cite(+Citations)// is det
Emit citations. This is indented to allow for [@cite1;@cite2] for generating LaTex.
 2211cite(Citations) -->
 2212    html('['), citations(Citations), html(']').
 2213
 2214citations([]) --> [].
 2215citations([H|T]) -->
 2216    citation(H),
 2217    (   {T==[]}
 2218    ->  []
 2219    ;   [';'],
 2220	citations(T)
 2221    ).
 2222
 2223citation(H) -->
 2224    html([@,H]).
 manref(+NameArity, -HREF, +Options) is det
Create reference to a manual page. When generating files, this listens to the option man_server(+Server).
 2232manref(PI, HREF, Options) :-
 2233    predname(PI, PredName),
 2234    (   option(files(_Map), Options)
 2235    ->  option(man_server(Server), Options,
 2236	       'http://www.swi-prolog.org/pldoc'),
 2237	uri_components(Server, Comp0),
 2238	uri_data(path, Comp0, Path0),
 2239	directory_file_path(Path0, man, Path),
 2240	uri_data(path, Comp0, Path, Components),
 2241	uri_query_components(Query, [predicate=PredName]),
 2242	uri_data(search, Components, Query),
 2243	uri_components(HREF, Components)
 2244    ;   http_link_to_id(pldoc_man, [predicate=PredName], HREF)
 2245    ).
 2246
 2247predname(Name/Arity, PredName) :-
 2248    !,
 2249    format(atom(PredName), '~w/~d', [Name, Arity]).
 2250predname(Module:Name/Arity, PredName) :-
 2251    !,
 2252    format(atom(PredName), '~w:~w/~d', [Module, Name, Arity]).
 pred_href(+NameArity, +Module, -HREF) is semidet
Create reference. Prefer:
  1. Local definition
  2. If from package and documented: package documentation
  3. From any file
bug
- Should analyse import list to find where the predicate comes from.
 2266pred_href(Name/Arity, Module, HREF) :-
 2267    format(string(FragmentId), '~w/~d', [Name, Arity]),
 2268    uri_data(fragment, Components, FragmentId),
 2269    functor(Head, Name, Arity),
 2270    (   catch(relative_file(Module:Head, File), _, fail)
 2271    ->  uri_data(path, Components, File),
 2272	uri_components(HREF, Components)
 2273    ;   in_file(Module:Head, File)
 2274    ->  (   current_prolog_flag(home, SWI),
 2275	    sub_atom(File, 0, _, _, SWI),
 2276	    prolog:doc_object_summary(Name/Arity, packages, _, _)
 2277	->  http_link_to_id(pldoc_man, [predicate=FragmentId], HREF)
 2278	;   http_location_by_id(pldoc_doc, DocHandler),
 2279	    atom_concat(DocHandler, File, Path),
 2280	    uri_data(path, Components, Path),
 2281	    uri_components(HREF, Components)
 2282	)
 2283    ).
 2284
 2285relative_file(Head, '') :-
 2286    b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
 2287    in_file(Head, CurrentFile),
 2288    !.
 2289relative_file(Head, RelFile) :-
 2290    b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
 2291    in_file(Head, DefFile),
 2292    relative_file_name(DefFile, CurrentFile, RelFile).
 pred_source_href(+Pred:predicate_indicator, +Module, -HREF) is semidet
HREF is a URL to show the predicate source in its file.
 2298pred_source_href(Name/Arity, Module, HREF) :-
 2299    format(string(FragmentId), '~w/~d', [Name, Arity]),
 2300    uri_data(fragment, Components, FragmentId),
 2301    uri_query_components(Query, [show=src]),
 2302    uri_data(search, Components, Query),
 2303    functor(Head, Name, Arity),
 2304    (   catch(relative_file(Module:Head, File), _, fail)
 2305    ->  uri_data(path, Components, File),
 2306	uri_components(HREF, Components)
 2307    ;   in_file(Module:Head, File0)
 2308    ->  insert_alias(File0, File),
 2309	http_location_by_id(pldoc_doc, DocHandler),
 2310	atom_concat(DocHandler, File, Path),
 2311	uri_data(path, Components, Path),
 2312	uri_components(HREF, Components)
 2313    ).
 object_ref(+Object, +Options)// is det
Create a hyperlink to Object. Points to the /doc_for URL. Object is as the first argument of doc_comment/4. Note this can be a list of objects.
 2322object_ref([], _) -->
 2323    !,
 2324    [].
 2325object_ref([H|T], Options) -->
 2326    !,
 2327    object_ref(H, Options),
 2328    (   {T == []}
 2329    ->  html(', '),
 2330	object_ref(T, Options)
 2331    ;   []
 2332    ).
 2333object_ref(Obj, Options) -->
 2334    { object_href(Obj, HREF, Options)
 2335    },
 2336    html(a(href(HREF), \object_name(Obj, Options))).
 object_href(+Object, -HREF) is det
 object_href(+Object, -HREF, +Options) is det
HREF is the URL to access Object.
 2343object_href(Obj, HREF) :-
 2344    object_href(Obj, HREF, []).
 2345
 2346object_href(M:PI0, HREF, Options) :-
 2347    option(files(Map), Options),
 2348    (   module_property(M, file(File))
 2349    ->  true
 2350    ;   xref_module(File, M)
 2351    ),
 2352    memberchk(file(File, DocFile), Map),
 2353    !,
 2354    file_base_name(DocFile, LocalFile),     % TBD: proper directory index
 2355    expand_pi(PI0, PI),
 2356    term_to_string(PI, PIS),
 2357    uri_data(path, Components, LocalFile),
 2358    uri_data(fragment, Components, PIS),
 2359    uri_components(HREF, Components).
 2360object_href(file(File), HREF, _Options) :-
 2361    doc_file_href(File, HREF),
 2362    !.
 2363object_href(directory(Dir), HREF, _Options) :-
 2364    directory_file_path(Dir, 'index.html', Index),
 2365    doc_file_href(Index, HREF),
 2366    !.
 2367object_href(Obj, HREF, _Options) :-
 2368    prolog:doc_object_href(Obj, HREF),
 2369    !.
 2370object_href(Obj0, HREF, _Options) :-
 2371    localise_object(Obj0, Obj),
 2372    term_to_string(Obj, String),
 2373    http_link_to_id(pldoc_object, [object=String], HREF).
 2374
 2375expand_pi(Name//Arity0, Name/Arity) :-
 2376    !,
 2377    Arity is Arity0+2.
 2378expand_pi(PI, PI).
 localise_object(+ObjIn, -ObjOut) is det
Abstract path-details to make references more stable over versions.
 2386localise_object(Obj0, Obj) :-
 2387    prolog:doc_canonical_object(Obj0, Obj),
 2388    !.
 2389localise_object(Obj, Obj).
 term_to_string(+Term, -String) is det
Convert Term, possibly holding variables, into a canonical string using A, B, ... for variables and _ for singletons.
 2397term_to_string(Term, String) :-
 2398    State = state(-),
 2399    (   numbervars(Term, 0, _, [singletons(true)]),
 2400	with_output_to(string(String),
 2401		       write_term(Term,
 2402				  [ numbervars(true),
 2403				    quoted(true)
 2404				  ])),
 2405	nb_setarg(1, State, String),
 2406	fail
 2407    ;   arg(1, State, String)
 2408    ).
 object_name(+Obj, +Options)// is det
HTML description of documented Obj. Obj is as the first argument of doc_comment/4. Options:
style(+Style)
One of inline or title
qualify(+Boolean)
Qualify predicates by their module
secref_style(Style)
One of number, title or number_title
 2422object_name(Obj, Options) -->
 2423    { option(style(Style), Options, inline)
 2424    },
 2425    object_name(Style, Obj, Options).
 2426
 2427object_name(title, Obj, Options) -->
 2428    { merge_options(Options, [secref_style(title)], Options1) },
 2429    prolog:doc_object_link(Obj, Options1),
 2430    !.
 2431object_name(inline, Obj, Options) -->
 2432    prolog:doc_object_link(Obj, Options),
 2433    !.
 2434object_name(title, f(Name/Arity), _Options) -->
 2435    !,
 2436    html(['Function ', Name, /, Arity]).
 2437object_name(inline, f(Name/Arity), _Options) -->
 2438    !,
 2439    html([Name, /, Arity]).
 2440object_name(Style, PI, Options) -->
 2441    { is_pi(PI) },
 2442    !,
 2443    pi(Style, PI, Options).
 2444object_name(inline, Module:module(_Title), _) -->
 2445    !,
 2446    { module_property(Module, file(File)),
 2447      file_base_name(File, Base)
 2448    },
 2449    !,
 2450    html(Base).
 2451object_name(title, Module:module(Title), _) -->
 2452    { module_property(Module, file(File)),
 2453      file_base_name(File, Base)
 2454    },
 2455    !,
 2456    html([Base, ' -- ', Title]).
 2457object_name(title, file(File), _) -->
 2458    { module_property(Module, file(File)),
 2459      doc_comment(Module:module(Title), _, _, _),
 2460      !,
 2461      file_base_name(File, Base)
 2462    },
 2463    html([Base, ' -- ', Title]).
 2464object_name(_, file(File), _) -->
 2465    { file_base_name(File, Base) },
 2466    html(Base).
 2467object_name(_, directory(Dir), _) -->
 2468    { file_base_name(Dir, Base) },
 2469    html(Base).
 2470object_name(_, module(Title), _Options) -->
 2471    { print_message(warning,
 2472		    pldoc(module_comment_outside_module(Title)))
 2473    }.
 2474
 2475pi(title, PI, Options) -->
 2476    pi_type(PI),
 2477    pi(PI, Options).
 2478pi(inline, PI, Options) -->
 2479    pi(PI, Options).
 2480
 2481pi(M:PI, Options) -->
 2482    !,
 2483    (   { option(qualify(true), Options) }
 2484    ->  html([span(class(module), M), :])
 2485    ;   []
 2486    ),
 2487    pi(PI, Options).
 2488pi(Name/Arity, _) -->
 2489    !,
 2490    html([Name, /, \arity(Arity)]).
 2491pi(Name//Arity, _) -->
 2492    html([Name, //, \arity(Arity)]).
 2493
 2494arity(Arity) -->
 2495    { var(Arity) },
 2496    !,
 2497    html('_').
 2498arity(Arity) -->
 2499    html(Arity).
 2500
 2501pi_type(_:PI) -->
 2502    !,
 2503    pi_type(PI).
 2504pi_type(_/_) -->
 2505    html(['Predicate ']).
 2506pi_type(_//_) -->
 2507    html(['Grammar rule ']).
 in_file(+Head, ?File) is nondet
File is the name of a file containing the Predicate Head. Head may be qualified with a module.
To be done
- Prefer local, then imported, then `just anywhere'
- Look for documented and/or public predicates.
 2519in_file(Module:Head, File) :-
 2520    !,
 2521    distinct(File, in_file(Module, Head, File)).
 2522in_file(Head, File) :-
 2523    distinct(File, in_file(_, Head, File)).
 2524
 2525in_file(Module, Head, File) :-
 2526    var(Module),
 2527    (   predicate_property(system:Head, foreign)
 2528    ->  !,
 2529	fail
 2530    ;   predicate_property(system:Head, file(File)),
 2531	\+ system_arithmetic_function(Head)
 2532    ->  !
 2533    ;   predicate_property(Head, autoload(File0))
 2534    ->  !,
 2535	file_name_extension(File0, pl, File)
 2536    ;   exported_from(Module, Head, File),
 2537	module_property(Module, class(library))
 2538    ).
 2539in_file(Module, Head, File) :-
 2540    nonvar(Module),
 2541    predicate_property(Module:Head, file(File)),
 2542    \+ predicate_property(Module:Head, imported_from(_)).
 2543in_file(Module, Head, File) :-
 2544    xref_defined(File, Head, How),
 2545    xref_current_source(File),
 2546    atom(File),                     % only plain files
 2547    xref_module(File, Module),
 2548    How \= imported(_From).
 2549in_file(Module, Head, File) :-
 2550    exported_from(Module, Head, File).
 2551in_file(Module, Head, File) :-
 2552    predicate_property(Module:Head, file(File)),
 2553    \+ predicate_property(Module:Head, imported_from(_)).
 2554in_file(Module, Head, File) :-
 2555    current_module(Module),
 2556    source_file(Module:Head, File).
 2557
 2558exported_from(Module, Head, File) :-
 2559    distinct(Primary,
 2560	     (   predicate_property(Module:Head, exported),
 2561		 (   predicate_property(Module:Head, imported_from(Primary))
 2562		 ->  true
 2563		 ;   Primary = Module
 2564		 ))),
 2565    module_property(Primary, file(File)).
 2566
 2567:- multifile
 2568    arithmetic:evaluable/2. 2569
 2570system_arithmetic_function(Head) :-
 2571    functor(Head, Name, Arity),
 2572    FArith is Arity-1,
 2573    FArith >= 0,
 2574    functor(FHead, Name, FArith),
 2575    arithmetic:evaluable(FHead, system).
 file(+FileName)// is det
 file(+FileName, +Options)// is det
Create a link to another filename if the file exists. Called by \file(File) terms in the DOM term generated by wiki.pl. Supported options are:
label(+Label)
Label to use for the link to the file.
absolute_path(+Path)
Absolute location of the referenced file.
href(+HREF)
Explicitely provided link; overrule link computation.
map_extension(+Pairs)
Map the final extension if OldExt-NewExt is in Pairs.
files(+Map)
List of file(Name, Link) that specifies that we must user Link for the given physical file Name.
edit_handler(+Id)
HTTP handler Id to call if the user clicks the edit button.
To be done
- Translation of files to HREFS is a mess. How to relate these elegantly?
 2606file(File) -->
 2607    file(File, []).
 2608
 2609file(File, Options) -->
 2610    { catch(nb_getval(pldoc_options, GenOptions), _, GenOptions = []),
 2611      merge_options(Options, GenOptions, FinalOptions)
 2612    },
 2613    link_file(File, FinalOptions),
 2614    !.
 2615file(File, Options) -->
 2616    { option(edit_handler(Handler), Options),
 2617      http_current_request(Request),
 2618      memberchk(path(Path), Request),
 2619      absolute_file_name(File, Location,
 2620			 [ relative_to(Path)
 2621			 ]),
 2622      http_link_to_id(Handler, [location(Location)], HREF),
 2623      format(atom(Title), 'Click to create ~w', [File])
 2624    },
 2625    html(a([href(HREF), class(nofile), title(Title)], File)).
 2626file(File, _) -->
 2627    html(code(class(nofile), File)).
 2628
 2629link_file(File, Options) -->
 2630    { file_href(File, HREF, Options),
 2631      option(label(Label), Options, File),
 2632      option(class(Class), Options, file)
 2633    },
 2634    html(a([class(Class), href(HREF)], Label)).
 file_href(+FilePath, -HREF, +Options) is det
Find URL for refering to FilePath based on Options.
 2640file_href(_, HREF, Options) :-
 2641    option(href(HREF), Options),
 2642    !.
 2643file_href(File, HREF, Options) :-
 2644    file_href_real(File, HREF0, Options),
 2645    map_extension(HREF0, HREF, Options).
 map_extension(+HREFIn, -HREFOut, Options) is det
Replace extension using the option
 2653map_extension(HREF0, HREF, Options) :-
 2654    option(map_extension(Map), Options),
 2655    file_name_extension(Base, Old, HREF0),
 2656    memberchk(Old-New, Map),
 2657    !,
 2658    file_name_extension(Base, New, HREF).
 2659map_extension(HREF, HREF, _).
 2660
 2661
 2662file_href_real(File, HREF, Options) :-
 2663    (   option(absolute_path(Path), Options)
 2664    ;   existing_linked_file(File, Path)
 2665    ),
 2666    !,
 2667    (   option(files(Map), Options),
 2668	memberchk(file(Path, LinkFile), Map)
 2669    ->  true
 2670    ;   LinkFile = Path
 2671    ),
 2672    file_href(LinkFile, HREF).
 2673file_href_real(File, HREF, _) :-
 2674    directory_alias(Alias),
 2675    Term =.. [Alias,File],
 2676    absolute_file_name(Term, _,
 2677		       [ access(read),
 2678			 file_errors(fail)
 2679		       ]),
 2680    !,
 2681    http_absolute_location(Term, HREF, []).
 2682
 2683directory_alias(icons).
 2684directory_alias(css).
 file_href(+FilePath, -HREF) is det
Create a relative URL from the current location to the given absolute file name. It resolves the filename relative to the file being processed that is available through the global variable pldoc_file.
 2694file_href(Path, HREF) :-                % a loaded Prolog file
 2695    source_file(Path),
 2696    !,
 2697    doc_file_href(Path, HREF).
 2698file_href(Path, HREF) :-
 2699    (   nb_current(pldoc_output, CFile)
 2700    ;   nb_current(pldoc_file, CFile)
 2701    ),
 2702    CFile \== [],
 2703    !,
 2704    relative_file_name(Path, CFile, HREF).
 2705file_href(Path, Path).
 existing_linked_file(+File, -Path) is semidet
True if File is a path to an existing file relative to the current file. Path is the absolute location of File.
 2713existing_linked_file(File, Path) :-
 2714    catch(b_getval(pldoc_file, CurrentFile), _, fail),
 2715    CurrentFile \== [],
 2716    absolute_file_name(File, Path,
 2717		       [ relative_to(CurrentFile),
 2718			 access(read),
 2719			 file_errors(fail)
 2720		       ]).
 include(+FileName, +Type, +Options)// is det
Inline FileName. If this is an image file, show an inline image. Else we create a link like file//1. Called by \include(File, Type) terms in the DOM term generated by wiki.pl if it encounters [[file.ext]].
 2730include(PI, predicate, _) -->
 2731    !,
 2732    (   html_tokens_for_predicates(PI, [])
 2733    ->  []
 2734    ;   html(['[[', \predref(PI), ']]'])
 2735    ).
 2736include(File, image, Options) -->
 2737    { file_name_extension(_, svg, File),
 2738      file_href(File, HREF, Options),
 2739      !,
 2740      include(image_attribute, Options, Attrs0),
 2741      merge_options(Attrs0,
 2742		    [ alt(File),
 2743		      data(HREF),
 2744		      type('image/svg+xml')
 2745		    ], Attrs)
 2746    },
 2747    (   { option(caption(Caption), Options) }
 2748    ->  html(div(class(figure),
 2749		 [ div(class(image), object(Attrs, [])),
 2750		   div(class(caption), Caption)
 2751		 ]))
 2752    ;   html(object(Attrs, []))
 2753    ).
 2754include(File, image, Options) -->
 2755    { file_href(File, HREF, Options),
 2756      !,
 2757      include(image_attribute, Options, Attrs0),
 2758      merge_options(Attrs0,
 2759		    [ alt(File),
 2760		      border(0),
 2761		      src(HREF)
 2762		    ], Attrs)
 2763    },
 2764    (   { option(caption(Caption), Options) }
 2765    ->  html(div(class(figure),
 2766		 [ div(class(image), img(Attrs)),
 2767		   div(class(caption), Caption)
 2768		 ]))
 2769    ;   html(img(Attrs))
 2770    ).
 2771include(File, wiki, _Options) -->       % [[file.txt]] is included
 2772    { access_file(File, read),
 2773      !,
 2774      read_file_to_codes(File, String, []),
 2775      wiki_codes_to_dom(String, [], DOM)
 2776    },
 2777    html(DOM).
 2778include(File, _Type, Options) -->
 2779    link_file(File, Options),
 2780    !.
 2781include(File, _, _) -->
 2782    html(code(class(nofile), ['[[',File,']]'])).
 2783
 2784image_attribute(src(_)).
 2785image_attribute(alt(_)).
 2786image_attribute(title(_)).
 2787image_attribute(align(_)).
 2788image_attribute(width(_)).
 2789image_attribute(height(_)).
 2790image_attribute(border(_)).
 2791image_attribute(class(_)).
 2792image_attribute(style(_)).
 html_tokens_for_predicates(+PI, +Options)// is semidet
Inline description for a predicate as produced by the text below from wiki processing.
        * [[member/2]]
        * [[append/3]]
 2805html_tokens_for_predicates([], _Options) -->
 2806    [].
 2807html_tokens_for_predicates([H|T], Options) -->
 2808    !,
 2809    html_tokens_for_predicates(H, Options),
 2810    html_tokens_for_predicates(T, Options).
 2811html_tokens_for_predicates(PI, Options) -->
 2812    { PI = _:_/_,
 2813      !,
 2814      (   doc_comment(PI, Pos, _Summary, Comment)
 2815      ->  true
 2816      ;   Comment = ''
 2817      )
 2818    },
 2819    object(PI, [Pos-Comment], [dl], _, Options).
 2820html_tokens_for_predicates(Spec, Options) -->
 2821    { findall(PI, documented_pi(Spec, PI), List),
 2822      List \== [], !
 2823    },
 2824    html_tokens_for_predicates(List, Options).
 2825html_tokens_for_predicates(Spec, Options) -->
 2826    man_page(Spec,
 2827	     [ links(false),                % no header
 2828	       navtree(false),              % no navigation tree
 2829	       footer(false),               % no footer
 2830	       synopsis(false)              % no synopsis
 2831	     | Options
 2832	     ]).
 2833
 2834
 2835documented_pi(Spec, PI) :-
 2836    generalise_spec(Spec, PI),
 2837    doc_comment(PI, _Pos, _Summary, _Comment).
 2838
 2839generalise_spec(Name/Arity, _M:Name/Arity).
 2840generalise_spec(Name//Arity, _M:Name//Arity).
 2841
 2842
 2843		 /*******************************
 2844		 *           WIKI FILES         *
 2845		 *******************************/
 doc_for_wiki_file(+File, +Options) is det
Write HTML for the File containing wiki data.
 2852doc_for_wiki_file(FileSpec, Options) :-
 2853    absolute_file_name(FileSpec, File,
 2854		       [ access(read)
 2855		       ]),
 2856    read_file_to_codes(File, String, []),
 2857    b_setval(pldoc_file, File),
 2858    call_cleanup(reply_wiki_page(File, String, Options),
 2859		 nb_delete(pldoc_file)).
 2860
 2861reply_wiki_page(File, String, Options) :-
 2862    wiki_codes_to_dom(String, [], DOM0),
 2863    title(DOM0, File, Title),
 2864    insert_edit_button(DOM0, File, DOM, Options),
 2865    reply_html_page(pldoc(wiki),
 2866		    title(Title),
 2867		    [ \html_requires(pldoc)
 2868		    | DOM
 2869		    ]).
 2870
 2871title(DOM, _, Title) :-
 2872    sub_term(h1(_,Title), DOM),
 2873    !.
 2874title(_, File, Title) :-
 2875    file_base_name(File, Title).
 2876
 2877insert_edit_button(DOM, _, DOM, Options) :-
 2878    option(edit(false), Options, false),
 2879    !.
 2880insert_edit_button([h1(Attrs,Title)|DOM], File,
 2881		   [h1(Attrs,[ span(style('float:right'),
 2882				   \edit_button(File, [edit(true)]))
 2883			     | Title
 2884			     ])|DOM], _) :- !.
 2885insert_edit_button(DOM, File,
 2886		   [ h1(class(wiki),
 2887			[ span(style('float:right'),
 2888			       \edit_button(File, [edit(true)]))
 2889			])
 2890		   | DOM
 2891		   ], _).
 2892
 2893
 2894		 /*******************************
 2895		 *            ANCHORS           *
 2896		 *******************************/
 mode_anchor_name(+Mode, -Anchor:atom) is det
Get the anchor name for a mode.
 2902mode_anchor_name(Var, _) :-
 2903    var(Var),
 2904    !,
 2905    instantiation_error(Var).
 2906mode_anchor_name(mode(Head, _), Anchor) :-
 2907    !,
 2908    mode_anchor_name(Head, Anchor).
 2909mode_anchor_name(Head is _Det, Anchor) :-
 2910    !,
 2911    mode_anchor_name(Head, Anchor).
 2912mode_anchor_name(Head, Anchor) :-
 2913    pred_anchor_name(Head, _, Anchor).
 pred_anchor_name(+Head, -PI:atom/integer, -Anchor:atom) is det
Create an HTML anchor name from Head.
 2920pred_anchor_name(//(Head), Name/Arity, Anchor) :-
 2921    !,
 2922    functor(Head, Name, DCGArity),
 2923    Arity is DCGArity+2,
 2924    format(atom(Anchor), '~w/~d', [Name, Arity]).
 2925pred_anchor_name(Head, Name/Arity, Anchor) :-
 2926    functor(Head, Name, Arity),
 2927    format(atom(Anchor), '~w/~d', [Name, Arity]).
 2928
 2929:- multifile prolog:message//1. 2930
 2931prolog:message(pldoc(module_comment_outside_module(Title))) -->
 2932    [ 'PlDoc comment <module> ~w does not appear in a module'-[Title] ]