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)).
77:- predicate_options(doc_file_name/3, 3,
78 [ format(oneof([html,tex]))
79 ]).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
object(Object, Summary, Comment) and optionally
signature(Object, Signature).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(_).
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) }.
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 !.
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 ).
225doc_file_has_comments(Source) :-
226 source_file_property(Source, module(M)),
227 locally_defined(M:'$pldoc'/4),
228 M:'$pldoc'(_, _, _, _),
229 !.module(ModuleTitle)
Comment appearing in a module.xpce(Class, Kind, Name) for members of xpce classes.If Object is unbound and multiple objects share the same description, Object is unified with a list of terms described above.
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).
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 *******************************/
comments
option. It creates clauses of the form
where Id is one of
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(_, _, _, _, _).
det,
etc.).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).
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).
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).
%!,
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.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).
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 578prologmessage(pldoc(invalid_comment(File:Line, Comment))) --> 579 [ url(File:Line), ': PlDoc: failed to process structured comment:~n~s~n'- 580 [Comment] 581 ]
Process source documentation
The pldoc module processes structured comments in Prolog source files into well formatted HTML documents.