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    ;   sub_atom(Func, 0, _, _, 'S')
 1127    },
 1128    !,
 1129    synopsis([span(class(cfunc), 'C-language interface function')]).
 1130object_synopsis(_, _) --> [].
 1131
 1132synopsis(Text) -->
 1133    html(div(class(synopsis),
 1134             [ span(class('synopsis-hdr'), 'Availability:')
 1135             | Text
 1136             ])).
 unquote_filespec(+Spec, -Unquoted) is det
Translate e.g. library('semweb/rdf_db') into library(semweb/rdf_db).
 1143unquote_filespec(Spec, Unquoted) :-
 1144    compound(Spec),
 1145    Spec =.. [Alias,Path],
 1146    atom(Path),
 1147    atomic_list_concat(Parts, /, Path),
 1148    maplist(need_no_quotes, Parts),
 1149    !,
 1150    parts_to_path(Parts, UnquotedPath),
 1151    Unquoted =.. [Alias, UnquotedPath].
 1152unquote_filespec(Spec, Spec).
 1153
 1154need_no_quotes(Atom) :-
 1155    format(atom(A), '~q', [Atom]),
 1156    \+ sub_atom(A, 0, _, _, '\'').
 1157
 1158parts_to_path([One], One) :- !.
 1159parts_to_path(List, More/T) :-
 1160    (   append(H, [T], List)
 1161    ->  parts_to_path(H, More)
 1162    ).
 1163
 1164
 1165                 /*******************************
 1166                 *             PRINT            *
 1167                 *******************************/
 doc_write_html(+Out:stream, +Title:atomic, +DOM) is det
Write HTML for the documentation page DOM using Title to Out.
 1173doc_write_html(Out, Title, Doc) :-
 1174    doc_page_dom(Title, Doc, DOM),
 1175    phrase(html(DOM), Tokens),
 1176    print_html_head(Out),
 1177    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.
 1184doc_page_dom(Title, Body, DOM) :-
 1185    DOM = html([ head([ title(Title),
 1186                        link([ rel(stylesheet),
 1187                               type('text/css'),
 1188                               href(location_by_id(pldoc_resource)+'pldoc.css')
 1189                             ]),
 1190                        script([ src(location_by_id(pldoc_resource)+'pldoc.js'),
 1191                                 type('text/javascript')
 1192                               ], [])
 1193                      ]),
 1194                 body(Body)
 1195               ]).
 print_html_head(+Out:stream) is det
Print the DOCTYPE line.
 1201print_html_head(Out) :-
 1202    format(Out,
 1203           '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" \c
 1204               "http://www.w3.org/TR/html4/strict.dtd">~n', []).
 1205
 1206% Rendering rules
 1207%
 1208% 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.
 1216tags(Tags) -->
 1217    html(dl(class=tags, Tags)).
 tag(+Tag, +Values:list)// is det
Called from \tag(Name, Values) terms produced by doc_wiki.pl.
 1223tag(Tag, Values) -->
 1224    {   doc_tag_title(Tag, Title),
 1225        atom_concat('keyword-', Tag, Class)
 1226    },
 1227    html([ dt(class=Class, Title),
 1228           \tag_values(Values, Class)
 1229         ]).
 1230
 1231tag_values([], _) -->
 1232    [].
 1233tag_values([H|T], Class) -->
 1234    html(dd(class=Class, ['- '|H])),
 1235    tag_values(T, Class).
 doc_tag_title(+Tag, -Title) is det
Title is the name to use for Tag in the generated documentation.
 1242doc_tag_title(Tag, Title) :-
 1243    tag_title(Tag, Title),
 1244    !.
 1245doc_tag_title(Tag, Tag).
 1246
 1247tag_title(compat, 'Compatibility').
 1248tag_title(tbd,    'To be done').
 1249tag_title(see,    'See also').
 1250tag_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).
 1257args(Params) -->
 1258    html([ dt(class=tag, 'Arguments:'),
 1259           dd(table(class=arglist,
 1260                    \arg_list(Params)))
 1261         ]).
 1262
 1263arg_list([]) -->
 1264    [].
 1265arg_list([H|T]) -->
 1266    argument(H),
 1267    arg_list(T).
 1268
 1269argument(arg(Name,Descr)) -->
 1270    html(tr([td(var(Name)), td(class=argdescr, ['- '|Descr])])).
 1271
 1272
 1273                 /*******************************
 1274                 *         NAVIGATION TREE      *
 1275                 *******************************/
 objects_nav_tree(+Objects, -Tree) is det
