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_process,
   38          [ doc_comment/4,              % ?Object, ?Pos, ?Summary, ?Comment
   39            doc_file_has_comments/1,    % +File
   40            is_structured_comment/2,    % +Comment, -Prefixes
   41            parse_comment/3,            % +Comment, +FilePos, -Parsed
   42            parse_comment/5,            % +Comment, +FilePos, +Term, +VarNames,
   43                                        % -Parsed
   44            comment_modes/2,            % +Comment, -Synopsis
   45            process_comments/3,         % +Comments, +StartTermPos, +File
   46            process_comments/5,         % +Comments, +StartTermPos, +Term,
   47                                        % +VarNames, +File
   48            doc_signature/2,            % ?Object, ?Signature
   49            doc_file_name/3,            % +Source, -Doc, +Options
   50            doc_clean/1                 % +Module
   51          ]).   52
   53:- dynamic   user:file_search_path/2.   54:- multifile user:file_search_path/2.   55
   56user:file_search_path(pldoc, library(pldoc)).
   57
   58:- use_module(pldoc(doc_register)).   59:- use_module(pldoc(doc_modes)).   60:- use_module(pldoc(doc_wiki)).   61:- use_module(library(debug)).   62:- use_module(library(option)).   63:- use_module(library(lists)).   64:- use_module(library(apply)).   65:- use_module(library(operators)).   66:- use_module(library(prolog_source)).

Process source documentation

The pldoc module processes structured comments in Prolog source files into well formatted HTML documents.

author
- Jan Wielemaker
license
- GPL
   77:- predicate_options(doc_file_name/3, 3,
   78                     [ format(oneof([html,tex]))
   79                     ]).
 prolog:predicate_summary(+PI, -Summary) is semidet
Provide predicate summaries to the XPCE class prolog_predicate, used by the IDE tools.
   86:- multifile
   87    prolog:predicate_summary/2,     % ?PI, -Summary
   88    prolog:doc_compile_comment/6.   % +Comment, +Lines, +FilePos, +Term,
   89                                    % +VarNames, -Compiled
 prolog:doc_compile_comment(+Comment:string, +Lines, +FilePos, ?Term, +VarNames, -Compiled:list) is semidet
Hook to compile structured comments that do not document a predicate or section, such as the members of an xpce class. The hook is tried before the default processing. Lines are the indented lines of Comment and Term is the term that follows the comment. Term is unbound if Comment is not the last comment before the term. VarNames is a list Name=Var for the variables of Term. If the hook succeeds, Compiled is a list of terms as described with parse_comment/3, normally holding object(Object, Summary, Comment) and optionally signature(Object, Signature).
 is_structured_comment(+Comment:string, -Prefixes:list(codes)) is semidet
True if Comment is a structured comment that should use Prefixes to extract the plain text using indented_lines/3.
  112is_structured_comment(Comment, Prefixes) :-
  113    is_structured_comment(Comment, Prefixes, _Style).
  114
  115is_structured_comment(_Pos-Comment, Prefixes, Style) :-
  116    !,
  117    is_structured_comment(Comment, Prefixes, Style).
  118is_structured_comment(Comment, Prefixes, Style) :-
  119    is_list(Comment),
  120    !,
  121    (   phrase(structured_comment(Prefixes, Style), Comment, _)
  122    ->  true
  123    ).
  124is_structured_comment(Comment, Prefixes, Style) :-
  125    atom_string(CommentA, Comment),
  126    structured_command_start(Start, Prefixes, Style),
  127    sub_atom(CommentA, 0, Len, _, Start),
  128    !,
  129    sub_atom(CommentA, Len, 1, _, Space),
  130    char_type(Space, space),
  131    (   Style == block
  132    ->  true
  133    ;   \+ blanks_to_nl(CommentA)
  134    ).
  135
  136structured_command_start('%%',  ["%"], percent_percent).        % Deprecated
  137structured_command_start('%!',  ["%"], percent_bang).           % New style
  138structured_command_start('/**', ["/**", " *"], block).          % block
  139
  140blanks_to_nl(CommentA) :-
  141    sub_atom(CommentA, At, 1, _, Char),
  142    At >= 2,
  143    (   char_type(Char, end_of_line)
  144    ->  !
  145    ;   (   char_type(Char, space)
  146        ;   Char == '%'
  147        )
  148    ->  fail
  149    ;   !, fail
  150    ).
  151blanks_to_nl(_).
 structured_comment(-Prefixes:list(codes), -Style) is semidet
