37
38:- module(pldoc_html,
39 [ doc_for_file/2, 40 doc_write_html/3, 41 doc_for_wiki_file/2, 42 43 doc_page_dom/3, 44 print_html_head/1, 45 predref//1, 46 predref//2, 47 nopredref//1, 48 module_info/3, 49 doc_hide_private/3, 50 edit_button//2, 51 source_button//2, 52 zoom_button//2, 53 pred_edit_button//2, 54 object_edit_button//2, 55 object_source_button//2, 56 doc_resources//1, 57 ensure_doc_objects/1, 58 59 doc_file_objects/5, 60 existing_linked_file/2, 61 unquote_filespec/2, 62 doc_tag_title/2, 63 mode_anchor_name/2, 64 pred_anchor_name/3, 65 private/2, 66 (multifile)/2, 67 is_pi/1, 68 is_op_type/2, 69 70 file//1, 71 file//2, 72 include//3, 73 tags//1, 74 term//3, 75 file_header//2, 76 flagref//1, 77 objects//2, 78 object_ref//2, 79 object_name//2, 80 object_href/2, 81 object_tree//3, 82 object_page//2, 83 object_page_header//2, 84 object_synopsis//2, 85 object_footer//2, 86 object_page_footer//2, 87 cite//1 88 ]). 90:- if(exists_source(library(http/http_dispatch))). 91:- use_module(library(http/http_dispatch)). 92:- use_module(library(http/http_wrapper)). 93:- use_module(library(http/jquery)). 94
95pldoc_server(true).
96:- else. 97
98:- multifile
99 http:location/3. 100
101http:location(pldoc_resource, '/pldoc/res', []).
102
103pldoc_server(false).
104:- endif. 105
106:- use_module(library(lists)). 107:- use_module(library(option)). 108:- use_module(library(uri)). 109:- use_module(library(readutil)). 110:- use_module(library(http/html_write)). 111:- use_module(library(http/http_path)). 112:- use_module(library(http/html_head)). 113:- use_module(library(http/term_html)). 114:- use_module(library(debug)). 115:- use_module(library(apply)). 116:- use_module(library(pairs)). 117:- use_module(library(filesex)). 118:- use_module(doc_process). 119:- use_module(doc_man). 120:- use_module(doc_modes). 121:- use_module(doc_wiki). 122:- use_module(doc_search). 123:- use_module(doc_index). 124:- use_module(doc_util). 125:- use_module(library(solution_sequences)). 126:- use_module(library(error)). 127:- use_module(library(occurs)). 128:- use_module(library(prolog_source)). 129:- use_module(library(prolog_xref)). 130
131:- include(hooks). 132
133
142
143:- public
144 args//1, 145 pred_dt//3, 146 section//2,
147 tag//2. 148
149
150:- predicate_options(doc_for_wiki_file/2, 2,
151 [ edit(boolean)
152 ]). 153:- predicate_options(doc_hide_private/3, 3,
154 [module(atom), public(list), public_only(boolean)]). 155:- predicate_options(edit_button//2, 2,
156 [ edit(boolean)
157 ]). 158:- predicate_options(file//2, 2,
159 [ label(any),
160 absolute_path(atom),
161 href(atom),
162 map_extension(list),
163 files(list),
164 edit_handler(atom)
165 ]). 166:- predicate_options(file_header//2, 2,
167 [ edit(boolean),
168 files(list),
169 public_only(boolean)
170 ]). 171:- predicate_options(include//3, 3,
172 [ absolute_path(atom),
173 class(atom),
174 files(list),
175 href(atom),
176 label(any),
177 map_extension(list)
178 ]). 179:- predicate_options(object_edit_button//2, 2,
180 [ edit(boolean),
181 pass_to(pred_edit_button//2, 2)
182 ]). 183:- predicate_options(object_page//2, 2,
184 [ for(any),
185 header(boolean),
186 links(boolean),
187 no_manual(boolean),
188 try_manual(boolean),
189 search_in(oneof([all,app,man])),
190 search_match(oneof([name,summary])),
191 search_options(boolean)
192 ]). 193:- predicate_options(object_ref//2, 2,
194 [ files(list),
195 qualify(boolean),
196 style(oneof([number,title,number_title])),
197 secref_style(oneof([number,title,number_title]))
198 ]). 199:- predicate_options(object_synopsis//2, 2,
200 [ href(atom)
201 ]). 202:- predicate_options(pred_dt//3, 3,
203 [ edit(boolean)
204 ]). 205:- predicate_options(pred_edit_button//2, 2,
206 [ edit(boolean)
207 ]). 208:- predicate_options(predref//2, 2,
209 [ files(list),
210 prefer(oneof([manual,app])),
211 pass_to(object_ref/4, 2)
212 ]). 213:- predicate_options(private/2, 2,
214 [ module(atom),
215 public(list)
216 ]). 217:- predicate_options(source_button//2, 2,
218 [ files(list)
219 ]). 220
221
222 225
226:- if(pldoc_server(true)). 227:- html_resource(pldoc_css,
228 [ virtual(true),
229 requires([ pldoc_resource('pldoc.css')
230 ])
231 ]). 232:- html_resource(pldoc_resource('pldoc.js'),
233 [ requires([ jquery
234 ])
235 ]). 236:- html_resource(pldoc_js,
237 [ virtual(true),
238 requires([ pldoc_resource('pldoc.js')
239 ])
240 ]). 241:- html_resource(pldoc,
242 [ virtual(true),
243 requires([ pldoc_css,
244 pldoc_js
245 ])
246 ]). 247:- else. 248:- html_resource(pldoc_css, [virtual(true)]). 249:- html_resource(pldoc_resource('pldoc.js'), [virtual(true)]). 250:- html_resource(pldoc_js, [virtual(true)]). 251:- html_resource(pldoc, [virtual(true)]). 252:- endif. 253
254
255 258
277
278doc_for_file(FileSpec, Options) :-
279 doc_file_objects(FileSpec, File, Objects, FileOptions, Options),
280 doc_file_title(File, Title, FileOptions, Options),
281 doc_write_page(
282 pldoc(file(File, Title)),
283 title(Title),
284 \prolog_file(File, Objects, FileOptions, Options),
285 Options).
286
287doc_file_title(_, Title, _, Options) :-
288 option(title(Title), Options),
289 !.
290doc_file_title(File, Title, FileOptions, _) :-
291 memberchk(file(Title0, _Comment), FileOptions),
292 !,
293 file_base_name(File, Base),
294 atomic_list_concat([Base, ' -- ', Title0], Title).
295doc_file_title(File, Title, _, _) :-
296 file_base_name(File, Title).
297
298:- html_meta doc_write_page(+, html, html, +). 299
300doc_write_page(Style, Head, Body, Options) :-
301 option(files(_), Options),
302 !,
303 phrase(page(Style, Head, Body), HTML),
304 print_html(HTML).
305doc_write_page(Style, Head, Body, _) :-
306 reply_html_page(Style, Head, Body).
307
308
309prolog_file(File, Objects, FileOptions, Options) -->
310 { b_setval(pldoc_file, File), 311 file_directory_name(File, Dir)
312 },
313 html([ \doc_resources(Options),
314 \doc_links(Dir, FileOptions),
315 \file_header(File, FileOptions)
316 | \objects(Objects, FileOptions)
317 ]),
318 undocumented(File, Objects, FileOptions).
319
324
325doc_resources(Options) -->
326 { option(resource_directory(ResDir), Options),
327 nb_current(pldoc_output, OutputFile),
328 !,
329 directory_file_path(ResDir, 'pldoc.css', Res),
330 relative_file_name(Res, OutputFile, Ref)
331 },
332 html_requires(Ref).
333doc_resources(Options) -->
334 { option(html_resources(Resoures), Options, pldoc)
335 },
336 html_requires(Resoures).
337
338
364
365doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
366 xref_current_source(FileSpec),
367 xref_option(FileSpec, comments(collect)),
368 !,
369 File = FileSpec,
370 findall(Object, xref_doc_object(File, Object), Objects0),
371 reply_file_objects(File, Objects0, Objects, FileOptions, Options).
372doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
373 absolute_file_name(FileSpec, File,
374 [ file_type(prolog),
375 access(read)
376 ]),
377 source_file(File),
378 !,
379 ensure_doc_objects(File),
380 Pos = File:Line,
381 findall(Line-doc(Obj,Pos,Comment),
382 doc_comment(Obj, Pos, _, Comment), Pairs),
383 sort(Pairs, Pairs1), 384 keysort(Pairs1, ByLine),
385 pairs_values(ByLine, Objs0),
386 reply_file_objects(File, Objs0, Objects, FileOptions, Options).
387doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
388 absolute_file_name(FileSpec, File,
389 [ file_type(prolog),
390 access(read)
391 ]),
392 xref_source(File, [silent(true)]),
393 findall(Object, xref_doc_object(File, Object), Objects0),
394 reply_file_objects(File, Objects0, Objects, FileOptions, Options).
395
396
397reply_file_objects(File, Objs0, Objects, FileOptions, Options) :-
398 module_info(File, ModuleOptions, Options),
399 file_info(Objs0, Objs1, FileOptions, ModuleOptions),
400 doc_hide_private(Objs1, ObjectsSelf, ModuleOptions),
401 include_reexported(ObjectsSelf, Objects1, File, FileOptions),
402 remove_doc_duplicates(Objects1, Objects, []).
403
404remove_doc_duplicates([], [], _).
405remove_doc_duplicates([H|T0], [H|T], Seen) :-
406 H = doc(_, _, Comment),
407 \+ memberchk(Comment, Seen),
408 !,
409 remove_doc_duplicates(T0, T, [Comment|Seen]).
410remove_doc_duplicates([_|T0], T, Seen) :-
411 remove_doc_duplicates(T0, T, Seen).
412
413include_reexported(SelfObjects, Objects, File, Options) :-
414 option(include_reexported(true), Options),
415 option(module(Module), Options),
416 option(public(Exports), Options),
417 select_undocumented(Exports, Module, SelfObjects, Undoc),
418 re_exported_doc(Undoc, File, Module, REObjs, _),
419 REObjs \== [],
420 !,
421 append(SelfObjects, REObjs, Objects).
422include_reexported(Objects, Objects, _, _).
423
424
426
427xref_doc_object(File, doc(M:module(Title),File:0,Comment)) :-
428 xref_comment(File, Title, Comment),
429 xref_module(File, M).
430xref_doc_object(File, doc(M:Name/Arity,File:0,Comment)) :-
431 xref_comment(File, Head, _Summary, Comment),
432 xref_module(File, Module),
433 strip_module(Module:Head, M, Plain),
434 functor(Plain, Name, Arity).
435
444
445:- dynamic
446 no_comments/2. 447
448ensure_doc_objects(File) :-
449 source_file(File),
450 !,
451 ( doc_file_has_comments(File)
452 -> true
453 ; no_comments(File, TimeChecked),
454 time_file(File, TimeChecked)
455 -> true
456 ; xref_source(File, [silent(true), comments(store)]),
457 retractall(no_comments(File, _)),
458 ( doc_file_has_comments(File)
459 -> true
460 ; time_file(File, TimeChecked),
461 assertz(no_comments(File, TimeChecked))
462 )
463 ).
464ensure_doc_objects(File) :-
465 xref_source(File, [silent(true)]).
466
471
472module_info(File, [module(Module), public(Exports)|Options], Options) :-
473 module_property(Module, file(File)),
474 !,
475 module_property(Module, exports(Exports)).
476module_info(File, [module(Module), public(Exports)|Options], Options) :-
477 xref_module(File, Module),
478 !,
479 findall(PI, xref_exported_pi(File, PI), Exports).
480module_info(_, Options, Options).
481
482xref_exported_pi(Src, Name/Arity) :-
483 xref_exported(Src, Head),
484 functor(Head, Name, Arity).
485
489
490doc_hide_private(Objs, Objs, Options) :-
491 option(public_only(false), Options, true),
492 !.
493doc_hide_private(Objs0, Objs, Options) :-
494 hide_private(Objs0, Objs, Options).
495
496hide_private([], [], _).
497hide_private([H|T0], T, Options) :-
498 obj(H, Obj),
499 private(Obj, Options),
500 !,
501 hide_private(T0, T, Options).
502hide_private([H|T0], [H|T], Options) :-
503 hide_private(T0, T, Options).
504
510
511obj(doc(Obj0, _Pos, _Summary), Obj) :-
512 !,
513 ( Obj0 = [Obj|_]
514 -> true
515 ; Obj = Obj0
516 ).
517obj(Obj0, Obj) :-
518 ( Obj0 = [Obj|_]
519 -> true
520 ; Obj = Obj0
521 ).
522
523
529
530:- multifile
531 prolog:doc_is_public_object/1. 532
533private(Object, _Options):-
534 prolog:doc_is_public_object(Object), !, fail.
535private(Module:PI, Options) :-
536 multifile(Module:PI, Options), !, fail.
537private(Module:PI, Options) :-
538 public(Module:PI, Options), !, fail.
539private(Module:PI, Options) :-
540 option(module(Module), Options),
541 option(public(Public), Options),
542 !,
543 \+ ( member(PI2, Public),
544 eq_pi(PI, PI2)
545 ).
546private(Module:PI, _Options) :-
547 module_property(Module, file(_)), 548 !,
549 module_property(Module, exports(Exports)),
550 \+ ( member(PI2, Exports),
551 eq_pi(PI, PI2)
552 ).
553private(Module:PI, _Options) :-
554 \+ (pi_to_head(PI, Head),
555 xref_exported(Source, Head),
556 xref_module(Source, Module)).
557
562
566
567multifile(Obj, _Options) :-
568 strip_module(user:Obj, Module, PI),
569 pi_to_head(PI, Head),
570 ( predicate_property(Module:Head, multifile)
571 ; xref_module(Source, Module),
572 xref_defined(Source, Head, multifile(_Line))
573 ),
574 !.
575
579
580public(Obj, _Options) :-
581 strip_module(user:Obj, Module, PI),
582 pi_to_head(PI, Head),
583 ( predicate_property(Module:Head, public)
584 ; xref_module(Source, Module),
585 xref_defined(Source, Head, public(_Line))
586 ),
587 !.
588
589pi_to_head(Var, _) :-
590 var(Var), !, fail.
591pi_to_head(Name/Arity, Term) :-
592 functor(Term, Name, Arity).
593pi_to_head(Name//DCGArity, Term) :-
594 Arity is DCGArity+2,
595 functor(Term, Name, Arity).
596
600
601file_info(Comments, RestComments, [file(Title, Comment)|Opts], Opts) :-
602 select(doc(_:module(Title),_,Comment), Comments, RestComments),
603 !.
604file_info(Comments, Comments, Opts, Opts).
605
606
610
(File, Options) -->
612 { memberchk(file(Title, Comment), Options),
613 !,
614 file_base_name(File, Base)
615 },
616 file_title([Base, ' -- ', Title], File, Options),
617 { is_structured_comment(Comment, Prefixes),
618 string_codes(Comment, Codes),
619 indented_lines(Codes, Prefixes, Lines),
620 section_comment_header(Lines, _Header, Lines1),
621 wiki_lines_to_dom(Lines1, [], DOM)
622 },
623 html(DOM).
624file_header(File, Options) -->
625 { file_base_name(File, Base)
626 },
627 file_title([Base], File, Options).
628
629
633
634file_title(Title, File, Options) -->
635 prolog:doc_file_title(Title, File, Options),
636 !.
637file_title(Title, File, Options) -->
638 { file_base_name(File, Base)
639 },
640 html(h1(class(file),
641 [ span(style('float:right'),
642 [ \reload_button(File, Base, Options),
643 \zoom_button(Base, Options),
644 \source_button(Base, Options),
645 \edit_button(File, Options)
646 ])
647 | Title
648 ])).
649
650
657
658reload_button(File, _Base, Options) -->
659 { \+ source_file(File),
660 \+ option(files(_), Options)
661 },
662 !,
663 html(span(class(file_anot), '[not loaded]')).
664reload_button(_File, Base, Options) -->
665 { option(edit(true), Options),
666 !,
667 option(public_only(Public), Options, true)
668 },
669 html(a(href(Base+[reload(true), public_only(Public)]),
670 img([ class(action),
671 alt('Reload'),
672 title('Make & Reload'),
673 src(location_by_id(pldoc_resource)+'reload.png')
674 ]))).
675reload_button(_, _, _) --> [].
676
682
683edit_button(File, Options) -->
684 { option(edit(true), Options)
685 },
686 !,
687 html(a([ onClick('HTTPrequest(\'' +
688 location_by_id(pldoc_edit) + [file(File)] +
689 '\')')
690 ],
691 img([ class(action),
692 alt(edit),
693 title('Edit file'),
694 src(location_by_id(pldoc_resource)+'edit.png')
695 ]))).
696edit_button(_, _) -->
697 [].
698
699
703
704zoom_button(_, Options) -->
705 { option(files(_Map), Options) },
706 !. 707zoom_button(Base, Options) -->
708 { ( option(public_only(true), Options, true)
709 -> Zoom = 'public.png',
710 Alt = 'Public',
711 Title = 'Click to include private',
712 PublicOnly = false
713 ; Zoom = 'private.png',
714 Alt = 'All predicates',
715 Title = 'Click to show exports only',
716 PublicOnly = true
717 )
718 },
719 html(a(href(Base+[public_only(PublicOnly)]),
720 img([ class(action),
721 alt(Alt),
722 title(Title),
723 src(location_by_id(pldoc_resource)+Zoom)
724 ]))).
725
726
730
731source_button(_File, Options) -->
732 { option(files(_Map), Options) },
733 !. 734source_button(File, _Options) -->
735 { ( is_absolute_file_name(File)
736 -> doc_file_href(File, HREF0)
737 ; HREF0 = File
738 )
739 },
740 html(a(href(HREF0+[show(src)]),
741 img([ class(action),
742 alt('Show source'),
743 title('Show source'),
744 src(location_by_id(pldoc_resource)+'source.png')
745 ]))).
746
747
754
755objects(Objects, Options) -->
756 { option(navtree(true), Options),
757 !,
758 objects_nav_tree(Objects, Tree)
759 },
760 html([ div(class(navtree),
761 div(class(navwindow),
762 \nav_tree(Tree, Objects, Options))),
763 div(class(navcontent),
764 \objects_nt(Objects, Options))
765 ]).
766objects(Objects, Options) -->
767 objects_nt(Objects, Options).
768
769objects_nt(Objects, Options) -->
770 objects(Objects, [body], Options).
771
772objects([], Mode, _) -->
773 pop_mode(body, Mode, _).
774objects([Obj|T], Mode, Options) -->
775 object(Obj, Mode, Mode1, Options),
776 objects(T, Mode1, Options).
777
786
787object(doc(Obj,Pos,Comment), Mode0, Mode, Options) -->
788 !,
789 object(Obj, [Pos-Comment], Mode0, Mode, [scope(file)|Options]).
790object(Obj, Mode0, Mode, Options) -->
791 { findall(Pos-Comment,
792 doc_comment(Obj, Pos, _Summary, Comment),
793 Pairs)
794 },
795 !,
796 { b_setval(pldoc_object, Obj) },
797 object(Obj, Pairs, Mode0, Mode, Options).
798
799object(Obj, Pairs, Mode0, Mode, Options) -->
800 { is_pi(Obj),
801 !,
802 maplist(pred_dom(Obj, Options), Pairs, DOMS),
803 append(DOMS, DOM)
804 },
805 need_mode(dl, Mode0, Mode),
806 html(DOM).
807object([Obj|_Same], Pairs, Mode0, Mode, Options) -->
808 !,
809 object(Obj, Pairs, Mode0, Mode, Options).
810object(Obj, _Pairs, Mode, Mode, _Options) -->
811 { debug(pldoc, 'Skipped ~p', [Obj]) },
812 [].
813
814pred_dom(Obj, Options, Pos-Comment, DOM) :-
815 is_structured_comment(Comment, Prefixes),
816 string_codes(Comment, Codes),
817 indented_lines(Codes, Prefixes, Lines),
818 strip_module(user:Obj, Module, _),
819 process_modes(Lines, Module, Pos, Modes, Args, Lines1),
820 ( private(Obj, Options)
821 -> Class = privdef 822 ; multifile(Obj, Options)
823 -> ( option(scope(file), Options)
824 -> ( more_doc(Obj, Pos)
825 -> Class = multidef(object(Obj))
826 ; Class = multidef
827 )
828 ; Class = multidef(file((Pos)))
829 )
830 ; public(Obj, Options)
831 -> Class = publicdef 832 ; Class = pubdef 833 ),
834 ( Obj = Module:_
835 -> POptions = [module(Module)|Options]
836 ; POptions = Options
837 ),
838 Pos = File:Line,
839 DTOptions = [file(File),line(Line)|POptions],
840 DOM = [\pred_dt(Modes, Class, DTOptions), dd(class=defbody, DOM1)],
841 wiki_lines_to_dom(Lines1, Args, DOM0),
842 strip_leading_par(DOM0, DOM1).
843
844more_doc(Obj, File:_) :-
845 doc_comment(Obj, File2:_, _, _),
846 File2 \== File,
847 !.
848
855
856need_mode(Mode, Stack, Stack) -->
857 { Stack = [Mode|_] },
858 !,
859 [].
860need_mode(Mode, Stack, Rest) -->
861 { memberchk(Mode, Stack)
862 },
863 !,
864 pop_mode(Mode, Stack, Rest).
865need_mode(Mode, Stack, [Mode|Stack]) -->
866 !,
867 html_begin(Mode).
868
869pop_mode(Mode, Stack, Stack) -->
870 { Stack = [Mode|_] },
871 !,
872 [].
873pop_mode(Mode, [H|Rest0], Rest) -->
874 html_end(H),
875 pop_mode(Mode, Rest0, Rest).
876
880
881undocumented(File, Objs, Options) -->
882 { memberchk(module(Module), Options),
883 memberchk(public(Exports), Options),
884 select_undocumented(Exports, Module, Objs, Undoc),
885 re_exported_doc(Undoc, File, Module, REObjs, ReallyUnDoc)
886 },
887 !,
888 re_exported_doc(REObjs, Options),
889 undocumented(ReallyUnDoc, Options).
890undocumented(_, _, _) -->
891 [].
892
893re_exported_doc([], _) --> !.
894re_exported_doc(Objs, Options) -->
895 reexport_header(Objs, Options),
896 objects(Objs, Options).
897
(_, Options) -->
899 { option(reexport_header(true), Options, true)
900 },
901 !,
902 html([ h2(class(wiki), 'Re-exported predicates'),
903 p([ "The following predicates are exported from this file \c
904 while their implementation is defined in imported modules \c
905 or non-module files loaded by this module."
906 ])
907 ]).
908reexport_header(_, _) -->
909 [].
910
911undocumented([], _) --> !.
912undocumented(UnDoc, Options) -->
913 html([ h2(class(undoc), 'Undocumented predicates'),
914 p(['The following predicates are exported, but not ',
915 'or incorrectly documented.'
916 ]),
917 dl(class(undoc),
918 \undocumented_predicates(UnDoc, Options))
919 ]).
920
921
922undocumented_predicates([], _) -->
923 [].
924undocumented_predicates([H|T], Options) -->
925 undocumented_pred(H, Options),
926 undocumented_predicates(T, Options).
927
928undocumented_pred(Name/Arity, Options) -->
929 { functor(Head, Name, Arity) },
930 html(dt(class=undoc, \pred_mode(Head, [], _, Options))).
931
932select_undocumented([], _, _, []).
933select_undocumented([PI|T0], M, Objs, [PI|T]) :-
934 is_pi(PI),
935 \+ in_doc(M:PI, Objs),
936 !,
937 select_undocumented(T0, M, Objs, T).
938select_undocumented([_|T0], M, Objs, T) :-
939 select_undocumented(T0, M, Objs, T).
940
941in_doc(PI, Objs) :-
942 member(doc(O,_,_), Objs),
943 ( is_list(O)
944 -> member(O2, O),
945 eq_pi(PI, O2)
946 ; eq_pi(PI, O)
947 ).
948
949
953
954eq_pi(PI, PI) :- !.
955eq_pi(M:PI1, M:PI2) :-
956 atom(M),
957 !,
958 eq_pi(PI1, PI2).
959eq_pi(Name/A, Name//DCGA) :-
960 A =:= DCGA+2,
961 !.
962eq_pi(Name//DCGA, Name/A) :-
963 A =:= DCGA+2.
964
968
969is_pi(Var) :-
970 var(Var),
971 !,
972 fail.
973is_pi(_:PI) :-
974 !,
975 is_pi(PI).
976is_pi(_/_).
977is_pi(_//_).
978
979
982
983re_exported_doc([], _, _, [], []).
984re_exported_doc([PI|T0], File, Module, [doc(Orig:PI,Pos,Comment)|ObjT], UnDoc) :-
985 pi_to_head(PI, Head),
986 ( predicate_property(Module:Head, imported_from(Orig))
987 -> true
988 ; predicate_property(Module:Head, exported)
989 -> Orig = Module
990 ; xref_defined(File, Head, imported(File2)),
991 ensure_doc_objects(File2),
992 xref_module(File2, Orig)
993 ),
994 doc_comment(Orig:PI, Pos, _, Comment),
995 !,
996 re_exported_doc(T0, File, Module, ObjT, UnDoc).
997re_exported_doc([PI|T0], File, Module, REObj, [PI|UnDoc]) :-
998 re_exported_doc(T0, File, Module, REObj, UnDoc).
999
1000
1001 1004
1012
1013object_page(Obj, Options) -->
1014 prolog:doc_object_page(Obj, Options),
1015 !,
1016 object_page_footer(Obj, Options).
1017object_page(Obj, Options) -->
1018 { doc_comment(Obj, File:_Line, _Summary, _Comment)
1019 },
1020 !,
1021 ( { \+ ( doc_comment(Obj, File2:_, _, _),
1022 File2 \== File )
1023 }
1024 -> html([ \html_requires(pldoc),
1025 \object_page_header(File, Options),
1026 \object_synopsis(Obj, []),
1027 \objects([Obj], Options)
1028 ])
1029 ; html([ \html_requires(pldoc),
1030 \object_page_header(-, Options),
1031 \objects([Obj], [synopsis(true)|Options])
1032 ])
1033 ),
1034 object_page_footer(Obj, Options).
1035object_page(M:Name/Arity, Options) --> 1036 { functor(Head, Name, Arity),
1037 ( predicate_property(M:Head, exported)
1038 -> module_property(M, class(library))
1039 ; \+ predicate_property(M:Head, defined)
1040 )
1041 },
1042 prolog:doc_object_page(Name/Arity, Options),
1043 !,
1044 object_page_footer(Name/Arity, Options).
1045
(File, Options) -->
1047 prolog:doc_page_header(file(File), Options),
1048 !.
1049object_page_header(File, Options) -->
1050 { option(header(true), Options, true) },
1051 !,
1052 html(div(class(navhdr),
1053 [ div(class(jump), \file_link(File)),
1054 div(class(search), \search_form(Options)),
1055 br(clear(right))
1056 ])).
1057object_page_header(_, _) --> [].
1058
1059file_link(-) -->
1060 !,
1061 places_menu(-).
1062file_link(File) -->
1063 { file_directory_name(File, Dir)
1064 },
1065 places_menu(Dir),
1066 html([ div(a(href(location_by_id(pldoc_doc)+File), File))
1067 ]).
1068
1073
(Obj, Options) -->
1075 prolog:doc_object_footer(Obj, Options),
1076 !.
1077object_footer(_, _) --> [].
1078
1079
1084
(Obj, Options) -->
1086 prolog:doc_object_page_footer(Obj, Options),
1087 !.
1088object_page_footer(_, _) --> [].
1089
1090
1101
1102object_synopsis(Name/Arity, _) -->
1103 { functor(Head, Name, Arity),
1104 predicate_property(system:Head, built_in)
1105 },
1106 synopsis([span(class(builtin), 'built-in')]).
1107object_synopsis(Name/Arity, Options) -->
1108 !,
1109 object_synopsis(_:Name/Arity, Options).
1110object_synopsis(M:Name/Arity, Options) -->
1111 { functor(Head, Name, Arity),
1112 ( option(source(Spec), Options)
1113 -> absolute_file_name(Spec, File,
1114 [ access(read),
1115 file_type(prolog),
1116 file_errors(fail)
1117 ])
1118 ; predicate_property(M:Head, exported),
1119 \+ predicate_property(M:Head, imported_from(_)),
1120 module_property(M, file(File)),
1121 file_name_on_path(File, Spec)
1122 ),
1123 !,
1124 unquote_filespec(Spec, Unquoted),
1125 ( predicate_property(Head, autoload(FileBase)),
1126 file_name_extension(FileBase, _Ext, File)
1127 -> Extra = [span(class(autoload), '(can be autoloaded)')]
1128 ; Extra = []
1129 )
1130 },
1131 ( { option(href(HREF), Options) }
1132 -> synopsis([code([':- use_module(',a(href(HREF), '~q'-[Unquoted]),').'])|Extra])
1133 ; synopsis([code(':- use_module(~q).'-[Unquoted])|Extra])
1134 ).
1135object_synopsis(Name//Arity, Options) -->
1136 !,
1137 { DCGArity is Arity+2 },
1138 object_synopsis(Name/DCGArity, Options).
1139object_synopsis(Module:Name//Arity, Options) -->
1140 !,
1141 { DCGArity is Arity+2 },
1142 object_synopsis(Module:Name/DCGArity, Options).
1143object_synopsis(f(_/_), _) -->
1144 synopsis(span(class(function),
1145 [ 'Arithmetic function (see ',
1146 \object_ref(is/2, []),
1147 ')'
1148 ])).
1149object_synopsis(c(Func), _) -->
1150 { sub_atom(Func, 0, _, _, 'PL_')
1151 ; sub_atom(Func, 0, _, _, 'S')
1152 },
1153 !,
1154 synopsis([span(class(cfunc), 'C-language interface function')]).
1155object_synopsis(_, _) --> [].
1156
1157synopsis(Text) -->
1158 html(div(class(synopsis),
1159 [ span(class('synopsis-hdr'), 'Availability:')
1160 | Text
1161 ])).
1162
1167
1168unquote_filespec(Spec, Unquoted) :-
1169 compound(Spec),
1170 Spec =.. [Alias,Path],
1171 atom(Path),
1172 atomic_list_concat(Parts, /, Path),
1173 maplist(need_no_quotes, Parts),
1174 !,
1175 parts_to_path(Parts, UnquotedPath),
1176 Unquoted =.. [Alias, UnquotedPath].
1177unquote_filespec(Spec, Spec).
1178
1179need_no_quotes(Atom) :-
1180 format(atom(A), '~q', [Atom]),
1181 \+ sub_atom(A, 0, _, _, '\'').
1182
1183parts_to_path([One], One) :- !.
1184parts_to_path(List, More/T) :-
1185 ( append(H, [T], List)
1186 -> parts_to_path(H, More)
1187 ).
1188
1189
1190 1193
1197
1198doc_write_html(Out, Title, Doc) :-
1199 doc_page_dom(Title, Doc, DOM),
1200 phrase(html(DOM), Tokens),
1201 print_html_head(Out),
1202 print_html(Out, Tokens).
1203
1208
1209doc_page_dom(Title, Body, DOM) :-
1210 DOM = html([ head([ title(Title),
1211 link([ rel(stylesheet),
1212 type('text/css'),
1213 href(location_by_id(pldoc_resource)+'pldoc.css')
1214 ]),
1215 script([ src(location_by_id(pldoc_resource)+'pldoc.js'),
1216 type('text/javascript')
1217 ], [])
1218 ]),
1219 body(Body)
1220 ]).
1221
1225
1226print_html_head(Out) :-
1227 format(Out,
1228 '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" \c
1229 "http://www.w3.org/TR/html4/strict.dtd">~n', []).
1230
1234
1240
1241tags(Tags) -->
1242 html(dl(class=tags, Tags)).
1243
1247
1248tag(Tag, Values) -->
1249 { doc_tag_title(Tag, Title),
1250 atom_concat('keyword-', Tag, Class)
1251 },
1252 html([ dt(class=Class, Title),
1253 \tag_values(Values, Class)
1254 ]).
1255
1256tag_values([], _) -->
1257 [].
1258tag_values([H|T], Class) -->
1259 html(dd(class=Class, ['- '|H])),
1260 tag_values(T, Class).
1261
1262
1266
1267doc_tag_title(Tag, Title) :-
1268 tag_title(Tag, Title),
1269 !.
1270doc_tag_title(Tag, Tag).
1271
1272tag_title(compat, 'Compatibility').
1273tag_title(tbd, 'To be done').
1274tag_title(see, 'See also').
1275tag_title(error, 'Errors').
1276
1281
1282args(Params) -->
1283 html([ dt(class=tag, 'Arguments:'),
1284 dd(table(class=arglist,
1285 \arg_list(Params)))
1286 ]).
1287
1288arg_list([]) -->
1289 [].
1290arg_list([H|T]) -->
1291 argument(H),
1292 arg_list(T).
1293
1294argument(arg(Name,Descr)) -->
1295 html(tr([td(var(Name)), td(class=argdescr, ['- '|Descr])])).
1296
1297
1298 1301
1306
1307objects_nav_tree(Objects, Tree) :-
1308 maplist(object_nav_tree, Objects, Trees),
1309 union_trees(Trees, Tree0),
1310 remove_unique_root(Tree0, Tree).
1311
1312object_nav_tree(Obj, Tree) :-
1313 Node = node(directory(Dir), FileNodes),
1314 FileNode = node(file(File), Siblings),
1315 doc_comment(Obj, File:_Line, _Summary, _Comment),
1316 !,
1317 file_directory_name(File, Dir),
1318 sibling_file_nodes(Dir, FileNodes0),
1319 selectchk(node(file(File),[]), FileNodes0, FileNode, FileNodes),
1320 findall(Sibling, doc_comment(Sibling, File:_, _, _), Siblings0),
1321 delete(Siblings0, _:module(_), Siblings1),
1322 doc_hide_private(Siblings1, Siblings2, []),
1323 flatten(Siblings2, Siblings), 1324 embed_directories(Node, Tree).
1325
1326sibling_file_nodes(Dir, Nodes) :-
1327 findall(node(file(File), []),
1328 ( source_file(File),
1329 file_directory_name(File, Dir)
1330 ),
1331 Nodes).
1332
1333embed_directories(Node, Tree) :-
1334 Node = node(file(File), _),
1335 !,
1336 file_directory_name(File, Dir),
1337 Super = node(directory(Dir), [Node]),
1338 embed_directories(Super, Tree).
1339embed_directories(Node, Tree) :-
1340 Node = node(directory(Dir), _),
1341 file_directory_name(Dir, SuperDir),
1342 SuperDir \== Dir,
1343 !,
1344 Super = node(directory(SuperDir), [Node]),
1345 embed_directories(Super, Tree).
1346embed_directories(Tree, Tree).
1347
1348
1349union_trees([Tree], Tree) :- !.
1350union_trees([T1,T2|Trees], Tree) :-
1351 merge_trees(T1, T2, M1),
1352 union_trees([M1|Trees], Tree).
1353
1354merge_trees(node(R, Ch1), node(R, Ch2), node(R, Ch)) :-
1355 merge_nodes(Ch1, Ch2, Ch).
1356
1357merge_nodes([], Ch, Ch) :- !.
1358merge_nodes(Ch, [], Ch) :- !.
1359merge_nodes([node(Root, Ch1)|T1], N1, [T1|Nodes]) :-
1360 selectchk(node(Root, Ch2), N1, N2),
1361 !,
1362 merge_trees(node(Root, Ch1), node(Root, Ch2), T1),
1363 merge_nodes(T1, N2, Nodes).
1364merge_nodes([Node|T1], N1, [Node|Nodes]) :-
1365 merge_nodes(T1, N1, Nodes).
1366
1370
1371remove_unique_root(node(_, [node(R1, [R2])]), Tree) :-
1372 !,
1373 remove_unique_root(node(R1, [R2]), Tree).
1374remove_unique_root(Tree, Tree).
1375
1379
1380nav_tree(Tree, Current, Options) -->
1381 html(ul(class(nav),
1382 \object_tree(Tree, Current, Options))).
1383
1387
1388object_tree(node(Id, []), Target, Options) -->
1389 !,
1390 { node_class(Id, Target, Class) },
1391 html(li(class(Class),
1392 \node(Id, Options))).
1393object_tree(node(Id, Children), Target, Options) -->
1394 !,
1395 { node_class(Id, Target, Class) },
1396 html(li(class(Class),
1397 [ \node(Id, Options),
1398 ul(class(nav),
1399 \object_trees(Children, Target, Options))
1400 ])).
1401object_tree(Id, Target, Options) -->
1402 !,
1403 { node_class(Id, Target, Class) },
1404 html(li(class([obj|Class]), \node(Id, Options))).
1405
1406object_trees([], _, _) --> [].
1407object_trees([H|T], Target, Options) -->
1408 object_tree(H, Target, Options),
1409 object_trees(T, Target, Options).
1410
1411node_class(Ids, Current, Class) :-
1412 is_list(Ids),
1413 !,
1414 ( member(Id, Ids), memberchk(Id, Current)
1415 -> Class = [nav,current]
1416 ; Class = [nav]
1417 ).
1418node_class(Id, Current, Class) :-
1419 ( memberchk(Id, Current)
1420 -> Class = [nav,current]
1421 ; Class = [nav]
1422 ).
1423
1424node(file(File), Options) -->
1425 !,
1426 object_ref(file(File), [style(title)|Options]).
1427node(Id, Options) -->
1428 object_ref(Id, Options).
1429
1430
1431 1434
1435section(Type, Title) -->
1436 { string_codes(Title, Codes),
1437 wiki_codes_to_dom(Codes, [], Content0),
1438 strip_leading_par(Content0, Content),
1439 make_section(Type, Content, HTML)
1440 },
1441 html(HTML).
1442
1443make_section(module, Title, h1(class=module, Title)).
1444make_section(section, Title, h1(class=section, Title)).
1445
1446
1447 1450
1456
1457pred_dt(Modes, Class, Options) -->
1458 pred_dt(Modes, Class, [], _Done, Options).
1459
1460pred_dt([], _, Done, Done, _) -->
1461 [].
1462pred_dt([H|T], Class, Done0, Done, Options) -->
1463 { functor(Class, CSSClass, _) },
1464 html(dt(class=CSSClass,
1465 [ \pred_mode(H, Done0, Done1, Options),
1466 \mode_anot(Class)
1467 ])),
1468 pred_dt(T, Class, Done1, Done, Options).
1469
1470mode_anot(privdef) -->
1471 !,
1472 html(span([class(anot), style('float:right')],
1473 '[private]')).
1474mode_anot(multidef(object(Obj))) -->
1475 !,
1476 { object_href(Obj, HREF) },
1477 html(span([class(anot), style('float:right')],
1478 ['[', a(href(HREF), multifile), ']'
1479 ])).
1480mode_anot(multidef(file(File:_))) -->
1481 !,
1482 { file_name_on_path(File, Spec),
1483 unquote_filespec(Spec, Unquoted),
1484 doc_file_href(File, HREF)
1485 },
1486 html(span([class(anot), style('float:right')],
1487 ['[multifile, ', a(href(HREF), '~q'-[Unquoted]), ']'
1488 ])).
1489mode_anot(multidef) -->
1490 !,
1491 html(span([class(anot), style('float:right')],
1492 '[multifile]')).
1493mode_anot(_) -->
1494 [].
1495
1496pred_mode(mode(Head,Vars), Done0, Done, Options) -->
1497 !,
1498 { bind_vars(Head, Vars) },
1499 pred_mode(Head, Done0, Done, Options).
1500pred_mode(Head is Det, Done0, Done, Options) -->
1501 !,
1502 anchored_pred_head(Head, Done0, Done, Options),
1503 pred_det(Det).
1504pred_mode(Head, Done0, Done, Options) -->
1505 anchored_pred_head(Head, Done0, Done, Options).
1506
1507bind_vars(Term, Bindings) :-
1508 bind_vars(Bindings),
1509 anon_vars(Term).
1510
1511bind_vars([]).
1512bind_vars([Name=Var|T]) :-
1513 Var = '$VAR'(Name),
1514 bind_vars(T).
1515
1520
1521anon_vars(Var) :-
1522 var(Var),
1523 !,
1524 Var = '$VAR'('_').
1525anon_vars(Term) :-
1526 compound(Term),
1527 !,
1528 Term =.. [_|Args],
1529 maplist(anon_vars, Args).
1530anon_vars(_).
1531
1532
1533anchored_pred_head(Head, Done0, Done, Options) -->
1534 { pred_anchor_name(Head, PI, Name) },
1535 ( { memberchk(PI, Done0) }
1536 -> { Done = Done0 },
1537 pred_head(Head)
1538 ; html([ span(style('float:right'),
1539 [ \pred_edit_or_source_button(Head, Options),
1540 &(nbsp)
1541 ]),
1542 a(name=Name, \pred_head(Head))
1543 ]),
1544 { Done = [PI|Done0] }
1545 ).
1546
1547
1548pred_edit_or_source_button(Head, Options) -->
1549 { option(edit(true), Options) },
1550 !,
1551 pred_edit_button(Head, Options).
1552pred_edit_or_source_button(Head, Options) -->
1553 { option(source_link(true), Options) },
1554 !,
1555 pred_source_button(Head, Options).
1556pred_edit_or_source_button(_, _) --> [].
1557
1569
1570pred_edit_button(_, Options) -->
1571 { \+ option(edit(true), Options) },
1572 !.
1573pred_edit_button(PI0, Options0) -->
1574 { canonicalise_predref(PI0, PI, Options0, Options) },
1575 pred_edit_button2(PI, Options).
1576
1577pred_edit_button2(Name/Arity, Options) -->
1578 { \+ ( memberchk(file(_), Options), 1579 memberchk(line(_), Options) 1580 ),
1581 functor(Head, Name, Arity),
1582 option(module(M), Options, _),
1583 \+ ( current_module(M),
1584 source_file(M:Head, _File)
1585 )
1586 },
1587 !.
1588pred_edit_button2(Name/Arity, Options) -->
1589 { include(edit_param, Options, Extra),
1590 http_link_to_id(pldoc_edit,
1591 [name(Name),arity(Arity)|Extra],
1592 EditHREF)
1593 },
1594 html(a(onClick('HTTPrequest(\'' + EditHREF + '\')'),
1595 img([ class(action),
1596 alt('Edit predicate'),
1597 title('Edit predicate'),
1598 src(location_by_id(pldoc_resource)+'editpred.png')
1599 ]))).
1600pred_edit_button2(_, _) -->
1601 !,
1602 [].
1603
1604edit_param(module(_)).
1605edit_param(file(_)).
1606edit_param(line(_)).
1607
1608
1612
1613object_edit_button(_, Options) -->
1614 { \+ option(edit(true), Options) },
1615 !.
1616object_edit_button(PI, Options) -->
1617 { is_pi(PI) },
1618 !,
1619 pred_edit_button(PI, Options).
1620object_edit_button(_, _) -->
1621 [].
1622
1623
1627
1628pred_source_button(PI0, Options0) -->
1629 { canonicalise_predref(PI0, PI, Options0, Options),
1630 option(module(M), Options, _),
1631 pred_source_href(PI, M, HREF), !
1632 },
1633 html(a([ href(HREF)
1634 ],
1635 img([ class(action),
1636 alt('Source'),
1637 title('Show source'),
1638 src(location_by_id(pldoc_resource)+'source.png')
1639 ]))).
1640pred_source_button(_, _) -->
1641 [].
1642
1643
1647
1648object_source_button(PI, Options) -->
1649 { is_pi(PI),
1650 option(source_link(true), Options, true)
1651 },
1652 !,
1653 pred_source_button(PI, Options).
1654object_source_button(_, _) -->
1655 [].
1656
1657
1662
1663canonicalise_predref(M:PI0, PI, Options0, [module(M)|Options]) :-
1664 !,
1665 canonicalise_predref(PI0, PI, Options0, Options).
1666canonicalise_predref(//(Head), PI, Options0, Options) :-
1667 !,
1668 functor(Head, Name, Arity),
1669 PredArity is Arity + 2,
1670 canonicalise_predref(Name/PredArity, PI, Options0, Options).
1671canonicalise_predref(Name//Arity, PI, Options0, Options) :-
1672 integer(Arity), Arity >= 0,
1673 !,
1674 PredArity is Arity + 2,
1675 canonicalise_predref(Name/PredArity, PI, Options0, Options).
1676canonicalise_predref(PI, PI, Options, Options) :-
1677 PI = Name/Arity,
1678 atom(Name), integer(Arity), Arity >= 0,
1679 !.
1680canonicalise_predref(Head, PI, Options0, Options) :-
1681 functor(Head, Name, Arity),
1682 canonicalise_predref(Name/Arity, PI, Options0, Options).
1683
1684
1689
1690pred_head(Var) -->
1691 { var(Var),
1692 !,
1693 instantiation_error(Var)
1694 }.
1695pred_head(//(Head)) -->
1696 !,
1697 pred_head(Head),
1698 html(//).
1699pred_head(M:Head) -->
1700 html([span(class=module, M), :]),
1701 pred_head(Head).
1702pred_head(Head) -->
1703 { atom(Head) },
1704 !,
1705 html(b(class=pred, Head)).
1706pred_head(Head) --> 1707 { Head =.. [Functor,Left,Right],
1708 is_op_type(Functor, infix)
1709 },
1710 !,
1711 html([ var(class=arglist, \pred_arg(Left, 1)),
1712 ' ', b(class=pred, Functor), ' ',
1713 var(class=arglist, \pred_arg(Right, 2))
1714 ]).
1715pred_head(Head) --> 1716 { Head =.. [Functor,Arg],
1717 is_op_type(Functor, prefix)
1718 },
1719 !,
1720 html([ b(class=pred, Functor), ' ',
1721 var(class=arglist, \pred_arg(Arg, 1))
1722 ]).
1723pred_head(Head) --> 1724 { Head =.. [Functor,Arg],
1725 is_op_type(Functor, postfix)
1726 },
1727 !,
1728 html([ var(class=arglist, \pred_arg(Arg, 1)),
1729 ' ', b(class=pred, Functor)
1730 ]).
1731pred_head({Head}) -->
1732 !,
1733 html([ b(class=pred, '{'),
1734 var(class=arglist,
1735 \pred_args([Head], 1)),
1736 b(class=pred, '}')
1737 ]).
1738pred_head(Head) --> 1739 { Head =.. [Functor|Args] },
1740 html([ b(class=pred, Functor),
1741 var(class=arglist,
1742 [ '(', \pred_args(Args, 1), ')' ])
1743 ]).
1744
1749
1750is_op_type(Functor, Type) :-
1751 current_op(_Pri, F, Functor),
1752 op_type(F, Type).
1753
1754op_type(fx, prefix).
1755op_type(fy, prefix).
1756op_type(xf, postfix).
1757op_type(yf, postfix).
1758op_type(xfx, infix).
1759op_type(xfy, infix).
1760op_type(yfx, infix).
1761op_type(yfy, infix).
1762
1763
1764pred_args([], _) -->
1765 [].
1766pred_args([H|T], I) -->
1767 pred_arg(H, I),
1768 ( {T==[]}
1769 -> []
1770 ; html(', '),
1771 { I2 is I + 1 },
1772 pred_args(T, I2)
1773 ).
1774
1775pred_arg(Var, I) -->
1776 { var(Var) },
1777 !,
1778 html(['Arg', I]).
1779pred_arg(...(Term), I) -->
1780 !,
1781 pred_arg(Term, I),
1782 html('...').
1783pred_arg(Term, I) -->
1784 { Term =.. [Ind,Arg],
1785 mode_indicator(Ind)
1786 },
1787 !,
1788 html([Ind, \pred_arg(Arg, I)]).
1789pred_arg(Arg:Type, _) -->
1790 !,
1791 html([\argname(Arg), :, \argtype(Type)]).
1792pred_arg(Arg, _) -->
1793 argname(Arg).
1794
1795argname('$VAR'(Name)) -->
1796 !,
1797 html(Name).
1798argname(Name) -->
1799 !,
1800 html(Name).
1801
1802argtype(Term) -->
1803 { format(string(S), '~W',
1804 [ Term,
1805 [ quoted(true),
1806 numbervars(true)
1807 ]
1808 ]) },
1809 html(S).
1810
1811pred_det(unknown) -->
1812 [].
1813pred_det(Det) -->
1814 html([' is ', b(class=det, Det)]).
1815
1816
1822
1823term(_, Atom, []) -->
1824 { atomic(Atom),
1825 !,
1826 format(string(S), '~W', [Atom,[quoted(true)]])
1827 },
1828 html(span(class=functor, S)).
1829term(_, Key:Type, [TypeName=Type]) -->
1830 { atomic(Key)
1831 },
1832 !,
1833 html([span(class='pl-key', Key), :, span(class('pl-var'), TypeName)]).
1834term(_, Term, Bindings) -->
1835 { is_mode(Term is det), 1836 bind_vars(Bindings)
1837 },
1838 !,
1839 pred_head(Term).
1840term(_, Term, Bindings) -->
1841 term(Term,
1842 [ variable_names(Bindings),
1843 quoued(true)
1844 ]).
1845
1846
1847 1850
1861
1862predref(Term) -->
1863 { catch(nb_getval(pldoc_options, Options), _, Options = []) },
1864 predref(Term, Options).
1865
1866predref(Obj, Options) -->
1867 { Obj = _:_,
1868 doc_comment(Obj, File:_Line, _, _),
1869 ( ( option(files(Map), Options)
1870 -> memberchk(file(File,_), Map)
1871 ; true
1872 )
1873 -> object_href(Obj, HREF, Options)
1874 ; manref(Obj, HREF, Options)
1875 )
1876 },
1877 !,
1878 html(a(href(HREF), \object_name(Obj, [qualify(true)|Options]))).
1879predref(M:Term, Options) -->
1880 !,
1881 predref(Term, M, Options).
1882predref(Term, Options) -->
1883 predref(Term, _, Options).
1884
1885predref(Name/Arity, _, Options) --> 1886 { prolog:doc_object_summary(Name/Arity, manual, _, _),
1887 !,
1888 manref(Name/Arity, HREF, Options)
1889 },
1890 html(a([class=builtin, href=HREF], [Name, /, Arity])).
1891predref(Name/Arity, _, Options) --> 1892 { option(prefer(manual), Options),
1893 prolog:doc_object_summary(Name/Arity, Category, _, _),
1894 !,
1895 manref(Name/Arity, HREF, Options)
1896 },
1897 html(a([class=Category, href=HREF], [Name, /, Arity])).
1898predref(Obj, Module, Options) --> 1899 { doc_comment(Module:Obj, File:_Line, _, _),
1900 ( option(files(Map), Options)
1901 -> memberchk(file(File,_), Map)
1902 ; true
1903 )
1904 },
1905 !,
1906 object_ref(Module:Obj, Options).
1907predref(Name/Arity, Module, Options) -->
1908 { \+ option(files(_), Options),
1909 pred_href(Name/Arity, Module, HREF)
1910 },
1911 !,
1912 html(a(href=HREF, [Name, /, Arity])).
1913predref(Name//Arity, Module, Options) -->
1914 { \+ option(files(_), Options),
1915 PredArity is Arity + 2,
1916 pred_href(Name/PredArity, Module, HREF)
1917 },
1918 !,
1919 html(a(href=HREF, [Name, //, Arity])).
1920predref(PI, _, Options) --> 1921 { canonical_pi(PI, CPI, HTML),
1922 ( option(files(_), Options)
1923 -> Category = extmanual
1924 ; prolog:doc_object_summary(CPI, Category, _, _)
1925 ),
1926 manref(CPI, HREF, Options)
1927 },
1928 html(a([class=Category, href=HREF], HTML)).
1929predref(PI, _, _Options) -->
1930 { canonical_pi(PI, _CPI, HTML)
1931 },
1932 !,
1933 html(span(class=undef, HTML)).
1934predref(Callable, Module, Options) -->
1935 { callable(Callable),
1936 functor(Callable, Name, Arity)
1937 },
1938 predref(Name/Arity, Module, Options).
1939
1940canonical_pi(Name/Arity, Name/Arity, [Name, /, Arity]) :-
1941 atom(Name), integer(Arity),
1942 !.
1943canonical_pi(Name//Arity, Name/Arity2, [Name, //, Arity]) :-
1944 atom(Name), integer(Arity),
1945 !,
1946 Arity2 is Arity+2.
1947
1951
1952nopredref(PI) -->
1953 { canonical_pi(PI, _CPI, HTML)
1954 },
1955 !,
1956 html(span(class=nopredref, HTML)).
1957
1963
1964flagref(Flag) -->
1965 html(code(Flag)).
1966
1971
1972cite(Citations) -->
1973 html('['), citations(Citations), html(']').
1974
1975citations([]) --> [].
1976citations([H|T]) -->
1977 citation(H),
1978 ( {T==[]}
1979 -> []
1980 ; [';'],
1981 citations(T)
1982 ).
1983
1984citation(H) -->
1985 html([@,H]).
1986
1987
1992
1993manref(PI, HREF, Options) :-
1994 predname(PI, PredName),
1995 ( option(files(_Map), Options)
1996 -> option(man_server(Server), Options,
1997 'http://www.swi-prolog.org/pldoc'),
1998 uri_components(Server, Comp0),
1999 uri_data(path, Comp0, Path0),
2000 directory_file_path(Path0, man, Path),
2001 uri_data(path, Comp0, Path, Components),
2002 uri_query_components(Query, [predicate=PredName]),
2003 uri_data(search, Components, Query),
2004 uri_components(HREF, Components)
2005 ; http_link_to_id(pldoc_man, [predicate=PredName], HREF)
2006 ).
2007
2008predname(Name/Arity, PredName) :-
2009 !,
2010 format(atom(PredName), '~w/~d', [Name, Arity]).
2011predname(Module:Name/Arity, PredName) :-
2012 !,
2013 format(atom(PredName), '~w:~w/~d', [Module, Name, Arity]).
2014
2015
2026
2027pred_href(Name/Arity, Module, HREF) :-
2028 format(string(FragmentId), '~w/~d', [Name, Arity]),
2029 uri_data(fragment, Components, FragmentId),
2030 functor(Head, Name, Arity),
2031 ( catch(relative_file(Module:Head, File), _, fail)
2032 -> uri_data(path, Components, File),
2033 uri_components(HREF, Components)
2034 ; in_file(Module:Head, File)
2035 -> ( current_prolog_flag(home, SWI),
2036 sub_atom(File, 0, _, _, SWI),
2037 prolog:doc_object_summary(Name/Arity, packages, _, _)
2038 -> http_link_to_id(pldoc_man, [predicate=FragmentId], HREF)
2039 ; http_location_by_id(pldoc_doc, DocHandler),
2040 atom_concat(DocHandler, File, Path),
2041 uri_data(path, Components, Path),
2042 uri_components(HREF, Components)
2043 )
2044 ).
2045
2046relative_file(Head, '') :-
2047 b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
2048 in_file(Head, CurrentFile),
2049 !.
2050relative_file(Head, RelFile) :-
2051 b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
2052 in_file(Head, DefFile),
2053 relative_file_name(DefFile, CurrentFile, RelFile).
2054
2058
2059pred_source_href(Name/Arity, Module, HREF) :-
2060 format(string(FragmentId), '~w/~d', [Name, Arity]),
2061 uri_data(fragment, Components, FragmentId),
2062 uri_query_components(Query, [show=src]),
2063 uri_data(search, Components, Query),
2064 functor(Head, Name, Arity),
2065 ( catch(relative_file(Module:Head, File), _, fail)
2066 -> uri_data(path, Components, File),
2067 uri_components(HREF, Components)
2068 ; in_file(Module:Head, File0)
2069 -> insert_alias(File0, File),
2070 http_location_by_id(pldoc_doc, DocHandler),
2071 atom_concat(DocHandler, File, Path),
2072 uri_data(path, Components, Path),
2073 uri_components(HREF, Components)
2074 ).
2075
2076
2082
2083object_ref([], _) -->
2084 !,
2085 [].
2086object_ref([H|T], Options) -->
2087 !,
2088 object_ref(H, Options),
2089 ( {T == []}
2090 -> html(', '),
2091 object_ref(T, Options)
2092 ; []
2093 ).
2094object_ref(Obj, Options) -->
2095 { object_href(Obj, HREF, Options)
2096 },
2097 html(a(href(HREF), \object_name(Obj, Options))).
2098
2103
2104object_href(Obj, HREF) :-
2105 object_href(Obj, HREF, []).
2106
2107object_href(M:PI0, HREF, Options) :-
2108 option(files(Map), Options),
2109 ( module_property(M, file(File))
2110 -> true
2111 ; xref_module(File, M)
2112 ),
2113 memberchk(file(File, DocFile), Map),
2114 !,
2115 file_base_name(DocFile, LocalFile), 2116 expand_pi(PI0, PI),
2117 term_to_string(PI, PIS),
2118 uri_data(path, Components, LocalFile),
2119 uri_data(fragment, Components, PIS),
2120 uri_components(HREF, Components).
2121object_href(file(File), HREF, _Options) :-
2122 doc_file_href(File, HREF),
2123 !.
2124object_href(directory(Dir), HREF, _Options) :-
2125 directory_file_path(Dir, 'index.html', Index),
2126 doc_file_href(Index, HREF),
2127 !.
2128object_href(Obj, HREF, _Options) :-
2129 prolog:doc_object_href(Obj, HREF),
2130 !.
2131object_href(Obj0, HREF, _Options) :-
2132 localise_object(Obj0, Obj),
2133 term_to_string(Obj, String),
2134 http_link_to_id(pldoc_object, [object=String], HREF).
2135
2136expand_pi(Name//Arity0, Name/Arity) :-
2137 !,
2138 Arity is Arity0+2.
2139expand_pi(PI, PI).
2140
2141
2146
2147localise_object(Obj0, Obj) :-
2148 prolog:doc_canonical_object(Obj0, Obj),
2149 !.
2150localise_object(Obj, Obj).
2151
2152
2157
2158term_to_string(Term, String) :-
2159 State = state(-),
2160 ( numbervars(Term, 0, _, [singletons(true)]),
2161 with_output_to(string(String),
2162 write_term(Term,
2163 [ numbervars(true),
2164 quoted(true)
2165 ])),
2166 nb_setarg(1, State, String),
2167 fail
2168 ; arg(1, State, String)
2169 ).
2170
2182
2183object_name(Obj, Options) -->
2184 { option(style(Style), Options, inline)
2185 },
2186 object_name(Style, Obj, Options).
2187
2188object_name(title, Obj, Options) -->
2189 { merge_options(Options, [secref_style(title)], Options1) },
2190 prolog:doc_object_link(Obj, Options1),
2191 !.
2192object_name(inline, Obj, Options) -->
2193 prolog:doc_object_link(Obj, Options),
2194 !.
2195object_name(title, f(Name/Arity), _Options) -->
2196 !,
2197 html(['Function ', Name, /, Arity]).
2198object_name(inline, f(Name/Arity), _Options) -->
2199 !,
2200 html([Name, /, Arity]).
2201object_name(Style, PI, Options) -->
2202 { is_pi(PI) },
2203 !,
2204 pi(Style, PI, Options).
2205object_name(inline, Module:module(_Title), _) -->
2206 !,
2207 { module_property(Module, file(File)),
2208 file_base_name(File, Base)
2209 },
2210 !,
2211 html(Base).
2212object_name(title, Module:module(Title), _) -->
2213 { module_property(Module, file(File)),
2214 file_base_name(File, Base)
2215 },
2216 !,
2217 html([Base, ' -- ', Title]).
2218object_name(title, file(File), _) -->
2219 { module_property(Module, file(File)),
2220 doc_comment(Module:module(Title), _, _, _),
2221 !,
2222 file_base_name(File, Base)
2223 },
2224 html([Base, ' -- ', Title]).
2225object_name(_, file(File), _) -->
2226 { file_base_name(File, Base) },
2227 html(Base).
2228object_name(_, directory(Dir), _) -->
2229 { file_base_name(Dir, Base) },
2230 html(Base).
2231object_name(_, module(Title), _Options) -->
2232 { print_message(warning,
2233 pldoc(module_comment_outside_module(Title)))
2234 }.
2235
2236pi(title, PI, Options) -->
2237 pi_type(PI),
2238 pi(PI, Options).
2239pi(inline, PI, Options) -->
2240 pi(PI, Options).
2241
2242pi(M:PI, Options) -->
2243 !,
2244 ( { option(qualify(true), Options) }
2245 -> html([span(class(module), M), :])
2246 ; []
2247 ),
2248 pi(PI, Options).
2249pi(Name/Arity, _) -->
2250 !,
2251 html([Name, /, Arity]).
2252pi(Name//Arity, _) -->
2253 html([Name, //, Arity]).
2254
2255pi_type(_:PI) -->
2256 !,
2257 pi_type(PI).
2258pi_type(_/_) -->
2259 html(['Predicate ']).
2260pi_type(_//_) -->
2261 html(['Grammar rule ']).
2262
2263
2264
2272
2273in_file(Module:Head, File) :-
2274 !,
2275 distinct(File, in_file(Module, Head, File)).
2276in_file(Head, File) :-
2277 distinct(File, in_file(_, Head, File)).
2278
2279in_file(Module, Head, File) :-
2280 var(Module),
2281 ( predicate_property(system:Head, foreign)
2282 -> !,
2283 fail
2284 ; predicate_property(system:Head, file(File)),
2285 \+ system_arithmetic_function(Head)
2286 -> !
2287 ; predicate_property(Head, autoload(File0))
2288 -> !,
2289 file_name_extension(File0, pl, File)
2290 ; exported_from(Module, Head, File),
2291 module_property(Module, class(library))
2292 ).
2293in_file(Module, Head, File) :-
2294 nonvar(Module),
2295 predicate_property(Module:Head, file(File)),
2296 \+ predicate_property(Module:Head, imported_from(_)).
2297in_file(Module, Head, File) :-
2298 xref_defined(File, Head, How),
2299 xref_current_source(File),
2300 atom(File), 2301 xref_module(File, Module),
2302 How \= imported(_From).
2303in_file(Module, Head, File) :-
2304 exported_from(Module, Head, File).
2305in_file(Module, Head, File) :-
2306 predicate_property(Module:Head, file(File)),
2307 \+ predicate_property(Module:Head, imported_from(_)).
2308in_file(Module, Head, File) :-
2309 current_module(Module),
2310 source_file(Module:Head, File).
2311
2312exported_from(Module, Head, File) :-
2313 distinct(Primary,
2314 ( predicate_property(Module:Head, exported),
2315 ( predicate_property(Module:Head, imported_from(Primary))
2316 -> true
2317 ; Primary = Module
2318 ))),
2319 module_property(Primary, file(File)).
2320
2321:- multifile
2322 arithmetic:evaluable/2. 2323
2324system_arithmetic_function(Head) :-
2325 functor(Head, Name, Arity),
2326 FArith is Arity-1,
2327 FArith >= 0,
2328 functor(FHead, Name, FArith),
2329 arithmetic:evaluable(FHead, system).
2330
2359
2360file(File) -->
2361 file(File, []).
2362
2363file(File, Options) -->
2364 { catch(nb_getval(pldoc_options, GenOptions), _, GenOptions = []),
2365 merge_options(Options, GenOptions, FinalOptions)
2366 },
2367 link_file(File, FinalOptions),
2368 !.
2369file(File, Options) -->
2370 { option(edit_handler(Handler), Options),
2371 http_current_request(Request),
2372 memberchk(path(Path), Request),
2373 absolute_file_name(File, Location,
2374 [ relative_to(Path)
2375 ]),
2376 http_link_to_id(Handler, [location(Location)], HREF),
2377 format(atom(Title), 'Click to create ~w', [File])
2378 },
2379 html(a([href(HREF), class(nofile), title(Title)], File)).
2380file(File, _) -->
2381 html(code(class(nofile), File)).
2382
2383link_file(File, Options) -->
2384 { file_href(File, HREF, Options),
2385 option(label(Label), Options, File),
2386 option(class(Class), Options, file)
2387 },
2388 html(a([class(Class), href(HREF)], Label)).
2389
2393
2394file_href(_, HREF, Options) :-
2395 option(href(HREF), Options),
2396 !.
2397file_href(File, HREF, Options) :-
2398 file_href_real(File, HREF0, Options),
2399 map_extension(HREF0, HREF, Options).
2400
2406
2407map_extension(HREF0, HREF, Options) :-
2408 option(map_extension(Map), Options),
2409 file_name_extension(Base, Old, HREF0),
2410 memberchk(Old-New, Map),
2411 !,
2412 file_name_extension(Base, New, HREF).
2413map_extension(HREF, HREF, _).
2414
2415
2416file_href_real(File, HREF, Options) :-
2417 ( option(absolute_path(Path), Options)
2418 ; existing_linked_file(File, Path)
2419 ),
2420 !,
2421 ( option(files(Map), Options),
2422 memberchk(file(Path, LinkFile), Map)
2423 -> true
2424 ; LinkFile = Path
2425 ),
2426 file_href(LinkFile, HREF).
2427file_href_real(File, HREF, _) :-
2428 directory_alias(Alias),
2429 Term =.. [Alias,File],
2430 absolute_file_name(Term, _,
2431 [ access(read),
2432 file_errors(fail)
2433 ]),
2434 !,
2435 http_absolute_location(Term, HREF, []).
2436
2437directory_alias(icons).
2438directory_alias(css).
2439
2440
2447
2448file_href(Path, HREF) :- 2449 source_file(Path),
2450 !,
2451 doc_file_href(Path, HREF).
2452file_href(Path, HREF) :-
2453 ( nb_current(pldoc_output, CFile)
2454 ; nb_current(pldoc_file, CFile)
2455 ),
2456 CFile \== [],
2457 !,
2458 relative_file_name(Path, CFile, HREF).
2459file_href(Path, Path).
2460
2461
2466
2467existing_linked_file(File, Path) :-
2468 catch(b_getval(pldoc_file, CurrentFile), _, fail),
2469 CurrentFile \== [],
2470 absolute_file_name(File, Path,
2471 [ relative_to(CurrentFile),
2472 access(read),
2473 file_errors(fail)
2474 ]).
2475
2476
2483
2484include(PI, predicate, _) -->
2485 !,
2486 ( html_tokens_for_predicates(PI, [])
2487 -> []
2488 ; html(['[[', \predref(PI), ']]'])
2489 ).
2490include(File, image, Options) -->
2491 { file_name_extension(_, svg, File),
2492 file_href(File, HREF, Options),
2493 !,
2494 include(image_attribute, Options, Attrs0),
2495 merge_options(Attrs0,
2496 [ alt(File),
2497 data(HREF),
2498 type('image/svg+xml')
2499 ], Attrs)
2500 },
2501 ( { option(caption(Caption), Options) }
2502 -> html(div(class(figure),
2503 [ div(class(image), object(Attrs, [])),
2504 div(class(caption), Caption)
2505 ]))
2506 ; html(object(Attrs, []))
2507 ).
2508include(File, image, Options) -->
2509 { file_href(File, HREF, Options),
2510 !,
2511 include(image_attribute, Options, Attrs0),
2512 merge_options(Attrs0,
2513 [ alt(File),
2514 border(0),
2515 src(HREF)
2516 ], Attrs)
2517 },
2518 ( { option(caption(Caption), Options) }
2519 -> html(div(class(figure),
2520 [ div(class(image), img(Attrs)),
2521 div(class(caption), Caption)
2522 ]))
2523 ; html(img(Attrs))
2524 ).
2525include(File, wiki, _Options) --> 2526 { access_file(File, read),
2527 !,
2528 read_file_to_codes(File, String, []),
2529 wiki_codes_to_dom(String, [], DOM)
2530 },
2531 html(DOM).
2532include(File, _Type, Options) -->
2533 link_file(File, Options),
2534 !.
2535include(File, _, _) -->
2536 html(code(class(nofile), ['[[',File,']]'])).
2537
2538image_attribute(src(_)).
2539image_attribute(alt(_)).
2540image_attribute(title(_)).
2541image_attribute(align(_)).
2542image_attribute(width(_)).
2543image_attribute(height(_)).
2544image_attribute(border(_)).
2545image_attribute(class(_)).
2546image_attribute(style(_)).
2547
2548
2558
2559html_tokens_for_predicates([], _Options) -->
2560 [].
2561html_tokens_for_predicates([H|T], Options) -->
2562 !,
2563 html_tokens_for_predicates(H, Options),
2564 html_tokens_for_predicates(T, Options).
2565html_tokens_for_predicates(PI, Options) -->
2566 { PI = _:_/_,
2567 !,
2568 ( doc_comment(PI, Pos, _Summary, Comment)
2569 -> true
2570 ; Comment = ''
2571 )
2572 },
2573 object(PI, [Pos-Comment], [dl], _, Options).
2574html_tokens_for_predicates(Spec, Options) -->
2575 { findall(PI, documented_pi(Spec, PI), List),
2576 List \== [], !
2577 },
2578 html_tokens_for_predicates(List, Options).
2579html_tokens_for_predicates(Spec, Options) -->
2580 man_page(Spec,
2581 [ links(false), 2582 navtree(false), 2583 footer(false), 2584 synopsis(false) 2585 | Options
2586 ]).
2587
2588
2589documented_pi(Spec, PI) :-
2590 generalise_spec(Spec, PI),
2591 doc_comment(PI, _Pos, _Summary, _Comment).
2592
2593generalise_spec(Name/Arity, _M:Name/Arity).
2594generalise_spec(Name//Arity, _M:Name//Arity).
2595
2596
2597 2600
2601
2605
2606doc_for_wiki_file(FileSpec, Options) :-
2607 absolute_file_name(FileSpec, File,
2608 [ access(read)
2609 ]),
2610 read_file_to_codes(File, String, []),
2611 b_setval(pldoc_file, File),
2612 call_cleanup(reply_wiki_page(File, String, Options),
2613 nb_delete(pldoc_file)).
2614
2615reply_wiki_page(File, String, Options) :-
2616 wiki_codes_to_dom(String, [], DOM0),
2617 title(DOM0, File, Title),
2618 insert_edit_button(DOM0, File, DOM, Options),
2619 reply_html_page(pldoc(wiki),
2620 title(Title),
2621 [ \html_requires(pldoc)
2622 | DOM
2623 ]).
2624
2625title(DOM, _, Title) :-
2626 sub_term(h1(_,Title), DOM),
2627 !.
2628title(_, File, Title) :-
2629 file_base_name(File, Title).
2630
2631insert_edit_button(DOM, _, DOM, Options) :-
2632 option(edit(false), Options, false),
2633 !.
2634insert_edit_button([h1(Attrs,Title)|DOM], File,
2635 [h1(Attrs,[ span(style('float:right'),
2636 \edit_button(File, [edit(true)]))
2637 | Title
2638 ])|DOM], _) :- !.
2639insert_edit_button(DOM, File,
2640 [ h1(class(wiki),
2641 [ span(style('float:right'),
2642 \edit_button(File, [edit(true)]))
2643 ])
2644 | DOM
2645 ], _).
2646
2647
2648 2651
2655
2656mode_anchor_name(Var, _) :-
2657 var(Var),
2658 !,
2659 instantiation_error(Var).
2660mode_anchor_name(mode(Head, _), Anchor) :-
2661 !,
2662 mode_anchor_name(Head, Anchor).
2663mode_anchor_name(Head is _Det, Anchor) :-
2664 !,
2665 mode_anchor_name(Head, Anchor).
2666mode_anchor_name(Head, Anchor) :-
2667 pred_anchor_name(Head, _, Anchor).
2668
2669
2673
2674pred_anchor_name(//(Head), Name/Arity, Anchor) :-
2675 !,
2676 functor(Head, Name, DCGArity),
2677 Arity is DCGArity+2,
2678 format(atom(Anchor), '~w/~d', [Name, Arity]).
2679pred_anchor_name(Head, Name/Arity, Anchor) :-
2680 functor(Head, Name, Arity),
2681 format(atom(Anchor), '~w/~d', [Name, Arity]).
2682
2683:- multifile prolog:message//1. 2684
2685prolog:message(pldoc(module_comment_outside_module(Title))) -->
2686 [ 'PlDoc comment <module> ~w does not appear in a module'-[Title] ]