Provide a navigation tree showing the context of Object. Tree is of the form node(Object, Children).
 1282objects_nav_tree(Objects, Tree) :-
 1283    maplist(object_nav_tree, Objects, Trees),
 1284    union_trees(Trees, Tree0),
 1285    remove_unique_root(Tree0, Tree).
 1286
 1287object_nav_tree(Obj, Tree) :-
 1288    Node = node(directory(Dir), FileNodes),
 1289    FileNode = node(file(File), Siblings),
 1290    doc_comment(Obj, File:_Line, _Summary, _Comment),
 1291    !,
 1292    file_directory_name(File, Dir),
 1293    sibling_file_nodes(Dir, FileNodes0),
 1294    selectchk(node(file(File),[]), FileNodes0, FileNode, FileNodes),
 1295    findall(Sibling, doc_comment(Sibling, File:_, _, _), Siblings0),
 1296    delete(Siblings0, _:module(_), Siblings1),
 1297    doc_hide_private(Siblings1, Siblings2, []),
 1298    flatten(Siblings2, Siblings),   % a comment may describe objects
 1299    embed_directories(Node, Tree).
 1300
 1301sibling_file_nodes(Dir, Nodes) :-
 1302    findall(node(file(File), []),
 1303            (   source_file(File),
 1304                file_directory_name(File, Dir)
 1305            ),
 1306            Nodes).
 1307
 1308embed_directories(Node, Tree) :-
 1309    Node = node(file(File), _),
 1310    !,
 1311    file_directory_name(File, Dir),
 1312    Super = node(directory(Dir), [Node]),
 1313    embed_directories(Super, Tree).
 1314embed_directories(Node, Tree) :-
 1315    Node = node(directory(Dir), _),
 1316    file_directory_name(Dir, SuperDir),
 1317    SuperDir \== Dir,
 1318    !,
 1319    Super = node(directory(SuperDir), [Node]),
 1320    embed_directories(Super, Tree).
 1321embed_directories(Tree, Tree).
 1322
 1323
 1324union_trees([Tree], Tree) :- !.
 1325union_trees([T1,T2|Trees], Tree) :-
 1326    merge_trees(T1, T2, M1),
 1327    union_trees([M1|Trees], Tree).
 1328
 1329merge_trees(node(R, Ch1), node(R, Ch2), node(R, Ch)) :-
 1330    merge_nodes(Ch1, Ch2, Ch).
 1331
 1332merge_nodes([], Ch, Ch) :- !.
 1333merge_nodes(Ch, [], Ch) :- !.
 1334merge_nodes([node(Root, Ch1)|T1], N1, [T1|Nodes]) :-
 1335    selectchk(node(Root, Ch2), N1, N2),
 1336    !,
 1337    merge_trees(node(Root, Ch1), node(Root, Ch2), T1),
 1338    merge_nodes(T1, N2, Nodes).
 1339merge_nodes([Node|T1], N1, [Node|Nodes]) :-
 1340    merge_nodes(T1, N1, Nodes).
 remove_unique_root(+TreeIn, -Tree)
Remove the root part that does not branch
 1346remove_unique_root(node(_, [node(R1, [R2])]), Tree) :-
 1347    !,
 1348    remove_unique_root(node(R1, [R2]), Tree).
 1349remove_unique_root(Tree, Tree).
 nav_tree(+Tree, +Current, +Options)// is det
