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-2017, University of Amsterdam
    7                              VU University Amsterdam
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36:- module(pldoc_html,
   37          [ doc_for_file/2,             % +FileSpec, +Options
   38            doc_write_html/3,           % +Stream, +Title, +Term
   39            doc_for_wiki_file/2,        % +FileSpec, +Options
   40                                        % Support doc_index
   41            doc_page_dom/3,             % +Title, +Body, -DOM
   42            print_html_head/1,          % +Stream
   43            predref//1,                 % +PI //
   44            predref//2,                 % +PI, Options //
   45            module_info/3,              % +File, +Options0, -Options
   46            doc_hide_private/3,         % +Doc0, -Doc, +Options
   47            edit_button//2,             % +File, +Options, //
   48            source_button//2,           % +File, +Options, //
   49            zoom_button//2,             % +File, +Options, //
   50            pred_edit_button//2,        % +PredInd, +Options, //
   51            object_edit_button//2,      % +Obj, +Options, //
   52            object_source_button//2,    % +Obj, +Options, //
   53            doc_resources//1,           % +Options
   54            ensure_doc_objects/1,       % +File
   55                                        % Support other backends
   56            doc_file_objects/5,         % +FSpec, -File, -Objs, -FileOpts, +Opts
   57            existing_linked_file/2,     % +FileSpec, -Path
   58            unquote_filespec/2,         % +FileSpec, -Unquoted
   59            doc_tag_title/2,            % +Tag, -Title
   60            mode_anchor_name/2,         % +Mode, -Anchor
   61            pred_anchor_name/3,         % +Head, -PI, -Anchor
   62            private/2,                  % +Obj, +Options
   63            (multifile)/2,              % +Obj, +Options
   64            is_pi/1,                    % @Term
   65            is_op_type/2,               % +Atom, ?Type
   66                                        % Output routines
   67            file//1,                    % +File, //
   68            file//2,                    % +File, +Options, //
   69            include//3,                 % +File, +Type, +Options //
   70            tags//1,                    % +Tags, //
   71            term//3,                    % +Text, +Term, +Bindings, //
   72            file_header//2,             % +File, +Options, //
   73            objects//2,                 % +Objects, +Options, //
   74            object_ref//2,              % +Object, +Options, //
   75            object_name//2,             % +Object, +Object
   76            object_href/2,              % +Object, -URL
   77            object_tree//3,             % +Tree, +Current, +Options
   78            object_page//2,             % +Object, +Options, //
   79            object_page_header//2,      % +File, +Options, //
   80            object_synopsis//2,         % +Object, +Options, //
   81            object_page_footer//2       % +Object, +Options, //
   82          ]).   83:- use_module(library(lists)).   84:- use_module(library(option)).   85:- use_module(library(uri)).   86:- use_module(library(readutil)).   87:- use_module(library(http/html_write)).   88:- use_module(library(http/http_dispatch)).   89:- use_module(library(http/http_wrapper)).   90:- use_module(library(http/http_path)).   91:- use_module(library(http/html_head)).   92:- use_module(library(http/term_html)).   93:- use_module(library(http/jquery)).   94:- use_module(library(debug)).   95:- use_module(library(apply)).   96:- use_module(library(pairs)).   97:- use_module(library(filesex)).   98:- use_module(doc_process).   99:- use_module(doc_man).  100:- use_module(doc_modes).  101:- use_module(doc_wiki).  102:- use_module(doc_search).  103:- use_module(doc_index).  104:- use_module(doc_util).  105:- 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. */
  116:- public
  117    args//1,                        % Called from \Term output created
  118    pred_dt//3,                     % by the wiki renderer
  119    section//2,
  120    tag//2.  121
  122
  123:- predicate_options(doc_for_wiki_file/2, 2,
  124                     [ edit(boolean)
  125                     ]).  126:- predicate_options(doc_hide_private/3, 3,
  127                     [module(atom), public(list), public_only(boolean)]).  128:- predicate_options(edit_button//2, 2,
  129                     [ edit(boolean)
  130                     ]).  131:- predicate_options(file//2, 2,
  132                     [ label(any),
  133                       absolute_path(atom),
  134                       href(atom),
  135                       map_extension(list),
  136                       files(list),
  137                       edit_handler(atom)
  138                     ]).  139:- predicate_options(file_header//2, 2,
  140                     [ edit(boolean),
  141                       files(list),
  142                       public_only(boolean)
  143                     ]).  144:- predicate_options(include//3, 3,
  145                     [ absolute_path(atom),
  146                       class(atom),
  147                       files(list),
  148                       href(atom),
  149                       label(any),
  150                       map_extension(list)
  151                     ]).  152:- predicate_options(object_edit_button//2, 2,
  153                     [ edit(boolean),
  154                       pass_to(pred_edit_button//2, 2)
  155                     ]).  156:- predicate_options(object_page//2, 2,
  157                     [ for(any),
  158                       header(boolean),
  159                       links(boolean),
  160                       no_manual(boolean),
  161                       try_manual(boolean),
  162                       search_in(oneof([all,app,man])),
  163                       search_match(oneof([name,summary])),
  164                       search_options(boolean)
  165                     ]).  166:- predicate_options(object_ref//2, 2,
  167                     [ files(list),
  168                       qualify(boolean),
  169                       style(oneof([number,title,number_title])),
  170                       secref_style(oneof([number,title,number_title]))
  171                     ]).  172:- predicate_options(object_synopsis//2, 2,
  173                     [ href(atom)
  174                     ]).  175:- predicate_options(pred_dt//3, 3,
  176                     [ edit(boolean)
  177                     ]).  178:- predicate_options(pred_edit_button//2, 2,
  179                     [ edit(boolean)
  180                     ]).  181:- predicate_options(predref//2, 2,
  182                     [ files(list),
  183                       prefer(oneof([manual,app])),
  184                       pass_to(object_ref/4, 2)
  185                     ]).  186:- predicate_options(private/2, 2,
  187                     [ module(atom),
  188                       public(list)
  189                     ]).  190:- predicate_options(source_button//2, 2,
  191                     [ files(list)
  192                     ]).  193
  194
  195                 /*******************************
  196                 *           RESOURCES          *
  197                 *******************************/
  198
  199:- html_resource(pldoc_css,
  200                 [ virtual(true),
  201                   requires([ pldoc_resource('pldoc.css')
  202                            ])
  203                 ]).  204:- html_resource(pldoc_resource('pldoc.js'),
  205                 [ requires([ jquery
  206                            ])
  207                 ]).  208:- html_resource(pldoc_js,
  209                 [ virtual(true),
  210                   requires([ pldoc_resource('pldoc.js')
  211                            ])
  212                 ]).  213:- html_resource(pldoc,
  214                 [ virtual(true),
  215                   requires([ pldoc_css,
  216                              pldoc_js
  217                            ])
  218                 ]).  219
  220
  221                 /*******************************
  222                 *       FILE PROCESSING        *
  223                 *******************************/
 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.
  244doc_for_file(FileSpec, Options) :-
  245    doc_file_objects(FileSpec, File, Objects, FileOptions, Options),
  246    doc_file_title(File, Title, FileOptions, Options),
  247    doc_write_page(
  248        pldoc(file(File, Title)),
  249        title(Title),
  250        \prolog_file(File, Objects, FileOptions, Options),
  251        Options).
  252
  253doc_file_title(_, Title, _, Options) :-
  254    option(title(Title), Options),
  255    !.
  256doc_file_title(File, Title, FileOptions, _) :-
  257    memberchk(file(Title0, _Comment), FileOptions),
  258    !,
  259    file_base_name(File, Base),
  260    atomic_list_concat([Base, ' -- ', Title0], Title).
  261doc_file_title(File, Title, _, _) :-
  262    file_base_name(File, Title).
  263
  264:- html_meta doc_write_page(+, html, html, +).  265
  266doc_write_page(Style, Head, Body, Options) :-
  267    option(files(_), Options),
  268    !,
  269    phrase(page(Style, Head, Body), HTML),
  270    print_html(HTML).
  271doc_write_page(Style, Head, Body, _) :-
  272    reply_html_page(Style, Head, Body).
  273
  274
  275prolog_file(File, Objects, FileOptions, Options) -->
  276    { b_setval(pldoc_file, File),   % TBD: delete?
  277      file_directory_name(File, Dir)
  278    },
  279    html([ \doc_resources(Options),
  280           \doc_links(Dir, FileOptions),
  281           \file_header(File, FileOptions)
  282         | \objects(Objects, FileOptions)
  283         ]),
  284    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 ...
  291doc_resources(Options) -->
  292    { option(resource_directory(ResDir), Options),
  293      nb_current(pldoc_output, OutputFile),
  294      !,
  295      directory_file_path(ResDir, 'pldoc.css', Res),
  296      relative_file_name(Res, OutputFile, Ref)
  297    },
  298    html_requires(Ref).
  299doc_resources(Options) -->
  300    { option(html_resources(Resoures), Options, pldoc)
  301    },
  302    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
  331doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  332    xref_current_source(FileSpec),
  333    xref_option(FileSpec, comments(collect)),
  334    !,
  335    File = FileSpec,
  336    findall(Object, xref_doc_object(File, Object), Objects0),
  337    reply_file_objects(File, Objects0, Objects, FileOptions, Options).
  338doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  339    absolute_file_name(FileSpec, File,
  340                       [ file_type(prolog),
  341                         access(read)
  342                       ]),
  343    source_file(File),
  344    !,
  345    ensure_doc_objects(File),
  346    Pos = File:Line,
  347    findall(Line-doc(Obj,Pos,Comment),
  348            doc_comment(Obj, Pos, _, Comment), Pairs),
  349    sort(Pairs, Pairs1),            % remove duplicates
  350    keysort(Pairs1, ByLine),
  351    pairs_values(ByLine, Objs0),
  352    reply_file_objects(File, Objs0, Objects, FileOptions, Options).
  353doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
  354    absolute_file_name(FileSpec, File,
  355                       [ file_type(prolog),
  356                         access(read)
  357                       ]),
  358    xref_source(File, [silent(true)]),
  359    findall(Object, xref_doc_object(File, Object), Objects0),
  360    reply_file_objects(File, Objects0, Objects, FileOptions, Options).
  361
  362
  363reply_file_objects(File, Objs0, Objects, FileOptions, Options) :-
  364    module_info(File, ModuleOptions, Options),
  365    file_info(Objs0, Objs1, FileOptions, ModuleOptions),
  366    doc_hide_private(Objs1, ObjectsSelf, ModuleOptions),
  367    include_reexported(ObjectsSelf, Objects, File, FileOptions).
  368
  369include_reexported(SelfObjects, Objects, File, Options) :-
  370    option(include_reexported(true), Options),
  371    option(module(Module), Options),
  372    option(public(Exports), Options),
  373    select_undocumented(Exports, Module, SelfObjects, Undoc),
  374    re_exported_doc(Undoc, File, Module, REObjs, _),
  375    REObjs \== [],
  376    !,
  377    append(SelfObjects, REObjs, Objects).
  378include_reexported(Objects, Objects, _, _).
 xref_doc_object(File, DocObject) is nondet
  383xref_doc_object(File, doc(M:module(Title),File:0,Comment)) :-
  384    xref_comment(File, Title, Comment),
  385    xref_module(File, M).
  386xref_doc_object(File, doc(M:Name/Arity,File:0,Comment)) :-
  387    xref_comment(File, Head, _Summary, Comment),
  388    xref_module(File, Module),
  389    strip_module(Module:Head, M, Plain),
  390    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.
  401:- dynamic
  402    no_comments/2.  403
  404ensure_doc_objects(File) :-
  405    source_file(File),
  406    !,
  407    (   doc_file_has_comments(File)
  408    ->  true
  409    ;   no_comments(File, TimeChecked),
  410        time_file(File, TimeChecked)
  411    ->  true
  412    ;   xref_source(File, [silent(true), comments(store)]),
  413        retractall(no_comments(File, _)),
  414        (   doc_file_has_comments(File)
  415        ->  true
  416        ;   time_file(File, TimeChecked),
  417            assertz(no_comments(File, TimeChecked))
  418        )
  419    ).
  420ensure_doc_objects(File) :-
  421    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.
  428module_info(File, [module(Module), public(Exports)|Options], Options) :-
  429    module_property(Module, file(File)),
  430    !,
  431    module_property(Module, exports(Exports)).
  432module_info(File, [module(Module), public(Exports)|Options], Options) :-
  433    xref_module(File, Module),
  434    !,
  435    findall(PI, xref_exported_pi(File, PI), Exports).
  436module_info(_, Options, Options).
  437
  438xref_exported_pi(Src, Name/Arity) :-
  439    xref_exported(Src, Head),
  440    functor(Head, Name, Arity).
 doc_hide_private(+Objs, +Public, +Options)
Remove the private objects from Objs according to Options.
  446doc_hide_private(Objs, Objs, Options) :-
  447    option(public_only(false), Options, true),
  448    !.
  449doc_hide_private(Objs0, Objs, Options) :-
  450    hide_private(Objs0, Objs, Options).
  451
  452hide_private([], [], _).
  453hide_private([H|T0], T, Options) :-
  454    obj(H, Obj),
  455    private(Obj, Options),
  456    !,
  457    hide_private(T0, T, Options).
  458hide_private([H|T0], [H|T], Options) :-
  459    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.
  467obj(doc(Obj0, _Pos, _Summary), Obj) :-
  468    !,
  469    (   Obj0 = [Obj|_]
  470    ->  true
  471    ;   Obj = Obj0
  472    ).
  473obj(Obj0, Obj) :-
  474    (   Obj0 = [Obj|_]
  475    ->  true
  476    ;   Obj = Obj0
  477    ).
 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.
  486:- multifile
  487    prolog:doc_is_public_object/1.  488
  489private(Object, _Options):-
  490    prolog:doc_is_public_object(Object), !, fail.
  491private(Module:PI, Options) :-
  492    multifile(Module:PI, Options), !, fail.
  493private(Module:PI, Options) :-
  494    option(module(Module), Options),
  495    option(public(Public), Options),
  496    !,
  497    \+ ( member(PI2, Public),
  498         eq_pi(PI, PI2)
  499       ).
  500private(Module:PI, _Options) :-
  501    module_property(Module, file(_)),      % A loaded module
  502    !,
  503    export_list(Module, Exports),
  504    \+ ( member(PI2, Exports),
  505         eq_pi(PI, PI2)
  506       ).
  507private(Module:PI, _Options) :-
  508    \+ (pi_to_head(PI, Head),
  509        xref_exported(Source, Head),
  510        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.
  521multifile(Obj, _Options) :-
  522    strip_module(user:Obj, Module, PI),
  523    pi_to_head(PI, Head),
  524    (   predicate_property(Module:Head, multifile)
  525    ;   xref_module(Source, Module),
  526        xref_defined(Source, Head, multifile(_))
  527    ),
  528    !.
  529
  530pi_to_head(Var, _) :-
  531    var(Var), !, fail.
  532pi_to_head(Name/Arity, Term) :-
  533    functor(Term, Name, Arity).
  534pi_to_head(Name//DCGArity, Term) :-
  535    Arity is DCGArity+2,
  536    functor(Term, Name, Arity).
 file_info(+Comments, -RestComment, -FileOptions, +OtherOptions) is det
Add options file(Title, Comment) to OtherOptions if available.
  542file_info(Comments, RestComments, [file(Title, Comment)|Opts], Opts) :-
  543    select(doc(_:module(Title),_,Comment), Comments, RestComments),
  544    !.
  545file_info(Comments, Comments, Opts, Opts).
 file_header(+File, +Options)// is det
Create the file header.
  552file_header(File, Options) -->
  553    { memberchk(file(Title, Comment), Options),
  554      !,
  555      file_base_name(File, Base)
  556    },
  557    file_title([Base, ' -- ', Title], File, Options),
  558    { is_structured_comment(Comment, Prefixes),
  559      string_codes(Comment, Codes),
  560      indented_lines(Codes, Prefixes, Lines),
  561      section_comment_header(Lines, _Header, Lines1),
  562      wiki_lines_to_dom(Lines1, [], DOM)
  563    },
  564    html(DOM).
  565file_header(File, Options) -->
  566    { file_base_name(File, Base)
  567    },
  568    file_title([Base], File, Options).
 file_title(+Title:list, +File, +Options)// is det
Emit the file-header and manipulation buttons.
  575file_title(Title, File, Options) -->
  576    prolog:doc_file_title(Title, File, Options),
  577    !.
  578file_title(Title, File, Options) -->
  579    { file_base_name(File, Base)
  580    },
  581    html(h1(class(file),
  582            [ span(style('float:right'),
  583                   [ \reload_button(File, Base, Options),
  584                     \zoom_button(Base, Options),
  585                     \source_button(Base, Options),
  586                     \edit_button(File, Options)
  587                   ])
  588            | Title
  589            ])).
 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.
  599reload_button(File, _Base, Options) -->
  600    { \+ source_file(File),
  601      \+ option(files(_), Options)
  602    },
  603    !,
  604    html(span(class(file_anot), '[not loaded]')).
  605reload_button(_File, Base, Options) -->
  606    { option(edit(true), Options),
  607      !,
  608      option(public_only(Public), Options, true)
  609    },
  610    html(a(href(Base+[reload(true), public_only(Public)]),
  611           img([ class(action),
  612                 alt('Reload'),
  613                 title('Make & Reload'),
  614                 src(location_by_id(pldoc_resource)+'reload.png')
  615               ]))).
  616reload_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.
  624edit_button(File, Options) -->
  625    { option(edit(true), Options)
  626    },
  627    !,
  628    html(a([ onClick('HTTPrequest(\'' +
  629                     location_by_id(pldoc_edit) + [file(File)] +
  630                     '\')')
  631           ],
  632           img([ class(action),
  633                 alt(edit),
  634                 title('Edit file'),
  635                 src(location_by_id(pldoc_resource)+'edit.png')
  636             ]))).
  637edit_button(_, _) -->
  638    [].
 zoom_button(BaseName, +Options)// is det
Add zoom in/out button to show/hide the private documentation.
  645zoom_button(_, Options) -->
  646    { option(files(_Map), Options) },
  647    !.    % generating files
  648zoom_button(Base, Options) -->
  649    {   (   option(public_only(true), Options, true)
  650        ->  Zoom = 'public.png',
  651            Alt = 'Public',
  652            Title = 'Click to include private',
  653            PublicOnly = false
  654        ;   Zoom = 'private.png',
  655            Alt = 'All predicates',
  656            Title = 'Click to show exports only',
  657            PublicOnly = true
  658        )
  659    },
  660    html(a(href(Base+[public_only(PublicOnly)]),
  661           img([ class(action),
  662                 alt(Alt),
  663                 title(Title),
  664                 src(location_by_id(pldoc_resource)+Zoom)
  665               ]))).
 source_button(+File, +Options)// is det
Add show-source button.
  672source_button(_File, Options) -->
  673    { option(files(_Map), Options) },
  674    !.    % generating files
  675source_button(File, _Options) -->
  676    { (   is_absolute_file_name(File)
  677      ->  doc_file_href(File, HREF0)
  678      ;   HREF0 = File
  679      )
  680    },
  681    html(a(href(HREF0+[show(src)]),
  682           img([ class(action),
  683                 alt('Show source'),
  684                 title('Show source'),
  685                 src(location_by_id(pldoc_resource)+'source.png')
  686               ]))).
 objects(+Objects:list, +Options)// is det
Emit the documentation body. Options includes:
navtree(+Boolean)
If true, provide a navitation tree.
  696objects(Objects, Options) -->
  697    { option(navtree(true), Options),
  698      !,
  699      objects_nav_tree(Objects, Tree)
  700    },
  701    html([ div(class(navtree),
  702               div(class(navwindow),
  703                   \nav_tree(Tree, Objects, Options))),
  704           div(class(navcontent),
  705               \objects_nt(Objects, Options))
  706         ]).
  707objects(Objects, Options) -->
  708    objects_nt(Objects, Options).
  709
  710objects_nt(Objects, Options) -->
  711    objects(Objects, [body], Options).
  712
  713objects([], Mode, _) -->
  714    pop_mode(body, Mode, _).
  715objects([Obj|T], Mode, Options) -->
  716    object(Obj, Mode, Mode1, Options),
  717    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.
  728object(doc(Obj,Pos,Comment), Mode0, Mode, Options) -->
  729    !,
  730    object(Obj, [Pos-Comment], Mode0, Mode, [scope(file)|Options]).
  731object(Obj, Mode0, Mode, Options) -->
  732    { findall(Pos-Comment,
  733              doc_comment(Obj, Pos, _Summary, Comment),
  734              Pairs)
  735    },
  736    !,
  737    { b_setval(pldoc_object, Obj) },
  738    object(Obj, Pairs, Mode0, Mode, Options).
  739
  740object(Obj, Pairs, Mode0, Mode, Options) -->
  741    { is_pi(Obj),
  742      !,
  743      maplist(pred_dom(Obj, Options), Pairs, DOMS),
  744      append(DOMS, DOM)
  745    },
  746    need_mode(dl, Mode0, Mode),
  747    html(DOM).
  748object([Obj|_Same], Pairs, Mode0, Mode, Options) -->
  749    !,
  750    object(Obj, Pairs, Mode0, Mode, Options).
  751object(Obj, _Pairs, Mode, Mode, _Options) -->
  752    { debug(pldoc, 'Skipped ~p', [Obj]) },
  753    [].
  754
  755pred_dom(Obj, Options, Pos-Comment, DOM) :-
  756    is_structured_comment(Comment, Prefixes),
  757    string_codes(Comment, Codes),
  758    indented_lines(Codes, Prefixes, Lines),
  759    strip_module(user:Obj, Module, _),
  760    process_modes(Lines, Module, Pos, Modes, Args, Lines1),
  761    (   private(Obj, Options)
  762    ->  Class = privdef             % private definition
  763    ;   multifile(Obj, Options)
  764    ->  (   option(scope(file), Options)
  765        ->  (   more_doc(Obj, Pos)
  766            ->  Class = multidef(object(Obj))
  767            ;   Class = multidef
  768            )
  769        ;   Class = multidef(file((Pos)))
  770        )
  771    ;   Class = pubdef              % public definition
  772    ),
  773    (   Obj = Module:_
  774    ->  POptions = [module(Module)|Options]
  775    ;   POptions = Options
  776    ),
  777    Pos = File:Line,
  778    DTOptions = [file(File),line(Line)|POptions],
  779    DOM = [\pred_dt(Modes, Class, DTOptions), dd(class=defbody, DOM1)],
  780    wiki_lines_to_dom(Lines1, Args, DOM0),
  781    strip_leading_par(DOM0, DOM1).
  782
  783more_doc(Obj, File:_) :-
  784    doc_comment(Obj, File2:_, _, _),
  785    File2 \== File,
  786    !.
 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.
  795need_mode(Mode, Stack, Stack) -->
  796    { Stack = [Mode|_] },
  797    !,
  798    [].
  799need_mode(Mode, Stack, Rest) -->
  800    { memberchk(Mode, Stack)
  801    },
  802    !,
  803    pop_mode(Mode, Stack, Rest).
  804need_mode(Mode, Stack, [Mode|Stack]) -->
  805    !,
  806    html_begin(Mode).
  807
  808pop_mode(Mode, Stack, Stack) -->
  809    { Stack = [Mode|_] },
  810    !,
  811    [].
  812pop_mode(Mode, [H|Rest0], Rest) -->
  813    html_end(H),
  814    pop_mode(Mode, Rest0, Rest).
 undocumented(+File, +Objects, +Options)// is det
Describe undocumented predicates if the file is a module file.
  820undocumented(File, Objs, Options) -->
  821    { memberchk(module(Module), Options),
  822      memberchk(public(Exports), Options),
  823      select_undocumented(Exports, Module, Objs, Undoc),
  824      re_exported_doc(Undoc, File, Module, REObjs, ReallyUnDoc)
  825    },
  826    !,
  827    re_exported_doc(REObjs, Options),
  828    undocumented(ReallyUnDoc, Options).
  829undocumented(_, _, _) -->
  830    [].
  831
  832re_exported_doc([], _) --> !.
  833re_exported_doc(Objs, Options) -->
  834    reexport_header(Objs, Options),
  835    objects(Objs, Options).
  836
  837reexport_header(_, Options) -->
  838    { option(reexport_header(true), Options, true)
  839    },
  840    !,
  841    html([ h2(class(wiki), 'Re-exported predicates'),
  842           p([ 'The following predicates are re-exported from other ',
  843               'modules'
  844             ])
  845         ]).
  846reexport_header(_, _) -->
  847    [].
  848
  849undocumented([], _) --> !.
  850undocumented(UnDoc, Options) -->
  851    html([ h2(class(undoc), 'Undocumented predicates'),
  852           p(['The following predicates are exported, but not ',
  853              'or incorrectly documented.'
  854             ]),
  855           dl(class(undoc),
  856              \undocumented_predicates(UnDoc, Options))
  857         ]).
  858
  859
  860undocumented_predicates([], _) -->
  861    [].
  862undocumented_predicates([H|T], Options) -->
  863    undocumented_pred(H, Options),
  864    undocumented_predicates(T, Options).
  865
  866undocumented_pred(Name/Arity, Options) -->
  867    { functor(Head, Name, Arity) },
  868    html(dt(class=undoc, \pred_mode(Head, [], _, Options))).
  869
  870select_undocumented([], _, _, []).
  871select_undocumented([PI|T0], M, Objs, [PI|T]) :-
  872    is_pi(PI),
  873    \+ in_doc(M:PI, Objs),
  874    !,
  875    select_undocumented(T0, M, Objs, T).
  876select_undocumented([_|T0], M, Objs, T) :-
  877    select_undocumented(T0, M, Objs, T).
  878
  879in_doc(PI, Objs) :-
  880    member(doc(O,_,_), Objs),
  881    (   is_list(O)
  882    ->  member(O2, O),
  883        eq_pi(PI, O2)
  884    ;   eq_pi(PI, O)
  885    ).
 eq_pi(PI1, PI2) is semidet
True if PI1 and PI2 refer to the same predicate.
  892eq_pi(PI, PI) :- !.
  893eq_pi(M:PI1, M:PI2) :-
  894    atom(M),
  895    !,
  896    eq_pi(PI1, PI2).
  897eq_pi(Name/A, Name//DCGA) :-
  898    A =:= DCGA+2,
  899    !.
  900eq_pi(Name//DCGA, Name/A) :-
  901    A =:= DCGA+2.
 is_pi(@Term) is semidet
True if Term is a predicate indicator.
  907is_pi(Var) :-
  908    var(Var),
  909    !,
  910    fail.
  911is_pi(_:PI) :-
  912    !,
  913    is_pi(PI).
  914is_pi(_/_).
  915is_pi(_//_).
 re_exported_doc(+Undoc:list(pi), +File:atom, +Module:atom, -ImportedDoc, -ReallyUnDoc:list(pi))
  921re_exported_doc([], _, _, [], []).
  922re_exported_doc([PI|T0], File, Module, [doc(Orig:PI,Pos,Comment)|ObjT], UnDoc) :-
  923    pi_to_head(PI, Head),
  924    (   predicate_property(Module:Head, imported_from(Orig))
  925    ->  true
  926    ;   xref_defined(File, Head, imported(File2)),
  927        ensure_doc_objects(File2),
  928        xref_module(File2, Orig)
  929    ),
  930    doc_comment(Orig:PI, Pos, _, Comment),
  931    !,
  932    re_exported_doc(T0, File, Module, ObjT, UnDoc).
  933re_exported_doc([PI|T0], File, Module, REObj, [PI|UnDoc]) :-
  934    re_exported_doc(T0, File, Module, REObj, UnDoc).
  935
  936
  937                 /*******************************
  938                 *      SINGLE OBJECT PAGE      *
  939                 *******************************/
 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.
  949object_page(Obj, Options) -->
  950    prolog:doc_object_page(Obj, Options),
  951    !,
  952    object_page_footer(Obj, Options).
  953object_page(Obj, Options) -->
  954    { doc_comment(Obj, File:_Line, _Summary, _Comment)
  955    },
  956    !,
  957    (   { \+ ( doc_comment(Obj, File2:_, _, _),
  958               File2 \== File )
  959        }
  960    ->  html([ \html_requires(pldoc),
  961               \object_page_header(File, Options),
  962               \object_synopsis(Obj, []),
  963               \objects([Obj], Options)
  964             ])
  965    ;   html([ \html_requires(pldoc),
  966               \object_page_header(-, Options),
  967               \objects([Obj], [synopsis(true)|Options])
  968             ])
  969    ),
  970    object_page_footer(Obj, Options).
  971object_page(M:Name/Arity, Options) -->          % specified module, but public
  972    { functor(Head, Name, Arity),
  973      (   predicate_property(M:Head, exported)
  974      ->  module_property(M, class(library))
  975      ;   \+ predicate_property(M:Head, defined)
  976      )
  977    },
  978    prolog:doc_object_page(Name/Arity, Options),
  979    !,
  980    object_page_footer(Name/Arity, Options).
  981
  982object_page_header(File, Options) -->
  983    prolog:doc_page_header(file(File), Options),
  984    !.
  985object_page_header(File, Options) -->
  986    { option(header(true), Options, true) },
  987    !,
  988    html(div(class(navhdr),
  989             [ div(class(jump), \file_link(File)),
  990               div(class(search), \search_form(Options)),
  991               br(clear(right))
  992             ])).
  993object_page_header(_, _) --> [].
  994
  995file_link(-) -->
  996    !,
  997    places_menu(-).
  998file_link(File) -->
  999    { file_directory_name(File, Dir)
 1000    },
 1001    places_menu(Dir),
 1002    html([ div(a(href(location_by_id(pldoc_doc)+File), File))
 1003         ]).
 object_page_footer(+Obj, +Options)// is det
Call the hook doc_object_page_footer//2. This hook will be used to deal with annotations.
 1010object_page_footer(Obj, Options) -->
 1011    prolog:doc_object_page_footer(Obj, Options).
 1012object_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.
 1026object_synopsis(Name/Arity, _) -->
 1027    { functor(Head, Name, Arity),
 1028      predicate_property(system:Head, built_in)
 1029    },
 1030    synopsis([span(class(builtin), 'built-in')]).
 1031object_synopsis(Name/Arity, Options) -->
 1032    !,
 1033    object_synopsis(_:Name/Arity, Options).
 1034object_synopsis(M:Name/Arity, Options) -->
 1035    { functor(Head, Name, Arity),
 1036      predicate_property(M:Head, exported),
 1037      \+ predicate_property(M:Head, imported_from(_)),
 1038      module_property(M, file(File)),
 1039      file_name_on_path(File, Spec),
 1040      !,
 1041      unquote_filespec(Spec, Unquoted),
 1042      (   predicate_property(Head, autoload(FileBase)),
 1043          file_name_extension(FileBase, _Ext, File)
 1044      ->  Extra = [span(class(autoload), '(can be autoloaded)')]
 1045      ;   Extra = []
 1046      )
 1047    },
 1048    (   { option(href(HREF), Options) }
 1049    ->  synopsis([code([':- use_module(',a(href(HREF), '~q'-[Unquoted]),').'])|Extra])
 1050    ;   synopsis([code(':- use_module(~q).'-[Unquoted])|Extra])
 1051    ).
 1052object_synopsis(f(_/_), _) -->
 1053    synopsis(span(class(function),
 1054                  [ 'Arithmetic function (see ',
 1055                    \object_ref(is/2, []),
 1056                    ')'
 1057                  ])).
 1058object_synopsis(c(Func), _) -->
 1059    { sub_atom(Func, 0, _, _, 'PL_')
 1060    },
 1061    !,
 1062    synopsis([span(class(cfunc), 'C-language interface function')]).
 1063object_synopsis(_, _) --> [].
 1064
 1065synopsis(Text) -->
 1066    html(div(class(synopsis),
 1067             [ span(class('synopsis-hdr'), 'Availability:')
 1068             | Text
 1069             ])).
 unquote_filespec(+Spec, -Unquoted) is det
Translate e.g. library('semweb/rdf_db') into library(semweb/rdf_db).
 1076unquote_filespec(Spec, Unquoted) :-
 1077    compound(Spec),
 1078    Spec =.. [Alias,Path],
 1079    atomic_list_concat(Parts, /, Path),
 1080    maplist(need_no_quotes, Parts),
 1081    !,
 1082    parts_to_path(Parts, UnquotedPath),
 1083    Unquoted =.. [Alias, UnquotedPath].
 1084unquote_filespec(Spec, Spec).
 1085
 1086need_no_quotes(Atom) :-
 1087    format(atom(A), '~q', [Atom]),
 1088    \+ sub_atom(A, 0, _, _, '\'').
 1089
 1090parts_to_path([One], One) :- !.
 1091parts_to_path(List, More/T) :-
 1092    (   append(H, [T], List)
 1093    ->  parts_to_path(H, More)
 1094    ).
 1095
 1096
 1097                 /*******************************
 1098                 *             PRINT            *
 1099                 *******************************/
 doc_write_html(+Out:stream, +Title:atomic, +DOM) is det
Write HTML for the documentation page DOM using Title to Out.
 1105doc_write_html(Out, Title, Doc) :-
 1106    doc_page_dom(Title, Doc, DOM),
 1107    phrase(html(DOM), Tokens),
 1108    print_html_head(Out),
 1109    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.
 1116doc_page_dom(Title, Body, DOM) :-
 1117    DOM = html([ head([ title(Title),
 1118                        link([ rel(stylesheet),
 1119                               type('text/css'),
 1120                               href(location_by_id(pldoc_resource)+'pldoc.css')
 1121                             ]),
 1122                        script([ src(location_by_id(pldoc_resource)+'pldoc.js'),
 1123                                 type('text/javascript')
 1124                               ], [])
 1125                      ]),
 1126                 body(Body)
 1127               ]).
 print_html_head(+Out:stream) is det
Print the DOCTYPE line.
 1133print_html_head(Out) :-
 1134    format(Out,
 1135           '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" \c
 1136               "http://www.w3.org/TR/html4/strict.dtd">~n', []).
 1137
 1138% Rendering rules
 1139%
 1140% 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.
 1148tags(Tags) -->
 1149    html(dl(class=tags, Tags)).
 tag(+Tag, +Values:list)// is det
Called from \tag(Name, Values) terms produced by doc_wiki.pl.
 1155tag(Tag, Values) -->
 1156    {   doc_tag_title(Tag, Title),
 1157        atom_concat('keyword-', Tag, Class)
 1158    },
 1159    html([ dt(class=Class, Title),
 1160           \tag_values(Values, Class)
 1161         ]).
 1162
 1163tag_values([], _) -->
 1164    [].
 1165tag_values([H|T], Class) -->
 1166    html(dd(class=Class, ['- '|H])),
 1167    tag_values(T, Class).
 doc_tag_title(+Tag, -Title) is det
Title is the name to use for Tag in the generated documentation.
 1174doc_tag_title(Tag, Title) :-
 1175    tag_title(Tag, Title),
 1176    !.
 1177doc_tag_title(Tag, Tag).
 1178
 1179tag_title(compat, 'Compatibility').
 1180tag_title(tbd,    'To be done').
 1181tag_title(see,    'See also').
 1182tag_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).
 1189args(Params) -->
 1190    html([ dt(class=tag, 'Arguments:'),
 1191           dd(table(class=arglist,
 1192                    \arg_list(Params)))
 1193         ]).
 1194
 1195arg_list([]) -->
 1196    [].
 1197arg_list([H|T]) -->
 1198    argument(H),
 1199    arg_list(T).
 1200
 1201argument(arg(Name,Descr)) -->
 1202    html(tr([td(var(Name)), td(class=argdescr, ['- '|Descr])])).
 1203
 1204
 1205                 /*******************************
 1206                 *         NAVIGATION TREE      *
 1207                 *******************************/
 objects_nav_tree(+Objects, -Tree) is det
Provide a navigation tree showing the context of Object. Tree is of the form node(Object, Children).
 1214objects_nav_tree(Objects, Tree) :-
 1215    maplist(object_nav_tree, Objects, Trees),
 1216    union_trees(Trees, Tree0),
 1217    remove_unique_root(Tree0, Tree).
 1218
 1219object_nav_tree(Obj, Tree) :-
 1220    Node = node(directory(Dir), FileNodes),
 1221    FileNode = node(file(File), Siblings),
 1222    doc_comment(Obj, File:_Line, _Summary, _Comment),
 1223    !,
 1224    file_directory_name(File, Dir),
 1225    sibling_file_nodes(Dir, FileNodes0),
 1226    selectchk(node(file(File),[]), FileNodes0, FileNode, FileNodes),
 1227    findall(Sibling, doc_comment(Sibling, File:_, _, _), Siblings0),
 1228    delete(Siblings0, _:module(_), Siblings1),
 1229    doc_hide_private(Siblings1, Siblings2, []),
 1230    flatten(Siblings2, Siblings),   % a comment may describe objects
 1231    embed_directories(Node, Tree).
 1232
 1233sibling_file_nodes(Dir, Nodes) :-
 1234    findall(node(file(File), []),
 1235            (   source_file(File),
 1236                file_directory_name(File, Dir)
 1237            ),
 1238            Nodes).
 1239
 1240embed_directories(Node, Tree) :-
 1241    Node = node(file(File), _),
 1242    !,
 1243    file_directory_name(File, Dir),
 1244    Super = node(directory(Dir), [Node]),
 1245    embed_directories(Super, Tree).
 1246embed_directories(Node, Tree) :-
 1247    Node = node(directory(Dir), _),
 1248    file_directory_name(Dir, SuperDir),
 1249    SuperDir \== Dir,
 1250    !,
 1251    Super = node(directory(SuperDir), [Node]),
 1252    embed_directories(Super, Tree).
 1253embed_directories(Tree, Tree).
 1254
 1255
 1256union_trees([Tree], Tree) :- !.
 1257union_trees([T1,T2|Trees], Tree) :-
 1258    merge_trees(T1, T2, M1),
 1259    union_trees([M1|Trees], Tree).
 1260
 1261merge_trees(node(R, Ch1), node(R, Ch2), node(R, Ch)) :-
 1262    merge_nodes(Ch1, Ch2, Ch).
 1263
 1264merge_nodes([], Ch, Ch) :- !.
 1265merge_nodes(Ch, [], Ch) :- !.
 1266merge_nodes([node(Root, Ch1)|T1], N1, [T1|Nodes]) :-
 1267    selectchk(node(Root, Ch2), N1, N2),
 1268    !,
 1269    merge_trees(node(Root, Ch1), node(Root, Ch2), T1),
 1270    merge_nodes(T1, N2, Nodes).
 1271merge_nodes([Node|T1], N1, [Node|Nodes]) :-
 1272    merge_nodes(T1, N1, Nodes).
 remove_unique_root(+TreeIn, -Tree)
Remove the root part that does not branch
 1278remove_unique_root(node(_, [node(R1, [R2])]), Tree) :-
 1279    !,
 1280    remove_unique_root(node(R1, [R2]), Tree).
 1281remove_unique_root(Tree, Tree).
 nav_tree(+Tree, +Current, +Options)// is det
Render the navigation tree
 1287nav_tree(Tree, Current, Options) -->
 1288    html(ul(class(nav),
 1289            \object_tree(Tree, Current, Options))).
 object_tree(+Tree, +Current, +Options)// is det
Render a tree of objects used for navigation.
 1295object_tree(node(Id, []), Target, Options) -->
 1296    !,
 1297    { node_class(Id, Target, Class) },
 1298    html(li(class(Class),
 1299            \node(Id, Options))).
 1300object_tree(node(Id, Children), Target, Options) -->
 1301    !,
 1302    { node_class(Id, Target, Class) },
 1303    html(li(class(Class),
 1304            [ \node(Id, Options),
 1305              ul(class(nav),
 1306                 \object_trees(Children, Target, Options))
 1307            ])).
 1308object_tree(Id, Target, Options) -->
 1309    !,
 1310    { node_class(Id, Target, Class) },
 1311    html(li(class([obj|Class]), \node(Id, Options))).
 1312
 1313object_trees([], _, _) --> [].
 1314object_trees([H|T], Target, Options) -->
 1315    object_tree(H, Target, Options),
 1316    object_trees(T, Target, Options).
 1317
 1318node_class(Ids, Current, Class) :-
 1319    is_list(Ids),
 1320    !,
 1321    (   member(Id, Ids), memberchk(Id, Current)
 1322    ->  Class = [nav,current]
 1323    ;   Class = [nav]
 1324    ).
 1325node_class(Id, Current, Class) :-
 1326    (   memberchk(Id, Current)
 1327    ->  Class = [nav,current]
 1328    ;   Class = [nav]
 1329    ).
 1330
 1331node(file(File), Options) -->
 1332    !,
 1333    object_ref(file(File), [style(title)|Options]).
 1334node(Id, Options) -->
 1335    object_ref(Id, Options).
 1336
 1337
 1338                 /*******************************
 1339                 *            SECTIONS          *
 1340                 *******************************/
 1341
 1342section(Type, Title) -->
 1343    { string_codes(Title, Codes),
 1344      wiki_codes_to_dom(Codes, [], Content0),
 1345      strip_leading_par(Content0, Content),
 1346      make_section(Type, Content, HTML)
 1347    },
 1348    html(HTML).
 1349
 1350make_section(module,  Title, h1(class=module,  Title)).
 1351make_section(section, Title, h1(class=section, Title)).
 1352
 1353
 1354                 /*******************************
 1355                 *       PRED MODE HEADER       *
 1356                 *******************************/
 pred_dt(+Modes, +Class, Options)// is det
Emit the predicate header.
Arguments:
Modes- List as returned by process_modes/5.
 1364pred_dt(Modes, Class, Options) -->
 1365    pred_dt(Modes, Class, [], _Done, Options).
 1366
 1367pred_dt([], _, Done, Done, _) -->
 1368    [].
 1369pred_dt([H|T], Class, Done0, Done, Options) -->
 1370    { functor(Class, CSSClass, _) },
 1371    html(dt(class=CSSClass,
 1372            [ \pred_mode(H, Done0, Done1, Options),
 1373              \mode_anot(Class)
 1374            ])),
 1375    pred_dt(T, Class, Done1, Done, Options).
 1376
 1377mode_anot(privdef) -->
 1378    !,
 1379    html(span([class(anot), style('float:right')],
 1380              '[private]')).
 1381mode_anot(multidef(object(Obj))) -->
 1382    !,
 1383    { object_href(Obj, HREF) },
 1384    html(span([class(anot), style('float:right')],
 1385              ['[', a(href(HREF), multifile), ']'
 1386              ])).
 1387mode_anot(multidef(file(File:_))) -->
 1388    !,
 1389    { file_name_on_path(File, Spec),
 1390      unquote_filespec(Spec, Unquoted),
 1391      doc_file_href(File, HREF)
 1392    },
 1393    html(span([class(anot), style('float:right')],
 1394              ['[multifile, ', a(href(HREF), '~q'-[Unquoted]), ']'
 1395              ])).
 1396mode_anot(multidef) -->
 1397    !,
 1398    html(span([class(anot), style('float:right')],
 1399              '[multifile]')).
 1400mode_anot(_) -->
 1401    [].
 1402
 1403pred_mode(mode(Head,Vars), Done0, Done, Options) -->
 1404    !,
 1405    { bind_vars(Head, Vars) },
 1406    pred_mode(Head, Done0, Done, Options).
 1407pred_mode(Head is Det, Done0, Done, Options) -->
 1408    !,
 1409    anchored_pred_head(Head, Done0, Done, Options),
 1410    pred_det(Det).
 1411pred_mode(Head, Done0, Done, Options) -->
 1412    anchored_pred_head(Head, Done0, Done, Options).
 1413
 1414bind_vars(Term, Bindings) :-
 1415    bind_vars(Bindings),
 1416    anon_vars(Term).
 1417
 1418bind_vars([]).
 1419bind_vars([Name=Var|T]) :-
 1420    Var = '$VAR'(Name),
 1421    bind_vars(T).
 anon_vars(+Term) is det
Bind remaining variables in Term to '$VAR'('_'), so they are printed as '_'.
 1428anon_vars(Var) :-
 1429    var(Var),
 1430    !,
 1431    Var = '$VAR'('_').
 1432anon_vars(Term) :-
 1433    compound(Term),
 1434    !,
 1435    Term =.. [_|Args],
 1436    maplist(anon_vars, Args).
 1437anon_vars(_).
 1438
 1439
 1440anchored_pred_head(Head, Done0, Done, Options) -->
 1441    { pred_anchor_name(Head, PI, Name) },
 1442    (   { memberchk(PI, Done0) }
 1443    ->  { Done = Done0 },
 1444        pred_head(Head)
 1445    ;   html([ span(style('float:right'),
 1446                    [ \pred_edit_or_source_button(Head, Options),
 1447                      &(nbsp)
 1448                    ]),
 1449               a(name=Name, \pred_head(Head))
 1450             ]),
 1451        { Done = [PI|Done0] }
 1452    ).
 1453
 1454
 1455pred_edit_or_source_button(Head, Options) -->
 1456    { option(edit(true), Options) },
 1457    !,
 1458    pred_edit_button(Head, Options).
 1459pred_edit_or_source_button(Head, Options) -->
 1460    { option(source_link(true), Options) },
 1461    !,
 1462    pred_source_button(Head, Options).
 1463pred_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)
 1477pred_edit_button(_, Options) -->
 1478    { \+ option(edit(true), Options) },
 1479    !.
 1480pred_edit_button(PI0, Options0) -->
 1481    { canonicalise_predref(PI0, PI, Options0, Options) },
 1482    pred_edit_button2(PI, Options).
 1483
 1484pred_edit_button2(Name/Arity, Options) -->
 1485    { \+ ( memberchk(file(_), Options), % always edit if file and line
 1486           memberchk(line(_), Options)  % are given.
 1487         ),
 1488      functor(Head, Name, Arity),
 1489      option(module(M), Options, _),
 1490      \+ ( current_module(M),
 1491           source_file(M:Head, _File)
 1492         )
 1493    },
 1494    !.
 1495pred_edit_button2(Name/Arity, Options) -->
 1496    { include(edit_param, Options, Extra),
 1497      http_link_to_id(pldoc_edit,
 1498                      [name(Name),arity(Arity)|Extra],
 1499                      EditHREF)
 1500    },
 1501    html(a(onClick('HTTPrequest(\'' + EditHREF + '\')'),
 1502           img([ class(action),
 1503                 alt('Edit predicate'),
 1504                 title('Edit predicate'),
 1505                 src(location_by_id(pldoc_resource)+'editpred.png')
 1506               ]))).
 1507pred_edit_button2(_, _) -->
 1508    !,
 1509    [].
 1510
 1511edit_param(module(_)).
 1512edit_param(file(_)).
 1513edit_param(line(_)).
 object_edit_button(+Object, +Options)// is det
Create a button for editing Object.
 1520object_edit_button(_, Options) -->
 1521    { \+ option(edit(true), Options) },
 1522    !.
 1523object_edit_button(PI, Options) -->
 1524    { is_pi(PI) },
 1525    !,
 1526    pred_edit_button(PI, Options).
 1527object_edit_button(_, _) -->
 1528    [].
 pred_source_button(+PredIndicator, +Options)// is det
Create a button for viewing the source of a predicate.
 1535pred_source_button(PI0, Options0) -->
 1536    { canonicalise_predref(PI0, PI, Options0, Options),
 1537      option(module(M), Options, _),
 1538      pred_source_href(PI, M, HREF), !
 1539    },
 1540    html(a([ href(HREF)
 1541           ],
 1542           img([ class(action),
 1543                 alt('Source'),
 1544                 title('Show source'),
 1545                 src(location_by_id(pldoc_resource)+'source.png')
 1546               ]))).
 1547pred_source_button(_, _) -->
 1548    [].
 object_source_button(+Object, +Options)// is det
Create a button for showing the source of Object.
 1555object_source_button(PI, Options) -->
 1556    { is_pi(PI),
 1557      option(source_link(true), Options, true)
 1558    },
 1559    !,
 1560    pred_source_button(PI, Options).
 1561object_source_button(_, _) -->
 1562    [].
 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.
 1570canonicalise_predref(M:PI0, PI, Options0, [module(M)|Options]) :-
 1571    !,
 1572    canonicalise_predref(PI0, PI, Options0, Options).
 1573canonicalise_predref(//(Head), PI, Options0, Options) :-
 1574    !,
 1575    functor(Head, Name, Arity),
 1576    PredArity is Arity + 2,
 1577    canonicalise_predref(Name/PredArity, PI, Options0, Options).
 1578canonicalise_predref(Name//Arity, PI, Options0, Options) :-
 1579    integer(Arity), Arity >= 0,
 1580    !,
 1581    PredArity is Arity + 2,
 1582    canonicalise_predref(Name/PredArity, PI, Options0, Options).
 1583canonicalise_predref(PI, PI, Options, Options) :-
 1584    PI = Name/Arity,
 1585    atom(Name), integer(Arity), Arity >= 0,
 1586    !.
 1587canonicalise_predref(Head, PI, Options0, Options) :-
 1588    functor(Head, Name, Arity),
 1589    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.
 1597pred_head(Var) -->
 1598    { var(Var),
 1599      !,
 1600      instantiation_error(Var)
 1601    }.
 1602pred_head(//(Head)) -->
 1603    !,
 1604    pred_head(Head),
 1605    html(//).
 1606pred_head(M:Head) -->
 1607    html([span(class=module, M), :]),
 1608    pred_head(Head).
 1609pred_head(Head) -->
 1610    { atom(Head) },
 1611    !,
 1612    html(b(class=pred, Head)).
 1613pred_head(Head) -->                     % Infix operators
 1614    { Head =.. [Functor,Left,Right],
 1615      is_op_type(Functor, infix)
 1616    },
 1617    !,
 1618    html([ var(class=arglist, \pred_arg(Left, 1)),
 1619           ' ', b(class=pred, Functor), ' ',
 1620           var(class=arglist, \pred_arg(Right, 2))
 1621         ]).
 1622pred_head(Head) -->                     % Prefix operators
 1623    { Head =.. [Functor,Arg],
 1624      is_op_type(Functor, prefix)
 1625    },
 1626    !,
 1627    html([ b(class=pred, Functor), ' ',
 1628           var(class=arglist, \pred_arg(Arg, 1))
 1629         ]).
 1630pred_head(Head) -->                     % Postfix operators
 1631    { Head =.. [Functor,Arg],
 1632      is_op_type(Functor, postfix)
 1633    },
 1634    !,
 1635    html([ var(class=arglist, \pred_arg(Arg, 1)),
 1636           ' ', b(class=pred, Functor)
 1637         ]).
 1638pred_head(Head) -->                     % Plain terms
 1639    { Head =.. [Functor|Args] },
 1640    html([ b(class=pred, Functor),
 1641           var(class=arglist,
 1642               [ '(', \pred_args(Args, 1), ')' ])
 1643         ]).
 is_op_type(+Atom, ?Type)
True if Atom is an operator of Type. Type is one of prefix, infix or postfix.
 1650is_op_type(Functor, Type) :-
 1651    current_op(_Pri, F, Functor),
 1652    op_type(F, Type).
 1653
 1654op_type(fx,  prefix).
 1655op_type(fy,  prefix).
 1656op_type(xf,  postfix).
 1657op_type(yf,  postfix).
 1658op_type(xfx, infix).
 1659op_type(xfy, infix).
 1660op_type(yfx, infix).
 1661op_type(yfy, infix).
 1662
 1663
 1664pred_args([], _) -->
 1665    [].
 1666pred_args([H|T], I) -->
 1667    pred_arg(H, I),
 1668    (   {T==[]}
 1669    ->  []
 1670    ;   html(', '),
 1671        { I2 is I + 1 },
 1672        pred_args(T, I2)
 1673    ).
 1674
 1675pred_arg(Var, I) -->
 1676    { var(Var) },
 1677    !,
 1678    html(['Arg', I]).
 1679pred_arg(...(Term), I) -->
 1680    !,
 1681    pred_arg(Term, I),
 1682    html('...').
 1683pred_arg(Term, I) -->
 1684    { Term =.. [Ind,Arg],
 1685      mode_indicator(Ind)
 1686    },
 1687    !,
 1688    html([Ind, \pred_arg(Arg, I)]).
 1689pred_arg(Arg:Type, _) -->
 1690    !,
 1691    html([\argname(Arg), :, \argtype(Type)]).
 1692pred_arg(Arg, _) -->
 1693    argname(Arg).
 1694
 1695argname('$VAR'(Name)) -->
 1696    !,
 1697    html(Name).
 1698argname(Name) -->
 1699    !,
 1700    html(Name).
 1701
 1702argtype(Term) -->
 1703    { format(string(S), '~W',
 1704             [ Term,
 1705               [ quoted(true),
 1706                 numbervars(true)
 1707               ]
 1708             ]) },
 1709    html(S).
 1710
 1711pred_det(unknown) -->
 1712    [].
 1713pred_det(Det) -->
 1714    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
 1723term(_, Atom, []) -->
 1724    { atomic(Atom),
 1725      !,
 1726      format(string(S), '~W', [Atom,[quoted(true)]])
 1727    },
 1728    html(span(class=functor, S)).
 1729term(_, Key:Type, [TypeName=Type]) -->
 1730    { atomic(Key)
 1731    },
 1732    !,
 1733    html([span(class='pl-key', Key), :, span(class('pl-var'), TypeName)]).
 1734term(_, Term, Bindings) -->
 1735    { is_mode(Term is det),         % HACK. Bit too strict?
 1736      bind_vars(Bindings)
 1737    },
 1738    !,
 1739    pred_head(Term).
 1740term(_, Term, Bindings) -->
 1741    term(Term,
 1742         [ variable_names(Bindings),
 1743           quoued(true)
 1744         ]).
 1745
 1746
 1747                 /*******************************
 1748                 *             PREDREF          *
 1749                 *******************************/
 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.

 1762predref(Term) -->
 1763    { catch(nb_getval(pldoc_options, Options), _, Options = []) },
 1764    predref(Term, Options).
 1765
 1766predref(Obj, Options) -->
 1767    { Obj = _:_,
 1768      doc_comment(Obj, File:_Line, _, _),
 1769      (   (   option(files(Map), Options)
 1770          ->  memberchk(file(File,_), Map)
 1771          ;   true
 1772          )
 1773      ->  object_href(Obj, HREF, Options)
 1774      ;   manref(Obj, HREF, Options)
 1775      )
 1776    },
 1777    !,
 1778    html(a(href(HREF), \object_name(Obj, [qualify(true)|Options]))).
 1779predref(M:Term, Options) -->
 1780    !,
 1781    predref(Term, M, Options).
 1782predref(Term, Options) -->
 1783    predref(Term, _, Options).
 1784
 1785predref(Name/Arity, _, Options) -->             % Builtin; cannot be overruled
 1786    { prolog:doc_object_summary(Name/Arity, manual, _, _),
 1787      !,
 1788      manref(Name/Arity, HREF, Options)
 1789    },
 1790    html(a([class=builtin, href=HREF], [Name, /, Arity])).
 1791predref(Name/Arity, _, Options) -->             % From packages
 1792    { option(prefer(manual), Options),
 1793      prolog:doc_object_summary(Name/Arity, Category, _, _),
 1794      !,
 1795      manref(Name/Arity, HREF, Options)
 1796    },
 1797    html(a([class=Category, href=HREF], [Name, /, Arity])).
 1798predref(Obj, Module, Options) -->               % Local
 1799    { doc_comment(Module:Obj, File:_Line, _, _),
 1800      (   option(files(Map), Options)
 1801      ->  memberchk(file(File,_), Map)
 1802      ;   true
 1803      )
 1804    },
 1805    !,
 1806    object_ref(Module:Obj, Options).
 1807predref(Name/Arity, Module, Options) -->
 1808    { \+ option(files(_), Options),
 1809      pred_href(Name/Arity, Module, HREF)
 1810    },
 1811    !,
 1812    html(a(href=HREF, [Name, /, Arity])).
 1813predref(Name//Arity, Module, Options) -->
 1814    { \+ option(files(_), Options),
 1815      PredArity is Arity + 2,
 1816      pred_href(Name/PredArity, Module, HREF)
 1817    },
 1818    !,
 1819    html(a(href=HREF, [Name, //, Arity])).
 1820predref(PI, _, Options) -->             % From packages
 1821    { canonical_pi(PI, CPI, HTML),
 1822      (   option(files(_), Options)
 1823      ->  Category = extmanual
 1824      ;   prolog:doc_object_summary(CPI, Category, _, _)
 1825      ),
 1826      manref(CPI, HREF, Options)
 1827    },
 1828    html(a([class=Category, href=HREF], HTML)).
 1829predref(PI, _, _Options) -->
 1830    { canonical_pi(PI, _CPI, HTML)
 1831    },
 1832    !,
 1833    html(span(class=undef, HTML)).
 1834predref(Callable, Module, Options) -->
 1835    { callable(Callable),
 1836      functor(Callable, Name, Arity)
 1837    },
 1838    predref(Name/Arity, Module, Options).
 1839
 1840canonical_pi(Name/Arity, Name/Arity, [Name, /, Arity]) :-
 1841    atom(Name), integer(Arity),
 1842    !.
 1843canonical_pi(Name//Arity, Name/Arity2, [Name, //, Arity]) :-
 1844    atom(Name), integer(Arity),
 1845    !,
 1846    Arity2 is Arity+2.
 manref(+NameArity, -HREF, +Options) is det
Create reference to a manual page. When generating files, this listens to the option man_server(+Server).
 1854manref(PI, HREF, Options) :-
 1855    predname(PI, PredName),
 1856    (   option(files(_Map), Options)
 1857    ->  option(man_server(Server), Options,
 1858               'http://www.swi-prolog.org/pldoc'),
 1859        uri_components(Server, Comp0),
 1860        uri_data(path, Comp0, Path0),
 1861        directory_file_path(Path0, man, Path),
 1862        uri_data(path, Comp0, Path, Components),
 1863        uri_query_components(Query, [predicate=PredName]),
 1864        uri_data(search, Components, Query),
 1865        uri_components(HREF, Components)
 1866    ;   http_link_to_id(pldoc_man, [predicate=PredName], HREF)
 1867    ).
 1868
 1869predname(Name/Arity, PredName) :-
 1870    !,
 1871    format(atom(PredName), '~w/~d', [Name, Arity]).
 1872predname(Module:Name/Arity, PredName) :-
 1873    !,
 1874    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.
 1888pred_href(Name/Arity, Module, HREF) :-
 1889    format(string(FragmentId), '~w/~d', [Name, Arity]),
 1890    uri_data(fragment, Components, FragmentId),
 1891    functor(Head, Name, Arity),
 1892    (   catch(relative_file(Module:Head, File), _, fail)
 1893    ->  uri_data(path, Components, File),
 1894        uri_components(HREF, Components)
 1895    ;   in_file(Module:Head, File)
 1896    ->  (   current_prolog_flag(home, SWI),
 1897            sub_atom(File, 0, _, _, SWI),
 1898            prolog:doc_object_summary(Name/Arity, packages, _, _)
 1899        ->  http_link_to_id(pldoc_man, [predicate=FragmentId], HREF)
 1900        ;   http_location_by_id(pldoc_doc, DocHandler),
 1901            atom_concat(DocHandler, File, Path),
 1902            uri_data(path, Components, Path),
 1903            uri_components(HREF, Components)
 1904        )
 1905    ).
 1906
 1907relative_file(Head, '') :-
 1908    b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
 1909    in_file(Head, CurrentFile),
 1910    !.
 1911relative_file(Head, RelFile) :-
 1912    b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
 1913    in_file(Head, DefFile),
 1914    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.
 1920pred_source_href(Name/Arity, Module, HREF) :-
 1921    format(string(FragmentId), '~w/~d', [Name, Arity]),
 1922    uri_data(fragment, Components, FragmentId),
 1923    uri_query_components(Query, [show=src]),
 1924    uri_data(search, Components, Query),
 1925    functor(Head, Name, Arity),
 1926    (   catch(relative_file(Module:Head, File), _, fail)
 1927    ->  uri_data(path, Components, File),
 1928        uri_components(HREF, Components)
 1929    ;   in_file(Module:Head, File0),
 1930        insert_alias(File0, File),
 1931        http_location_by_id(pldoc_doc, DocHandler),
 1932        atom_concat(DocHandler, File, Path),
 1933        uri_data(path, Components, Path),
 1934        uri_components(HREF, Components)
 1935    ).
 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.
 1944object_ref([], _) -->
 1945    !,
 1946    [].
 1947object_ref([H|T], Options) -->
 1948    !,
 1949    object_ref(H, Options),
 1950    (   {T == []}
 1951    ->  html(', '),
 1952        object_ref(T, Options)
 1953    ;   []
 1954    ).
 1955object_ref(Obj, Options) -->
 1956    { object_href(Obj, HREF, Options)
 1957    },
 1958    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.
 1965object_href(Obj, HREF) :-
 1966    object_href(Obj, HREF, []).
 1967
 1968object_href(M:PI0, HREF, Options) :-
 1969    option(files(Map), Options),
 1970    (   module_property(M, file(File))
 1971    ->  true
 1972    ;   xref_module(File, M)
 1973    ),
 1974    memberchk(file(File, DocFile), Map),
 1975    !,
 1976    file_base_name(DocFile, LocalFile),     % TBD: proper directory index
 1977    expand_pi(PI0, PI),
 1978    term_to_string(PI, PIS),
 1979    uri_data(path, Components, LocalFile),
 1980    uri_data(fragment, Components, PIS),
 1981    uri_components(HREF, Components).
 1982object_href(file(File), HREF, _Options) :-
 1983    doc_file_href(File, HREF),
 1984    !.
 1985object_href(directory(Dir), HREF, _Options) :-
 1986    directory_file_path(Dir, 'index.html', Index),
 1987    doc_file_href(Index, HREF),
 1988    !.
 1989object_href(Obj, HREF, _Options) :-
 1990    prolog:doc_object_href(Obj, HREF),
 1991    !.
 1992object_href(Obj0, HREF, _Options) :-
 1993    localise_object(Obj0, Obj),
 1994    term_to_string(Obj, String),
 1995    http_link_to_id(pldoc_object, [object=String], HREF).
 1996
 1997expand_pi(Name//Arity0, Name/Arity) :-
 1998    !,
 1999    Arity is Arity0+2.
 2000expand_pi(PI, PI).
 localise_object(+ObjIn, -ObjOut) is det
Abstract path-details to make references more stable over versions.
 2008localise_object(Obj0, Obj) :-
 2009    prolog:doc_canonical_object(Obj0, Obj),
 2010    !.
 2011localise_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.
 2019term_to_string(Term, String) :-
 2020    State = state(-),
 2021    (   numbervars(Term, 0, _, [singletons(true)]),
 2022        with_output_to(string(String),
 2023                       write_term(Term,
 2024                                  [ numbervars(true),
 2025                                    quoted(true)
 2026                                  ])),
 2027        nb_setarg(1, State, String),
 2028        fail
 2029    ;   arg(1, State, String)
 2030    ).
 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
 2044object_name(Obj, Options) -->
 2045    { option(style(Style), Options, inline)
 2046    },
 2047    object_name(Style, Obj, Options).
 2048
 2049object_name(title, Obj, Options) -->
 2050    { merge_options(Options, [secref_style(title)], Options1) },
 2051    prolog:doc_object_link(Obj, Options1),
 2052    !.
 2053object_name(inline, Obj, Options) -->
 2054    prolog:doc_object_link(Obj, Options),
 2055    !.
 2056object_name(title, f(Name/Arity), _Options) -->
 2057    !,
 2058    html(['Function ', Name, /, Arity]).
 2059object_name(inline, f(Name/Arity), _Options) -->
 2060    !,
 2061    html([Name, /, Arity]).
 2062object_name(Style, PI, Options) -->
 2063    { is_pi(PI) },
 2064    !,
 2065    pi(Style, PI, Options).
 2066object_name(inline, Module:module(_Title), _) -->
 2067    !,
 2068    { module_property(Module, file(File)),
 2069      file_base_name(File, Base)
 2070    },
 2071    !,
 2072    html(Base).
 2073object_name(title, Module:module(Title), _) -->
 2074    { module_property(Module, file(File)),
 2075      file_base_name(File, Base)
 2076    },
 2077    !,
 2078    html([Base, ' -- ', Title]).
 2079object_name(title, file(File), _) -->
 2080    { module_property(Module, file(File)),
 2081      doc_comment(Module:module(Title), _, _, _),
 2082      !,
 2083      file_base_name(File, Base)
 2084    },
 2085    html([Base, ' -- ', Title]).
 2086object_name(_, file(File), _) -->
 2087    { file_base_name(File, Base) },
 2088    html(Base).
 2089object_name(_, directory(Dir), _) -->
 2090    { file_base_name(Dir, Base) },
 2091    html(Base).
 2092
 2093pi(title, PI, Options) -->
 2094    pi_type(PI),
 2095    pi(PI, Options).
 2096pi(inline, PI, Options) -->
 2097    pi(PI, Options).
 2098
 2099pi(M:PI, Options) -->
 2100    !,
 2101    (   { option(qualify(true), Options) }
 2102    ->  html([span(class(module), M), :])
 2103    ;   []
 2104    ),
 2105    pi(PI, Options).
 2106pi(Name/Arity, _) -->
 2107    !,
 2108    html([Name, /, Arity]).
 2109pi(Name//Arity, _) -->
 2110    html([Name, //, Arity]).
 2111
 2112pi_type(_:PI) -->
 2113    !,
 2114    pi_type(PI).
 2115pi_type(_/_) -->
 2116    html(['Predicate ']).
 2117pi_type(_//_) -->
 2118    html(['Grammer 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.
 2130in_file(Module:Head, File) :-
 2131    !,
 2132    in_file(Module, Head, File).
 2133in_file(Head, File) :-
 2134    in_file(_, Head, File).
 2135
 2136in_file(_, Head, File) :-
 2137    xref_current_source(File),
 2138    atom(File),                     % only plain files
 2139    xref_defined(File, Head, How),
 2140    How \= imported(_From).
 2141in_file(Module, Head, File) :-
 2142    predicate_property(Module:Head, exported),
 2143    (   predicate_property(Module:Head, imported_from(Primary))
 2144    ->  true
 2145    ;   Primary = Module
 2146    ),
 2147    module_property(Primary, file(File)).
 2148in_file(Module, Head, File) :-
 2149    predicate_property(Module:Head, file(File)).
 2150in_file(Module, Head, File) :-
 2151    current_module(Module),
 2152    source_file(Module:Head, File).
 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?
 2183file(File) -->
 2184    file(File, []).
 2185
 2186file(File, Options) -->
 2187    { catch(nb_getval(pldoc_options, GenOptions), _, GenOptions = []),
 2188      merge_options(Options, GenOptions, FinalOptions)
 2189    },
 2190    link_file(File, FinalOptions),
 2191    !.
 2192file(File, Options) -->
 2193    { option(edit_handler(Handler), Options),
 2194      http_current_request(Request),
 2195      memberchk(path(Path), Request),
 2196      absolute_file_name(File, Location,
 2197                         [ relative_to(Path)
 2198                         ]),
 2199      http_link_to_id(Handler, [location(Location)], HREF),
 2200      format(atom(Title), 'Click to create ~w', [File])
 2201    },
 2202    html(a([href(HREF), class(nofile), title(Title)], File)).
 2203file(File, _) -->
 2204    html(code(class(nofile), File)).
 2205
 2206link_file(File, Options) -->
 2207    { file_href(File, HREF, Options),
 2208      option(label(Label), Options, File),
 2209      option(class(Class), Options, file)
 2210    },
 2211    html(a([class(Class), href(HREF)], Label)).
 file_href(+FilePath, -HREF, +Options) is det
Find URL for refering to FilePath based on Options.
 2217file_href(_, HREF, Options) :-
 2218    option(href(HREF), Options),
 2219    !.
 2220file_href(File, HREF, Options) :-
 2221    file_href_real(File, HREF0, Options),
 2222    map_extension(HREF0, HREF, Options).
 map_extension(+HREFIn, -HREFOut, Options) is det
Replace extension using the option
 2230map_extension(HREF0, HREF, Options) :-
 2231    option(map_extension(Map), Options),
 2232    file_name_extension(Base, Old, HREF0),
 2233    memberchk(Old-New, Map),
 2234    !,
 2235    file_name_extension(Base, New, HREF).
 2236map_extension(HREF, HREF, _).
 2237
 2238
 2239file_href_real(File, HREF, Options) :-
 2240    (   option(absolute_path(Path), Options)
 2241    ;   existing_linked_file(File, Path)
 2242    ),
 2243    !,
 2244    (   option(files(Map), Options),
 2245        memberchk(file(Path, LinkFile), Map)
 2246    ->  true
 2247    ;   LinkFile = Path
 2248    ),
 2249    file_href(LinkFile, HREF).
 2250file_href_real(File, HREF, _) :-
 2251    directory_alias(Alias),
 2252    Term =.. [Alias,File],
 2253    absolute_file_name(Term, _,
 2254                       [ access(read),
 2255                         file_errors(fail)
 2256                       ]),
 2257    !,
 2258    http_absolute_location(Term, HREF, []).
 2259
 2260directory_alias(icons).
 2261directory_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.
 2271file_href(Path, HREF) :-                % a loaded Prolog file
 2272    source_file(Path),
 2273    !,
 2274    doc_file_href(Path, HREF).
 2275file_href(Path, HREF) :-
 2276    (   nb_current(pldoc_output, CFile)
 2277    ;   nb_current(pldoc_file, CFile)
 2278    ),
 2279    CFile \== [],
 2280    !,
 2281    relative_file_name(Path, CFile, HREF).
 2282file_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.
 2290existing_linked_file(File, Path) :-
 2291    catch(b_getval(pldoc_file, CurrentFile), _, fail),
 2292    CurrentFile \== [],
 2293    absolute_file_name(File, Path,
 2294                       [ relative_to(CurrentFile),
 2295                         access(read),
 2296                         file_errors(fail)
 2297                       ]).
 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]].
 2307include(PI, predicate, _) -->
 2308    !,
 2309    (   html_tokens_for_predicates(PI, [])
 2310    ->  []
 2311    ;   html(['[[', \predref(PI), ']]'])
 2312    ).
 2313include(File, image, Options) -->
 2314    { file_name_extension(_, svg, File),
 2315      file_href(File, HREF, Options),
 2316      !,
 2317      include(image_attribute, Options, Attrs0),
 2318      merge_options(Attrs0,
 2319                    [ alt(File),
 2320                      data(HREF),
 2321                      type('image/svg+xml')
 2322                    ], Attrs)
 2323    },
 2324    (   { option(caption(Caption), Options) }
 2325    ->  html(div(class(figure),
 2326                 [ div(class(image), object(Attrs, [])),
 2327                   div(class(caption), Caption)
 2328                 ]))
 2329    ;   html(object(Attrs, []))
 2330    ).
 2331include(File, image, Options) -->
 2332    { file_href(File, HREF, Options),
 2333      !,
 2334      include(image_attribute, Options, Attrs0),
 2335      merge_options(Attrs0,
 2336                    [ alt(File),
 2337                      border(0),
 2338                      src(HREF)
 2339                    ], Attrs)
 2340    },
 2341    (   { option(caption(Caption), Options) }
 2342    ->  html(div(class(figure),
 2343                 [ div(class(image), img(Attrs)),
 2344                   div(class(caption), Caption)
 2345                 ]))
 2346    ;   html(img(Attrs))
 2347    ).
 2348include(File, wiki, _Options) -->       % [[file.txt]] is included
 2349    { access_file(File, read),
 2350      !,
 2351      read_file_to_codes(File, String, []),
 2352      wiki_codes_to_dom(String, [], DOM)
 2353    },
 2354    html(DOM).
 2355include(File, _Type, Options) -->
 2356    link_file(File, Options),
 2357    !.
 2358include(File, _, _) -->
 2359    html(code(class(nofile), ['[[',File,']]'])).
 2360
 2361image_attribute(src(_)).
 2362image_attribute(alt(_)).
 2363image_attribute(title(_)).
 2364image_attribute(align(_)).
 2365image_attribute(width(_)).
 2366image_attribute(height(_)).
 2367image_attribute(border(_)).
 2368image_attribute(class(_)).
 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]]
 2381html_tokens_for_predicates([], _Options) -->
 2382    [].
 2383html_tokens_for_predicates([H|T], Options) -->
 2384    !,
 2385    html_tokens_for_predicates(H, Options),
 2386    html_tokens_for_predicates(T, Options).
 2387html_tokens_for_predicates(PI, Options) -->
 2388    { PI = _:_/_,
 2389      !,
 2390      (   doc_comment(PI, Pos, _Summary, Comment)
 2391      ->  true
 2392      ;   Comment = ''
 2393      )
 2394    },
 2395    object(PI, [Pos-Comment], [dl], _, Options).
 2396html_tokens_for_predicates(Spec, Options) -->
 2397    { findall(PI, documented_pi(Spec, PI), List),
 2398      List \== [], !
 2399    },
 2400    html_tokens_for_predicates(List, Options).
 2401html_tokens_for_predicates(Spec, Options) -->
 2402    man_page(Spec,
 2403             [ links(false),                % no header
 2404               navtree(false),              % no navigation tree
 2405               footer(false)                % no footer
 2406             | Options
 2407             ]).
 2408
 2409
 2410documented_pi(Spec, PI) :-
 2411    generalise_spec(Spec, PI),
 2412    doc_comment(PI, _Pos, _Summary, _Comment).
 2413
 2414generalise_spec(Name/Arity, _M:Name/Arity).
 2415generalise_spec(Name//Arity, _M:Name//Arity).
 2416
 2417
 2418                 /*******************************
 2419                 *           WIKI FILES         *
 2420                 *******************************/
 doc_for_wiki_file(+File, +Options) is det
Write HTML for the File containing wiki data.
 2427doc_for_wiki_file(FileSpec, Options) :-
 2428    absolute_file_name(FileSpec, File,
 2429                       [ access(read)
 2430                       ]),
 2431    read_file_to_codes(File, String, []),
 2432    b_setval(pldoc_file, File),
 2433    call_cleanup(reply_wiki_page(File, String, Options),
 2434                 nb_delete(pldoc_file)).
 2435
 2436reply_wiki_page(File, String, Options) :-
 2437    wiki_codes_to_dom(String, [], DOM0),
 2438    title(DOM0, File, Title),
 2439    insert_edit_button(DOM0, File, DOM, Options),
 2440    reply_html_page(pldoc(wiki),
 2441                    title(Title),
 2442                    [ \html_requires(pldoc)
 2443                    | DOM
 2444                    ]).
 2445
 2446title(DOM, _, Title) :-
 2447    sub_term(h1(_,Title), DOM),
 2448    !.
 2449title(_, File, Title) :-
 2450    file_base_name(File, Title).
 2451
 2452insert_edit_button(DOM, _, DOM, Options) :-
 2453    option(edit(false), Options, false),
 2454    !.
 2455insert_edit_button([h1(Attrs,Title)|DOM], File,
 2456                   [h1(Attrs,[ span(style('float:right'),
 2457                                   \edit_button(File, [edit(true)]))
 2458                             | Title
 2459                             ])|DOM], _) :- !.
 2460insert_edit_button(DOM, File,
 2461                   [ h1(class(wiki),
 2462                        [ span(style('float:right'),
 2463                               \edit_button(File, [edit(true)]))
 2464                        ])
 2465                   | DOM
 2466                   ], _).
 2467
 2468
 2469                 /*******************************
 2470                 *            ANCHORS           *
 2471                 *******************************/
 mode_anchor_name(+Mode, -Anchor:atom) is det
Get the anchor name for a mode.
 2477mode_anchor_name(Var, _) :-
 2478    var(Var),
 2479    !,
 2480    instantiation_error(Var).
 2481mode_anchor_name(mode(Head, _), Anchor) :-
 2482    !,
 2483    mode_anchor_name(Head, Anchor).
 2484mode_anchor_name(Head is _Det, Anchor) :-
 2485    !,
 2486    mode_anchor_name(Head, Anchor).
 2487mode_anchor_name(Head, Anchor) :-
 2488    pred_anchor_name(Head, _, Anchor).
 pred_anchor_name(+Head, -PI:atom/integer, -Anchor:atom) is det
Create an HTML anchor name from Head.
 2495pred_anchor_name(//(Head), Name/Arity, Anchor) :-
 2496    !,
 2497    functor(Head, Name, DCGArity),
 2498    Arity is DCGArity+2,
 2499    format(atom(Anchor), '~w/~d', [Name, Arity]).
 2500pred_anchor_name(Head, Name/Arity, Anchor) :-
 2501    functor(Head, Name, Arity),
 2502    format(atom(Anchor), '~w/~d', [Name, Arity])