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, UREObjs, ReallyUnDoc),
886 sort(2, @=<, UREObjs, REObjs) 887 888 },
889 !,
890 re_exported_doc(REObjs, Options),
891 undocumented(ReallyUnDoc, Options).
892undocumented(_, _, _) -->
893 [].
894
895re_exported_doc([], _) --> !.
896re_exported_doc(Objs, Options) -->
897 reexport_header(Objs, Options),
898 objects(Objs, Options).
899
(_, Options) -->
901 { option(reexport_header(true), Options, true)
902 },
903 !,
904 html([ h2(class(wiki), 'Re-exported predicates'),
905 p([ "The following predicates are exported from this file \c
906 while their implementation is defined in imported modules \c
907 or non-module files loaded by this module."
908 ])
909 ]).
910reexport_header(_, _) -->
911 [].
912
913undocumented([], _) --> !.
914undocumented(UnDoc, Options) -->
915 html([ h2(class(undoc), 'Undocumented predicates'),
916 p(['The following predicates are exported, but not ',
917 'or incorrectly documented.'
918 ]),
919 dl(class(undoc),
920 \undocumented_predicates(UnDoc, Options))
921 ]).
922
923
924undocumented_predicates([], _) -->
925 [].
926undocumented_predicates([H|T], Options) -->
927 undocumented_pred(H, Options),
928 undocumented_predicates(T, Options).
929
930undocumented_pred(Name/Arity, Options) -->
931 { functor(Head, Name, Arity) },
932 html(dt(class=undoc, \pred_mode(Head, [], _, Options))).
933
934select_undocumented([], _, _, []).
935select_undocumented([PI|T0], M, Objs, [PI|T]) :-
936 is_pi(PI),
937 \+ in_doc(M:PI, Objs),
938 !,
939 select_undocumented(T0, M, Objs, T).
940select_undocumented([_|T0], M, Objs, T) :-
941 select_undocumented(T0, M, Objs, T).
942
943in_doc(PI, Objs) :-
944 member(doc(O,_,_), Objs),
945 ( is_list(O)
946 -> member(O2, O),
947 eq_pi(PI, O2)
948 ; eq_pi(PI, O)
949 ).
950
951
955
956eq_pi(PI, PI) :- !.
957eq_pi(M:PI1, M:PI2) :-
958 atom(M),
959 !,
960 eq_pi(PI1, PI2).
961eq_pi(Name/A, Name//DCGA) :-
962 A =:= DCGA+2,
963 !.
964eq_pi(Name//DCGA, Name/A) :-
965 A =:= DCGA+2.
966
970
971is_pi(Var) :-
972 var(Var),
973 !,
974 fail.
975is_pi(_:PI) :-
976 !,
977 is_pi(PI).
978is_pi(_/_).
979is_pi(_//_).
980
981
984
985re_exported_doc([], _, _, [], []).
986re_exported_doc([PI|T0], File, Module, [doc(Orig:PI,Pos,Comment)|ObjT], UnDoc) :-
987 pi_to_head(PI, Head),
988 ( predicate_property(Module:Head, imported_from(Orig))
989 -> true
990 ; predicate_property(Module:Head, exported)
991 -> Orig = Module
992 ; xref_defined(File, Head, imported(File2)),
993 ensure_doc_objects(File2),
994 xref_module(File2, Orig)
995 ),
996 doc_comment(Orig:PI, Pos, _, Comment),
997 !,
998 re_exported_doc(T0, File, Module, ObjT, UnDoc).
999re_exported_doc([PI|T0], File, Module, REObj, [PI|UnDoc]) :-
1000 re_exported_doc(T0, File, Module, REObj, UnDoc).
1001
1002
1003 1006
1014
1015object_page(Obj, Options) -->
1016 prolog:doc_object_page(Obj, Options),
1017 !,
1018 object_page_footer(Obj, Options).
1019object_page(Obj, Options) -->
1020 { doc_comment(Obj, File:_Line, _Summary, _Comment)
1021 },
1022 !,
1023 ( { \+ ( doc_comment(Obj, File2:_, _, _),
1024 File2 \== File )
1025 }
1026 -> html([ \html_requires(pldoc),
1027 \object_page_header(File, Options),
1028 \object_synopsis(Obj, []),
1029 \objects([Obj], Options)
1030 ])
1031 ; html([ \html_requires(pldoc),
1032 \object_page_header(-, Options),
1033 \objects([Obj], [synopsis(true)|Options])
1034 ])
1035 ),
1036 object_page_footer(Obj, Options).
1037object_page(M:Name/Arity, Options) --> 1038 { functor(Head, Name, Arity),
1039 ( predicate_property(M:Head, exported)
1040 -> module_property(M, class(library))
1041 ; \+ predicate_property(M:Head, defined)
1042 )
1043 },
1044 prolog:doc_object_page(Name/Arity, Options),
1045 !,
1046 object_page_footer(Name/Arity, Options).
1047
(File, Options) -->
1049 prolog:doc_page_header(file(File), Options),
1050 !.
1051object_page_header(File, Options) -->
1052 { option(header(true), Options, true) },
1053 !,
1054 html(div(class(navhdr),
1055 [ div(class(jump), \file_link(File)),
1056 div(class(search), \search_form(Options)),
1057 br(clear(right))
1058 ])).
1059object_page_header(_, _) --> [].
1060
1061file_link(-) -->
1062 !,
1063 places_menu(-).
1064file_link(File) -->
1065 { file_directory_name(File, Dir)
1066 },
1067 places_menu(Dir),
1068 html([ div(a(href(location_by_id(pldoc_doc)+File), File))
1069 ]).
1070
1075
(Obj, Options) -->
1077 prolog:doc_object_footer(Obj, Options),
1078 !.
1079object_footer(_, _) --> [].
1080
1081
1086
(Obj, Options) -->
1088 prolog:doc_object_page_footer(Obj, Options),
1089 !.
1090object_page_footer(_, _) --> [].
1091
1092
1103
1104object_synopsis(Name/Arity, _) -->
1105 { functor(Head, Name, Arity),
1106 predicate_property(system:Head, built_in)
1107 },
1108 synopsis([span(class(builtin), 'built-in')]).
1109object_synopsis(Name/Arity, Options) -->
1110 !,
1111 object_synopsis(_:Name/Arity, Options).
1112object_synopsis(M:Name/Arity, Options) -->
1113 { functor(Head, Name, Arity),
1114 ( option(source(Spec), Options)
1115 -> absolute_file_name(Spec, File,
1116 [ access(read),
1117 file_type(prolog),
1118 file_errors(fail)
1119 ])
1120 ; predicate_property(M:Head, exported),
1121 \+ predicate_property(M:Head, imported_from(_)),
1122 module_property(M, file(File)),
1123 file_name_on_path(File, Spec)
1124 ),
1125 !,
1126 unquote_filespec(Spec, Unquoted),
1127 ( predicate_property(Head, autoload(FileBase)),
1128 file_name_extension(FileBase, _Ext, File)
1129 -> Extra = [span(class(autoload), '(can be autoloaded)')]
1130 ; Extra = []
1131 )
1132 },
1133 ( { option(href(HREF), Options) }
1134 -> synopsis([code([':- use_module(',a(href(HREF), '~q'-[Unquoted]),').'])|Extra])
1135 ; synopsis([code(':- use_module(~q).'-[Unquoted])|Extra])
1136 ).
1137object_synopsis(Name//Arity, Options) -->
1138 !,
1139 { DCGArity is Arity+2 },
1140 object_synopsis(Name/DCGArity, Options).
1141object_synopsis(Module:Name//Arity, Options) -->
1142 !,
1143 { DCGArity is Arity+2 },
1144 object_synopsis(Module:Name/DCGArity, Options).
1145object_synopsis(f(_/_), _) -->
1146 synopsis(span(class(function),
1147 [ 'Arithmetic function (see ',
1148 \object_ref(is/2, []),
1149 ')'
1150 ])).
1151object_synopsis(c(Func), _) -->
1152 { sub_atom(Func, 0, _, _, 'PL_')
1153 ; sub_atom(Func, 0, _, _, 'S')
1154 },
1155 !,
1156 synopsis([span(class(cfunc), 'C-language interface function')]).
1157object_synopsis(_, _) --> [].
1158
1159synopsis(Text) -->
1160 html(div(class(synopsis),
1161 [ span(class('synopsis-hdr'), 'Availability:')
1162 | Text
1163 ])).
1164
1169
1170unquote_filespec(Spec, Unquoted) :-
1171 compound(Spec),
1172 Spec =.. [Alias,Path],
1173 atom(Path),
1174 atomic_list_concat(Parts, /, Path),
1175 maplist(need_no_quotes, Parts),
1176 !,
1177 parts_to_path(Parts, UnquotedPath),
1178 Unquoted =.. [Alias, UnquotedPath].
1179unquote_filespec(Spec, Spec).
1180
1181need_no_quotes(Atom) :-
1182 format(atom(A), '~q', [Atom]),
1183 \+ sub_atom(A, 0, _, _, '\'').
1184
1185parts_to_path([One], One) :- !.
1186parts_to_path(List, More/T) :-
1187 ( append(H, [T], List)
1188 -> parts_to_path(H, More)
1189 ).
1190
1191
1192 1195
1199
1200doc_write_html(Out, Title, Doc) :-
1201 doc_page_dom(Title, Doc, DOM),
1202 phrase(html(DOM), Tokens),
1203 print_html_head(Out),
1204 print_html(Out, Tokens).
1205
1210
1211doc_page_dom(Title, Body, DOM) :-
1212 DOM = html([ head([ title(Title),
1213 link([ rel(stylesheet),
1214 type('text/css'),
1215 href(location_by_id(pldoc_resource)+'pldoc.css')
1216 ]),
1217 script([ src(location_by_id(pldoc_resource)+'pldoc.js'),
1218 type('text/javascript')
1219 ], [])
1220 ]),
1221 body(Body)
1222 ]).
1223
1227
1228print_html_head(Out) :-
1229 format(Out,
1230 '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" \c
1231 "http://www.w3.org/TR/html4/strict.dtd">~n', []).
1232
1236
1242
1243tags(Tags) -->
1244 html(dl(class=tags, Tags)).
1245
1249
1250tag(Tag, Values) -->
1251 { doc_tag_title(Tag, Title),
1252 atom_concat('keyword-', Tag, Class)
1253 },
1254 html([ dt(class=Class, Title),
1255 \tag_values(Values, Class)
1256 ]).
1257
1258tag_values([], _) -->
1259 [].
1260tag_values([H|T], Class) -->
1261 html(dd(class=Class, ['- '|H])),
1262 tag_values(T, Class).
1263
1264
1268
1269doc_tag_title(Tag, Title) :-
1270 tag_title(Tag, Title),
1271 !.
1272doc_tag_title(Tag, Tag).
1273
1274tag_title(compat, 'Compatibility').
1275tag_title(tbd, 'To be done').
1276tag_title(see, 'See also').
1277tag_title(error, 'Errors').
1278tag_title(since, 'Since').
1279
1284
1285args(Params) -->
1286 html([ dt(class=tag, 'Arguments:'),
1287 dd(table(class=arglist,
1288 \arg_list(Params)))
1289 ]).
1290
1291arg_list([]) -->
1292 [].
1293arg_list([H|T]) -->
1294 argument(H),
1295 arg_list(T).
1296
1297argument(arg(Name,Descr)) -->
1298 html(tr([td(var(Name)), td(class=argdescr, ['- '|Descr])])).
1299
1300
1301 1304
1309
1310objects_nav_tree(Objects, Tree) :-
1311 maplist(object_nav_tree, Objects, Trees),
1312 union_trees(Trees, Tree0),
1313 remove_unique_root(Tree0, Tree).
1314
1315object_nav_tree(Obj, Tree) :-
1316 Node = node(directory(Dir), FileNodes),
1317 FileNode = node(file(File), Siblings),
1318 doc_comment(Obj, File:_Line, _Summary, _Comment),
1319 !,
1320 file_directory_name(File, Dir),
1321 sibling_file_nodes(Dir, FileNodes0),
1322 selectchk(node(file(File),[]), FileNodes0, FileNode, FileNodes),
1323 findall(Sibling, doc_comment(Sibling, File:_, _, _), Siblings0),
1324 delete(Siblings0, _:module(_), Siblings1),
1325 doc_hide_private(Siblings1, Siblings2, []),
1326 flatten(Siblings2, Siblings), 1327 embed_directories(Node, Tree).
1328
1329sibling_file_nodes(Dir, Nodes) :-
1330 findall(node(file(File), []),
1331 ( source_file(File),
1332 file_directory_name(File, Dir)
1333 ),
1334 Nodes).
1335
1336embed_directories(Node, Tree) :-
1337 Node = node(file(File), _),
1338 !,
1339 file_directory_name(File, Dir),
1340 Super = node(directory(Dir), [Node]),
1341 embed_directories(Super, Tree).
1342embed_directories(Node, Tree) :-
1343 Node = node(directory(Dir), _),
1344 file_directory_name(Dir, SuperDir),
1345 SuperDir \== Dir,
1346 !,
1347 Super = node(directory(SuperDir), [Node]),
1348 embed_directories(Super, Tree).
1349embed_directories(Tree, Tree).
1350
1351
1352union_trees([Tree], Tree) :- !.
1353union_trees([T1,T2|Trees], Tree) :-
1354 merge_trees(T1, T2, M1),
1355 union_trees([M1|Trees], Tree).
1356
1357merge_trees(node(R, Ch1), node(R, Ch2), node(R, Ch)) :-
1358 merge_nodes(Ch1, Ch2, Ch).
1359
1360merge_nodes([], Ch, Ch) :- !.
1361merge_nodes(Ch, [], Ch) :- !.
1362merge_nodes([node(Root, Ch1)|T1], N1, [T1|Nodes]) :-
1363 selectchk(node(Root, Ch2), N1, N2),
1364 !,
1365 merge_trees(node(Root, Ch1), node(Root, Ch2), T1),
1366 merge_nodes(T1, N2, Nodes).
1367merge_nodes([Node|T1], N1, [Node|Nodes]) :-
1368 merge_nodes(T1, N1, Nodes).
1369
1373
1374remove_unique_root(node(_, [node(R1, [R2])]), Tree) :-
1375 !,
1376 remove_unique_root(node(R1, [R2]), Tree).
1377remove_unique_root(Tree, Tree).
1378
1382
1383nav_tree(Tree, Current, Options) -->
1384 html(ul(class(nav),
1385 \object_tree(Tree, Current, Options))).
1386
1390
1391object_tree(node(Id, []), Target, Options) -->
1392 !,
1393 { node_class(Id, Target, Class) },
1394 html(li(class(Class),
1395 \node(Id, Options))).
1396object_tree(node(Id, Children), Target, Options) -->
1397 !,
1398 { node_class(Id, Target, Class) },
1399 html(li(class(Class),
1400 [ \node(Id, Options),
1401 ul(class(nav),
1402 \object_trees(Children, Target, Options))
1403 ])).
1404object_tree(Id, Target, Options) -->
1405 !,
1406 { node_class(Id, Target, Class) },
1407 html(li(class([obj|Class]), \node(Id, Options))).
1408
1409object_trees([], _, _) --> [].
1410object_trees([H|T], Target, Options) -->
1411 object_tree(H, Target, Options),
1412 object_trees(T, Target, Options).
1413
1414node_class(Ids, Current, Class) :-
1415 is_list(Ids),
1416 !,
1417 ( member(Id, Ids), memberchk(Id, Current)
1418 -> Class = [nav,current]
1419 ; Class = [nav]
1420 ).
1421node_class(Id, Current, Class) :-
1422 ( memberchk(Id, Current)
1423 -> Class = [nav,current]
1424 ; Class = [nav]
1425 ).
1426
1427node(file(File), Options) -->
1428 !,
1429 object_ref(file(File), [style(title)|Options]).
1430node(Id, Options) -->
1431 object_ref(Id, Options).
1432
1433
1434 1437
1438section(Type, Title) -->
1439 { string_codes(Title, Codes),
1440 wiki_codes_to_dom(Codes, [], Content0),
1441 strip_leading_par(Content0, Content),
1442 make_section(Type, Content, HTML)
1443 },
1444 html(HTML).
1445
1446make_section(module, Title, h1(class=module, Title)).
1447make_section(section, Title, h1(class=section, Title)).
1448
1449
1450 1453
1459
1460pred_dt(Modes, Class, Options) -->
1461 pred_dt(Modes, Class, [], _Done, Options).
1462
1463pred_dt([], _, Done, Done, _) -->
1464 [].
1465pred_dt([H|T], Class, Done0, Done, Options) -->
1466 { functor(Class, CSSClass, _) },
1467 html(dt(class=CSSClass,
1468 [ \pred_mode(H, Done0, Done1, Options),
1469 \mode_anot(Class)
1470 ])),
1471 pred_dt(T, Class, Done1, Done, Options).
1472
1473mode_anot(privdef) -->
1474 !,
1475 html(span([class(anot), style('float:right')],
1476 '[private]')).
1477mode_anot(multidef(object(Obj))) -->
1478 !,
1479 { object_href(Obj, HREF) },
1480 html(span([class(anot), style('float:right')],
1481 ['[', a(href(HREF), multifile), ']'
1482 ])).
1483mode_anot(multidef(file(File:_))) -->
1484 !,
1485 { file_name_on_path(File, Spec),
1486 unquote_filespec(Spec, Unquoted),
1487 doc_file_href(File, HREF)
1488 },
1489 html(span([class(anot), style('float:right')],
1490 ['[multifile, ', a(href(HREF), '~q'-[Unquoted]), ']'
1491 ])).
1492mode_anot(multidef) -->
1493 !,
1494 html(span([class(anot), style('float:right')],
1495 '[multifile]')).
1496mode_anot(_) -->
1497 [].
1498
1499pred_mode(mode(Head,Vars), Done0, Done, Options) -->
1500 !,
1501 { bind_vars(Head, Vars) },
1502 pred_mode(Head, Done0, Done, Options).
1503pred_mode(Head is Det, Done0, Done, Options) -->
1504 !,
1505 anchored_pred_head(Head, Done0, Done, Options),
1506 pred_det(Det).
1507pred_mode(Head, Done0, Done, Options) -->
1508 anchored_pred_head(Head, Done0, Done, Options).
1509
1510bind_vars(Term, Bindings) :-
1511 bind_vars(Bindings),
1512 anon_vars(Term).
1513
1514bind_vars([]).
1515bind_vars([Name=Var|T]) :-
1516 Var = '$VAR'(Name),
1517 bind_vars(T).
1518
1523
1524anon_vars(Var) :-
1525 var(Var),
1526 !,
1527 Var = '$VAR'('_').
1528anon_vars(Term) :-
1529 compound(Term),
1530 !,
1531 Term =.. [_|Args],
1532 maplist(anon_vars, Args).
1533anon_vars(_).
1534
1535
1536anchored_pred_head(Head, Done0, Done, Options) -->
1537 { pred_anchor_name(Head, PI, Name) },
1538 ( { memberchk(PI, Done0) }
1539 -> { Done = Done0 },
1540 pred_head(Head)
1541 ; html([ span(style('float:right'),
1542 [ \pred_edit_or_source_button(Head, Options),
1543 &(nbsp)
1544 ]),
1545 a(name=Name, \pred_head(Head))
1546 ]),
1547 { Done = [PI|Done0] }
1548 ).
1549
1550
1551pred_edit_or_source_button(Head, Options) -->
1552 { option(edit(true), Options) },
1553 !,
1554 pred_edit_button(Head, Options).
1555pred_edit_or_source_button(Head, Options) -->
1556 { option(source_link(true), Options) },
1557 !,
1558 pred_source_button(Head, Options).
1559pred_edit_or_source_button(_, _) --> [].
1560
1572
1573pred_edit_button(_, Options) -->
1574 { \+ option(edit(true), Options) },
1575 !.
1576pred_edit_button(PI0, Options0) -->
1577 { canonicalise_predref(PI0, PI, Options0, Options) },
1578 pred_edit_button2(PI, Options).
1579
1580pred_edit_button2(Name/Arity, Options) -->
1581 { \+ ( memberchk(file(_), Options), 1582 memberchk(line(_), Options) 1583 ),
1584 functor(Head, Name, Arity),
1585 option(module(M), Options, _),
1586 \+ ( current_module(M),
1587 source_file(M:Head, _File)
1588 )
1589 },
1590 !.
1591pred_edit_button2(Name/Arity, Options) -->
1592 { include(edit_param, Options, Extra),
1593 http_link_to_id(pldoc_edit,
1594 [name(Name),arity(Arity)|Extra],
1595 EditHREF)
1596 },
1597 html(a(onClick('HTTPrequest(\'' + EditHREF + '\')'),
1598 img([ class(action),
1599 alt('Edit predicate'),
1600 title('Edit predicate'),
1601 src(location_by_id(pldoc_resource)+'editpred.png')
1602 ]))).
1603pred_edit_button2(_, _) -->
1604 !,
1605 [].
1606
1607edit_param(module(_)).
1608edit_param(file(_)).
1609edit_param(line(_)).
1610
1611
1615
1616object_edit_button(_, Options) -->
1617 { \+ option(edit(true), Options) },
1618 !.
1619object_edit_button(PI, Options) -->
1620 { is_pi(PI) },
1621 !,
1622 pred_edit_button(PI, Options).
1623object_edit_button(_, _) -->
1624 [].
1625
1626
1630
1631pred_source_button(PI0, Options0) -->
1632 { canonicalise_predref(PI0, PI, Options0, Options),
1633 option(module(M), Options, _),
1634 pred_source_href(PI, M, HREF), !
1635 },
1636 html(a([ href(HREF)
1637 ],
1638 img([ class(action),
1639 alt('Source'),
1640 title('Show source'),
1641 src(location_by_id(pldoc_resource)+'source.png')
1642 ]))).
1643pred_source_button(_, _) -->
1644 [].
1645
1646
1650
1651object_source_button(PI, Options) -->
1652 { is_pi(PI),
1653 option(source_link(true), Options, true)
1654 },
1655 !,
1656 pred_source_button(PI, Options).
1657object_source_button(_, _) -->
1658 [].
1659
1660
1665
1666canonicalise_predref(M:PI0, PI, Options0, [module(M)|Options]) :-
1667 !,
1668 canonicalise_predref(PI0, PI, Options0, Options).
1669canonicalise_predref(//(Head), PI, Options0, Options) :-
1670 !,
1671 functor(Head, Name, Arity),
1672 PredArity is Arity + 2,
1673 canonicalise_predref(Name/PredArity, PI, Options0, Options).
1674canonicalise_predref(Name//Arity, PI, Options0, Options) :-
1675 integer(Arity), Arity >= 0,
1676 !,
1677 PredArity is Arity + 2,
1678 canonicalise_predref(Name/PredArity, PI, Options0, Options).
1679canonicalise_predref(PI, PI, Options, Options) :-
1680 PI = Name/Arity,
1681 atom(Name), integer(Arity), Arity >= 0,
1682 !.
1683canonicalise_predref(Head, PI, Options0, Options) :-
1684 functor(Head, Name, Arity),
1685 canonicalise_predref(Name/Arity, PI, Options0, Options).
1686
1687
1692
1693pred_head(Var) -->
1694 { var(Var),
1695 !,
1696 instantiation_error(Var)
1697 }.
1698pred_head(//(Head)) -->
1699 !,
1700 pred_head(Head),
1701 html(//).
1702pred_head(M:Head) -->
1703 html([span(class=module, M), :]),
1704 pred_head(Head).
1705pred_head(Head) -->
1706 { atom(Head) },
1707 !,
1708 html(b(class=pred, Head)).
1709pred_head(Head) --> 1710 { Head =.. [Functor,Left,Right],
1711 is_op_type(Functor, infix)
1712 },
1713 !,
1714 html([ var(class=arglist, \pred_arg(Left, 1)),
1715 ' ', b(class=pred, Functor), ' ',
1716 var(class=arglist, \pred_arg(Right, 2))
1717 ]).
1718pred_head(Head) --> 1719 { Head =.. [Functor,Arg],
1720 is_op_type(Functor, prefix)
1721 },
1722 !,
1723 html([ b(class=pred, Functor), ' ',
1724 var(class=arglist, \pred_arg(Arg, 1))
1725 ]).
1726pred_head(Head) --> 1727 { Head =.. [Functor,Arg],
1728 is_op_type(Functor, postfix)
1729 },
1730 !,
1731 html([ var(class=arglist, \pred_arg(Arg, 1)),
1732 ' ', b(class=pred, Functor)
1733 ]).
1734pred_head({Head}) -->
1735 !,
1736 html([ b(class=pred, '{'),
1737 var(class=arglist,
1738 \pred_args([Head], 1)),
1739 b(class=pred, '}')
1740 ]).
1741pred_head(Head) --> 1742 { Head =.. [Functor|Args] },
1743 html([ b(class=pred, Functor),
1744 var(class=arglist,
1745 [ '(', \pred_args(Args, 1), ')' ])
1746 ]).
1747
1752
1753is_op_type(Functor, Type) :-
1754 current_op(_Pri, F, Functor),
1755 op_type(F, Type).
1756
1757op_type(fx, prefix).
1758op_type(fy, prefix).
1759op_type(xf, postfix).
1760op_type(yf, postfix).
1761op_type(xfx, infix).
1762op_type(xfy, infix).
1763op_type(yfx, infix).
1764op_type(yfy, infix).
1765
1766
1767pred_args([], _) -->
1768 [].
1769pred_args([H|T], I) -->
1770 pred_arg(H, I),
1771 ( {T==[]}
1772 -> []
1773 ; html(', '),
1774 { I2 is I + 1 },
1775 pred_args(T, I2)
1776 ).
1777
1778pred_arg(Var, I) -->
1779 { var(Var) },
1780 !,
1781 html(['Arg', I]).
1782pred_arg(...(Term), I) -->
1783 !,
1784 pred_arg(Term, I),
1785 html('...').
1786pred_arg(Term, I) -->
1787 { Term =.. [Ind,Arg],
1788 mode_indicator(Ind)
1789 },
1790 !,
1791 html([Ind, \pred_arg(Arg, I)]).
1792pred_arg(Arg:Type, _) -->
1793 !,
1794 html([\argname(Arg), :, \argtype(Type)]).
1795pred_arg(Arg, _) -->
1796 argname(Arg).
1797
1798argname('$VAR'(Name)) -->
1799 !,
1800 html(Name).
1801argname(Name) -->
1802 !,
1803 html(Name).
1804
1805argtype(Term) -->
1806 { format(string(S), '~W',
1807 [ Term,
1808 [ quoted(true),
1809 numbervars(true)
1810 ]
1811 ]) },
1812 html(S).
1813
1814pred_det(unknown) -->
1815 [].
1816pred_det(Det) -->
1817 html([' is ', b(class=det, Det)]).
1818
1819
1825
1826term(_, Atom, []) -->
1827 { atomic(Atom),
1828 !,
1829 format(string(S), '~W', [Atom,[quoted(true)]])
1830 },
1831 html(span(class=functor, S)).
1832term(_, Key:Type, [TypeName=Type]) -->
1833 { atomic(Key)
1834 },
1835 !,
1836 html([span(class='pl-key', Key), :, span(class('pl-var'), TypeName)]).
1837term(_, Term, Bindings) -->
1838 { is_mode(Term is det), 1839 bind_vars(Bindings)
1840 },
1841 !,
1842 pred_head(Term).
1843term(_, Term, Bindings) -->
1844 term(Term,
1845 [ variable_names(Bindings),
1846 quoued(true)
1847 ]).
1848
1849
1850 1853
1864
1865predref(Term) -->
1866 { catch(nb_getval(pldoc_options, Options), _, Options = []) },
1867 predref(Term, Options).
1868
1869predref(Obj, Options) -->
1870 { Obj = _:_,
1871 doc_comment(Obj, File:_Line, _, _),
1872 ( ( option(files(Map), Options)
1873 -> memberchk(file(File,_), Map)
1874 ; true
1875 )
1876 -> object_href(Obj, HREF, Options)
1877 ; manref(Obj, HREF, Options)
1878 )
1879 },
1880 !,
1881 html(a(href(HREF), \object_name(Obj, [qualify(true)|Options]))).
1882predref(M:Term, Options) -->
1883 !,
1884 predref(Term, M, Options).
1885predref(Term, Options) -->
1886 predref(Term, _, Options).
1887
1888predref(Name/Arity, _, Options) --> 1889 { prolog:doc_object_summary(Name/Arity, manual, _, _),
1890 !,
1891 manref(Name/Arity, HREF, Options)
1892 },
1893 html(a([class=builtin, href=HREF], [Name, /, Arity])).
1894predref(Name/Arity, _, Options) --> 1895 { option(prefer(manual), Options),
1896 prolog:doc_object_summary(Name/Arity, Category, _, _),
1897 !,
1898 manref(Name/Arity, HREF, Options)
1899 },
1900 html(a([class=Category, href=HREF], [Name, /, Arity])).
1901predref(Obj, Module, Options) --> 1902 { doc_comment(Module:Obj, File:_Line, _, _),
1903 ( option(files(Map), Options)
1904 -> memberchk(file(File,_), Map)
1905 ; true
1906 )
1907 },
1908 !,
1909 object_ref(Module:Obj, Options).
1910predref(Name/Arity, Module, Options) -->
1911 { \+ option(files(_), Options),
1912 pred_href(Name/Arity, Module, HREF)
1913 },
1914 !,
1915 html(a(href=HREF, [Name, /, Arity])).
1916predref(Name//Arity, Module, Options) -->
1917 { \+ option(files(_), Options),
1918 PredArity is Arity + 2,
1919 pred_href(Name/PredArity, Module, HREF)
1920 },
1921 !,
1922 html(a(href=HREF, [Name, //, Arity])).
1923predref(PI, _, Options) --> 1924 { canonical_pi(PI, CPI, HTML),
1925 ( option(files(_), Options)
1926 -> Category = extmanual
1927 ; prolog:doc_object_summary(CPI, Category, _, _)
1928 ),
1929 manref(CPI, HREF, Options)
1930 },
1931 html(a([class=Category, href=HREF], HTML)).
1932predref(PI, _, _Options) -->
1933 { canonical_pi(PI, _CPI, HTML)
1934 },
1935 !,
1936 html(span(class=undef, HTML)).
1937predref(Callable, Module, Options) -->
1938 { callable(Callable),
1939 functor(Callable, Name, Arity)
1940 },
1941 predref(Name/Arity, Module, Options).
1942
1943canonical_pi(Name/Arity, Name/Arity, [Name, /, Arity]) :-
1944 atom(Name), integer(Arity),
1945 !.
1946canonical_pi(Name//Arity, Name/Arity2, [Name, //, Arity]) :-
1947 atom(Name), integer(Arity),
1948 !,
1949 Arity2 is Arity+2.
1950
1954
1955nopredref(PI) -->
1956 { canonical_pi(PI, _CPI, HTML)
1957 },
1958 !,
1959 html(span(class=nopredref, HTML)).
1960
1966
1967flagref(Flag) -->
1968 html(code(Flag)).
1969
1974
1975cite(Citations) -->
1976 html('['), citations(Citations), html(']').
1977
1978citations([]) --> [].
1979citations([H|T]) -->
1980 citation(H),
1981 ( {T==[]}
1982 -> []
1983 ; [';'],
1984 citations(T)
1985 ).
1986
1987citation(H) -->
1988 html([@,H]).
1989
1990
1995
1996manref(PI, HREF, Options) :-
1997 predname(PI, PredName),
1998 ( option(files(_Map), Options)
1999 -> option(man_server(Server), Options,
2000 'http://www.swi-prolog.org/pldoc'),
2001 uri_components(Server, Comp0),
2002 uri_data(path, Comp0, Path0),
2003 directory_file_path(Path0, man, Path),
2004 uri_data(path, Comp0, Path, Components),
2005 uri_query_components(Query, [predicate=PredName]),
2006 uri_data(search, Components, Query),
2007 uri_components(HREF, Components)
2008 ; http_link_to_id(pldoc_man, [predicate=PredName], HREF)
2009 ).
2010
2011predname(Name/Arity, PredName) :-
2012 !,
2013 format(atom(PredName), '~w/~d', [Name, Arity]).
2014predname(Module:Name/Arity, PredName) :-
2015 !,
2016 format(atom(PredName), '~w:~w/~d', [Module, Name, Arity]).
2017
2018
2029
2030pred_href(Name/Arity, Module, HREF) :-
2031 format(string(FragmentId), '~w/~d', [Name, Arity]),
2032 uri_data(fragment, Components, FragmentId),
2033 functor(Head, Name, Arity),
2034 ( catch(relative_file(Module:Head, File), _, fail)
2035 -> uri_data(path, Components, File),
2036 uri_components(HREF, Components)
2037 ; in_file(Module:Head, File)
2038 -> ( current_prolog_flag(home, SWI),
2039 sub_atom(File, 0, _, _, SWI),
2040 prolog:doc_object_summary(Name/Arity, packages, _, _)
2041 -> http_link_to_id(pldoc_man, [predicate=FragmentId], HREF)
2042 ; http_location_by_id(pldoc_doc, DocHandler),
2043 atom_concat(DocHandler, File, Path),
2044 uri_data(path, Components, Path),
2045 uri_components(HREF, Components)
2046 )
2047 ).
2048
2049relative_file(Head, '') :-
2050 b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
2051 in_file(Head, CurrentFile),
2052 !.
2053relative_file(Head, RelFile) :-
2054 b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
2055 in_file(Head, DefFile),
2056 relative_file_name(DefFile, CurrentFile, RelFile).
2057
2061
2062pred_source_href(Name/Arity, Module, HREF) :-
2063 format(string(FragmentId), '~w/~d', [Name, Arity]),
2064 uri_data(fragment, Components, FragmentId),
2065 uri_query_components(Query, [show=src]),
2066 uri_data(search, Components, Query),
2067 functor(Head, Name, Arity),
2068 ( catch(relative_file(Module:Head, File), _, fail)
2069 -> uri_data(path, Components, File),
2070 uri_components(HREF, Components)
2071 ; in_file(Module:Head, File0)
2072 -> insert_alias(File0, File),
2073 http_location_by_id(pldoc_doc, DocHandler),
2074 atom_concat(DocHandler, File, Path),
2075 uri_data(path, Components, Path),
2076 uri_components(HREF, Components)
2077 ).
2078
2079
2085
2086object_ref([], _) -->
2087 !,
2088 [].
2089object_ref([H|T], Options) -->
2090 !,
2091 object_ref(H, Options),
2092 ( {T == []}
2093 -> html(', '),
2094 object_ref(T, Options)
2095 ; []
2096 ).
2097object_ref(Obj, Options) -->
2098 { object_href(Obj, HREF, Options)
2099 },
2100 html(a(href(HREF), \object_name(Obj, Options))).
2101
2106
2107object_href(Obj, HREF) :-
2108 object_href(Obj, HREF, []).
2109
2110object_href(M:PI0, HREF, Options) :-
2111 option(files(Map), Options),
2112 ( module_property(M, file(File))
2113 -> true
2114 ; xref_module(File, M)
2115 ),
2116 memberchk(file(File, DocFile), Map),
2117 !,
2118 file_base_name(DocFile, LocalFile), 2119 expand_pi(PI0, PI),
2120 term_to_string(PI, PIS),
2121 uri_data(path, Components, LocalFile),
2122 uri_data(fragment, Components, PIS),
2123 uri_components(HREF, Components).
2124object_href(file(File), HREF, _Options) :-
2125 doc_file_href(File, HREF),
2126 !.
2127object_href(directory(Dir), HREF, _Options) :-
2128 directory_file_path(Dir, 'index.html', Index),
2129 doc_file_href(Index, HREF),
2130 !.
2131object_href(Obj, HREF, _Options) :-
2132 prolog:doc_object_href(Obj, HREF),
2133 !.
2134object_href(Obj0, HREF, _Options) :-
2135 localise_object(Obj0, Obj),
2136 term_to_string(Obj, String),
2137 http_link_to_id(pldoc_object, [object=String], HREF).
2138
2139expand_pi(Name//Arity0, Name/Arity) :-
2140 !,
2141 Arity is Arity0+2.
2142expand_pi(PI, PI).
2143
2144
2149
2150localise_object(Obj0, Obj) :-
2151 prolog:doc_canonical_object(Obj0, Obj),
2152 !.
2153localise_object(Obj, Obj).
2154
2155
2160
2161term_to_string(Term, String) :-
2162 State = state(-),
2163 ( numbervars(Term, 0, _, [singletons(true)]),
2164 with_output_to(string(String),
2165 write_term(Term,
2166 [ numbervars(true),
2167 quoted(true)
2168 ])),
2169 nb_setarg(1, State, String),
2170 fail
2171 ; arg(1, State, String)
2172 ).
2173
2185
2186object_name(Obj, Options) -->
2187 { option(style(Style), Options, inline)
2188 },
2189 object_name(Style, Obj, Options).
2190
2191object_name(title, Obj, Options) -->
2192 { merge_options(Options, [secref_style(title)], Options1) },
2193 prolog:doc_object_link(Obj, Options1),
2194 !.
2195object_name(inline, Obj, Options) -->
2196 prolog:doc_object_link(Obj, Options),
2197 !.
2198object_name(title, f(Name/Arity), _Options) -->
2199 !,
2200 html(['Function ', Name, /, Arity]).
2201object_name(inline, f(Name/Arity), _Options) -->
2202 !,
2203 html([Name, /, Arity]).
2204object_name(Style, PI, Options) -->
2205 { is_pi(PI) },
2206 !,
2207 pi(Style, PI, Options).
2208object_name(inline, Module:module(_Title), _) -->
2209 !,
2210 { module_property(Module, file(File)),
2211 file_base_name(File, Base)
2212 },
2213 !,
2214 html(Base).
2215object_name(title, Module:module(Title), _) -->
2216 { module_property(Module, file(File)),
2217 file_base_name(File, Base)
2218 },
2219 !,
2220 html([Base, ' -- ', Title]).
2221object_name(title, file(File), _) -->
2222 { module_property(Module, file(File)),
2223 doc_comment(Module:module(Title), _, _, _),
2224 !,
2225 file_base_name(File, Base)
2226 },
2227 html([Base, ' -- ', Title]).
2228object_name(_, file(File), _) -->
2229 { file_base_name(File, Base) },
2230 html(Base).
2231object_name(_, directory(Dir), _) -->
2232 { file_base_name(Dir, Base) },
2233 html(Base).
2234object_name(_, module(Title), _Options) -->
2235 { print_message(warning,
2236 pldoc(module_comment_outside_module(Title)))
2237 }.
2238
2239pi(title, PI, Options) -->
2240 pi_type(PI),
2241 pi(PI, Options).
2242pi(inline, PI, Options) -->
2243 pi(PI, Options).
2244
2245pi(M:PI, Options) -->
2246 !,
2247 ( { option(qualify(true), Options) }
2248 -> html([span(class(module), M), :])
2249 ; []
2250 ),
2251 pi(PI, Options).
2252pi(Name/Arity, _) -->
2253 !,
2254 html([Name, /, \arity(Arity)]).
2255pi(Name//Arity, _) -->
2256 html([Name, //, \arity(Arity)]).
2257
2258arity(Arity) -->
2259 { var(Arity) },
2260 !,
2261 html('_').
2262arity(Arity) -->
2263 html(Arity).
2264
2265pi_type(_:PI) -->
2266 !,
2267 pi_type(PI).
2268pi_type(_/_) -->
2269 html(['Predicate ']).
2270pi_type(_//_) -->
2271 html(['Grammar rule ']).
2272
2273
2274
2282
2283in_file(Module:Head, File) :-
2284 !,
2285 distinct(File, in_file(Module, Head, File)).
2286in_file(Head, File) :-
2287 distinct(File, in_file(_, Head, File)).
2288
2289in_file(Module, Head, File) :-
2290 var(Module),
2291 ( predicate_property(system:Head, foreign)
2292 -> !,
2293 fail
2294 ; predicate_property(system:Head, file(File)),
2295 \+ system_arithmetic_function(Head)
2296 -> !
2297 ; predicate_property(Head, autoload(File0))
2298 -> !,
2299 file_name_extension(File0, pl, File)
2300 ; exported_from(Module, Head, File),
2301 module_property(Module, class(library))
2302 ).
2303in_file(Module, Head, File) :-
2304 nonvar(Module),
2305 predicate_property(Module:Head, file(File)),
2306 \+ predicate_property(Module:Head, imported_from(_)).
2307in_file(Module, Head, File) :-
2308 xref_defined(File, Head, How),
2309 xref_current_source(File),
2310 atom(File), 2311 xref_module(File, Module),
2312 How \= imported(_From).
2313in_file(Module, Head, File) :-
2314 exported_from(Module, Head, File).
2315in_file(Module, Head, File) :-
2316 predicate_property(Module:Head, file(File)),
2317 \+ predicate_property(Module:Head, imported_from(_)).
2318in_file(Module, Head, File) :-
2319 current_module(Module),
2320 source_file(Module:Head, File).
2321
2322exported_from(Module, Head, File) :-
2323 distinct(Primary,
2324 ( predicate_property(Module:Head, exported),
2325 ( predicate_property(Module:Head, imported_from(Primary))
2326 -> true
2327 ; Primary = Module
2328 ))),
2329 module_property(Primary, file(File)).
2330
2331:- multifile
2332 arithmetic:evaluable/2. 2333
2334system_arithmetic_function(Head) :-
2335 functor(Head, Name, Arity),
2336 FArith is Arity-1,
2337 FArith >= 0,
2338 functor(FHead, Name, FArith),
2339 arithmetic:evaluable(FHead, system).
2340
2369
2370file(File) -->
2371 file(File, []).
2372
2373file(File, Options) -->
2374 { catch(nb_getval(pldoc_options, GenOptions), _, GenOptions = []),
2375 merge_options(Options, GenOptions, FinalOptions)
2376 },
2377 link_file(File, FinalOptions),
2378 !.
2379file(File, Options) -->
2380 { option(edit_handler(Handler), Options),
2381 http_current_request(Request),
2382 memberchk(path(Path), Request),
2383 absolute_file_name(File, Location,
2384 [ relative_to(Path)
2385 ]),
2386 http_link_to_id(Handler, [location(Location)], HREF),
2387 format(atom(Title), 'Click to create ~w', [File])
2388 },
2389 html(a([href(HREF), class(nofile), title(Title)], File)).
2390file(File, _) -->
2391 html(code(class(nofile), File)).
2392
2393link_file(File, Options) -->
2394 { file_href(File, HREF, Options),
2395 option(label(Label), Options, File),
2396 option(class(Class), Options, file)
2397 },
2398 html(a([class(Class), href(HREF)], Label)).
2399
2403
2404file_href(_, HREF, Options) :-
2405 option(href(HREF), Options),
2406 !.
2407file_href(File, HREF, Options) :-
2408 file_href_real(File, HREF0, Options),
2409 map_extension(HREF0, HREF, Options).
2410
2416
2417map_extension(HREF0, HREF, Options) :-
2418 option(map_extension(Map), Options),
2419 file_name_extension(Base, Old, HREF0),
2420 memberchk(Old-New, Map),
2421 !,
2422 file_name_extension(Base, New, HREF).
2423map_extension(HREF, HREF, _).
2424
2425
2426file_href_real(File, HREF, Options) :-
2427 ( option(absolute_path(Path), Options)
2428 ; existing_linked_file(File, Path)
2429 ),
2430 !,
2431 ( option(files(Map), Options),
2432 memberchk(file(Path, LinkFile), Map)
2433 -> true
2434 ; LinkFile = Path
2435 ),
2436 file_href(LinkFile, HREF).
2437file_href_real(File, HREF, _) :-
2438 directory_alias(Alias),
2439 Term =.. [Alias,File],
2440 absolute_file_name(Term, _,
2441 [ access(read),
2442 file_errors(fail)
2443 ]),
2444 !,
2445 http_absolute_location(Term, HREF, []).
2446
2447directory_alias(icons).
2448directory_alias(css).
2449
2450
2457
2458file_href(Path, HREF) :- 2459 source_file(Path),
2460 !,
2461 doc_file_href(Path, HREF).
2462file_href(Path, HREF) :-
2463 ( nb_current(pldoc_output, CFile)
2464 ; nb_current(pldoc_file, CFile)
2465 ),
2466 CFile \== [],
2467 !,
2468 relative_file_name(Path, CFile, HREF).
2469file_href(Path, Path).
2470
2471
2476
2477existing_linked_file(File, Path) :-
2478 catch(b_getval(pldoc_file, CurrentFile), _, fail),
2479 CurrentFile \== [],
2480 absolute_file_name(File, Path,
2481 [ relative_to(CurrentFile),
2482 access(read),
2483 file_errors(fail)
2484 ]).
2485
2486
2493
2494include(PI, predicate, _) -->
2495 !,
2496 ( html_tokens_for_predicates(PI, [])
2497 -> []
2498 ; html(['[[', \predref(PI), ']]'])
2499 ).
2500include(File, image, Options) -->
2501 { file_name_extension(_, svg, File),
2502 file_href(File, HREF, Options),
2503 !,
2504 include(image_attribute, Options, Attrs0),
2505 merge_options(Attrs0,
2506 [ alt(File),
2507 data(HREF),
2508 type('image/svg+xml')
2509 ], Attrs)
2510 },
2511 ( { option(caption(Caption), Options) }
2512 -> html(div(class(figure),
2513 [ div(class(image), object(Attrs, [])),
2514 div(class(caption), Caption)
2515 ]))
2516 ; html(object(Attrs, []))
2517 ).
2518include(File, image, Options) -->
2519 { file_href(File, HREF, Options),
2520 !,
2521 include(image_attribute, Options, Attrs0),
2522 merge_options(Attrs0,
2523 [ alt(File),
2524 border(0),
2525 src(HREF)
2526 ], Attrs)
2527 },
2528 ( { option(caption(Caption), Options) }
2529 -> html(div(class(figure),
2530 [ div(class(image), img(Attrs)),
2531 div(class(caption), Caption)
2532 ]))
2533 ; html(img(Attrs))
2534 ).
2535include(File, wiki, _Options) --> 2536 { access_file(File, read),
2537 !,
2538 read_file_to_codes(File, String, []),
2539 wiki_codes_to_dom(String, [], DOM)
2540 },
2541 html(DOM).
2542include(File, _Type, Options) -->
2543 link_file(File, Options),
2544 !.
2545include(File, _, _) -->
2546 html(code(class(nofile), ['[[',File,']]'])).
2547
2548image_attribute(src(_)).
2549image_attribute(alt(_)).
2550image_attribute(title(_)).
2551image_attribute(align(_)).
2552image_attribute(width(_)).
2553image_attribute(height(_)).
2554image_attribute(border(_)).
2555image_attribute(class(_)).
2556image_attribute(style(_)).
2557
2558
2568
2569html_tokens_for_predicates([], _Options) -->
2570 [].
2571html_tokens_for_predicates([H|T], Options) -->
2572 !,
2573 html_tokens_for_predicates(H, Options),
2574 html_tokens_for_predicates(T, Options).
2575html_tokens_for_predicates(PI, Options) -->
2576 { PI = _:_/_,
2577 !,
2578 ( doc_comment(PI, Pos, _Summary, Comment)
2579 -> true
2580 ; Comment = ''
2581 )
2582 },
2583 object(PI, [Pos-Comment], [dl], _, Options).
2584html_tokens_for_predicates(Spec, Options) -->
2585 { findall(PI, documented_pi(Spec, PI), List),
2586 List \== [], !
2587 },
2588 html_tokens_for_predicates(List, Options).
2589html_tokens_for_predicates(Spec, Options) -->
2590 man_page(Spec,
2591 [ links(false), 2592 navtree(false), 2593 footer(false), 2594 synopsis(false) 2595 | Options
2596 ]).
2597
2598
2599documented_pi(Spec, PI) :-
2600 generalise_spec(Spec, PI),
2601 doc_comment(PI, _Pos, _Summary, _Comment).
2602
2603generalise_spec(Name/Arity, _M:Name/Arity).
2604generalise_spec(Name//Arity, _M:Name//Arity).
2605
2606
2607 2610
2611
2615
2616doc_for_wiki_file(FileSpec, Options) :-
2617 absolute_file_name(FileSpec, File,
2618 [ access(read)
2619 ]),
2620 read_file_to_codes(File, String, []),
2621 b_setval(pldoc_file, File),
2622 call_cleanup(reply_wiki_page(File, String, Options),
2623 nb_delete(pldoc_file)).
2624
2625reply_wiki_page(File, String, Options) :-
2626 wiki_codes_to_dom(String, [], DOM0),
2627 title(DOM0, File, Title),
2628 insert_edit_button(DOM0, File, DOM, Options),
2629 reply_html_page(pldoc(wiki),
2630 title(Title),
2631 [ \html_requires(pldoc)
2632 | DOM
2633 ]).
2634
2635title(DOM, _, Title) :-
2636 sub_term(h1(_,Title), DOM),
2637 !.
2638title(_, File, Title) :-
2639 file_base_name(File, Title).
2640
2641insert_edit_button(DOM, _, DOM, Options) :-
2642 option(edit(false), Options, false),
2643 !.
2644insert_edit_button([h1(Attrs,Title)|DOM], File,
2645 [h1(Attrs,[ span(style('float:right'),
2646 \edit_button(File, [edit(true)]))
2647 | Title
2648 ])|DOM], _) :- !.
2649insert_edit_button(DOM, File,
2650 [ h1(class(wiki),
2651 [ span(style('float:right'),
2652 \edit_button(File, [edit(true)]))
2653 ])
2654 | DOM
2655 ], _).
2656
2657
2658 2661
2665
2666mode_anchor_name(Var, _) :-
2667 var(Var),
2668 !,
2669 instantiation_error(Var).
2670mode_anchor_name(mode(Head, _), Anchor) :-
2671 !,
2672 mode_anchor_name(Head, Anchor).
2673mode_anchor_name(Head is _Det, Anchor) :-
2674 !,
2675 mode_anchor_name(Head, Anchor).
2676mode_anchor_name(Head, Anchor) :-
2677 pred_anchor_name(Head, _, Anchor).
2678
2679
2683
2684pred_anchor_name(//(Head), Name/Arity, Anchor) :-
2685 !,
2686 functor(Head, Name, DCGArity),
2687 Arity is DCGArity+2,
2688 format(atom(Anchor), '~w/~d', [Name, Arity]).
2689pred_anchor_name(Head, Name/Arity, Anchor) :-
2690 functor(Head, Name, Arity),
2691 format(atom(Anchor), '~w/~d', [Name, Arity]).
2692
2693:- multifile prolog:message//1. 2694
2695prolog:message(pldoc(module_comment_outside_module(Title))) -->
2696 [ 'PlDoc comment <module> ~w does not appear in a module'-[Title] ]