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    public(Module:PI, Options), !, fail.
  517private(Module:PI, Options) :-
  518    option(module(Module), Options),
  519    option(public(Public), Options),
  520    !,
  521    \+ ( member(PI2, Public),
  522         eq_pi(PI, PI2)
  523       ).
  524private(Module:PI, _Options) :-
  525    module_property(Module, file(_)),      % A loaded module
  526    !,
  527    module_property(Module, exports(Exports)),
  528    \+ ( member(PI2, Exports),
  529         eq_pi(PI, PI2)
  530       ).
  531private(Module:PI, _Options) :-
  532    \+ (pi_to_head(PI, Head),
  533        xref_exported(Source, Head),
  534        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.
  545multifile(Obj, _Options) :-
  546    strip_module(user:Obj, Module, PI),
  547    pi_to_head(PI, Head),
  548    (   predicate_property(Module:Head, multifile)
  549    ;   xref_module(Source, Module),
  550        xref_defined(Source, Head, multifile(_Line))
  551    ),
  552    !.
 public(+Options, +Options)
True if Obj is declared using public/1.
  558public(Obj, _Options) :-
  559    strip_module(user:Obj, Module, PI),
  560    pi_to_head(PI, Head),
  561    (   predicate_property(Module:Head, public)
  562    ;   xref_module(Source, Module),
  563        xref_defined(Source, Head, public(_Line))
  564    ),
  565    !.
  566
  567pi_to_head(Var, _) :-
  568    var(Var), !, fail.
  569pi_to_head(Name/Arity, Term) :-
  570    functor(Term, Name, Arity).
  571pi_to_head(Name//DCGArity, Term) :-
  572    Arity is DCGArity+2,
  573    functor(Term, Name, Arity).
 file_info(+Comments, -RestComment, -FileOptions, +OtherOptions) is det
Add options file(Title, Comment) to OtherOptions if available.
  579file_info(Comments, RestComments, [file(Title, Comment)|Opts], Opts) :-
  580    select(doc(_:module(Title),_,Comment), Comments, RestComments),
  581    !.
  582file_info(Comments, Comments, Opts, Opts).
 file_header(+File, +Options)// is det
Create the file header.
  589file_header(File, Options) -->
  590    { memberchk(file(Title, Comment), Options),
  591      !,
  592      file_base_name(File, Base)
  593    },
  594    file_title([Base, ' -- ', Title], File, Options),
  595    { is_structured_comment(Comment, Prefixes),
  596      string_codes(Comment, Codes),
  597      indented_lines(Codes, Prefixes, Lines),
  598      section_comment_header(Lines, _Header, Lines1),
  599      wiki_lines_to_dom(Lines1, [], DOM)
  600    },
  601    html(DOM).
  602file_header(File, Options) -->
  603    { file_base_name(File, Base)
  604    },
  605    file_title([Base], File, Options).
 file_title(+Title:list, +File, +Options)// is det
Emit the file-header and manipulation buttons.
  612file_title(Title, File, Options) -->
  613    prolog:doc_file_title(Title, File, Options),
  614    !.
  615file_title(Title, File, Options) -->
  616    { file_base_name(File, Base)
  617    },
  618    html(h1(class(file),
  619            [ span(style('float:right'),
  620                   [ \reload_button(File, Base, Options),
  621                     \zoom_button(Base, Options),
  622                     \source_button(Base, Options),
  623                     \edit_button(File, Options)
  624                   ])
  625            | Title
  626            ])).
 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.
  636reload_button(File, _Base, Options) -->
  637    { \+ source_file(File),
  638      \+ option(files(_), Options)
  639    },
  640    !,
  641    html(span(class(file_anot), '[not loaded]')).
  642reload_button(_File, Base, Options) -->
  643    { option(edit(true), Options),
  644      !,
  645      option(public_only(Public), Options, true)
  646    },
  647    html(a(href(Base+[reload(true), public_only(Public)]),
  648           img([ class(action),
  649                 alt('Reload'),
  650                 title('Make & Reload'),
  651                 src(location_by_id(pldoc_resource)+'reload.png')
  652               ]))).
  653reload_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.
  661edit_button(File, Options) -->
  662    { option(edit(true), Options)
  663    },
  664    !,
  665    html(a([ onClick('HTTPrequest(\'' +
  666                     location_by_id(pldoc_edit) + [file(File)] +
  667                     '\')')
  668           ],
  669           img([ class(action),
  670                 alt(edit),
  671                 title('Edit file'),
  672                 src(location_by_id(pldoc_resource)+'edit.png')
  673             ]))).
  674edit_button(_, _) -->
  675    [].
 zoom_button(BaseName, +Options)// is det
Add zoom in/out button to show/hide the private documentation.
  682zoom_button(_, Options) -->
  683    { option(files(_Map), Options) },
  684    !.    % generating files
  685zoom_button(Base, Options) -->
  686    {   (   option(public_only(true), Options, true)
  687        ->  Zoom = 'public.png',
  688            Alt = 'Public',
  689            Title = 'Click to include private',
  690            PublicOnly = false
  691        ;   Zoom = 'private.png',
  692            Alt = 'All predicates',
  693            Title = 'Click to show exports only',
  694            PublicOnly = true
  695        )
  696    },
  697    html(a(href(Base+[public_only(PublicOnly)]),
  698           img([ class(action),
  699                 alt(Alt),
  700                 title(Title),
  701                 src(location_by_id(pldoc_resource)+Zoom)
  702               ]))).
 source_button(+File, +Options)// is det
Add show-source button.
  709source_button(_File, Options) -->
  710    { option(files(_Map), Options) },
  711    !.    % generating files
  712source_button(File, _Options) -->
  713    { (   is_absolute_file_name(File)
  714      ->  doc_file_href(File, HREF0)
  715      ;   HREF0 = File
  716      )
  717    },
  718    html(a(href(HREF0+[show(src)]),
  719           img([ class(action),
  720                 alt('Show source'),
  721                 title('Show source'),
  722                 src(location_by_id(pldoc_resource)+'source.png')
  723               ]))).
 objects(+Objects:list, +Options)// is det
Emit the documentation body. Options includes:
navtree(+Boolean)
If true, provide a navitation tree.
  733objects(Objects, Options) -->
  734    { option(navtree(true), Options),
  735      !,
  736      objects_nav_tree(Objects, Tree)
  737    },
  738    html([ div(class(navtree),
  739               div(class(navwindow),
  740                   \nav_tree(Tree, Objects, Options))),
  741           div(class(navcontent),
  742               \objects_nt(Objects, Options))
  743         ]).
  744objects(Objects, Options) -->
  745    objects_nt(Objects, Options).
  746
  747objects_nt(Objects, Options) -->
  748    objects(Objects, [body], Options).
  749
  750objects([], Mode, _) -->
  751    pop_mode(body, Mode, _).
  752objects([Obj|T], Mode, Options) -->
  753    object(Obj, Mode, Mode1, Options),
  754    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.
  765object(doc(Obj,Pos,Comment), Mode0, Mode, Options) -->
  766    !,
  767    object(Obj, [Pos-Comment], Mode0, Mode, [scope(file)|Options]).
  768object(Obj, Mode0, Mode, Options) -->
  769    { findall(Pos-Comment,
  770              doc_comment(Obj, Pos, _Summary, Comment),
  771              Pairs)
  772    },
  773    !,
  774    { b_setval(pldoc_object, Obj) },
  775    object(Obj, Pairs, Mode0, Mode, Options).
  776
  777object(Obj, Pairs, Mode0, Mode, Options) -->
  778    { is_pi(Obj),
  779      !,
  780      maplist(pred_dom(Obj, Options), Pairs, DOMS),
  781      append(DOMS, DOM)
  782    },
  783    need_mode(dl, Mode0, Mode),
  784    html(DOM).
  785object([Obj|_Same], Pairs, Mode0, Mode, Options) -->
  786    !,
  787    object(Obj, Pairs, Mode0, Mode, Options).
  788object(Obj, _Pairs, Mode, Mode, _Options) -->
  789    { debug(pldoc, 'Skipped ~p', [Obj]) },
  790    [].
  791
  792pred_dom(Obj, Options, Pos-Comment, DOM) :-
  793    is_structured_comment(Comment, Prefixes),
  794    string_codes(Comment, Codes),
  795    indented_lines(Codes, Prefixes, Lines),
  796    strip_module(user:Obj, Module, _),
  797    process_modes(Lines, Module, Pos, Modes, Args, Lines1),
  798    (   private(Obj, Options)
  799    ->  Class = privdef             % private definition
  800    ;   multifile(Obj, Options)
  801    ->  (   option(scope(file), Options)
  802        ->  (   more_doc(Obj, Pos)
  803            ->  Class = multidef(object(Obj))
  804            ;   Class = multidef
  805            )
  806        ;   Class = multidef(file((Pos)))
  807        )
  808    ;   public(Obj, Options)
  809    ->  Class = publicdef           % :- public definition
  810    ;   Class = pubdef              % exported definition
  811    ),
  812    (   Obj = Module:_
  813    ->  POptions = [module(Module)|Options]
  814    ;   POptions = Options
  815    ),
  816    Pos = File:Line,
  817    DTOptions = [file(File),line(Line)|POptions],
  818    DOM = [\pred_dt(Modes, Class, DTOptions), dd(class=defbody, DOM1)],
  819    wiki_lines_to_dom(Lines1, Args, DOM0),
  820    strip_leading_par(DOM0, DOM1).
  821
  822more_doc(Obj, File:_) :-
  823    doc_comment(Obj, File2:_, _, _),
  824    File2 \== File,
  825    !.
 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.
  834need_mode(Mode, Stack, Stack) -->
  835    { Stack = [Mode|_] },
  836    !,
  837    [].
  838need_mode(Mode, Stack, Rest) -->
  839    { memberchk(Mode, Stack)
  840    },
  841    !,
  842    pop_mode(Mode, Stack, Rest).
  843need_mode(Mode, Stack, [Mode|Stack]) -->
  844    !,
  845    html_begin(Mode).
  846
  847pop_mode(Mode, Stack, Stack) -->
  848    { Stack = [Mode|_] },
  849    !,
  850    [].
  851pop_mode(Mode, [H|Rest0], Rest) -->
  852    html_end(H),
  853    pop_mode(Mode, Rest0, Rest).
 undocumented(+File, +Objects, +Options)// is det
Describe undocumented predicates if the file is a module file.
  859undocumented(File, Objs, Options) -->
  860    { memberchk(module(Module), Options),
  861      memberchk(public(Exports), Options),
  862      select_undocumented(Exports, Module, Objs, Undoc),
  863      re_exported_doc(Undoc, File, Module, REObjs, ReallyUnDoc)
  864    },
  865    !,
  866    re_exported_doc(REObjs, Options),
  867    undocumented(ReallyUnDoc, Options).
  868undocumented(_, _, _) -->
  869    [].
  870
  871re_exported_doc([], _) --> !.
  872re_exported_doc(Objs, Options) -->
  873    reexport_header(Objs, Options),
  874    objects(Objs, Options).
  875
  876reexport_header(_, Options) -->
  877    { option(reexport_header(true), Options, true)
  878    },
  879    !,
  880    html([ h2(class(wiki), 'Re-exported predicates'),
  881           p([ 'The following predicates are re-exported from other ',
  882               'modules'
  883             ])
  884         ]).
  885reexport_header(_, _) -->
  886    [].
  887
  888undocumented([], _) --> !.
  889undocumented(UnDoc, Options) -->
  890    html([ h2(class(undoc), 'Undocumented predicates'),
  891           p(['The following predicates are exported, but not ',
  892              'or incorrectly documented.'
  893             ]),
  894           dl(class(undoc),
  895              \undocumented_predicates(UnDoc, Options))
  896         ]).
  897
  898
  899undocumented_predicates([], _) -->
  900    [].
  901undocumented_predicates([H|T], Options) -->
  902    undocumented_pred(H, Options),
  903    undocumented_predicates(T, Options).
  904
  905undocumented_pred(Name/Arity, Options) -->
  906    { functor(Head, Name, Arity) },
  907    html(dt(class=undoc, \pred_mode(Head, [], _, Options))).
  908
  909select_undocumented([], _, _, []).
  910select_undocumented([PI|T0], M, Objs, [PI|T]) :-
  911    is_pi(PI),
  912    \+ in_doc(M:PI, Objs),
  913    !,
  914    select_undocumented(T0, M, Objs, T).
  915select_undocumented([_|T0], M, Objs, T) :-
  916    select_undocumented(T0, M, Objs, T).
  917
  918in_doc(PI, Objs) :-
  919    member(doc(O,_,_), Objs),
  920    (   is_list(O)
  921    ->  member(O2, O),
  922        eq_pi(PI, O2)
  923    ;   eq_pi(PI, O)
  924    ).
 eq_pi(PI1, PI2) is semidet
True if PI1 and PI2 refer to the same predicate.
  931eq_pi(PI, PI) :- !.
  932eq_pi(M:PI1, M:PI2) :-
  933    atom(M),
  934    !,
  935    eq_pi(PI1, PI2).
  936eq_pi(Name/A, Name//DCGA) :-
  937    A =:= DCGA+2,
  938    !.
  939eq_pi(Name//DCGA, Name/A) :-
  940    A =:= DCGA+2.
 is_pi(@Term) is semidet
True if Term is a predicate indicator.
  946is_pi(Var) :-
  947    var(Var),
  948    !,
  949    fail.
  950is_pi(_:PI) :-
  951    !,
  952    is_pi(PI).
  953is_pi(_/_).
  954is_pi(_//_).
 re_exported_doc(+Undoc:list(pi), +File:atom, +Module:atom, -ImportedDoc, -ReallyUnDoc:list(pi))
  960re_exported_doc([], _, _, [], []).
  961re_exported_doc([PI|T0], File, Module, [doc(Orig:PI,Pos,Comment)|ObjT], UnDoc) :-
  962    pi_to_head(PI, Head),
  963    (   predicate_property(Module:Head, imported_from(Orig))
  964    ->  true
  965    ;   xref_defined(File, Head, imported(File2)),
  966        ensure_doc_objects(File2),
  967        xref_module(File2, Orig)
  968    ),
  969    doc_comment(Orig:PI, Pos, _, Comment),
  970    !,
  971    re_exported_doc(T0, File, Module, ObjT, UnDoc).
  972re_exported_doc([PI|T0], File, Module, REObj, [PI|UnDoc]) :-
  973    re_exported_doc(T0, File, Module, REObj, UnDoc).
  974
  975
  976                 /*******************************
  977                 *      SINGLE OBJECT PAGE      *
  978                 *******************************/
 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.
  988object_page(Obj, Options) -->
  989    prolog:doc_object_page(Obj, Options),
  990    !,
  991    object_page_footer(Obj, Options).
  992object_page(Obj, Options) -->
  993    { doc_comment(Obj, File:_Line, _Summary, _Comment)
  994    },
  995    !,
  996    (   { \+ ( doc_comment(Obj, File2:_, _, _),
  997               File2 \== File )
  998        }
  999    ->  html([ \html_requires(pldoc),
 1000               \object_page_header(File, Options),
 1001               \object_synopsis(Obj, []),
 1002               \objects([Obj], Options)
 1003             ])
 1004    ;   html([ \html_requires(pldoc),
 1005               \object_page_header(-, Options),
 1006               \objects([Obj], [synopsis(true)|Options])
 1007             ])
 1008    ),
 1009    object_page_footer(Obj, Options).
 1010object_page(M:Name/Arity, Options) -->          % specified module, but public
 1011    { functor(Head, Name, Arity),
 1012      (   predicate_property(M:Head, exported)
 1013      ->  module_property(M, class(library))
 1014      ;   \+ predicate_property(M:Head, defined)
 1015      )
 1016    },
 1017    prolog:doc_object_page(Name/Arity, Options),
 1018    !,
 1019    object_page_footer(Name/Arity, Options).
 1020
 1021object_page_header(File, Options) -->
 1022    prolog:doc_page_header(file(File), Options),
 1023    !.
 1024object_page_header(File, Options) -->
 1025    { option(header(true), Options, true) },
 1026    !,
 1027    html(div(class(navhdr),
 1028             [ div(class(jump), \file_link(File)),
 1029               div(class(search), \search_form(Options)),
 1030               br(clear(right))
 1031             ])).
 1032object_page_header(_, _) --> [].
 1033
 1034file_link(-) -->
 1035    !,
 1036    places_menu(-).
 1037file_link(File) -->
 1038    { file_directory_name(File, Dir)
 1039    },
 1040    places_menu(Dir),
 1041    html([ div(a(href(location_by_id(pldoc_doc)+File), File))
 1042         ]).
 object_footer(+Obj, +Options)// is det
Call the hook doc_object_footer//2. This hook will be used to deal with examples.
 1049object_footer(Obj, Options) -->
 1050    prolog:doc_object_footer(Obj, Options),
 1051    !.
 1052object_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.
 1060object_page_footer(Obj, Options) -->
 1061    prolog:doc_object_page_footer(Obj, Options),
 1062    !.
 1063object_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.
 1077object_synopsis(Name/Arity, _) -->
 1078    { functor(Head, Name, Arity),
 1079      predicate_property(system:Head, built_in)
 1080    },
 1081    synopsis([span(class(builtin), 'built-in')]).
 1082object_synopsis(Name/Arity, Options) -->
 1083    !,
 1084    object_synopsis(_:Name/Arity, Options).
 1085object_synopsis(M:Name/Arity, Options) -->
 1086    { functor(Head, Name, Arity),
 1087      (   option(source(Spec), Options)
 1088      ->  absolute_file_name(Spec, File,
 1089                             [ access(read),
 1090                               file_type(prolog),
 1091                               file_errors(fail)
 1092                             ])
 1093      ;   predicate_property(M:Head, exported),
 1094          \+ predicate_property(M:Head, imported_from(_)),
 1095          module_property(M, file(File)),
 1096          file_name_on_path(File, Spec)
 1097      ),
 1098      !,
 1099      unquote_filespec(Spec, Unquoted),
 1100      (   predicate_property(Head, autoload(FileBase)),
 1101          file_name_extension(FileBase, _Ext, File)
 1102      ->  Extra = [span(class(autoload), '(can be autoloaded)')]
 1103      ;   Extra = []
 1104      )
 1105    },
 1106    (   { option(href(HREF), Options) }
 1107    ->  synopsis([code([':- use_module(',a(href(HREF), '~q'-[Unquoted]),').'])|Extra])
 1108    ;   synopsis([code(':- use_module(~q).'-[Unquoted])|Extra])
 1109    ).
 1110object_synopsis(Name//Arity, Options) -->
 1111    !,
 1112    { DCGArity is Arity+2 },
 1113    object_synopsis(Name/DCGArity, Options).
 1114object_synopsis(Module:Name//Arity, Options) -->
 1115    !,
 1116    { DCGArity is Arity+2 },
 1117    object_synopsis(Module:Name/DCGArity, Options).
 1118object_synopsis(f(_/_), _) -->
 1119    synopsis(span(class(function),
 1120                  [ 'Arithmetic function (see ',
 1121                    \object_ref(is/2, []),
 1122                    ')'
 1123                  ])).
 1124object_synopsis(c(Func), _) -->
 1125    { sub_atom(Func, 0, _, _, 'PL_')
 1126    },
 1127    !,
 1128    synopsis([span(class(cfunc), 'C-language interface function')]).
 1129object_synopsis(_, _) --> [].
 1130
 1131synopsis(Text) -->
 1132    html(div(class(synopsis),
 1133             [ span(class('synopsis-hdr'), 'Availability:')
 1134             | Text
 1135             ])).
 unquote_filespec(+Spec, -Unquoted) is det
Translate e.g. library('semweb/rdf_db') into library(semweb/rdf_db).
 1142unquote_filespec(Spec, Unquoted) :-
 1143    compound(Spec),
 1144    Spec =.. [Alias,Path],
 1145    atom(Path),
 1146    atomic_list_concat(Parts, /, Path),
 1147    maplist(need_no_quotes, Parts),
 1148    !,
 1149    parts_to_path(Parts, UnquotedPath),
 1150    Unquoted =.. [Alias, UnquotedPath].
 1151unquote_filespec(Spec, Spec).
 1152
 1153need_no_quotes(Atom) :-
 1154    format(atom(A), '~q', [Atom]),
 1155    \+ sub_atom(A, 0, _, _, '\'').
 1156
 1157parts_to_path([One], One) :- !.
 1158parts_to_path(List, More/T) :-
 1159    (   append(H, [T], List)
 1160    ->  parts_to_path(H, More)
 1161    ).
 1162
 1163
 1164                 /*******************************
 1165                 *             PRINT            *
 1166                 *******************************/
 doc_write_html(+Out:stream, +Title:atomic, +DOM) is det
Write HTML for the documentation page DOM using Title to Out.
 1172doc_write_html(Out, Title, Doc) :-
 1173    doc_page_dom(Title, Doc, DOM),
 1174    phrase(html(DOM), Tokens),
 1175    print_html_head(Out),
 1176    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.
 1183doc_page_dom(Title, Body, DOM) :-
 1184    DOM = html([ head([ title(Title),
 1185                        link([ rel(stylesheet),
 1186                               type('text/css'),
 1187                               href(location_by_id(pldoc_resource)+'pldoc.css')
 1188                             ]),
 1189                        script([ src(location_by_id(pldoc_resource)+'pldoc.js'),
 1190                                 type('text/javascript')
 1191                               ], [])
 1192                      ]),
 1193                 body(Body)
 1194               ]).
 print_html_head(+Out:stream) is det
Print the DOCTYPE line.
 1200print_html_head(Out) :-
 1201    format(Out,
 1202           '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" \c
 1203               "http://www.w3.org/TR/html4/strict.dtd">~n', []).
 1204
 1205% Rendering rules
 1206%
 1207% 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.
 1215tags(Tags) -->
 1216    html(dl(class=tags, Tags)).
 tag(+Tag, +Values:list)// is det
Called from \tag(Name, Values) terms produced by doc_wiki.pl.
 1222tag(Tag, Values) -->
 1223    {   doc_tag_title(Tag, Title),
 1224        atom_concat('keyword-', Tag, Class)
 1225    },
 1226    html([ dt(class=Class, Title),
 1227           \tag_values(Values, Class)
 1228         ]).
 1229
 1230tag_values([], _) -->
 1231    [].
 1232tag_values([H|T], Class) -->
 1233    html(dd(class=Class, ['- '|H])),
 1234    tag_values(T, Class).
 doc_tag_title(+Tag, -Title) is det
Title is the name to use for Tag in the generated documentation.
 1241doc_tag_title(Tag, Title) :-
 1242    tag_title(Tag, Title),
 1243    !.
 1244doc_tag_title(Tag, Tag).
 1245
 1246tag_title(compat, 'Compatibility').
 1247tag_title(tbd,    'To be done').
 1248tag_title(see,    'See also').
 1249tag_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).
 1256args(Params) -->
 1257    html([ dt(class=tag, 'Arguments:'),
 1258           dd(table(class=arglist,
 1259                    \arg_list(Params)))
 1260         ]).
 1261
 1262arg_list([]) -->
 1263    [].
 1264arg_list([H|T]) -->
 1265    argument(H),
 1266    arg_list(T).
 1267
 1268argument(arg(Name,Descr)) -->
 1269    html(tr([td(var(Name)), td(class=argdescr, ['- '|Descr])])).
 1270
 1271
 1272                 /*******************************
 1273                 *         NAVIGATION TREE      *
 1274                 *******************************/
 objects_nav_tree(+Objects, -Tree) is det
Provide a navigation tree showing the context of Object. Tree is of the form node(Object, Children).
 1281objects_nav_tree(Objects, Tree) :-
 1282    maplist(object_nav_tree, Objects, Trees),
 1283    union_trees(Trees, Tree0),
 1284    remove_unique_root(Tree0, Tree).
 1285
 1286object_nav_tree(Obj, Tree) :-
 1287    Node = node(directory(Dir), FileNodes),
 1288    FileNode = node(file(File), Siblings),
 1289    doc_comment(Obj, File:_Line, _Summary, _Comment),
 1290    !,
 1291    file_directory_name(File, Dir),
 1292    sibling_file_nodes(Dir, FileNodes0),
 1293    selectchk(node(file(File),[]), FileNodes0, FileNode, FileNodes),
 1294    findall(Sibling, doc_comment(Sibling, File:_, _, _), Siblings0),
 1295    delete(Siblings0, _:module(_), Siblings1),
 1296    doc_hide_private(Siblings1, Siblings2, []),
 1297    flatten(Siblings2, Siblings),   % a comment may describe objects
 1298    embed_directories(Node, Tree).
 1299
 1300sibling_file_nodes(Dir, Nodes) :-
 1301    findall(node(file(File), []),
 1302            (   source_file(File),
 1303                file_directory_name(File, Dir)
 1304            ),
 1305            Nodes).
 1306
 1307embed_directories(Node, Tree) :-
 1308    Node = node(file(File), _),
 1309    !,
 1310    file_directory_name(File, Dir),
 1311    Super = node(directory(Dir), [Node]),
 1312    embed_directories(Super, Tree).
 1313embed_directories(Node, Tree) :-
 1314    Node = node(directory(Dir), _),
 1315    file_directory_name(Dir, SuperDir),
 1316    SuperDir \== Dir,
 1317    !,
 1318    Super = node(directory(SuperDir), [Node]),
 1319    embed_directories(Super, Tree).
 1320embed_directories(Tree, Tree).
 1321
 1322
 1323union_trees([Tree], Tree) :- !.
 1324union_trees([T1,T2|Trees], Tree) :-
 1325    merge_trees(T1, T2, M1),
 1326    union_trees([M1|Trees], Tree).
 1327
 1328merge_trees(node(R, Ch1), node(R, Ch2), node(R, Ch)) :-
 1329    merge_nodes(Ch1, Ch2, Ch).
 1330
 1331merge_nodes([], Ch, Ch) :- !.
 1332merge_nodes(Ch, [], Ch) :- !.
 1333merge_nodes([node(Root, Ch1)|T1], N1, [T1|Nodes]) :-
 1334    selectchk(node(Root, Ch2), N1, N2),
 1335    !,
 1336    merge_trees(node(Root, Ch1), node(Root, Ch2), T1),
 1337    merge_nodes(T1, N2, Nodes).
 1338merge_nodes([Node|T1], N1, [Node|Nodes]) :-
 1339    merge_nodes(T1, N1, Nodes).
 remove_unique_root(+TreeIn, -Tree)
Remove the root part that does not branch
 1345remove_unique_root(node(_, [node(R1, [R2])]), Tree) :-
 1346    !,
 1347    remove_unique_root(node(R1, [R2]), Tree).
 1348remove_unique_root(Tree, Tree).
 nav_tree(+Tree, +Current, +Options)// is det
Render the navigation tree
 1354nav_tree(Tree, Current, Options) -->
 1355    html(ul(class(nav),
 1356            \object_tree(Tree, Current, Options))).
 object_tree(+Tree, +Current, +Options)// is det
Render a tree of objects used for navigation.
 1362object_tree(node(Id, []), Target, Options) -->
 1363    !,
 1364    { node_class(Id, Target, Class) },
 1365    html(li(class(Class),
 1366            \node(Id, Options))).
 1367object_tree(node(Id, Children), Target, Options) -->
 1368    !,
 1369    { node_class(Id, Target, Class) },
 1370    html(li(class(Class),
 1371            [ \node(Id, Options),
 1372              ul(class(nav),
 1373                 \object_trees(Children, Target, Options))
 1374            ])).
 1375object_tree(Id, Target, Options) -->
 1376    !,
 1377    { node_class(Id, Target, Class) },
 1378    html(li(class([obj|Class]), \node(Id, Options))).
 1379
 1380object_trees([], _, _) --> [].
 1381object_trees([H|T], Target, Options) -->
 1382    object_tree(H, Target, Options),
 1383    object_trees(T, Target, Options).
 1384
 1385node_class(Ids, Current, Class) :-
 1386    is_list(Ids),
 1387    !,
 1388    (   member(Id, Ids), memberchk(Id, Current)
 1389    ->  Class = [nav,current]
 1390    ;   Class = [nav]
 1391    ).
 1392node_class(Id, Current, Class) :-
 1393    (   memberchk(Id, Current)
 1394    ->  Class = [nav,current]
 1395    ;   Class = [nav]
 1396    ).
 1397
 1398node(file(File), Options) -->
 1399    !,
 1400    object_ref(file(File), [style(title)|Options]).
 1401node(Id, Options) -->
 1402    object_ref(Id, Options).
 1403
 1404
 1405                 /*******************************
 1406                 *            SECTIONS          *
 1407                 *******************************/
 1408
 1409section(Type, Title) -->
 1410    { string_codes(Title, Codes),
 1411      wiki_codes_to_dom(Codes, [], Content0),
 1412      strip_leading_par(Content0, Content),
 1413      make_section(Type, Content, HTML)
 1414    },
 1415    html(HTML).
 1416
 1417make_section(module,  Title, h1(class=module,  Title)).
 1418make_section(section, Title, h1(class=section, Title)).
 1419
 1420
 1421                 /*******************************
 1422                 *       PRED MODE HEADER       *
 1423                 *******************************/
 pred_dt(+Modes, +Class, Options)// is det
Emit the predicate header.
Arguments:
Modes- List as returned by process_modes/5.
 1431pred_dt(Modes, Class, Options) -->
 1432    pred_dt(Modes, Class, [], _Done, Options).
 1433
 1434pred_dt([], _, Done, Done, _) -->
 1435    [].
 1436pred_dt([H|T], Class, Done0, Done, Options) -->
 1437    { functor(Class, CSSClass, _) },
 1438    html(dt(class=CSSClass,
 1439            [ \pred_mode(H, Done0, Done1, Options),
 1440              \mode_anot(Class)
 1441            ])),
 1442    pred_dt(T, Class, Done1, Done, Options).
 1443
 1444mode_anot(privdef) -->
 1445    !,
 1446    html(span([class(anot), style('float:right')],
 1447              '[private]')).
 1448mode_anot(multidef(object(Obj))) -->
 1449    !,
 1450    { object_href(Obj, HREF) },
 1451    html(span([class(anot), style('float:right')],
 1452              ['[', a(href(HREF), multifile), ']'
 1453              ])).
 1454mode_anot(multidef(file(File:_))) -->
 1455    !,
 1456    { file_name_on_path(File, Spec),
 1457      unquote_filespec(Spec, Unquoted),
 1458      doc_file_href(File, HREF)
 1459    },
 1460    html(span([class(anot), style('float:right')],
 1461              ['[multifile, ', a(href(HREF), '~q'-[Unquoted]), ']'
 1462              ])).
 1463mode_anot(multidef) -->
 1464    !,
 1465    html(span([class(anot), style('float:right')],
 1466              '[multifile]')).
 1467mode_anot(_) -->
 1468    [].
 1469
 1470pred_mode(mode(Head,Vars), Done0, Done, Options) -->
 1471    !,
 1472    { bind_vars(Head, Vars) },
 1473    pred_mode(Head, Done0, Done, Options).
 1474pred_mode(Head is Det, Done0, Done, Options) -->
 1475    !,
 1476    anchored_pred_head(Head, Done0, Done, Options),
 1477    pred_det(Det).
 1478pred_mode(Head, Done0, Done, Options) -->
 1479    anchored_pred_head(Head, Done0, Done, Options).
 1480
 1481bind_vars(Term, Bindings) :-
 1482    bind_vars(Bindings),
 1483    anon_vars(Term).
 1484
 1485bind_vars([]).
 1486bind_vars([Name=Var|T]) :-
 1487    Var = '$VAR'(Name),
 1488    bind_vars(T).
 anon_vars(+Term) is det
Bind remaining variables in Term to '$VAR'('_'), so they are printed as '_'.
 1495anon_vars(Var) :-
 1496    var(Var),
 1497    !,
 1498    Var = '$VAR'('_').
 1499anon_vars(Term) :-
 1500    compound(Term),
 1501    !,
 1502    Term =.. [_|Args],
 1503    maplist(anon_vars, Args).
 1504anon_vars(_).
 1505
 1506
 1507anchored_pred_head(Head, Done0, Done, Options) -->
 1508    { pred_anchor_name(Head, PI, Name) },
 1509    (   { memberchk(PI, Done0) }
 1510    ->  { Done = Done0 },
 1511        pred_head(Head)
 1512    ;   html([ span(style('float:right'),
 1513                    [ \pred_edit_or_source_button(Head, Options),
 1514                      &(nbsp)
 1515                    ]),
 1516               a(name=Name, \pred_head(Head))
 1517             ]),
 1518        { Done = [PI|Done0] }
 1519    ).
 1520
 1521
 1522pred_edit_or_source_button(Head, Options) -->
 1523    { option(edit(true), Options) },
 1524    !,
 1525    pred_edit_button(Head, Options).
 1526pred_edit_or_source_button(Head, Options) -->
 1527    { option(source_link(true), Options) },
 1528    !,
 1529    pred_source_button(Head, Options).
 1530pred_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)
 1544pred_edit_button(_, Options) -->
 1545    { \+ option(edit(true), Options) },
 1546    !.
 1547pred_edit_button(PI0, Options0) -->
 1548    { canonicalise_predref(PI0, PI, Options0, Options) },
 1549    pred_edit_button2(PI, Options).
 1550
 1551pred_edit_button2(Name/Arity, Options) -->
 1552    { \+ ( memberchk(file(_), Options), % always edit if file and line
 1553           memberchk(line(_), Options)  % are given.
 1554         ),
 1555      functor(Head, Name, Arity),
 1556      option(module(M), Options, _),
 1557      \+ ( current_module(M),
 1558           source_file(M:Head, _File)
 1559         )
 1560    },
 1561    !.
 1562pred_edit_button2(Name/Arity, Options) -->
 1563    { include(edit_param, Options, Extra),
 1564      http_link_to_id(pldoc_edit,
 1565                      [name(Name),arity(Arity)|Extra],
 1566                      EditHREF)
 1567    },
 1568    html(a(onClick('HTTPrequest(\'' + EditHREF + '\')'),
 1569           img([ class(action),
 1570                 alt('Edit predicate'),
 1571                 title('Edit predicate'),
 1572                 src(location_by_id(pldoc_resource)+'editpred.png')
 1573               ]))).
 1574pred_edit_button2(_, _) -->
 1575    !,
 1576    [].
 1577
 1578edit_param(module(_)).
 1579edit_param(file(_)).
 1580edit_param(line(_)).
 object_edit_button(+Object, +Options)// is det
Create a button for editing Object.
 1587object_edit_button(_, Options) -->
 1588    { \+ option(edit(true), Options) },
 1589    !.
 1590object_edit_button(PI, Options) -->
 1591    { is_pi(PI) },
 1592    !,
 1593    pred_edit_button(PI, Options).
 1594object_edit_button(_, _) -->
 1595    [].
 pred_source_button(+PredIndicator, +Options)// is det
Create a button for viewing the source of a predicate.
 1602pred_source_button(PI0, Options0) -->
 1603    { canonicalise_predref(PI0, PI, Options0, Options),
 1604      option(module(M), Options, _),
 1605      pred_source_href(PI, M, HREF), !
 1606    },
 1607    html(a([ href(HREF)
 1608           ],
 1609           img([ class(action),
 1610                 alt('Source'),
 1611                 title('Show source'),
 1612                 src(location_by_id(pldoc_resource)+'source.png')
 1613               ]))).
 1614pred_source_button(_, _) -->
 1615    [].
 object_source_button(+Object, +Options)// is det
Create a button for showing the source of Object.
 1622object_source_button(PI, Options) -->
 1623    { is_pi(PI),
 1624      option(source_link(true), Options, true)
 1625    },
 1626    !,
 1627    pred_source_button(PI, Options).
 1628object_source_button(_, _) -->
 1629    [].
 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.
 1637canonicalise_predref(M:PI0, PI, Options0, [module(M)|Options]) :-
 1638    !,
 1639    canonicalise_predref(PI0, PI, Options0, Options).
 1640canonicalise_predref(//(Head), PI, Options0, Options) :-
 1641    !,
 1642    functor(Head, Name, Arity),
 1643    PredArity is Arity + 2,
 1644    canonicalise_predref(Name/PredArity, PI, Options0, Options).
 1645canonicalise_predref(Name//Arity, PI, Options0, Options) :-
 1646    integer(Arity), Arity >= 0,
 1647    !,
 1648    PredArity is Arity + 2,
 1649    canonicalise_predref(Name/PredArity, PI, Options0, Options).
 1650canonicalise_predref(PI, PI, Options, Options) :-
 1651    PI = Name/Arity,
 1652    atom(Name), integer(Arity), Arity >= 0,
 1653    !.
 1654canonicalise_predref(Head, PI, Options0, Options) :-
 1655    functor(Head, Name, Arity),
 1656    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.
 1664pred_head(Var) -->
 1665    { var(Var),
 1666      !,
 1667      instantiation_error(Var)
 1668    }.
 1669pred_head(//(Head)) -->
 1670    !,
 1671    pred_head(Head),
 1672    html(//).
 1673pred_head(M:Head) -->
 1674    html([span(class=module, M), :]),
 1675    pred_head(Head).
 1676pred_head(Head) -->
 1677    { atom(Head) },
 1678    !,
 1679    html(b(class=pred, Head)).
 1680pred_head(Head) -->                     % Infix operators
 1681    { Head =.. [Functor,Left,Right],
 1682      is_op_type(Functor, infix)
 1683    },
 1684    !,
 1685    html([ var(class=arglist, \pred_arg(Left, 1)),
 1686           ' ', b(class=pred, Functor), ' ',
 1687           var(class=arglist, \pred_arg(Right, 2))
 1688         ]).
 1689pred_head(Head) -->                     % Prefix operators
 1690    { Head =.. [Functor,Arg],
 1691      is_op_type(Functor, prefix)
 1692    },
 1693    !,
 1694    html([ b(class=pred, Functor), ' ',
 1695           var(class=arglist, \pred_arg(Arg, 1))
 1696         ]).
 1697pred_head(Head) -->                     % Postfix operators
 1698    { Head =.. [Functor,Arg],
 1699      is_op_type(Functor, postfix)
 1700    },
 1701    !,
 1702    html([ var(class=arglist, \pred_arg(Arg, 1)),
 1703           ' ', b(class=pred, Functor)
 1704         ]).
 1705pred_head({Head}) -->
 1706    !,
 1707    html([ b(class=pred, '{'),
 1708           var(class=arglist,
 1709               \pred_args([Head], 1)),
 1710           b(class=pred, '}')
 1711         ]).
 1712pred_head(Head) -->                     % Plain terms
 1713    { Head =.. [Functor|Args] },
 1714    html([ b(class=pred, Functor),
 1715           var(class=arglist,
 1716               [ '(', \pred_args(Args, 1), ')' ])
 1717         ]).
 is_op_type(+Atom, ?Type)
True if Atom is an operator of Type. Type is one of prefix, infix or postfix.
 1724is_op_type(Functor, Type) :-
 1725    current_op(_Pri, F, Functor),
 1726    op_type(F, Type).
 1727
 1728op_type(fx,  prefix).
 1729op_type(fy,  prefix).
 1730op_type(xf,  postfix).
 1731op_type(yf,  postfix).
 1732op_type(xfx, infix).
 1733op_type(xfy, infix).
 1734op_type(yfx, infix).
 1735op_type(yfy, infix).
 1736
 1737
 1738pred_args([], _) -->
 1739    [].
 1740pred_args([H|T], I) -->
 1741    pred_arg(H, I),
 1742    (   {T==[]}
 1743    ->  []
 1744    ;   html(', '),
 1745        { I2 is I + 1 },
 1746        pred_args(T, I2)
 1747    ).
 1748
 1749pred_arg(Var, I) -->
 1750    { var(Var) },
 1751    !,
 1752    html(['Arg', I]).
 1753pred_arg(...(Term), I) -->
 1754    !,
 1755    pred_arg(Term, I),
 1756    html('...').
 1757pred_arg(Term, I) -->
 1758    { Term =.. [Ind,Arg],
 1759      mode_indicator(Ind)
 1760    },
 1761    !,
 1762    html([Ind, \pred_arg(Arg, I)]).
 1763pred_arg(Arg:Type, _) -->
 1764    !,
 1765    html([\argname(Arg), :, \argtype(Type)]).
 1766pred_arg(Arg, _) -->
 1767    argname(Arg).
 1768
 1769argname('$VAR'(Name)) -->
 1770    !,
 1771    html(Name).
 1772argname(Name) -->
 1773    !,
 1774    html(Name).
 1775
 1776argtype(Term) -->
 1777    { format(string(S), '~W',
 1778             [ Term,
 1779               [ quoted(true),
 1780                 numbervars(true)
 1781               ]
 1782             ]) },
 1783    html(S).
 1784
 1785pred_det(unknown) -->
 1786    [].
 1787pred_det(Det) -->
 1788    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
 1797term(_, Atom, []) -->
 1798    { atomic(Atom),
 1799      !,
 1800      format(string(S), '~W', [Atom,[quoted(true)]])
 1801    },
 1802    html(span(class=functor, S)).
 1803term(_, Key:Type, [TypeName=Type]) -->
 1804    { atomic(Key)
 1805    },
 1806    !,
 1807    html([span(class='pl-key', Key), :, span(class('pl-var'), TypeName)]).
 1808term(_, Term, Bindings) -->
 1809    { is_mode(Term is det),         % HACK. Bit too strict?
 1810      bind_vars(Bindings)
 1811    },
 1812    !,
 1813    pred_head(Term).
 1814term(_, Term, Bindings) -->
 1815    term(Term,
 1816         [ variable_names(Bindings),
 1817           quoued(true)
 1818         ]).
 1819
 1820
 1821                 /*******************************
 1822                 *             PREDREF          *
 1823                 *******************************/
 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.

 1836predref(Term) -->
 1837    { catch(nb_getval(pldoc_options, Options), _, Options = []) },
 1838    predref(Term, Options).
 1839
 1840predref(Obj, Options) -->
 1841    { Obj = _:_,
 1842      doc_comment(Obj, File:_Line, _, _),
 1843      (   (   option(files(Map), Options)
 1844          ->  memberchk(file(File,_), Map)
 1845          ;   true
 1846          )
 1847      ->  object_href(Obj, HREF, Options)
 1848      ;   manref(Obj, HREF, Options)
 1849      )
 1850    },
 1851    !,
 1852    html(a(href(HREF), \object_name(Obj, [qualify(true)|Options]))).
 1853predref(M:Term, Options) -->
 1854    !,
 1855    predref(Term, M, Options).
 1856predref(Term, Options) -->
 1857    predref(Term, _, Options).
 1858
 1859predref(Name/Arity, _, Options) -->             % Builtin; cannot be overruled
 1860    { prolog:doc_object_summary(Name/Arity, manual, _, _),
 1861      !,
 1862      manref(Name/Arity, HREF, Options)
 1863    },
 1864    html(a([class=builtin, href=HREF], [Name, /, Arity])).
 1865predref(Name/Arity, _, Options) -->             % From packages
 1866    { option(prefer(manual), Options),
 1867      prolog:doc_object_summary(Name/Arity, Category, _, _),
 1868      !,
 1869      manref(Name/Arity, HREF, Options)
 1870    },
 1871    html(a([class=Category, href=HREF], [Name, /, Arity])).
 1872predref(Obj, Module, Options) -->               % Local
 1873    { doc_comment(Module:Obj, File:_Line, _, _),
 1874      (   option(files(Map), Options)
 1875      ->  memberchk(file(File,_), Map)
 1876      ;   true
 1877      )
 1878    },
 1879    !,
 1880    object_ref(Module:Obj, Options).
 1881predref(Name/Arity, Module, Options) -->
 1882    { \+ option(files(_), Options),
 1883      pred_href(Name/Arity, Module, HREF)
 1884    },
 1885    !,
 1886    html(a(href=HREF, [Name, /, Arity])).
 1887predref(Name//Arity, Module, Options) -->
 1888    { \+ option(files(_), Options),
 1889      PredArity is Arity + 2,
 1890      pred_href(Name/PredArity, Module, HREF)
 1891    },
 1892    !,
 1893    html(a(href=HREF, [Name, //, Arity])).
 1894predref(PI, _, Options) -->             % From packages
 1895    { canonical_pi(PI, CPI, HTML),
 1896      (   option(files(_), Options)
 1897      ->  Category = extmanual
 1898      ;   prolog:doc_object_summary(CPI, Category, _, _)
 1899      ),
 1900      manref(CPI, HREF, Options)
 1901    },
 1902    html(a([class=Category, href=HREF], HTML)).
 1903predref(PI, _, _Options) -->
 1904    { canonical_pi(PI, _CPI, HTML)
 1905    },
 1906    !,
 1907    html(span(class=undef, HTML)).
 1908predref(Callable, Module, Options) -->
 1909    { callable(Callable),
 1910      functor(Callable, Name, Arity)
 1911    },
 1912    predref(Name/Arity, Module, Options).
 1913
 1914canonical_pi(Name/Arity, Name/Arity, [Name, /, Arity]) :-
 1915    atom(Name), integer(Arity),
 1916    !.
 1917canonical_pi(Name//Arity, Name/Arity2, [Name, //, Arity]) :-
 1918    atom(Name), integer(Arity),
 1919    !,
 1920    Arity2 is Arity+2.
 nopredref(+PI)//
Result of name/arity, non-linking predicate indicator.
 1926nopredref(PI) -->
 1927    { canonical_pi(PI, _CPI, HTML)
 1928    },
 1929    !,
 1930    html(span(class=nopredref, HTML)).
 flagref(+Flag)//
Reference to a Prolog flag.
To be done
- generate a link to the Prolog website?
 1938flagref(Flag) -->
 1939    html(code(Flag)).
 cite(+Citations)// is det
Emit citations. This is indented to allow for [@cite1;@cite2] for generating LaTex.
 1946cite(Citations) -->
 1947    html('['), citations(Citations), html(']').
 1948
 1949citations([]) --> [].
 1950citations([H|T]) -->
 1951    citation(H),
 1952    (   {T==[]}
 1953    ->  []
 1954    ;   [';'],
 1955        citations(T)
 1956    ).
 1957
 1958citation(H) -->
 1959    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).
 1967manref(PI, HREF, Options) :-
 1968    predname(PI, PredName),
 1969    (   option(files(_Map), Options)
 1970    ->  option(man_server(Server), Options,
 1971               'http://www.swi-prolog.org/pldoc'),
 1972        uri_components(Server, Comp0),
 1973        uri_data(path, Comp0, Path0),
 1974        directory_file_path(Path0, man, Path),
 1975        uri_data(path, Comp0, Path, Components),
 1976        uri_query_components(Query, [predicate=PredName]),
 1977        uri_data(search, Components, Query),
 1978        uri_components(HREF, Components)
 1979    ;   http_link_to_id(pldoc_man, [predicate=PredName], HREF)
 1980    ).
 1981
 1982predname(Name/Arity, PredName) :-
 1983    !,
 1984    format(atom(PredName), '~w/~d', [Name, Arity]).
 1985predname(Module:Name/Arity, PredName) :-
 1986    !,
 1987    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.
 2001pred_href(Name/Arity, Module, HREF) :-
 2002    format(string(FragmentId), '~w/~d', [Name, Arity]),
 2003    uri_data(fragment, Components, FragmentId),
 2004    functor(Head, Name, Arity),
 2005    (   catch(relative_file(Module:Head, File), _, fail)
 2006    ->  uri_data(path, Components, File),
 2007        uri_components(HREF, Components)
 2008    ;   in_file(Module:Head, File)
 2009    ->  (   current_prolog_flag(home, SWI),
 2010            sub_atom(File, 0, _, _, SWI),
 2011            prolog:doc_object_summary(Name/Arity, packages, _, _)
 2012        ->  http_link_to_id(pldoc_man, [predicate=FragmentId], HREF)
 2013        ;   http_location_by_id(pldoc_doc, DocHandler),
 2014            atom_concat(DocHandler, File, Path),
 2015            uri_data(path, Components, Path),
 2016            uri_components(HREF, Components)
 2017        )
 2018    ).
 2019
 2020relative_file(Head, '') :-
 2021    b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
 2022    in_file(Head, CurrentFile),
 2023    !.
 2024relative_file(Head, RelFile) :-
 2025    b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
 2026    in_file(Head, DefFile),
 2027    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.
 2033pred_source_href(Name/Arity, Module, HREF) :-
 2034    format(string(FragmentId), '~w/~d', [Name, Arity]),
 2035    uri_data(fragment, Components, FragmentId),
 2036    uri_query_components(Query, [show=src]),
 2037    uri_data(search, Components, Query),
 2038    functor(Head, Name, Arity),
 2039    (   catch(relative_file(Module:Head, File), _, fail)
 2040    ->  uri_data(path, Components, File),
 2041        uri_components(HREF, Components)
 2042    ;   in_file(Module:Head, File0)
 2043    ->  insert_alias(File0, File),
 2044        http_location_by_id(pldoc_doc, DocHandler),
 2045        atom_concat(DocHandler, File, Path),
 2046        uri_data(path, Components, Path),
 2047        uri_components(HREF, Components)
 2048    ).
 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.
 2057object_ref([], _) -->
 2058    !,
 2059    [].
 2060object_ref([H|T], Options) -->
 2061    !,
 2062    object_ref(H, Options),
 2063    (   {T == []}
 2064    ->  html(', '),
 2065        object_ref(T, Options)
 2066    ;   []
 2067    ).
 2068object_ref(Obj, Options) -->
 2069    { object_href(Obj, HREF, Options)
 2070    },
 2071    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.
 2078object_href(Obj, HREF) :-
 2079    object_href(Obj, HREF, []).
 2080
 2081object_href(M:PI0, HREF, Options) :-
 2082    option(files(Map), Options),
 2083    (   module_property(M, file(File))
 2084    ->  true
 2085    ;   xref_module(File, M)
 2086    ),
 2087    memberchk(file(File, DocFile), Map),
 2088    !,
 2089    file_base_name(DocFile, LocalFile),     % TBD: proper directory index
 2090    expand_pi(PI0, PI),
 2091    term_to_string(PI, PIS),
 2092    uri_data(path, Components, LocalFile),
 2093    uri_data(fragment, Components, PIS),
 2094    uri_components(HREF, Components).
 2095object_href(file(File), HREF, _Options) :-
 2096    doc_file_href(File, HREF),
 2097    !.
 2098object_href(directory(Dir), HREF, _Options) :-
 2099    directory_file_path(Dir, 'index.html', Index),
 2100    doc_file_href(Index, HREF),
 2101    !.
 2102object_href(Obj, HREF, _Options) :-
 2103    prolog:doc_object_href(Obj, HREF),
 2104    !.
 2105object_href(Obj0, HREF, _Options) :-
 2106    localise_object(Obj0, Obj),
 2107    term_to_string(Obj, String),
 2108    http_link_to_id(pldoc_object, [object=String], HREF).
 2109
 2110expand_pi(Name//Arity0, Name/Arity) :-
 2111    !,
 2112    Arity is Arity0+2.
 2113expand_pi(PI, PI).
 localise_object(+ObjIn, -ObjOut) is det
Abstract path-details to make references more stable over versions.
 2121localise_object(Obj0, Obj) :-
 2122    prolog:doc_canonical_object(Obj0, Obj),
 2123    !.
 2124localise_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.
 2132term_to_string(Term, String) :-
 2133    State = state(-),
 2134    (   numbervars(Term, 0, _, [singletons(true)]),
 2135        with_output_to(string(String),
 2136                       write_term(Term,
 2137                                  [ numbervars(true),
 2138                                    quoted(true)
 2139                                  ])),
 2140        nb_setarg(1, State, String),
 2141        fail
 2142    ;   arg(1, State, String)
 2143    ).
 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
 2157object_name(Obj, Options) -->
 2158    { option(style(Style), Options, inline)
 2159    },
 2160    object_name(Style, Obj, Options).
 2161
 2162object_name(title, Obj, Options) -->
 2163    { merge_options(Options, [secref_style(title)], Options1) },
 2164    prolog:doc_object_link(Obj, Options1),
 2165    !.
 2166object_name(inline, Obj, Options) -->
 2167    prolog:doc_object_link(Obj, Options),
 2168    !.
 2169object_name(title, f(Name/Arity), _Options) -->
 2170    !,
 2171    html(['Function ', Name, /, Arity]).
 2172object_name(inline, f(Name/Arity), _Options) -->
 2173    !,
 2174    html([Name, /, Arity]).
 2175object_name(Style, PI, Options) -->
 2176    { is_pi(PI) },
 2177    !,
 2178    pi(Style, PI, Options).
 2179object_name(inline, Module:module(_Title), _) -->
 2180    !,
 2181    { module_property(Module, file(File)),
 2182      file_base_name(File, Base)
 2183    },
 2184    !,
 2185    html(Base).
 2186object_name(title, Module:module(Title), _) -->
 2187    { module_property(Module, file(File)),
 2188      file_base_name(File, Base)
 2189    },
 2190    !,
 2191    html([Base, ' -- ', Title]).
 2192object_name(title, file(File), _) -->
 2193    { module_property(Module, file(File)),
 2194      doc_comment(Module:module(Title), _, _, _),
 2195      !,
 2196      file_base_name(File, Base)
 2197    },
 2198    html([Base, ' -- ', Title]).
 2199object_name(_, file(File), _) -->
 2200    { file_base_name(File, Base) },
 2201    html(Base).
 2202object_name(_, directory(Dir), _) -->
 2203    { file_base_name(Dir, Base) },
 2204    html(Base).
 2205object_name(_, module(Title), _Options) -->
 2206    { print_message(warning,
 2207                    pldoc(module_comment_outside_module(Title)))
 2208    }.
 2209
 2210pi(title, PI, Options) -->
 2211    pi_type(PI),
 2212    pi(PI, Options).
 2213pi(inline, PI, Options) -->
 2214    pi(PI, Options).
 2215
 2216pi(M:PI, Options) -->
 2217    !,
 2218    (   { option(qualify(true), Options) }
 2219    ->  html([span(class(module), M), :])
 2220    ;   []
 2221    ),
 2222    pi(PI, Options).
 2223pi(Name/Arity, _) -->
 2224    !,
 2225    html([Name, /, Arity]).
 2226pi(Name//Arity, _) -->
 2227    html([Name, //, Arity]).
 2228
 2229pi_type(_:PI) -->
 2230    !,
 2231    pi_type(PI).
 2232pi_type(_/_) -->
 2233    html(['Predicate ']).
 2234pi_type(_//_) -->
 2235    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.
 2247in_file(Module:Head, File) :-
 2248    !,
 2249    distinct(File, in_file(Module, Head, File)).
 2250in_file(Head, File) :-
 2251    distinct(File, in_file(_, Head, File)).
 2252
 2253in_file(Module, Head, File) :-
 2254    var(Module),
 2255    (   predicate_property(system:Head, foreign)
 2256    ->  !,
 2257        fail
 2258    ;   predicate_property(system:Head, file(File)),
 2259        \+ system_arithmetic_function(Head)
 2260    ->  !
 2261    ;   predicate_property(Head, autoload(File0))
 2262    ->  !,
 2263        file_name_extension(File0, pl, File)
 2264    ;   exported_from(Module, Head, File),
 2265        module_property(Module, class(library))
 2266    ).
 2267in_file(Module, Head, File) :-
 2268    xref_defined(File, Head, How),
 2269    xref_current_source(File),
 2270    atom(File),                     % only plain files
 2271    xref_module(File, Module),
 2272    How \= imported(_From).
 2273in_file(Module, Head, File) :-
 2274    exported_from(Module, Head, File).
 2275in_file(Module, Head, File) :-
 2276    predicate_property(Module:Head, file(File)),
 2277    \+ predicate_property(Module:Head, imported_from(_)).
 2278in_file(Module, Head, File) :-
 2279    current_module(Module),
 2280    source_file(Module:Head, File).
 2281
 2282exported_from(Module, Head, File) :-
 2283    distinct(Primary,
 2284             (   predicate_property(Module:Head, exported),
 2285                 (   predicate_property(Module:Head, imported_from(Primary))
 2286                 ->  true
 2287                 ;   Primary = Module
 2288                 ))),
 2289    module_property(Primary, file(File)).
 2290
 2291:- multifile
 2292    arithmetic:evaluable/2. 2293
 2294system_arithmetic_function(Head) :-
 2295    functor(Head, Name, Arity),
 2296    FArith is Arity-1,
 2297    FArith >= 0,
 2298    functor(FHead, Name, FArith),
 2299    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?
 2330file(File) -->
 2331    file(File, []).
 2332
 2333file(File, Options) -->
 2334    { catch(nb_getval(pldoc_options, GenOptions), _, GenOptions = []),
 2335      merge_options(Options, GenOptions, FinalOptions)
 2336    },
 2337    link_file(File, FinalOptions),
 2338    !.
 2339file(File, Options) -->
 2340    { option(edit_handler(Handler), Options),
 2341      http_current_request(Request),
 2342      memberchk(path(Path), Request),
 2343      absolute_file_name(File, Location,
 2344                         [ relative_to(Path)
 2345                         ]),
 2346      http_link_to_id(Handler, [location(Location)], HREF),
 2347      format(atom(Title), 'Click to create ~w', [File])
 2348    },
 2349    html(a([href(HREF), class(nofile), title(Title)], File)).
 2350file(File, _) -->
 2351    html(code(class(nofile), File)).
 2352
 2353link_file(File, Options) -->
 2354    { file_href(File, HREF, Options),
 2355      option(label(Label), Options, File),
 2356      option(class(Class), Options, file)
 2357    },
 2358    html(a([class(Class), href(HREF)], Label)).
 file_href(+FilePath, -HREF, +Options) is det
Find URL for refering to FilePath based on Options.
 2364file_href(_, HREF, Options) :-
 2365    option(href(HREF), Options),
 2366    !.
 2367file_href(File, HREF, Options) :-
 2368    file_href_real(File, HREF0, Options),
 2369    map_extension(HREF0, HREF, Options).
 map_extension(+HREFIn, -HREFOut, Options) is det
Replace extension using the option
 2377map_extension(HREF0, HREF, Options) :-
 2378    option(map_extension(Map), Options),
 2379    file_name_extension(Base, Old, HREF0),
 2380    memberchk(Old-New, Map),
 2381    !,
 2382    file_name_extension(Base, New, HREF).
 2383map_extension(HREF, HREF, _).
 2384
 2385
 2386file_href_real(File, HREF, Options) :-
 2387    (   option(absolute_path(Path), Options)
 2388    ;   existing_linked_file(File, Path)
 2389    ),
 2390    !,
 2391    (   option(files(Map), Options),
 2392        memberchk(file(Path, LinkFile), Map)
 2393    ->  true
 2394    ;   LinkFile = Path
 2395    ),
 2396    file_href(LinkFile, HREF).
 2397file_href_real(File, HREF, _) :-
 2398    directory_alias(Alias),
 2399    Term =.. [Alias,File],
 2400    absolute_file_name(Term, _,
 2401                       [ access(read),
 2402                         file_errors(fail)
 2403                       ]),
 2404    !,
 2405    http_absolute_location(Term, HREF, []).
 2406
 2407directory_alias(icons).
 2408directory_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.
 2418file_href(Path, HREF) :-                % a loaded Prolog file
 2419    source_file(Path),
 2420    !,
 2421    doc_file_href(Path, HREF).
 2422file_href(Path, HREF) :-
 2423    (   nb_current(pldoc_output, CFile)
 2424    ;   nb_current(pldoc_file, CFile)
 2425    ),
 2426    CFile \== [],
 2427    !,
 2428    relative_file_name(Path, CFile, HREF).
 2429file_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.
 2437existing_linked_file(File, Path) :-
 2438    catch(b_getval(pldoc_file, CurrentFile), _, fail),
 2439    CurrentFile \== [],
 2440    absolute_file_name(File, Path,
 2441                       [ relative_to(CurrentFile),
 2442                         access(read),
 2443                         file_errors(fail)
 2444                       ]).
 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]].
 2454include(PI, predicate, _) -->
 2455    !,
 2456    (   html_tokens_for_predicates(PI, [])
 2457    ->  []
 2458    ;   html(['[[', \predref(PI), ']]'])
 2459    ).
 2460include(File, image, Options) -->
 2461    { file_name_extension(_, svg, File),
 2462      file_href(File, HREF, Options),
 2463      !,
 2464      include(image_attribute, Options, Attrs0),
 2465      merge_options(Attrs0,
 2466                    [ alt(File),
 2467                      data(HREF),
 2468                      type('image/svg+xml')
 2469                    ], Attrs)
 2470    },
 2471    (   { option(caption(Caption), Options) }
 2472    ->  html(div(class(figure),
 2473                 [ div(class(image), object(Attrs, [])),
 2474                   div(class(caption), Caption)
 2475                 ]))
 2476    ;   html(object(Attrs, []))
 2477    ).
 2478include(File, image, Options) -->
 2479    { file_href(File, HREF, Options),
 2480      !,
 2481      include(image_attribute, Options, Attrs0),
 2482      merge_options(Attrs0,
 2483                    [ alt(File),
 2484                      border(0),
 2485                      src(HREF)
 2486                    ], Attrs)
 2487    },
 2488    (   { option(caption(Caption), Options) }
 2489    ->  html(div(class(figure),
 2490                 [ div(class(image), img(Attrs)),
 2491                   div(class(caption), Caption)
 2492                 ]))
 2493    ;   html(img(Attrs))
 2494    ).
 2495include(File, wiki, _Options) -->       % [[file.txt]] is included
 2496    { access_file(File, read),
 2497      !,
 2498      read_file_to_codes(File, String, []),
 2499      wiki_codes_to_dom(String, [], DOM)
 2500    },
 2501    html(DOM).
 2502include(File, _Type, Options) -->
 2503    link_file(File, Options),
 2504    !.
 2505include(File, _, _) -->
 2506    html(code(class(nofile), ['[[',File,']]'])).
 2507
 2508image_attribute(src(_)).
 2509image_attribute(alt(_)).
 2510image_attribute(title(_)).
 2511image_attribute(align(_)).
 2512image_attribute(width(_)).
 2513image_attribute(height(_)).
 2514image_attribute(border(_)).
 2515image_attribute(class(_)).
 2516image_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]]
 2529html_tokens_for_predicates([], _Options) -->
 2530    [].
 2531html_tokens_for_predicates([H|T], Options) -->
 2532    !,
 2533    html_tokens_for_predicates(H, Options),
 2534    html_tokens_for_predicates(T, Options).
 2535html_tokens_for_predicates(PI, Options) -->
 2536    { PI = _:_/_,
 2537      !,
 2538      (   doc_comment(PI, Pos, _Summary, Comment)
 2539      ->  true
 2540      ;   Comment = ''
 2541      )
 2542    },
 2543    object(PI, [Pos-Comment], [dl], _, Options).
 2544html_tokens_for_predicates(Spec, Options) -->
 2545    { findall(PI, documented_pi(Spec, PI), List),
 2546      List \== [], !
 2547    },
 2548    html_tokens_for_predicates(List, Options).
 2549html_tokens_for_predicates(Spec, Options) -->
 2550    man_page(Spec,
 2551             [ links(false),                % no header
 2552               navtree(false),              % no navigation tree
 2553               footer(false),               % no footer
 2554               synopsis(false)              % no synopsis
 2555             | Options
 2556             ]).
 2557
 2558
 2559documented_pi(Spec, PI) :-
 2560    generalise_spec(Spec, PI),
 2561    doc_comment(PI, _Pos, _Summary, _Comment).
 2562
 2563generalise_spec(Name/Arity, _M:Name/Arity).
 2564generalise_spec(Name//Arity, _M:Name//Arity).
 2565
 2566
 2567                 /*******************************
 2568                 *           WIKI FILES         *
 2569                 *******************************/
 doc_for_wiki_file(+File, +Options) is det
Write HTML for the File containing wiki data.
 2576doc_for_wiki_file(FileSpec, Options) :-
 2577    absolute_file_name(FileSpec, File,
 2578                       [ access(read)
 2579                       ]),
 2580    read_file_to_codes(File, String, []),
 2581    b_setval(pldoc_file, File),
 2582    call_cleanup(reply_wiki_page(File, String, Options),
 2583                 nb_delete(pldoc_file)).
 2584
 2585reply_wiki_page(File, String, Options) :-
 2586    wiki_codes_to_dom(String, [], DOM0),
 2587    title(DOM0, File, Title),
 2588    insert_edit_button(DOM0, File, DOM, Options),
 2589    reply_html_page(pldoc(wiki),
 2590                    title(Title),
 2591                    [ \html_requires(pldoc)
 2592                    | DOM
 2593                    ]).
 2594
 2595title(DOM, _, Title) :-
 2596    sub_term(h1(_,Title), DOM),
 2597    !.
 2598title(_, File, Title) :-
 2599    file_base_name(File, Title).
 2600
 2601insert_edit_button(DOM, _, DOM, Options) :-
 2602    option(edit(false), Options, false),
 2603    !.
 2604insert_edit_button([h1(Attrs,Title)|DOM], File,
 2605                   [h1(Attrs,[ span(style('float:right'),
 2606                                   \edit_button(File, [edit(true)]))
 2607                             | Title
 2608                             ])|DOM], _) :- !.
 2609insert_edit_button(DOM, File,
 2610                   [ h1(class(wiki),
 2611                        [ span(style('float:right'),
 2612                               \edit_button(File, [edit(true)]))
 2613                        ])
 2614                   | DOM
 2615                   ], _).
 2616
 2617
 2618                 /*******************************
 2619                 *            ANCHORS           *
 2620                 *******************************/
 mode_anchor_name(+Mode, -Anchor:atom) is det
Get the anchor name for a mode.
 2626mode_anchor_name(Var, _) :-
 2627    var(Var),
 2628    !,
 2629    instantiation_error(Var).
 2630mode_anchor_name(mode(Head, _), Anchor) :-
 2631    !,
 2632    mode_anchor_name(Head, Anchor).
 2633mode_anchor_name(Head is _Det, Anchor) :-
 2634    !,
 2635    mode_anchor_name(Head, Anchor).
 2636mode_anchor_name(Head, Anchor) :-
 2637    pred_anchor_name(Head, _, Anchor).
 pred_anchor_name(+Head, -PI:atom/integer, -Anchor:atom) is det
Create an HTML anchor name from Head.
 2644pred_anchor_name(//(Head), Name/Arity, Anchor) :-
 2645    !,
 2646    functor(Head, Name, DCGArity),
 2647    Arity is DCGArity+2,
 2648    format(atom(Anchor), '~w/~d', [Name, Arity]).
 2649pred_anchor_name(Head, Name/Arity, Anchor) :-
 2650    functor(Head, Name, Arity),
 2651    format(atom(Anchor), '~w/~d', [Name, Arity]).
 2652
 2653:- multifile prolog:message//1. 2654
 2655prolog:message(pldoc(module_comment_outside_module(Title))) -->
 2656    [ 'PlDoc comment <module> ~w does not appear in a module'-[Title] ]