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	    localise_object/2,          % +Object, -Canonical
   82	    object_tree//3,             % +Tree, +Current, +Options
   83	    object_page//2,             % +Object, +Options, //
   84	    object_page_header//2,      % +File, +Options, //
   85	    object_synopsis//2,         % +Object, +Options, //
   86	    object_footer//2,           % +Object, +Options, //
   87	    object_page_footer//2,      % +Object, +Options, //
   88	    cite//1                     % +Citations
   89	  ]).   90% Get HTTP server infrastructure when available
   91:- if(exists_source(library(http/http_dispatch))).   92:- use_module(library(http/http_dispatch)).   93:- use_module(library(http/http_wrapper)).   94:- use_module(library(http/jquery)).   95:- use_module(library(apply)).   96:- use_module(library(debug)).   97:- use_module(library(error)).   98:- use_module(library(filesex)).   99:- use_module(library(lists)).  100:- use_module(library(occurs)).  101:- use_module(library(option)).  102:- use_module(library(pairs)).  103:- use_module(library(prolog_source)).  104:- use_module(library(prolog_xref)).  105:- use_module(library(readutil)).  106:- use_module(library(solution_sequences)).  107:- use_module(library(uri)).  108:- use_module(library(http/html_head)).  109:- use_module(library(http/html_write)).  110:- use_module(library(http/http_path)).  111:- use_module(library(http/term_html)).  112:- use_module(pldoc(doc_index)).  113:- use_module(pldoc(doc_man)).  114:- use_module(pldoc(doc_modes)).  115:- use_module(pldoc(doc_process)).  116:- use_module(pldoc(doc_search)).  117:- use_module(pldoc(doc_util)).  118:- use_module(pldoc(doc_wiki)).  119:- use_module(library(dcg/high_order)).  120:- use_module(library(dcg/basics)).  121
  122pldoc_server(true).
  123:- else.  124
  125:- multifile
  126    http:location/3.  127
  128http:location(pldoc_resource, '/pldoc/res', []).
  129
  130pldoc_server(false).
  131:- endif.  132
  133:- use_module(library(lists)).  134:- use_module(library(option)).  135:- use_module(library(uri)).  136:- use_module(library(readutil)).  137:- use_module(library(http/html_write)).  138:- use_module(library(http/http_path)).  139:- use_module(library(http/html_head)).  140:- use_module(library(http/term_html)).  141:- use_module(library(debug)).  142:- use_module(library(apply)).  143:- use_module(library(pairs)).  144:- use_module(library(filesex)).  145:- use_module(doc_process).  146:- use_module(doc_man).  147:- use_module(doc_modes).  148:- use_module(doc_wiki).  149:- use_module(doc_search).  150:- use_module(doc_index).  151:- use_module(doc_util).  152:- use_module(library(solution_sequences)).  153:- use_module(library(error)).  154:- use_module(library(occurs)).  155:- use_module(library(prolog_source)).  156:- use_module(library(prolog_xref)).  157
  158:- 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.
  170:- public
  171    args//1,                        % Called from \Term output created
  172    pred_dt//3,                     % by the wiki renderer
  173    section//2,
  174    tag//2.  175
  176
  177:- predicate_options(doc_for_wiki_file/2, 2,
  178		     [ edit(boolean)
  179		     ]).  180:- predicate_options(doc_hide_private/3, 3,
  181		     [module(atom), public(list), public_only(boolean)]).  182:- predicate_options(edit_button//2, 2,
  183		     [ edit(boolean)
  184		     ]).  185:- predicate_options(file//2, 2,
  186		     [ label(any),
  187		       absolute_path(atom),
  188		       href(atom),
  189		       map_extension(list),
  190		       files(list),
  191		       edit_handler(atom)
  192		     ]).  193:- predicate_options(file_header//2, 2,
  194		     [ edit(boolean),
  195		       files(list),
  196		       public_only(boolean)
  197		     ]).  198:- predicate_options(include//3, 3,
  199		     [ absolute_path(atom),
  200		       class(atom),
  201		       files(list),
  202		       href(atom),
  203		       label(any),
  204		       map_extension(list)
  205		     ]).  206:- predicate_options(object_edit_button//2, 2,
  207		     [ edit(boolean),
  208		       pass_to(pred_edit_button//2, 2)
  209		     ]).  210:- predicate_options(object_page//2, 2,
  211		     [ for(any),
  212		       header(boolean),
  213		       links(boolean),
  214		       no_manual(boolean),
  215		       try_manual(boolean),
  216		       search_in(oneof([all,app,man])),
  217		       search_match(oneof([name,summary])),
  218		       search_options(boolean)
  219		     ]).  220:- predicate_options(object_ref//2, 2,
  221		     [ files(list),
  222		       qualify(boolean),
  223		       style(oneof([number,title,number_title])),
  224		       secref_style(oneof([number,title,number_title]))
  225		     ]).  226:- predicate_options(object_synopsis//2, 2,
  227		     [ href(atom)
  228		     ]).  229:- predicate_options(pred_dt//3, 3,
  230		     [ edit(boolean)
  231		     ]).  232:- predicate_options(pred_edit_button//2, 2,
  233		     [ edit(boolean)
  234		     ]).  235:- predicate_options(predref//2, 2,
  236		     [ files(list),
  237		       prefer(oneof([manual,app])),
  238		       pass_to(object_ref/4, 2)
  239		     ]).  240:- predicate_options(private/2, 2,
  241		     [ module(atom),
  242		       public(list)
  243		     ]).  244:- predicate_options(source_button//2, 2,
  245		     [ files(list)
  246		     ]).  247
  248
  249		 /*******************************
  250		 *           RESOURCES          *
  251		 *******************************/
  252
  253:- if(pldoc_server(true)).  254:- html_resource(pldoc_css,
  255		 [ virtual(true),
  256		   requires([ pldoc_resource('pldoc.css')
  257			    ])
  258		 ]).  259:- html_resource(pldoc_resource('pldoc.js'),
  260		 [ requires([ jquery
  261			    ])
  262		 ]).  263:- html_resource(pldoc_js,
  264		 [ virtual(true),
  265		   requires([ pldoc_resource('pldoc.js')
  266			    ])
  267		 ]).  268:- html_resource(pldoc,
  269		 [ virtual(true),
  270		   requires([ pldoc_css,
  271			      pldoc_js
  272			    ])
  273		 ]).  274:- else.  275:- html_resource(pldoc_css, [virtual(true)]).  276:- html_resource(pldoc_resource('pldoc.js'), [virtual(true)]).  277:- html_resource(pldoc_js, [virtual(true)]).  278:- html_resource(pldoc, [virtual(true)]).  279:- endif.  280
  281
  282		 /*******************************
  283		 *       FILE PROCESSING        *
  284		 *******************************/
 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.
  305doc_for_file(FileSpec, Options) :-
  306    doc_file_objects(FileSpec, File, Objects, FileOptions, Options),
  307    doc_file_title(File, Title, FileOptions, Options),
  308    doc_write_page(
  309	pldoc(file(File, Title)),
  310	title(Title),
  311	\prolog_file(File, Objects, FileOptions, Options),
  312	Options).
  313
  314doc_file_title(_, Title, _, Options) :-
  315    option(title(Title), Options),
  316    !.
  317doc_file_title(File, Title, FileOptions, _) :-
  318    memberchk(file(Title0, _Comment), FileOptions),
  319    !,
  320    file_base_name(File, Base),
  321    atomic_list_concat([Base, ' -- ', Title0], Title).
  322doc_file_title(File, Title, _, _) :-
  323    file_base_name(File, Title).
  324
  325:- html_meta doc_write_page(+, html, html, +).  326
  327doc_write_page(Style, Head, Body, Options) :-
  328    option(files(_), Options),
  329    !,
  330    phrase(page(Style, Head, Body), HTML),
  331    print_html(HTML).
  332doc_write_page(Style, Head, Body, _) :-
  333    reply_html_page(Style, Head, Body).
  334
  335
  336prolog_file(File, Objects, FileOptions, Options) -->
  337    { b_setval(pldoc_file, File),   % TBD: delete?
  338      file_directory_name(File, Dir)
  339    },
  340    html([ \doc_resources(Options),
  341	   \doc_links(Dir, FileOptions),
  342	   \file_header(File, FileOptions)
  343	 | \objects(Objects, FileOptions)
  344	 ]),
  345    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 ...
  352doc_resources(Options) -->
  353    { option(resource_directory(ResDir), Options),
  354      nb_current(pldoc_output, OutputFile),
  355      !,
  356      directory_file_path(ResDir, 'pldoc.css', Res),
  357      relative_file_name(Res, OutputFile, Ref)
  358    },
  359    html_requires(Ref).
  360doc_resources(Options) -->
  361    { option(html_resources(Resoures), Options, pldoc)
  362    },
  363    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
  392doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  393    xref_current_source(FileSpec),
  394    xref_option(FileSpec, comments(collect)),
  395    !,
  396    File = FileSpec,
  397    findall(Object, xref_doc_object(File, Object), Objects0),
  398    reply_file_objects(File, Objects0, Objects, FileOptions, Options).
  399doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  400    absolute_file_name(FileSpec, File,
  401		       [ file_type(prolog),
  402			 access(read)
  403		       ]),
  404    source_file(File),
  405    !,
  406    ensure_doc_objects(File),
  407    Pos = File:Line,
  408    findall(Line-doc(Obj,Pos,Comment),
  409	    doc_comment(Obj, Pos, _, Comment), Pairs),
  410    sort(Pairs, Pairs1),            % remove duplicates
  411    keysort(Pairs1, ByLine),
  412    pairs_values(ByLine, Objs0),
  413    reply_file_objects(File, Objs0, Objects, FileOptions, Options).
  414doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  415    absolute_file_name(FileSpec, File,
  416		       [ file_type(prolog),
  417			 access(read)
  418		       ]),
  419    xref_source(File, [silent(true)]),
  420    findall(Object, xref_doc_object(File, Object), Objects0),
  421    reply_file_objects(File, Objects0, Objects, FileOptions, Options).
  422
  423
  424reply_file_objects(File, Objs0, Objects, FileOptions, Options) :-
  425    module_info(File, ModuleOptions, Options),
  426    file_info(Objs0, Objs1, FileOptions, ModuleOptions),
  427    doc_hide_private(Objs1, ObjectsSelf, ModuleOptions),
  428    include_reexported(ObjectsSelf, Objects1, File, FileOptions),
  429    remove_doc_duplicates(Objects1, Objects, []).
  430
  431remove_doc_duplicates([], [], _).
  432remove_doc_duplicates([H|T0], [H|T], Seen) :-
  433    H = doc(_, _, Comment),
  434    \+ memberchk(Comment, Seen),
  435    !,
  436    remove_doc_duplicates(T0, T, [Comment|Seen]).
  437remove_doc_duplicates([_|T0], T, Seen) :-
  438    remove_doc_duplicates(T0, T, Seen).
  439
  440include_reexported(SelfObjects, Objects, File, Options) :-
  441    option(include_reexported(true), Options),
  442    option(module(Module), Options),
  443    option(public(Exports), Options),
  444    select_undocumented(Exports, Module, SelfObjects, Undoc),
  445    re_exported_doc(Undoc, File, Module, REObjs, _),
  446    REObjs \== [],
  447    !,
  448    append(SelfObjects, REObjs, Objects).
  449include_reexported(Objects, Objects, _, _).
 xref_doc_object(File, DocObject) is nondet
  454xref_doc_object(File, doc(M:module(Title),File:0,Comment)) :-
  455    xref_comment(File, Title, Comment),
  456    xref_module(File, M).
  457xref_doc_object(File, doc(M:Name/Arity,File:0,Comment)) :-
  458    xref_comment(File, Head, _Summary, Comment),
  459    xref_module(File, Module),
  460    strip_module(Module:Head, M, Plain),
  461    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.
  472:- dynamic
  473    no_comments/2.  474
  475ensure_doc_objects(File) :-
  476    source_file(File),
  477    !,
  478    (   doc_file_has_comments(File)
  479    ->  true
  480    ;   no_comments(File, TimeChecked),
  481	time_file(File, TimeChecked)
  482    ->  true
  483    ;   xref_source(File, [silent(true), comments(store)]),
  484	retractall(no_comments(File, _)),
  485	(   doc_file_has_comments(File)
  486	->  true
  487	;   time_file(File, TimeChecked),
  488	    assertz(no_comments(File, TimeChecked))
  489	)
  490    ).
  491ensure_doc_objects(File) :-
  492    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.
  499module_info(File, [module(Module), public(Exports)|Options], Options) :-
  500    module_property(Module, file(File)),
  501    !,
  502    module_property(Module, exports(Exports)).
  503module_info(File, [module(Module), public(Exports)|Options], Options) :-
  504    xref_module(File, Module),
  505    !,
  506    findall(PI, xref_exported_pi(File, PI), Exports).
  507module_info(_, Options, Options).
  508
  509xref_exported_pi(Src, Name/Arity) :-
  510    xref_exported(Src, Head),
  511    functor(Head, Name, Arity).
 doc_hide_private(+Objs, +Public, +Options)
Remove the private objects from Objs according to Options.
  517doc_hide_private(Objs, Objs, Options) :-
  518    option(public_only(false), Options, true),
  519    !.
  520doc_hide_private(Objs0, Objs, Options) :-
  521    hide_private(Objs0, Objs, Options).
  522
  523hide_private([], [], _).
  524hide_private([H|T0], T, Options) :-
  525    obj(H, Obj),
  526    private(Obj, Options),
  527    !,
  528    hide_private(T0, T, Options).
  529hide_private([H|T0], [H|T], Options) :-
  530    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.
  538obj(doc(Obj0, _Pos, _Summary), Obj) :-
  539    !,
  540    (   Obj0 = [Obj|_]
  541    ->  true
  542    ;   Obj = Obj0
  543    ).
  544obj(Obj0, Obj) :-
  545    (   Obj0 = [Obj|_]
  546    ->  true
  547    ;   Obj = Obj0
  548    ).
 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.
  557:- multifile
  558    prolog:doc_is_public_object/1.  559
  560private(Object, _Options) :-
  561    prolog:doc_is_public_object(Object), !, fail.
  562private(Module:PI, Options) :-
  563    multifile(Module:PI, Options), !, fail.
  564private(Module:PI, Options) :-
  565    public(Module:PI, Options), !, fail.
  566private(Module:PI, Options) :-
  567    option(module(Module), Options),
  568    option(public(Public), Options),
  569    !,
  570    \+ ( member(PI2, Public),
  571	 eq_pi(PI, PI2)
  572       ).
  573private(Module:PI, _Options) :-
  574    module_property(Module, file(_)),      % A loaded module
  575    !,
  576    module_property(Module, exports(Exports)),
  577    \+ ( member(PI2, Exports),
  578	 eq_pi(PI, PI2)
  579       ).
  580private(Module:PI, _Options) :-
  581    \+ (pi_to_head(PI, Head),
  582	xref_exported(Source, Head),
  583	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.
  594multifile(Obj, _Options) :-
  595    strip_module(user:Obj, Module, PI),
  596    pi_to_head(PI, Head),
  597    (   predicate_property(Module:Head, multifile)
  598    ;   xref_module(Source, Module),
  599	xref_defined(Source, Head, multifile(_Line))
  600    ),
  601    !.
 public(+Options, +Options)
True if Obj is declared using public/1.
  607public(Obj, _Options) :-
  608    strip_module(user:Obj, Module, PI),
  609    pi_to_head(PI, Head),
  610    (   predicate_property(Module:Head, public)
  611    ;   xref_module(Source, Module),
  612	xref_defined(Source, Head, public(_Line))
  613    ),
  614    !.
  615
  616pi_to_head(Var, _) :-
  617    var(Var), !, fail.
  618pi_to_head(Name/Arity, Term) :-
  619    functor(Term, Name, Arity).
  620pi_to_head(Name//DCGArity, Term) :-
  621    Arity is DCGArity+2,
  622    functor(Term, Name, Arity).
 file_info(+Comments, -RestComment, -FileOptions, +OtherOptions) is det
Add options file(Title, Comment) to OtherOptions if available.
  628file_info(Comments, RestComments, [file(Title, Comment)|Opts], Opts) :-
  629    select(doc(_:module(Title),_,Comment), Comments, RestComments),
  630    !.
  631file_info(Comments, Comments, Opts, Opts).
 file_header(+File, +Options)// is det
Create the file header.
  638file_header(File, Options) -->
  639    { memberchk(file(Title, Comment), Options),
  640      !,
  641      file_base_name(File, Base)
  642    },
  643    file_title([Base, ' -- ', Title], File, Options),
  644    { is_structured_comment(Comment, Prefixes),
  645      string_codes(Comment, Codes),
  646      indented_lines(Codes, Prefixes, Lines),
  647      section_comment_header(Lines, _Header, Lines1),
  648      wiki_lines_to_dom(Lines1, [], DOM)
  649    },
  650    html(DOM).
  651file_header(File, Options) -->
  652    { file_base_name(File, Base)
  653    },
  654    file_title([Base], File, Options).
 file_title(+Title:list, +File, +Options)// is det
Emit the file-header and manipulation buttons.
  661file_title(Title, File, Options) -->
  662    prolog:doc_file_title(Title, File, Options),
  663    !.
  664file_title(Title, File, Options) -->
  665    { file_base_name(File, Base)
  666    },
  667    html(h1(class(file),
  668	    [ span(style('float:right'),
  669		   [ \reload_button(File, Base, Options),
  670		     \zoom_button(Base, Options),
  671		     \source_button(Base, Options),
  672		     \edit_button(File, Options)
  673		   ])
  674	    | Title
  675	    ])).
 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.
  685reload_button(File, _Base, Options) -->
  686    { \+ source_file(File),
  687      \+ option(files(_), Options)
  688    },
  689    !,
  690    html(span(class(file_anot), '[not loaded]')).
  691reload_button(_File, Base, Options) -->
  692    { option(edit(true), Options),
  693      !,
  694      option(public_only(Public), Options, true)
  695    },
  696    html(a(href(Base+[reload(true), public_only(Public)]),
  697	   img([ class(action),
  698		 alt('Reload'),
  699		 title('Make & Reload'),
  700		 src(location_by_id(pldoc_resource)+'reload.png')
  701	       ]))).
  702reload_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.
  710edit_button(File, Options) -->
  711    { option(edit(true), Options)
  712    },
  713    !,
  714    html(a([ onClick('HTTPrequest(\'' +
  715		     location_by_id(pldoc_edit) + [file(File)] +
  716		     '\')')
  717	   ],
  718	   img([ class(action),
  719		 alt(edit),
  720		 title('Edit file'),
  721		 src(location_by_id(pldoc_resource)+'edit.png')
  722	     ]))).
  723edit_button(_, _) -->
  724    [].
 zoom_button(BaseName, +Options)// is det
Add zoom in/out button to show/hide the private documentation.
  731zoom_button(_, Options) -->
  732    { option(files(_Map), Options) },
  733    !.    % generating files
  734zoom_button(Base, Options) -->
  735    {   (   option(public_only(true), Options, true)
  736	->  Zoom = 'public.png',
  737	    Alt = 'Public',
  738	    Title = 'Click to include private',
  739	    PublicOnly = false
  740	;   Zoom = 'private.png',
  741	    Alt = 'All predicates',
  742	    Title = 'Click to show exports only',
  743	    PublicOnly = true
  744	)
  745    },
  746    html(a(href(Base+[public_only(PublicOnly)]),
  747	   img([ class(action),
  748		 alt(Alt),
  749		 title(Title),
  750		 src(location_by_id(pldoc_resource)+Zoom)
  751	       ]))).
 source_button(+File, +Options)// is det
Add show-source button.
  758source_button(_File, Options) -->
  759    { option(files(_Map), Options) },
  760    !.    % generating files
  761source_button(File, _Options) -->
  762    { (   is_absolute_file_name(File)
  763      ->  doc_file_href(File, HREF0)
  764      ;   HREF0 = File
  765      )
  766    },
  767    html(a(href(HREF0+[show(src)]),
  768	   img([ class(action),
  769		 alt('Show source'),
  770		 title('Show source'),
  771		 src(location_by_id(pldoc_resource)+'source.png')
  772	       ]))).
 objects(+Objects:list, +Options)// is det
Emit the documentation body. Options includes:
navtree(+Boolean)
If true, provide a navitation tree.
  782objects(Objects, Options) -->
  783    { option(navtree(true), Options),
  784      !,
  785      objects_nav_tree(Objects, Tree)
  786    },
  787    html([ div(class(navtree),
  788	       div(class(navwindow),
  789		   \nav_tree(Tree, Objects, Options))),
  790	   div(class(navcontent),
  791	       \objects_nt(Objects, Options))
  792	 ]).
  793objects(Objects, Options) -->
  794    objects_nt(Objects, Options).
  795
  796objects_nt(Objects, Options) -->
  797    objects(Objects, [body], Options).
  798
  799objects([], Mode, _) -->
  800    pop_mode(body, Mode, _).
  801objects([Obj|T], Mode, Options) -->
  802    object(Obj, Mode, Mode1, Options),
  803    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.
  814object(doc(Obj,Pos,Comment), Mode0, Mode, Options) -->
  815    !,
  816    object(Obj, [Pos-Comment], Mode0, Mode, [scope(file)|Options]).
  817object(Obj, Mode0, Mode, Options) -->
  818    { findall(Pos-Comment,
  819	      doc_comment(Obj, Pos, _Summary, Comment),
  820	      Pairs)
  821    },
  822    !,
  823    { b_setval(pldoc_object, Obj) },
  824    object(Obj, Pairs, Mode0, Mode, Options).
  825
  826object(Obj, Pairs, Mode0, Mode, Options) -->
  827    { is_pi(Obj),
  828      !,
  829      maplist(pred_dom(Obj, Options), Pairs, DOMS),
  830      append(DOMS, DOM)
  831    },
  832    need_mode(dl, Mode0, Mode),
  833    html(DOM).
  834object([Obj|_Same], Pairs, Mode0, Mode, Options) -->
  835    !,
  836    object(Obj, Pairs, Mode0, Mode, Options).
  837object(Obj, _Pairs, Mode, Mode, _Options) -->
  838    { debug(pldoc, 'Skipped ~p', [Obj]) },
  839    [].
  840
  841pred_dom(Obj, Options, Pos-Comment, DOM) :-
  842    is_structured_comment(Comment, Prefixes),
  843    string_codes(Comment, Codes),
  844    indented_lines(Codes, Prefixes, Lines),
  845    strip_module(user:Obj, Module, _),
  846    process_modes(Lines, Module, Pos, Modes, Args, Lines1),
  847    (   private(Obj, Options)
  848    ->  Class = privdef             % private definition
  849    ;   multifile(Obj, Options)
  850    ->  (   option(scope(file), Options)
  851	->  (   more_doc(Obj, Pos)
  852	    ->  Class = multidef(object(Obj))
  853	    ;   Class = multidef
  854	    )
  855	;   Class = multidef(file((Pos)))
  856	)
  857    ;   public(Obj, Options)
  858    ->  Class = publicdef           % :- public definition
  859    ;   Class = pubdef              % exported definition
  860    ),
  861    (   Obj = Module:_
  862    ->  POptions = [module(Module)|Options]
  863    ;   POptions = Options
  864    ),
  865    Pos = File:Line,
  866    DTOptions = [file(File),line(Line)|POptions],
  867    DOM = [\pred_dt(Modes, Class, DTOptions), dd(class=defbody, DOM1)],
  868    wiki_lines_to_dom(Lines1, Args, DOM0),
  869    strip_leading_par(DOM0, DOM1).
  870
  871more_doc(Obj, File:_) :-
  872    doc_comment(Obj, File2:_, _, _),
  873    File2 \== File,
  874    !.
 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.
  883need_mode(Mode, Stack, Stack) -->
  884    { Stack = [Mode|_] },
  885    !,
  886    [].
  887need_mode(Mode, Stack, Rest) -->
  888    { memberchk(Mode, Stack)
  889    },
  890    !,
  891    pop_mode(Mode, Stack, Rest).
  892need_mode(Mode, Stack, [Mode|Stack]) -->
  893    !,
  894    html_begin(Mode).
  895
  896pop_mode(Mode, Stack, Stack) -->
  897    { Stack = [Mode|_] },
  898    !,
  899    [].
  900pop_mode(Mode, [H|Rest0], Rest) -->
  901    html_end(H),
  902    pop_mode(Mode, Rest0, Rest).
 undocumented(+File, +Objects, +Options)// is det
Describe undocumented predicates if the file is a module file.
  908undocumented(File, Objs, Options) -->
  909    { option(module(Module), Options),
  910      option(public(Exports), Options),
  911      select_undocumented(Exports, Module, Objs, Undoc),
  912      re_exported_doc(Undoc, File, Module, UREObjs, ReallyUnDoc),
  913      sort(2, @=<, UREObjs, REObjs) % UREObjs = doc(PI,Pos,Comment)
  914                                    % i.e., sort on Pos
  915    },
  916    !,
  917    re_exported_doc(REObjs, Options),
  918    undocumented(ReallyUnDoc, Options).
  919undocumented(_, _, _) -->
  920    [].
  921
  922re_exported_doc([], _) --> !.
  923re_exported_doc(Objs, Options) -->
  924    reexport_header(Objs, Options),
  925    objects(Objs, Options).
  926
  927reexport_header(_, Options) -->
  928    { option(reexport_header(true), Options, true)
  929    },
  930    !,
  931    html([ h2(class(wiki), 'Re-exported predicates'),
  932	   p([ "The following predicates are exported from this file \c
  933                while their implementation is defined in imported modules \c
  934                or non-module files loaded by this module."
  935	     ])
  936	 ]).
  937reexport_header(_, _) -->
  938    [].
  939
  940undocumented([], _) --> !.
  941undocumented(UnDoc, Options) -->
  942    html([ h2(class(undoc), 'Undocumented predicates'),
  943	   p(['The following predicates are exported, but not ',
  944	      'or incorrectly documented.'
  945	     ]),
  946	   dl(class(undoc),
  947	      \undocumented_predicates(UnDoc, Options))
  948	 ]).
  949
  950
  951undocumented_predicates([], _) -->
  952    [].
  953undocumented_predicates([H|T], Options) -->
  954    undocumented_pred(H, Options),
  955    undocumented_predicates(T, Options).
  956
  957undocumented_pred(Name/Arity, Options) -->
  958    { functor(Head, Name, Arity) },
  959    html(dt(class=undoc, \pred_mode(Head, [], _, Options))).
  960
  961select_undocumented([], _, _, []).
  962select_undocumented([PI|T0], M, Objs, [PI|T]) :-
  963    is_pi(PI),
  964    \+ in_doc(M:PI, Objs),
  965    !,
  966    select_undocumented(T0, M, Objs, T).
  967select_undocumented([_|T0], M, Objs, T) :-
  968    select_undocumented(T0, M, Objs, T).
  969
  970in_doc(PI, Objs) :-
  971    member(doc(O,_,_), Objs),
  972    (   is_list(O)
  973    ->  member(O2, O),
  974	eq_pi(PI, O2)
  975    ;   eq_pi(PI, O)
  976    ).
 eq_pi(PI1, PI2) is semidet
True if PI1 and PI2 refer to the same predicate.
  983eq_pi(PI, PI) :- !.
  984eq_pi(M:PI1, M:PI2) :-
  985    atom(M),
  986    !,
  987    eq_pi(PI1, PI2).
  988eq_pi(Name/A, Name//DCGA) :-
  989    A =:= DCGA+2,
  990    !.
  991eq_pi(Name//DCGA, Name/A) :-
  992    A =:= DCGA+2.
 is_pi(@Term) is semidet
True if Term is a predicate indicator.
  998is_pi(Var) :-
  999    var(Var),
 1000    !,
 1001    fail.
 1002is_pi(_:PI) :-
 1003    !,
 1004    is_pi(PI).
 1005is_pi(_/_).
 1006is_pi(_//_).
 re_exported_doc(+Undoc:list(pi), +File:atom, +Module:atom, -ImportedDoc, -ReallyUnDoc:list(pi))
 1012re_exported_doc([], _, _, [], []).
 1013re_exported_doc([PI|T0], File, Module, [doc(Orig:PI,Pos,Comment)|ObjT], UnDoc) :-
 1014    pi_to_head(PI, Head),
 1015    (   predicate_property(Module:Head, imported_from(Orig))
 1016    ->  true
 1017    ;   predicate_property(Module:Head, exported)
 1018    ->  Orig = Module
 1019    ;   xref_defined(File, Head, imported(File2)),
 1020	ensure_doc_objects(File2),
 1021	xref_module(File2, Orig)
 1022    ),
 1023    doc_comment(Orig:PI, Pos, _, Comment),
 1024    !,
 1025    re_exported_doc(T0, File, Module, ObjT, UnDoc).
 1026re_exported_doc([PI|T0], File, Module, REObj, [PI|UnDoc]) :-
 1027    re_exported_doc(T0, File, Module, REObj, UnDoc).
 1028
 1029
 1030		 /*******************************
 1031		 *      SINGLE OBJECT PAGE      *
 1032		 *******************************/
 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.
 1042object_page(Obj, Options) -->
 1043    prolog:doc_object_page(Obj, Options),
 1044    !,
 1045    object_page_footer(Obj, Options).
 1046object_page(Obj, Options) -->
 1047    { doc_comment(Obj, File:_Line, _Summary, _Comment)
 1048    },
 1049    !,
 1050    (   { \+ ( doc_comment(Obj, File2:_, _, _),
 1051	       File2 \== File )
 1052	}
 1053    ->  html([ \html_requires(pldoc),
 1054	       \object_page_header(File, Options),
 1055	       \object_synopsis(Obj, []),
 1056	       \objects([Obj], Options)
 1057	     ])
 1058    ;   html([ \html_requires(pldoc),
 1059	       \object_page_header(-, Options),
 1060	       \objects([Obj], [synopsis(true)|Options])
 1061	     ])
 1062    ),
 1063    object_page_footer(Obj, Options).
 1064object_page(M:Name/Arity, Options) -->          % specified module, but public
 1065    { functor(Head, Name, Arity),
 1066      (   predicate_property(M:Head, exported)
 1067      ->  module_property(M, class(library))
 1068      ;   \+ predicate_property(M:Head, defined)
 1069      )
 1070    },
 1071    prolog:doc_object_page(Name/Arity, Options),
 1072    !,
 1073    object_page_footer(Name/Arity, Options).
 1074
 1075object_page_header(File, Options) -->
 1076    prolog:doc_page_header(file(File), Options),
 1077    !.
 1078object_page_header(File, Options) -->
 1079    { option(header(true), Options, true) },
 1080    !,
 1081    html(div(class(navhdr),
 1082	     [ div(class(jump), \file_link(File)),
 1083	       div(class(search), \search_form(Options)),
 1084	       br(clear(right))
 1085	     ])).
 1086object_page_header(_, _) --> [].
 1087
 1088file_link(-) -->
 1089    !,
 1090    places_menu(-).
 1091file_link(File) -->
 1092    { file_directory_name(File, Dir)
 1093    },
 1094    places_menu(Dir),
 1095    html([ div(a(href(location_by_id(pldoc_doc)+File), File))
 1096	 ]).
 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.
 1106object_footer(Obj, Options) -->
 1107    object_changelog(Obj, Options),
 1108    (   prolog:doc_object_footer(Obj, Options)
 1109    ->  []
 1110    ;   []
 1111    ).
 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.
 1122:- if(exists_source(library(pldoc/doc_changes))). 1123:- use_module(library(pldoc/doc_changes),
 1124              [doc_introduced/3, doc_last_changed/4]). 1125
 1126object_changelog(Objs, Options) -->
 1127    { is_list(Objs), !,
 1128      objs_pis(Objs, PIs)
 1129    },
 1130    object_changelog_pis(PIs, Options).
 1131object_changelog(Obj, Options) -->
 1132    { objs_pis([Obj], PIs)
 1133    },
 1134    object_changelog_pis(PIs, Options).
 1135
 1136object_changelog_pis(PIs, _Options) -->
 1137    { changelog_union(PIs, Events),
 1138      Events \== []
 1139    },
 1140    !,
 1141    html(div(class('pldoc-changes'),
 1142             [ div(class('pldoc-changes-header'), "History"),
 1143               ul(\changelog_events(Events)),
 1144               div(class('pldoc-changes-footer'),
 1145                   "Disclaimer: heuristically mined from GIT")
 1146             ])).
 1147object_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.
 1158objs_pis(Objs, PIs) :-
 1159    convlist(obj_pi, Objs, PIs).
 1160
 1161obj_pi(_:O, PI) :- !, obj_pi(O, PI).
 1162obj_pi(Name/Arity,  Name/Arity)  :- atom(Name), integer(Arity).
 1163obj_pi(Name//Arity, Name/PArity) :-
 1164    atom(Name), integer(Arity),
 1165    PArity is Arity + 2.
 1166
 1167changelog_events(Events) -->
 1168    sequence(changelog_event, Events).
 1169
 1170changelog_event(Event) -->
 1171    html(li(\changelog_event_(Event))).
 1172
 1173changelog_event_(event(Type, Version, Hash, Subject, Repo)) -->
 1174    { clean_subject(Subject, Subject1)
 1175    },
 1176    html([ \event_type(Type), " in ",
 1177           \version_commit_link(Version, Hash, Repo),
 1178           " ", em(Subject1)
 1179         ]).
 1180
 1181clean_subject(Subject0, Subject) :-
 1182    string_codes(Subject0, Codes),
 1183    phrase((tag, string(Msg)), Codes),
 1184    !,
 1185    string_codes(Subject, Msg).
 1186clean_subject(Subject, Subject).
 1187
 1188tag -->
 1189    capitals, ":", whites.
 1190
 1191capitals -->
 1192    [C], { between(0'A, 0'Z, C) }, !,
 1193    capitals.
 1194capitals -->
 1195    [].
 1196
 1197event_type(introduced) ==> html(b(title("The predicate was added"),
 1198                                  "Introduced")).
 1199event_type(added)      ==> html(b(title("New functinality was added"),
 1200                                  "Extended")).
 1201event_type(enhanced)   ==> html(b(title("Implementation has been improved"),
 1202                                  "Enhanced")).
 1203event_type(fixed)      ==> html(b(title("A bug was fixed"),
 1204                                  "Fixed")).
 1205event_type(modified)   ==> html(b(title("The modification may break compatibility"),
 1206                                  "Modified")).
 1207event_type(Id)         ==> html(b(title("Unknow tag"),
 1208                                  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.
 1218changelog_union(PIs, [Event]) :-
 1219    all_introduced(PIs, Event).
 1220changelog_union(PIs, Events) :-
 1221    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).
 1231all_introduced(PIs, Event) :-
 1232    maplist(any_introduction, PIs, Events),
 1233    E0 = event(introduced, V0, _, _, _),
 1234    aggregate_all(max(V0, E0),
 1235                  member(E0, Events),
 1236                  max(_, Event)).
 1237
 1238any_introduction(PI, Event) :-
 1239    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.
 1246union_last_changed(PIs, Events) :-
 1247    Ev = event(T0, V0, _H0, _S0, _R0),
 1248    findall(Ev,
 1249            ( member(PI, PIs),
 1250              doc_last_changed(PI, V0, T0, Ev)
 1251            ),
 1252            Events0),
 1253    sort(2, >=, Events0, Events).
 1254
 1255version_commit_link(V, Hash, Repo) -->
 1256    { version_string(V, VStr),
 1257      short_hash(Hash, Short),
 1258      commit_url(Repo, Hash, URL)
 1259    },
 1260    html([ b(VStr), ' (👉',
 1261           a([href(URL), target('_blank')], Short),
 1262           ')'
 1263         ]).
 1264
 1265version_string(N, VStr) :-
 1266    Major is N // 10000,
 1267    Minor is (N // 100) mod 100,
 1268    Patch is N mod 100,
 1269    format(string(VStr), "~d.~d.~d", [Major, Minor, Patch]).
 1270
 1271short_hash(Hash, Short) :-
 1272    string(Hash),
 1273    !,
 1274    sub_string(Hash, 0, 7, _, Short).
 1275short_hash(Hash, Short) :-
 1276    sub_atom(Hash, 0, 7, _, Short),
 1277    !.
 1278short_hash(Hash, Hash).
 1279
 1280commit_url(Repo, Hash, URL) :-
 1281    atom(Repo), Repo \== unknown,
 1282    !,
 1283    format(atom(URL), "https://github.com/~w/commit/~w", [Repo, Hash]).
 1284commit_url(_, Hash, URL) :-
 1285    format(atom(URL), "#~w", [Hash]).
 1286:- else.	% library(pldoc/doc_changes) is missing
 1287object_changelog(_, _) --> [].
 1288:- 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.
 1296object_page_footer(Obj, Options) -->
 1297    prolog:doc_object_page_footer(Obj, Options),
 1298    !.
 1299object_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.
 1313object_synopsis(Name/Arity, _) -->
 1314    { functor(Head, Name, Arity),
 1315      predicate_property(system:Head, built_in)
 1316    },
 1317    synopsis([span(class(builtin), 'built-in')]).
 1318object_synopsis(Name/Arity, Options) -->
 1319    !,
 1320    object_synopsis(_:Name/Arity, Options).
 1321object_synopsis(M:Name/Arity, Options) -->
 1322    { functor(Head, Name, Arity),
 1323      (   option(source(Spec), Options)
 1324      ->  absolute_file_name(Spec, File,
 1325			     [ access(read),
 1326			       file_type(prolog),
 1327			       file_errors(fail)
 1328			     ])
 1329      ;   predicate_property(M:Head, exported),
 1330	  \+ predicate_property(M:Head, imported_from(_)),
 1331	  module_property(M, file(File)),
 1332	  file_name_on_path(File, Spec)
 1333      ),
 1334      !,
 1335      unquote_filespec(Spec, Unquoted0),
 1336      (   prolog:pldoc_synopsis_spec(Unquoted0, Unquoted)
 1337      ->  true
 1338      ;   Unquoted = Unquoted0
 1339      )
 1340    },
 1341    use_module_synopsis(Head, File, Unquoted, Options).
 1342object_synopsis(Name//Arity, Options) -->
 1343    !,
 1344    { DCGArity is Arity+2 },
 1345    object_synopsis(Name/DCGArity, Options).
 1346object_synopsis(Module:Name//Arity, Options) -->
 1347    !,
 1348    { DCGArity is Arity+2 },
 1349    object_synopsis(Module:Name/DCGArity, Options).
 1350object_synopsis(f(_/_), _) -->
 1351    synopsis(span(class(function),
 1352		  [ 'Arithmetic function (see ',
 1353		    \object_ref(is/2, []),
 1354		    ')'
 1355		  ])).
 1356object_synopsis(c(Func), _) -->
 1357    {   sub_atom(Func, 0, _, _, 'PL_')
 1358    ;   sub_atom(Func, 0, _, _, 'S')
 1359    },
 1360    !,
 1361    synopsis([span(class(cfunc), 'C-language interface function')]).
 1362object_synopsis(_, _) --> [].
 1363
 1364:- html_meta(synopsis(html,?,?)). 1365
 1366use_module_synopsis(Head, File, Unquoted, Options) -->
 1367    { Args = [class(copy), title('Click to copy')] },
 1368    (   { option(href(HREF), Options) }
 1369    ->  synopsis([ code(Args, [':- use_module(',a(href(HREF), '~q'-[Unquoted]),').'])
 1370                 | \can_autoload(Head, File)
 1371                 ])
 1372    ;   synopsis([ code(Args, ':- use_module(~q).'-[Unquoted])
 1373                 | \can_autoload(Head, File)
 1374                 ])
 1375    ).
 1376
 1377synopsis(HTML) -->
 1378    html(div(class(synopsis),
 1379	     [ span(class('synopsis-hdr'), 'Availability:')
 1380	     | HTML
 1381	     ])).
 1382
 1383can_autoload(Head, File) -->
 1384    { predicate_property(Head, autoload(FileBase)),
 1385      file_name_extension(FileBase, _Ext, File)
 1386    },
 1387    !,
 1388    html(span(class(autoload), \can_be_autoloaded)).
 1389can_autoload(_, _) -->
 1390    [].
 1391
 1392can_be_autoloaded -->
 1393    { catch(http_link_to_id(pldoc_man, [section(autoload)], HREF),
 1394            error(_,_), fail)
 1395    },
 1396    html(['(can be ', a(href(HREF), autoloaded), ')']).
 1397can_be_autoloaded -->
 1398    html('(can be autoloaded)').
 unquote_filespec(+Spec, -Unquoted) is det
Translate e.g. library('semweb/rdf_db') into library(semweb/rdf_db).
 1406unquote_filespec(Spec, Unquoted) :-
 1407    compound(Spec),
 1408    Spec =.. [Alias,Path],
 1409    atom(Path),
 1410    atomic_list_concat(Parts, /, Path),
 1411    maplist(need_no_quotes, Parts),
 1412    !,
 1413    parts_to_path(Parts, UnquotedPath),
 1414    Unquoted =.. [Alias, UnquotedPath].
 1415unquote_filespec(Spec, Spec).
 1416
 1417need_no_quotes(Atom) :-
 1418    format(atom(A), '~q', [Atom]),
 1419    \+ sub_atom(A, 0, _, _, '\'').
 1420
 1421parts_to_path([One], One) :- !.
 1422parts_to_path(List, More/T) :-
 1423    (   append(H, [T], List)
 1424    ->  parts_to_path(H, More)
 1425    ).
 1426
 1427
 1428		 /*******************************
 1429		 *             PRINT            *
 1430		 *******************************/
 doc_write_html(+Out:stream, +Title:atomic, +DOM) is det
Write HTML for the documentation page DOM using Title to Out.
 1436doc_write_html(Out, Title, Doc) :-
 1437    doc_page_dom(Title, Doc, DOM),
 1438    phrase(html(DOM), Tokens),
 1439    print_html_head(Out),
 1440    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.
 1447doc_page_dom(Title, Body, DOM) :-
 1448    DOM = html([ head([ title(Title),
 1449			link([ rel(stylesheet),
 1450			       type('text/css'),
 1451			       href(location_by_id(pldoc_resource)+'pldoc.css')
 1452			     ]),
 1453			script([ src(location_by_id(pldoc_resource)+'pldoc.js'),
 1454				 type('text/javascript')
 1455			       ], [])
 1456		      ]),
 1457		 body(Body)
 1458	       ]).
 print_html_head(+Out:stream) is det
Print the DOCTYPE line.
 1464print_html_head(Out) :-
 1465    format(Out,
 1466	   '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" \c
 1467	       "http://www.w3.org/TR/html4/strict.dtd">~n', []).
 1468
 1469% Rendering rules
 1470%
 1471% 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.
 1479tags(Tags) -->
 1480    html(dl(class=tags, Tags)).
 tag(+Tag, +Values:list)// is det
Called from \tag(Name, Values) terms produced by doc_wiki.pl.
 1486tag(Tag, Values) -->
 1487    {   doc_tag_title(Tag, Title),
 1488	atom_concat('keyword-', Tag, Class)
 1489    },
 1490    html([ dt(class=Class, Title),
 1491	   \tag_values(Values, Class)
 1492	 ]).
 1493
 1494tag_values([], _) -->
 1495    [].
 1496tag_values([H|T], Class) -->
 1497    html(dd(class=Class, ['- '|H])),
 1498    tag_values(T, Class).
 doc_tag_title(+Tag, -Title) is det
Title is the name to use for Tag in the generated documentation.
 1505doc_tag_title(Tag, Title) :-
 1506    tag_title(Tag, Title),
 1507    !.
 1508doc_tag_title(Tag, Tag).
 1509
 1510tag_title(compat, 'Compatibility').
 1511tag_title(tbd,    'To be done').
 1512tag_title(see,    'See also').
 1513tag_title(error,  'Errors').
 1514tag_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).
 1521args(Params) -->
 1522    html([ dt(class=tag, 'Arguments:'),
 1523	   dd(table(class=arglist,
 1524		    \arg_list(Params)))
 1525	 ]).
 1526
 1527arg_list([]) -->
 1528    [].
 1529arg_list([H|T]) -->
 1530    argument(H),
 1531    arg_list(T).
 1532
 1533argument(arg(Name,Descr)) -->
 1534    html(tr([td(var(Name)), td(class=argdescr, ['- '|Descr])])).
 1535
 1536
 1537		 /*******************************
 1538		 *         NAVIGATION TREE      *
 1539		 *******************************/
 objects_nav_tree(+Objects, -Tree) is det
Provide a navigation tree showing the context of Object. Tree is of the form node(Object, Children).
 1546objects_nav_tree(Objects, Tree) :-
 1547    maplist(object_nav_tree, Objects, Trees),
 1548    union_trees(Trees, Tree0),
 1549    remove_unique_root(Tree0, Tree).
 1550
 1551object_nav_tree(Obj, Tree) :-
 1552    Node = node(directory(Dir), FileNodes),
 1553    FileNode = node(file(File), Siblings),
 1554    doc_comment(Obj, File:_Line, _Summary, _Comment),
 1555    !,
 1556    file_directory_name(File, Dir),
 1557    sibling_file_nodes(Dir, FileNodes0),
 1558    selectchk(node(file(File),[]), FileNodes0, FileNode, FileNodes),
 1559    findall(Sibling, doc_comment(Sibling, File:_, _, _), Siblings0),
 1560    delete(Siblings0, _:module(_), Siblings1),
 1561    doc_hide_private(Siblings1, Siblings2, []),
 1562    flatten(Siblings2, Siblings),   % a comment may describe objects
 1563    embed_directories(Node, Tree).
 1564
 1565sibling_file_nodes(Dir, Nodes) :-
 1566    findall(node(file(File), []),
 1567	    (   source_file(File),
 1568		file_directory_name(File, Dir)
 1569	    ),
 1570	    Nodes).
 1571
 1572embed_directories(Node, Tree) :-
 1573    Node = node(file(File), _),
 1574    !,
 1575    file_directory_name(File, Dir),
 1576    Super = node(directory(Dir), [Node]),
 1577    embed_directories(Super, Tree).
 1578embed_directories(Node, Tree) :-
 1579    Node = node(directory(Dir), _),
 1580    file_directory_name(Dir, SuperDir),
 1581    SuperDir \== Dir,
 1582    !,
 1583    Super = node(directory(SuperDir), [Node]),
 1584    embed_directories(Super, Tree).
 1585embed_directories(Tree, Tree).
 1586
 1587
 1588union_trees([Tree], Tree) :- !.
 1589union_trees([T1,T2|Trees], Tree) :-
 1590    merge_trees(T1, T2, M1),
 1591    union_trees([M1|Trees], Tree).
 1592
 1593merge_trees(node(R, Ch1), node(R, Ch2), node(R, Ch)) :-
 1594    merge_nodes(Ch1, Ch2, Ch).
 1595
 1596merge_nodes([], Ch, Ch) :- !.
 1597merge_nodes(Ch, [], Ch) :- !.
 1598merge_nodes([node(Root, Ch1)|T1], N1, [T1|Nodes]) :-
 1599    selectchk(node(Root, Ch2), N1, N2),
 1600    !,
 1601    merge_trees(node(Root, Ch1), node(Root, Ch2), T1),
 1602    merge_nodes(T1, N2, Nodes).
 1603merge_nodes([Node|T1], N1, [Node|Nodes]) :-
 1604    merge_nodes(T1, N1, Nodes).
 remove_unique_root(+TreeIn, -Tree)
Remove the root part that does not branch
 1610remove_unique_root(node(_, [node(R1, [R2])]), Tree) :-
 1611    !,
 1612    remove_unique_root(node(R1, [R2]), Tree).
 1613remove_unique_root(Tree, Tree).
 nav_tree(+Tree, +Current, +Options)// is det
Render the navigation tree
 1619nav_tree(Tree, Current, Options) -->
 1620    html(ul(class(nav),
 1621	    \object_tree(Tree, Current, Options))).
 object_tree(+Tree, +Current, +Options)// is det
Render a tree of objects used for navigation.
 1627object_tree(node(Id, []), Target, Options) -->
 1628    !,
 1629    { node_class(Id, Target, Class) },
 1630    html(li(class(Class),
 1631	    \node(Id, Options))).
 1632object_tree(node(Id, Children), Target, Options) -->
 1633    !,
 1634    { node_class(Id, Target, Class) },
 1635    html(li(class(Class),
 1636	    [ \node(Id, Options),
 1637	      ul(class(nav),
 1638		 \object_trees(Children, Target, Options))
 1639	    ])).
 1640object_tree(Id, Target, Options) -->
 1641    !,
 1642    { node_class(Id, Target, Class) },
 1643    html(li(class([obj|Class]), \node(Id, Options))).
 1644
 1645object_trees([], _, _) --> [].
 1646object_trees([H|T], Target, Options) -->
 1647    object_tree(H, Target, Options),
 1648    object_trees(T, Target, Options).
 1649
 1650node_class(Ids, Current, Class) :-
 1651    is_list(Ids),
 1652    !,
 1653    (   member(Id, Ids), memberchk(Id, Current)
 1654    ->  Class = [nav,current]
 1655    ;   Class = [nav]
 1656    ).
 1657node_class(Id, Current, Class) :-
 1658    (   memberchk(Id, Current)
 1659    ->  Class = [nav,current]
 1660    ;   Class = [nav]
 1661    ).
 1662
 1663node(file(File), Options) -->
 1664    !,
 1665    object_ref(file(File), [style(title)|Options]).
 1666node(Id, Options) -->
 1667    object_ref(Id, Options).
 1668
 1669
 1670		 /*******************************
 1671		 *            SECTIONS          *
 1672		 *******************************/
 1673
 1674section(Type, Title) -->
 1675    { string_codes(Title, Codes),
 1676      wiki_codes_to_dom(Codes, [], Content0),
 1677      strip_leading_par(Content0, Content),
 1678      make_section(Type, Content, HTML)
 1679    },
 1680    html(HTML).
 1681
 1682make_section(module,  Title, h1(class=module,  Title)).
 1683make_section(section, Title, h1(class=section, Title)).
 1684
 1685
 1686		 /*******************************
 1687		 *       PRED MODE HEADER       *
 1688		 *******************************/
 pred_dt(+Modes, +Class, Options)// is det
Emit the predicate header.
Arguments:
Modes- List as returned by process_modes/5.
 1696pred_dt(Modes, Class, Options) -->
 1697    pred_dt(Modes, Class, [], _Done, Options).
 1698
 1699pred_dt([], _, Done, Done, _) -->
 1700    [].
 1701pred_dt([H|T], Class, Done0, Done, Options) -->
 1702    { functor(Class, CSSClass, _) },
 1703    html(dt(class=CSSClass,
 1704	    [ \pred_mode(H, Done0, Done1, Options),
 1705	      \mode_anot(Class)
 1706	    ])),
 1707    pred_dt(T, Class, Done1, Done, Options).
 1708
 1709mode_anot(privdef) -->
 1710    !,
 1711    html(span([class(anot), style('float:right')],
 1712	      '[private]')).
 1713mode_anot(multidef(object(Obj))) -->
 1714    !,
 1715    { object_href(Obj, HREF) },
 1716    html(span([class(anot), style('float:right')],
 1717	      ['[', a(href(HREF), multifile), ']'
 1718	      ])).
 1719mode_anot(multidef(file(File:_))) -->
 1720    !,
 1721    { file_name_on_path(File, Spec),
 1722      unquote_filespec(Spec, Unquoted),
 1723      doc_file_href(File, HREF)
 1724    },
 1725    html(span([class(anot), style('float:right')],
 1726	      ['[multifile, ', a(href(HREF), '~q'-[Unquoted]), ']'
 1727	      ])).
 1728mode_anot(multidef) -->
 1729    !,
 1730    html(span([class(anot), style('float:right')],
 1731	      '[multifile]')).
 1732mode_anot(_) -->
 1733    [].
 1734
 1735pred_mode(mode(Head,Vars), Done0, Done, Options) -->
 1736    !,
 1737    { bind_vars(Head, Vars) },
 1738    pred_mode(Head, Done0, Done, Options).
 1739pred_mode(Head is Det, Done0, Done, Options) -->
 1740    !,
 1741    anchored_pred_head(Head, Done0, Done, Options),
 1742    pred_det(Det).
 1743pred_mode(Head, Done0, Done, Options) -->
 1744    anchored_pred_head(Head, Done0, Done, Options).
 1745
 1746bind_vars(Term, Bindings) :-
 1747    bind_vars(Bindings),
 1748    anon_vars(Term).
 1749
 1750bind_vars([]).
 1751bind_vars([Name=Var|T]) :-
 1752    Var = '$VAR'(Name),
 1753    bind_vars(T).
 anon_vars(+Term) is det
Bind remaining variables in Term to '$VAR'('_'), so they are printed as '_'.
 1760anon_vars(Var) :-
 1761    var(Var),
 1762    !,
 1763    Var = '$VAR'('_').
 1764anon_vars(Term) :-
 1765    compound(Term),
 1766    !,
 1767    Term =.. [_|Args],
 1768    maplist(anon_vars, Args).
 1769anon_vars(_).
 1770
 1771
 1772anchored_pred_head(Head, Done0, Done, Options) -->
 1773    { pred_anchor_name(Head, PI, Name) },
 1774    (   { memberchk(PI, Done0) }
 1775    ->  { Done = Done0 },
 1776	pred_head(Head)
 1777    ;   html([ span(style('float:right'),
 1778		    [ \pred_edit_or_source_button(Head, Options),
 1779		      &(nbsp)
 1780		    ]),
 1781	       a(name=Name, \pred_head(Head))
 1782	     ]),
 1783	{ Done = [PI|Done0] }
 1784    ).
 1785
 1786
 1787pred_edit_or_source_button(Head, Options) -->
 1788    { option(edit(true), Options) },
 1789    !,
 1790    pred_edit_button(Head, Options).
 1791pred_edit_or_source_button(Head, Options) -->
 1792    { option(source_link(true), Options) },
 1793    !,
 1794    pred_source_button(Head, Options).
 1795pred_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)
 1809pred_edit_button(_, Options) -->
 1810    { \+ option(edit(true), Options) },
 1811    !.
 1812pred_edit_button(PI0, Options0) -->
 1813    { canonicalise_predref(PI0, PI, Options0, Options) },
 1814    pred_edit_button2(PI, Options).
 1815
 1816pred_edit_button2(Name/Arity, Options) -->
 1817    { \+ ( memberchk(file(_), Options), % always edit if file and line
 1818	   memberchk(line(_), Options)  % are given.
 1819	 ),
 1820      functor(Head, Name, Arity),
 1821      option(module(M), Options, _),
 1822      \+ ( current_module(M),
 1823	   source_file(M:Head, _File)
 1824	 )
 1825    },
 1826    !.
 1827pred_edit_button2(Name/Arity, Options) -->
 1828    { include(edit_param, Options, Extra),
 1829      http_link_to_id(pldoc_edit,
 1830		      [name(Name),arity(Arity)|Extra],
 1831		      EditHREF)
 1832    },
 1833    html(a(onClick('HTTPrequest(\'' + EditHREF + '\')'),
 1834	   img([ class(action),
 1835		 alt('Edit predicate'),
 1836		 title('Edit predicate'),
 1837		 src(location_by_id(pldoc_resource)+'editpred.png')
 1838	       ]))).
 1839pred_edit_button2(_, _) -->
 1840    !,
 1841    [].
 1842
 1843edit_param(module(_)).
 1844edit_param(file(_)).
 1845edit_param(line(_)).
 object_edit_button(+Object, +Options)// is det
Create a button for editing Object.
 1852object_edit_button(_, Options) -->
 1853    { \+ option(edit(true), Options) },
 1854    !.
 1855object_edit_button(PI, Options) -->
 1856    { is_pi(PI) },
 1857    !,
 1858    pred_edit_button(PI, Options).
 1859object_edit_button(_, _) -->
 1860    [].
 pred_source_button(+PredIndicator, +Options)// is det
Create a button for viewing the source of a predicate.
 1867pred_source_button(PI0, Options0) -->
 1868    { canonicalise_predref(PI0, PI, Options0, Options),
 1869      option(module(M), Options, _),
 1870      pred_source_href(PI, M, HREF), !
 1871    },
 1872    html(a([ href(HREF),
 1873             class(source)
 1874	   ],
 1875	   img([ class(action),
 1876		 alt('Source'),
 1877		 title('Show source'),
 1878		 src(location_by_id(pldoc_resource)+'source.png')
 1879	       ]))).
 1880pred_source_button(_, _) -->
 1881    [].
 object_source_button(+Object, +Options)// is det
Create a button for showing the source of Object.
 1888object_source_button(PI, Options) -->
 1889    { is_pi(PI),
 1890      option(source_link(true), Options, true)
 1891    },
 1892    !,
 1893    pred_source_button(PI, Options).
 1894object_source_button(_, _) -->
 1895    [].
 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.
 1903canonicalise_predref(M:PI0, PI, Options0, [module(M)|Options]) :-
 1904    !,
 1905    canonicalise_predref(PI0, PI, Options0, Options).
 1906canonicalise_predref(//(Head), PI, Options0, Options) :-
 1907    !,
 1908    functor(Head, Name, Arity),
 1909    PredArity is Arity + 2,
 1910    canonicalise_predref(Name/PredArity, PI, Options0, Options).
 1911canonicalise_predref(Name//Arity, PI, Options0, Options) :-
 1912    integer(Arity), Arity >= 0,
 1913    !,
 1914    PredArity is Arity + 2,
 1915    canonicalise_predref(Name/PredArity, PI, Options0, Options).
 1916canonicalise_predref(PI, PI, Options, Options) :-
 1917    PI = Name/Arity,
 1918    atom(Name), integer(Arity), Arity >= 0,
 1919    !.
 1920canonicalise_predref(Head, PI, Options0, Options) :-
 1921    functor(Head, Name, Arity),
 1922    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.
 1930pred_head(Var) -->
 1931    { var(Var),
 1932      !,
 1933      instantiation_error(Var)
 1934    }.
 1935pred_head(//(Head)) -->
 1936    !,
 1937    pred_head(Head),
 1938    html(//).
 1939pred_head(M:Head) -->
 1940    html([span(class=module, M), :]),
 1941    pred_head(Head).
 1942pred_head(Head) -->
 1943    { atom(Head) },
 1944    !,
 1945    html(b(class=pred, Head)).
 1946pred_head(Head) -->                     % Infix operators
 1947    { Head =.. [Functor,Left,Right],
 1948      is_op_type(Functor, infix)
 1949    },
 1950    !,
 1951    html([ var(class=arglist, \pred_arg(Left, 1)),
 1952	   ' ', b(class=pred, Functor), ' ',
 1953	   var(class=arglist, \pred_arg(Right, 2))
 1954	 ]).
 1955pred_head(Head) -->                     % Prefix operators
 1956    { Head =.. [Functor,Arg],
 1957      is_op_type(Functor, prefix)
 1958    },
 1959    !,
 1960    html([ b(class=pred, Functor), ' ',
 1961	   var(class=arglist, \pred_arg(Arg, 1))
 1962	 ]).
 1963pred_head(Head) -->                     % Postfix operators
 1964    { Head =.. [Functor,Arg],
 1965      is_op_type(Functor, postfix)
 1966    },
 1967    !,
 1968    html([ var(class=arglist, \pred_arg(Arg, 1)),
 1969	   ' ', b(class=pred, Functor)
 1970	 ]).
 1971pred_head({Head}) -->
 1972    !,
 1973    html([ b(class=pred, '{'),
 1974	   var(class=arglist,
 1975	       \pred_args([Head], 1)),
 1976	   b(class=pred, '}')
 1977	 ]).
 1978pred_head(Head) -->                     % Plain terms
 1979    { Head =.. [Functor|Args] },
 1980    html([ b(class=pred, Functor),
 1981	   var(class=arglist,
 1982	       [ '(', \pred_args(Args, 1), ')' ])
 1983	 ]).
 is_op_type(+Atom, ?Type)
True if Atom is an operator of Type. Type is one of prefix, infix or postfix.
 1990is_op_type(Functor, Type) :-
 1991    current_op(_Pri, F, Functor),
 1992    op_type(F, Type).
 1993
 1994op_type(fx,  prefix).
 1995op_type(fy,  prefix).
 1996op_type(xf,  postfix).
 1997op_type(yf,  postfix).
 1998op_type(xfx, infix).
 1999op_type(xfy, infix).
 2000op_type(yfx, infix).
 2001op_type(yfy, infix).
 2002
 2003
 2004pred_args([], _) -->
 2005    [].
 2006pred_args([H|T], I) -->
 2007    pred_arg(H, I),
 2008    (   {T==[]}
 2009    ->  []
 2010    ;   html(', '),
 2011	{ I2 is I + 1 },
 2012	pred_args(T, I2)
 2013    ).
 2014
 2015pred_arg(Var, I) -->
 2016    { var(Var) },
 2017    !,
 2018    html(['Arg', I]).
 2019pred_arg(...(Term), I) -->
 2020    !,
 2021    pred_arg(Term, I),
 2022    html('...').
 2023pred_arg(Term, I) -->
 2024    { Term =.. [Ind,Arg],
 2025      mode_indicator(Ind)
 2026    },
 2027    !,
 2028    html([Ind, \pred_arg(Arg, I)]).
 2029pred_arg(Arg:Type, _) -->
 2030    !,
 2031    html([\argname(Arg), :, \argtype(Type)]).
 2032pred_arg(Arg, _) -->
 2033    argname(Arg).
 2034
 2035argname('$VAR'(Name)) -->
 2036    !,
 2037    html(Name).
 2038argname(Name) -->
 2039    !,
 2040    html(Name).
 2041
 2042argtype(Term) -->
 2043    { format(string(S), '~W',
 2044	     [ Term,
 2045	       [ quoted(true),
 2046		 numbervars(true)
 2047	       ]
 2048	     ]) },
 2049    html(S).
 2050
 2051pred_det(unknown) -->
 2052    [].
 2053pred_det(Det) -->
 2054    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
 2063term(_, Atom, []) -->
 2064    { atomic(Atom),
 2065      !,
 2066      format(string(S), '~W', [Atom,[quoted(true)]])
 2067    },
 2068    html(span(class=functor, S)).
 2069term(_, Key:Type, [TypeName=Type]) -->
 2070    { atomic(Key)
 2071    },
 2072    !,
 2073    html([span(class='pl-key', Key), :, span(class('pl-var'), TypeName)]).
 2074term(_, Term, Bindings) -->
 2075    { is_mode(Term is det),         % HACK. Bit too strict?
 2076      bind_vars(Bindings)
 2077    },
 2078    !,
 2079    pred_head(Term).
 2080term(_, Term, Bindings) -->
 2081    term(Term,
 2082	 [ variable_names(Bindings),
 2083	   quoued(true)
 2084	 ]).
 2085
 2086
 2087		 /*******************************
 2088		 *             PREDREF          *
 2089		 *******************************/
 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.

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