36
37:- module(pldoc_man,
38 [ man_page//2, 39 man_overview//1, 40
41 man_content_tree/2, 42 man_packages_tree/1 43 ]). 44:- use_module(library(xpath),[xpath/3, op(_,_,_)]). 45:- use_module(library(http/html_write)). 46
47:- autoload(doc_html,
48 [ object_tree/5, private/2, object_page_header/4, objects/4,
49 object_href/2, object_synopsis/4, object_footer/4,
50 object_page_footer/4,
51 object_ref/4, object_page/4,
52 object_source_button//2
53 ]). 54:- autoload(doc_process,[doc_comment/4]). 55:- autoload(doc_search,[search_form/3]). 56:- autoload(doc_util,[atom_to_object/2,atom_pi/2]). 57:- autoload(man_index,[manual_object/5]). 58:- autoload(library(apply),[maplist/2,maplist/3,convlist/3]). 59:- autoload(library(debug),[assertion/1,debug/3]). 60:- autoload(library(error),[permission_error/3,existence_error/2]). 61:- autoload(library(filesex),
62 [directory_file_path/3,relative_file_name/3]). 63:- autoload(library(lists),
64 [select/4,append/3,member/2,last/2,selectchk/3]). 65:- autoload(library(option),[merge_options/3,option/2,option/3]). 66:- autoload(library(pairs),[pairs_values/2,pairs_keys/2]). 67:- autoload(library(prolog_xref),[xref_public_list/3]). 68:- autoload(library(sgml),
69 [ load_html/3, dtd/2, new_sgml_parser/2, set_sgml_parser/2,
70 sgml_parse/2, free_sgml_parser/1
71 ]). 72:- autoload(library(uri),[uri_encoded/3]). 73:- autoload(library(www_browser),[expand_url_path/2]). 74:- autoload(library(http/html_head),[html_requires/3]). 75:- if(exists_source(library(http/http_dispatch))). 76:- autoload(library(http/http_dispatch),
77 [ http_link_to_id/3, http_location_by_id/2,
78 http_handler/3, http_reply_file/3, http_redirect/3
79 ]). 80:- endif. 81:- autoload(library(http/http_path),[http_absolute_location/3]). 82:- autoload(library(http/mimetype),[file_mime_type/2]). 83
84:- include(hooks). 85
89
90:- predicate_options(man_page//2, 2,
91 [ for(atom),
92 links(boolean),
93 navtree(boolean),
94 synopsis(boolean),
95 footer(boolean),
96 link_source(boolean),
97 no_manual(oneof([fail,error])),
98 search_in(oneof([all, app, man])),
99 search_match(oneof([name, summary])),
100 search_options(boolean)
101 ]). 102
103
104 107
112
113man_nav_tree(Obj, Options) -->
114 { ensure_man_tree,
115 man_nav_tree(Obj, Tree, Options),
116 TreeOptions = [ secref_style(title)
117 | Options
118 ]
119 },
120 html(ul(class(nav),
121 \object_tree(Tree, [Obj], TreeOptions))).
122
123
129
130man_nav_tree(Obj, Tree, _Options) :-
131 man_child_of(Obj, Parent),
132 !,
133 findall(Neighbour, man_child_of(Neighbour, Parent), Neighbours0),
134 ( findall(Child, man_child_of(Child, Obj), Children),
135 Children \== []
136 -> select(Obj, Neighbours0, node(Obj, Children), Neighbours)
137 ; Neighbours = Neighbours0
138 ),
139 path_up(node(Parent, Neighbours), Tree).
140man_nav_tree(Obj, node(Obj, Children), _Options) :-
141 findall(Child, man_child_of(Child, Obj), Children).
142
143
144path_up(Node, Tree) :-
145 node_id(Node, Id),
146 man_child_of(Id, Parent),
147 !,
148 ( Parent == root
149 -> findall(Neighbour, man_child_of(Neighbour, Parent), Neighbours0),
150 select(Id, Neighbours0, Node, Neighbours),
151 Tree = node(root, Neighbours)
152 ; path_up(node(Parent, [Node]), Tree)
153 ).
154path_up(Tree, Tree).
155
156
160
161man_child_of(Child, Parent) :-
162 term_hash(Child, ChildHash),
163 term_hash(Parent, ParentHash),
164 man_child_of(ChildHash, Child, ParentHash, Parent).
165
166:- dynamic
167 man_child_of/4,
168 man_tree_done/0. 169
173
174ensure_man_tree :-
175 man_tree_done,
176 !.
177ensure_man_tree :-
178 with_mutex(man_tree,
179 make_man_tree).
180
181make_man_tree :-
182 man_tree_done,
183 !.
184make_man_tree :-
185 man_content_tree(swi_man_manual('.'), ManTree),
186 man_packages_tree(PkgTree),
187 assert_tree(node(root, [ManTree, PkgTree])),
188 assertz(man_tree_done).
189
190assert_tree(node(Id, Children)) :-
191 !,
192 maplist(assert_parent(Id), Children),
193 maplist(assert_tree, Children).
194assert_tree(_).
195
196assert_parent(Id, Child) :-
197 node_id(Child, ChildId),
198 term_hash(Id, ParentHash),
199 term_hash(ChildId, ChildHash),
200 assertz(man_child_of(ChildHash, ChildId, ParentHash, Id)).
201
202node_id(node(Id, _), Id) :- !.
203node_id(Id, Id).
204
205
212
213man_content_tree(Spec, node(manual, Chapters)) :-
214 absolute_file_name(Spec, Dir,
215 [ file_type(directory),
216 access(read)
217 ]),
218 directory_file_path(Dir, 'Contents.html', ContentsFile),
219 load_html(ContentsFile, DOM, [cdata(string)]),
220 findall(Level-Path,
221 ( xpath(DOM, //div(@class=Class), DIV),
222 class_level(Class, Level),
223 xpath(DIV, a(@class=sec,@href=File), _),
224 \+ sub_atom(File, _, _, _, #),
225 directory_file_path(Dir, File, Path)
226 ),
227 Pairs),
228 index_chapters(Pairs, Chapters).
229
230class_level('toc-h1', 1).
231class_level('toc-h2', 2).
232class_level('toc-h3', 3).
233class_level('toc-h4', 4).
234
235index_chapters([], []).
236index_chapters([Level-File|T0], [node(Chapter, Children)|T]) :-
237 html_content_tree(File, Node),
238 Node = node(Chapter, Children0),
239 append(Children0, Sections, Children),
240 index_sections(T0, Level, Sections, T1),
241 index_chapters(T1, T).
242
243index_sections([], _, [], []) :- !.
244index_sections([SLevel-File|T0], Level, [Node|T], Rest) :-
245 SLevel > Level,
246 !,
247 html_content_tree(File, Node),
248 index_sections(T0, Level, T, Rest).
249index_sections(Rest, _, [], Rest).
250
251
255
256man_packages_tree(node(packages, Packages)) :-
257 Section = section(0, _, _, _),
258 findall(File,
259 manual_object(Section, _Title, File, packages, _),
260 Files),
261 maplist(package_node, Files, Packages).
262
263package_node(File, Tree) :-
264 html_content_tree(File, Tree).
265
276
277html_content_tree(FileIn, Tree) :-
278 absolute_file_name(FileIn, File),
279 findall(Offset-Obj,
280 manual_object(Obj, _Summary, File, _Class, Offset),
281 Pairs),
282 keysort(Pairs, Sorted),
283 pairs_values(Sorted, Objects),
284 make_tree(Objects, Trees),
285 assertion(Trees = [_]),
286 Trees = [Tree].
287
288make_tree([], []).
289make_tree([Obj|T0], [node(Obj, Children)|T]) :-
290 children(T0, Obj, Children, T1),
291 make_tree(T1, T).
292
293children([], _, [], []) :- !.
294children([Obj|T0], Root, [Node|T], Rest) :-
295 section_level(Obj, ObjLevel),
296 section_level(Root, Level),
297 ObjLevel > Level,
298 !,
299 Node = node(Obj, Children),
300 children(T0, Obj, Children, T1),
301 children(T1, Root, T, Rest).
302children([Obj|T0], Root, [Obj|T], Rest) :-
303 \+ section_level(Obj, _),
304 !,
305 children(T0, Root, T, Rest).
306children(Rest, _, [], Rest).
307
308section_level(section(Level, _Nr, _Id, _File), Level).
309
310
311 314
321
322load_man_object(Obj, ParentSection, Path, DOM) :-
323 resolve_section(Obj, For),
324 For = section(_,SN,_ID,Path),
325 parent_section(For, ParentSection),
326 findall(Nr-Pos, section_start(Path, Nr, Pos), Pairs),
327 ( ( Pairs = [SN-_|_]
328 ; Pairs == []
329 )
330 -> !,
331 load_html(Path, DOM, [cdata(string)]) 332 ; append(_, [SN-Start|Rest], Pairs)
333 -> !,
334 ( member(N-End, Rest),
335 \+ sub_atom(N, 0, _, _, SN),
336 Len is End - Start,
337 Options = [content_length(Len)]
338 -> true
339 ; Options = []
340 ),
341 open(Path, read, In, [type(binary)]),
342 seek(In, Start, bof, _),
343 dtd(html, DTD),
344 new_sgml_parser(Parser,
345 [ dtd(DTD)
346 ]),
347 set_sgml_parser(Parser, file(Path)),
348 set_sgml_parser(Parser, dialect(sgml)),
349 set_sgml_parser(Parser, shorttag(false)),
350 set_sgml_parser(Parser, defaults(false)),
351 call_cleanup(sgml_parse(Parser,
352 [ document(DOM),
353 source(In),
354 syntax_errors(quiet),
355 cdata(string)
356 | Options
357 ]),
358 ( free_sgml_parser(Parser),
359 close(In)
360 ))
361 ).
362load_man_object(For, Parent, Path, DOM) :-
363 object_spec(For, Obj),
364 manual_object(Obj, _, Path, _, Position),
365 ( object_section(Path, Position, Parent)
366 -> true
367 ; Parent = Path
368 ),
369 open(Path, read, In, [type(binary)]),
370 seek(In, Position, bof, _),
371 dtd(html, DTD),
372 new_sgml_parser(Parser,
373 [ dtd(DTD)
374 ]),
375 set_sgml_parser(Parser, file(Path)),
376 set_sgml_parser(Parser, dialect(sgml)),
377 set_sgml_parser(Parser, shorttag(false)),
378 set_sgml_parser(Parser, defaults(false)),
379 call_cleanup(parse_dts_upto_dd(Parser, In, DOM),
380 ( free_sgml_parser(Parser),
381 close(In)
382 )).
383
384parse_dts_upto_dd(Parser, In, Description) :-
385 sgml_parse(Parser,
386 [ document(DOM0),
387 cdata(string),
388 source(In),
389 parse(element),
390 syntax_errors(quiet)
391 ]),
392 ( DOM0 = [Element],
393 Element = element(dt, _, _)
394 -> Description = [Element|More],
395 parse_dts_upto_dd(Parser, In, More)
396 ; Description = DOM0
397 ).
398
399section_start(Path, Nr, Pos) :-
400 manual_object(section(_,Nr,_,_), _, Path, _, Pos).
401
407
408resolve_section(section(Level, No, Spec), Section) :-
409 !,
410 resolve_section(section(Level, No, _, Spec), Section).
411resolve_section(section(Level, No, ID, Path),
412 section(Level, No, ID, Path)) :-
413 nonvar(ID),
414 manual_object(section(Level,No,ID,Path), _, _, _, _),
415 !.
416resolve_section(section(Level, No, ID, Spec),
417 section(Level, No, ID, Path)) :-
418 ground(Spec),
419 absolute_file_name(Spec, Path,
420 [ access(read)
421 ]),
422 ( manual_object(section(Level, No, ID, Path), _, _, _, _)
423 -> true
424 ; path_allowed(Path)
425 -> true
426 ; permission_error(read, manual_file, Spec)
427 ).
428
429
430path_allowed(Path) :- 431 absolute_file_name(swi(doc), Parent,
432 [ access(read),
433 file_type(directory)
434 ]),
435 sub_atom(Path, 0, _, _, Parent).
436
437
444
445parent_section(section(Level, Nr, _ID, File), Parent) :-
446 integer(Level),
447 Parent = section(PL, PNr, _PID, _PFile),
448 PL is Level - 1,
449 findall(B, sub_atom(Nr, B, _, _, '.'), BL),
450 last(BL, Before),
451 sub_atom(Nr, 0, Before, _, PNr),
452 ( manual_object(Parent, _, File, _, _)
453 -> true
454 ; manual_object(Parent, _, ParentFile, _, _),
455 same_dir(File, ParentFile)
456 -> true
457 ; manual_object(Parent, _, _, _, _)
458 ),
459 !.
460parent_section(section(Level, _, _, File), Parent) :-
461 Parent = section(ParentLevel, _, _, File),
462 manual_object(Parent, _, _, _, _),
463 ParentLevel < Level,
464 !.
465parent_section(section(_, _, _, File), File).
466
467
472
473object_section(Path, Pos, Section) :-
474 Section = section(_,_,_,_),
475 findall(Section,
476 (manual_object(Section, _, Path, _, SecPos), SecPos =< Pos),
477 List),
478 last(List, Section).
479
480same_dir(File1, File2) :-
481 file_directory_name(File1, Dir),
482 file_directory_name(File2, Dir).
483
488
489object_spec(Spec, Spec) :-
490 compound(Spec),
491 !.
492object_spec(Atom, Spec) :-
493 catch(atom_to_term(Atom, Spec, _), _, fail),
494 !,
495 Atom \== Spec.
496object_spec(Atom, PI) :-
497 atom_to_object(Atom, PI).
498
499
500 503
541
542man_page(Obj, Options) -->
543 { ground(Obj),
544 special_node(Obj)
545 },
546 !,
547 html_requires(pldoc),
548 man_links([], Options),
549 man_matches([Obj], Obj, Options).
550man_page(Obj0, Options) --> 551 { full_page(Obj0, Obj),
552 findall((Parent+Path)-(Obj+DOM),
553 load_man_object(Obj, Parent, Path, DOM),
554 Matches),
555 Matches = [_|_],
556 !,
557 pairs_keys(Matches, ParentPaths),
558 Matches = [Parent+Path-_|_]
559 },
560 html_requires(pldoc),
561 man_links(ParentPaths, Options),
562 man_matches(Matches, Obj, Options).
563man_page(Obj, Options) --> 564 { full_object(Obj, Full),
565 findall(Full-File,
566 ( doc_comment(Full, File:_, _, _),
567 \+ private(Full, Options)
568 ),
569 Pairs),
570 Pairs \== [],
571 pairs_keys(Pairs, Objs)
572 },
573 !,
574 html_requires(pldoc),
575 ( { Pairs = [_-File] }
576 -> object_page_header(File, Options)
577 ; object_page_header(-, Options)
578 ),
579 { merge_options(Options,
580 [ synopsis(true),
581 navtree(true)
582 ], Options2)
583 },
584 objects(Objs, Options2).
585man_page(Obj, Options) --> 586 { \+ option(no_manual(fail), Options)
587 },
588 html_requires(pldoc),
589 man_links([], Options),
590 html(p(class(noman),
591 [ 'Sorry, No manual entry for ',
592 b('~w'-[Obj])
593 ])).
594
596special_node(root).
597special_node(packages).
598
599full_page(Obj, _) :-
600 var(Obj), !, fail.
601full_page(Obj, Obj) :-
602 Obj = section(_,_,_,_),
603 !.
604full_page(section(ID), section(_,_,ID,_)) :- !.
605full_page(manual, section(_,_,'sec:intro',_)) :- !.
606full_page(Obj0, Obj) :-
607 ground(Obj0),
608 alt_obj(Obj0, Obj),
609 manual_object(Obj, _, _, _, _),
610 !.
611full_page(Obj, Obj) :-
612 ground(Obj).
613
614alt_obj(Obj, Obj).
615alt_obj(Name/Arity, Name//DCGArity) :-
616 integer(Arity),
617 Arity >= 2,
618 DCGArity is Arity - 2.
619alt_obj(Name//DCGArity, Name/Arity) :-
620 integer(DCGArity),
621 Arity is DCGArity + 2.
622
626
627full_object(Object, M:Obj) :-
628 qualify(Object, M:Obj0),
629 alt_obj(Obj0, Obj),
630 doc_comment(M:Obj, _, _, _),
631 !.
632
633qualify(M:O, M:O).
634qualify(O, _:O).
635
645
646man_qualified_object(Text, Parent, LibOpt, Object, Section) :-
647 atom(Text),
648 atom_pi(Text, PI),
649 ground(PI),
650 !,
651 man_qualified_object_2(PI, Parent, LibOpt, Object, Section).
652man_qualified_object(Object0, Parent, LibOpt, Object, Section) :-
653 man_qualified_object_2(Object0, Parent, LibOpt, Object, Section).
654
655man_qualified_object_2(Name/Arity, Parent,
656 LibOpt, Module:Name/Arity, Section) :-
657 object_module(Parent, Module, Section, LibOpt),
658 !.
659man_qualified_object_2(Object, Parent, [], Object, Parent).
660
661
666
667:- public
668 man_synopsis//2. 669
670man_synopsis(PI, Section) -->
671 man_synopsis(PI, Section, []).
672
673man_synopsis(PI, Section, Options) -->
674 { object_href(Section, HREF)
675 },
676 object_synopsis(PI, [href(HREF)|Options]).
677
681
682object_module(Section0, Module, Section, [source(Term)]) :-
683 parent_section_ndet(Section0, Section),
684 manual_object(Section, Title, _File, _Class, _Offset),
685 ( once(sub_atom(Title, B, _, _, :)),
686 sub_atom(Title, 0, B, _, Atom),
687 catch(term_to_atom(Term, Atom), _, fail),
688 ground(Term),
689 Term = library(_)
690 -> !,
691 absolute_file_name(Term, PlFile,
692 [ file_type(prolog),
693 access(read),
694 file_errors(fail)
695 ]),
696 ( module_property(Module, file(PlFile))
697 -> true
698 ; xref_public_list(PlFile, -, 699 [ module(Module)
700 ])
701 )
702 ).
703
704parent_section_ndet(Section, Section).
705parent_section_ndet(Section, Parent) :-
706 parent_section(Section, Parent0),
707 parent_section_ndet(Parent0, Parent).
708
709
710man_matches(Matches, Object, Options) -->
711 { option(navtree(false), Options) },
712 !,
713 man_matches_nt(Matches, Object, Options).
714man_matches(Matches, Object, Options) -->
715 html([ div(class(navtree),
716 div(class(navwindow),
717 \man_nav_tree(Object, Options))),
718 div(class(navcontent),
719 \man_matches_nt(Matches, Object, Options))
720 ]).
721
722
723man_matches_nt([Match], Object, Options) -->
724 { option(footer(true), Options, true) },
725 !,
726 man_match(Match, Object, Options),
727 object_page_footer(Object, []).
728man_matches_nt(Matches, Object, Options) -->
729 man_matches_list(Matches, Object, Options).
730
731man_matches_list([], _, _) --> [].
732man_matches_list([H|T], Obj, Options) -->
733 man_match(H, Obj, Options),
734 man_matches_list(T, Obj, Options).
735
740
741man_match(packages, packages, _) -->
742 !,
743 html({|html||
744 <p>
745 Packages are relatively independent add-on libraries that
746 may not be available in all installations. Packages are
747 part of the source code releases of SWI-Prolog and may be
748 enabled or disabled during the build.</p>
749
750 <p>
751 See also <a href="/pack/list">Add-ons</a> for extensions
752 provided by the community that must be installed separately
753 using
754 <a href="/pldoc/doc_for?object=pack_install/1">pack_install/1</a>.</p>
755 |}).
756man_match(root, root, _) -->
757 !,
758 man_overview([]).
759man_match((Parent+Path)-(Obj+DOM), Obj, Options) -->
760 { \+ option(synopsis(false), Options),
761 DOM = [element(dt,A,C0)|DD],
762 convlist(dt_obj, DOM, Objs),
763 option(link_source(Link), Options, true),
764 man_qualified_object(Obj, Parent, LibOpt, QObj, Section),
765 !,
766 C = [ span(style('float:right;margin-left:5px;'),
767 \object_source_button(QObj, [source_link(Link)]))
768 | C0
769 ]
770 },
771 dom_list([ element(dt,[],[\man_synopsis(QObj, Section, LibOpt)]),
772 element(dt,A,C)
773 | DD
774 ], Path, Options),
775 object_footer(Objs, Options).
776man_match((_Parent+Path)-(Obj+DOM), Obj, Options) -->
777 dom_list(DOM, Path, Options).
778
779dt_obj(element(dt,_,C), Obj) :-
780 xpath(C, //a(@id=Atom), _),
781 atom_to_object(Atom, Obj).
782
783:- html_meta
784 dom_list(html, +, +, ?, ?). 785
786dom_list(_:[], _, _) -->
787 !,
788 [].
789dom_list(M:[H|T], Path, Options) -->
790 dom(H, Path, Options),
791 dom_list(M:T, Path, Options).
792
793dom(element(E, Atts, Content), Path, Options) -->
794 !,
795 dom_element(E, Atts, Content, Path, Options).
796dom(CDATA, _, _) -->
797 html(CDATA).
798
799dom_element(a, _, [], _, _) --> 800 !,
801 [].
802dom_element(a, Att, Content, Path, Options) -->
803 { memberchk(href=HREF, Att),
804 ( memberchk(class=Class, Att)
805 -> true
806 ; Class = unknown
807 ),
808 rewrite_ref(Class, HREF, Path, Myref, Options)
809 },
810 !,
811 html(a(href(Myref), \dom_list(Content, Path, Options))).
812dom_element(span, Att, [CDATA], _, Options) -->
813 { memberchk(class='pred-ext', Att),
814 atom_pi(CDATA, PI),
815 documented(PI),
816 ( option(server(false), Options)
817 -> public_link(predicate(CDATA), HREF)
818 ; http_link_to_id(pldoc_man, [predicate=CDATA], HREF)
819 )
820 },
821 !,
822 html(a(href(HREF), CDATA)).
823dom_element(img, Att0, [], Path, _Options) -->
824 { selectchk(src=Src, Att0, Att1),
825 relative_file_name(ImgFile, Path, Src),
826 handler_alias(Handler, DirAlias),
827 absolute_file_name(DirAlias, Dir,
828 [ file_errors(fail),
829 solutions(all),
830 file_type(directory)
831 ]),
832 ensure_slash(Dir, DirS),
833 atom_concat(DirS, NewSrc, ImgFile),
834 !,
835 http_link_to_id(Handler, [], ManRef),
836 directory_file_path(ManRef, NewSrc, NewPath),
837 Begin =.. [img, src(NewPath) | Att1]
838 },
839 html_begin(Begin),
840 html_end(img).
841dom_element(div, Att, _, _, _) -->
842 { memberchk(class=navigate, Att) },
843 !.
844dom_element(html, _, Content, Path, Options) -->
845 !, 846 dom_list(Content, Path, Options).
847dom_element(head, _, Content, Path, Options) -->
848 !, 849 dom_list(Content, Path, Options).
850dom_element(title, _, _, _, _) --> !.
851dom_element(link, _, _, _, _) --> !.
852dom_element(body, _, Content, Path, Options) -->
853 !, 854 dom_list(Content, Path, Options).
855dom_element(Name, Attrs, Content, Path, Options) -->
856 { Begin =.. [Name|Attrs] },
857 html_begin(Begin),
858 dom_list(Content, Path, Options),
859 html_end(Name).
860
861handler_alias(manual_file, swi_man_manual(.)).
862handler_alias(pldoc_package, swi_man_packages(.)).
863
864ensure_slash(Dir, DirS) :-
865 ( sub_atom(Dir, _, _, 0, /)
866 -> DirS = Dir
867 ; atom_concat(Dir, /, DirS)
868 ).
869
876
877public_link(predicate(CDATA), HREF) :-
878 uri_encoded(query_value, CDATA, Encoded),
879 atom_concat('https://www.swi-prolog.org/pldoc/doc_for?object=',
880 Encoded, HREF).
881
882
886
887documented(PI) :-
888 manual_object(PI, _, _, _, _),
889 !.
890documented(PI) :-
891 full_object(PI, _Obj).
892
924
925rewrite_ref(_Class, Ref, _Path, Ref, Options) :-
926 option(server(false), Options),
927 !.
928rewrite_ref(Class, Ref0, Path, ManRef, _Options) :-
929 rewrite_ref(Class, Ref0, Path, ManRef).
930
931rewrite_ref(pred, Ref0, _, Ref) :- 932 sub_atom(Ref0, _, _, A, '#'),
933 !,
934 sub_atom(Ref0, _, A, 0, Fragment),
935 atom_to_object(Fragment, PI),
936 manual_object(PI, _, _, _, _),
937 uri_encoded(query_value, Fragment, Enc),
938 http_location_by_id(pldoc_man, ManHandler),
939 format(string(Ref), '~w?predicate=~w', [ManHandler, Enc]).
940rewrite_ref(function, Ref0, _, Ref) :- 941 sub_atom(Ref0, _, _, A, '#'),
942 !,
943 sub_atom(Ref0, _, A, 0, Fragment),
944 atom_to_object(Fragment, PI),
945 manual_object(PI, _, _, _, _),
946 PI=f(Name/Arity),
947 format(atom(PIName), '~w/~w', [Name,Arity]),
948 uri_encoded(query_value, PIName, Enc),
949 http_location_by_id(pldoc_man, ManHandler),
950 format(string(Ref), '~w?function=~w', [ManHandler, Enc]).
951rewrite_ref(func, Ref0, _, Ref) :- 952 sub_atom(Ref0, _, _, A, '#'),
953 !,
954 sub_atom(Ref0, _, A, 0, Fragment),
955 atom_to_object(Fragment, Obj),
956 manual_object(Obj, _, _, _, _),
957 Obj = c(Function),
958 uri_encoded(query_value, Function, Enc),
959 http_location_by_id(pldoc_man, ManHandler),
960 format(string(Ref), '~w?CAPI=~w', [ManHandler, Enc]).
961rewrite_ref(sec, Ref0, Path, Ref) :- 962 sub_atom(Ref0, B, _, A, '#'),
963 !,
964 sub_atom(Ref0, _, A, 0, Fragment),
965 sub_atom(Ref0, 0, B, _, File),
966 referenced_section(Fragment, File, Path, Section),
967 object_href(Section, Ref).
968rewrite_ref(sec, File, Path, Ref) :- 969 file_directory_name(Path, Dir),
970 atomic_list_concat([Dir, /, File], SecPath),
971 Obj = section(_, _, _, SecPath),
972 manual_object(Obj, _, _, _, _),
973 !,
974 object_href(Obj, Ref).
975rewrite_ref(cite, Ref0, Path, Ref) :- 976 debug(pldoc(cite), 'Cite ref ~q ~q', [Ref0, Path]),
977 sub_atom(Ref0, _, _, A, '#'),
978 !,
979 sub_atom(Ref0, _, A, 0, Fragment),
980 uri_encoded(query_value, Fragment, Enc),
981 http_location_by_id(pldoc_man, ManHandler),
982 format(string(Ref), '~w?section=bibliography#~w', [ManHandler, Enc]).
983rewrite_ref(flag, Ref0, Path, Ref) :-
984 sub_atom(Ref0, B, _, A, '#'),
985 !,
986 sub_atom(Ref0, 0, B, _, File),
987 sub_atom(Ref0, _, A, 0, Fragment),
988 file_directory_name(Path, Dir),
989 atomic_list_concat([Dir, /, File], SecPath),
990 Obj = section(_, _, _, SecPath),
991 manual_object(Obj, _, _, _, _),
992 !,
993 object_href(Obj, Ref1),
994 format(string(Ref), '~w#~w', [Ref1, Fragment]).
995rewrite_ref(gloss, Ref0, Path, Ref) :-
996 sub_atom(Ref0, B, _, A, '#'),
997 !,
998 sub_atom(Ref0, 0, B, _, File),
999 sub_atom(Ref0, _, A, 0, Fragment),
1000 file_directory_name(Path, Dir),
1001 atomic_list_concat([Dir, /, File], SecPath),
1002 Obj = section(_, _, _, SecPath),
1003 manual_object(Obj, _, _, _, _),
1004 !,
1005 object_href(Obj, Ref1),
1006 format(string(Ref), '~w#~w', [Ref1, Fragment]).
1007
1009
1010referenced_section(Fragment, File, Path, section(Level, Nr, ID, SecPath)) :-
1011 atom_concat('sec:', Nr, Fragment),
1012 ( File == ''
1013 -> SecPath = Path
1014 ; file_directory_name(Path, Dir),
1015 atomic_list_concat([Dir, /, File], SecPath)
1016 ),
1017 manual_object(section(Level, Nr, ID, SecPath), _, _, _, _).
1018
1019
1023
1024man_links(ParentPaths, Options) -->
1025 prolog:doc_page_header(parents(ParentPaths), Options),
1026 !.
1027man_links(ParentPaths, Options) -->
1028 { option(links(true), Options, true),
1029 option(header(true), Options, true)
1030 },
1031 !,
1032 html([ div(class(navhdr),
1033 [ div(class(jump), \man_parent(ParentPaths)),
1034 div(class(search), \search_form(Options)),
1035 br(clear(right))
1036 ]),
1037 p([])
1038 ]).
1039man_links(_, _) -->
1040 [].
1041
1042man_parent(ParentPaths) -->
1043 { maplist(parent_to_section, ParentPaths, [Section|MoreSections]),
1044 maplist(=(Section), MoreSections)
1045 },
1046 !,
1047 object_ref(Section, [secref_style(number_title)]).
1048man_parent(_) --> [].
1049
1050parent_to_section(X+_, X) :-
1051 X = section(_,_,_,_),
1052 !.
1053parent_to_section(File+_, Section) :-
1054 atom(File),
1055 manual_object(Section, _Title, File, _Class, _Offset),
1056 !.
1057
1064
1065section_link(Section, Options) -->
1066 { option(secref_style(Style), Options, number)
1067 },
1068 section_link(Style, Section, Options).
1069
1070section_link(number, section(_, Number, _, _), _Options) -->
1071 !,
1072 ( {Number == '0'} 1073 -> []
1074 ; html(['Sec. ', Number])
1075 ).
1076section_link(title, Obj, _Options) -->
1077 !,
1078 { manual_object(Obj, Title, _File, _Class, _Offset)
1079 },
1080 html(Title).
1081section_link(_, Obj, _Options) -->
1082 !,
1083 { Obj = section(_, Number, _, _),
1084 manual_object(Obj, Title, _File, _Class, _Offset)
1085 },
1086 ( { Number == '0' }
1087 -> html(Title)
1088 ; html([Number, ' ', Title])
1089 ).
1090
1094
1095function_link(Function, _) -->
1096 html([Function, '()']).
1097
1098
1099 1102
1107
1108man_overview(Options) -->
1109 { http_absolute_location(pldoc_man(.), RefMan, [])
1110 },
1111 html([ h1('SWI-Prolog documentation'),
1112 blockquote(class(refman_link),
1113 a(href(RefMan),
1114 'SWI-Prolog reference manual')),
1115 \package_overview(Options),
1116 \paperback(Options)
1117 ]).
1118
1119package_overview(Options) -->
1120 html([ h2(class(package_doc_title),
1121 'SWI-Prolog package documentation'),
1122 blockquote(class(package_overview),
1123 \packages(Options))
1124 ]).
1125
1126packages(Options) -->
1127 { findall(Pkg, current_package(Pkg), Pkgs)
1128 },
1129 packages(Pkgs, Options).
1130
1131packages([], _) -->
1132 [].
1133packages([Pkg|T], Options) -->
1134 package(Pkg, Options),
1135 packages(T, Options).
1136
1137package(pkg(Title, HREF, HavePackage), Options) -->
1138 { package_class(HavePackage, Class, Options)
1139 },
1140 html(div(class(Class),
1141 a([href(HREF)], Title))).
1142
1143package_class(true, pkg_link, _).
1144package_class(false, no_pkg_link, _).
1145
1146current_package(pkg(Title, HREF, HavePackage)) :-
1147 manual_object(section(0, _, _, _), Title, File, packages, _),
1148 file_base_name(File, FileNoDir),
1149 file_name_extension(Base, _, FileNoDir),
1150 ( exists_source(library(Base))
1151 -> HavePackage = true
1152 ; HavePackage = false
1153 ),
1154 http_absolute_location(pldoc_pkg(FileNoDir), HREF, []).
1155
1156
1157:- if(current_predicate(http_handler/3)). 1158:- http_handler(pldoc(jpl), pldoc_jpl, [prefix]). 1159:- http_handler(pldoc_pkg(.), pldoc_package, [prefix]). 1160:- http_handler(pldoc_man(.), pldoc_refman, [prefix]). 1161:- http_handler(pldoc(packages), pldoc_package_overview, []). 1162
1166
1167pldoc_jpl(Request) :-
1168 memberchk(path_info(JPLFile), Request),
1169 atom_concat('doc/packages/jpl', JPLFile, Path),
1170 http_reply_file(swi(Path), [], Request).
1171
1178
1179pldoc_package(Request) :-
1180 ( \+ option(path_info(_), Request)
1181 -> true
1182 ; option(path_info(/), Request)
1183 ),
1184 http_link_to_id(pldoc_object, [object=packages], HREF),
1185 http_redirect(see_other, HREF, Request).
1186pldoc_package(Request) :-
1187 memberchk(path_info(Img), Request),
1188 file_mime_type(Img, image/_),
1189 !,
1190 http_reply_file(swi_man_packages(Img), [], Request).
1191pldoc_package(Request) :-
1192 memberchk(path_info('jpl'), Request),
1193 !,
1194 memberchk(path(Path0), Request),
1195 atom_concat(Path0, /, Path),
1196 http_redirect(moved, Path, Request).
1197pldoc_package(Request) :-
1198 memberchk(path_info(JPLFile), Request),
1199 ( JPLFile == 'jpl/'
1200 -> Path = 'doc/packages/jpl/index.html'
1201 ; sub_atom(JPLFile, 0, _, _, 'jpl/')
1202 -> atom_concat('doc/packages/', JPLFile, Path)
1203 ),
1204 http_reply_file(swi(Path), [], Request).
1205pldoc_package(Request) :-
1206 memberchk(path_info(PkgDoc), Request),
1207 ensure_html_ext(PkgDoc, PkgHtml),
1208 atom_concat('packages/', PkgHtml, Path),
1209 term_to_atom(section(Path), Object),
1210 http_link_to_id(pldoc_object, [object=Object], HREF),
1211 http_redirect(see_other, HREF, Request).
1212
1213ensure_html_ext(Pkg, PkgHtml) :-
1214 file_name_extension(_, html, Pkg),
1215 !,
1216 PkgHtml = Pkg.
1217ensure_html_ext(Pkg, PkgHtml) :-
1218 file_name_extension(Pkg, html, PkgHtml).
1219
1223
1224pldoc_package_overview(_Request) :-
1225 reply_html_page(
1226 pldoc(packages),
1227 title('SWI-Prolog package documentation'),
1228 \package_overview([])).
1229:- endif. 1230
1234
1235paperback(_Options) -->
1236 { expand_url_path(swipl_book(.), HREF)
1237 },
1238 html([ h2('The manual as a book'),
1239 p([ 'A paperback version of the manual is ',
1240 a(href(HREF), 'available'), '.'
1241 ])
1242 ]).
1243
1248
1249pldoc_refman(Request) :-
1250 memberchk(path_info(Section), Request),
1251 \+ sub_atom(Section, _, _, _, /),
1252 Obj = section(0,_,_,_),
1253 manual_object(Obj, Title, File, manual, _),
1254 file_base_name(File, Section),
1255 !,
1256 reply_html_page(pldoc(man),
1257 title(Title),
1258 \object_page(Obj, [])).
1259pldoc_refman(Request) :- 1260 \+ memberchk(path_info(_), Request),
1261 !,
1262 http_link_to_id(pldoc_object, [object(manual)], HREF),
1263 http_redirect(see_other, HREF, Request).
1264pldoc_refman(Request) :-
1265 memberchk(path(Path), Request),
1266 existence_error(http_location, Path).
1267
1268
1269 1272
1273prolog:doc_object_summary(section(ID), Class, File, Summary) :-
1274 nonvar(ID), 1275 manual_object(section(_Level, _No, ID, _Path), Summary, File, Class, _Offset).
1276prolog:doc_object_summary(Obj, Class, File, Summary) :-
1277 manual_object(Obj, Summary, File, Class, _Offset).
1278
1279prolog:doc_object_page(Obj, Options) -->
1280 man_page(Obj, [no_manual(fail),footer(false)|Options]).
1281
1285
1286prolog:doc_object_link(Obj, Options) -->
1287 { Obj = section(_,_,_,_) },
1288 !,
1289 section_link(Obj, Options).
1290prolog:doc_object_link(Obj0, Options) -->
1291 { Obj0 = section(ID),
1292 Obj = section(_Level, _No, ID, _Path),
1293 manual_object(Obj, _, _, _, _)
1294 },
1295 !,
1296 section_link(Obj, Options).
1297prolog:doc_object_link(Obj, Options) -->
1298 { Obj = c(Function) },
1299 !,
1300 function_link(Function, Options).
1301prolog:doc_object_link(root, _) -->
1302 !,
1303 html('Documentation').
1304prolog:doc_object_link(manual, _Options) -->
1305 !,
1306 html('Reference manual').
1307prolog:doc_object_link(packages, _) -->
1308 html('Packages').
1309
1310prolog:doc_category(manual, 30, 'Reference Manual').
1311prolog:doc_category(packages, 40, 'Packages').
1312
1313prolog:(File, Options) -->
1314 { Section = section(_Level, _No, _ID, File),
1315 manual_object(Section, _Summary, File, _Cat, _Offset)
1316 },
1317 !,
1318 html(tr(th([colspan(3), class(section)],
1319 [ \object_ref(Section,
1320 [ secref_style(number_title)
1321 | Options
1322 ])
1323 ]))).
1324
1325prolog:doc_object_title(Obj, Title) :-
1326 Obj = section(_,_,_,_),
1327 manual_object(Obj, Title, _, _, _),
1328 !.
1329
1330prolog:doc_canonical_object(section(_Level, _No, ID, _Path),
1331 section(ID)).
1332
1336
1337prolog:doc_object_href(section(ID), HREF) :-
1338 nonvar(ID),
1339 atom_concat('sec:', Sec, ID),
1340 http_link_to_id(pldoc_man, [section(Sec)], HREF).
1341prolog:doc_object_href(section(_Level, _No, ID, _Path), HREF) :-
1342 nonvar(ID),
1343 atom_concat('sec:', Sec, ID),
1344 http_link_to_id(pldoc_man, [section(Sec)], HREF).
1345
1346 1349
1350:- if(\+current_predicate(http_link_to_id/3)). 1351
1352http_link_to_id(Id, Parameters, HREF) :-
1353 must_be(list, Parameters),
1354 format(atom(Location), '/pldoc/~w', [Id]),
1355 ( Parameters == []
1356 -> HREF = Location
1357 ; uri_data(path, Components, Location),
1358 uri_query_components(String, Parameters),
1359 uri_data(search, Components, String),
1360 uri_components(HREF, Components)
1361 ).
1362
1363:- endif. 1364
1365 1368
1369:- multifile prolog:message//1. 1370
1371prolog:message(pldoc(no_section_id(File, Title))) -->
1372 [ 'PlDoc: ~w: no id for section "~w"'-[File, Title] ].
1373prolog:message(pldoc(duplicate_ids(L))) -->
1374 [ 'PlDoc: duplicate manual section IDs:'-[], nl
1375 ],
1376 duplicate_ids(L).
1377
1378duplicate_ids([]) --> [].
1379duplicate_ids([H|T]) --> duplicate_id(H), duplicate_ids(T).
1380
1381duplicate_id(Id) -->
1382 { findall(File, manual_object(section(_,_,Id,File),_,_,_,_), Files) },
1383 [ ' ~w: ~p'-[Id, Files], nl ]