View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2006-2020, University of Amsterdam
    7                              VU University Amsterdam
    8                              CWI, Amsterdam
    9    All rights reserved.
   10
   11    Redistribution and use in source and binary forms, with or without
   12    modification, are permitted provided that the following conditions
   13    are met:
   14
   15    1. Redistributions of source code must retain the above copyright
   16       notice, this list of conditions and the following disclaimer.
   17
   18    2. Redistributions in binary form must reproduce the above copyright
   19       notice, this list of conditions and the following disclaimer in
   20       the documentation and/or other materials provided with the
   21       distribution.
   22
   23    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   24    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   25    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   26    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   27    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   28    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   29    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   30    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   31    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   33    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   34    POSSIBILITY OF SUCH DAMAGE.
   35*/
   36
   37:- module(pldoc_html,
   38          [ doc_for_file/2,             % +FileSpec, +Options
   39            doc_write_html/3,           % +Stream, +Title, +Term
   40            doc_for_wiki_file/2,        % +FileSpec, +Options
   41                                        % Support doc_index
   42            doc_page_dom/3,             % +Title, +Body, -DOM
   43            print_html_head/1,          % +Stream
   44            predref//1,                 % +PI //
   45            predref//2,                 % +PI, Options //
   46            module_info/3,              % +File, +Options0, -Options
   47            doc_hide_private/3,         % +Doc0, -Doc, +Options
   48            edit_button//2,             % +File, +Options, //
   49            source_button//2,           % +File, +Options, //
   50            zoom_button//2,             % +File, +Options, //
   51            pred_edit_button//2,        % +PredInd, +Options, //
   52            object_edit_button//2,      % +Obj, +Options, //
   53            object_source_button//2,    % +Obj, +Options, //
   54            doc_resources//1,           % +Options
   55            ensure_doc_objects/1,       % +File
   56                                        % Support other backends
   57            doc_file_objects/5,         % +FSpec, -File, -Objs, -FileOpts, +Opts
   58            existing_linked_file/2,     % +FileSpec, -Path
   59            unquote_filespec/2,         % +FileSpec, -Unquoted
   60            doc_tag_title/2,            % +Tag, -Title
   61            mode_anchor_name/2,         % +Mode, -Anchor
   62            pred_anchor_name/3,         % +Head, -PI, -Anchor
   63            private/2,                  % +Obj, +Options
   64            (multifile)/2,              % +Obj, +Options
   65            is_pi/1,                    % @Term
   66            is_op_type/2,               % +Atom, ?Type
   67                                        % Output routines
   68            file//1,                    % +File, //
   69            file//2,                    % +File, +Options, //
   70            include//3,                 % +File, +Type, +Options //
   71            tags//1,                    % +Tags, //
   72            term//3,                    % +Text, +Term, +Bindings, //
   73            file_header//2,             % +File, +Options, //
   74            objects//2,                 % +Objects, +Options, //
   75            object_ref//2,              % +Object, +Options, //
   76            object_name//2,             % +Object, +Object
   77            object_href/2,              % +Object, -URL
   78            object_tree//3,             % +Tree, +Current, +Options
   79            object_page//2,             % +Object, +Options, //
   80            object_page_header//2,      % +File, +Options, //
   81            object_synopsis//2,         % +Object, +Options, //
   82            object_page_footer//2       % +Object, +Options, //
   83          ]).   84:- use_module(library(lists)).   85:- use_module(library(option)).   86:- use_module(library(uri)).   87:- use_module(library(readutil)).   88:- use_module(library(http/html_write)).   89:- use_module(library(http/http_dispatch)).   90:- use_module(library(http/http_wrapper)).   91:- use_module(library(http/http_path)).   92:- use_module(library(http/html_head)).   93:- use_module(library(http/term_html)).   94:- use_module(library(http/jquery)).   95:- use_module(library(debug)).   96:- use_module(library(apply)).   97:- use_module(library(pairs)).   98:- use_module(library(filesex)).   99:- use_module(doc_process).  100:- use_module(doc_man).  101:- use_module(doc_modes).  102:- use_module(doc_wiki).  103:- use_module(doc_search).  104:- use_module(doc_index).  105:- use_module(doc_util).  106:- use_module(library(solution_sequences)).  107:- use_module(library(error)).  108:- use_module(library(occurs)).  109:- use_module(library(prolog_source)).  110:- use_module(library(prolog_xref)).  111
  112:- 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. */
  124:- public
  125    args//1,                        % Called from \Term output created
  126    pred_dt//3,                     % by the wiki renderer
  127    section//2,
  128    tag//2.  129
  130
  131:- predicate_options(doc_for_wiki_file/2, 2,
  132                     [ edit(boolean)
  133                     ]).  134:- predicate_options(doc_hide_private/3, 3,
  135                     [module(atom), public(list), public_only(boolean)]).  136:- predicate_options(edit_button//2, 2,
  137                     [ edit(boolean)
  138                     ]).  139:- predicate_options(file//2, 2,
  140                     [ label(any),
  141                       absolute_path(atom),
  142                       href(atom),
  143                       map_extension(list),
  144                       files(list),
  145                       edit_handler(atom)
  146                     ]).  147:- predicate_options(file_header//2, 2,
  148                     [ edit(boolean),
  149                       files(list),
  150                       public_only(boolean)
  151                     ]).  152:- predicate_options(include//3, 3,
  153                     [ absolute_path(atom),
  154                       class(atom),
  155                       files(list),
  156                       href(atom),
  157                       label(any),
  158                       map_extension(list)
  159                     ]).  160:- predicate_options(object_edit_button//2, 2,
  161                     [ edit(boolean),
  162                       pass_to(pred_edit_button//2, 2)
  163                     ]).  164:- predicate_options(object_page//2, 2,
  165                     [ for(any),
  166                       header(boolean),
  167                       links(boolean),
  168                       no_manual(boolean),
  169                       try_manual(boolean),
  170                       search_in(oneof([all,app,man])),
  171                       search_match(oneof([name,summary])),
  172                       search_options(boolean)
  173                     ]).  174:- predicate_options(object_ref//2, 2,
  175                     [ files(list),
  176                       qualify(boolean),
  177                       style(oneof([number,title,number_title])),
  178                       secref_style(oneof([number,title,number_title]))
  179                     ]).  180:- predicate_options(object_synopsis//2, 2,
  181                     [ href(atom)
  182                     ]).  183:- predicate_options(pred_dt//3, 3,
  184                     [ edit(boolean)
  185                     ]).  186:- predicate_options(pred_edit_button//2, 2,
  187                     [ edit(boolean)
  188                     ]).  189:- predicate_options(predref//2, 2,
  190                     [ files(list),
  191                       prefer(oneof([manual,app])),
  192                       pass_to(object_ref/4, 2)
  193                     ]).  194:- predicate_options(private/2, 2,
  195                     [ module(atom),
  196                       public(list)
  197                     ]).  198:- predicate_options(source_button//2, 2,
  199                     [ files(list)
  200                     ]).  201
  202
  203                 /*******************************
  204                 *           RESOURCES          *
  205                 *******************************/
  206
  207:- html_resource(pldoc_css,
  208                 [ virtual(true),
  209                   requires([ pldoc_resource('pldoc.css')
  210                            ])
  211                 ]).  212:- html_resource(pldoc_resource('pldoc.js'),
  213                 [ requires([ jquery
  214                            ])
  215                 ]).  216:- html_resource(pldoc_js,
  217                 [ virtual(true),
  218                   requires([ pldoc_resource('pldoc.js')
  219                            ])
  220                 ]).  221:- html_resource(pldoc,
  222                 [ virtual(true),
  223                   requires([ pldoc_css,
  224                              pldoc_js
  225                            ])
  226                 ]).  227
  228
  229                 /*******************************
  230                 *       FILE PROCESSING        *
  231                 *******************************/
 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.
  252doc_for_file(FileSpec, Options) :-
  253    doc_file_objects(FileSpec, File, Objects, FileOptions, Options),
  254    doc_file_title(File, Title, FileOptions, Options),
  255    doc_write_page(
  256        pldoc(file(File, Title)),
  257        title(Title),
  258        \prolog_file(File, Objects, FileOptions, Options),
  259        Options).
  260
  261doc_file_title(_, Title, _, Options) :-
  262    option(title(Title), Options),
  263    !.
  264doc_file_title(File, Title, FileOptions, _) :-
  265    memberchk(file(Title0, _Comment), FileOptions),
  266    !,
  267    file_base_name(File, Base),
  268    atomic_list_concat([Base, ' -- ', Title0], Title).
  269doc_file_title(File, Title, _, _) :-
  270    file_base_name(File, Title).
  271
  272:- html_meta doc_write_page(+, html, html, +).  273
  274doc_write_page(Style, Head, Body, Options) :-
  275    option(files(_), Options),
  276    !,
  277    phrase(page(Style, Head, Body), HTML),
  278    print_html(HTML).
  279doc_write_page(Style, Head, Body, _) :-
  280    reply_html_page(Style, Head, Body).
  281
  282
  283prolog_file(File, Objects, FileOptions, Options) -->
  284    { b_setval(pldoc_file, File),   % TBD: delete?
  285      file_directory_name(File, Dir)
  286    },
  287    html([ \doc_resources(Options),
  288           \doc_links(Dir, FileOptions),
  289           \file_header(File, FileOptions)
  290         | \objects(Objects, FileOptions)
  291         ]),
  292    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 ...
  299doc_resources(Options) -->
  300    { option(resource_directory(ResDir), Options),
  301      nb_current(pldoc_output, OutputFile),
  302      !,
  303      directory_file_path(ResDir, 'pldoc.css', Res),
  304      relative_file_name(Res, OutputFile, Ref)
  305    },
  306    html_requires(Ref).
  307doc_resources(Options) -->
  308    { option(html_resources(Resoures), Options, pldoc)
  309    },
  310    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
  339doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  340    xref_current_source(FileSpec),
  341    xref_option(FileSpec, comments(collect)),
  342    !,
  343    File = FileSpec,
  344    findall(Object, xref_doc_object(File, Object), Objects0),
  345    reply_file_objects(File, Objects0, Objects, FileOptions, Options).
  346doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  347    absolute_file_name(FileSpec, File,
  348                       [ file_type(prolog),
  349                         access(read)
  350                       ]),
  351    source_file(File),
  352    !,
  353    ensure_doc_objects(File),
  354    Pos = File:Line,
  355    findall(Line-doc(Obj,Pos,Comment),
  356            doc_comment(Obj, Pos, _, Comment), Pairs),
  357    sort(Pairs, Pairs1),            % remove duplicates
  358    keysort(Pairs1, ByLine),
  359    pairs_values(ByLine, Objs0),
  360    reply_file_objects(File, Objs0, Objects, FileOptions, Options).
  361doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  362    absolute_file_name(FileSpec, File,
  363                       [ file_type(prolog),
  364                         access(read)
  365                       ]),
  366    xref_source(File, [silent(true)]),
  367    findall(Object, xref_doc_object(File, Object), Objects0),
  368    reply_file_objects(File, Objects0, Objects, FileOptions, Options).
  369
  370
  371reply_file_objects(File, Objs0, Objects, FileOptions, Options) :-
  372    module_info(File, ModuleOptions, Options),
  373    file_info(Objs0, Objs1, FileOptions, ModuleOptions),
  374    doc_hide_private(Objs1, ObjectsSelf, ModuleOptions),
  375    include_reexported(ObjectsSelf, Objects1, File, FileOptions),
  376    remove_doc_duplicates(Objects1, Objects, []).
  377
  378remove_doc_duplicates([], [], _).
  379remove_doc_duplicates([H|T0], [H|T], Seen) :-
  380    H = doc(_, _, Comment),
  381    \+ memberchk(Comment, Seen),
  382    !,
  383    remove_doc_duplicates(T0, T, [Comment|Seen]).
  384remove_doc_duplicates([_|T0], T, Seen) :-
  385    remove_doc_duplicates(T0, T, Seen).
  386
  387include_reexported(SelfObjects, Objects, File, Options) :-
  388    option(include_reexported(true), Options),
  389    option(module(Module), Options),
  390    option(public(Exports), Options),
  391    select_undocumented(Exports, Module, SelfObjects, Undoc),
  392    re_exported_doc(Undoc, File, Module, REObjs, _),
  393    REObjs \== [],
  394    !,
  395    append(SelfObjects, REObjs, Objects).
  396include_reexported(Objects, Objects, _, _).
 xref_doc_object(File, DocObject) is nondet
  401xref_doc_object(File, doc(M:module(Title),File:0,Comment)) :-
  402    xref_comment(File, Title, Comment),
  403    xref_module(File, M).
  404xref_doc_object(File, doc(M:Name/Arity,File:0,Comment)) :-
  405    xref_comment(File, Head, _Summary, Comment),
  406    xref_module(File, Module),
  407    strip_module(Module:Head, M, Plain),
  408    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.
  419:- dynamic
  420    no_comments/2.  421
  422ensure_doc_objects(File) :-
  423    source_file(File),
  424    !,
  425    (   doc_file_has_comments(File)
  426    ->  true
  427    ;   no_comments(File, TimeChecked),
  428        time_file(File, TimeChecked)
  429    ->  true
  430    ;   xref_source(File, [silent(true), comments(store)]),
  431        retractall(no_comments(File, _)),
  432        (   doc_file_has_comments(File)
  433        ->  true
  434        ;   time_file(File, TimeChecked),
  435            assertz(no_comments(File, TimeChecked))
  436        )
  437    ).
  438ensure_doc_objects(File) :-
  439    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.
  446module_info(File, [module(Module), public(Exports)|Options], Options) :-
  447    module_property(Module, file(File)),
  448    !,
  449    module_property(Module, exports(Exports)).
  450module_info(File, [module(Module), public(Exports)|Options], Options) :-
  451    xref_module(File, Module),
  452    !,
  453    findall(PI, xref_exported_pi(File, PI), Exports).
  454module_info(_, Options, Options).
  455
  456xref_exported_pi(Src, Name/Arity) :-
  457    xref_exported(Src, Head),
  458    functor(Head, Name, Arity).
 doc_hide_private(+Objs, +Public, +Options)
Remove the private objects from Objs according to Options.
  464doc_hide_private(Objs, Objs, Options) :-
  465    option(public_only(false), Options, true),
  466    !.
  467doc_hide_private(Objs0, Objs, Options) :-
  468    hide_private(Objs0, Objs, Options).
  469
  470hide_private([], [], _).
  471hide_private([H|T0], T, Options) :-
  472    obj(H, Obj),
  473    private(Obj, Options),
  474    !,
  475    hide_private(T0, T, Options).
  476hide_private([H|T0], [H|T], Options) :-
  477    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.
  485obj(doc(Obj0, _Pos, _Summary), Obj) :-
  486    !,
  487    (   Obj0 = [Obj|_]
  488    ->  true
  489    ;   Obj = Obj0
  490    ).
  491obj(Obj0, Obj) :-
  492    (   Obj0 = [Obj|_]
  493    ->  true
  494    ;   Obj = Obj0
  495    ).
 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.
  504:- multifile
  505    prolog:doc_is_public_object/1.  506
  507private(Object, _Options):-
  508    prolog:doc_is_public_object(Object), !, fail.
  509private(Module:PI, Options) :-
  510    multifile(Module:PI, Options), !, fail.
  511private(Module:PI, Options) :-
  512    option(module(Module), Options),
  513    option(public(Public), Options),
  514    !,
  515    \+ ( member(PI2, Public),
  516         eq_pi(PI, PI2)
  517       ).
  518private(Module:PI, _Options) :-
  519    module_property(Module, file(_)),      % A loaded module
  520    !,
  521    module_property(Module, exports(Exports)),
  522    \+ ( member(PI2, Exports),
  523         eq_pi(PI, PI2)
  524       ).
  525private(Module:PI, _Options) :-
  526    \+ (pi_to_head(PI, Head),
  527        xref_exported(Source, Head),
  528        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.
  539multifile(Obj, _Options) :-
  540    strip_module(user:Obj, Module, PI),
  541    pi_to_head(PI, Head),
  542    (   predicate_property(Module:Head, multifile)
  543    ;   xref_module(Source, Module),
  544        xref_defined(Source, Head, multifile(_))
  545    ),
  546    !.
  547
  548pi_to_head(Var, _) :-
  549    var(Var), !, fail.
  550pi_to_head(Name/Arity, Term) :-
  551    functor(Term, Name, Arity).
  552pi_to_head(Name//DCGArity, Term) :-
  553    Arity is DCGArity+2,
  554    functor(Term, Name, Arity).
 file_info(+Comments, -RestComment, -FileOptions, +OtherOptions) is det
Add options file(Title, Comment) to OtherOptions if available.
  560file_info(Comments, RestComments, [file(Title, Comment)|Opts], Opts) :-
  561    select(doc(_:module(Title),_,Comment), Comments, RestComments),
  562    !.
  563file_info(Comments, Comments, Opts, Opts).
 file_header(+File, +Options)// is det
Create the file header.
  570file_header(File, Options) -->
  571    { memberchk(file(Title, Comment), Options),
  572      !,
  573      file_base_name(File, Base)
  574    },
  575    file_title([Base, ' -- ', Title], File, Options),
  576    { is_structured_comment(Comment, Prefixes),
  577      string_codes(Comment, Codes),
  578      indented_lines(Codes, Prefixes, Lines),
  579      section_comment_header(Lines, _Header, Lines1),
  580      wiki_lines_to_dom(Lines1, [], DOM)
  581    },
  582    html(DOM).
  583file_header(File, Options) -->
  584    { file_base_name(File, Base)
  585    },
  586    file_title([Base], File, Options).
 file_title(+Title:list, +File, +Options)// is det
Emit the file-header and manipulation buttons.
  593file_title(Title, File, Options) -->
  594    prolog:doc_file_title(Title, File, Options),
  595    !.
  596file_title(Title, File, Options) -->
  597    { file_base_name(File, Base)
  598    },
  599    html(h1(class(file),
  600            [ span(style('float:right'),
  601                   [ \reload_button(File, Base, Options),
  602                     \zoom_button(Base, Options),
  603                     \source_button(Base, Options),
  604                     \edit_button(File, Options)
  605                   ])
  606            | Title
  607            ])).
 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.
  617reload_button(File, _Base, Options) -->
  618    { \+ source_file(File),
  619      \+ option(files(_), Options)
  620    },
  621    !,
  622    html(span(class(file_anot), '[not loaded]')).
  623reload_button(_File, Base, Options) -->
  624    { option(edit(true), Options),
  625      !,
  626      option(public_only(Public), Options, true)
  627    },
  628    html(a(href(Base+[reload(true), public_only(Public)]),
  629           img([ class(action),
  630                 alt('Reload'),
  631                 title('Make & Reload'),
  632                 src(location_by_id(pldoc_resource)+'reload.png')
  633               ]))).
  634reload_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.
  642edit_button(File, Options) -->
  643    { option(edit(true), Options)
  644    },
  645    !,
  646    html(a([ onClick('HTTPrequest(\'' +
  647                     location_by_id(pldoc_edit) + [file(File)] +
  648                     '\')')
  649           ],
  650           img([ class(action),
  651                 alt(edit),
  652                 title('Edit file'),
  653                 src(location_by_id(pldoc_resource)+'edit.png')
  654             ]))).
  655edit_button(_, _) -->
  656    [].
 zoom_button(BaseName, +Options)// is det
Add zoom in/out button to show/hide the private documentation.
  663zoom_button(_, Options) -->
  664    { option(files(_Map), Options) },
  665    !.    % generating files
  666zoom_button(Base, Options) -->
  667    {   (   option(public_only(true), Options, true)
  668        ->  Zoom = 'public.png',
  669            Alt = 'Public',
  670            Title = 'Click to include private',
  671            PublicOnly = false
  672        ;   Zoom = 'private.png',
  673            Alt = 'All predicates',
  674            Title = 'Click to show exports only',
  675            PublicOnly = true
  676        )
  677    },
  678    html(a(href(Base+[public_only(PublicOnly)]),
  679           img([ class(action),
  680                 alt(Alt),
  681                 title(Title),
  682                 src(location_by_id(pldoc_resource)+Zoom)
  683               ]))).
 source_button(+File, +Options)// is det
Add show-source button.
  690source_button(_File, Options) -->
  691    { option(files(_Map), Options) },
  692    !.    % generating files
  693source_button(File, _Options) -->
  694    { (   is_absolute_file_name(File)
  695      ->  doc_file_href(File, HREF0)
  696      ;   HREF0 = File
  697      )
  698    },
  699    html(a(href(HREF0+[show(src)]),
  700           img([ class(action),
  701                 alt('Show source'),
  702                 title('Show source'),
  703                 src(location_by_id(pldoc_resource)+'source.png')
  704               ]))).
 objects(+Objects:list, +Options)// is det
Emit the documentation body. Options includes:
navtree(+Boolean)
If true, provide a navitation tree.
  714objects(Objects, Options) -->
  715    { option(navtree(true), Options),
  716      !,
  717      objects_nav_tree(Objects, Tree)
  718    },
  719    html([ div(class(navtree),
  720               div(class(navwindow),
  721                   \nav_tree(Tree, Objects, Options))),
  722           div(class(navcontent),
  723               \objects_nt(Objects, Options))
  724         ]).
  725objects(Objects, Options) -->
  726    objects_nt(Objects, Options).
  727
  728objects_nt(Objects, Options) -->
  729    objects(Objects, [body], Options).
  730
  731objects([], Mode, _) -->
  732    pop_mode(body, Mode, _).
  733objects([Obj|T], Mode, Options) -->
  734    object(Obj, Mode, Mode1, Options),
  735    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.
  746object(doc(Obj,Pos,Comment), Mode0, Mode, Options) -->
  747    !,
  748    object(Obj, [Pos-Comment], Mode0, Mode, [scope(file)|Options]).
  749object(Obj, Mode0, Mode, Options) -->
  750    { findall(Pos-Comment,
  751              doc_comment(Obj, Pos, _Summary, Comment),
  752              Pairs)
  753    },
  754    !,
  755    { b_setval(pldoc_object, Obj) },
  756    object(Obj, Pairs, Mode0, Mode, Options).
  757
  758object(Obj, Pairs, Mode0, Mode, Options) -->
  759    { is_pi(Obj),
  760      !,
  761      maplist(pred_dom(Obj, Options), Pairs, DOMS),
  762      append(DOMS, DOM)
  763    },
  764    need_mode(dl, Mode0, Mode),
  765    html(DOM).
  766object([Obj|_Same], Pairs, Mode0, Mode, Options) -->
  767    !,
  768    object(Obj, Pairs, Mode0, Mode, Options).
  769object(Obj, _Pairs, Mode, Mode, _Options) -->
  770    { debug(pldoc, 'Skipped ~p', [Obj]) },
  771    [].
  772
  773pred_dom(Obj, Options, Pos-Comment, DOM) :-
  774    is_structured_comment(Comment, Prefixes),
  775    string_codes(Comment, Codes),
  776    indented_lines(Codes, Prefixes, Lines),
  777    strip_module(user:Obj, Module, _),
  778    process_modes(Lines, Module, Pos, Modes, Args, Lines1),
  779    (   private(Obj, Options)
  780    ->  Class = privdef             % private definition
  781    ;   multifile(Obj, Options)
  782    ->  (   option(scope(file), Options)
  783        ->  (   more_doc(Obj, Pos)
  784            ->  Class = multidef(object(Obj))
  785            ;   Class = multidef
  786            )
  787        ;   Class = multidef(file((Pos)))
  788        )
  789    ;   Class = pubdef              % public definition
  790    ),
  791    (   Obj = Module:_
  792    ->  POptions = [module(Module)|Options]
  793    ;   POptions = Options
  794    ),
  795    Pos = File:Line,
  796    DTOptions = [file(File),line(Line)|POptions],
  797    DOM = [\pred_dt(Modes, Class, DTOptions), dd(class=defbody, DOM1)],
  798    wiki_lines_to_dom(Lines1, Args, DOM0),
  799    strip_leading_par(DOM0, DOM1).
  800
  801more_doc(Obj, File:_) :-
  802    doc_comment(Obj, File2:_, _, _),
  803    File2 \== File,
  804    !.
 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.
  813need_mode(Mode, Stack, Stack) -->
  814    { Stack = [Mode|_] },
  815    !,
  816    [].
  817need_mode(Mode, Stack, Rest) -->
  818    { memberchk(Mode, Stack)
  819    },
  820    !,
  821    pop_mode(Mode, Stack, Rest).
  822need_mode(Mode, Stack, [Mode|Stack]) -->
  823    !,
  824    html_begin(Mode).
  825
  826pop_mode(Mode, Stack, Stack) -->
  827    { Stack = [Mode|_] },
  828    !,
  829    [].
  830pop_mode(Mode, [H|Rest0], Rest) -->
  831    html_end(H),
  832    pop_mode(Mode, Rest0, Rest).
 undocumented(+File, +Objects, +Options)// is det
Describe undocumented predicates if the file is a module file.
  838undocumented(File, Objs, Options) -->
  839    { memberchk(module(Module), Options),
  840      memberchk(public(Exports), Options),
  841      select_undocumented(Exports, Module, Objs, Undoc),
  842      re_exported_doc(Undoc, File, Module, REObjs, ReallyUnDoc)
  843    },
  844    !,
  845    re_exported_doc(REObjs, Options),
  846    undocumented(ReallyUnDoc, Options).
  847undocumented(_, _, _) -->
  848    [].
  849
  850re_exported_doc([], _) --> !.
  851re_exported_doc(Objs, Options) -->
  852    reexport_header(Objs, Options),
  853    objects(Objs, Options).
  854
  855reexport_header(_, Options) -->
  856    { option(reexport_header(true), Options, true)
  857    },
  858    !,
  859    html([ h2(class(wiki), 'Re-exported predicates'),
  860           p([ 'The following predicates are re-exported from other ',
  861               'modules'
  862             ])
  863         ]).
  864reexport_header(_, _) -->
  865    [].
  866
  867undocumented([], _) --> !.
  868undocumented(UnDoc, Options) -->
  869    html([ h2(class(undoc), 'Undocumented predicates'),
  870           p(['The following predicates are exported, but not ',
  871              'or incorrectly documented.'
  872             ]),
  873           dl(class(undoc),
  874              \undocumented_predicates(UnDoc, Options))
  875         ]).
  876
  877
  878undocumented_predicates([], _) -->
  879    [].
  880undocumented_predicates([H|T], Options) -->
  881    undocumented_pred(H, Options),
  882    undocumented_predicates(T, Options).
  883
  884undocumented_pred(Name/Arity, Options) -->
  885    { functor(Head, Name, Arity) },
  886    html(dt(class=undoc, \pred_mode(Head, [], _, Options))).
  887
  888select_undocumented([], _, _, []).
  889select_undocumented([PI|T0], M, Objs, [PI|T]) :-
  890    is_pi(PI),
  891    \+ in_doc(M:PI, Objs),
  892    !,
  893    select_undocumented(T0, M, Objs, T).
  894select_undocumented([_|T0], M, Objs, T) :-
  895    select_undocumented(T0, M, Objs, T).
  896
  897in_doc(PI, Objs) :-
  898    member(doc(O,_,_), Objs),
  899    (   is_list(O)
  900    ->  member(O2, O),
  901        eq_pi(PI, O2)
  902    ;   eq_pi(PI, O)
  903    ).
 eq_pi(PI1, PI2) is semidet
True if PI1 and PI2 refer to the same predicate.
  910eq_pi(PI, PI) :- !.
  911eq_pi(M:PI1, M:PI2) :-
  912    atom(M),
  913    !,
  914    eq_pi(PI1, PI2).
  915eq_pi(Name/A, Name//DCGA) :-
  916    A =:= DCGA+2,
  917    !.
  918eq_pi(Name//DCGA, Name/A) :-
  919    A =:= DCGA+2.
 is_pi(@Term) is semidet
True if Term is a predicate indicator.
  925is_pi(Var) :-
  926    var(Var),
  927    !,
  928    fail.
  929is_pi(_:PI) :-
  930    !,
  931    is_pi(PI).
  932is_pi(_/_).
  933is_pi(_//_).
 re_exported_doc(+Undoc:list(pi), +File:atom, +Module:atom, -ImportedDoc, -ReallyUnDoc:list(pi))
  939re_exported_doc([], _, _, [], []).
  940re_exported_doc([PI|T0], File, Module, [doc(Orig:PI,Pos,Comment)|ObjT], UnDoc) :-
  941    pi_to_head(PI, Head),
  942    (   predicate_property(Module:Head, imported_from(Orig))
  943    ->  true
  944    ;   xref_defined(File, Head, imported(File2)),
  945        ensure_doc_objects(File2),
  946        xref_module(File2, Orig)
  947    ),
  948    doc_comment(Orig:PI, Pos, _, Comment),
  949    !,
  950    re_exported_doc(T0, File, Module, ObjT, UnDoc).
  951re_exported_doc([PI|T0], File, Module, REObj, [PI|UnDoc]) :-
  952    re_exported_doc(T0, File, Module, REObj, UnDoc).
  953
  954
  955                 /*******************************
  956                 *      SINGLE OBJECT PAGE      *
  957                 *******************************/
 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.
  967object_page(Obj, Options) -->
  968    prolog:doc_object_page(Obj, Options),
  969    !,
  970    object_page_footer(Obj, Options).
  971object_page(Obj, Options) -->
  972    { doc_comment(Obj, File:_Line, _Summary, _Comment)
  973    },
  974    !,
  975    (   { \+ ( doc_comment(Obj, File2:_, _, _),
  976               File2 \== File )
  977        }
  978    ->  html([ \html_requires(pldoc),
  979               \object_page_header(File, Options),
  980               \object_synopsis(Obj, []),
  981               \objects([Obj], Options)
  982             ])
  983    ;   html([ \html_requires(pldoc),
  984               \object_page_header(-, Options),
  985               \objects([Obj], [synopsis(true)|Options])
  986             ])
  987    ),
  988    object_page_footer(Obj, Options).
  989object_page(M:Name/Arity, Options) -->          % specified module, but public
  990    { functor(Head, Name, Arity),
  991      (   predicate_property(M:Head, exported)
  992      ->  module_property(M, class(library))
  993      ;   \+ predicate_property(M:Head, defined)
  994      )
  995    },
  996    prolog:doc_object_page(Name/Arity, Options),
  997    !,
  998    object_page_footer(Name/Arity, Options).
  999
 1000object_page_header(File, Options) -->
 1001    prolog:doc_page_header(file(File), Options),
 1002    !.
 1003object_page_header(File, Options) -->
 1004    { option(header(true), Options, true) },
 1005    !,
 1006    html(div(class(navhdr),
 1007             [ div(class(jump), \file_link(File)),
 1008               div(class(search), \search_form(Options)),
 1009               br(clear(right))
 1010             ])).
 1011object_page_header(_, _) --> [].
 1012
 1013file_link(-) -->
 1014    !,
 1015    places_menu(-).
 1016file_link(File) -->
 1017    { file_directory_name(File, Dir)
 1018    },
 1019    places_menu(Dir),
 1020    html([ div(a(href(location_by_id(pldoc_doc)+File), File))
 1021         ]).
 object_page_footer(+Obj, +Options)// is det
Call the hook doc_object_page_footer//2. This hook will be used to deal with annotations.
 1028object_page_footer(Obj, Options) -->
 1029    prolog:doc_object_page_footer(Obj, Options).
 1030object_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.
 1044object_synopsis(Name/Arity, _) -->
 1045    { functor(Head, Name, Arity),
 1046      predicate_property(system:Head, built_in)
 1047    },
 1048    synopsis([span(class(builtin), 'built-in')]).
 1049object_synopsis(Name/Arity, Options) -->
 1050    !,
 1051    object_synopsis(_:Name/Arity, Options).
 1052object_synopsis(M:Name/Arity, Options) -->
 1053    { functor(Head, Name, Arity),
 1054      (   option(source(Spec), Options)
 1055      ->  absolute_file_name(Spec, File,
 1056                             [ access(read),
 1057                               file_type(prolog),
 1058                               file_errors(fail)
 1059                             ])
 1060      ;   predicate_property(M:Head, exported),
 1061          \+ predicate_property(M:Head, imported_from(_)),
 1062          module_property(M, file(File)),
 1063          file_name_on_path(File, Spec)
 1064      ),
 1065      !,
 1066      unquote_filespec(Spec, Unquoted),
 1067      (   predicate_property(Head, autoload(FileBase)),
 1068          file_name_extension(FileBase, _Ext, File)
 1069      ->  Extra = [span(class(autoload), '(can be autoloaded)')]
 1070      ;   Extra = []
 1071      )
 1072    },
 1073    (   { option(href(HREF), Options) }
 1074    ->  synopsis([code([':- use_module(',a(href(HREF), '~q'-[Unquoted]),').'])|Extra])
 1075    ;   synopsis([code(':- use_module(~q).'-[Unquoted])|Extra])
 1076    ).
 1077object_synopsis(Name//Arity, Options) -->
 1078    !,
 1079    { DCGArity is Arity+2 },
 1080    object_synopsis(Name/DCGArity, Options).
 1081object_synopsis(Module:Name//Arity, Options) -->
 1082    !,
 1083    { DCGArity is Arity+2 },
 1084    object_synopsis(Module:Name/DCGArity, Options).
 1085object_synopsis(f(_/_), _) -->
 1086    synopsis(span(class(function),
 1087                  [ 'Arithmetic function (see ',
 1088                    \object_ref(is/2, []),
 1089                    ')'
 1090                  ])).
 1091object_synopsis(c(Func), _) -->
 1092    { sub_atom(Func, 0, _, _, 'PL_')
 1093    },
 1094    !,
 1095    synopsis([span(class(cfunc), 'C-language interface function')]).
 1096object_synopsis(_, _) --> [].
 1097
 1098synopsis(Text) -->
 1099    html(div(class(synopsis),
 1100             [ span(class('synopsis-hdr'), 'Availability:')
 1101             | Text
 1102             ])).
 unquote_filespec(+Spec, -Unquoted) is det
Translate e.g. library('semweb/rdf_db') into library(semweb/rdf_db).
 1109unquote_filespec(Spec, Unquoted) :-
 1110    compound(Spec),
 1111    Spec =.. [Alias,Path],
 1112    atom(Path),
 1113    atomic_list_concat(Parts, /, Path),
 1114    maplist(need_no_quotes, Parts),
 1115    !,
 1116    parts_to_path(Parts, UnquotedPath),
 1117    Unquoted =.. [Alias, UnquotedPath].
 1118unquote_filespec(Spec, Spec).
 1119
 1120need_no_quotes(Atom) :-
 1121    format(atom(A), '~q', [Atom]),
 1122    \+ sub_atom(A, 0, _, _, '\'').
 1123
 1124parts_to_path([One], One) :- !.
 1125parts_to_path(List, More/T) :-
 1126    (   append(H, [T], List)
 1127    ->  parts_to_path(H, More)
 1128    ).
 1129
 1130
 1131                 /*******************************
 1132                 *             PRINT            *
 1133                 *******************************/
 doc_write_html(+Out:stream, +Title:atomic, +DOM) is det
Write HTML for the documentation page DOM using Title to Out.
 1139doc_write_html(Out, Title, Doc) :-
 1140    doc_page_dom(Title, Doc, DOM),
 1141    phrase(html(DOM), Tokens),
 1142    print_html_head(Out),
 1143    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.
 1150doc_page_dom(Title, Body, DOM) :-
 1151    DOM = html([ head([ title(Title),
 1152                        link([ rel(stylesheet),
 1153                               type('text/css'),
 1154                               href(location_by_id(pldoc_resource)+'pldoc.css')
 1155                             ]),
 1156                        script([ src(location_by_id(pldoc_resource)+'pldoc.js'),
 1157                                 type('text/javascript')
 1158                               ], [])
 1159                      ]),
 1160                 body(Body)
 1161               ]).
 print_html_head(+Out:stream) is det
Print the DOCTYPE line.
 1167print_html_head(Out) :-
 1168    format(Out,
 1169           '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" \c
 1170               "http://www.w3.org/TR/html4/strict.dtd">~n', []).
 1171
 1172% Rendering rules
 1173%
 1174% 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.
 1182tags(Tags) -->
 1183    html(dl(class=tags, Tags)).
 tag(+Tag, +Values:list)// is det
Called from \tag(Name, Values) terms produced by doc_wiki.pl.
 1189tag(Tag, Values) -->
 1190    {   doc_tag_title(Tag, Title),
 1191        atom_concat('keyword-', Tag, Class)
 1192    },
 1193    html([ dt(class=Class, Title),
 1194           \tag_values(Values, Class)
 1195         ]).
 1196
 1197tag_values([], _) -->
 1198    [].
 1199tag_values([H|T], Class) -->
 1200    html(dd(class=Class, ['- '|H])),
 1201    tag_values(T, Class).
 doc_tag_title(+Tag, -Title) is det
Title is the name to use for Tag in the generated documentation.
 1208doc_tag_title(Tag, Title) :-
 1209    tag_title(Tag, Title),
 1210    !.
 1211doc_tag_title(Tag, Tag).
 1212
 1213tag_title(compat, 'Compatibility').
 1214tag_title(tbd,    'To be done').
 1215tag_title(see,    'See also').
 1216tag_title(error,  'Errors').
 args(+Params:list) is det
Called from \args(List) created by doc_wiki.pl. Params is a list of arg(Name, Descr).
 1223args(Params) -->
 1224    html([ dt(class=tag, 'Arguments:'),
 1225           dd(table(class=arglist,
 1226                    \arg_list(Params)))
 1227         ]).
 1228
 1229arg_list([]) -->
 1230    [].
 1231arg_list([H|T]) -->
 1232    argument(H),
 1233    arg_list(T).
 1234
 1235argument(arg(Name,Descr)) -->
 1236    html(tr([td(var(Name)), td(class=argdescr, ['- '|Descr])])).
 1237
 1238
 1239                 /*******************************
 1240                 *         NAVIGATION TREE      *
 1241                 *******************************/
 objects_nav_tree(+Objects, -Tree) is det
Provide a navigation tree showing the context of Object. Tree is of the form node(Object, Children).
 1248objects_nav_tree(Objects, Tree) :-
 1249    maplist(object_nav_tree, Objects, Trees),
 1250    union_trees(Trees, Tree0),
 1251    remove_unique_root(Tree0, Tree).
 1252
 1253object_nav_tree(Obj, Tree) :-
 1254    Node = node(directory(Dir), FileNodes),
 1255    FileNode = node(file(File), Siblings),
 1256    doc_comment(Obj, File:_Line, _Summary, _Comment),
 1257    !,
 1258    file_directory_name(File, Dir),
 1259    sibling_file_nodes(Dir, FileNodes0),
 1260    selectchk(node(file(File),[]), FileNodes0, FileNode, FileNodes),
 1261    findall(Sibling, doc_comment(Sibling, File:_, _, _), Siblings0),
 1262    delete(Siblings0, _:module(_), Siblings1),
 1263    doc_hide_private(Siblings1, Siblings2, []),
 1264    flatten(Siblings2, Siblings),   % a comment may describe objects
 1265    embed_directories(Node, Tree).
 1266
 1267sibling_file_nodes(Dir, Nodes) :-
 1268    findall(node(file(File), []),
 1269            (   source_file(File),
 1270                file_directory_name(File, Dir)
 1271            ),
 1272            Nodes).
 1273
 1274embed_directories(Node, Tree) :-
 1275    Node = node(file(File), _),
 1276    !,
 1277    file_directory_name(File, Dir),
 1278    Super = node(directory(Dir), [Node]),
 1279    embed_directories(Super, Tree).
 1280embed_directories(Node, Tree) :-
 1281    Node = node(directory(Dir), _),
 1282    file_directory_name(Dir, SuperDir),
 1283    SuperDir \== Dir,
 1284    !,
 1285    Super = node(directory(SuperDir), [Node]),
 1286    embed_directories(Super, Tree).
 1287embed_directories(Tree, Tree).
 1288
 1289
 1290union_trees([Tree], Tree) :- !.
 1291union_trees([T1,T2|Trees], Tree) :-
 1292    merge_trees(T1, T2, M1),
 1293    union_trees([M1|Trees], Tree).
 1294
 1295merge_trees(node(R, Ch1), node(R, Ch2), node(R, Ch)) :-
 1296    merge_nodes(Ch1, Ch2, Ch).
 1297
 1298merge_nodes([], Ch, Ch) :- !.
 1299merge_nodes(Ch, [], Ch) :- !.
 1300merge_nodes([node(Root, Ch1)|T1], N1, [T1|Nodes]) :-
 1301    selectchk(node(Root, Ch2), N1, N2),
 1302    !,
 1303    merge_trees(node(Root, Ch1), node(Root, Ch2), T1),
 1304    merge_nodes(T1, N2, Nodes).
 1305merge_nodes([Node|T1], N1, [Node|Nodes]) :-
 1306    merge_nodes(T1, N1, Nodes).
 remove_unique_root(+TreeIn, -Tree)
Remove the root part that does not branch
 1312remove_unique_root(node(_, [node(R1, [R2])]), Tree) :-
 1313    !,
 1314    remove_unique_root(node(R1, [R2]), Tree).
 1315remove_unique_root(Tree, Tree).
 nav_tree(+Tree, +Current, +Options)// is det
Render the navigation tree
 1321nav_tree(Tree, Current, Options) -->
 1322    html(ul(class(nav),
 1323            \object_tree(Tree, Current, Options))).
 object_tree(+Tree, +Current, +Options)// is det
Render a tree of objects used for navigation.
 1329object_tree(node(Id, []), Target, Options) -->
 1330    !,
 1331    { node_class(Id, Target, Class) },
 1332    html(li(class(Class),
 1333            \node(Id, Options))).
 1334object_tree(node(Id, Children), Target, Options) -->
 1335    !,
 1336    { node_class(Id, Target, Class) },
 1337    html(li(class(Class),
 1338            [ \node(Id, Options),
 1339              ul(class(nav),
 1340                 \object_trees(Children, Target, Options))
 1341            ])).
 1342object_tree(Id, Target, Options) -->
 1343    !,
 1344    { node_class(Id, Target, Class) },
 1345    html(li(class([obj|Class]), \node(Id, Options))).
 1346
 1347object_trees([], _, _) --> [].
 1348object_trees([H|T], Target, Options) -->
 1349    object_tree(H, Target, Options),
 1350    object_trees(T, Target, Options).
 1351
 1352node_class(Ids, Current, Class) :-
 1353    is_list(Ids),
 1354    !,
 1355    (   member(Id, Ids), memberchk(Id, Current)
 1356    ->  Class = [nav,current]
 1357    ;   Class = [nav]
 1358    ).
 1359node_class(Id, Current, Class) :-
 1360    (   memberchk(Id, Current)
 1361    ->  Class = [nav,current]
 1362    ;   Class = [nav]
 1363    ).
 1364
 1365node(file(File), Options) -->
 1366    !,
 1367    object_ref(file(File), [style(title)|Options]).
 1368node(Id, Options) -->
 1369    object_ref(Id, Options).
 1370
 1371
 1372                 /*******************************
 1373                 *            SECTIONS          *
 1374                 *******************************/
 1375
 1376section(Type, Title) -->
 1377    { string_codes(Title, Codes),
 1378      wiki_codes_to_dom(Codes, [], Content0),
 1379      strip_leading_par(Content0, Content),
 1380      make_section(Type, Content, HTML)
 1381    },
 1382    html(HTML).
 1383
 1384make_section(module,  Title, h1(class=module,  Title)).
 1385make_section(section, Title, h1(class=section, Title)).
 1386
 1387
 1388                 /*******************************
 1389                 *       PRED MODE HEADER       *
 1390                 *******************************/
 pred_dt(+Modes, +Class, Options)// is det
Emit the predicate header.
Arguments:
Modes- List as returned by process_modes/5.
 1398pred_dt(Modes, Class, Options) -->
 1399    pred_dt(Modes, Class, [], _Done, Options).
 1400
 1401pred_dt([], _, Done, Done, _) -->
 1402    [].
 1403pred_dt([H|T], Class, Done0, Done, Options) -->
 1404    { functor(Class, CSSClass, _) },
 1405    html(dt(class=CSSClass,
 1406            [ \pred_mode(H, Done0, Done1, Options),
 1407              \mode_anot(Class)
 1408            ])),
 1409    pred_dt(T, Class, Done1, Done, Options).
 1410
 1411mode_anot(privdef) -->
 1412    !,
 1413    html(span([class(anot), style('float:right')],
 1414              '[private]')).
 1415mode_anot(multidef(object(Obj))) -->
 1416    !,
 1417    { object_href(Obj, HREF) },
 1418    html(span([class(anot), style('float:right')],
 1419              ['[', a(href(HREF), multifile), ']'
 1420              ])).
 1421mode_anot(multidef(file(File:_))) -->
 1422    !,
 1423    { file_name_on_path(File, Spec),
 1424      unquote_filespec(Spec, Unquoted),
 1425      doc_file_href(File, HREF)
 1426    },
 1427    html(span([class(anot), style('float:right')],
 1428              ['[multifile, ', a(href(HREF), '~q'-[Unquoted]), ']'
 1429              ])).
 1430mode_anot(multidef) -->
 1431    !,
 1432    html(span([class(anot), style('float:right')],
 1433              '[multifile]')).
 1434mode_anot(_) -->
 1435    [].
 1436
 1437pred_mode(mode(Head,Vars), Done0, Done, Options) -->
 1438    !,
 1439    { bind_vars(Head, Vars) },
 1440    pred_mode(Head, Done0, Done, Options).
 1441pred_mode(Head is Det, Done0, Done, Options) -->
 1442    !,
 1443    anchored_pred_head(Head, Done0, Done, Options),
 1444    pred_det(Det).
 1445pred_mode(Head, Done0, Done, Options) -->
 1446    anchored_pred_head(Head, Done0, Done, Options).
 1447
 1448bind_vars(Term, Bindings) :-
 1449    bind_vars(Bindings),
 1450    anon_vars(Term).
 1451
 1452bind_vars([]).
 1453bind_vars([Name=Var|T]) :-
 1454    Var = '$VAR'(Name),
 1455    bind_vars(T).
 anon_vars(+Term) is det
Bind remaining variables in Term to '$VAR'('_'), so they are printed as '_'.
 1462anon_vars(Var) :-
 1463    var(Var),
 1464    !,
 1465    Var = '$VAR'('_').
 1466anon_vars(Term) :-
 1467    compound(Term),
 1468    !,
 1469    Term =.. [_|Args],
 1470    maplist(anon_vars, Args).
 1471anon_vars(_).
 1472
 1473
 1474anchored_pred_head(Head, Done0, Done, Options) -->
 1475    { pred_anchor_name(Head, PI, Name) },
 1476    (   { memberchk(PI, Done0) }
 1477    ->  { Done = Done0 },
 1478        pred_head(Head)
 1479    ;   html([ span(style('float:right'),
 1480                    [ \pred_edit_or_source_button(Head, Options),
 1481                      &(nbsp)
 1482                    ]),
 1483               a(name=Name, \pred_head(Head))
 1484             ]),
 1485        { Done = [PI|Done0] }
 1486    ).
 1487
 1488
 1489pred_edit_or_source_button(Head, Options) -->
 1490    { option(edit(true), Options) },
 1491    !,
 1492    pred_edit_button(Head, Options).
 1493pred_edit_or_source_button(Head, Options) -->
 1494    { option(source_link(true), Options) },
 1495    !,
 1496    pred_source_button(Head, Options).
 1497pred_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)
 1511pred_edit_button(_, Options) -->
 1512    { \+ option(edit(true), Options) },
 1513    !.
 1514pred_edit_button(PI0, Options0) -->
 1515    { canonicalise_predref(PI0, PI, Options0, Options) },
 1516    pred_edit_button2(PI, Options).
 1517
 1518pred_edit_button2(Name/Arity, Options) -->
 1519    { \+ ( memberchk(file(_), Options), % always edit if file and line
 1520           memberchk(line(_), Options)  % are given.
 1521         ),
 1522      functor(Head, Name, Arity),
 1523      option(module(M), Options, _),
 1524      \+ ( current_module(M),
 1525           source_file(M:Head, _File)
 1526         )
 1527    },
 1528    !.
 1529pred_edit_button2(Name/Arity, Options) -->
 1530    { include(edit_param, Options, Extra),
 1531      http_link_to_id(pldoc_edit,
 1532                      [name(Name),arity(Arity)|Extra],
 1533                      EditHREF)
 1534    },
 1535    html(a(onClick('HTTPrequest(\'' + EditHREF + '\')'),
 1536           img([ class(action),
 1537                 alt('Edit predicate'),
 1538                 title('Edit predicate'),
 1539                 src(location_by_id(pldoc_resource)+'editpred.png')
 1540               ]))).
 1541pred_edit_button2(_, _) -->
 1542    !,
 1543    [].
 1544
 1545edit_param(module(_)).
 1546edit_param(file(_)).
 1547edit_param(line(_)).
 object_edit_button(+Object, +Options)// is det
Create a button for editing Object.
 1554object_edit_button(_, Options) -->
 1555    { \+ option(edit(true), Options) },
 1556    !.
 1557object_edit_button(PI, Options) -->
 1558    { is_pi(PI) },
 1559    !,
 1560    pred_edit_button(PI, Options).
 1561object_edit_button(_, _) -->
 1562    [].
 pred_source_button(+PredIndicator, +Options)// is det
Create a button for viewing the source of a predicate.
 1569pred_source_button(PI0, Options0) -->
 1570    { canonicalise_predref(PI0, PI, Options0, Options),
 1571      option(module(M), Options, _),
 1572      pred_source_href(PI, M, HREF), !
 1573    },
 1574    html(a([ href(HREF)
 1575           ],
 1576           img([ class(action),
 1577                 alt('Source'),
 1578                 title('Show source'),
 1579                 src(location_by_id(pldoc_resource)+'source.png')
 1580               ]))).
 1581pred_source_button(_, _) -->
 1582    [].
 object_source_button(+Object, +Options)// is det
Create a button for showing the source of Object.
 1589object_source_button(PI, Options) -->
 1590    { is_pi(PI),
 1591      option(source_link(true), Options, true)
 1592    },
 1593    !,
 1594    pred_source_button(PI, Options).
 1595object_source_button(_, _) -->
 1596    [].
 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.
 1604canonicalise_predref(M:PI0, PI, Options0, [module(M)|Options]) :-
 1605    !,
 1606    canonicalise_predref(PI0, PI, Options0, Options).
 1607canonicalise_predref(//(Head), PI, Options0, Options) :-
 1608    !,
 1609    functor(Head, Name, Arity),
 1610    PredArity is Arity + 2,
 1611    canonicalise_predref(Name/PredArity, PI, Options0, Options).
 1612canonicalise_predref(Name//Arity, PI, Options0, Options) :-
 1613    integer(Arity), Arity >= 0,
 1614    !,
 1615    PredArity is Arity + 2,
 1616    canonicalise_predref(Name/PredArity, PI, Options0, Options).
 1617canonicalise_predref(PI, PI, Options, Options) :-
 1618    PI = Name/Arity,
 1619    atom(Name), integer(Arity), Arity >= 0,
 1620    !.
 1621canonicalise_predref(Head, PI, Options0, Options) :-
 1622    functor(Head, Name, Arity),
 1623    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.
 1631pred_head(Var) -->
 1632    { var(Var),
 1633      !,
 1634      instantiation_error(Var)
 1635    }.
 1636pred_head(//(Head)) -->
 1637    !,
 1638    pred_head(Head),
 1639    html(//).
 1640pred_head(M:Head) -->
 1641    html([span(class=module, M), :]),
 1642    pred_head(Head).
 1643pred_head(Head) -->
 1644    { atom(Head) },
 1645    !,
 1646    html(b(class=pred, Head)).
 1647pred_head(Head) -->                     % Infix operators
 1648    { Head =.. [Functor,Left,Right],
 1649      is_op_type(Functor, infix)
 1650    },
 1651    !,
 1652    html([ var(class=arglist, \pred_arg(Left, 1)),
 1653           ' ', b(class=pred, Functor), ' ',
 1654           var(class=arglist, \pred_arg(Right, 2))
 1655         ]).
 1656pred_head(Head) -->                     % Prefix operators
 1657    { Head =.. [Functor,Arg],
 1658      is_op_type(Functor, prefix)
 1659    },
 1660    !,
 1661    html([ b(class=pred, Functor), ' ',
 1662           var(class=arglist, \pred_arg(Arg, 1))
 1663         ]).
 1664pred_head(Head) -->                     % Postfix operators
 1665    { Head =.. [Functor,Arg],
 1666      is_op_type(Functor, postfix)
 1667    },
 1668    !,
 1669    html([ var(class=arglist, \pred_arg(Arg, 1)),
 1670           ' ', b(class=pred, Functor)
 1671         ]).
 1672pred_head(Head) -->                     % Plain terms
 1673    { Head =.. [Functor|Args] },
 1674    html([ b(class=pred, Functor),
 1675           var(class=arglist,
 1676               [ '(', \pred_args(Args, 1), ')' ])
 1677         ]).
 is_op_type(+Atom, ?Type)
True if Atom is an operator of Type. Type is one of prefix, infix or postfix.
 1684is_op_type(Functor, Type) :-
 1685    current_op(_Pri, F, Functor),
 1686    op_type(F, Type).
 1687
 1688op_type(fx,  prefix).
 1689op_type(fy,  prefix).
 1690op_type(xf,  postfix).
 1691op_type(yf,  postfix).
 1692op_type(xfx, infix).
 1693op_type(xfy, infix).
 1694op_type(yfx, infix).
 1695op_type(yfy, infix).
 1696
 1697
 1698pred_args([], _) -->
 1699    [].
 1700pred_args([H|T], I) -->
 1701    pred_arg(H, I),
 1702    (   {T==[]}
 1703    ->  []
 1704    ;   html(', '),
 1705        { I2 is I + 1 },
 1706        pred_args(T, I2)
 1707    ).
 1708
 1709pred_arg(Var, I) -->
 1710    { var(Var) },
 1711    !,
 1712    html(['Arg', I]).
 1713pred_arg(...(Term), I) -->
 1714    !,
 1715    pred_arg(Term, I),
 1716    html('...').
 1717pred_arg(Term, I) -->
 1718    { Term =.. [Ind,Arg],
 1719      mode_indicator(Ind)
 1720    },
 1721    !,
 1722    html([Ind, \pred_arg(Arg, I)]).
 1723pred_arg(Arg:Type, _) -->
 1724    !,
 1725    html([\argname(Arg), :, \argtype(Type)]).
 1726pred_arg(Arg, _) -->
 1727    argname(Arg).
 1728
 1729argname('$VAR'(Name)) -->
 1730    !,
 1731    html(Name).
 1732argname(Name) -->
 1733    !,
 1734    html(Name).
 1735
 1736argtype(Term) -->
 1737    { format(string(S), '~W',
 1738             [ Term,
 1739               [ quoted(true),
 1740                 numbervars(true)
 1741               ]
 1742             ]) },
 1743    html(S).
 1744
 1745pred_det(unknown) -->
 1746    [].
 1747pred_det(Det) -->
 1748    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
 1757term(_, Atom, []) -->
 1758    { atomic(Atom),
 1759      !,
 1760      format(string(S), '~W', [Atom,[quoted(true)]])
 1761    },
 1762    html(span(class=functor, S)).
 1763term(_, Key:Type, [TypeName=Type]) -->
 1764    { atomic(Key)
 1765    },
 1766    !,
 1767    html([span(class='pl-key', Key), :, span(class('pl-var'), TypeName)]).
 1768term(_, Term, Bindings) -->
 1769    { is_mode(Term is det),         % HACK. Bit too strict?
 1770      bind_vars(Bindings)
 1771    },
 1772    !,
 1773    pred_head(Term).
 1774term(_, Term, Bindings) -->
 1775    term(Term,
 1776         [ variable_names(Bindings),
 1777           quoued(true)
 1778         ]).
 1779
 1780
 1781                 /*******************************
 1782                 *             PREDREF          *
 1783                 *******************************/
 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.

 1796predref(Term) -->
 1797    { catch(nb_getval(pldoc_options, Options), _, Options = []) },
 1798    predref(Term, Options).
 1799
 1800predref(Obj, Options) -->
 1801    { Obj = _:_,
 1802      doc_comment(Obj, File:_Line, _, _),
 1803      (   (   option(files(Map), Options)
 1804          ->  memberchk(file(File,_), Map)
 1805          ;   true
 1806          )
 1807      ->  object_href(Obj, HREF, Options)
 1808      ;   manref(Obj, HREF, Options)
 1809      )
 1810    },
 1811    !,
 1812    html(a(href(HREF), \object_name(Obj, [qualify(true)|Options]))).
 1813predref(M:Term, Options) -->
 1814    !,
 1815    predref(Term, M, Options).
 1816predref(Term, Options) -->
 1817    predref(Term, _, Options).
 1818
 1819predref(Name/Arity, _, Options) -->             % Builtin; cannot be overruled
 1820    { prolog:doc_object_summary(Name/Arity, manual, _, _),
 1821      !,
 1822      manref(Name/Arity, HREF, Options)
 1823    },
 1824    html(a([class=builtin, href=HREF], [Name, /, Arity])).
 1825predref(Name/Arity, _, Options) -->             % From packages
 1826    { option(prefer(manual), Options),
 1827      prolog:doc_object_summary(Name/Arity, Category, _, _),
 1828      !,
 1829      manref(Name/Arity, HREF, Options)
 1830    },
 1831    html(a([class=Category, href=HREF], [Name, /, Arity])).
 1832predref(Obj, Module, Options) -->               % Local
 1833    { doc_comment(Module:Obj, File:_Line, _, _),
 1834      (   option(files(Map), Options)
 1835      ->  memberchk(file(File,_), Map)
 1836      ;   true
 1837      )
 1838    },
 1839    !,
 1840    object_ref(Module:Obj, Options).
 1841predref(Name/Arity, Module, Options) -->
 1842    { \+ option(files(_), Options),
 1843      pred_href(Name/Arity, Module, HREF)
 1844    },
 1845    !,
 1846    html(a(href=HREF, [Name, /, Arity])).
 1847predref(Name//Arity, Module, Options) -->
 1848    { \+ option(files(_), Options),
 1849      PredArity is Arity + 2,
 1850      pred_href(Name/PredArity, Module, HREF)
 1851    },
 1852    !,
 1853    html(a(href=HREF, [Name, //, Arity])).
 1854predref(PI, _, Options) -->             % From packages
 1855    { canonical_pi(PI, CPI, HTML),
 1856      (   option(files(_), Options)
 1857      ->  Category = extmanual
 1858      ;   prolog:doc_object_summary(CPI, Category, _, _)
 1859      ),
 1860      manref(CPI, HREF, Options)
 1861    },
 1862    html(a([class=Category, href=HREF], HTML)).
 1863predref(PI, _, _Options) -->
 1864    { canonical_pi(PI, _CPI, HTML)
 1865    },
 1866    !,
 1867    html(span(class=undef, HTML)).
 1868predref(Callable, Module, Options) -->
 1869    { callable(Callable),
 1870      functor(Callable, Name, Arity)
 1871    },
 1872    predref(Name/Arity, Module, Options).
 1873
 1874canonical_pi(Name/Arity, Name/Arity, [Name, /, Arity]) :-
 1875    atom(Name), integer(Arity),
 1876    !.
 1877canonical_pi(Name//Arity, Name/Arity2, [Name, //, Arity]) :-
 1878    atom(Name), integer(Arity),
 1879    !,
 1880    Arity2 is Arity+2.
 manref(+NameArity, -HREF, +Options) is det
Create reference to a manual page. When generating files, this listens to the option man_server(+Server).
 1888manref(PI, HREF, Options) :-
 1889    predname(PI, PredName),
 1890    (   option(files(_Map), Options)
 1891    ->  option(man_server(Server), Options,
 1892               'http://www.swi-prolog.org/pldoc'),
 1893        uri_components(Server, Comp0),
 1894        uri_data(path, Comp0, Path0),
 1895        directory_file_path(Path0, man, Path),
 1896        uri_data(path, Comp0, Path, Components),
 1897        uri_query_components(Query, [predicate=PredName]),
 1898        uri_data(search, Components, Query),
 1899        uri_components(HREF, Components)
 1900    ;   http_link_to_id(pldoc_man, [predicate=PredName], HREF)
 1901    ).
 1902
 1903predname(Name/Arity, PredName) :-
 1904    !,
 1905    format(atom(PredName), '~w/~d', [Name, Arity]).
 1906predname(Module:Name/Arity, PredName) :-
 1907    !,
 1908    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.
 1922pred_href(Name/Arity, Module, HREF) :-
 1923    format(string(FragmentId), '~w/~d', [Name, Arity]),
 1924    uri_data(fragment, Components, FragmentId),
 1925    functor(Head, Name, Arity),
 1926    (   catch(relative_file(Module:Head, File), _, fail)
 1927    ->  uri_data(path, Components, File),
 1928        uri_components(HREF, Components)
 1929    ;   in_file(Module:Head, File)
 1930    ->  (   current_prolog_flag(home, SWI),
 1931            sub_atom(File, 0, _, _, SWI),
 1932            prolog:doc_object_summary(Name/Arity, packages, _, _)
 1933        ->  http_link_to_id(pldoc_man, [predicate=FragmentId], HREF)
 1934        ;   http_location_by_id(pldoc_doc, DocHandler),
 1935            atom_concat(DocHandler, File, Path),
 1936            uri_data(path, Components, Path),
 1937            uri_components(HREF, Components)
 1938        )
 1939    ).
 1940
 1941relative_file(Head, '') :-
 1942    b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
 1943    in_file(Head, CurrentFile),
 1944    !.
 1945relative_file(Head, RelFile) :-
 1946    b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
 1947    in_file(Head, DefFile),
 1948    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.
 1954pred_source_href(Name/Arity, Module, HREF) :-
 1955    format(string(FragmentId), '~w/~d', [Name, Arity]),
 1956    uri_data(fragment, Components, FragmentId),
 1957    uri_query_components(Query, [show=src]),
 1958    uri_data(search, Components, Query),
 1959    functor(Head, Name, Arity),
 1960    (   catch(relative_file(Module:Head, File), _, fail)
 1961    ->  uri_data(path, Components, File),
 1962        uri_components(HREF, Components)
 1963    ;   in_file(Module:Head, File0)
 1964    ->  insert_alias(File0, File),
 1965        http_location_by_id(pldoc_doc, DocHandler),
 1966        atom_concat(DocHandler, File, Path),
 1967        uri_data(path, Components, Path),
 1968        uri_components(HREF, Components)
 1969    ).
 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.
 1978object_ref([], _) -->
 1979    !,
 1980    [].
 1981object_ref([H|T], Options) -->
 1982    !,
 1983    object_ref(H, Options),
 1984    (   {T == []}
 1985    ->  html(', '),
 1986        object_ref(T, Options)
 1987    ;   []
 1988    ).
 1989object_ref(Obj, Options) -->
 1990    { object_href(Obj, HREF, Options)
 1991    },
 1992    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.
 1999object_href(Obj, HREF) :-
 2000    object_href(Obj, HREF, []).
 2001
 2002object_href(M:PI0, HREF, Options) :-
 2003    option(files(Map), Options),
 2004    (   module_property(M, file(File))
 2005    ->  true
 2006    ;   xref_module(File, M)
 2007    ),
 2008    memberchk(file(File, DocFile), Map),
 2009    !,
 2010    file_base_name(DocFile, LocalFile),     % TBD: proper directory index
 2011    expand_pi(PI0, PI),
 2012    term_to_string(PI, PIS),
 2013    uri_data(path, Components, LocalFile),
 2014    uri_data(fragment, Components, PIS),
 2015    uri_components(HREF, Components).
 2016object_href(file(File), HREF, _Options) :-
 2017    doc_file_href(File, HREF),
 2018    !.
 2019object_href(directory(Dir), HREF, _Options) :-
 2020    directory_file_path(Dir, 'index.html', Index),
 2021    doc_file_href(Index, HREF),
 2022    !.
 2023object_href(Obj, HREF, _Options) :-
 2024    prolog:doc_object_href(Obj, HREF),
 2025    !.
 2026object_href(Obj0, HREF, _Options) :-
 2027    localise_object(Obj0, Obj),
 2028    term_to_string(Obj, String),
 2029    http_link_to_id(pldoc_object, [object=String], HREF).
 2030
 2031expand_pi(Name//Arity0, Name/Arity) :-
 2032    !,
 2033    Arity is Arity0+2.
 2034expand_pi(PI, PI).
 localise_object(+ObjIn, -ObjOut) is det
Abstract path-details to make references more stable over versions.
 2042localise_object(Obj0, Obj) :-
 2043    prolog:doc_canonical_object(Obj0, Obj),
 2044    !.
 2045localise_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.
 2053term_to_string(Term, String) :-
 2054    State = state(-),
 2055    (   numbervars(Term, 0, _, [singletons(true)]),
 2056        with_output_to(string(String),
 2057                       write_term(Term,
 2058                                  [ numbervars(true),
 2059                                    quoted(true)
 2060                                  ])),
 2061        nb_setarg(1, State, String),
 2062        fail
 2063    ;   arg(1, State, String)
 2064    ).
 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
 2078object_name(Obj, Options) -->
 2079    { option(style(Style), Options, inline)
 2080    },
 2081    object_name(Style, Obj, Options).
 2082
 2083object_name(title, Obj, Options) -->
 2084    { merge_options(Options, [secref_style(title)], Options1) },
 2085    prolog:doc_object_link(Obj, Options1),
 2086    !.
 2087object_name(inline, Obj, Options) -->
 2088    prolog:doc_object_link(Obj, Options),
 2089    !.
 2090object_name(title, f(Name/Arity), _Options) -->
 2091    !,
 2092    html(['Function ', Name, /, Arity]).
 2093object_name(inline, f(Name/Arity), _Options) -->
 2094    !,
 2095    html([Name, /, Arity]).
 2096object_name(Style, PI, Options) -->
 2097    { is_pi(PI) },
 2098    !,
 2099    pi(Style, PI, Options).
 2100object_name(inline, Module:module(_Title), _) -->
 2101    !,
 2102    { module_property(Module, file(File)),
 2103      file_base_name(File, Base)
 2104    },
 2105    !,
 2106    html(Base).
 2107object_name(title, Module:module(Title), _) -->
 2108    { module_property(Module, file(File)),
 2109      file_base_name(File, Base)
 2110    },
 2111    !,
 2112    html([Base, ' -- ', Title]).
 2113object_name(title, file(File), _) -->
 2114    { module_property(Module, file(File)),
 2115      doc_comment(Module:module(Title), _, _, _),
 2116      !,
 2117      file_base_name(File, Base)
 2118    },
 2119    html([Base, ' -- ', Title]).
 2120object_name(_, file(File), _) -->
 2121    { file_base_name(File, Base) },
 2122    html(Base).
 2123object_name(_, directory(Dir), _) -->
 2124    { file_base_name(Dir, Base) },
 2125    html(Base).
 2126object_name(_, module(Title), _Options) -->
 2127    { print_message(warning,
 2128                    pldoc(module_comment_outside_module(Title)))
 2129    }.
 2130
 2131pi(title, PI, Options) -->
 2132    pi_type(PI),
 2133    pi(PI, Options).
 2134pi(inline, PI, Options) -->
 2135    pi(PI, Options).
 2136
 2137pi(M:PI, Options) -->
 2138    !,
 2139    (   { option(qualify(true), Options) }
 2140    ->  html([span(class(module), M), :])
 2141    ;   []
 2142    ),
 2143    pi(PI, Options).
 2144pi(Name/Arity, _) -->
 2145    !,
 2146    html([Name, /, Arity]).
 2147pi(Name//Arity, _) -->
 2148    html([Name, //, Arity]).
 2149
 2150pi_type(_:PI) -->
 2151    !,
 2152    pi_type(PI).
 2153pi_type(_/_) -->
 2154    html(['Predicate ']).
 2155pi_type(_//_) -->
 2156    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.
 2168in_file(Module:Head, File) :-
 2169    !,
 2170    distinct(File, in_file(Module, Head, File)).
 2171in_file(Head, File) :-
 2172    distinct(File, in_file(_, Head, File)).
 2173
 2174in_file(Module, Head, File) :-
 2175    var(Module),
 2176    (   predicate_property(system:Head, foreign)
 2177    ->  !,
 2178        fail
 2179    ;   predicate_property(system:Head, file(File)),
 2180        \+ system_arithmetic_function(Head)
 2181    ->  !
 2182    ;   predicate_property(Head, autoload(File0))
 2183    ->  !,
 2184        file_name_extension(File0, pl, File)
 2185    ;   exported_from(Module, Head, File),
 2186        module_property(Module, class(library))
 2187    ).
 2188in_file(Module, Head, File) :-
 2189    xref_defined(File, Head, How),
 2190    xref_current_source(File),
 2191    atom(File),                     % only plain files
 2192    xref_module(File, Module),
 2193    How \= imported(_From).
 2194in_file(Module, Head, File) :-
 2195    exported_from(Module, Head, File).
 2196in_file(Module, Head, File) :-
 2197    predicate_property(Module:Head, file(File)),
 2198    \+ predicate_property(Module:Head, imported_from(_)).
 2199in_file(Module, Head, File) :-
 2200    current_module(Module),
 2201    source_file(Module:Head, File).
 2202
 2203exported_from(Module, Head, File) :-
 2204    distinct(Primary,
 2205             (   predicate_property(Module:Head, exported),
 2206                 (   predicate_property(Module:Head, imported_from(Primary))
 2207                 ->  true
 2208                 ;   Primary = Module
 2209                 ))),
 2210    module_property(Primary, file(File)).
 2211
 2212:- multifile
 2213    arithmetic:evaluable/2. 2214
 2215system_arithmetic_function(Head) :-
 2216    functor(Head, Name, Arity),
 2217    FArith is Arity-1,
 2218    FArith >= 0,
 2219    functor(FHead, Name, FArith),
 2220    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?
 2251file(File) -->
 2252    file(File, []).
 2253
 2254file(File, Options) -->
 2255    { catch(nb_getval(pldoc_options, GenOptions), _, GenOptions = []),
 2256      merge_options(Options, GenOptions, FinalOptions)
 2257    },
 2258    link_file(File, FinalOptions),
 2259    !.
 2260file(File, Options) -->
 2261    { option(edit_handler(Handler), Options),
 2262      http_current_request(Request),
 2263      memberchk(path(Path), Request),
 2264      absolute_file_name(File, Location,
 2265                         [ relative_to(Path)
 2266                         ]),
 2267      http_link_to_id(Handler, [location(Location)], HREF),
 2268      format(atom(Title), 'Click to create ~w', [File])
 2269    },
 2270    html(a([href(HREF), class(nofile), title(Title)], File)).
 2271file(File, _) -->
 2272    html(code(class(nofile), File)).
 2273
 2274link_file(File, Options) -->
 2275    { file_href(File, HREF, Options),
 2276      option(label(Label), Options, File),
 2277      option(class(Class), Options, file)
 2278    },
 2279    html(a([class(Class), href(HREF)], Label)).
 file_href(+FilePath, -HREF, +Options) is det
Find URL for refering to FilePath based on Options.
 2285file_href(_, HREF, Options) :-
 2286    option(href(HREF), Options),
 2287    !.
 2288file_href(File, HREF, Options) :-
 2289    file_href_real(File, HREF0, Options),
 2290    map_extension(HREF0, HREF, Options).
 map_extension(+HREFIn, -HREFOut, Options) is det
Replace extension using the option
 2298map_extension(HREF0, HREF, Options) :-
 2299    option(map_extension(Map), Options),
 2300    file_name_extension(Base, Old, HREF0),
 2301    memberchk(Old-New, Map),
 2302    !,
 2303    file_name_extension(Base, New, HREF).
 2304map_extension(HREF, HREF, _).
 2305
 2306
 2307file_href_real(File, HREF, Options) :-
 2308    (   option(absolute_path(Path), Options)
 2309    ;   existing_linked_file(File, Path)
 2310    ),
 2311    !,
 2312    (   option(files(Map), Options),
 2313        memberchk(file(Path, LinkFile), Map)
 2314    ->  true
 2315    ;   LinkFile = Path
 2316    ),
 2317    file_href(LinkFile, HREF).
 2318file_href_real(File, HREF, _) :-
 2319    directory_alias(Alias),
 2320    Term =.. [Alias,File],
 2321    absolute_file_name(Term, _,
 2322                       [ access(read),
 2323                         file_errors(fail)
 2324                       ]),
 2325    !,
 2326    http_absolute_location(Term, HREF, []).
 2327
 2328directory_alias(icons).
 2329directory_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.
 2339file_href(Path, HREF) :-                % a loaded Prolog file
 2340    source_file(Path),
 2341    !,
 2342    doc_file_href(Path, HREF).
 2343file_href(Path, HREF) :-
 2344    (   nb_current(pldoc_output, CFile)
 2345    ;   nb_current(pldoc_file, CFile)
 2346    ),
 2347    CFile \== [],
 2348    !,
 2349    relative_file_name(Path, CFile, HREF).
 2350file_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.
 2358existing_linked_file(File, Path) :-
 2359    catch(b_getval(pldoc_file, CurrentFile), _, fail),
 2360    CurrentFile \== [],
 2361    absolute_file_name(File, Path,
 2362                       [ relative_to(CurrentFile),
 2363                         access(read),
 2364                         file_errors(fail)
 2365                       ]).
 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]].
 2375include(PI, predicate, _) -->
 2376    !,
 2377    (   html_tokens_for_predicates(PI, [])
 2378    ->  []
 2379    ;   html(['[[', \predref(PI), ']]'])
 2380    ).
 2381include(File, image, Options) -->
 2382    { file_name_extension(_, svg, File),
 2383      file_href(File, HREF, Options),
 2384      !,
 2385      include(image_attribute, Options, Attrs0),
 2386      merge_options(Attrs0,
 2387                    [ alt(File),
 2388                      data(HREF),
 2389                      type('image/svg+xml')
 2390                    ], Attrs)
 2391    },
 2392    (   { option(caption(Caption), Options) }
 2393    ->  html(div(class(figure),
 2394                 [ div(class(image), object(Attrs, [])),
 2395                   div(class(caption), Caption)
 2396                 ]))
 2397    ;   html(object(Attrs, []))
 2398    ).
 2399include(File, image, Options) -->
 2400    { file_href(File, HREF, Options),
 2401      !,
 2402      include(image_attribute, Options, Attrs0),
 2403      merge_options(Attrs0,
 2404                    [ alt(File),
 2405                      border(0),
 2406                      src(HREF)
 2407                    ], Attrs)
 2408    },
 2409    (   { option(caption(Caption), Options) }
 2410    ->  html(div(class(figure),
 2411                 [ div(class(image), img(Attrs)),
 2412                   div(class(caption), Caption)
 2413                 ]))
 2414    ;   html(img(Attrs))
 2415    ).
 2416include(File, wiki, _Options) -->       % [[file.txt]] is included
 2417    { access_file(File, read),
 2418      !,
 2419      read_file_to_codes(File, String, []),
 2420      wiki_codes_to_dom(String, [], DOM)
 2421    },
 2422    html(DOM).
 2423include(File, _Type, Options) -->
 2424    link_file(File, Options),
 2425    !.
 2426include(File, _, _) -->
 2427    html(code(class(nofile), ['[[',File,']]'])).
 2428
 2429image_attribute(src(_)).
 2430image_attribute(alt(_)).
 2431image_attribute(title(_)).
 2432image_attribute(align(_)).
 2433image_attribute(width(_)).
 2434image_attribute(height(_)).
 2435image_attribute(border(_)).
 2436image_attribute(class(_)).
 2437image_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]]
 2450html_tokens_for_predicates([], _Options) -->
 2451    [].
 2452html_tokens_for_predicates([H|T], Options) -->
 2453    !,
 2454    html_tokens_for_predicates(H, Options),
 2455    html_tokens_for_predicates(T, Options).
 2456html_tokens_for_predicates(PI, Options) -->
 2457    { PI = _:_/_,
 2458      !,
 2459      (   doc_comment(PI, Pos, _Summary, Comment)
 2460      ->  true
 2461      ;   Comment = ''
 2462      )
 2463    },
 2464    object(PI, [Pos-Comment], [dl], _, Options).
 2465html_tokens_for_predicates(Spec, Options) -->
 2466    { findall(PI, documented_pi(Spec, PI), List),
 2467      List \== [], !
 2468    },
 2469    html_tokens_for_predicates(List, Options).
 2470html_tokens_for_predicates(Spec, Options) -->
 2471    man_page(Spec,
 2472             [ links(false),                % no header
 2473               navtree(false),              % no navigation tree
 2474               footer(false),               % no footer
 2475               synopsis(false)              % no synopsis
 2476             | Options
 2477             ]).
 2478
 2479
 2480documented_pi(Spec, PI) :-
 2481    generalise_spec(Spec, PI),
 2482    doc_comment(PI, _Pos, _Summary, _Comment).
 2483
 2484generalise_spec(Name/Arity, _M:Name/Arity).
 2485generalise_spec(Name//Arity, _M:Name//Arity).
 2486
 2487
 2488                 /*******************************
 2489                 *           WIKI FILES         *
 2490                 *******************************/
 doc_for_wiki_file(+File, +Options) is det
Write HTML for the File containing wiki data.
 2497doc_for_wiki_file(FileSpec, Options) :-
 2498    absolute_file_name(FileSpec, File,
 2499                       [ access(read)
 2500                       ]),
 2501    read_file_to_codes(File, String, []),
 2502    b_setval(pldoc_file, File),
 2503    call_cleanup(reply_wiki_page(File, String, Options),
 2504                 nb_delete(pldoc_file)).
 2505
 2506reply_wiki_page(File, String, Options) :-
 2507    wiki_codes_to_dom(String, [], DOM0),
 2508    title(DOM0, File, Title),
 2509    insert_edit_button(DOM0, File, DOM, Options),
 2510    reply_html_page(pldoc(wiki),
 2511                    title(Title),
 2512                    [ \html_requires(pldoc)
 2513                    | DOM
 2514                    ]).
 2515
 2516title(DOM, _, Title) :-
 2517    sub_term(h1(_,Title), DOM),
 2518    !.
 2519title(_, File, Title) :-
 2520    file_base_name(File, Title).
 2521
 2522insert_edit_button(DOM, _, DOM, Options) :-
 2523    option(edit(false), Options, false),
 2524    !.
 2525insert_edit_button([h1(Attrs,Title)|DOM], File,
 2526                   [h1(Attrs,[ span(style('float:right'),
 2527                                   \edit_button(File, [edit(true)]))
 2528                             | Title
 2529                             ])|DOM], _) :- !.
 2530insert_edit_button(DOM, File,
 2531                   [ h1(class(wiki),
 2532                        [ span(style('float:right'),
 2533                               \edit_button(File, [edit(true)]))
 2534                        ])
 2535                   | DOM
 2536                   ], _).
 2537
 2538
 2539                 /*******************************
 2540                 *            ANCHORS           *
 2541                 *******************************/
 mode_anchor_name(+Mode, -Anchor:atom) is det
Get the anchor name for a mode.
 2547mode_anchor_name(Var, _) :-
 2548    var(Var),
 2549    !,
 2550    instantiation_error(Var).
 2551mode_anchor_name(mode(Head, _), Anchor) :-
 2552    !,
 2553    mode_anchor_name(Head, Anchor).
 2554mode_anchor_name(Head is _Det, Anchor) :-
 2555    !,
 2556    mode_anchor_name(Head, Anchor).
 2557mode_anchor_name(Head, Anchor) :-
 2558    pred_anchor_name(Head, _, Anchor).
 pred_anchor_name(+Head, -PI:atom/integer, -Anchor:atom) is det
Create an HTML anchor name from Head.
 2565pred_anchor_name(//(Head), Name/Arity, Anchor) :-
 2566    !,
 2567    functor(Head, Name, DCGArity),
 2568    Arity is DCGArity+2,
 2569    format(atom(Anchor), '~w/~d', [Name, Arity]).
 2570pred_anchor_name(Head, Name/Arity, Anchor) :-
 2571    functor(Head, Name, Arity),
 2572    format(atom(Anchor), '~w/~d', [Name, Arity]).
 2573
 2574:- multifile prolog:message//1. 2575
 2576prolog:message(pldoc(module_comment_outside_module(Title))) -->
 2577    [ 'PlDoc comment <module> ~w does not appear in a module'-[Title] ]