1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2018, VU University Amsterdam 7 All rights reserved. 8 9 Redistribution and use in source and binary forms, with or without 10 modification, are permitted provided that the following conditions 11 are met: 12 13 1. Redistributions of source code must retain the above copyright 14 notice, this list of conditions and the following disclaimer. 15 16 2. Redistributions in binary form must reproduce the above copyright 17 notice, this list of conditions and the following disclaimer in 18 the documentation and/or other materials provided with the 19 distribution. 20 21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 POSSIBILITY OF SUCH DAMAGE. 33*/ 34 35:- module(prolog_manual_index, 36 [ clean_man_index/0, % 37 save_man_index/0, 38 index_man_directory/2, % +DirSpec, +Options 39 index_man_file/2, % +Class, +FileSpec 40 % Query 41 current_man_object/1, % ?Object 42 man_object_property/2, % ?Object, ?Property 43 44 manual_object/5, % ?Obj, ?Summary, ?File, ?Class, ?Offset 45 doc_object_identifier/2 % +Obj, -Name 46 ]). 47:- use_module(library(debug),[debug/3]). 48:- autoload(doc_util,[atom_to_object/2,normalise_white_space/3]). 49:- autoload(library(apply),[maplist/2]). 50:- autoload(library(assoc),[empty_assoc/1,get_assoc/3,put_assoc/4]). 51:- autoload(library(error),[type_error/2]). 52:- autoload(library(filesex),[directory_file_path/3]). 53:- autoload(library(lists),[member/2,reverse/2,append/3]). 54:- autoload(library(occurs),[sub_term/2]). 55:- autoload(library(option),[select_option/4]). 56:- autoload(library(sgml), 57 [ dtd/2, 58 new_sgml_parser/2, 59 set_sgml_parser/2, 60 sgml_parse/2, 61 free_sgml_parser/1, 62 get_sgml_parser/2 63 ]).
72:- predicate_options(index_man_directory/2, 2, 73 [ class(oneof([manual,packages,misc])), 74 symbolic(any), 75 pass_to(system:absolute_file_name/3, 3) 76 ]). 77 78 79:- dynamic 80 man_index/5. % Object, Summary, File, Class, Offset
104manual_object(Object, Summary, File, Class, Offset) :-
105 index_manual,
106 man_index(Object, Summary, File, Class, Offset).
112clean_man_index :-
113 retractall(man_index(_,_,_,_,_)).121user:file_search_path(swi_man_manual, swi('doc/Manual')). 122user:file_search_path(swi_man_packages, swi('doc/packages')). 123user:file_search_path(swi_man_xpce, swi('xpce/man/refmanual')). 124 125manual_directory(Class, Spec, Dir) :- 126 man_path_spec(Class, Spec), 127 absolute_file_name(Spec, Dir, 128 [ file_type(directory), 129 access(read), 130 solutions(all) 131 ]). 132 133man_path_spec(manual, swi_man_manual(.)). 134man_path_spec(packages, swi_man_packages(.)). 135man_path_spec(packages, swi_man_xpce(.)). 136 137 138 /******************************* 139 * PARSE MANUAL * 140 *******************************/
148save_man_index :-
149 cached_index_file(write, File),
150 setup_call_cleanup(
151 open(File, write, Out, [encoding(utf8)]),
152 ( format(Out, '/* Generated manual index.~n', []),
153 format(Out, ' Do not edit.~n', []),
154 format(Out, '*/~n~n', []),
155 setup_call_cleanup(
156 b_setval(pldoc_save_index, Out),
157 do_index_manual,
158 nb_delete(pldoc_save_index))
159 ),
160 close(Out)).167index_manual :- 168 man_index(_,_,_,_,_), 169 !. 170index_manual :- 171 with_mutex(pldoc_man, 172 locked_index_manual). 173 174locked_index_manual :- 175 man_index(_,_,_,_,_), 176 !. 177locked_index_manual :- 178 cached_index_file(read, File), 179 catch(read_index(File), E, 180 print_message(warning, E)), 181 !. 182locked_index_manual :- 183 do_index_manual. 184 185do_index_manual :- 186 forall(manual_directory(Class, Symbolic, Dir), 187 index_man_directory(Dir, 188 [ class(Class), 189 symbolic(Symbolic), 190 file_errors(fail) 191 ])).
197read_index(File) :- 198 context_module(M), 199 empty_assoc(State0), 200 setup_call_cleanup( 201 open(File, read, In, [encoding(utf8)]), 202 read_man_index(In, State0, [module(M)]), 203 close(In)). 204 205read_man_index(In, State0, Options) :- 206 read_term(In, TermIn, Options), 207 ( TermIn == end_of_file 208 -> true 209 ; valid_term(TermIn, Term, State0, State), 210 assert(Term), 211 read_man_index(In, State, Options) 212 ). 213 214valid_term(TermIn, Term, State0, State) :- 215 ground(TermIn), 216 resolve_index(TermIn, Term, State0, State), 217 !. 218valid_term(Term, _, _, _) :- 219 type_error(man_index_term, Term). 220 221resolve_index(i(Object0, Summary, File0, Class, Offset), 222 man_index(Object, Summary, File, Class, Offset), 223 State0, State) :- 224 map_section(Object0, Object, File), 225 resolve_index_file(File0, File, State0, State). 226 227resolve_index_file(File0, File, State, State) :- 228 get_assoc(File0, State, File), 229 !. 230resolve_index_file(File0, File, State0, State) :- 231 absolute_file_name(File0, File, [access(read)]), 232 put_assoc(File0, State0, File, State). 233 234map_section(section(Level, Nr, Label), section(Level, Nr, Label, File), File) :- 235 !. 236map_section(Object, Object, _). 237 238cached_index_file(Access, File) :- 239 absolute_file_name(swi('doc/manindex.db'), File, 240 [ access(Access), 241 file_errors(fail) 242 ]).
250:- public 251 check_duplicate_ids/0. 252 253check_duplicate_ids :- 254 findall(Id, man_index(section(_,_,Id),_,_,_,_), Ids), 255 msort(Ids, Sorted), 256 duplicate_ids(Sorted, Duplicates), 257 ( Duplicates == [] 258 -> true 259 ; print_message(warning, pldoc(duplicate_ids(Duplicates))) 260 ). 261 262duplicate_ids([], []). 263duplicate_ids([H,H|T0], [H|D]) :- 264 !, 265 take_prefix(H,T0,T), 266 duplicate_ids(T, D). 267duplicate_ids([_|T], D) :- 268 duplicate_ids(T, D). 269 270take_prefix(H, [H|T0], T) :- 271 !, 272 take_prefix(H, T0, T). 273take_prefix(_, L, L).
Remaining Options are passed to absolute_file_name/3.
287index_man_directory(Spec, Options) :-
288 select_option(class(Class), Options, Options1, misc),
289 select_option(symbolic(Symbolic), Options1, Options2, Spec),
290 absolute_file_name(Spec, Dir,
291 [ file_type(directory),
292 access(read)
293 | Options2
294 ]),
295 atom_concat(Dir, '/*.html', Pattern),
296 expand_file_name(Pattern, Files),
297 maplist(index_man_file(Class, Symbolic), Files).306index_man_file(Class, File) :- 307 index_man_file(Class, File, File). 308index_man_file(Class, Symbolic, File) :- 309 absolute_file_name(File, Path, 310 [ access(read) 311 ]), 312 debug(pldoc(man_index), 'Indexing ~p ~p', [Class, File]), 313 open(Path, read, In, [type(binary)]), 314 dtd(html, DTD), 315 new_sgml_parser(Parser, [dtd(DTD)]), 316 set_sgml_parser(Parser, file(File)), 317 set_sgml_parser(Parser, dialect(sgml)), 318 set_sgml_parser(Parser, shorttag(false)), 319 nb_setval(pldoc_man_index, []), 320 nb_setval(pldoc_index_class, Class), 321 nb_setval(pldoc_man_dir, Symbolic), 322 call_cleanup(sgml_parse(Parser, 323 [ source(In), 324 syntax_errors(quiet), 325 call(begin, index_on_begin) 326 ]), 327 ( free_sgml_parser(Parser), 328 close(In), 329 nb_delete(pldoc_man_index), 330 nb_delete(pldoc_man_dir) 331 )).
340index_on_begin(dt, Attributes, Parser) :- 341 memberchk(class=Def, Attributes), 342 public_def(Def), 343 get_sgml_parser(Parser, charpos(Offset)), 344 get_sgml_parser(Parser, file(File)), 345 sgml_parse(Parser, 346 [ document(DT), 347 syntax_errors(quiet), 348 parse(content) 349 ]), 350 ( sub_term(element(a, AA, _), DT), 351 member(Attr, ['data-obj', id, name]), 352 memberchk(Attr=Id, AA), 353 atom_to_object(Id, PI) 354 -> true 355 ), 356 nb_getval(pldoc_man_index, DD0), 357 ( memberchk(dd(PI, File, _), DD0) 358 -> true 359 ; nb_setval(pldoc_man_index, [dd(PI, File, Offset)|DD0]) 360 ). 361index_on_begin(dd, _, Parser) :- 362 !, 363 nb_getval(pldoc_man_index, DDList0), DDList0 \== [], 364 nb_setval(pldoc_man_index, []), 365 sgml_parse(Parser, 366 [ document(DD), 367 syntax_errors(quiet), 368 parse(content) 369 ]), 370 summary(DD, Summary), 371 nb_getval(pldoc_index_class, Class), 372 reverse(DDList0, [dd(Object, File, Offset)|DDTail]), 373 assert_index(Object, Summary, File, Class, Offset), 374 forall(member(dd(Obj2,_,_), DDTail), 375 assert_index(Obj2, Summary, File, Class, Offset)). 376index_on_begin(div, Attributes, Parser) :- 377 !, 378 memberchk(class=title, Attributes), 379 get_sgml_parser(Parser, charpos(Offset)), 380 get_sgml_parser(Parser, file(File)), 381 sgml_parse(Parser, 382 [ document(DOM), 383 syntax_errors(quiet), 384 parse(content) 385 ]), 386 dom_to_text(DOM, Title), 387 nb_getval(pldoc_index_class, Class), 388 swi_local_path(File, Local), 389 assert_index(section(0, '0', Local, File), 390 Title, File, Class, Offset). 391index_on_begin(H, Attributes, Parser) :- % TBD: add class for document title. 392 heading(H, Level), 393 get_sgml_parser(Parser, charpos(Offset)), 394 get_sgml_parser(Parser, file(File)), 395 sgml_parse(Parser, 396 [ document(Doc), 397 syntax_errors(quiet), 398 parse(content) 399 ]), 400 dom_section(Doc, Nr, Title), 401 nb_getval(pldoc_index_class, Class), 402 section_id(Attributes, Title, File, ID), 403 assert_index(section(Level, Nr, ID, File), 404 Title, File, Class, Offset). 405 406assert_index(Object, Summary, File, Class, Offset) :- 407 nb_current(pldoc_save_index, Out), 408 !, 409 map_section(Object1, Object, File), 410 symbolic_file(File, Symbolic), 411 format(Out, '~k.~n', [i(Object1, Summary, Symbolic, Class, Offset)]). 412assert_index(Object, Summary, File, Class, Offset) :- 413 assertz(man_index(Object, Summary, File, Class, Offset)). 414 415symbolic_file(File, Symbolic) :- 416 nb_getval(pldoc_man_dir, Dir), 417 Dir =.. [Alias,'.'], 418 !, 419 file_base_name(File, Base), 420 Symbolic =.. [Alias,Base]. 421symbolic_file(File, File). 422 423section_id(Attributes, _Title, _, ID) :- 424 memberchk(id=ID, Attributes), 425 !. 426section_id(_, "Bibliography", _, 'sec:bibliography') :- !. 427section_id(_Attributes, Title, File, ID) :- 428 atomic_list_concat(Words, ' ', Title), 429 atomic_list_concat(Words, '_', ID0), 430 atom_concat('sec:', ID0, ID), 431 print_message(warning, pldoc(no_section_id(File, Title))). 432 433public_def(pubdef). 434public_def(multidef).
<HN> <A name="sec:nr"><span class='sec-nr'>NR</span>|_|
<span class='sec-title'>Title</span>
447dom_section(DOM, Nr, Title) :- 448 sub_term([ element(span, A1, [Nr]) | Rest ], DOM), 449 append(_Sep, [element(span, A2, TitleDOM)], Rest), 450 memberchk(class='sec-nr', A1), 451 memberchk(class='sec-title', A2), 452 !, 453 dom_to_text(TitleDOM, Title). 454dom_section(DOM, Nr, Title) :- 455 dom_to_text(DOM, Title), 456 section_number(Title, Nr, Title). 457 458section_number(Title, Nr, PlainTitle) :- 459 sub_atom(Title, 0, 1, _, Start), 460 ( char_type(Start, digit) 461 -> true 462 ; char_type(Start, upper), 463 sub_atom(Title, 1, 1, _, '.') % A., etc: Appendices 464 ), 465 sub_atom(Title, B, _, A, ' '), 466 !, 467 sub_atom(Title, 0, B, _, Nr), 468 sub_string(Title, _, A, 0, PlainTitle). 469 470heading(h1, 1). 471heading(h2, 2). 472heading(h3, 3). 473heading(h4, 4).
480summary(DOM, Summary) :- 481 phrase(summary(DOM, _), SummaryCodes0), 482 phrase(normalise_white_space(SummaryCodes), SummaryCodes0), 483 string_codes(Summary, SummaryCodes). 484 485summary([], _) --> 486 !, 487 []. 488summary(_, Done) --> 489 { Done == true }, 490 !, 491 []. 492summary([element(_,_,Content)|T], Done) --> 493 !, 494 summary(Content, Done), 495 summary(T, Done). 496summary([CDATA|T], Done) --> 497 { atom_codes(CDATA, Codes) 498 }, 499 ( { Codes = [Period|Rest], 500 code_type(Period, period), 501 space(Rest) 502 } 503 -> [ Period ], 504 { Done = true } 505 ; { append(Sentence, [C, Period|Rest], Codes), 506 code_type(Period, period), 507 \+ code_type(C, period), 508 space(Rest) 509 } 510 -> string(Sentence), 511 [C, Period], 512 { Done = true } 513 ; string(Codes), 514 summary(T, Done) 515 ). 516 517string([]) --> 518 []. 519string([H|T]) --> 520 [H], 521 string(T). 522 523space([C|_]) :- code_type(C, space), !. 524space([]).
531dom_to_text(Dom, Text) :- 532 phrase(cdata_list(Dom), CDATA), 533 with_output_to(codes(Codes0), 534 forall(member(T, CDATA), 535 write(T))), 536 phrase(normalise_white_space(Codes), Codes0), 537 string_codes(Text, Codes). 538 539cdata_list([]) --> 540 []. 541cdata_list([H|T]) --> 542 cdata(H), 543 cdata_list(T). 544 545cdata(element(_, _, Content)) --> 546 !, 547 cdata_list(Content). 548cdata(CDATA) --> 549 { atom(CDATA) }, 550 !, 551 [CDATA]. 552cdata(_) --> 553 [].
557current_man_object(Object) :-
558 index_manual,
559 man_index(Object, _, _, _, _).571man_object_property(Object, summary(Summary)) :- 572 index_manual, 573 man_index(Object, Summary, _, _, _). 574man_object_property(Object, id(File-CharNo)) :- 575 manual_object(Object, _, File, _, CharNo). 576 577swi_local_path(Path, Local) :- 578 atom(Path), 579 is_absolute_file_name(Path), 580 manual_root(RootSpec, Dir), 581 absolute_file_name(RootSpec, SWI, 582 [ file_type(directory), 583 solutions(all) 584 ]), 585 directory_file_path(SWI, ManLocal, Path), 586 !, 587 directory_file_path(Dir, ManLocal, Local). 588 589manual_root(swi_man_manual(.), 'Manual'). 590manual_root(swi_man_packages(.), 'packages').
596doc_object_identifier(Name/_, Name). 597doc_object_identifier(Name//_, Name). 598doc_object_identifier(_:Name/_, Name). 599doc_object_identifier(_:Name//_, Name). 600doc_object_identifier(M:_, M). 601doc_object_identifier(f(Name/_), Name). 602doc_object_identifier(c(Name), Name). 603doc_object_identifier(xpce(_, _, Name), Name)
Index the HTML manuals
This module pre-processes the HTML files that constitute the manual such that we can access the summary documentation of all predicates for usage in IDE tools.