Render the navigation tree
 1355nav_tree(Tree, Current, Options) -->
 1356    html(ul(class(nav),
 1357            \object_tree(Tree, Current, Options))).
 object_tree(+Tree, +Current, +Options)// is det
Render a tree of objects used for navigation.
 1363object_tree(node(Id, []), Target, Options) -->
 1364    !,
 1365    { node_class(Id, Target, Class) },
 1366    html(li(class(Class),
 1367            \node(Id, Options))).
 1368object_tree(node(Id, Children), Target, Options) -->
 1369    !,
 1370    { node_class(Id, Target, Class) },
 1371    html(li(class(Class),
 1372            [ \node(Id, Options),
 1373              ul(class(nav),
 1374                 \object_trees(Children, Target, Options))
 1375            ])).
 1376object_tree(Id, Target, Options) -->
 1377    !,
 1378    { node_class(Id, Target, Class) },
 1379    html(li(class([obj|Class]), \node(Id, Options))).
 1380
 1381object_trees([], _, _) --> [].
 1382object_trees([H|T], Target, Options) -->
 1383    object_tree(H, Target, Options),
 1384    object_trees(T, Target, Options).
 1385
 1386node_class(Ids, Current, Class) :-
 1387    is_list(Ids),
 1388    !,
 1389    (   member(Id, Ids), memberchk(Id, Current)
 1390    ->  Class = [nav,current]
 1391    ;   Class = [nav]
 1392    ).
 1393node_class(Id, Current, Class) :-
 1394    (   memberchk(Id, Current)
 1395    ->  Class = [nav,current]
 1396    ;   Class = [nav]
 1397    ).
 1398
 1399node(file(File), Options) -->
 1400    !,
 1401    object_ref(file(File), [style(title)|Options]).
 1402node(Id, Options) -->
 1403    object_ref(Id, Options).
 1404
 1405
 1406                 /*******************************
 1407                 *            SECTIONS          *
 1408                 *******************************/
 1409
 1410section(Type, Title) -->
 1411    { string_codes(Title, Codes),
 1412      wiki_codes_to_dom(Codes, [], Content0),
 1413      strip_leading_par(Content0, Content),
 1414      make_section(Type, Content, HTML)
 1415    },
 1416    html(HTML).
 1417
 1418make_section(module,  Title, h1(class=module,  Title)).
 1419make_section(section, Title, h1(class=section, Title)).
 1420
 1421
 1422                 /*******************************
 1423                 *       PRED MODE HEADER       *
 1424                 *******************************/
 pred_dt(+Modes, +Class, Options)// is det
Emit the predicate header.
Arguments:
Modes- List as returned by process_modes/5.
 1432pred_dt(Modes, Class, Options) -->
 1433    pred_dt(Modes, Class, [], _Done, Options).
 1434
 1435pred_dt([], _, Done, Done, _) -->
 1436    [].
 1437pred_dt([H|T], Class, Done0, Done, Options) -->
 1438    { functor(Class, CSSClass, _) },
 1439    html(dt(class=CSSClass,
 1440            [ \pred_mode(H, Done0, Done1, Options),
 1441              \mode_anot(Class)
 1442            ])),
 1443    pred_dt(T, Class, Done1, Done, Options).
 1444
 1445mode_anot(privdef) -->
 1446    !,
 1447    html(span([class(anot), style('float:right')],
 1448              '[private]')).
 1449mode_anot(multidef(object(Obj))) -->
 1450    !,
 1451    { object_href(Obj, HREF) },
 1452    html(span([class(anot), style('float:right')],
 1453              ['[', a(href(HREF), multifile), ']'
 1454              ])).
 1455mode_anot(multidef(file(File:_))) -->
 1456    !,
 1457    { file_name_on_path(File, Spec),
 1458      unquote_filespec(Spec, Unquoted),
 1459      doc_file_href(File, HREF)
 1460    },
 1461    html(span([class(anot), style('float:right')],
 1462              ['[multifile, ', a(href(HREF), '~q'-[Unquoted]), ']'
 1463              ])).
 1464mode_anot(multidef) -->
 1465    !,
 1466    html(span([class(anot), style('float:right')],
 1467              '[multifile]')).
 1468mode_anot(_) -->
 1469    [].
 1470
 1471pred_mode(mode(Head,Vars), Done0, Done, Options) -->
 1472    !,
 1473    { bind_vars(Head, Vars) },
 1474    pred_mode(Head, Done0, Done, Options).
 1475pred_mode(Head is Det, Done0, Done, Options) -->
 1476    !,
 1477    anchored_pred_head(Head, Done0, Done, Options),
 1478    pred_det(Det).
 1479pred_mode(Head, Done0, Done, Options) -->
 1480    anchored_pred_head(Head, Done0, Done, Options).
 1481
 1482bind_vars(Term, Bindings) :-
 1483    bind_vars(Bindings),
 1484    anon_vars(Term).
 1485
 1486bind_vars([]).
 1487bind_vars([Name=Var|T]) :-
 1488    Var = '$VAR'(Name),
 1489    bind_vars(T).
 anon_vars(+Term) is det
