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:- use_module(library(debug),[assertion/1,debug/3]). 47
48:- autoload(doc_html,
49 [ object_tree/5, private/2, object_page_header/4, objects/4,
50 object_href/2, object_synopsis/4, object_footer/4,
51 object_page_footer/4,
52 object_ref/4, object_page/4,
53 object_source_button//2
54 ]). 55:- autoload(doc_process,[doc_comment/4]). 56:- autoload(doc_search,[search_form/3]). 57:- autoload(doc_util,[atom_to_object/2,atom_pi/2]). 58:- autoload(man_index,[manual_object/5]). 59:- autoload(library(apply),[maplist/2,maplist/3,convlist/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, visible_doc_comment(Full, File, Options), Pairs),
566 Pairs \== [],
567 pairs_keys(Pairs, Objs)
568 },
569 !,
570 html_requires(pldoc),
571 ( { Pairs = [_-File] }
572 -> object_page_header(File, Options)
573 ; object_page_header(-, Options)
574 ),
575 { merge_options(Options,
576 [ synopsis(true),
577 navtree(true)
578 ], Options2)
579 },
580 objects(Objs, Options2).
581man_page(Obj, Options) --> 582 { \+ option(no_manual(fail), Options)
583 },
584 html_requires(pldoc),
585 man_links([], Options),
586 html(p(class(noman),
587 [ 'Sorry, No manual entry for ',
588 b('~w'-[Obj])
589 ])).
590
593
(Obj, File, Options) :-
595 doc_comment(Obj, File:_, _, _),
596 \+ ( private(Obj, Options),
597 \+ ( Obj = _:_,
598 option(qualified(always), Options)
599 )
600 ).
601
603special_node(root).
604special_node(packages).
605
606full_page(Obj, _) :-
607 var(Obj), !, fail.
608full_page(Obj, Obj) :-
609 Obj = section(_,_,_,_),
610 !.
611full_page(section(ID), section(_,_,ID,_)) :- !.
612full_page(manual, section(_,_,'sec:intro',_)) :- !.
613full_page(Obj0, Obj) :-
614 ground(Obj0),
615 alt_obj(Obj0, Obj),
616 manual_object(Obj, _, _, _, _),
617 !.
618full_page(Obj, Obj) :-
619 ground(Obj).
620
621alt_obj(Obj, Obj).
622alt_obj(Name/Arity, Name//DCGArity) :-
623 integer(Arity),
624 Arity >= 2,
625 DCGArity is Arity - 2.
626alt_obj(Name//DCGArity, Name/Arity) :-
627 integer(DCGArity),
628 Arity is DCGArity + 2.
629
633
634full_object(Object, M:Obj) :-
635 qualify(Object, M:Obj0),
636 alt_obj(Obj0, Obj),
637 doc_comment(M:Obj, _, _, _),
638 !.
639
640qualify(M:O, M:O).
641qualify(O, _:O).
642
652
653man_qualified_object(Text, Parent, LibOpt, Object, Section) :-
654 atom(Text),
655 atom_pi(Text, PI),
656 ground(PI),
657 !,
658 man_qualified_object_2(PI, Parent, LibOpt, Object, Section).
659man_qualified_object(Object0, Parent, LibOpt, Object, Section) :-
660 man_qualified_object_2(Object0, Parent, LibOpt, Object, Section).
661
662man_qualified_object_2(Name/Arity, Parent,
663 LibOpt, Module:Name/Arity, Section) :-
664 object_module(Parent, Module, Section, LibOpt),
665 !.
666man_qualified_object_2(Object, Parent, [], Object, Parent).
667
668
673
674:- public
675 man_synopsis//2. 676
677man_synopsis(PI, Section) -->
678 man_synopsis(PI, Section, []).
679
680man_synopsis(PI, Section, Options) -->
681 { object_href(Section, HREF)
682 },
683 object_synopsis(PI, [href(HREF)|Options]).
684
688
689object_module(Section0, Module, Section, [source(Term)]) :-
690 parent_section_ndet(Section0, Section),
691 manual_object(Section, Title, _File, _Class, _Offset),
692 ( once(sub_atom(Title, B, _, _, :)),
693 sub_atom(Title, 0, B, _, Atom),
694 catch(term_to_atom(Term, Atom), _, fail),
695 ground(Term),
696 Term = library(_)
697 -> !,
698 absolute_file_name(Term, PlFile,
699 [ file_type(prolog),
700 access(read),
701 file_errors(fail)
702 ]),
703 ( module_property(Module, file(PlFile))
704 -> true
705 ; xref_public_list(PlFile, -, 706 [ module(Module)
707 ])
708 )
709 ).
710
711parent_section_ndet(Section, Section).
712parent_section_ndet(Section, Parent) :-
713 parent_section(Section, Parent0),
714 parent_section_ndet(Parent0, Parent).
715
716
717man_matches(Matches, Object, Options) -->
718 { option(navtree(false), Options) },
719 !,
720 man_matches_nt(Matches, Object, Options).
721man_matches(Matches, Object, Options) -->
722 html([ div(class(navtree),
723 div(class(navwindow),
724 \man_nav_tree(Object, Options))),
725 div(class(navcontent),
726 \man_matches_nt(Matches, Object, Options))
727 ]).
728
729
730man_matches_nt([Match], Object, Options) -->
731 { option(footer(true), Options, true) },
732 !,
733 man_match(Match, Object, Options),
734 object_page_footer(Object, []).
735man_matches_nt(Matches, Object, Options) -->
736 man_matches_list(Matches, Object, Options).
737
738man_matches_list([], _, _) --> [].
739man_matches_list([H|T], Obj, Options) -->
740 man_match(H, Obj, Options),
741 man_matches_list(T, Obj, Options).
742
747
748man_match(packages, packages, _) -->
749 !,
750 html({|html||
751 <p>
752 Packages are relatively independent add-on libraries that
753 may not be available in all installations. Packages are
754 part of the source code releases of SWI-Prolog and may be
755 enabled or disabled during the build.</p>
756
757 <p>
758 See also <a href="/pack/list">Add-ons</a> for extensions
759 provided by the community that must be installed separately
760 using
761 <a href="/pldoc/doc_for?object=pack_install/1">pack_install/1</a>.</p>
762 |}).
763man_match(root, root, _) -->
764 !,
765 man_overview([]).
766man_match((Parent+Path)-(Obj+DOM), Obj, Options) -->
767 { \+ option(synopsis(false), Options),
768 DOM = [element(dt,A,C0)|DD],
769 convlist(dt_obj, DOM, Objs),
770 option(link_source(Link), Options, true),
771 man_qualified_object(Obj, Parent, LibOpt, QObj, Section),
772 !,
773 C = [ span(style('float:right;margin-left:5px;'),
774 \object_source_button(QObj, [source_link(Link)]))
775 | C0
776 ]
777 },
778 dom_list([ element(dt,[],[\man_synopsis(QObj, Section, LibOpt)]),
779 element(dt,A,C)
780 | DD
781 ], Path, Options),
782 object_footer(Objs, Options).
783man_match((_Parent+Path)-(Obj+DOM), Obj, Options) -->
784 dom_list(DOM, Path, Options).
785
786dt_obj(element(dt,_,C), Obj) :-
787 xpath(C, //a(@id=Atom), _),
788 atom_to_object(Atom, Obj).
789
790:- html_meta
791 dom_list(html, +, +, ?, ?). 792
793dom_list(_:[], _, _) -->
794 !,
795 [].
796dom_list(M:[H|T], Path, Options) -->
797 dom(H, Path, Options),
798 dom_list(M:T, Path, Options).
799
800dom(element(E, Atts, Content), Path, Options) -->
801 !,
802 dom_element(E, Atts, Content, Path, Options).
803dom(CDATA, _, _) -->
804 html(CDATA).
805
806dom_element(a, _, [], _, _) --> 807 !,
808 [].
809dom_element(a, Att, Content, Path, Options) -->
810 { memberchk(href=HREF, Att),
811 ( memberchk(class=Class, Att)
812 -> true
813 ; Class = unknown
814 ),
815 rewrite_ref(Class, HREF, Path, Myref, Options)
816 },
817 !,
818 html(a(href(Myref), \dom_list(Content, Path, Options))).
819dom_element(span, Att, [CDATA], _, Options) -->
820 { memberchk(class='pred-ext', Att),
821 atom_pi(CDATA, PI),
822 documented(PI),
823 ( option(server(false), Options)
824 -> public_link(predicate(CDATA), HREF)
825 ; http_link_to_id(pldoc_man, [predicate=CDATA], HREF)
826 )
827 },
828 !,
829 html(a(href(HREF), CDATA)).
830dom_element(img, Att0, [], Path, _Options) -->
831 { selectchk(src=Src, Att0, Att1),
832 relative_file_name(ImgFile, Path, Src),
833 handler_alias(Handler, DirAlias),
834 absolute_file_name(DirAlias, Dir,
835 [ file_errors(fail),
836 solutions(all),
837 file_type(directory)
838 ]),
839 ensure_slash(Dir, DirS),
840 atom_concat(DirS, NewSrc, ImgFile),
841 !,
842 http_link_to_id(Handler, [], ManRef),
843 directory_file_path(ManRef, NewSrc, NewPath),
844 Begin =.. [img, src(NewPath) | Att1]
845 },
846 html_begin(Begin),
847 html_end(img).
848dom_element(div, Att, _, _, _) -->
849 { memberchk(class=navigate, Att) },
850 !.
851dom_element(html, _, Content, Path, Options) -->
852 !, 853 dom_list(Content, Path, Options).
854dom_element(head, _, Content, Path, Options) -->
855 !, 856 dom_list(Content, Path, Options).
857dom_element(title, _, _, _, _) --> !.
858dom_element(link, _, _, _, _) --> !.
859dom_element(body, _, Content, Path, Options) -->
860 !, 861 dom_list(Content, Path, Options).
862dom_element(Name, Attrs, Content, Path, Options) -->
863 { Begin =.. [Name|Attrs] },
864 html_begin(Begin),
865 dom_list(Content, Path, Options),
866 html_end(Name).
867
868handler_alias(manual_file, swi_man_manual(.)).
869handler_alias(pldoc_package, swi_man_packages(.)).
870
871ensure_slash(Dir, DirS) :-
872 ( sub_atom(Dir, _, _, 0, /)
873 -> DirS = Dir
874 ; atom_concat(Dir, /, DirS)
875 ).
876
883
884public_link(predicate(CDATA), HREF) :-
885 uri_encoded(query_value, CDATA, Encoded),
886 atom_concat('https://www.swi-prolog.org/pldoc/doc_for?object=',
887 Encoded, HREF).
888
889
893
894documented(PI) :-
895 manual_object(PI, _, _, _, _),
896 !.
897documented(PI) :-
898 full_object(PI, _Obj).
899
931
932rewrite_ref(_Class, Ref, _Path, Ref, Options) :-
933 option(server(false), Options),
934 !.
935rewrite_ref(Class, Ref0, Path, ManRef, _Options) :-
936 rewrite_ref(Class, Ref0, Path, ManRef).
937
938rewrite_ref(pred, Ref0, _, Ref) :- 939 sub_atom(Ref0, _, _, A, '#'),
940 !,
941 sub_atom(Ref0, _, A, 0, Fragment),
942 atom_to_object(Fragment, PI),
943 manual_object(PI, _, _, _, _),
944 uri_encoded(query_value, Fragment, Enc),
945 http_location_by_id(pldoc_man, ManHandler),
946 format(string(Ref), '~w?predicate=~w', [ManHandler, Enc]).
947rewrite_ref(function, Ref0, _, Ref) :- 948 sub_atom(Ref0, _, _, A, '#'),
949 !,
950 sub_atom(Ref0, _, A, 0, Fragment),
951 atom_to_object(Fragment, PI),
952 manual_object(PI, _, _, _, _),
953 PI=f(Name/Arity),
954 format(atom(PIName), '~w/~w', [Name,Arity]),
955 uri_encoded(query_value, PIName, Enc),
956 http_location_by_id(pldoc_man, ManHandler),
957 format(string(Ref), '~w?function=~w', [ManHandler, Enc]).
958rewrite_ref(func, Ref0, _, Ref) :- 959 sub_atom(Ref0, _, _, A, '#'),
960 !,
961 sub_atom(Ref0, _, A, 0, Fragment),
962 atom_to_object(Fragment, Obj),
963 manual_object(Obj, _, _, _, _),
964 Obj = c(Function),
965 uri_encoded(query_value, Function, Enc),
966 http_location_by_id(pldoc_man, ManHandler),
967 format(string(Ref), '~w?CAPI=~w', [ManHandler, Enc]).
968rewrite_ref(sec, Ref0, Path, Ref) :- 969 sub_atom(Ref0, B, _, A, '#'),
970 !,
971 sub_atom(Ref0, _, A, 0, Fragment),
972 sub_atom(Ref0, 0, B, _, File),
973 referenced_section(Fragment, File, Path, Section),
974 object_href(Section, Ref).
975rewrite_ref(sec, File, Path, Ref) :- 976 file_directory_name(Path, Dir),
977 atomic_list_concat([Dir, /, File], SecPath),
978 Obj = section(_, _, _, SecPath),
979 manual_object(Obj, _, _, _, _),
980 !,
981 object_href(Obj, Ref).
982rewrite_ref(cite, Ref0, Path, Ref) :- 983 debug(pldoc(cite), 'Cite ref ~q ~q', [Ref0, Path]),
984 sub_atom(Ref0, _, _, A, '#'),
985 !,
986 sub_atom(Ref0, _, A, 0, Fragment),
987 uri_encoded(query_value, Fragment, Enc),
988 http_location_by_id(pldoc_man, ManHandler),
989 format(string(Ref), '~w?section=bibliography#~w', [ManHandler, Enc]).
990rewrite_ref(flag, Ref0, Path, Ref) :-
991 sub_atom(Ref0, B, _, A, '#'),
992 !,
993 sub_atom(Ref0, 0, B, _, File),
994 sub_atom(Ref0, _, A, 0, Fragment),
995 file_directory_name(Path, Dir),
996 atomic_list_concat([Dir, /, File], SecPath),
997 Obj = section(_, _, _, SecPath),
998 manual_object(Obj, _, _, _, _),
999 !,
1000 object_href(Obj, Ref1),
1001 format(string(Ref), '~w#~w', [Ref1, Fragment]).
1002rewrite_ref(gloss, Ref0, Path, Ref) :-
1003 sub_atom(Ref0, B, _, A, '#'),
1004 !,
1005 sub_atom(Ref0, 0, B, _, File),
1006 sub_atom(Ref0, _, A, 0, Fragment),
1007 file_directory_name(Path, Dir),
1008 atomic_list_concat([Dir, /, File], SecPath),
1009 Obj = section(_, _, _, SecPath),
1010 manual_object(Obj, _, _, _, _),
1011 !,
1012 object_href(Obj, Ref1),
1013 format(string(Ref), '~w#~w', [Ref1, Fragment]).
1014
1016
1017referenced_section(Fragment, File, Path, section(Level, Nr, ID, SecPath)) :-
1018 atom_concat('sec:', Nr, Fragment),
1019 ( File == ''
1020 -> SecPath = Path
1021 ; file_directory_name(Path, Dir),
1022 atomic_list_concat([Dir, /, File], SecPath)
1023 ),
1024 manual_object(section(Level, Nr, ID, SecPath), _, _, _, _).
1025
1026
1030
1031man_links(ParentPaths, Options) -->
1032 prolog:doc_page_header(parents(ParentPaths), Options),
1033 !.
1034man_links(ParentPaths, Options) -->
1035 { option(links(true), Options, true),
1036 option(header(true), Options, true)
1037 },
1038 !,
1039 html([ div(class(navhdr),
1040 [ div(class(jump), \man_parent(ParentPaths)),
1041 div(class(search), \search_form(Options)),
1042 br(clear(right))
1043 ]),
1044 p([])
1045 ]).
1046man_links(_, _) -->
1047 [].
1048
1049man_parent(ParentPaths) -->
1050 { maplist(parent_to_section, ParentPaths, [Section|MoreSections]),
1051 maplist(=(Section), MoreSections)
1052 },
1053 !,
1054 object_ref(Section, [secref_style(number_title)]).
1055man_parent(_) --> [].
1056
1057parent_to_section(X+_, X) :-
1058 X = section(_,_,_,_),
1059 !.
1060parent_to_section(File+_, Section) :-
1061 atom(File),
1062 manual_object(Section, _Title, File, _Class, _Offset),
1063 !.
1064
1071
1072section_link(Section, Options) -->
1073 { option(secref_style(Style), Options, number)
1074 },
1075 section_link(Style, Section, Options).
1076
1077section_link(number, section(_, Number, _, _), _Options) -->
1078 !,
1079 ( {Number == '0'} 1080 -> []
1081 ; html(['Sec. ', Number])
1082 ).
1083section_link(title, Obj, _Options) -->
1084 !,
1085 { manual_object(Obj, Title, _File, _Class, _Offset)
1086 },
1087 html(Title).
1088section_link(_, Obj, _Options) -->
1089 !,
1090 { Obj = section(_, Number, _, _),
1091 manual_object(Obj, Title, _File, _Class, _Offset)
1092 },
1093 ( { Number == '0' }
1094 -> html(Title)
1095 ; html([Number, ' ', Title])
1096 ).
1097
1101
1102function_link(Function, _) -->
1103 html([Function, '()']).
1104
1105
1106 1109
1114
1115man_overview(Options) -->
1116 { http_absolute_location(pldoc_man(.), RefMan, [])
1117 },
1118 html([ h1('SWI-Prolog documentation'),
1119 blockquote(class(refman_link),
1120 a(href(RefMan),
1121 'SWI-Prolog reference manual')),
1122 \package_overview(Options),
1123 \paperback(Options)
1124 ]).
1125
1126package_overview(Options) -->
1127 html([ h2(class(package_doc_title),
1128 'SWI-Prolog package documentation'),
1129 blockquote(class(package_overview),
1130 \packages(Options))
1131 ]).
1132
1133packages(Options) -->
1134 { findall(Pkg, current_package(Pkg), Pkgs)
1135 },
1136 packages(Pkgs, Options).
1137
1138packages([], _) -->
1139 [].
1140packages([Pkg|T], Options) -->
1141 package(Pkg, Options),
1142 packages(T, Options).
1143
1144package(pkg(Title, HREF, HavePackage), Options) -->
1145 { package_class(HavePackage, Class, Options)
1146 },
1147 html(div(class(Class),
1148 a([href(HREF)], Title))).
1149
1150package_class(true, pkg_link, _).
1151package_class(false, no_pkg_link, _).
1152
1153current_package(pkg(Title, HREF, HavePackage)) :-
1154 manual_object(section(0, _, _, _), Title, File, packages, _),
1155 file_base_name(File, FileNoDir),
1156 file_name_extension(Base, _, FileNoDir),
1157 ( exists_source(library(Base))
1158 -> HavePackage = true
1159 ; HavePackage = false
1160 ),
1161 http_absolute_location(pldoc_pkg(FileNoDir), HREF, []).
1162
1163
1164:- if(current_predicate(http_handler/3)). 1165:- http_handler(pldoc(jpl), pldoc_jpl, [prefix]). 1166:- http_handler(pldoc_pkg(.), pldoc_package, [prefix]). 1167:- http_handler(pldoc_man(.), pldoc_refman, [prefix]). 1168:- http_handler(pldoc(packages), pldoc_package_overview, []). 1169
1173
1174pldoc_jpl(Request) :-
1175 memberchk(path_info(JPLFile), Request),
1176 atom_concat('doc/packages/jpl', JPLFile, Path),
1177 http_reply_file(swi(Path), [], Request).
1178
1185
1186pldoc_package(Request) :-
1187 ( \+ option(path_info(_), Request)
1188 -> true
1189 ; option(path_info(/), Request)
1190 ),
1191 http_link_to_id(pldoc_object, [object=packages], HREF),
1192 http_redirect(see_other, HREF, Request).
1193pldoc_package(Request) :-
1194 memberchk(path_info(Img), Request),
1195 file_mime_type(Img, image/_),
1196 !,
1197 http_reply_file(swi_man_packages(Img), [], Request).
1198pldoc_package(Request) :-
1199 memberchk(path_info('jpl'), Request),
1200 !,
1201 memberchk(path(Path0), Request),
1202 atom_concat(Path0, /, Path),
1203 http_redirect(moved, Path, Request).
1204pldoc_package(Request) :-
1205 memberchk(path_info(JPLFile), Request),
1206 ( JPLFile == 'jpl/'
1207 -> Path = 'doc/packages/jpl/index.html'
1208 ; sub_atom(JPLFile, 0, _, _, 'jpl/')
1209 -> atom_concat('doc/packages/', JPLFile, Path)
1210 ),
1211 http_reply_file(swi(Path), [], Request).
1212pldoc_package(Request) :-
1213 memberchk(path_info(PkgDoc), Request),
1214 ensure_html_ext(PkgDoc, PkgHtml),
1215 atom_concat('packages/', PkgHtml, Path),
1216 term_to_atom(section(Path), Object),
1217 http_link_to_id(pldoc_object, [object=Object], HREF),
1218 http_redirect(see_other, HREF, Request).
1219
1220ensure_html_ext(Pkg, PkgHtml) :-
1221 file_name_extension(_, html, Pkg),
1222 !,
1223 PkgHtml = Pkg.
1224ensure_html_ext(Pkg, PkgHtml) :-
1225 file_name_extension(Pkg, html, PkgHtml).
1226
1230
1231pldoc_package_overview(_Request) :-
1232 reply_html_page(
1233 pldoc(packages),
1234 title('SWI-Prolog package documentation'),
1235 \package_overview([])).
1236:- endif. 1237
1241
1242paperback(_Options) -->
1243 { expand_url_path(swipl_book(.), HREF)
1244 },
1245 html([ h2('The manual as a book'),
1246 p([ 'A paperback version of the manual is ',
1247 a(href(HREF), 'available'), '.'
1248 ])
1249 ]).
1250
1255
1256pldoc_refman(Request) :-
1257 memberchk(path_info(Section), Request),
1258 \+ sub_atom(Section, _, _, _, /),
1259 Obj = section(0,_,_,_),
1260 manual_object(Obj, Title, File, manual, _),
1261 file_base_name(File, Section),
1262 !,
1263 reply_html_page(pldoc(man),
1264 title(Title),
1265 \object_page(Obj, [])).
1266pldoc_refman(Request) :- 1267 \+ memberchk(path_info(_), Request),
1268 !,
1269 http_link_to_id(pldoc_object, [object(manual)], HREF),
1270 http_redirect(see_other, HREF, Request).
1271pldoc_refman(Request) :-
1272 memberchk(path(Path), Request),
1273 existence_error(http_location, Path).
1274
1275
1276 1279
1280prolog:doc_object_summary(section(ID), Class, File, Summary) :-
1281 nonvar(ID), 1282 manual_object(section(_Level, _No, ID, _Path), Summary, File, Class, _Offset).
1283prolog:doc_object_summary(Obj, Class, File, Summary) :-
1284 manual_object(Obj, Summary, File, Class, _Offset).
1285
1286prolog:doc_object_page(Obj, Options) -->
1287 man_page(Obj, [no_manual(fail),footer(false)|Options]).
1288
1292
1293prolog:doc_object_link(Obj, Options) -->
1294 { Obj = section(_,_,_,_) },
1295 !,
1296 section_link(Obj, Options).
1297prolog:doc_object_link(Obj0, Options) -->
1298 { Obj0 = section(ID),
1299 Obj = section(_Level, _No, ID, _Path),
1300 manual_object(Obj, _, _, _, _)
1301 },
1302 !,
1303 section_link(Obj, Options).
1304prolog:doc_object_link(Obj, Options) -->
1305 { Obj = c(Function) },
1306 !,
1307 function_link(Function, Options).
1308prolog:doc_object_link(root, _) -->
1309 !,
1310 html('Documentation').
1311prolog:doc_object_link(manual, _Options) -->
1312 !,
1313 html('Reference manual').
1314prolog:doc_object_link(packages, _) -->
1315 html('Packages').
1316
1317prolog:doc_category(manual, 30, 'Reference Manual').
1318prolog:doc_category(packages, 40, 'Packages').
1319
1320prolog:(File, Options) -->
1321 { Section = section(_Level, _No, _ID, File),
1322 manual_object(Section, _Summary, File, _Cat, _Offset)
1323 },
1324 !,
1325 html(tr(th([colspan(3), class(section)],
1326 [ \object_ref(Section,
1327 [ secref_style(number_title)
1328 | Options
1329 ])
1330 ]))).
1331
1332prolog:doc_object_title(Obj, Title) :-
1333 Obj = section(_,_,_,_),
1334 manual_object(Obj, Title, _, _, _),
1335 !.
1336
1337prolog:doc_canonical_object(section(_Level, _No, ID, _Path),
1338 section(ID)).
1339
1343
1344prolog:doc_object_href(section(ID), HREF) :-
1345 nonvar(ID),
1346 atom_concat('sec:', Sec, ID),
1347 http_link_to_id(pldoc_man, [section(Sec)], HREF).
1348prolog:doc_object_href(section(_Level, _No, ID, _Path), HREF) :-
1349 nonvar(ID),
1350 atom_concat('sec:', Sec, ID),
1351 http_link_to_id(pldoc_man, [section(Sec)], HREF).
1352
1353 1356
1357:- if(\+current_predicate(http_link_to_id/3)). 1358
1359http_link_to_id(Id, Parameters, HREF) :-
1360 must_be(list, Parameters),
1361 format(atom(Location), '/pldoc/~w', [Id]),
1362 ( Parameters == []
1363 -> HREF = Location
1364 ; uri_data(path, Components, Location),
1365 uri_query_components(String, Parameters),
1366 uri_data(search, Components, String),
1367 uri_components(HREF, Components)
1368 ).
1369
1370:- endif. 1371
1372 1375
1376:- multifile prolog:message//1. 1377
1378prolog:message(pldoc(no_section_id(File, Title))) -->
1379 [ 'PlDoc: ~w: no id for section "~w"'-[File, Title] ].
1380prolog:message(pldoc(duplicate_ids(L))) -->
1381 [ 'PlDoc: duplicate manual section IDs:'-[], nl
1382 ],
1383 duplicate_ids(L).
1384
1385duplicate_ids([]) --> [].
1386duplicate_ids([H|T]) --> duplicate_id(H), duplicate_ids(T).
1387
1388duplicate_id(Id) -->
1389 { findall(File, manual_object(section(_,_,Id,File),_,_,_,_), Files) },
1390 [ ' ~w: ~p'-[Id, Files], nl ]