Grammar rule version of the above. Avoids the need for conversion.
  158structured_comment(["%"], percent_percent) -->
  159    "%%", space,
  160    \+ separator_line.
  161structured_comment(["%"], percent_bang) -->
  162    "%!", space.
  163structured_comment(Prefixes, block) -->
  164    "/**", space,
  165    { Prefixes = ["/**", " *"]
  166    }.
  167
  168space -->
  169    [H],
  170    { code_type(H, space) }.
 separator_line// is semidet
Matches a line like %% SWI or %%%%%%%%%%%%%%%%%%%%%%%%%, etc.
  176separator_line -->
  177    string(S), "\n",
  178    !,
  179    {   maplist(blank_or_percent, S)
  180    ;   contains(S, " SWI ")
  181    ;   contains(S, " SICStus ")
  182    ;   contains(S, " Mats ")
  183    }.
  184
  185string([]) --> [].
  186string([H|T]) --> [H], string(T).
  187
  188blank_or_percent(0'%) :- !.
  189blank_or_percent(C) :-
  190    code_type(C, space).
  191
  192contains(Haystack, Needle) :-
  193    string_codes(Needle, NeedleCodes),
  194    append(_, Start, Haystack),
  195    append(NeedleCodes, _, Start),
  196    !.
 doc_file_name(+Source:atom, -Doc:atom, +Options:list) is det
Doc is the name of the file for documenting Source.
Arguments:
Source- Prolog source to be documented
Doc- the name of the file documenting Source.
Options- Option list:
format(+Format)
Output format. One of html or tex
Errors
- permission_error(overwrite, Source)
  212doc_file_name(Source, Doc, Options) :-
  213    option(format(Format), Options, html),
  214    file_name_extension(Base, _Ext, Source),
  215    file_name_extension(Base, Format, Doc),
  216    (   Source == Doc
  217    ->  throw(error(permission_error(overwrite, Source), _))
  218    ;   true
  219    ).
 doc_file_has_comments(+Source:atom) is semidet
True if we have loaded comments from Source.
  225doc_file_has_comments(Source) :-
  226    source_file_property(Source, module(M)),
  227    locally_defined(M:'$pldoc'/4),
  228    M:'$pldoc'(_, _, _, _),
  229    !.
 doc_comment(?Objects, -Pos, -Summary:string, -Comment:string) is nondet
True if Comment is the comment describing object. Comment is returned as a string object containing the original from the source-code. Object is one of

If Object is unbound and multiple objects share the same description, Object is unified with a list of terms described above.

Arguments:
Summary- First sentence. Normalised spacing.
Comment- Comment string from the source-code (untranslated)
  259doc_comment(Object, Pos, Summary, Comment) :-
  260    var(Object),
  261    !,
  262    locally_defined(M:'$pldoc'/4),
  263    M:'$pldoc'(Obj, Pos, Summary, Comment),
  264    qualify(M, Obj, Object0),
  265    (   locally_defined(M:'$pldoc_link'/2),
  266        findall(L, M:'$pldoc_link'(L, Obj), Ls), Ls \== []
  267    ->  maplist(qualify(M), Ls, QLs),
  268        Object = [Object0|QLs]
  269    ;   Object = Object0
  270    ).
  271doc_comment(M:Object, Pos, Summary, Comment) :-
  272    !,
  273    locally_defined(M:'$pldoc'/4),
  274    (   M:'$pldoc'(Object, Pos, Summary, Comment)
  275    ;   locally_defined(M:'$pldoc_link'/2),
  276        M:'$pldoc_link'(Object, Obj2),
  277        M:'$pldoc'(Obj2, Pos, Summary, Comment)
  278    ).
  279doc_comment(Name/Arity, Pos, Summary, Comment) :-
  280    system_module(M),
  281    doc_comment(M:Name/Arity, Pos, Summary, Comment).
 doc_signature(?Object, ?Signature:string) is nondet
True when Signature is the signature of Object as provided by the hook prolog:doc_compile_comment/6. Object is qualified as with doc_comment/4.
  290doc_signature(M:Object, Signature) :-
  291    (   var(M)
  292    ->  locally_defined(M:'$pldoc_signature'/2)
  293    ;   true
  294    ),
  295    catch(M:'$pldoc_signature'(Object, Signature), error(_,_), fail).
  296doc_signature(Object, Signature) :-
  297    Object \= _:_,
  298    locally_defined(M:'$pldoc_signature'/2),
  299    M:'$pldoc_signature'(Object0, Signature),
  300    qualify(M, Object0, Object).
  301
  302locally_defined(M:Name/Arity) :-
  303    current_predicate(M:Name/Arity),
  304    functor(Head, Name, Arity),
  305%   \+ predicate_property(M:Head, imported_from(_)).
  306    \+ '$get_predicate_attribute'(M:Head, imported, _).
  307
  308
  309qualify(M, H, H) :- system_module(M), !.
  310qualify(M, H, H) :- sub_atom(M, 0, _, _, $), !.
  311qualify(M, H, M:H).
  312
  313system_module(user).
  314system_module(system).
  315
  316
  317%       Make the summary available to external tools on plugin basis.
  318
  319prolog:predicate_summary(PI, Summary) :-
  320    doc_comment(PI, _, Summary, _).
  321
  322
  323                 /*******************************
  324                 *      CALL-BACK COLLECT       *
  325                 *******************************/
 process_comments(+Comments:list, +TermPos, +File) is det
 process_comments(+Comments:list, +TermPos, ?Term, +VarNames, +File) is det
Processes comments returned by read_term/3 using the comments option. It creates clauses of the form

where Id is one of

module(Title)
Generated from /** <module> Title */
Name / Arity
Generated from Name(Arg, ...)
Name // Arity
Generated from Name(Arg, ...)//
Arguments:
Comments- is a list Pos-Comment returned by read_term/3
TermPos- is the start-location of the actual term
Term- is the term read. It is passed to the prolog:doc_compile_comment/6 hook for the last comment before the term.
VarNames- is a list Name=Var for the variables of Term.
File- is the file that is being loaded.
  355process_comments(Comments, TermPos, File) :-
  356    process_comments(Comments, TermPos, _, [], File).
  357
  358process_comments([], _, _, _, _).
  359process_comments([Pos-Comment|T], TermPos, Term, VarNames, File) :-
  360    (   Pos @> TermPos              % comments inside term
  361    ->  true
  362    ;   (   last_comment(T, TermPos)
  363        ->  CTerm = Term
  364        ;   true
  365        ),
  366        process_comment(Pos, Comment, CTerm, VarNames, File),
  367        process_comments(T, TermPos, Term, VarNames, File)
  368    ).
  369
  370last_comment([], _).
  371last_comment([Pos-_|_], TermPos) :-
  372    Pos @> TermPos.
  373
  374process_comment(Pos, Comment, Term, VarNames, File) :-
  375    is_structured_comment(Comment, Prefixes, Style),
  376    !,
  377    stream_position_data(line_count, Pos, Line),
  378    FilePos = File:Line,
  379    process_structured_comment(FilePos, Comment, Prefixes,
  380                               Term, VarNames, Style).
  381process_comment(_, _, _, _, _).
 parse_comment(+Comment, +FilePos, -Parsed) is semidet
 parse_comment(+Comment, +FilePos, ?Term, +VarNames, -Parsed) is semidet
True when Comment is a structured comment and Parsed is its parsed representation. Parsed is a list of the following terms:
section(Id, Title, Comment)
Generated from /** <module> Title Comment */ comments.
predicate(PI, Summary, Comment)
Comment for predicate PI
link(FromPI, ToPI)
Indicate that FromPI shares its comment with ToPI. The actual comment is in ToPI.
mode(Head, Determinism)
Mode declaration. Head is a term with Mode(Type) terms and Determinism describes the associated determinism (det, etc.).
object(Object, Summary, Comment)
Comment for some other Object, produced by the hook prolog:doc_compile_comment/6.
signature(Object, Signature)
Signature of Object as a string, produced by the hook prolog:doc_compile_comment/6.

Term is the term that follows Comment and VarNames is a list Name=Var for its variables. Both are passed to prolog:doc_compile_comment/6.

  412parse_comment(Comment, FilePos, Parsed) :-
  413    parse_comment(Comment, FilePos, _, [], Parsed).
  414
  415parse_comment(Comment, FilePos, Term, VarNames, Parsed) :-
  416    is_structured_comment(Comment, Prefixes),
  417    !,
  418    compile_comment(Comment, FilePos, Prefixes, Term, VarNames, Parsed).
 comment_modes(+Comment, -Modes:list) is semidet
  423comment_modes(Comment, Modes) :-
  424    is_structured_comment(Comment, Prefixes),
  425    string_codes(Comment, CommentCodes),
  426    indented_lines(CommentCodes, Prefixes, Lines),
  427    (   prolog_load_context(module, Module)
  428    ->  true
  429    ;   Module = user
  430    ),
  431    process_modes(Lines, Module, dummy:0, Modes0, _Vars, _),
  432    maplist(bind_mode, Modes0, Modes).
  433
  434bind_mode(mode(Mode, Bindings), Mode) :-
  435    maplist(bind_var, Bindings).
  436
  437bind_var(Name=Name).
 process_structured_comment(+FilePos, +Comment:string, +Prefixed:list, ?Term, +VarNames, +Style) is det
Proccess a structured comment, adding the documentation facts to the database. This predicate verifies that the comment has not already been loaded.
To be done
- Note that as of version 7.3.12 clauses from a file being reloaded are not wiped before the reloading and therefore we cannot test the clause while reloading a file. Ultimately we need a better test for this.
  454process_structured_comment(FilePos, Comment, _, _, _, _) :- % already processed
  455    prolog_load_context(module, M),
  456    locally_defined(M:'$pldoc'/4),
  457    catch(M:'$pldoc'(_, FilePos, _, Comment), _, fail),
  458    (   FilePos = File:_,
  459        source_file_property(File, reloading)
  460    ->  debug(pldoc(reload), 'Reloading ~q', [FilePos]),
  461        fail
  462    ;   true
  463    ),
  464    !.
  465process_structured_comment(FilePos, Comment, Prefixes, Term, VarNames,
  466                           Style) :-
  467    catch(compile_comment(Comment, FilePos, Prefixes, Term, VarNames,
  468                          Compiled), E,
  469          comment_warning(Style, E)),
  470    maplist(store_comment(FilePos), Compiled).
  471process_structured_comment(FilePos, Comment, _Prefixes, _Term, _VarNames,
  472                           Style) :-
  473    comment_style_warning_level(Style, Level),
  474    print_message(Level,
  475                  pldoc(invalid_comment(FilePos, Comment))).
  476
  477comment_style_warning_level(percent_percent, silent) :- !.
  478comment_style_warning_level(_, warning).
 comment_warning(+Style, +Error) is failure
Print a warning on structured comments that could not be processed. Since the recommended magic sequence is now %!, we remain silent about comments that start with %%.
  486comment_warning(Style, E) :-
  487    comment_style_warning_level(Style, Level),
  488    print_message(Level, E),
  489    fail.
 compile_comment(+Comment, +FilePos, +Prefixes, ?Term, +VarNames, -Compiled) is semidet
Compile structured Comment into a list of terms that describe the comment.
See also
- parse_comment/3 for the terms in Compiled.
  499compile_comment(Comment, FilePos, Prefixes, Term, VarNames, Compiled) :-
  500    string_codes(Comment, CommentCodes),
  501    indented_lines(CommentCodes, Prefixes, Lines),
  502    (   prolog:doc_compile_comment(Comment, Lines, FilePos, Term, VarNames,
  503                                   Compiled)
  504    ->  true
  505    ;   section_comment_header(Lines, Header, _RestLines)
  506    ->  Header = \section(Type, Title),
  507        Id =.. [Type,Title],
  508        Compiled = [section(Id, Title, Comment)]
  509    ;   prolog_load_context(module, Module),
  510        process_modes(Lines, Module, FilePos, Modes, _, RestLines)
  511    ->  maplist(compile_mode, Modes, ModeDecls),
  512        modes_to_predicate_indicators(Modes, AllPIs),
  513        decl_module(AllPIs, M, [PI0|PIs]),
  514        maplist(link_term(M:PI0), PIs, Links),
  515        summary_from_lines(RestLines, Codes),
  516        string_codes(Summary, Codes),
  517        append([ ModeDecls,
  518                 [ predicate(M:PI0, Summary, Comment) ],
  519                 Links
  520               ], Compiled)
  521    ),
  522    !.
  523
  524
  525store_comment(Pos, section(Id, Title, Comment)) :-
  526    !,
  527    compile_clause('$pldoc'(Id, Pos, Title, Comment), Pos).
  528store_comment(Pos, predicate(M:PI, Summary, Comment)) :-
  529    !,
  530    compile_clause(M:'$pldoc'(PI, Pos, Summary, Comment), Pos).
  531store_comment(Pos, link(PI, M:PI0)) :-
  532    !,
  533    compile_clause(M:'$pldoc_link'(PI, PI0), Pos).
  534store_comment(Pos, mode(Head, Det)) :-
  535    !,
  536    compile_clause('$mode'(Head, Det), Pos).
  537store_comment(Pos, object(Object, Summary, Comment)) :-
  538    !,
  539    compile_clause('$pldoc'(Object, Pos, Summary, Comment), Pos).
  540store_comment(Pos, signature(Object, Signature)) :-
  541    !,
  542    compile_clause('$pldoc_signature'(Object, Signature), Pos).
  543store_comment(_, Term) :-
  544    type_error(pldoc_term, Term).
  545
  546link_term(To, From, link(From,To)).
  547
  548decl_module([], M, []) :-
  549    (   var(M)
  550    ->  prolog_load_context(module, M)
  551    ;   true
  552    ).
  553decl_module([H0|T0], M, [H|T]) :-
  554    (   H0 = M1:H
  555    ->  M = M1
  556    ;   H = H0
  557    ),
  558    decl_module(T0, M, T).
 doc_clean(+Module) is det
Clean documentation for Module.
  564doc_clean(Module) :-
  565    abolish(Module:'$mode'/2),
  566    abolish(Module:'$pldoc'/4),
  567    abolish(Module:'$pldoc_link'/2),
  568    abolish(Module:'$pldoc_signature'/2).
  569
  570
  571                 /*******************************
  572                 *           MESSAGES           *
  573                 *******************************/
  574
  575:- multifile
  576    prolog:message//1.  577
  578prolog:message(pldoc(invalid_comment(File:Line, Comment))) -->
  579    [ url(File:Line), ': PlDoc: failed to process structured comment:~n~s~n'-
  580            [Comment]
  581    ]