35
36:- module(pldoc_html,
37 [ doc_for_file/2, 38 doc_write_html/3, 39 doc_for_wiki_file/2, 40 41 doc_page_dom/3, 42 print_html_head/1, 43 predref//1, 44 predref//2, 45 module_info/3, 46 doc_hide_private/3, 47 edit_button//2, 48 source_button//2, 49 zoom_button//2, 50 pred_edit_button//2, 51 object_edit_button//2, 52 object_source_button//2, 53 doc_resources//1, 54 ensure_doc_objects/1, 55 56 doc_file_objects/5, 57 existing_linked_file/2, 58 unquote_filespec/2, 59 doc_tag_title/2, 60 mode_anchor_name/2, 61 pred_anchor_name/3, 62 private/2, 63 (multifile)/2, 64 is_pi/1, 65 is_op_type/2, 66 67 file//1, 68 file//2, 69 include//3, 70 tags//1, 71 term//3, 72 file_header//2, 73 objects//2, 74 object_ref//2, 75 object_name//2, 76 object_href/2, 77 object_tree//3, 78 object_page//2, 79 object_page_header//2, 80 object_synopsis//2, 81 object_page_footer//2 82 ]).
83:- use_module(library(lists)).
84:- use_module(library(option)).
85:- use_module(library(uri)).
86:- use_module(library(readutil)).
87:- use_module(library(http/html_write)).
88:- use_module(library(http/http_dispatch)).
89:- use_module(library(http/http_wrapper)).
90:- use_module(library(http/http_path)).
91:- use_module(library(http/html_head)).
92:- use_module(library(http/jquery)).
93:- use_module(library(debug)).
94:- use_module(library(apply)).
95:- use_module(library(pairs)).
96:- use_module(library(filesex)).
97:- use_module(doc_process).
98:- use_module(doc_man).
99:- use_module(doc_modes).
100:- use_module(doc_wiki).
101:- use_module(doc_search).
102:- use_module(doc_index).
103:- include(hooks).
104
113
114:- public
115 args//1, 116 pred_dt//3, 117 section//2,
118 tag//2.
119
120
121:- predicate_options(doc_for_wiki_file/2, 2,
122 [ edit(boolean)
123 ]).
124:- predicate_options(doc_hide_private/3, 3,
125 [module(atom), public(list), public_only(boolean)]).
126:- predicate_options(edit_button//2, 2,
127 [ edit(boolean)
128 ]).
129:- predicate_options(file//2, 2,
130 [ label(any),
131 absolute_path(atom),
132 href(atom),
133 map_extension(list),
134 files(list),
135 edit_handler(atom)
136 ]).
137:- predicate_options(file_header//2, 2,
138 [ edit(boolean),
139 files(list),
140 public_only(boolean)
141 ]).
142:- predicate_options(include//3, 3,
143 [ absolute_path(atom),
144 class(atom),
145 files(list),
146 href(atom),
147 label(any),
148 map_extension(list)
149 ]).
150:- predicate_options(object_edit_button//2, 2,
151 [ edit(boolean),
152 pass_to(pred_edit_button//2, 2)
153 ]).
154:- predicate_options(object_page//2, 2,
155 [ for(any),
156 header(boolean),
157 links(boolean),
158 no_manual(boolean),
159 try_manual(boolean),
160 search_in(oneof([all,app,man])),
161 search_match(oneof([name,summary])),
162 search_options(boolean)
163 ]).
164:- predicate_options(object_ref//2, 2,
165 [ files(list),
166 qualify(boolean),
167 style(oneof([number,title,number_title])),
168 secref_style(oneof([number,title,number_title]))
169 ]).
170:- predicate_options(object_synopsis//2, 2,
171 [ href(atom)
172 ]).
173:- predicate_options(pred_dt//3, 3,
174 [ edit(boolean)
175 ]).
176:- predicate_options(pred_edit_button//2, 2,
177 [ edit(boolean)
178 ]).
179:- predicate_options(predref//2, 2,
180 [ files(list),
181 prefer(oneof([manual,app])),
182 pass_to(object_ref/4, 2)
183 ]).
184:- predicate_options(private/2, 2,
185 [ module(atom),
186 public(list)
187 ]).
188:- predicate_options(source_button//2, 2,
189 [ files(list)
190 ]).
191
192
193 196
197:- html_resource(pldoc_css,
198 [ virtual(true),
199 requires([ pldoc_resource('pldoc.css')
200 ])
201 ]).
202:- html_resource(pldoc_resource('pldoc.js'),
203 [ requires([ jquery
204 ])
205 ]).
206:- html_resource(pldoc_js,
207 [ virtual(true),
208 requires([ pldoc_resource('pldoc.js')
209 ])
210 ]).
211:- html_resource(pldoc,
212 [ virtual(true),
213 requires([ pldoc_css,
214 pldoc_js
215 ])
216 ]).
217
218
219 222
241
242doc_for_file(FileSpec, Options) :-
243 doc_file_objects(FileSpec, File, Objects, FileOptions, Options),
244 doc_file_title(File, Title, FileOptions, Options),
245 doc_write_page(
246 pldoc(file(File, Title)),
247 title(Title),
248 \prolog_file(File, Objects, FileOptions, Options),
249 Options).
250
251doc_file_title(_, Title, _, Options) :-
252 option(title(Title), Options),
253 !.
254doc_file_title(File, Title, FileOptions, _) :-
255 memberchk(file(Title0, _Comment), FileOptions),
256 !,
257 file_base_name(File, Base),
258 atomic_list_concat([Base, ' -- ', Title0], Title).
259doc_file_title(File, Title, _, _) :-
260 file_base_name(File, Title).
261
262:- html_meta doc_write_page(+, html, html, +).
263
264doc_write_page(Style, Head, Body, Options) :-
265 option(files(_), Options),
266 !,
267 phrase(page(Style, Head, Body), HTML),
268 print_html(HTML).
269doc_write_page(Style, Head, Body, _) :-
270 reply_html_page(Style, Head, Body).
271
272
273prolog_file(File, Objects, FileOptions, Options) -->
274 { b_setval(pldoc_file, File), % TBD: delete?
275 file_directory_name(File, Dir)
276 },
277 html([ \doc_resources(Options),
278 \doc_links(Dir, FileOptions),
279 \file_header(File, FileOptions)
280 | \objects(Objects, FileOptions)
281 ]),
282 undocumented(File, Objects, FileOptions).
283
288
289doc_resources(Options) -->
290 { option(resource_directory(ResDir), Options),
291 nb_current(pldoc_output, OutputFile),
292 !,
293 directory_file_path(ResDir, 'pldoc.css', Res),
294 relative_file_name(Res, OutputFile, Ref)
295 },
296 html_requires(Ref).
297doc_resources(Options) -->
298 { option(html_resources(Resoures), Options, pldoc)
299 },
300 html_requires(Resoures).
301
302
328
329doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
330 xref_current_source(FileSpec),
331 xref_option(FileSpec, comments(collect)),
332 !,
333 File = FileSpec,
334 findall(Object, xref_doc_object(File, Object), Objects0),
335 reply_file_objects(File, Objects0, Objects, FileOptions, Options).
336doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
337 absolute_file_name(FileSpec, File,
338 [ file_type(prolog),
339 access(read)
340 ]),
341 source_file(File),
342 !,
343 ensure_doc_objects(File),
344 Pos = File:Line,
345 findall(Line-doc(Obj,Pos,Comment),
346 doc_comment(Obj, Pos, _, Comment), Pairs),
347 sort(Pairs, Pairs1), 348 keysort(Pairs1, ByLine),
349 pairs_values(ByLine, Objs0),
350 reply_file_objects(File, Objs0, Objects, FileOptions, Options).
351doc_file_objects(FileSpec, File, Objects, FileOptions, Options) :-
352 absolute_file_name(FileSpec, File,
353 [ file_type(prolog),
354 access(read)
355 ]),
356 xref_source(File, [silent(true)]),
357 findall(Object, xref_doc_object(File, Object), Objects0),
358 reply_file_objects(File, Objects0, Objects, FileOptions, Options).
359
360
361reply_file_objects(File, Objs0, Objects, FileOptions, Options) :-
362 module_info(File, ModuleOptions, Options),
363 file_info(Objs0, Objs1, FileOptions, ModuleOptions),
364 doc_hide_private(Objs1, ObjectsSelf, ModuleOptions),
365 include_reexported(ObjectsSelf, Objects, File, FileOptions).
366
367include_reexported(SelfObjects, Objects, File, Options) :-
368 option(include_reexported(true), Options),
369 option(module(Module), Options),
370 option(public(Exports), Options),
371 select_undocumented(Exports, Module, SelfObjects, Undoc),
372 re_exported_doc(Undoc, File, Module, REObjs, _),
373 REObjs \== [],
374 !,
375 append(SelfObjects, REObjs, Objects).
376include_reexported(Objects, Objects, _, _).
377
378
380
381xref_doc_object(File, doc(M:module(Title),File:0,Comment)) :-
382 xref_comment(File, Title, Comment),
383 xref_module(File, M).
384xref_doc_object(File, doc(M:Name/Arity,File:0,Comment)) :-
385 xref_comment(File, Head, _Summary, Comment),
386 xref_module(File, Module),
387 strip_module(Module:Head, M, Plain),
388 functor(Plain, Name, Arity).
389
398
399:- dynamic
400 no_comments/2.
401
402ensure_doc_objects(File) :-
403 source_file(File),
404 !,
405 ( doc_file_has_comments(File)
406 -> true
407 ; no_comments(File, TimeChecked),
408 time_file(File, TimeChecked)
409 -> true
410 ; xref_source(File, [silent(true), comments(store)]),
411 retractall(no_comments(File, _)),
412 ( doc_file_has_comments(File)
413 -> true
414 ; time_file(File, TimeChecked),
415 assertz(no_comments(File, TimeChecked))
416 )
417 ).
418ensure_doc_objects(File) :-
419 xref_source(File, [silent(true)]).
420
425
426module_info(File, [module(Module), public(Exports)|Options], Options) :-
427 module_property(Module, file(File)),
428 !,
429 module_property(Module, exports(Exports)).
430module_info(File, [module(Module), public(Exports)|Options], Options) :-
431 xref_module(File, Module),
432 !,
433 findall(PI, xref_exported_pi(File, PI), Exports).
434module_info(_, Options, Options).
435
436xref_exported_pi(Src, Name/Arity) :-
437 xref_exported(Src, Head),
438 functor(Head, Name, Arity).
439
443
444doc_hide_private(Objs, Objs, Options) :-
445 option(public_only(false), Options, true),
446 !.
447doc_hide_private(Objs0, Objs, Options) :-
448 hide_private(Objs0, Objs, Options).
449
450hide_private([], [], _).
451hide_private([H|T0], T, Options) :-
452 obj(H, Obj),
453 private(Obj, Options),
454 !,
455 hide_private(T0, T, Options).
456hide_private([H|T0], [H|T], Options) :-
457 hide_private(T0, T, Options).
458
464
465obj(doc(Obj0, _Pos, _Summary), Obj) :-
466 !,
467 ( Obj0 = [Obj|_]
468 -> true
469 ; Obj = Obj0
470 ).
471obj(Obj0, Obj) :-
472 ( Obj0 = [Obj|_]
473 -> true
474 ; Obj = Obj0
475 ).
476
477
483
484:- multifile
485 prolog:doc_is_public_object/1.
486
487private(Object, _Options):-
488 prolog:doc_is_public_object(Object), !, fail.
489private(Module:PI, Options) :-
490 multifile(Module:PI, Options), !, fail.
491private(Module:PI, Options) :-
492 option(module(Module), Options),
493 option(public(Public), Options),
494 !,
495 \+ ( member(PI2, Public),
496 eq_pi(PI, PI2)
497 ).
498private(Module:PI, _Options) :-
499 module_property(Module, file(_)), 500 !,
501 export_list(Module, Exports),
502 \+ ( member(PI2, Exports),
503 eq_pi(PI, PI2)
504 ).
505private(Module:PI, _Options) :-
506 \+ (pi_to_head(PI, Head),
507 xref_exported(Source, Head),
508 xref_module(Source, Module)).
509
514
518
519multifile(Obj, _Options) :-
520 strip_module(user:Obj, Module, PI),
521 pi_to_head(PI, Head),
522 ( predicate_property(Module:Head, multifile)
523 ; xref_module(Source, Module),
524 xref_defined(Source, Head, multifile(_))
525 ),
526 !.
527
528pi_to_head(Var, _) :-
529 var(Var), !, fail.
530pi_to_head(Name/Arity, Term) :-
531 functor(Term, Name, Arity).
532pi_to_head(Name//DCGArity, Term) :-
533 Arity is DCGArity+2,
534 functor(Term, Name, Arity).
535
539
540file_info(Comments, RestComments, [file(Title, Comment)|Opts], Opts) :-
541 select(doc(_:module(Title),_,Comment), Comments, RestComments),
542 !.
543file_info(Comments, Comments, Opts, Opts).
544
545
549
(File, Options) -->
551 { memberchk(file(Title, Comment), Options),
552 !,
553 file_base_name(File, Base)
554 },
555 file_title([Base, ' -- ', Title], File, Options),
556 { is_structured_comment(Comment, Prefixes),
557 string_codes(Comment, Codes),
558 indented_lines(Codes, Prefixes, Lines),
559 section_comment_header(Lines, _Header, Lines1),
560 wiki_lines_to_dom(Lines1, [], DOM)
561 },
562 html(DOM).
563file_header(File, Options) -->
564 { file_base_name(File, Base)
565 },
566 file_title([Base], File, Options).
567
568
572
573file_title(Title, File, Options) -->
574 prolog:doc_file_title(Title, File, Options),
575 !.
576file_title(Title, File, Options) -->
577 { file_base_name(File, Base)
578 },
579 html(h1(class(file),
580 [ span(style('float:right'),
581 [ \reload_button(File, Base, Options),
582 \zoom_button(Base, Options),
583 \source_button(Base, Options),
584 \edit_button(File, Options)
585 ])
586 | Title
587 ])).
588
589
596
597reload_button(File, _Base, Options) -->
598 { \+ source_file(File),
599 \+ option(files(_), Options)
600 },
601 !,
602 html(span(class(file_anot), '[not loaded]')).
603reload_button(_File, Base, Options) -->
604 { option(edit(true), Options),
605 !,
606 option(public_only(Public), Options, true)
607 },
608 html(a(href(Base+[reload(true), public_only(Public)]),
609 img([ class(action),
610 alt('Reload'),
611 title('Make & Reload'),
612 src(location_by_id(pldoc_resource)+'reload.png')
613 ]))).
614reload_button(_, _, _) --> [].
615
621
622edit_button(File, Options) -->
623 { option(edit(true), Options)
624 },
625 !,
626 html(a([ onClick('HTTPrequest(\'' +
627 location_by_id(pldoc_edit) + [file(File)] +
628 '\')')
629 ],
630 img([ class(action),
631 alt(edit),
632 title('Edit file'),
633 src(location_by_id(pldoc_resource)+'edit.png')
634 ]))).
635edit_button(_, _) -->
636 [].
637
638
642
643zoom_button(_, Options) -->
644 { option(files(_Map), Options) },
645 !. 646zoom_button(Base, Options) -->
647 { ( option(public_only(true), Options, true)
648 -> Zoom = 'public.png',
649 Alt = 'Public',
650 Title = 'Click to include private',
651 PublicOnly = false
652 ; Zoom = 'private.png',
653 Alt = 'All predicates',
654 Title = 'Click to show exports only',
655 PublicOnly = true
656 )
657 },
658 html(a(href(Base+[public_only(PublicOnly)]),
659 img([ class(action),
660 alt(Alt),
661 title(Title),
662 src(location_by_id(pldoc_resource)+Zoom)
663 ]))).
664
665
669
670source_button(_File, Options) -->
671 { option(files(_Map), Options) },
672 !. 673source_button(File, _Options) -->
674 { ( is_absolute_file_name(File)
675 -> doc_file_href(File, HREF0)
676 ; HREF0 = File
677 )
678 },
679 html(a(href(HREF0+[show(src)]),
680 img([ class(action),
681 alt('Show source'),
682 title('Show source'),
683 src(location_by_id(pldoc_resource)+'source.png')
684 ]))).
685
686
693
694objects(Objects, Options) -->
695 { option(navtree(true), Options),
696 !,
697 objects_nav_tree(Objects, Tree)
698 },
699 html([ div(class(navtree),
700 div(class(navwindow),
701 \nav_tree(Tree, Objects, Options))),
702 div(class(navcontent),
703 \objects_nt(Objects, Options))
704 ]).
705objects(Objects, Options) -->
706 objects_nt(Objects, Options).
707
708objects_nt(Objects, Options) -->
709 objects(Objects, [body], Options).
710
711objects([], Mode, _) -->
712 pop_mode(body, Mode, _).
713objects([Obj|T], Mode, Options) -->
714 object(Obj, Mode, Mode1, Options),
715 objects(T, Mode1, Options).
716
725
726object(doc(Obj,Pos,Comment), Mode0, Mode, Options) -->
727 !,
728 object(Obj, [Pos-Comment], Mode0, Mode, [scope(file)|Options]).
729object(Obj, Mode0, Mode, Options) -->
730 { findall(Pos-Comment,
731 doc_comment(Obj, Pos, _Summary, Comment),
732 Pairs)
733 },
734 !,
735 { b_setval(pldoc_object, Obj) },
736 object(Obj, Pairs, Mode0, Mode, Options).
737
738object(Obj, Pairs, Mode0, Mode, Options) -->
739 { is_pi(Obj),
740 !,
741 maplist(pred_dom(Obj, Options), Pairs, DOMS),
742 append(DOMS, DOM)
743 },
744 need_mode(dl, Mode0, Mode),
745 html(DOM).
746object([Obj|_Same], Pairs, Mode0, Mode, Options) -->
747 !,
748 object(Obj, Pairs, Mode0, Mode, Options).
749object(Obj, _Pairs, Mode, Mode, _Options) -->
750 { debug(pldoc, 'Skipped ~p', [Obj]) },
751 [].
752
753pred_dom(Obj, Options, Pos-Comment, DOM) :-
754 is_structured_comment(Comment, Prefixes),
755 string_codes(Comment, Codes),
756 indented_lines(Codes, Prefixes, Lines),
757 strip_module(user:Obj, Module, _),
758 process_modes(Lines, Module, Pos, Modes, Args, Lines1),
759 ( private(Obj, Options)
760 -> Class = privdef 761 ; multifile(Obj, Options)
762 -> ( option(scope(file), Options)
763 -> ( more_doc(Obj, Pos)
764 -> Class = multidef(object(Obj))
765 ; Class = multidef
766 )
767 ; Class = multidef(file((Pos)))
768 )
769 ; Class = pubdef 770 ),
771 ( Obj = Module:_
772 -> POptions = [module(Module)|Options]
773 ; POptions = Options
774 ),
775 Pos = File:Line,
776 DTOptions = [file(File),line(Line)|POptions],
777 DOM = [\pred_dt(Modes, Class, DTOptions), dd(class=defbody, DOM1)],
778 wiki_lines_to_dom(Lines1, Args, DOM0),
779 strip_leading_par(DOM0, DOM1).
780
781more_doc(Obj, File:_) :-
782 doc_comment(Obj, File2:_, _, _),
783 File2 \== File,
784 !.
785
792
793need_mode(Mode, Stack, Stack) -->
794 { Stack = [Mode|_] },
795 !,
796 [].
797need_mode(Mode, Stack, Rest) -->
798 { memberchk(Mode, Stack)
799 },
800 !,
801 pop_mode(Mode, Stack, Rest).
802need_mode(Mode, Stack, [Mode|Stack]) -->
803 !,
804 html_begin(Mode).
805
806pop_mode(Mode, Stack, Stack) -->
807 { Stack = [Mode|_] },
808 !,
809 [].
810pop_mode(Mode, [H|Rest0], Rest) -->
811 html_end(H),
812 pop_mode(Mode, Rest0, Rest).
813
817
818undocumented(File, Objs, Options) -->
819 { memberchk(module(Module), Options),
820 memberchk(public(Exports), Options),
821 select_undocumented(Exports, Module, Objs, Undoc),
822 re_exported_doc(Undoc, File, Module, REObjs, ReallyUnDoc)
823 },
824 !,
825 re_exported_doc(REObjs, Options),
826 undocumented(ReallyUnDoc, Options).
827undocumented(_, _, _) -->
828 [].
829
830re_exported_doc([], _) --> !.
831re_exported_doc(Objs, Options) -->
832 reexport_header(Objs, Options),
833 objects(Objs, Options).
834
(_, Options) -->
836 { option(reexport_header(true), Options, true)
837 },
838 !,
839 html([ h2(class(wiki), 'Re-exported predicates'),
840 p([ 'The following predicates are re-exported from other ',
841 'modules'
842 ])
843 ]).
844reexport_header(_, _) -->
845 [].
846
847undocumented([], _) --> !.
848undocumented(UnDoc, Options) -->
849 html([ h2(class(undoc), 'Undocumented predicates'),
850 p(['The following predicates are exported, but not ',
851 'or incorrectly documented.'
852 ]),
853 dl(class(undoc),
854 \undocumented_predicates(UnDoc, Options))
855 ]).
856
857
858undocumented_predicates([], _) -->
859 [].
860undocumented_predicates([H|T], Options) -->
861 undocumented_pred(H, Options),
862 undocumented_predicates(T, Options).
863
864undocumented_pred(Name/Arity, Options) -->
865 { functor(Head, Name, Arity) },
866 html(dt(class=undoc, \pred_mode(Head, [], _, Options))).
867
868select_undocumented([], _, _, []).
869select_undocumented([PI|T0], M, Objs, [PI|T]) :-
870 is_pi(PI),
871 \+ in_doc(M:PI, Objs),
872 !,
873 select_undocumented(T0, M, Objs, T).
874select_undocumented([_|T0], M, Objs, T) :-
875 select_undocumented(T0, M, Objs, T).
876
877in_doc(PI, Objs) :-
878 member(doc(O,_,_), Objs),
879 ( is_list(O)
880 -> member(O2, O),
881 eq_pi(PI, O2)
882 ; eq_pi(PI, O)
883 ).
884
885
889
890eq_pi(PI, PI) :- !.
891eq_pi(M:PI1, M:PI2) :-
892 atom(M),
893 !,
894 eq_pi(PI1, PI2).
895eq_pi(Name/A, Name//DCGA) :-
896 A =:= DCGA+2,
897 !.
898eq_pi(Name//DCGA, Name/A) :-
899 A =:= DCGA+2.
900
904
905is_pi(Var) :-
906 var(Var),
907 !,
908 fail.
909is_pi(_:PI) :-
910 !,
911 is_pi(PI).
912is_pi(_/_).
913is_pi(_//_).
914
915
918
919re_exported_doc([], _, _, [], []).
920re_exported_doc([PI|T0], File, Module, [doc(Orig:PI,Pos,Comment)|ObjT], UnDoc) :-
921 pi_to_head(PI, Head),
922 ( predicate_property(Module:Head, imported_from(Orig))
923 -> true
924 ; xref_defined(File, Head, imported(File2)),
925 ensure_doc_objects(File2),
926 xref_module(File2, Orig)
927 ),
928 doc_comment(Orig:PI, Pos, _, Comment),
929 !,
930 re_exported_doc(T0, File, Module, ObjT, UnDoc).
931re_exported_doc([PI|T0], File, Module, REObj, [PI|UnDoc]) :-
932 re_exported_doc(T0, File, Module, REObj, UnDoc).
933
934
935 938
946
947object_page(Obj, Options) -->
948 prolog:doc_object_page(Obj, Options),
949 !,
950 object_page_footer(Obj, Options).
951object_page(Obj, Options) -->
952 { doc_comment(Obj, File:_Line, _Summary, _Comment)
953 },
954 !,
955 ( { \+ ( doc_comment(Obj, File2:_, _, _),
956 File2 \== File )
957 }
958 -> html([ \html_requires(pldoc),
959 \object_page_header(File, Options),
960 \object_synopsis(Obj, []),
961 \objects([Obj], Options)
962 ])
963 ; html([ \html_requires(pldoc),
964 \object_page_header(-, Options),
965 \objects([Obj], [synopsis(true)|Options])
966 ])
967 ),
968 object_page_footer(Obj, Options).
969object_page(M:Name/Arity, Options) --> 970 { functor(Head, Name, Arity),
971 ( predicate_property(M:Head, exported)
972 -> module_property(M, class(library))
973 ; \+ predicate_property(M:Head, defined)
974 )
975 },
976 prolog:doc_object_page(Name/Arity, Options),
977 !,
978 object_page_footer(Name/Arity, Options).
979
(File, Options) -->
981 prolog:doc_page_header(file(File), Options),
982 !.
983object_page_header(File, Options) -->
984 { option(header(true), Options, true) },
985 !,
986 html(div(class(navhdr),
987 [ div(class(jump), \file_link(File)),
988 div(class(search), \search_form(Options)),
989 br(clear(right))
990 ])).
991object_page_header(_, _) --> [].
992
993file_link(-) -->
994 !,
995 places_menu(-).
996file_link(File) -->
997 { file_directory_name(File, Dir)
998 },
999 places_menu(Dir),
1000 html([ div(a(href(location_by_id(pldoc_doc)+File), File))
1001 ]).
1002
1007
(Obj, Options) -->
1009 prolog:doc_object_page_footer(Obj, Options).
1010object_page_footer(_, _) --> [].
1011
1012
1023
1024object_synopsis(Name/Arity, _) -->
1025 { functor(Head, Name, Arity),
1026 predicate_property(system:Head, built_in)
1027 },
1028 synopsis([span(class(builtin), 'built-in')]).
1029object_synopsis(Name/Arity, Options) -->
1030 !,
1031 object_synopsis(_:Name/Arity, Options).
1032object_synopsis(M:Name/Arity, Options) -->
1033 { functor(Head, Name, Arity),
1034 predicate_property(M:Head, exported),
1035 \+ predicate_property(M:Head, imported_from(_)),
1036 module_property(M, file(File)),
1037 file_name_on_path(File, Spec),
1038 !,
1039 unquote_filespec(Spec, Unquoted),
1040 ( predicate_property(Head, autoload(FileBase)),
1041 file_name_extension(FileBase, _Ext, File)
1042 -> Extra = [span(class(autoload), '(can be autoloaded)')]
1043 ; Extra = []
1044 )
1045 },
1046 ( { option(href(HREF), Options) }
1047 -> synopsis([code([':- use_module(',a(href(HREF), '~q'-[Unquoted]),').'])|Extra])
1048 ; synopsis([code(':- use_module(~q).'-[Unquoted])|Extra])
1049 ).
1050object_synopsis(f(_/_), _) -->
1051 synopsis(span(class(function),
1052 [ 'Arithmetic function (see ',
1053 \object_ref(is/2, []),
1054 ')'
1055 ])).
1056object_synopsis(c(Func), _) -->
1057 { sub_atom(Func, 0, _, _, 'PL_')
1058 },
1059 !,
1060 synopsis([span(class(cfunc), 'C-language interface function')]).
1061object_synopsis(_, _) --> [].
1062
1063synopsis(Text) -->
1064 html(div(class(synopsis),
1065 [ span(class('synopsis-hdr'), 'Availability:')
1066 | Text
1067 ])).
1068
1073
1074unquote_filespec(Spec, Unquoted) :-
1075 compound(Spec),
1076 Spec =.. [Alias,Path],
1077 atomic_list_concat(Parts, /, Path),
1078 maplist(need_no_quotes, Parts),
1079 !,
1080 parts_to_path(Parts, UnquotedPath),
1081 Unquoted =.. [Alias, UnquotedPath].
1082unquote_filespec(Spec, Spec).
1083
1084need_no_quotes(Atom) :-
1085 format(atom(A), '~q', [Atom]),
1086 \+ sub_atom(A, 0, _, _, '\'').
1087
1088parts_to_path([One], One) :- !.
1089parts_to_path(List, More/T) :-
1090 ( append(H, [T], List)
1091 -> parts_to_path(H, More)
1092 ).
1093
1094
1095 1098
1102
1103doc_write_html(Out, Title, Doc) :-
1104 doc_page_dom(Title, Doc, DOM),
1105 phrase(html(DOM), Tokens),
1106 print_html_head(Out),
1107 print_html(Out, Tokens).
1108
1113
1114doc_page_dom(Title, Body, DOM) :-
1115 DOM = html([ head([ title(Title),
1116 link([ rel(stylesheet),
1117 type('text/css'),
1118 href(location_by_id(pldoc_resource)+'pldoc.css')
1119 ]),
1120 script([ src(location_by_id(pldoc_resource)+'pldoc.js'),
1121 type('text/javascript')
1122 ], [])
1123 ]),
1124 body(Body)
1125 ]).
1126
1130
1131print_html_head(Out) :-
1132 format(Out,
1133 '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" \c
1134 "http://www.w3.org/TR/html4/strict.dtd">~n', []).
1135
1139
1145
1146tags(Tags) -->
1147 html(dl(class=tags, Tags)).
1148
1152
1153tag(Tag, Values) -->
1154 { doc_tag_title(Tag, Title),
1155 atom_concat('keyword-', Tag, Class)
1156 },
1157 html([ dt(class=Class, Title),
1158 \tag_values(Values, Class)
1159 ]).
1160
1161tag_values([], _) -->
1162 [].
1163tag_values([H|T], Class) -->
1164 html(dd(class=Class, ['- '|H])),
1165 tag_values(T, Class).
1166
1167
1171
1172doc_tag_title(Tag, Title) :-
1173 tag_title(Tag, Title),
1174 !.
1175doc_tag_title(Tag, Tag).
1176
1177tag_title(compat, 'Compatibility').
1178tag_title(tbd, 'To be done').
1179tag_title(see, 'See also').
1180tag_title(error, 'Errors').
1181
1186
1187args(Params) -->
1188 html([ dt(class=tag, 'Arguments:'),
1189 dd(table(class=arglist,
1190 \arg_list(Params)))
1191 ]).
1192
1193arg_list([]) -->
1194 [].
1195arg_list([H|T]) -->
1196 argument(H),
1197 arg_list(T).
1198
1199argument(arg(Name,Descr)) -->
1200 html(tr([td(var(Name)), td(class=argdescr, ['- '|Descr])])).
1201
1202
1203 1206
1211
1212objects_nav_tree(Objects, Tree) :-
1213 maplist(object_nav_tree, Objects, Trees),
1214 union_trees(Trees, Tree0),
1215 remove_unique_root(Tree0, Tree).
1216
1217object_nav_tree(Obj, Tree) :-
1218 Node = node(directory(Dir), FileNodes),
1219 FileNode = node(file(File), Siblings),
1220 doc_comment(Obj, File:_Line, _Summary, _Comment),
1221 !,
1222 file_directory_name(File, Dir),
1223 sibling_file_nodes(Dir, FileNodes0),
1224 selectchk(node(file(File),[]), FileNodes0, FileNode, FileNodes),
1225 findall(Sibling, doc_comment(Sibling, File:_, _, _), Siblings0),
1226 delete(Siblings0, _:module(_), Siblings1),
1227 doc_hide_private(Siblings1, Siblings2, []),
1228 flatten(Siblings2, Siblings), 1229 embed_directories(Node, Tree).
1230
1231sibling_file_nodes(Dir, Nodes) :-
1232 findall(node(file(File), []),
1233 ( source_file(File),
1234 file_directory_name(File, Dir)
1235 ),
1236 Nodes).
1237
1238embed_directories(Node, Tree) :-
1239 Node = node(file(File), _),
1240 !,
1241 file_directory_name(File, Dir),
1242 Super = node(directory(Dir), [Node]),
1243 embed_directories(Super, Tree).
1244embed_directories(Node, Tree) :-
1245 Node = node(directory(Dir), _),
1246 file_directory_name(Dir, SuperDir),
1247 SuperDir \== Dir,
1248 !,
1249 Super = node(directory(SuperDir), [Node]),
1250 embed_directories(Super, Tree).
1251embed_directories(Tree, Tree).
1252
1253
1254union_trees([Tree], Tree) :- !.
1255union_trees([T1,T2|Trees], Tree) :-
1256 merge_trees(T1, T2, M1),
1257 union_trees([M1|Trees], Tree).
1258
1259merge_trees(node(R, Ch1), node(R, Ch2), node(R, Ch)) :-
1260 merge_nodes(Ch1, Ch2, Ch).
1261
1262merge_nodes([], Ch, Ch) :- !.
1263merge_nodes(Ch, [], Ch) :- !.
1264merge_nodes([node(Root, Ch1)|T1], N1, [T1|Nodes]) :-
1265 selectchk(node(Root, Ch2), N1, N2),
1266 !,
1267 merge_trees(node(Root, Ch1), node(Root, Ch2), T1),
1268 merge_nodes(T1, N2, Nodes).
1269merge_nodes([Node|T1], N1, [Node|Nodes]) :-
1270 merge_nodes(T1, N1, Nodes).
1271
1275
1276remove_unique_root(node(_, [node(R1, [R2])]), Tree) :-
1277 !,
1278 remove_unique_root(node(R1, [R2]), Tree).
1279remove_unique_root(Tree, Tree).
1280
1284
1285nav_tree(Tree, Current, Options) -->
1286 html(ul(class(nav),
1287 \object_tree(Tree, Current, Options))).
1288
1292
1293object_tree(node(Id, []), Target, Options) -->
1294 !,
1295 { node_class(Id, Target, Class) },
1296 html(li(class(Class),
1297 \node(Id, Options))).
1298object_tree(node(Id, Children), Target, Options) -->
1299 !,
1300 { node_class(Id, Target, Class) },
1301 html(li(class(Class),
1302 [ \node(Id, Options),
1303 ul(class(nav),
1304 \object_trees(Children, Target, Options))
1305 ])).
1306object_tree(Id, Target, Options) -->
1307 !,
1308 { node_class(Id, Target, Class) },
1309 html(li(class([obj|Class]), \node(Id, Options))).
1310
1311object_trees([], _, _) --> [].
1312object_trees([H|T], Target, Options) -->
1313 object_tree(H, Target, Options),
1314 object_trees(T, Target, Options).
1315
1316node_class(Ids, Current, Class) :-
1317 is_list(Ids),
1318 !,
1319 ( member(Id, Ids), memberchk(Id, Current)
1320 -> Class = [nav,current]
1321 ; Class = [nav]
1322 ).
1323node_class(Id, Current, Class) :-
1324 ( memberchk(Id, Current)
1325 -> Class = [nav,current]
1326 ; Class = [nav]
1327 ).
1328
1329node(file(File), Options) -->
1330 !,
1331 object_ref(file(File), [style(title)|Options]).
1332node(Id, Options) -->
1333 object_ref(Id, Options).
1334
1335
1336 1339
1340section(Type, Title) -->
1341 { string_codes(Title, Codes),
1342 wiki_codes_to_dom(Codes, [], Content0),
1343 strip_leading_par(Content0, Content),
1344 make_section(Type, Content, HTML)
1345 },
1346 html(HTML).
1347
1348make_section(module, Title, h1(class=module, Title)).
1349make_section(section, Title, h1(class=section, Title)).
1350
1351
1352 1355
1361
1362pred_dt(Modes, Class, Options) -->
1363 pred_dt(Modes, Class, [], _Done, Options).
1364
1365pred_dt([], _, Done, Done, _) -->
1366 [].
1367pred_dt([H|T], Class, Done0, Done, Options) -->
1368 { functor(Class, CSSClass, _) },
1369 html(dt(class=CSSClass,
1370 [ \pred_mode(H, Done0, Done1, Options),
1371 \mode_anot(Class)
1372 ])),
1373 pred_dt(T, Class, Done1, Done, Options).
1374
1375mode_anot(privdef) -->
1376 !,
1377 html(span([class(anot), style('float:right')],
1378 '[private]')).
1379mode_anot(multidef(object(Obj))) -->
1380 !,
1381 { object_href(Obj, HREF) },
1382 html(span([class(anot), style('float:right')],
1383 ['[', a(href(HREF), multifile), ']'
1384 ])).
1385mode_anot(multidef(file(File:_))) -->
1386 !,
1387 { file_name_on_path(File, Spec),
1388 unquote_filespec(Spec, Unquoted),
1389 doc_file_href(File, HREF)
1390 },
1391 html(span([class(anot), style('float:right')],
1392 ['[multifile, ', a(href(HREF), '~q'-[Unquoted]), ']'
1393 ])).
1394mode_anot(multidef) -->
1395 !,
1396 html(span([class(anot), style('float:right')],
1397 '[multifile]')).
1398mode_anot(_) -->
1399 [].
1400
1401pred_mode(mode(Head,Vars), Done0, Done, Options) -->
1402 !,
1403 { bind_vars(Head, Vars) },
1404 pred_mode(Head, Done0, Done, Options).
1405pred_mode(Head is Det, Done0, Done, Options) -->
1406 !,
1407 anchored_pred_head(Head, Done0, Done, Options),
1408 pred_det(Det).
1409pred_mode(Head, Done0, Done, Options) -->
1410 anchored_pred_head(Head, Done0, Done, Options).
1411
1412bind_vars(Term, Bindings) :-
1413 bind_vars(Bindings),
1414 anon_vars(Term).
1415
1416bind_vars([]).
1417bind_vars([Name=Var|T]) :-
1418 Var = '$VAR'(Name),
1419 bind_vars(T).
1420
1425
1426anon_vars(Var) :-
1427 var(Var),
1428 !,
1429 Var = '$VAR'('_').
1430anon_vars(Term) :-
1431 compound(Term),
1432 !,
1433 Term =.. [_|Args],
1434 maplist(anon_vars, Args).
1435anon_vars(_).
1436
1437
1438anchored_pred_head(Head, Done0, Done, Options) -->
1439 { pred_anchor_name(Head, PI, Name) },
1440 ( { memberchk(PI, Done0) }
1441 -> { Done = Done0 },
1442 pred_head(Head)
1443 ; html([ span(style('float:right'),
1444 \pred_edit_or_source_button(Head, Options)),
1445 a(name=Name, \pred_head(Head))
1446 ]),
1447 { Done = [PI|Done0] }
1448 ).
1449
1450
1451pred_edit_or_source_button(Head, Options) -->
1452 { option(edit(true), Options) },
1453 !,
1454 pred_edit_button(Head, Options).
1455pred_edit_or_source_button(Head, Options) -->
1456 { option(source_link(true), Options) },
1457 !,
1458 pred_source_button(Head, Options).
1459pred_edit_or_source_button(_, _) --> [].
1460
1472
1473pred_edit_button(_, Options) -->
1474 { \+ option(edit(true), Options) },
1475 !.
1476pred_edit_button(PI0, Options0) -->
1477 { canonicalise_predref(PI0, PI, Options0, Options) },
1478 pred_edit_button2(PI, Options).
1479
1480pred_edit_button2(Name/Arity, Options) -->
1481 { \+ ( memberchk(file(_), Options), % always edit if file and line
1482 memberchk(line(_), Options) % are given.
1483 ),
1484 functor(Head, Name, Arity),
1485 option(module(M), Options, _),
1486 \+ ( current_module(M),
1487 source_file(M:Head, _File)
1488 )
1489 },
1490 !.
1491pred_edit_button2(Name/Arity, Options) -->
1492 { include(edit_param, Options, Extra),
1493 http_link_to_id(pldoc_edit,
1494 [name(Name),arity(Arity)|Extra],
1495 EditHREF)
1496 },
1497 html(a(onClick('HTTPrequest(\'' + EditHREF + '\')'),
1498 img([ class(action),
1499 alt('Edit predicate'),
1500 title('Edit predicate'),
1501 src(location_by_id(pldoc_resource)+'editpred.png')
1502 ]))).
1503pred_edit_button2(_, _) -->
1504 !,
1505 [].
1506
1507edit_param(module(_)).
1508edit_param(file(_)).
1509edit_param(line(_)).
1510
1511
1515
1516object_edit_button(_, Options) -->
1517 { \+ option(edit(true), Options) },
1518 !.
1519object_edit_button(PI, Options) -->
1520 { is_pi(PI) },
1521 !,
1522 pred_edit_button(PI, Options).
1523object_edit_button(_, _) -->
1524 [].
1525
1526
1530
1531pred_source_button(PI0, Options0) -->
1532 { canonicalise_predref(PI0, PI, Options0, Options),
1533 option(module(M), Options, _),
1534 pred_source_href(PI, M, HREF), !
1535 },
1536 html(a([ href(HREF)
1537 ],
1538 img([ class(action),
1539 alt('Source'),
1540 title('Show source'),
1541 src(location_by_id(pldoc_resource)+'source.png')
1542 ]))).
1543pred_source_button(_, _) -->
1544 [].
1545
1546
1550
1551object_source_button(PI, Options) -->
1552 { is_pi(PI),
1553 option(source_link(true), Options, true)
1554 },
1555 !,
1556 pred_source_button(PI, Options).
1557object_source_button(_, _) -->
1558 [].
1559
1560
1565
1566canonicalise_predref(M:PI0, PI, Options0, [module(M)|Options]) :-
1567 !,
1568 canonicalise_predref(PI0, PI, Options0, Options).
1569canonicalise_predref(//(Head), PI, Options0, Options) :-
1570 !,
1571 functor(Head, Name, Arity),
1572 PredArity is Arity + 2,
1573 canonicalise_predref(Name/PredArity, PI, Options0, Options).
1574canonicalise_predref(Name//Arity, PI, Options0, Options) :-
1575 integer(Arity), Arity >= 0,
1576 !,
1577 PredArity is Arity + 2,
1578 canonicalise_predref(Name/PredArity, PI, Options0, Options).
1579canonicalise_predref(PI, PI, Options, Options) :-
1580 PI = Name/Arity,
1581 atom(Name), integer(Arity), Arity >= 0,
1582 !.
1583canonicalise_predref(Head, PI, Options0, Options) :-
1584 functor(Head, Name, Arity),
1585 canonicalise_predref(Name/Arity, PI, Options0, Options).
1586
1587
1592
1593pred_head(Var) -->
1594 { var(Var),
1595 !,
1596 instantiation_error(Var)
1597 }.
1598pred_head(//(Head)) -->
1599 !,
1600 pred_head(Head),
1601 html(//).
1602pred_head(M:Head) -->
1603 html([span(class=module, M), :]),
1604 pred_head(Head).
1605pred_head(Head) -->
1606 { atom(Head) },
1607 !,
1608 html(b(class=pred, Head)).
1609pred_head(Head) --> 1610 { Head =.. [Functor,Left,Right],
1611 is_op_type(Functor, infix)
1612 },
1613 !,
1614 html([ var(class=arglist, \pred_arg(Left, 1)),
1615 ' ', b(class=pred, Functor), ' ',
1616 var(class=arglist, \pred_arg(Right, 2))
1617 ]).
1618pred_head(Head) --> 1619 { Head =.. [Functor,Arg],
1620 is_op_type(Functor, prefix)
1621 },
1622 !,
1623 html([ b(class=pred, Functor), ' ',
1624 var(class=arglist, \pred_arg(Arg, 1))
1625 ]).
1626pred_head(Head) --> 1627 { Head =.. [Functor,Arg],
1628 is_op_type(Functor, postfix)
1629 },
1630 !,
1631 html([ var(class=arglist, \pred_arg(Arg, 1)),
1632 ' ', b(class=pred, Functor)
1633 ]).
1634pred_head(Head) --> 1635 { Head =.. [Functor|Args] },
1636 html([ b(class=pred, Functor),
1637 var(class=arglist,
1638 [ '(', \pred_args(Args, 1), ')' ])
1639 ]).
1640
1645
1646is_op_type(Functor, Type) :-
1647 current_op(_Pri, F, Functor),
1648 op_type(F, Type).
1649
1650op_type(fx, prefix).
1651op_type(fy, prefix).
1652op_type(xf, postfix).
1653op_type(yf, postfix).
1654op_type(xfx, infix).
1655op_type(xfy, infix).
1656op_type(yfx, infix).
1657op_type(yfy, infix).
1658
1659
1660pred_args([], _) -->
1661 [].
1662pred_args([H|T], I) -->
1663 pred_arg(H, I),
1664 ( {T==[]}
1665 -> []
1666 ; html(', '),
1667 { I2 is I + 1 },
1668 pred_args(T, I2)
1669 ).
1670
1671pred_arg(Var, I) -->
1672 { var(Var) },
1673 !,
1674 html(['Arg', I]).
1675pred_arg(...(Term), I) -->
1676 !,
1677 pred_arg(Term, I),
1678 html('...').
1679pred_arg(Term, I) -->
1680 { Term =.. [Ind,Arg],
1681 mode_indicator(Ind)
1682 },
1683 !,
1684 html([Ind, \pred_arg(Arg, I)]).
1685pred_arg(Arg:Type, _) -->
1686 !,
1687 html([\argname(Arg), :, \argtype(Type)]).
1688pred_arg(Arg, _) -->
1689 argname(Arg).
1690
1691argname('$VAR'(Name)) -->
1692 !,
1693 html(Name).
1694argname(Name) -->
1695 !,
1696 html(Name).
1697
1698argtype(Term) -->
1699 { format(string(S), '~W',
1700 [ Term,
1701 [ quoted(true),
1702 numbervars(true)
1703 ]
1704 ]) },
1705 html(S).
1706
1707pred_det(unknown) -->
1708 [].
1709pred_det(Det) -->
1710 html([' is ', b(class=det, Det)]).
1711
1712
1718
1719term(_, Atom, []) -->
1720 { atomic(Atom) },
1721 !,
1722 html(span(class=functor, Atom)).
1723term(_, Term, Bindings) -->
1724 { is_mode(Term is det), % HACK. Bit too strict?
1725 bind_vars(Bindings)
1726 },
1727 !,
1728 pred_head(Term).
1729term(_, Term, Bindings) -->
1730 { bind_vars(Term, Bindings) },
1731 argtype(Term).
1732
1733
1734 1737
1748
1749predref(Term) -->
1750 { catch(nb_getval(pldoc_options, Options), _, Options = []) },
1751 predref(Term, Options).
1752
1753predref(Obj, Options) -->
1754 { Obj = _:_,
1755 doc_comment(Obj, File:_Line, _, _),
1756 ( ( option(files(Map), Options)
1757 -> memberchk(file(File,_), Map)
1758 ; true
1759 )
1760 -> object_href(Obj, HREF, Options)
1761 ; manref(Obj, HREF, Options)
1762 )
1763 },
1764 !,
1765 html(a(href(HREF), \object_name(Obj, [qualify(true)|Options]))).
1766predref(M:Term, Options) -->
1767 !,
1768 predref(Term, M, Options).
1769predref(Term, Options) -->
1770 predref(Term, _, Options).
1771
1772predref(Name/Arity, _, Options) --> 1773 { prolog:doc_object_summary(Name/Arity, manual, _, _),
1774 !,
1775 manref(Name/Arity, HREF, Options)
1776 },
1777 html(a([class=builtin, href=HREF], [Name, /, Arity])).
1778predref(Name/Arity, _, Options) --> 1779 { option(prefer(manual), Options),
1780 prolog:doc_object_summary(Name/Arity, Category, _, _),
1781 !,
1782 manref(Name/Arity, HREF, Options)
1783 },
1784 html(a([class=Category, href=HREF], [Name, /, Arity])).
1785predref(Obj, Module, Options) --> 1786 { doc_comment(Module:Obj, File:_Line, _, _),
1787 ( option(files(Map), Options)
1788 -> memberchk(file(File,_), Map)
1789 ; true
1790 )
1791 },
1792 !,
1793 object_ref(Module:Obj, Options).
1794predref(Name/Arity, Module, Options) -->
1795 { \+ option(files(_), Options),
1796 pred_href(Name/Arity, Module, HREF)
1797 },
1798 !,
1799 html(a(href=HREF, [Name, /, Arity])).
1800predref(Name//Arity, Module, Options) -->
1801 { \+ option(files(_), Options),
1802 PredArity is Arity + 2,
1803 pred_href(Name/PredArity, Module, HREF)
1804 },
1805 !,
1806 html(a(href=HREF, [Name, //, Arity])).
1807predref(PI, _, Options) --> 1808 { canonical_pi(PI, CPI, HTML),
1809 ( option(files(_), Options)
1810 -> Category = extmanual
1811 ; prolog:doc_object_summary(CPI, Category, _, _)
1812 ),
1813 manref(CPI, HREF, Options)
1814 },
1815 html(a([class=Category, href=HREF], HTML)).
1816predref(PI, _, _Options) -->
1817 { canonical_pi(PI, _CPI, HTML)
1818 },
1819 !,
1820 html(span(class=undef, HTML)).
1821predref(Callable, Module, Options) -->
1822 { callable(Callable),
1823 functor(Callable, Name, Arity)
1824 },
1825 predref(Name/Arity, Module, Options).
1826
1827canonical_pi(Name/Arity, Name/Arity, [Name, /, Arity]) :-
1828 atom(Name), integer(Arity),
1829 !.
1830canonical_pi(Name//Arity, Name/Arity2, [Name, //, Arity]) :-
1831 atom(Name), integer(Arity),
1832 !,
1833 Arity2 is Arity+2.
1834
1835
1840
1841manref(PI, HREF, Options) :-
1842 predname(PI, PredName),
1843 ( option(files(_Map), Options)
1844 -> option(man_server(Server), Options,
1845 'http://www.swi-prolog.org/pldoc'),
1846 uri_components(Server, Comp0),
1847 uri_data(path, Comp0, Path0),
1848 directory_file_path(Path0, man, Path),
1849 uri_data(path, Comp0, Path, Components),
1850 uri_query_components(Query, [predicate=PredName]),
1851 uri_data(search, Components, Query),
1852 uri_components(HREF, Components)
1853 ; http_link_to_id(pldoc_man, [predicate=PredName], HREF)
1854 ).
1855
1856predname(Name/Arity, PredName) :-
1857 !,
1858 format(atom(PredName), '~w/~d', [Name, Arity]).
1859predname(Module:Name/Arity, PredName) :-
1860 !,
1861 format(atom(PredName), '~w:~w/~d', [Module, Name, Arity]).
1862
1863
1874
1875pred_href(Name/Arity, Module, HREF) :-
1876 format(string(FragmentId), '~w/~d', [Name, Arity]),
1877 uri_data(fragment, Components, FragmentId),
1878 functor(Head, Name, Arity),
1879 ( catch(relative_file(Module:Head, File), _, fail)
1880 -> uri_data(path, Components, File),
1881 uri_components(HREF, Components)
1882 ; in_file(Module:Head, File)
1883 -> ( current_prolog_flag(home, SWI),
1884 sub_atom(File, 0, _, _, SWI),
1885 prolog:doc_object_summary(Name/Arity, packages, _, _)
1886 -> http_link_to_id(pldoc_man, [predicate=FragmentId], HREF)
1887 ; http_location_by_id(pldoc_doc, DocHandler),
1888 atom_concat(DocHandler, File, Path),
1889 uri_data(path, Components, Path),
1890 uri_components(HREF, Components)
1891 )
1892 ).
1893
1894relative_file(Head, '') :-
1895 b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
1896 in_file(Head, CurrentFile),
1897 !.
1898relative_file(Head, RelFile) :-
1899 b_getval(pldoc_file, CurrentFile), CurrentFile \== [],
1900 in_file(Head, DefFile),
1901 relative_file_name(DefFile, CurrentFile, RelFile).
1902
1906
1907pred_source_href(Name/Arity, Module, HREF) :-
1908 format(string(FragmentId), '~w/~d', [Name, Arity]),
1909 uri_data(fragment, Components, FragmentId),
1910 uri_query_components(Query, [show=src]),
1911 uri_data(search, Components, Query),
1912 functor(Head, Name, Arity),
1913 ( catch(relative_file(Module:Head, File), _, fail)
1914 -> uri_data(path, Components, File),
1915 uri_components(HREF, Components)
1916 ; in_file(Module:Head, File),
1917 http_location_by_id(pldoc_doc, DocHandler),
1918 atom_concat(DocHandler, File, Path),
1919 uri_data(path, Components, Path),
1920 uri_components(HREF, Components)
1921 ).
1922
1923
1929
1930object_ref([], _) -->
1931 !,
1932 [].
1933object_ref([H|T], Options) -->
1934 !,
1935 object_ref(H, Options),
1936 ( {T == []}
1937 -> html(', '),
1938 object_ref(T, Options)
1939 ; []
1940 ).
1941object_ref(Obj, Options) -->
1942 { object_href(Obj, HREF, Options)
1943 },
1944 html(a(href(HREF), \object_name(Obj, Options))).
1945
1950
1951object_href(Obj, HREF) :-
1952 object_href(Obj, HREF, []).
1953
1954object_href(M:PI0, HREF, Options) :-
1955 option(files(Map), Options),
1956 ( module_property(M, file(File))
1957 -> true
1958 ; xref_module(File, M)
1959 ),
1960 memberchk(file(File, DocFile), Map),
1961 !,
1962 file_base_name(DocFile, LocalFile), 1963 expand_pi(PI0, PI),
1964 term_to_string(PI, PIS),
1965 uri_data(path, Components, LocalFile),
1966 uri_data(fragment, Components, PIS),
1967 uri_components(HREF, Components).
1968object_href(file(File), HREF, _Options) :-
1969 doc_file_href(File, HREF),
1970 !.
1971object_href(directory(Dir), HREF, _Options) :-
1972 directory_file_path(Dir, 'index.html', Index),
1973 doc_file_href(Index, HREF),
1974 !.
1975object_href(Obj, HREF, _Options) :-
1976 prolog:doc_object_href(Obj, HREF),
1977 !.
1978object_href(Obj0, HREF, _Options) :-
1979 localise_object(Obj0, Obj),
1980 term_to_string(Obj, String),
1981 http_link_to_id(pldoc_object, [object=String], HREF).
1982
1983expand_pi(Name//Arity0, Name/Arity) :-
1984 !,
1985 Arity is Arity0+2.
1986expand_pi(PI, PI).
1987
1988
1993
1994localise_object(Obj0, Obj) :-
1995 prolog:doc_canonical_object(Obj0, Obj),
1996 !.
1997localise_object(Obj, Obj).
1998
1999
2004
2005term_to_string(Term, String) :-
2006 State = state(-),
2007 ( numbervars(Term, 0, _, [singletons(true)]),
2008 with_output_to(string(String),
2009 write_term(Term,
2010 [ numbervars(true),
2011 quoted(true)
2012 ])),
2013 nb_setarg(1, State, String),
2014 fail
2015 ; arg(1, State, String)
2016 ).
2017
2029
2030object_name(Obj, Options) -->
2031 { option(style(Style), Options, inline)
2032 },
2033 object_name(Style, Obj, Options).
2034
2035object_name(title, Obj, Options) -->
2036 { merge_options(Options, [secref_style(title)], Options1) },
2037 prolog:doc_object_link(Obj, Options1),
2038 !.
2039object_name(inline, Obj, Options) -->
2040 prolog:doc_object_link(Obj, Options),
2041 !.
2042object_name(title, f(Name/Arity), _Options) -->
2043 !,
2044 html(['Function ', Name, /, Arity]).
2045object_name(inline, f(Name/Arity), _Options) -->
2046 !,
2047 html([Name, /, Arity]).
2048object_name(Style, PI, Options) -->
2049 { is_pi(PI) },
2050 !,
2051 pi(Style, PI, Options).
2052object_name(inline, Module:module(_Title), _) -->
2053 !,
2054 { module_property(Module, file(File)),
2055 file_base_name(File, Base)
2056 },
2057 !,
2058 html(Base).
2059object_name(title, Module:module(Title), _) -->
2060 { module_property(Module, file(File)),
2061 file_base_name(File, Base)
2062 },
2063 !,
2064 html([Base, ' -- ', Title]).
2065object_name(title, file(File), _) -->
2066 { module_property(Module, file(File)),
2067 doc_comment(Module:module(Title), _, _, _),
2068 !,
2069 file_base_name(File, Base)
2070 },
2071 html([Base, ' -- ', Title]).
2072object_name(_, file(File), _) -->
2073 { file_base_name(File, Base) },
2074 html(Base).
2075object_name(_, directory(Dir), _) -->
2076 { file_base_name(Dir, Base) },
2077 html(Base).
2078
2079pi(title, PI, Options) -->
2080 pi_type(PI),
2081 pi(PI, Options).
2082pi(inline, PI, Options) -->
2083 pi(PI, Options).
2084
2085pi(M:PI, Options) -->
2086 !,
2087 ( { option(qualify(true), Options) }
2088 -> html([span(class(module), M), :])
2089 ; []
2090 ),
2091 pi(PI, Options).
2092pi(Name/Arity, _) -->
2093 !,
2094 html([Name, /, Arity]).
2095pi(Name//Arity, _) -->
2096 html([Name, //, Arity]).
2097
2098pi_type(_:PI) -->
2099 !,
2100 pi_type(PI).
2101pi_type(_/_) -->
2102 html(['Predicate ']).
2103pi_type(_//_) -->
2104 html(['Grammer rule ']).
2105
2106
2107
2115
2116in_file(Module:Head, File) :-
2117 !,
2118 in_file(Module, Head, File).
2119in_file(Head, File) :-
2120 in_file(_, Head, File).
2121
2122in_file(_, Head, File) :-
2123 xref_current_source(File),
2124 atom(File), 2125 xref_defined(File, Head, How),
2126 How \= imported(_From).
2127in_file(Module, Head, File) :-
2128 predicate_property(Module:Head, exported),
2129 ( predicate_property(Module:Head, imported_from(Primary))
2130 -> true
2131 ; Primary = Module
2132 ),
2133 module_property(Primary, file(File)).
2134in_file(Module, Head, File) :-
2135 predicate_property(Module:Head, file(File)).
2136in_file(Module, Head, File) :-
2137 current_module(Module),
2138 source_file(Module:Head, File).
2139
2168
2169file(File) -->
2170 file(File, []).
2171
2172file(File, Options) -->
2173 { catch(nb_getval(pldoc_options, GenOptions), _, GenOptions = []),
2174 merge_options(Options, GenOptions, FinalOptions)
2175 },
2176 link_file(File, FinalOptions),
2177 !.
2178file(File, Options) -->
2179 { option(edit_handler(Handler), Options),
2180 http_current_request(Request),
2181 memberchk(path(Path), Request),
2182 absolute_file_name(File, Location,
2183 [ relative_to(Path)
2184 ]),
2185 http_link_to_id(Handler, [location(Location)], HREF),
2186 format(atom(Title), 'Click to create ~w', [File])
2187 },
2188 html(a([href(HREF), class(nofile), title(Title)], File)).
2189file(File, _) -->
2190 html(code(class(nofile), File)).
2191
2192link_file(File, Options) -->
2193 { file_href(File, HREF, Options),
2194 option(label(Label), Options, File),
2195 option(class(Class), Options, file)
2196 },
2197 html(a([class(Class), href(HREF)], Label)).
2198
2202
2203file_href(_, HREF, Options) :-
2204 option(href(HREF), Options),
2205 !.
2206file_href(File, HREF, Options) :-
2207 file_href_real(File, HREF0, Options),
2208 map_extension(HREF0, HREF, Options).
2209
2215
2216map_extension(HREF0, HREF, Options) :-
2217 option(map_extension(Map), Options),
2218 file_name_extension(Base, Old, HREF0),
2219 memberchk(Old-New, Map),
2220 !,
2221 file_name_extension(Base, New, HREF).
2222map_extension(HREF, HREF, _).
2223
2224
2225file_href_real(File, HREF, Options) :-
2226 ( option(absolute_path(Path), Options)
2227 ; existing_linked_file(File, Path)
2228 ),
2229 !,
2230 ( option(files(Map), Options),
2231 memberchk(file(Path, LinkFile), Map)
2232 -> true
2233 ; LinkFile = Path
2234 ),
2235 file_href(LinkFile, HREF).
2236file_href_real(File, HREF, _) :-
2237 directory_alias(Alias),
2238 Term =.. [Alias,File],
2239 absolute_file_name(Term, _,
2240 [ access(read),
2241 file_errors(fail)
2242 ]),
2243 !,
2244 http_absolute_location(Term, HREF, []).
2245
2246directory_alias(icons).
2247directory_alias(css).
2248
2249
2256
2257file_href(Path, HREF) :- 2258 source_file(Path),
2259 !,
2260 doc_file_href(Path, HREF).
2261file_href(Path, HREF) :-
2262 ( nb_current(pldoc_output, CFile)
2263 ; nb_current(pldoc_file, CFile)
2264 ),
2265 CFile \== [],
2266 !,
2267 relative_file_name(Path, CFile, HREF).
2268file_href(Path, Path).
2269
2270
2275
2276existing_linked_file(File, Path) :-
2277 catch(b_getval(pldoc_file, CurrentFile), _, fail),
2278 CurrentFile \== [],
2279 absolute_file_name(File, Path,
2280 [ relative_to(CurrentFile),
2281 access(read),
2282 file_errors(fail)
2283 ]).
2284
2285
2292
2293include(PI, predicate, _) -->
2294 !,
2295 ( html_tokens_for_predicates(PI, [])
2296 -> []
2297 ; html(['[[', \predref(PI), ']]'])
2298 ).
2299include(File, image, Options) -->
2300 { file_name_extension(_, svg, File),
2301 file_href(File, HREF, Options),
2302 !,
2303 include(image_attribute, Options, Attrs0),
2304 merge_options(Attrs0,
2305 [ alt(File),
2306 data(HREF),
2307 type('image/svg+xml')
2308 ], Attrs)
2309 },
2310 ( { option(caption(Caption), Options) }
2311 -> html(div(class(figure),
2312 [ div(class(image), object(Attrs, [])),
2313 div(class(caption), Caption)
2314 ]))
2315 ; html(object(Attrs, []))
2316 ).
2317include(File, image, Options) -->
2318 { file_href(File, HREF, Options),
2319 !,
2320 include(image_attribute, Options, Attrs0),
2321 merge_options(Attrs0,
2322 [ alt(File),
2323 border(0),
2324 src(HREF)
2325 ], Attrs)
2326 },
2327 ( { option(caption(Caption), Options) }
2328 -> html(div(class(figure),
2329 [ div(class(image), img(Attrs)),
2330 div(class(caption), Caption)
2331 ]))
2332 ; html(img(Attrs))
2333 ).
2334include(File, wiki, _Options) --> 2335 { access_file(File, read),
2336 !,
2337 read_file_to_codes(File, String, []),
2338 wiki_codes_to_dom(String, [], DOM)
2339 },
2340 html(DOM).
2341include(File, _Type, Options) -->
2342 link_file(File, Options),
2343 !.
2344include(File, _, _) -->
2345 html(code(class(nofile), ['[[',File,']]'])).
2346
2347image_attribute(src(_)).
2348image_attribute(alt(_)).
2349image_attribute(title(_)).
2350image_attribute(align(_)).
2351image_attribute(width(_)).
2352image_attribute(height(_)).
2353image_attribute(border(_)).
2354image_attribute(class(_)).
2355
2356
2366
2367html_tokens_for_predicates([], _Options) -->
2368 [].
2369html_tokens_for_predicates([H|T], Options) -->
2370 !,
2371 html_tokens_for_predicates(H, Options),
2372 html_tokens_for_predicates(T, Options).
2373html_tokens_for_predicates(PI, Options) -->
2374 { PI = _:_/_,
2375 !,
2376 ( doc_comment(PI, Pos, _Summary, Comment)
2377 -> true
2378 ; Comment = ''
2379 )
2380 },
2381 object(PI, [Pos-Comment], [dl], _, Options).
2382html_tokens_for_predicates(Spec, Options) -->
2383 { findall(PI, documented_pi(Spec, PI), List),
2384 List \== [], !
2385 },
2386 html_tokens_for_predicates(List, Options).
2387html_tokens_for_predicates(Spec, Options) -->
2388 man_page(Spec,
2389 [ links(false), 2390 navtree(false), 2391 footer(false) 2392 | Options
2393 ]).
2394
2395
2396documented_pi(Spec, PI) :-
2397 generalise_spec(Spec, PI),
2398 doc_comment(PI, _Pos, _Summary, _Comment).
2399
2400generalise_spec(Name/Arity, _M:Name/Arity).
2401generalise_spec(Name//Arity, _M:Name//Arity).
2402
2403
2404 2407
2408
2412
2413doc_for_wiki_file(FileSpec, Options) :-
2414 absolute_file_name(FileSpec, File,
2415 [ access(read)
2416 ]),
2417 read_file_to_codes(File, String, []),
2418 b_setval(pldoc_file, File),
2419 call_cleanup(reply_wiki_page(File, String, Options),
2420 nb_delete(pldoc_file)).
2421
2422reply_wiki_page(File, String, Options) :-
2423 wiki_codes_to_dom(String, [], DOM0),
2424 title(DOM0, File, Title),
2425 insert_edit_button(DOM0, File, DOM, Options),
2426 reply_html_page(pldoc(wiki),
2427 title(Title),
2428 [ \html_requires(pldoc)
2429 | DOM
2430 ]).
2431
2432title(DOM, _, Title) :-
2433 sub_term(h1(_,Title), DOM),
2434 !.
2435title(_, File, Title) :-
2436 file_base_name(File, Title).
2437
2438insert_edit_button(DOM, _, DOM, Options) :-
2439 option(edit(false), Options, false),
2440 !.
2441insert_edit_button([h1(Attrs,Title)|DOM], File,
2442 [h1(Attrs,[ span(style('float:right'),
2443 \edit_button(File, [edit(true)]))
2444 | Title
2445 ])|DOM], _) :- !.
2446insert_edit_button(DOM, File,
2447 [ h1(class(wiki),
2448 [ span(style('float:right'),
2449 \edit_button(File, [edit(true)]))
2450 ])
2451 | DOM
2452 ], _).
2453
2454
2455 2458
2462
2463mode_anchor_name(Var, _) :-
2464 var(Var),
2465 !,
2466 instantiation_error(Var).
2467mode_anchor_name(mode(Head, _), Anchor) :-
2468 !,
2469 mode_anchor_name(Head, Anchor).
2470mode_anchor_name(Head is _Det, Anchor) :-
2471 !,
2472 mode_anchor_name(Head, Anchor).
2473mode_anchor_name(Head, Anchor) :-
2474 pred_anchor_name(Head, _, Anchor).
2475
2476
2480
2481pred_anchor_name(//(Head), Name/Arity, Anchor) :-
2482 !,
2483 functor(Head, Name, DCGArity),
2484 Arity is DCGArity+2,
2485 format(atom(Anchor), '~w/~d', [Name, Arity]).
2486pred_anchor_name(Head, Name/Arity, Anchor) :-
2487 functor(Head, Name, Arity),
2488 format(atom(Anchor), '~w/~d', [Name, Arity]).