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            nopredref//1,               % +PI //
   47            module_info/3,              % +File, +Options0, -Options
   48            doc_hide_private/3,         % +Doc0, -Doc, +Options
   49            edit_button//2,             % +File, +Options, //
   50            source_button//2,           % +File, +Options, //
   51            zoom_button//2,             % +File, +Options, //
   52            pred_edit_button//2,        % +PredInd, +Options, //
   53            object_edit_button//2,      % +Obj, +Options, //
   54            object_source_button//2,    % +Obj, +Options, //
   55            doc_resources//1,           % +Options
   56            ensure_doc_objects/1,       % +File
   57                                        % Support other backends
   58            doc_file_objects/5,         % +FSpec, -File, -Objs, -FileOpts, +Opts
   59            existing_linked_file/2,     % +FileSpec, -Path
   60            unquote_filespec/2,         % +FileSpec, -Unquoted
   61            doc_tag_title/2,            % +Tag, -Title
   62            mode_anchor_name/2,         % +Mode, -Anchor
   63            pred_anchor_name/3,         % +Head, -PI, -Anchor
   64            private/2,                  % +Obj, +Options
   65            (multifile)/2,              % +Obj, +Options
   66            is_pi/1,                    % @Term
   67            is_op_type/2,               % +Atom, ?Type
   68                                        % Output routines
   69            file//1,                    % +File, //
   70            file//2,                    % +File, +Options, //
   71            include//3,                 % +File, +Type, +Options //
   72            tags//1,                    % +Tags, //
   73            term//3,                    % +Text, +Term, +Bindings, //
   74            file_header//2,             % +File, +Options, //
   75            flagref//1,                 % +Flag
   76            objects//2,                 % +Objects, +Options, //
   77            object_ref//2,              % +Object, +Options, //
   78            object_name//2,             % +Object, +Object
   79            object_href/2,              % +Object, -URL
   80            object_tree//3,             % +Tree, +Current, +Options
   81            object_page//2,             % +Object, +Options, //
   82            object_page_header//2,      % +File, +Options, //
   83            object_synopsis//2,         % +Object, +Options, //
   84            object_footer//2,           % +Object, +Options, //
   85            object_page_footer//2,      % +Object, +Options, //
   86            cite//1                     % +Citations
   87          ]).   88:- use_module(library(lists)).   89:- use_module(library(option)).   90:- use_module(library(uri)).   91:- use_module(library(readutil)).   92:- use_module(library(http/html_write)).   93:- use_module(library(http/http_dispatch)).   94:- use_module(library(http/http_wrapper)).   95:- use_module(library(http/http_path)).   96:- use_module(library(http/html_head)).   97:- use_module(library(http/term_html)).   98:- use_module(library(http/jquery)).   99:- use_module(library(debug)).  100:- use_module(library(apply)).  101:- use_module(library(pairs)).  102:- use_module(library(filesex)).  103:- use_module(doc_process).  104:- use_module(doc_man).  105:- use_module(doc_modes).  106:- use_module(doc_wiki).  107:- use_module(doc_search).  108:- use_module(doc_index).  109:- use_module(doc_util).  110:- use_module(library(solution_sequences)).  111:- use_module(library(error)).  112:- use_module(library(occurs)).  113:- use_module(library(prolog_source)).  114:- use_module(library(prolog_xref)).  115
  116:- 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. */
  128:- public
  129    args//1,                        % Called from \Term output created
  130    pred_dt//3,                     % by the wiki renderer
  131    section//2,
  132    tag//2.  133
  134
  135:- predicate_options(doc_for_wiki_file/2, 2,
  136                     [ edit(boolean)
  137                     ]).  138:- predicate_options(doc_hide_private/3, 3,
  139                     [module(atom), public(list), public_only(boolean)]).  140:- predicate_options(edit_button//2, 2,
  141                     [ edit(boolean)
  142                     ]).  143:- predicate_options(file//2, 2,
  144                     [ label(any),
  145                       absolute_path(atom),
  146                       href(atom),
  147                       map_extension(list),
  148                       files(list),
  149                       edit_handler(atom)
  150                     ]).  151:- predicate_options(file_header//2, 2,
  152                     [ edit(boolean),
  153                       files(list),
  154                       public_only(boolean)
  155                     ]).  156:- predicate_options(include//3, 3,
  157                     [ absolute_path(atom),
  158                       class(atom),
  159                       files(list),
  160                       href(atom),
  161                       label(any),
  162                       map_extension(list)
  163                     ]).  164:- predicate_options(object_edit_button//2, 2,
  165                     [ edit(boolean),
  166                       pass_to(pred_edit_button//2, 2)
  167                     ]).  168:- predicate_options(object_page//2, 2,
  169                     [ for(any),
  170                       header(boolean),
  171                       links(boolean),
  172                       no_manual(boolean),
  173                       try_manual(boolean),
  174                       search_in(oneof([all,app,man])),
  175                       search_match(oneof([name,summary])),
  176                       search_options(boolean)
  177                     ]).  178:- predicate_options(object_ref//2, 2,
  179                     [ files(list),
  180                       qualify(boolean),
  181                       style(oneof([number,title,number_title])),
  182                       secref_style(oneof([number,title,number_title]))
  183                     ]).  184:- predicate_options(object_synopsis//2, 2,
  185                     [ href(atom)
  186                     ]).  187:- predicate_options(pred_dt//3, 3,
  188                     [ edit(boolean)
  189                     ]).  190:- predicate_options(pred_edit_button//2, 2,
  191                     [ edit(boolean)
  192                     ]).  193:- predicate_options(predref//2, 2,
  194                     [ files(list),
  195                       prefer(oneof([manual,app])),
  196                       pass_to(object_ref/4, 2)
  197                     ]).  198:- predicate_options(private/2, 2,
  199                     [ module(atom),
  200                       public(list)
  201                     ]).  202:- predicate_options(source_button//2, 2,
  203                     [ files(list)
  204                     ]).  205
  206
  207                 /*******************************
  208                 *           RESOURCES          *
  209                 *******************************/
  210
  211:- html_resource(pldoc_css,
  212                 [ virtual(true),
  213                   requires([ pldoc_resource('pldoc.css')
  214                            ])
  215                 ]).  216:- html_resource(pldoc_resource('pldoc.js'),
  217                 [ requires([ jquery
  218                            ])
  219                 ]).  220:- html_resource(pldoc_js,
  221                 [ virtual(true),
  222                   requires([ pldoc_resource('pldoc.js')
  223                            ])
  224                 ]).  225:- html_resource(pldoc,
  226                 [ virtual(true),
  227                   requires([ pldoc_css,
  228                              pldoc_js
  229                            ])
  230                 ]).  231
  232
  233                 /*******************************
  234                 *       FILE PROCESSING        *
  235                 *******************************/
 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.
  256doc_for_file(FileSpec, Options) :-
  257    doc_file_objects(FileSpec, File, Objects, FileOptions, Options),
  258    doc_file_title(File, Title, FileOptions, Options),
  259    doc_write_page(
  260        pldoc(file(File, Title)),
  261        title(Title),
  262        \prolog_file(File, Objects, FileOptions, Options),
  263        Options).
  264
  265doc_file_title(_, Title, _, Options) :-
  266    option(title(Title), Options),
  267    !.
  268doc_file_title(File, Title, FileOptions, _) :-
  269    memberchk(file(Title0, _Comment), FileOptions),
  270    !,
  271    file_base_name(File, Base),
  272    atomic_list_concat([Base, ' -- ', Title0], Title).
  273doc_file_title(File, Title, _, _) :-
  274    file_base_name(File, Title).
  275
  276:- html_meta doc_write_page(+, html, html, +).  277
  278doc_write_page(Style, Head, Body, Options) :-
  279    option(files(_), Options),
  280    !,
  281    phrase(page(Style, Head, Body), HTML),
  282    print_html(HTML).
  283doc_write_page(Style, Head, Body, _) :-
  284    reply_html_page(Style, Head, Body).
  285
  286
  287prolog_file(File, Objects, FileOptions, Options) -->
  288    { b_setval(pldoc_file, File),   % TBD: delete?
  289      file_directory_name(File, Dir)
  290    },
  291    html([ \doc_resources(Options),
  292           \doc_links(Dir, FileOptions),
  293           \file_header(File, FileOptions)
  294         | \objects(Objects, FileOptions)
  295         ]),
  296    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 ...
  303doc_resources(Options) -->
  304    { option(resource_directory(ResDir), Options),
  305      nb_current(pldoc_output, OutputFile),
  306      !,
  307      directory_file_path(ResDir, 'pldoc.css', Res),
  308      relative_file_name(Res, OutputFile, Ref)
  309    },
  310    html_requires(Ref).
  311doc_resources(Options) -->
  312    { option(html_resources(Resoures), Options, pldoc)
  313    },
  314    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
  343doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  344    xref_current_source(FileSpec),
  345    xref_option(FileSpec, comments(collect)),
  346    !,
  347    File = FileSpec,
  348    findall(Object, xref_doc_object(File, Object), Objects0),
  349    reply_file_objects(File, Objects0, Objects, FileOptions, Options).
  350doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  351    absolute_file_name(FileSpec, File,
  352                       [ file_type(prolog),
  353                         access(read)
  354                       ]),
  355    source_file(File),
  356    !,
  357    ensure_doc_objects(File),
  358    Pos = File:Line,
  359    findall(Line-doc(Obj,Pos,Comment),
  360            doc_comment(Obj, Pos, _, Comment), Pairs),
  361    sort(Pairs, Pairs1),            % remove duplicates
  362    keysort(Pairs1, ByLine),
  363    pairs_values(ByLine, Objs0),
  364    reply_file_objects(File, Objs0, Objects, FileOptions, Options).
  365doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  366    absolute_file_name(FileSpec, File,
  367                       [ file_type(prolog),
  368                         access(read)
  369                       ]),
  370    xref_source(File, [silent(true)]),
  371    findall(Object, xref_doc_object(File, Object), Objects0),
  372    reply_file_objects(File, Objects0, Objects, FileOptions, Options).
  373
  374
  375reply_file_objects(File, Objs0, Objects, FileOptions, Options) :-
  376    module_info(File, ModuleOptions, Options),
  377    file_info(Objs0, Objs1, FileOptions, ModuleOptions),
  378    doc_hide_private(Objs1, ObjectsSelf, ModuleOptions),
  379    include_reexported(ObjectsSelf, Objects1, File, FileOptions),
  380    remove_doc_duplicates(Objects1, Objects, []).
  381
  382remove_doc_duplicates([], [], _).
  383remove_doc_duplicates([H|T0], [H|T], Seen) :-
  384    H = doc(_, _, Comment),
  385    \+ memberchk(Comment, Seen),
  386    !,
  387    remove_doc_duplicates(T0, T, [Comment|Seen]).
  388remove_doc_duplicates([_|T0], T, Seen) :-
  389    remove_doc_duplicates(T0, T, Seen).
  390
  391include_reexported(SelfObjects, Objects, File, Options) :-
  392    option(include_reexported(true), Options),
  393    option(module(Module), Options),
  394    option(public(Exports), Options),
  395    select_undocumented(Exports, Module, SelfObjects, Undoc),
  396    re_exported_doc(Undoc, File, Module, REObjs, _),
  397    REObjs \== [],
  398    !,
  399    append(SelfObjects, REObjs, Objects).
  400include_reexported(Objects, Objects, _, _).
 xref_doc_object(File, DocObject) is nondet
  405xref_doc_object(File, doc(M:module(Title),File:0,Comment)) :-
  406    xref_comment(File, Title, Comment),
  407    xref_module(File, M).
  408xref_doc_object(File, doc(M:Name/Arity,File:0,Comment)) :-
  409    xref_comment(File, Head, _Summary, Comment),
  410    xref_module(File, Module),
  411    strip_module(Module:Head, M, Plain),
  412    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.
  423:- dynamic
  424    no_comments/2.  425
  426ensure_doc_objects(File) :-
  427    source_file(File),
  428    !,
  429    (   doc_file_has_comments(File)
  430    ->  true
  431    ;   no_comments(File, TimeChecked),
  432        time_file(File, TimeChecked)
  433    ->  true
  434    ;   xref_source(File, [silent(true), comments(store)]),
  435        retractall(no_comments(File, _)),
  436        (   doc_file_has_comments(File)
  437        ->  true
  438        ;   time_file(File, TimeChecked),
  439            assertz(no_comments(File, TimeChecked))
  440        )
  441    ).
  442ensure_doc_objects(File) :-
  443    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.
  450module_info(File, [module(Module), public(Exports)|Options], Options) :-
  451    module_property(Module, file(File)),
  452    !,
  453    module_property(Module, exports(Exports)).
  454module_info(File, [module(Module), public(Exports)|Options], Options) :-
  455    xref_module(File, Module),
  456    !,
  457    findall(PI, xref_exported_pi(File, PI), Exports).
  458module_info(_, Options, Options).
  459
  460xref_exported_pi(Src, Name/Arity) :-
  461    xref_exported(Src, Head),
  462    functor(Head, Name, Arity).
 doc_hide_private(+Objs, +Public, +Options)
Remove the private objects from Objs according to Options.
  468doc_hide_private(Objs, Objs, Options) :-
  469    option(public_only(false), Options, true),
  470    !.
  471doc_hide_private(Objs0, Objs, Options) :-
  472    hide_private(Objs0, Objs, Options).
  473
  474hide_private([], [], _).
  475hide_private([H|T0], T, Options) :-
  476    obj(H, Obj),
  477    private(Obj, Options),
  478    !,
  479    hide_private(T0, T, Options).
  480hide_private([H|T0], [H|T], Options) :-
  481    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.
  489obj(doc(Obj0, _Pos, _Summary), Obj) :-
  490    !,
  491    (   Obj0 = [Obj|_]
  492    ->  true
  493    ;   Obj = Obj0
  494    ).
  495obj(Obj0, Obj) :-
  496    (   Obj0 = [Obj|_]
  497    ->  true
  498    ;   Obj = Obj0
  499    ).
 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.
  508:- multifile
  509    prolog:doc_is_public_object/1.  510
  511private(Object, _Options):-
  512    prolog:doc_is_public_object(Object), !, fail.
  513private(Module:PI, Options) :-
  514    multifile(Module:PI, Options), !, fail.
  515private(Module:PI, Options) :-
  516    option(module(Module), Options),
  517    option(public(Public), Options),
  518    !,
  519    \+ ( member(PI2, Public),
  520         eq_pi(PI, PI2)
  521       ).
  522private(Module:PI, _Options) :-
  523    module_property(Module, file(_)),      % A loaded module
  524    !,
  525    module_property(Module, exports(Exports)),
  526    \+ ( member(PI2, Exports),
  527         eq_pi(PI, PI2)
  528       ).
  529private(Module:PI, _Options) :-
  530    \+ (pi_to_head(PI, Head),
  531        xref_exported(Source, Head),
  532        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.
  543multifile(Obj, _Options) :-
  544    strip_module(user:Obj, Module, PI),
  545    pi_to_head(PI, Head),
  546    (   predicate_property(Module:Head, multifile)
  547    ;   xref_module(Source, Module),
  548        xref_defined(Source, Head, multifile(_))
  549    ),
  550    !.
  551
  552pi_to_head(Var, _) :-
  553    var(Var), !, fail.
  554pi_to_head(Name/Arity, Term) :-
  555    functor(Term, Name, Arity).
  556pi_to_head(Name//DCGArity, Term) :-
  557    Arity is DCGArity+2,
  558    functor(Term, Name, Arity).
 file_info(+Comments, -RestComment, -FileOptions, +OtherOptions) is det
Add options file(Title, Comment) to OtherOptions if available.
  564file_info(Comments, RestComments, [file(Title, Comment)|Opts], Opts) :-
  565    select(doc(_:module(Title),_,Comment), Comments, RestComments),
  566    !.
  567file_info(Comments, Comments, Opts, Opts).
 file_header(+File, +Options)// is det
Create the file header.
  574file_header(File, Options) -->
  575    { memberchk(file(Title, Comment), Options),
  576      !,
  577      file_base_name(File, Base)
  578    },
  579    file_title([Base, ' -- ', Title], File, Options),
  580    { is_structured_comment(Comment, Prefixes),
  581      string_codes(Comment, Codes),
  582      indented_lines(Codes, Prefixes, Lines),
  583      section_comment_header(Lines, _Header, Lines1),
  584      wiki_lines_to_dom(Lines1, [], DOM)
  585    },
  586    html(DOM).
  587file_header(File, Options) -->
  588    { file_base_name(File, Base)
  589    },
  590    file_title([Base], File, Options).
 file_title(+Title:list, +File, +Options)// is det
Emit the file-header and manipulation buttons.
  597file_title(Title, File, Options) -->
  598    prolog:doc_file_title(Title, File, Options),
  599    !.
  600file_title(Title, File, Options) -->
  601    { file_base_name(File, Base)
  602    },
  603    html(h1(class(file),
  604            [ span(style('float:right'),
  605                   [ \reload_button(File, Base, Options),
  606                     \zoom_button(Base, Options),
  607                     \source_button(Base, Options),
  608                     \edit_button(File, Options)
  609                   ])
  610            | Title
  611            ])).
 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.
  621reload_button(File, _Base, Options) -->
  622    { \+ source_file(File),
  623      \+ option(files(_), Options)
  624    },
  625    !,
  626    html(span(class(file_anot), '[not loaded]')).
  627reload_button(_File, Base, Options) -->
  628    { option(edit(true), Options),
  629      !,
  630      option(public_only(Public), Options, true)
  631    },
  632    html(a(href(Base+[reload(true), public_only(Public)]),
  633           img([ class(action),
  634                 alt('Reload'),
  635                 title('Make & Reload'),
  636                 src(location_by_id(pldoc_resource)+'reload.png')
  637               ]))).
  638reload_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.
  646edit_button(File, Options) -->
  647    { option(edit(true), Options)
  648    },
  649    !,
  650    html(a([ onClick('HTTPrequest(\'' +
  651                     location_by_id(pldoc_edit) + [file(File)] +
  652                     '\')')
  653           ],
  654           img([ class(action),
  655                 alt(edit),
  656                 title('Edit file'),
  657                 src(location_by_id(pldoc_resource)+'edit.png')
  658             ]))).
  659edit_button(_, _) -->
  660    [].
 zoom_button(BaseName, +Options)// is det
Add zoom in/out button to show/hide the private documentation.
  667zoom_button(_, Options) -->
  668    { option(files(_Map), Options) },
  669    !.    % generating files
  670zoom_button(Base, Options) -->
  671    {   (   option(public_only(true), Options, true)
  672        ->  Zoom = 'public.png',
  673            Alt = 'Public',
  674            Title = 'Click to include private',
  675            PublicOnly = false
  676        ;   Zoom = 'private.png',
  677            Alt = 'All predicates',
  678            Title = 'Click to show exports only',
  679            PublicOnly = true
  680        )
  681    },
  682    html(a(href(Base+[public_only(PublicOnly)]),
  683           img([ class(action),
  684                 alt(Alt),
  685                 title(Title),
  686                 src(location_by_id(pldoc_resource)+Zoom)
  687               ]))).
 source_button(+File, +Options)// is det
Add show-source button.
  694source_button(_File, Options) -->
  695    { option(files(_Map), Options) },
  696    !.    % generating files
  697source_button(File, _Options) -->
  698    { (   is_absolute_file_name(File)
  699      ->  doc_file_href(File, HREF0)
  700      ;   HREF0 = File
  701      )
  702    },
  703    html(a(href(HREF0+[show(src)]),
  704           img([ class(action),
  705                 alt('Show source'),
  706                 title('Show source'),
  707                 src(location_by_id(pldoc_resource)+'source.png')
  708               ]))).
 objects(+Objects:list, +Options)// is det
Emit the documentation body. Options includes:
navtree(+Boolean)
If true, provide a navitation tree.
  718objects(Objects, Options) -->
  719    { option(navtree(true), Options),
  720      !,
  721      objects_nav_tree(Objects, Tree)
  722    },
  723    html([ div(class(navtree),
  724               div(class(navwindow),
  725                   \nav_tree(Tree, Objects, Options))),
  726           div(class(navcontent),
  727               \objects_nt(Objects, Options))
  728         ]).
  729objects(Objects, Options) -->
  730    objects_nt(Objects, Options).
  731
  732objects_nt(Objects, Options) -->
  733    objects(Objects, [body], Options).
  734
  735objects([], Mode, _) -->
  736    pop_mode(body, Mode, _).
  737objects([Obj|T], Mode, Options) -->
  738    object(Obj, Mode, Mode1, Options),
  739    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.
  750object(doc(Obj,Pos,Comment), Mode0, Mode, Options) -->
  751    !,
  752    object(Obj, [Pos-Comment], Mode0, Mode, [scope(file)|Options]).
  753object(Obj, Mode0, Mode, Options) -->
  754    { findall(Pos-Comment,
  755              doc_comment(Obj, Pos, _Summary, Comment),
  756              Pairs)
  757    },
  758    !,
  759    { b_setval(pldoc_object, Obj) },
  760    object(Obj, Pairs, Mode0, Mode, Options).
  761
  762object(Obj, Pairs, Mode0, Mode, Options) -->
  763    { is_pi(Obj),
  764      !,
  765      maplist(pred_dom(Obj, Options), Pairs, DOMS),
  766      append(DOMS, DOM)
  767    },
  768    need_mode(dl, Mode0, Mode),
  769    html(DOM).
  770object([Obj|_Same], Pairs, Mode0, Mode, Options) -->
  771    !,
  772    object(Obj, Pairs, Mode0, Mode, Options).
  773object(Obj, _Pairs, Mode, Mode, _Options) -->
  774    { debug(pldoc, 'Skipped ~p', [Obj]) },
  775    [].
  776
  777pred_dom(Obj, Options, Pos-Comment, DOM) :-
  778    is_structured_comment(Comment, Prefixes),
  779    string_codes(Comment, Codes),
  780    indented_lines(Codes, Prefixes, Lines),
  781    strip_module(user:Obj, Module, _),
  782    process_modes(Lines, Module, Pos, Modes, Args, Lines1),
  783    (   private(Obj, Options)
  784    ->  Class = privdef             % private definition
  785    ;   multifile(Obj, Options)
  786    ->  (   option(scope(file), Options)
  787        ->  (   more_doc(Obj, Pos)
  788            ->  Class = multidef(object(Obj))
  789            ;   Class = multidef
  790            )
  791        ;   Class = multidef(file((Pos)))
  792        )
  793    ;   Class = pubdef              % public definition
  794    ),
  795    (   Obj = Module:_
  796    ->  POptions = [module(Module)|Options]
  797    ;   POptions = Options
  798    ),
  799    Pos = File:Line,
  800    DTOptions = [file(File),line(Line)|POptions],
  801    DOM = [\pred_dt(Modes, Class, DTOptions), dd(class=defbody, DOM1)],
  802    wiki_lines_to_dom(Lines1, Args, DOM0),
  803    strip_leading_par(DOM0, DOM1).
  804
  805more_doc(Obj, File:_) :-
  806    doc_comment(Obj, File2:_, _, _),
  807    File2 \== File,
  808    !.
 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.
  817need_mode(Mode, Stack, Stack) -->
  818    { Stack = [Mode|_] },
  819    !,
  820    [].
  821need_mode(Mode, Stack, Rest) -->
  822    { memberchk(Mode, Stack)
  823    },
  824    !,
  825    pop_mode(Mode, Stack, Rest).
  826need_mode(Mode, Stack, [Mode|Stack]) -->
  827    !,
  828    html_begin(Mode).
  829
  830pop_mode(Mode, Stack, Stack) -->
  831    { Stack = [Mode|_] },
  832    !,
  833    [].
  834pop_mode(Mode, [H|Rest0], Rest) -->
  835    html_end(H),
  836    pop_mode(Mode, Rest0, Rest).
 undocumented(+File, +Objects, +Options)// is det
Describe undocumented predicates if the file is a module file.
  842undocumented(File, Objs, Options) -->
  843    { memberchk(module(Module), Options),
  844      memberchk(public(Exports), Options),
  845      select_undocumented(Exports, Module, Objs, Undoc),
  846      re_exported_doc(Undoc, File, Module, REObjs, ReallyUnDoc)
  847    },
  848    !,
  849    re_exported_doc(REObjs, Options),
  850    undocumented(ReallyUnDoc, Options).
  851undocumented(_, _, _) -->
  852    [].
  853
  854re_exported_doc([], _) --> !.
  855re_exported_doc(Objs, Options) -->
  856    reexport_header(Objs, Options),
  857    objects(Objs, Options).
  858
  859reexport_header(_, Options) -->
  860    { option(reexport_header(true), Options, true)
  861    },
  862    !,
  863    html([ h2(class(wiki), 'Re-exported predicates'),
  864           p([ 'The following predicates are re-exported from other ',
  865               'modules'
  866             ])
  867         ]).
  868reexport_header(_, _) -->
  869    [].
  870
  871undocumented([], _) --> !.
  872undocumented(UnDoc, Options) -->
  873    html([ h2(class(undoc), 'Undocumented predicates'),
  874           p(['The following predicates are exported, but not ',
  875              'or incorrectly documented.'
  876             ]),
  877           dl(class(undoc),
  878              \undocumented_predicates(UnDoc, Options))
  879         ]).
  880
  881
  882undocumented_predicates([], _) -->
  883    [].
  884undocumented_predicates([H|T], Options) -->
  885    undocumented_pred(H, Options),
  886    undocumented_predicates(T, Options).
  887
  888undocumented_pred(Name/Arity, Options) -->
  889    { functor(Head, Name, Arity) },
  890    html(dt(class=undoc, \pred_mode(Head, [], _, Options))).
  891
  892select_undocumented([], _, _, []).
  893select_undocumented([PI|T0], M, Objs, [PI|T]) :-
  894    is_pi(PI),
  895    \+ in_doc(M:PI, Objs),
  896    !,
  897    select_undocumented(T0, M, Objs, T).
  898select_undocumented([_|T0], M, Objs, T) :-
  899    select_undocumented(T0, M, Objs, T).
  900
  901in_doc(PI, Objs) :-
  902    member(doc(O,_,_), Objs),
  903    (   is_list(O)
  904    ->  member(O2, O),
  905        eq_pi(PI, O2)
  906    ;   eq_pi(PI, O)
  907    ).
 eq_pi(PI1, PI2) is semidet
True if PI1 and PI2 refer to the same predicate.
  914eq_pi(PI, PI) :- !.
  915eq_pi(M:PI1, M:PI2) :-
  916    atom(M),
  917    !,
  918    eq_pi(PI1, PI2).
  919eq_pi(Name/A, Name//DCGA) :-
  920    A =:= DCGA+2,
  921    !.
  922eq_pi(Name//DCGA, Name/A) :-
  923    A =:= DCGA+2.
 is_pi(@Term) is semidet
True if Term is a predicate indicator.
  929is_pi(Var) :-
  930    var(Var),
  931    !,
  932    fail.
  933is_pi(_:PI) :-
  934    !,
  935    is_pi(PI).
  936is_pi(_/_).
  937is_pi(_//_).
 re_exported_doc(+Undoc:list(pi), +File:atom, +Module:atom, -ImportedDoc, -ReallyUnDoc:list(pi))
  943re_exported_doc([], _, _, [], []).
  944re_exported_doc([PI|T0], File, Module, [doc(Orig:PI,Pos,Comment)|ObjT], UnDoc) :-
  945    pi_to_head(PI, Head),
  946    (   predicate_property(Module:Head, imported_from(Orig))
  947    ->  true
  948    ;   xref_defined(File, Head, imported(File2)),
  949        ensure_doc_objects(File2),
  950        xref_module(File2, Orig)
  951    ),
  952    doc_comment(Orig:PI, Pos, _, Comment),
  953    !,
  954    re_exported_doc(T0, File, Module, ObjT, UnDoc).
  955re_exported_doc([PI|T0], File, Module, REObj, [PI|UnDoc]) :-
  956    re_exported_doc(T0, File, Module, REObj, UnDoc).
  957
  958
  959                 /*******************************
  960                 *      SINGLE OBJECT PAGE      *
  961                 *******************************/
 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.
  971object_page(Obj, Options) -->
  972    prolog:doc_object_page(Obj, Options),
  973    !,
  974    object_page_footer(Obj, Options).
  975object_page(Obj, Options) -->
  976    { doc_comment(Obj, File:_Line, _Summary, _Comment)
  977    },
  978    !,
  979    (   { \+ ( doc_comment(Obj, File2:_, _, _),
  980               File2 \== File )
  981        }
  982    ->  html([ \html_requires(pldoc),
  983               \object_page_header(File, Options),
  984               \object_synopsis(Obj, []),
  985               \objects([Obj], Options)
  986             ])
  987    ;   html([ \html_requires(pldoc),
  988               \object_page_header(-, Options),
  989               \objects([Obj], [synopsis(true)|Options])
  990             ])
  991    ),
  992    object_page_footer(Obj, Options).
  993object_page(M:Name/Arity, Options) -->          % specified module, but public
  994    { functor(Head, Name, Arity),
  995      (   predicate_property(M:Head, exported)
  996      ->  module_property(M, class(library))
  997      ;   \+ predicate_property(M:Head, defined)
  998      )
  999    },
 1000    prolog:doc_object_page(Name/Arity, Options),
 1001    !,
 1002    object_page_footer(Name/Arity, Options).
 1003
 1004object_page_header(File, Options) -->
 1005    prolog:doc_page_header(file(File), Options),
 1006    !.
 1007object_page_header(File, Options) -->
 1008    { option(header(true), Options, true) },
 1009    !,
 1010    html(div(class(navhdr),
 1011             [ div(class(jump), \file_link(File)),
 1012               div(class(search), \search_form(Options)),
 1013               br(clear(right))
 1014             ])).
 1015object_page_header(_, _) --> [].
 1016
 1017file_link(-) -->
 1018    !,
 1019    places_menu(-).
 1020file_link(File) -->
 1021    { file_directory_name(File, Dir)
 1022    },
 1023    places_menu(Dir),
 1024    html([ div(a(href(location_by_id(pldoc_doc)+File), File))
 1025         ]).
 object_footer(+Obj, +Options)// is det
Call the hook doc_object_footer//2. This hook will be used to deal with examples.
 1032object_footer(Obj, Options) -->
 1033    prolog:doc_object_footer(Obj, Options),
 1034    !.
 1035object_footer(_, _) --> [].
 object_page_footer(+Obj, +Options)// is det
Call the hook doc_object_page_footer//2. This hook will be used to deal with annotations.
 1043object_page_footer(Obj, Options) -->
 1044    prolog:doc_object_page_footer(Obj, Options),
 1045    !.
 1046object_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.
 1060object_synopsis(Name/Arity, _) -->
 1061    { functor(Head, Name, Arity),
 1062      predicate_property(system:Head, built_in)
 1063    },
 1064    synopsis([span(class(builtin), 'built-in')]).
 1065object_synopsis(Name/Arity, Options) -->
 1066    !,
 1067    object_synopsis(_:Name/Arity, Options).
 1068object_synopsis(M:Name/Arity, Options) -->
 1069    { functor(Head, Name, Arity),
 1070      (   option(source(Spec), Options)
 1071      ->  absolute_file_name(Spec, File,
 1072                             [ access(read),
 1073                               file_type(prolog),
 1074                               file_errors(fail)
 1075                             ])
 1076      ;   predicate_property(M:Head, exported),
 1077          \+ predicate_property(M:Head, imported_from(_)),
 1078          module_property(M, file(File)),
 1079          file_name_on_path(File, Spec)
 1080      ),
 1081      !,
 1082      unquote_filespec(Spec, Unquoted),
 1083      (   predicate_property(Head, autoload(FileBase)),
 1084          file_name_extension(FileBase, _Ext, File)
 1085      ->  Extra = [span(class(autoload), '(can be autoloaded)')]
 1086      ;   Extra = []
 1087      )
 1088    },
 1089    (   { option(href(HREF), Options) }
 1090    ->  synopsis([code([':- use_module(',a(href(HREF), '~q'-[Unquoted]),').'])|Extra])
 1091    ;   synopsis([code(':- use_module(~q).'-[Unquoted])|Extra])
 1092    ).
 1093object_synopsis(Name//Arity, Options) -->
 1094    !,
 1095    { DCGArity is Arity+2 },
 1096    object_synopsis(Name/DCGArity, Options).
 1097object_synopsis(Module:Name//Arity, Options) -->
 1098    !,
 1099    { DCGArity is Arity+2 },
 1100    object_synopsis(Module:Name/DCGArity, Options).
 1101object_synopsis(f(_/_), _) -->
 1102    synopsis(span(class(function),
 1103                  [ 'Arithmetic function (see ',
 1104                    \object_ref(is/2, []),
 1105                    ')'
 1106                  ])).
 1107object_synopsis(c(Func), _) -->
 1108    { sub_atom(Func, 0, _, _, 'PL_')
 1109    },
 1110    !,
 1111    synopsis([span(class(cfunc), 'C-language interface function')]).
 1112object_synopsis(_, _) --> [].
 1113
 1114synopsis(Text) -->
 1115    html(div(class(synopsis),
 1116             [ span(class('synopsis-hdr'), 'Availability:')
 1117             | Text
 1118             ])).
 unquote_filespec(+Spec, -Unquoted) is det
Translate e.g. library('semweb/rdf_db') into library(semweb/rdf_db).
 1125unquote_filespec(Spec, Unquoted) :-
 1126    compound(Spec),
 1127    Spec =.. [Alias,Path],
 1128    atom(Path),
 1129    atomic_list_concat(Parts, /, Path),
 1130    maplist(need_no_quotes, Parts),
 1131    !,
 1132    parts_to_path(Parts, UnquotedPath),
 1133    Unquoted =.. [Alias, UnquotedPath].
 1134unquote_filespec(Spec, Spec).
 1135
 1136need_no_quotes(Atom) :-
 1137    format(atom(A), '~q', [Atom]),
 1138    \+ sub_atom(A, 0, _, _, '\'').
 1139
 1140parts_to_path([One], One) :- !.
 1141parts_to_path(List, More/T) :-
 1142    (   append(H, [T], List)
 1143    ->  parts_to_path(H, More)
 1144    ).
 1145
 1146
 1147                 /*******************************
 1148                 *             PRINT            *
 1149                 *******************************/
 doc_write_html(+Out:stream, +Title:atomic, +DOM) is det
Write HTML for the documentation page DOM using Title to Out.
 1155doc_write_html(Out, Title, Doc) :-
 1156    doc_page_dom(Title, Doc, DOM),
 1157    phrase(html(DOM), Tokens),
 1158    print_html_head(Out),
 1159    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.
 1166doc_page_dom(Title, Body, DOM) :-
 1167    DOM = html([ head([ title(Title),
 1168                        link([ rel(stylesheet),
 1169                               type('text/css'),
 1170                               href(location_by_id(pldoc_resource)+'pldoc.css')
 1171                             ]),
 1172                        script([ src(location_by_id(pldoc_resource)+'pldoc.js'),
 1173                                 type('text/javascript')
 1174                               ], [])
 1175                      ]),
 1176                 body(Body)
 1177               ]).
 print_html_head(+Out:stream) is det
Print the DOCTYPE line.
 1183print_html_head(Out) :-
 1184    format(Out,
 1185           '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" \c
 1186               "http://www.w3.org/TR/html4/strict.dtd">~n', []).
 1187
 1188% Rendering rules
 1189%
 1190% 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.
 1198tags(Tags) -->
 1199    html(dl(class=tags, Tags)).
 tag(+Tag, +Values:list)// is det
Called from \tag(Name, Values) terms produced by doc_wiki.pl.
 1205tag(Tag, Values) -->
 1206    {   doc_tag_title(Tag, Title),
 1207        atom_concat('keyword-', Tag, Class)
 1208    },
 1209    html([ dt(class=Class, Title),
 1210           \tag_values(Values, Class)
 1211         ]).
 1212
 1213tag_values([], _) -->
 1214    [].
 1215tag_values([H|T], Class) -->
 1216    html(dd(class=Class, ['- '|H])),
 1217    tag_values(T, Class).
 doc_tag_title(+Tag, -Title) is det
Title is the name to use for Tag in the generated documentation.
 1224doc_tag_title(Tag, Title) :-
 1225    tag_title(Tag, Title),
 1226    !.
 1227doc_tag_title(Tag, Tag).
 1228
 1229tag_title(compat, 'Compatibility').
 1230tag_title(tbd,    'To be done').
 1231tag_title(see,    'See also').
 1232tag_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).
 1239args(Params) -->
 1240    html([ dt(class=tag, 'Arguments:'),
 1241           dd(table(class=arglist,
 1242                    \arg_list(Params)))
 1243         ]).
 1244
 1245arg_list([]) -->
 1246    [].
 1247arg_list([H|T]) -->
 1248    argument(H),
 1249    arg_list(T).
 1250
 1251argument(arg(Name,Descr)) -->
 1252    html(tr([td(var(Name)), td(class=argdescr, ['- '|Descr])])).
 1253
 1254
 1255                 /*******************************
 1256                 *         NAVIGATION TREE      *
 1257                 *******************************/
 objects_nav_tree(+Objects, -Tree) is det
Provide a navigation tree showing the context of Object. Tree is of the form node(Object, Children).
 1264objects_nav_tree(Objects, Tree) :-
 1265    maplist(object_nav_tree, Objects, Trees),
 1266    union_trees(Trees, Tree0),
 1267    remove_unique_root(Tree0, Tree).
 1268
 1269object_nav_tree(Obj, Tree) :-
 1270    Node = node(directory(Dir), FileNodes),
 1271    FileNode = node(file(File), Siblings),
 1272    doc_comment(Obj, File:_Line, _Summary, _Comment),
 1273    !,
 1274    file_directory_name(File, Dir),
 1275    sibling_file_nodes(Dir, FileNodes0),
 1276    selectchk(node(file(File),[]), FileNodes0, FileNode, FileNodes),
 1277    findall(Sibling, doc_comment(Sibling, File:_, _, _), Siblings0),
 1278    delete(Siblings0, _:module(_), Siblings1),
 1279    doc_hide_private(Siblings1, Siblings2, []),
 1280    flatten(Siblings2, Siblings),   % a comment may describe objects
 1281    embed_directories(Node, Tree).
 1282
 1283sibling_file_nodes(Dir, Nodes) :-
 1284    findall(node(file(File), []),
 1285            (   source_file(File),
 1286                file_directory_name(File, Dir)
 1287            ),
 1288            Nodes).
 1289
 1290embed_directories(Node, Tree) :-
 1291    Node = node(file(File), _),
 1292    !,
 1293    file_directory_name(File, Dir),
 1294    Super = node(directory(Dir), [Node]),
 1295    embed_directories(Super, Tree).
 1296embed_directories(Node, Tree) :-
 1297    Node = node(directory(Dir), _),
 1298    file_directory_name(Dir, SuperDir),
 1299    SuperDir \== Dir,
 1300    !,
 1301    Super = node(directory(SuperDir), [Node]),
 1302    embed_directories(Super, Tree).
 1303embed_directories(Tree, Tree).
 1304
 1305
 1306union_trees([Tree], Tree) :- !.
 1307union_trees([T1,T2|Trees], Tree) :-
 1308    merge_trees(T1, T2, M1),
 1309    union_trees([M1|Trees], Tree).
 1310
 1311merge_trees(node(R, Ch1), node(R, Ch2), node(R, Ch)) :-
 1312    merge_nodes(Ch1, Ch2, Ch).
 1313
 1314merge_nodes([], Ch, Ch) :- !.
 1315merge_nodes(Ch, [], Ch) :- !.
 1316merge_nodes([node(Root, Ch1)|T1], N1, [T1|Nodes]) :-
 1317    selectchk(node(Root, Ch2), N1, N2),
 1318    !,
 1319    merge_trees(node(Root, Ch1), node(Root, Ch2), T1),
 1320    merge_nodes(T1, N2, Nodes).
 1321merge_nodes([Node|T1], N1, [Node|Nodes]) :-
 1322    merge_nodes(T1, N1, Nodes).
 remove_unique_root(+TreeIn, -Tree)
Remove the root part that does not branch
 1328remove_unique_root(node(_, [node(R1, [R2])]), Tree) :-
 1329    !,
 1330    remove_unique_root(node(R1, [R2]), Tree).
 1331remove_unique_root(Tree, Tree).
 nav_tree(+Tree, +Current, +Options)// is det
Render the navigation tree
 1337nav_tree(Tree, Current, Options) -->
 1338    html(ul(class(nav),
 1339            \object_tree(Tree, Current, Options))).
 object_tree(+Tree, +Current, +Options)// is det
Render a tree of objects used for navigation.
 1345object_tree(node(Id, []), Target, Options) -->
 1346    !,
 1347    { node_class(Id, Target, Class) },
 1348    html(li(class(Class),
 1349            \node(Id, Options))).
 1350object_tree(node(Id, Children), Target, Options) -->
 1351    !,
 1352    { node_class(Id, Target, Class) },
 1353    html(li(class(Class),
 1354            [ \node(Id, Options),
 1355              ul(class(nav),
 1356                 \object_trees(Children, Target, Options))
 1357            ])).
 1358object_tree(Id, Target, Options) -->
 1359    !,
 1360    { node_class(Id, Target, Class) },
 1361    html(li(class([obj|Class]), \node(Id, Options))).
 1362
 1363object_trees([], _, _) --> [].
 1364object_trees([H|T], Target, Options) -->
 1365    object_tree(H, Target, Options),
 1366    object_trees(T, Target, Options).
 1367
 1368node_class(Ids, Current, Class) :-
 1369    is_list(Ids),
 1370    !,
 1371    (   member(Id, Ids), memberchk(Id, Current)
 1372    ->  Class = [nav,current]
 1373    ;   Class = [nav]
 1374    ).
 1375node_class(Id, Current, Class) :-
 1376    (   memberchk(Id, Current)
 1377    ->  Class = [nav,current]
 1378    ;   Class = [nav]
 1379    ).
 1380
 1381node(file(File), Options) -->
 1382    !,
 1383    object_ref(file(File), [style(title)|Options]).
 1384node(Id, Options) -->
 1385    object_ref(Id, Options).
 1386
 1387
 1388                 /*******************************
 1389                 *            SECTIONS          *
 1390                 *******************************/
 1391
 1392section(Type, Title) -->
 1393    { string_codes(Title, Codes),
 1394      wiki_codes_to_dom(Codes, [], Content0),
 1395      strip_leading_par(Content0, Content),
 1396      make_section(Type, Content, HTML)
 1397    },
 1398    html(HTML).
 1399
 1400make_section(module,  Title, h1(class=module,  Title)).
 1401make_section(section, Title, h1(class=section, Title)).
 1402
 1403
 1404                 /*******************************
 1405                 *       PRED MODE HEADER       *
 1406                 *******************************/
 pred_dt(+Modes, +Class, Options)// is det
Emit the predicate header.
Arguments:
Modes- List as returned by process_modes/5.
 1414pred_dt(Modes, Class, Options) -->
 1415    pred_dt(Modes, Class, [], _Done, Options).
 1416
 1417pred_dt([], _, Done, Done, _) -->
 1418    [].
 1419pred_dt([H|T], Class, Done0, Done, Options) -->
 1420    { functor(Class, CSSClass, _) },
 1421    html(dt(class=CSSClass,
 1422            [ \pred_mode(H, Done0, Done1, Options),
 1423              \mode_anot(Class)
 1424            ])),
 1425    pred_dt(T, Class, Done1, Done, Options).
 1426
 1427mode_anot(privdef) -->
 1428    !,
 1429    html(span([class(anot), style('float:right')],
 1430              '[private]')).
 1431mode_anot(multidef(object(Obj))) -->
 1432    !,
 1433    { object_href(Obj, HREF) },
 1434    html(span([class(anot), style('float:right')],
 1435              ['[', a(href(HREF), multifile), ']'
 1436              ])).
 1437mode_anot(multidef(file(File:_))) -->
 1438    !,
 1439    { file_name_on_path(File, Spec),
 1440      unquote_filespec(Spec, Unquoted),
 1441      doc_file_href(File, HREF)
 1442    },
 1443    html(span([class(anot), style('float:right')],
 1444              ['[multifile, ', a(href(HREF), '~q'-[Unquoted]), ']'
 1445              ])).
 1446mode_anot(multidef) -->
 1447    !,
 1448    html(span([class(anot), style('float:right')],
 1449              '[multifile]')).
 1450mode_anot(_) -->
 1451    [].
 1452
 1453pred_mode(mode(Head,Vars), Done0, Done, Options) -->
 1454    !,
 1455    { bind_vars(Head, Vars) },
 1456    pred_mode(Head, Done0, Done, Options).
 1457pred_mode(Head is Det, Done0, Done, Options) -->
 1458    !,
 1459    anchored_pred_head(Head, Done0, Done, Options),
 1460    pred_det(Det).
 1461pred_mode(Head, Done0, Done, Options) -->
 1462    anchored_pred_head(Head, Done0, Done, Options).
 1463
 1464bind_vars(Term, Bindings) :-
 1465    bind_vars(Bindings),
 1466    anon_vars(Term).
 1467
 1468bind_vars([]).
 1469bind_vars([Name=Var|T]) :-
 1470    Var = '$VAR'(Name),
 1471    bind_vars(T).
 anon_vars(+Term) is det
Bind remaining variables in Term to '$VAR'('_'), so they are printed as '_'.
 1478anon_vars(Var) :-
 1479    var(Var),
 1480    !,
 1481    Var = '$VAR'('_').
 1482anon_vars(Term) :-
 1483    compound(Term),
 1484    !,
 1485    Term =.. [_|Args],
 1486    maplist(anon_vars, Args).
 1487anon_vars(_).
 1488
 1489
 1490anchored_pred_head(Head, Done0, Done, Options) -->
 1491    { pred_anchor_name(Head, PI, Name) },
 1492    (   { memberchk(PI, Done0) }
 1493    ->  { Done = Done0 },
 1494        pred_head(Head)
 1495    ;   html([ span(style('float:right'),
 1496                    [ \pred_edit_or_source_button(Head, Options),
 1497                      &(nbsp)
 1498                    ]),
 1499               a(name=Name, \pred_head(Head))
 1500             ]),
 1501        { Done = [PI|Done0] }
 1502    ).
 1503
 1504
 1505pred_edit_or_source_button(Head, Options) -->
 1506    { option(edit(true), Options) },
 1507    !,
 1508    pred_edit_button(Head, Options).
 1509pred_edit_or_source_button(Head, Options) -->
 1510    { option(source_link(true), Options) },
 1511    !,
 1512    pred_source_button(Head, Options).
 1513pred_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)
 1527pred_edit_button(_, Options) -->
 1528    { \+ option(edit(true), Options) },
 1529    !.
 1530pred_edit_button(PI0, Options0) -->
 1531    { canonicalise_predref(PI0, PI, Options0, Options) },
 1532    pred_edit_button2(PI, Options).
 1533
 1534pred_edit_button2(Name/Arity, Options) -->
 1535    { \+ ( memberchk(file(_), Options), % always edit if file and line
 1536           memberchk(line(_), Options)  % are given.
 1537         ),
 1538      functor(Head, Name, Arity),
 1539      option(module(M), Options, _),
 1540      \+ ( current_module(M),
 1541           source_file(M:Head, _File)
 1542         )
 1543    },
 1544    !.
 1545pred_edit_button2(Name/Arity, Options) -->
 1546    { include(edit_param, Options, Extra),
 1547      http_link_to_id(pldoc_edit,
 1548                      [name(Name),arity(Arity)|Extra],
 1549                      EditHREF)
 1550    },
 1551    html(a(onClick('HTTPrequest(\'' + EditHREF + '\')'),
 1552           img([ class(action),
 1553                 alt('Edit predicate'),
 1554                 title('Edit predicate'),
 1555                 src(location_by_id(pldoc_resource)+'editpred.png')
 1556               ]))).
 1557pred_edit_button2(_, _) -->
 1558    !,
 1559    [].
 1560
 1561edit_param(module(_)).
 1562edit_param(file(_)).
 1563edit_param(line(_)).
 object_edit_button(+Object, +Options)// is det
Create a button for editing Object.
 1570object_edit_button(_, Options) -->
 1571    { \+ option(edit(true), Options) },
 1572    !.
 1573object_edit_button(PI, Options) -->
 1574    { is_pi(PI) },
 1575    !,
 1576    pred_edit_button(PI, Options).
 1577object_edit_button(_, _) -->
 1578    [].
 pred_source_button(+PredIndicator, +Options)// is det
Create a button for viewing the source of a predicate.
 1585pred_source_button(PI0, Options0) -->
 1586    { canonicalise_predref(PI0, PI, Options0, Options),
 1587      option(module(M), Options, _),
 1588      pred_source_href(PI, M, HREF), !
 1589    },
 1590    html(a([ href(HREF)
 1591           ],
 1592           img([ class(action),
 1593                 alt('Source'),
 1594                 title('Show source'),
 1595                 src(location_by_id(pldoc_resource)+'source.png')
 1596               ]))).
 1597pred_source_button(_, _) -->
 1598    [].
 object_source_button(+Object, +Options)// is det
Create a button for showing the source of Object.
 1605object_source_button(PI, Options) -->
 1606    { is_pi(PI),
 1607      option(source_link(true), Options, true)
 1608    },
 1609    !,
 1610    pred_source_button(PI, Options).
 1611object_source_button(_, _) -->
 1612    [].
 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.
 1620canonicalise_predref(M:PI0, PI, Options0, [module(M)|Options]) :-
 1621    !,
 1622    canonicalise_predref(PI0, PI, Options0, Options).
 1623canonicalise_predref(//(Head), PI, Options0, Options) :-
 1624    !,
 1625    functor(Head, Name, Arity),
 1626    PredArity is Arity + 2,
 1627    canonicalise_predref(Name/PredArity, PI, Options0, Options).
 1628canonicalise_predref(Name//Arity, PI, Options0, Options) :-
 1629    integer(Arity), Arity >= 0,
 1630    !,
 1631    PredArity is Arity + 2,
 1632    canonicalise_predref(Name/PredArity, PI, Options0, Options).
 1633canonicalise_predref(PI, PI, Options, Options) :-
 1634    PI = Name/Arity,
 1635    atom(Name), integer(Arity), Arity >= 0,
 1636    !.
 1637canonicalise_predref(Head, PI, Options0, Options) :-
 1638    functor(Head, Name, Arity),
 1639    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.
 1647pred_head(Var) -->
 1648    { var(Var),
 1649      !,
 1650      instantiation_error(Var)
 1651    }.
 1652pred_head(//(Head)) -->
 1653    !,
 1654    pred_head(Head),
 1655    html(//).
 1656pred_head(M:Head) -->
 1657    html([span(class=module, M), :]),
 1658    pred_head(Head).
 1659pred_head(Head) -->
 1660    { atom(Head) },
 1661    !,
 1662    html(b(class=pred, Head)).
 1663pred_head(Head) -->                     % Infix operators
 1664    { Head =.. [Functor,Left,Right],
 1665      is_op_type(Functor, infix)
 1666    },
 1667    !,
 1668    html([ var(class=arglist, \pred_arg(Left, 1)),
 1669           ' ', b(class=pred, Functor), ' ',
 1670           var(class=arglist, \pred_arg(Right, 2))
 1671         ]).
 1672pred_head(Head) -->                     % Prefix operators
 1673    { Head =.. [Functor,Arg],
 1674      is_op_type(Functor, prefix)
 1675    },
 1676    !,
 1677    html([ b(class=pred, Functor), ' ',
 1678           var(class=arglist, \pred_arg(Arg, 1))
 1679         ]).
 1680pred_head(Head) -->                     % Postfix operators
 1681    { Head =.. [Functor,Arg],
 1682      is_op_type(Functor, postfix)
 1683    },
 1684    !,
 1685    html([ var(class=arglist, \pred_arg(Arg, 1)),
 1686           ' ', b(class=pred, Functor)
 1687         ]).
 1688pred_head({Head}) -->
 1689    !,
 1690    html([ b(class=pred, '{'),
 1691           var(class=arglist,
 1692               \pred_args([Head], 1)),
 1693           b(class=pred, '}')
 1694         ]).
 1695pred_head(Head) -->                     % Plain terms
 1696    { Head =.. [Functor|Args] },
 1697    html([ b(class=pred, Functor),
 1698           var(class=arglist,
 1699               [ '(', \pred_args(Args, 1), ')' ])
 1700         ]).
 is_op_type(+Atom, ?Type)
True if Atom is an operator of Type. Type is one of prefix, infix or postfix.
 1707is_op_type(Functor, Type) :-
 1708    current_op(_Pri, F, Functor),
 1709    op_type(F, Type).
 1710
 1711op_type(fx,  prefix).
 1712op_type(fy,  prefix).
 1713op_type(xf,  postfix).
 1714op_type(yf,  postfix).
 1715op_type(xfx, infix).
 1716op_type(xfy, infix).
 1717op_type(yfx, infix).
 1718op_type(yfy, infix).
 1719
 1720
 1721pred_args([], _) -->
 1722    [].
 1723pred_args([H|T], I) -->
 1724    pred_arg(H, I),
 1725    (   {T==[]}
 1726    ->  []
 1727    ;   html(', '),
 1728        { I2 is I + 1 },
 1729        pred_args(T, I2)
 1730    ).
 1731
 1732pred_arg(Var, I) -->
 1733    { var(Var) },
 1734    !,
 1735    html(['Arg', I]).
 1736pred_arg(...(Term), I) -->
 1737    !,
 1738    pred_arg(Term, I),
 1739    html('...').
 1740pred_arg(Term, I) -->
 1741    { Term =.. [Ind,Arg],
 1742      mode_indicator(Ind)
 1743    },
 1744    !,
 1745    html([Ind, \pred_arg(Arg, I)]).
 1746pred_arg(Arg:Type, _) -->
 1747    !,
 1748    html([\argname(Arg), :, \argtype(Type)]).
 1749pred_arg(Arg, _) -->
 1750    argname(Arg).
 1751
 1752argname('$VAR'(Name)) -->
 1753    !,
 1754    html(Name).
 1755argname(Name) -->
 1756    !,
 1757    html(Name).
 1758
 1759argtype(Term) -->
 1760    { format(string(S), '~W',
 1761             [ Term,
 1762               [ quoted(true),
 1763                 numbervars(true)
 1764               ]
 1765             ]) },
 1766    html(S).
 1767
 1768pred_det(unknown) -->
 1769    [].
 1770pred_det(Det) -->
 1771    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
 1780term(_, Atom, []) -->
 1781    { atomic(Atom),
 1782      !,
 1783      format(string(S), '~W', [Atom,[quoted(true)]])
 1784    },
 1785    html(span(class=functor, S)).
 1786term(_, Key:Type, [TypeName=Type]) -->
 1787    { atomic(Key)
 1788    },
 1789    !,
 1790    html([span(class='pl-key', Key), :, span(class('pl-var'), TypeName)]).
 1791term(_, Term, Bindings) -->
 1792    { is_mode(Term is det),         % HACK. Bit too strict?
 1793      bind_vars(Bindings)
 1794    },
 1795    !,
 1796    pred_head(Term).
 1797term(_, Term, Bindings) -->
 1798    term(Term,
 1799         [ variable_names(Bindings),
 1800           quoued(true)
 1801         ]).
 1802
 1803
 1804                 /*******************************
 1805                 *             PREDREF          *
 1806                 *******************************/
 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.

 1819predref(Term) -->
 1820    { catch(nb_getval(pldoc_options, Options), _, Options = []) },
 1821    predref(Term, Options).
 1822
 1823predref(Obj, Options) -->
 1824    { Obj = _:_,
 1825      doc_comment(Obj, File:_Line, _, _),
 1826      (   (   option(files(Map), Options)
 1827          ->  memberchk(file(File,_), Map)
 1828          ;   true
 1829          )
 1830      ->  object_href(Obj, HREF, Options)
 1831      ;   manref(Obj, HREF, Options)
 1832      )
 1833    },
 1834    !,
 1835    html(a(href(HREF), \object_name(Obj, [qualify(true)|Options]))).
 1836predref(M:Term, Options) -->
 1837    !,
 1838    predref(Term, M, Options).
 1839predref(Term, Options) -->
 1840    predref(Term, _, Options).
 1841
 1842predref(Name/Arity, _, Options) -->             % Builtin; cannot be overruled
 1843    { prolog:doc_object_summary(Name/Arity, manual, _, _),
 1844      !,
 1845      manref(Name/Arity, HREF, Options)
 1846    },
 1847    html(a([class=builtin, href=HREF], [Name, /, Arity])).
 1848predref(Name/Arity, _, Options) -->             % From packages
 1849    { option(prefer(manual), Options),
 1850      prolog:doc_object_summary(Name/Arity, Category, _, _),
 1851      !,
 1852      manref(Name/Arity, HREF, Options)
 1853    },
 1854    html(a([class=Category, href=HREF], [Name, /, Arity])).
 1855predref(Obj, Module, Options) -->               % Local
 1856    { doc_comment(Module:Obj, File:_Line, _, _),
 1857      (   option(files(Map), Options)
 1858      ->  memberchk(file(File,_), Map)
 1859      ;   true
 1860      )
 1861    },
 1862    !,
 1863    object_ref(Module:Obj, Options).
 1864predref(Name/Arity, Module, Options) -->
 1865    { \+ option(files(_), Options),
 1866      pred_href(Name/Arity, Module, HREF)
 1867    },
 1868    !,
 1869    html(a(href=HREF, [Name, /, Arity])).
 1870predref(Name//Arity, Module, Options) -->
 1871    { \+ option(files(_), Options),
 1872      PredArity is Arity + 2,
 1873      pred_href(Name/PredArity, Module, HREF)
 1874    },
 1875    !,
 1876    html(a(href=HREF, [Name, //, Arity])).
 1877predref(PI, _, Options) -->             % From packages
 1878    { canonical_pi(PI, CPI, HTML),
 1879      (   option(files(_), Options)
 1880      ->  Category = extmanual
 1881      ;   prolog:doc_object_summary(CPI, Category, _, _)
 1882      ),
 1883      manref(CPI, HREF, Options)
 1884    },
 1885    html(a([class=Category, href=HREF], HTML)).
 1886predref(PI, _, _Options) -->
 1887    { canonical_pi(PI, _CPI, HTML)
 1888    },
 1889    !,
 1890    html(span(class=undef, HTML)).
 1891predref(Callable, Module, Options) -->
 1892    { callable(Callable),
 1893      functor(Callable, Name, Arity)
 1894    },
 1895    predref(Name/Arity, Module, Options).
 1896
 1897canonical_pi(Name/Arity, Name/Arity, [Name, /, Arity]) :-
 1898    atom(Name), integer(Arity),
 1899    !.
 1900canonical_pi(Name//Arity, Name/Arity2, [Name, //, Arity]) :-
 1901    atom(Name), integer(Arity),
 1902    !,
 1903    Arity2 is Arity+2.
 nopredref(+PI)//
Result of name/arity, non-linking predicate indicator.
 1909nopredref(PI) -->
 1910    { canonical_pi(PI, _CPI, HTML)
 1911    },
 1912    !,
 1913    html(span(class=nopredref, HTML)).
 flagref(+Flag)//
Reference to a Prolog flag.
To be done
- generate a link to the Prolog website?
 1921flagref(Flag) -->
 1922    html(code(Flag)).
 cite(+Citations)// is det
Emit citations. This is indented to allow for [@cite1;@cite2] for generating LaTex.
 1929cite(Citations) -->
 1930    html('['), citations(Citations), html(']').
 1931
 1932citations([]) --> [].
 1933citations([H|T]) -->
 1934    citation(H),
 1935    (   {T==[]}
 1936    ->  []
 1937    ;   [';'],
 1938        citations(T)
 1939    ).
 1940
 1941citation(H) -->
 1942    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).
 1950manref(PI, HREF, Options) :-
 1951    predname(PI, PredName),
 1952    (   option(files(_Map), Options)
 1953    ->  option(man_server(Server), Options,
 1954               'http://www.swi-prolog.org/pldoc'),
 1955        uri_components(Server, Comp0),
 1956        uri_data(path, Comp0, Path0),
 1957        directory_file_path(Path0, man, Path),
 1958        uri_data(path, Comp0, Path, Components),
 1959        uri_query_components(Query, [predicate=PredName]),
 1960        uri_data(search, Components, Query),
 1961        uri_components(HREF, Components)
 1962    ;   http_link_to_id(pldoc_man, [predicate=PredName], HREF)
 1963    ).
 1964
 1965predname(Name/Arity, PredName) :-
 1966    !,
 1967    format(atom(PredName), '~w/~d', [Name, Arity]).
 1968predname(Module:Name/Arity, PredName) :-
 1969    !,
 1970    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.
 1984pred_href(Name/Arity, Module, HREF) :-
 1985    format(string(FragmentId), '~w/~d', [Name, Arity]),
 1986    uri_data(fragment, Components, FragmentId),
 1987    functor(Head, Name, Arity),
 1988    (   catch(relative_file(Module:Head, File), _, fail)
 1989    ->  uri_data(path, Components, File),
 1990        uri_components(HREF, Components)
 1991    ;   in_file(Module:Head, File)
 1992    ->  (   current_prolog_flag(home, SWI),
 1993            sub_atom(File, 0, _, _, SWI),
 1994            prolog:doc_object_summary(Name/Arity, packages, _, _)
 1995        ->  http_link_to_id(pldoc_man, [predicate=FragmentId], HREF)
 1996        ;   http_location_by_id(pldoc_doc, DocHandler),
 1997            atom_concat(DocHandler, File, Path),
 1998            uri_data(path, Components, Path),
 1999            uri_components(HREF, Components)
 2000        )
 2001    ).
 2002
 2003relative_file(Head, '') :-
 2004    b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
 2005    in_file(Head, CurrentFile),
 2006    !.
 2007relative_file(Head, RelFile) :-
 2008    b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
 2009    in_file(Head, DefFile),
 2010    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.
 2016pred_source_href(Name/Arity, Module, HREF) :-
 2017    format(string(FragmentId), '~w/~d', [Name, Arity]),
 2018    uri_data(fragment, Components, FragmentId),
 2019    uri_query_components(Query, [show=src]),
 2020    uri_data(search, Components, Query),
 2021    functor(Head, Name, Arity),
 2022    (   catch(relative_file(Module:Head, File), _, fail)
 2023    ->  uri_data(path, Components, File),
 2024        uri_components(HREF, Components)
 2025    ;   in_file(Module:Head, File0)
 2026    ->  insert_alias(File0, File),
 2027        http_location_by_id(pldoc_doc, DocHandler),
 2028        atom_concat(DocHandler, File, Path),
 2029        uri_data(path, Components, Path),
 2030        uri_components(HREF, Components)
 2031    ).
 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.
 2040object_ref([], _) -->
 2041    !,
 2042    [].
 2043object_ref([H|T], Options) -->
 2044    !,
 2045    object_ref(H, Options),
 2046    (   {T == []}
 2047    ->  html(', '),
 2048        object_ref(T, Options)
 2049    ;   []
 2050    ).
 2051object_ref(Obj, Options) -->
 2052    { object_href(Obj, HREF, Options)
 2053    },
 2054    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.
 2061object_href(Obj, HREF) :-
 2062    object_href(Obj, HREF, []).
 2063
 2064object_href(M:PI0, HREF, Options) :-
 2065    option(files(Map), Options),
 2066    (   module_property(M, file(File))
 2067    ->  true
 2068    ;   xref_module(File, M)
 2069    ),
 2070    memberchk(file(File, DocFile), Map),
 2071    !,
 2072    file_base_name(DocFile, LocalFile),     % TBD: proper directory index
 2073    expand_pi(PI0, PI),
 2074    term_to_string(PI, PIS),
 2075    uri_data(path, Components, LocalFile),
 2076    uri_data(fragment, Components, PIS),
 2077    uri_components(HREF, Components).
 2078object_href(file(File), HREF, _Options) :-
 2079    doc_file_href(File, HREF),
 2080    !.
 2081object_href(directory(Dir), HREF, _Options) :-
 2082    directory_file_path(Dir, 'index.html', Index),
 2083    doc_file_href(Index, HREF),
 2084    !.
 2085object_href(Obj, HREF, _Options) :-
 2086    prolog:doc_object_href(Obj, HREF),
 2087    !.
 2088object_href(Obj0, HREF, _Options) :-
 2089    localise_object(Obj0, Obj),
 2090    term_to_string(Obj, String),
 2091    http_link_to_id(pldoc_object, [object=String], HREF).
 2092
 2093expand_pi(Name//Arity0, Name/Arity) :-
 2094    !,
 2095    Arity is Arity0+2.
 2096expand_pi(PI, PI).
 localise_object(+ObjIn, -ObjOut) is det
Abstract path-details to make references more stable over versions.
 2104localise_object(Obj0, Obj) :-
 2105    prolog:doc_canonical_object(Obj0, Obj),
 2106    !.
 2107localise_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.
 2115term_to_string(Term, String) :-
 2116    State = state(-),
 2117    (   numbervars(Term, 0, _, [singletons(true)]),
 2118        with_output_to(string(String),
 2119                       write_term(Term,
 2120                                  [ numbervars(true),
 2121                                    quoted(true)
 2122                                  ])),
 2123        nb_setarg(1, State, String),
 2124        fail
 2125    ;   arg(1, State, String)
 2126    ).
 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
 2140object_name(Obj, Options) -->
 2141    { option(style(Style), Options, inline)
 2142    },
 2143    object_name(Style, Obj, Options).
 2144
 2145object_name(title, Obj, Options) -->
 2146    { merge_options(Options, [secref_style(title)], Options1) },
 2147    prolog:doc_object_link(Obj, Options1),
 2148    !.
 2149object_name(inline, Obj, Options) -->
 2150    prolog:doc_object_link(Obj, Options),
 2151    !.
 2152object_name(title, f(Name/Arity), _Options) -->
 2153    !,
 2154    html(['Function ', Name, /, Arity]).
 2155object_name(inline, f(Name/Arity), _Options) -->
 2156    !,
 2157    html([Name, /, Arity]).
 2158object_name(Style, PI, Options) -->
 2159    { is_pi(PI) },
 2160    !,
 2161    pi(Style, PI, Options).
 2162object_name(inline, Module:module(_Title), _) -->
 2163    !,
 2164    { module_property(Module, file(File)),
 2165      file_base_name(File, Base)
 2166    },
 2167    !,
 2168    html(Base).
 2169object_name(title, Module:module(Title), _) -->
 2170    { module_property(Module, file(File)),
 2171      file_base_name(File, Base)
 2172    },
 2173    !,
 2174    html([Base, ' -- ', Title]).
 2175object_name(title, file(File), _) -->
 2176    { module_property(Module, file(File)),
 2177      doc_comment(Module:module(Title), _, _, _),
 2178      !,
 2179      file_base_name(File, Base)
 2180    },
 2181    html([Base, ' -- ', Title]).
 2182object_name(_, file(File), _) -->
 2183    { file_base_name(File, Base) },
 2184    html(Base).
 2185object_name(_, directory(Dir), _) -->
 2186    { file_base_name(Dir, Base) },
 2187    html(Base).
 2188object_name(_, module(Title), _Options) -->
 2189    { print_message(warning,
 2190                    pldoc(module_comment_outside_module(Title)))
 2191    }.
 2192
 2193pi(title, PI, Options) -->
 2194    pi_type(PI),
 2195    pi(PI, Options).
 2196pi(inline, PI, Options) -->
 2197    pi(PI, Options).
 2198
 2199pi(M:PI, Options) -->
 2200    !,
 2201    (   { option(qualify(true), Options) }
 2202    ->  html([span(class(module), M), :])
 2203    ;   []
 2204    ),
 2205    pi(PI, Options).
 2206pi(Name/Arity, _) -->
 2207    !,
 2208    html([Name, /, Arity]).
 2209pi(Name//Arity, _) -->
 2210    html([Name, //, Arity]).
 2211
 2212pi_type(_:PI) -->
 2213    !,
 2214    pi_type(PI).
 2215pi_type(_/_) -->
 2216    html(['Predicate ']).
 2217pi_type(_//_) -->
 2218    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.
 2230in_file(Module:Head, File) :-
 2231    !,
 2232    distinct(File, in_file(Module, Head, File)).
 2233in_file(Head, File) :-
 2234    distinct(File, in_file(_, Head, File)).
 2235
 2236in_file(Module, Head, File) :-
 2237    var(Module),
 2238    (   predicate_property(system:Head, foreign)
 2239    ->  !,
 2240        fail
 2241    ;   predicate_property(system:Head, file(File)),
 2242        \+ system_arithmetic_function(Head)
 2243    ->  !
 2244    ;   predicate_property(Head, autoload(File0))
 2245    ->  !,
 2246        file_name_extension(File0, pl, File)
 2247    ;   exported_from(Module, Head, File),
 2248        module_property(Module, class(library))
 2249    ).
 2250in_file(Module, Head, File) :-
 2251    xref_defined(File, Head, How),
 2252    xref_current_source(File),
 2253    atom(File),                     % only plain files
 2254    xref_module(File, Module),
 2255    How \= imported(_From).
 2256in_file(Module, Head, File) :-
 2257    exported_from(Module, Head, File).
 2258in_file(Module, Head, File) :-
 2259    predicate_property(Module:Head, file(File)),
 2260    \+ predicate_property(Module:Head, imported_from(_)).
 2261in_file(Module, Head, File) :-
 2262    current_module(Module),
 2263    source_file(Module:Head, File).
 2264
 2265exported_from(Module, Head, File) :-
 2266    distinct(Primary,
 2267             (   predicate_property(Module:Head, exported),
 2268                 (   predicate_property(Module:Head, imported_from(Primary))
 2269                 ->  true
 2270                 ;   Primary = Module
 2271                 ))),
 2272    module_property(Primary, file(File)).
 2273
 2274:- multifile
 2275    arithmetic:evaluable/2. 2276
 2277system_arithmetic_function(Head) :-
 2278    functor(Head, Name, Arity),
 2279    FArith is Arity-1,
 2280    FArith >= 0,
 2281    functor(FHead, Name, FArith),
 2282    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?
 2313file(File) -->
 2314    file(File, []).
 2315
 2316file(File, Options) -->
 2317    { catch(nb_getval(pldoc_options, GenOptions), _, GenOptions = []),
 2318      merge_options(Options, GenOptions, FinalOptions)
 2319    },
 2320    link_file(File, FinalOptions),
 2321    !.
 2322file(File, Options) -->
 2323    { option(edit_handler(Handler), Options),
 2324      http_current_request(Request),
 2325      memberchk(path(Path), Request),
 2326      absolute_file_name(File, Location,
 2327                         [ relative_to(Path)
 2328                         ]),
 2329      http_link_to_id(Handler, [location(Location)], HREF),
 2330      format(atom(Title), 'Click to create ~w', [File])
 2331    },
 2332    html(a([href(HREF), class(nofile), title(Title)], File)).
 2333file(File, _) -->
 2334    html(code(class(nofile), File)).
 2335
 2336link_file(File, Options) -->
 2337    { file_href(File, HREF, Options),
 2338      option(label(Label), Options, File),
 2339      option(class(Class), Options, file)
 2340    },
 2341    html(a([class(Class), href(HREF)], Label)).
 file_href(+FilePath, -HREF, +Options) is det
Find URL for refering to FilePath based on Options.
 2347file_href(_, HREF, Options) :-
 2348    option(href(HREF), Options),
 2349    !.
 2350file_href(File, HREF, Options) :-
 2351    file_href_real(File, HREF0, Options),
 2352    map_extension(HREF0, HREF, Options).
 map_extension(+HREFIn, -HREFOut, Options) is det
Replace extension using the option
 2360map_extension(HREF0, HREF, Options) :-
 2361    option(map_extension(Map), Options),
 2362    file_name_extension(Base, Old, HREF0),
 2363    memberchk(Old-New, Map),
 2364    !,
 2365    file_name_extension(Base, New, HREF).
 2366map_extension(HREF, HREF, _).
 2367
 2368
 2369file_href_real(File, HREF, Options) :-
 2370    (   option(absolute_path(Path), Options)
 2371    ;   existing_linked_file(File, Path)
 2372    ),
 2373    !,
 2374    (   option(files(Map), Options),
 2375        memberchk(file(Path, LinkFile), Map)
 2376    ->  true
 2377    ;   LinkFile = Path
 2378    ),
 2379    file_href(LinkFile, HREF).
 2380file_href_real(File, HREF, _) :-
 2381    directory_alias(Alias),
 2382    Term =.. [Alias,File],
 2383    absolute_file_name(Term, _,
 2384                       [ access(read),
 2385                         file_errors(fail)
 2386                       ]),
 2387    !,
 2388    http_absolute_location(Term, HREF, []).
 2389
 2390directory_alias(icons).
 2391directory_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.
 2401file_href(Path, HREF) :-                % a loaded Prolog file
 2402    source_file(Path),
 2403    !,
 2404    doc_file_href(Path, HREF).
 2405file_href(Path, HREF) :-
 2406    (   nb_current(pldoc_output, CFile)
 2407    ;   nb_current(pldoc_file, CFile)
 2408    ),
 2409    CFile \== [],
 2410    !,
 2411    relative_file_name(Path, CFile, HREF).
 2412file_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.
 2420existing_linked_file(File, Path) :-
 2421    catch(b_getval(pldoc_file, CurrentFile), _, fail),
 2422    CurrentFile \== [],
 2423    absolute_file_name(File, Path,
 2424                       [ relative_to(CurrentFile),
 2425                         access(read),
 2426                         file_errors(fail)
 2427                       ]).
 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]].
 2437include(PI, predicate, _) -->
 2438    !,
 2439    (   html_tokens_for_predicates(PI, [])
 2440    ->  []
 2441    ;   html(['[[', \predref(PI), ']]'])
 2442    ).
 2443include(File, image, Options) -->
 2444    { file_name_extension(_, svg, File),
 2445      file_href(File, HREF, Options),
 2446      !,
 2447      include(image_attribute, Options, Attrs0),
 2448      merge_options(Attrs0,
 2449                    [ alt(File),
 2450                      data(HREF),
 2451                      type('image/svg+xml')
 2452                    ], Attrs)
 2453    },
 2454    (   { option(caption(Caption), Options) }
 2455    ->  html(div(class(figure),
 2456                 [ div(class(image), object(Attrs, [])),
 2457                   div(class(caption), Caption)
 2458                 ]))
 2459    ;   html(object(Attrs, []))
 2460    ).
 2461include(File, image, Options) -->
 2462    { file_href(File, HREF, Options),
 2463      !,
 2464      include(image_attribute, Options, Attrs0),
 2465      merge_options(Attrs0,
 2466                    [ alt(File),
 2467                      border(0),
 2468                      src(HREF)
 2469                    ], Attrs)
 2470    },
 2471    (   { option(caption(Caption), Options) }
 2472    ->  html(div(class(figure),
 2473                 [ div(class(image), img(Attrs)),
 2474                   div(class(caption), Caption)
 2475                 ]))
 2476    ;   html(img(Attrs))
 2477    ).
 2478include(File, wiki, _Options) -->       % [[file.txt]] is included
 2479    { access_file(File, read),
 2480      !,
 2481      read_file_to_codes(File, String, []),
 2482      wiki_codes_to_dom(String, [], DOM)
 2483    },
 2484    html(DOM).
 2485include(File, _Type, Options) -->
 2486    link_file(File, Options),
 2487    !.
 2488include(File, _, _) -->
 2489    html(code(class(nofile), ['[[',File,']]'])).
 2490
 2491image_attribute(src(_)).
 2492image_attribute(alt(_)).
 2493image_attribute(title(_)).
 2494image_attribute(align(_)).
 2495image_attribute(width(_)).
 2496image_attribute(height(_)).
 2497image_attribute(border(_)).
 2498image_attribute(class(_)).
 2499image_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]]
 2512html_tokens_for_predicates([], _Options) -->
 2513    [].
 2514html_tokens_for_predicates([H|T], Options) -->
 2515    !,
 2516    html_tokens_for_predicates(H, Options),
 2517    html_tokens_for_predicates(T, Options).
 2518html_tokens_for_predicates(PI, Options) -->
 2519    { PI = _:_/_,
 2520      !,
 2521      (   doc_comment(PI, Pos, _Summary, Comment)
 2522      ->  true
 2523      ;   Comment = ''
 2524      )
 2525    },
 2526    object(PI, [Pos-Comment], [dl], _, Options).
 2527html_tokens_for_predicates(Spec, Options) -->
 2528    { findall(PI, documented_pi(Spec, PI), List),
 2529      List \== [], !
 2530    },
 2531    html_tokens_for_predicates(List, Options).
 2532html_tokens_for_predicates(Spec, Options) -->
 2533    man_page(Spec,
 2534             [ links(false),                % no header
 2535               navtree(false),              % no navigation tree
 2536               footer(false),               % no footer
 2537               synopsis(false)              % no synopsis
 2538             | Options
 2539             ]).
 2540
 2541
 2542documented_pi(Spec, PI) :-
 2543    generalise_spec(Spec, PI),
 2544    doc_comment(PI, _Pos, _Summary, _Comment).
 2545
 2546generalise_spec(Name/Arity, _M:Name/Arity).
 2547generalise_spec(Name//Arity, _M:Name//Arity).
 2548
 2549
 2550                 /*******************************
 2551                 *           WIKI FILES         *
 2552                 *******************************/
 doc_for_wiki_file(+File, +Options) is det
Write HTML for the File containing wiki data.
 2559doc_for_wiki_file(FileSpec, Options) :-
 2560    absolute_file_name(FileSpec, File,
 2561                       [ access(read)
 2562                       ]),
 2563    read_file_to_codes(File, String, []),
 2564    b_setval(pldoc_file, File),
 2565    call_cleanup(reply_wiki_page(File, String, Options),
 2566                 nb_delete(pldoc_file)).
 2567
 2568reply_wiki_page(File, String, Options) :-
 2569    wiki_codes_to_dom(String, [], DOM0),
 2570    title(DOM0, File, Title),
 2571    insert_edit_button(DOM0, File, DOM, Options),
 2572    reply_html_page(pldoc(wiki),
 2573                    title(Title),
 2574                    [ \html_requires(pldoc)
 2575                    | DOM
 2576                    ]).
 2577
 2578title(DOM, _, Title) :-
 2579    sub_term(h1(_,Title), DOM),
 2580    !.
 2581title(_, File, Title) :-
 2582    file_base_name(File, Title).
 2583
 2584insert_edit_button(DOM, _, DOM, Options) :-
 2585    option(edit(false), Options, false),
 2586    !.
 2587insert_edit_button([h1(Attrs,Title)|DOM], File,
 2588                   [h1(Attrs,[ span(style('float:right'),
 2589                                   \edit_button(File, [edit(true)]))
 2590                             | Title
 2591                             ])|DOM], _) :- !.
 2592insert_edit_button(DOM, File,
 2593                   [ h1(class(wiki),
 2594                        [ span(style('float:right'),
 2595                               \edit_button(File, [edit(true)]))
 2596                        ])
 2597                   | DOM
 2598                   ], _).
 2599
 2600
 2601                 /*******************************
 2602                 *            ANCHORS           *
 2603                 *******************************/
 mode_anchor_name(+Mode, -Anchor:atom) is det
Get the anchor name for a mode.
 2609mode_anchor_name(Var, _) :-
 2610    var(Var),
 2611    !,
 2612    instantiation_error(Var).
 2613mode_anchor_name(mode(Head, _), Anchor) :-
 2614    !,
 2615    mode_anchor_name(Head, Anchor).
 2616mode_anchor_name(Head is _Det, Anchor) :-
 2617    !,
 2618    mode_anchor_name(Head, Anchor).
 2619mode_anchor_name(Head, Anchor) :-
 2620    pred_anchor_name(Head, _, Anchor).
 pred_anchor_name(+Head, -PI:atom/integer, -Anchor:atom) is det
Create an HTML anchor name from Head.
 2627pred_anchor_name(//(Head), Name/Arity, Anchor) :-
 2628    !,
 2629    functor(Head, Name, DCGArity),
 2630    Arity is DCGArity+2,
 2631    format(atom(Anchor), '~w/~d', [Name, Arity]).
 2632pred_anchor_name(Head, Name/Arity, Anchor) :-
 2633    functor(Head, Name, Arity),
 2634    format(atom(Anchor), '~w/~d', [Name, Arity]).
 2635
 2636:- multifile prolog:message//1. 2637
 2638prolog:message(pldoc(module_comment_outside_module(Title))) -->
 2639    [ 'PlDoc comment <module> ~w does not appear in a module'-[Title] ]