Bind remaining variables in Term to '$VAR'('_'), so they are printed as '_'.
 1496anon_vars(Var) :-
 1497    var(Var),
 1498    !,
 1499    Var = '$VAR'('_').
 1500anon_vars(Term) :-
 1501    compound(Term),
 1502    !,
 1503    Term =.. [_|Args],
 1504    maplist(anon_vars, Args).
 1505anon_vars(_).
 1506
 1507
 1508anchored_pred_head(Head, Done0, Done, Options) -->
 1509    { pred_anchor_name(Head, PI, Name) },
 1510    (   { memberchk(PI, Done0) }
 1511    ->  { Done = Done0 },
 1512        pred_head(Head)
 1513    ;   html([ span(style('float:right'),
 1514                    [ \pred_edit_or_source_button(Head, Options),
 1515                      &(nbsp)
 1516                    ]),
 1517               a(name=Name, \pred_head(Head))
 1518             ]),
 1519        { Done = [PI|Done0] }
 1520    ).
 1521
 1522
 1523pred_edit_or_source_button(Head, Options) -->
 1524    { option(edit(true), Options) },
 1525    !,
 1526    pred_edit_button(Head, Options).
 1527pred_edit_or_source_button(Head, Options) -->
 1528    { option(source_link(true), Options) },
 1529    !,
 1530    pred_source_button(Head, Options).
 1531pred_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)
 1545pred_edit_button(_, Options) -->
 1546    { \+ option(edit(true), Options) },
 1547    !.
 1548pred_edit_button(PI0, Options0) -->
 1549    { canonicalise_predref(PI0, PI, Options0, Options) },
 1550    pred_edit_button2(PI, Options).
 1551
 1552pred_edit_button2(Name/Arity, Options) -->
 1553    { \+ ( memberchk(file(_), Options), % always edit if file and line
 1554           memberchk(line(_), Options)  % are given.
 1555         ),
 1556      functor(Head, Name, Arity),
 1557      option(module(M), Options, _),
 1558      \+ ( current_module(M),
 1559           source_file(M:Head, _File)
 1560         )
 1561    },
 1562    !.
 1563pred_edit_button2(Name/Arity, Options) -->
 1564    { include(edit_param, Options, Extra),
 1565      http_link_to_id(pldoc_edit,
 1566                      [name(Name),arity(Arity)|Extra],
 1567                      EditHREF)
 1568    },
 1569    html(a(onClick('HTTPrequest(\'' + EditHREF + '\')'),
 1570           img([ class(action),
 1571                 alt('Edit predicate'),
 1572                 title('Edit predicate'),
 1573                 src(location_by_id(pldoc_resource)+'editpred.png')
 1574               ]))).
 1575pred_edit_button2(_, _) -->
 1576    !,
 1577    [].
 1578
 1579edit_param(module(_)).
 1580edit_param(file(_)).
 1581edit_param(line(_)).
 object_edit_button(+Object, +Options)// is det
Create a button for editing Object.
 1588object_edit_button(_, Options) -->
 1589    { \+ option(edit(true), Options) },
 1590    !.
 1591object_edit_button(PI, Options) -->
 1592    { is_pi(PI) },
 1593    !,
 1594    pred_edit_button(PI, Options).
 1595object_edit_button(_, _) -->
 1596    [].
 pred_source_button(+PredIndicator, +Options)// is det
Create a button for viewing the source of a predicate.
 1603pred_source_button(PI0, Options0) -->
 1604    { canonicalise_predref(PI0, PI, Options0, Options),
 1605      option(module(M), Options, _),
 1606      pred_source_href(PI, M, HREF), !
 1607    },
 1608    html(a([ href(HREF)
 1609           ],
 1610           img([ class(action),
 1611                 alt('Source'),
 1612                 title('Show source'),
 1613                 src(location_by_id(pldoc_resource)+'source.png')
 1614               ]))).
 1615pred_source_button(_, _) -->
 1616    [].
 object_source_button(+Object, +Options)// is det
Create a button for showing the source of Object.
 1623object_source_button(PI, Options) -->
 1624    { is_pi(PI),
 1625      option(source_link(true), Options, true)
 1626    },
 1627    !,
 1628    pred_source_button(PI, Options).
 1629object_source_button(_, _) -->
 1630    [].
 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.
 1638canonicalise_predref(M:PI0, PI, Options0, [module(M)|Options]) :-
 1639    !,
 1640    canonicalise_predref(PI0, PI, Options0, Options).
 1641canonicalise_predref(//(Head), PI, Options0, Options) :-
 1642    !,
 1643    functor(Head, Name, Arity),
 1644    PredArity is Arity + 2,
 1645    canonicalise_predref(Name/PredArity, PI, Options0, Options).
 1646canonicalise_predref(Name//Arity, PI, Options0, Options) :-
 1647    integer(Arity), Arity >= 0,
 1648    !,
 1649    PredArity is Arity + 2,
 1650    canonicalise_predref(Name/PredArity, PI, Options0, Options).
 1651canonicalise_predref(PI, PI, Options, Options) :-
 1652    PI = Name/Arity,
 1653    atom(Name), integer(Arity), Arity >= 0,
 1654    !.
 1655canonicalise_predref(Head, PI, Options0, Options) :-
 1656    functor(Head, Name, Arity),
 1657    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.
 1665pred_head(Var) -->
 1666    { var(Var),
 1667      !,
 1668      instantiation_error(Var)
 1669    }.
 1670pred_head(//(Head)) -->
 1671    !,
 1672    pred_head(Head),
 1673    html(//).
 1674pred_head(M:Head) -->
 1675    html([span(class=module, M), :]),
 1676    pred_head(Head).
 1677pred_head(Head) -->
 1678    { atom(Head) },
 1679    !,
 1680    html(b(class=pred, Head)).
 1681pred_head(Head) -->                     % Infix operators
 1682    { Head =.. [Functor,Left,Right],
 1683      is_op_type(Functor, infix)
 1684    },
 1685    !,
 1686    html([ var(class=arglist, \pred_arg(Left, 1)),
 1687           ' ', b(class=pred, Functor), ' ',
 1688           var(class=arglist, \pred_arg(Right, 2))
 1689         ]).
 1690pred_head(Head) -->                     % Prefix operators
 1691    { Head =.. [Functor,Arg],
 1692      is_op_type(Functor, prefix)
 1693    },
 1694    !,
 1695    html([ b(class=pred, Functor), ' ',
 1696           var(class=arglist, \pred_arg(Arg, 1))
 1697         ]).
 1698pred_head(Head) -->                     % Postfix operators
 1699    { Head =.. [Functor,Arg],
 1700      is_op_type(Functor, postfix)
 1701    },
 1702    !,
 1703    html([ var(class=arglist, \pred_arg(Arg, 1)),
 1704           ' ', b(class=pred, Functor)
 1705         ]).
 1706pred_head({Head}) -->
 1707    !,
 1708    html([ b(class=pred, '{'),
 1709           var(class=arglist,
 1710               \pred_args([Head], 1)),
 1711           b(class=pred, '}')
 1712         ]).
 1713pred_head(Head) -->                     % Plain terms
 1714    { Head =.. [Functor|Args] },
 1715    html([ b(class=pred, Functor),
 1716           var(class=arglist,
 1717               [ '(', \pred_args(Args, 1), ')' ])
 1718         ]).
 is_op_type(+Atom, ?Type)
True if Atom is an operator of Type. Type is one of prefix, infix or postfix.
 1725is_op_type(Functor, Type) :-
 1726    current_op(_Pri, F, Functor),
 1727    op_type(F, Type).
 1728
 1729op_type(fx,  prefix).
 1730op_type(fy,  prefix).
 1731op_type(xf,  postfix).
 1732op_type(yf,  postfix).
 1733op_type(xfx, infix).
 1734op_type(xfy, infix).
 1735op_type(yfx, infix).
 1736op_type(yfy, infix).
 1737
 1738
 1739pred_args([], _) -->
 1740    [].
 1741pred_args([H|T], I) -->
 1742    pred_arg(H, I),
 1743    (   {T==[]}
 1744    ->  []
 1745    ;   html(', '),
 1746        { I2 is I + 1 },
 1747        pred_args(T, I2)
 1748    ).
 1749
 1750pred_arg(Var, I) -->
 1751    { var(Var) },
 1752    !,
 1753    html(['Arg', I]).
 1754pred_arg(...(Term), I) -->
 1755    !,
 1756    pred_arg(Term, I),
 1757    html('...').
 1758pred_arg(Term, I) -->
 1759    { Term =.. [Ind,Arg],
 1760      mode_indicator(Ind)
 1761    },
 1762    !,
 1763    html([Ind, \pred_arg(Arg, I)]).
 1764pred_arg(Arg:Type, _) -->
 1765    !,
 1766    html([\argname(Arg), :, \argtype(Type)]).
 1767pred_arg(Arg, _) -->
 1768    argname(Arg).
 1769
 1770argname('$VAR'(Name)) -->
 1771    !,
 1772    html(Name).
 1773argname(Name) -->
 1774    !,
 1775    html(Name).
 1776
 1777argtype(Term) -->
 1778    { format(string(S), '~W',
 1779             [ Term,
 1780               [ quoted(true),
 1781                 numbervars(true)
 1782               ]
 1783             ]) },
 1784    html(S).
 1785
 1786pred_det(unknown) -->
 1787    [].
 1788pred_det(Det) -->
 1789    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
 1798term(_, Atom, []) -->
 1799    { atomic(Atom),
 1800      !,
 1801      format(string(S), '~W', [Atom,[quoted(true)]])
 1802    },
 1803    html(span(class=functor, S)).
 1804term(_, Key:Type, [TypeName=Type]) -->
 1805    { atomic(Key)
 1806    },
 1807    !,
 1808    html([span(class='pl-key', Key), :, span(class('pl-var'), TypeName)]).
 1809term(_, Term, Bindings) -->
 1810    { is_mode(Term is det),         % HACK. Bit too strict?
 1811      bind_vars(Bindings)
 1812    },
 1813    !,
 1814    pred_head(Term).
 1815term(_, Term, Bindings) -->
 1816    term(Term,
 1817         [ variable_names(Bindings),
 1818           quoued(true)
 1819         ]).
 1820
 1821
 1822                 /*******************************
 1823                 *             PREDREF          *
 1824                 *******************************/
 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.

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