36
37:- module(pldoc_wiki,
38 [ wiki_codes_to_dom/3, 39 wiki_lines_to_dom/3, 40 section_comment_header/3, 41 summary_from_lines/2, 42 indented_lines/3, 43 strip_leading_par/2, 44 autolink_extension/2, 45 autolink_file/2 46 ]). 47:- use_module(library(lists)). 48:- use_module(library(debug)). 49:- use_module(library(error)). 50:- use_module(library(pairs)). 51:- use_module(library(option)). 52:- use_module(library(debug)). 53:- use_module(library(apply)). 54:- use_module(library(dcg/basics)). 55
56:- use_module(doc_util). 57
58
69
70:- multifile
71 prolog:doc_wiki_face//2, 72 prolog:doc_url_expansion/3, 73 prolog:url_expansion_hook/3, 74 prolog:doc_autolink_extension/2. 75
76
77 80
85
86wiki_lines_to_dom(Lines, Args, HTML) :-
87 tokenize_lines(Lines, Tokens0),
88 normalise_indentation(Tokens0, Tokens),
89 wiki_structure(Tokens, -1, Pars),
90 wiki_faces(Pars, Args, HTML).
91
92
98
99wiki_codes_to_dom(Codes, Args, DOM) :-
100 indented_lines(Codes, [], Lines),
101 wiki_lines_to_dom(Lines, Args, DOM).
102
103
110
111wiki_structure([], _, []) :- !.
112wiki_structure([_-[]|T], BI, Pars) :- 113 !,
114 wiki_structure(T, BI, Pars).
115wiki_structure(Lines, _, [\tags(Tags)]) :-
116 tags(Lines, Tags),
117 !.
118wiki_structure(Lines, BI, [P1|PL]) :-
119 take_block(Lines, BI, P1, RestLines),
120 wiki_structure(RestLines, BI, PL).
121
126
127take_block([_-[]|Lines], BaseIndent, Block, Rest) :-
128 !,
129 take_block(Lines, BaseIndent, Block, Rest).
130take_block([N-_|_], BaseIndent, _, _) :-
131 N < BaseIndent,
132 !,
133 fail. 134take_block(Lines, BaseIndent, List, Rest) :-
135 list_item(Lines, Type, Indent, LI, LIT, Rest0),
136 !,
137 Indent > BaseIndent,
138 rest_list(Rest0, Type, Indent, LIT, [], Rest),
139 List0 =.. [Type, LI],
140 ( ul_to_dl(List0, List)
141 -> true
142 ; List0 = dl(Items)
143 -> List = dl(class=wiki, Items)
144 ; List = List0
145 ).
146take_block([N-['|'|RL1]|LT], _, Table, Rest) :-
147 phrase(row(R0), RL1),
148 take_table(LT, N, R0, Table, Rest),
149 !.
150take_block([0-[-,-|More]|LT], _, Block, LT) :- 151 maplist(=(-), More),
152 !,
153 Block = hr([]).
154take_block([_-Line|LT], _, Block, LT) :- 155 ruler(Line),
156 !,
157 Block = hr([]).
158take_block([_-[@|_]], _, _, _) :- 159 !,
160 fail.
161take_block(Lines, _BaseIndent, Section, RestLines) :-
162 section_header(Lines, Section, RestLines),
163 !.
164take_block([_-Verb|Lines], _, Verb, Lines) :-
165 verbatim_term(Verb),
166 !.
167take_block([I-L1|LT], BaseIndent, Elem, Rest) :-
168 !,
169 append(L1, PT, Par),
170 rest_par(LT, PT, I, BaseIndent, MaxI, Rest),
171 ( MaxI >= BaseIndent+16
172 -> Elem = center(Par)
173 ; phrase(blockquote(BQ), Par)
174 -> Elem = blockquote(BQ)
175 ; Elem = p(Par)
176 ).
177take_block([Verb|Lines], _, Verb, Lines).
178
179blockquote(Clean) -->
180 [>, ' '],
181 bq_lines(Clean).
182
183bq_lines([' '|Par]) -->
184 ['\n'], !, [>,' '],
185 bq_lines(Par).
186bq_lines([H|T]) -->
187 [H],
188 bq_lines(T).
189bq_lines([]) -->
190 [].
191
192
196
197ruler([C0|Line]) :-
198 rule_char(C0),
199 phrase(ruler(C0, 1), Line).
200
201ruler(C, N) --> [C], !, { N2 is N+1 }, ruler(C, N2).
202ruler(C, N) --> [' '], !, ruler(C, N).
203ruler(_, N) --> { N >= 3 }.
204
205rule_char('-').
206rule_char('_').
207rule_char('*').
208
216
217list_item([Indent-Line|LT], Type, Indent, Items, ItemT, Rest) :-
218 !,
219 list_item_prefix(Type, Line, L1),
220 ( Type == dl
221 -> split_dt(L1, DT0, DD1),
222 append(DD1, LIT, DD),
223 strip_ws_tokens(DT0, DT),
224 Items = [dt(DT),dd(DD)|ItemT]
225 ; append(L1, LIT, LI0),
226 Items = [li(LI0)|ItemT]
227 ),
228 rest_list_item(LT, Type, Indent, LIT, Rest).
229
233
234rest_list_item(Lines, _Type, Indent, RestItem, RestLines) :-
235 take_blocks_at_indent(Lines, Indent, Blocks, RestLines),
236 ( Blocks = [p(Par)|MoreBlocks]
237 -> append(['\n'|Par], MoreBlocks, RestItem)
238 ; RestItem = Blocks
239 ).
240
244
245take_blocks_at_indent(Lines, _, [], Lines) :-
246 skip_empty_lines(Lines, Lines1),
247 section_header(Lines1, _, _),
248 !.
249take_blocks_at_indent(Lines, N, [Block|RestBlocks], RestLines) :-
250 take_block(Lines, N, Block, Rest0),
251 !,
252 take_blocks_at_indent(Rest0, N, RestBlocks, RestLines).
253take_blocks_at_indent(Lines, _, [], Lines).
254
255
258
259rest_list(Lines, Type, N, Items, IT, Rest) :-
260 skip_empty_lines(Lines, Lines1),
261 list_item(Lines1, Type, N, Items, IT0, Rest0),
262 !,
263 rest_list(Rest0, Type, N, IT0, IT, Rest).
264rest_list(Rest, _, _, IT, IT, Rest).
265
267
268list_item_prefix(ul, [*, ' '|T], T) :- !.
269list_item_prefix(ul, [-, ' '|T], T) :- !.
270list_item_prefix(dl, [$, ' '|T], T) :-
271 split_dt(T, _, _),
272 !.
273list_item_prefix(ol, [w(N), '.', ' '|T], T) :-
274 atom_codes(N, [D]),
275 between(0'0, 0'9, D).
276
281
282split_dt(In, DT, []) :-
283 append(DT, [':'], In),
284 !.
285split_dt(In, DT, Rest) :-
286 append(DT, [':'|Rest0], In),
287 ( Rest0 == []
288 -> Rest = []
289 ; Rest0 = [' '|Rest]
290 ),
291 !.
292
293
300
301ul_to_dl(ul(Items), Description) :-
302 term_items(Items, DLItems, []),
303 ( terms_to_predicate_includes(DLItems, Preds)
304 -> Description = dl(class(predicates), Preds)
305 ; member(dd(DD), DLItems), DD \== []
306 -> Description = dl(class(termlist), DLItems)
307 ).
308
309term_items([], T, T).
310term_items([LI|LIs], DLItems, Tail) :-
311 term_item(LI, DLItems, Tail1),
312 term_items(LIs, Tail1, Tail).
313
320
321term_item(li(Tokens),
322 [ dt(class=term, \term(Text, Term, Bindings)),
323 dd(Descr)
324 | Tail
325 ], Tail) :-
326 ( ( append(TermTokens, ['\n'|Descr], Tokens)
327 -> true
328 ; TermTokens = Tokens,
329 Descr = []
330 )
331 -> with_output_to(string(Tmp),
332 ( forall(member(T, TermTokens),
333 write_token(T)),
334 write(' .\n'))),
335 E = error(_,_),
336 catch(setup_call_cleanup(
337 open_string(Tmp, In),
338 ( read_dt_term(In, Term, Bindings),
339 read_dt_term(In, end_of_file, []),
340 atom_string(Text, Tmp)
341 ),
342 close(In)),
343 E, fail)
344 ).
345
346write_token(w(X)) :-
347 !,
348 write(X).
349write_token(X) :-
350 write(X).
351
352read_dt_term(In, Term, Bindings) :-
353 read_term(In, Term,
354 [ variable_names(Bindings),
355 module(pldoc_modes)
356 ]).
357
358terms_to_predicate_includes([], []).
359terms_to_predicate_includes([dt(class=term, \term(_, [[PI]], [])), dd([])|T0],
360 [\include(PI, predicate, [])|T]) :-
361 is_pi(PI),
362 terms_to_predicate_includes(T0, T).
363
364is_pi(Name/Arity) :-
365 atom(Name),
366 integer(Arity),
367 between(0, 20, Arity).
368is_pi(Name//Arity) :-
369 atom(Name),
370 integer(Arity),
371 between(0, 20, Arity).
372
373
375
376row([C0|CL]) -->
377 cell(C0),
378 !,
379 row(CL).
380row([]) -->
381 [].
382
383cell(td(C)) -->
384 face_tokens(C0),
385 ['|'],
386 !,
387 { strip_ws_tokens(C0, C)
388 }.
389
390face_tokens([]) -->
391 [].
392face_tokens(Tokens) -->
393 face_token(H), 394 token('|'),
395 face_tokens(Face),
396 token('|'),
397 face_token(H),
398 !,
399 { append([[H,'|'], Face, ['|', H], Rest], Tokens) },
400 face_tokens(Rest).
401face_tokens([H|T]) -->
402 token(H),
403 face_tokens(T).
404
405face_token(=) --> [=].
406face_token(*) --> [*].
407face_token('_') --> ['_'].
408
409take_table(Lines, Indent, Row0, Table, Rest) :-
410 rest_table(Lines, Indent, Rows, Rest),
411 ( Rows = [align(Align)|Rows1]
412 -> maplist(align_row(Align), Rows1, Rows2),
413 ( maplist(=(td([])), Row0) 414 -> Table = table(class=wiki, Rows2)
415 ; maplist(td_to_th, Row0, Header),
416 Table = table(class=wiki, [tr(Header)|Rows2])
417 )
418 ; Table = table(class=wiki, [tr(Row0)|Rows])
419 ).
420
421td_to_th(td(X), th(X)) :- !.
422td_to_th(X, X).
423
424align_row(Align, tr(Row0), tr(Row)) :-
425 align_cells(Align, Row0, Row).
426
427align_cells([Align|AT], [Cell0|T0], [Cell|T]) :-
428 align_cell(Align, Cell0, Cell),
429 align_cells(AT, T0, T).
430align_cells(_, Cells, Cells).
431
432align_cell(Align, td(Content), td(class=Align, Content)).
433
435
436rest_table([N-Line|LT], N, [align(Align)|RL], Rest) :-
437 phrase(column_alignment(Align), Line),
438 !,
439 rest_table2(LT, N, RL, Rest).
440rest_table(Lines, N, RL, Rest) :-
441 rest_table2(Lines, N, RL, Rest).
442
443rest_table2([N-['|'|RL1]|LT], N, [tr(R0)|RL], Rest) :-
444 !,
445 phrase(row(R0), RL1),
446 rest_table2(LT, N, RL, Rest).
447rest_table2(Rest, _, [], Rest).
448
452
453column_alignment([H|T]) -->
454 ['|'],
455 ( colspec(H)
456 -> column_alignment(T)
457 ; {T=[]}
458 ).
459
460colspec(Align) -->
461 ws_tokens, [':'], dashes3,
462 ( [':']
463 -> {Align = center}
464 ; {Align = left}
465 ),
466 ws_tokens.
467colspec(Align) -->
468 ws_tokens, dashes3,
469 ( [':']
470 -> {Align = right}
471 ; {Align = left}
472 ),
473 ws_tokens.
474
475dashes3 -->
476 [-,-,-],
477 dashes.
478
479dashes --> [-], !, dashes.
480dashes --> [].
481
482ws_tokens --> [' '], !, ws_tokens.
483ws_tokens --> [].
484
492
493rest_par([], [], BI, MaxI0, MaxI, []) :-
494 !,
495 MaxI is max(BI, MaxI0).
496rest_par([_-[]|Rest], [], _, MaxI, MaxI, Rest) :- !.
497rest_par(Lines, [], _, MaxI, MaxI, Lines) :-
498 Lines = [_-Verb|_],
499 verbatim_term(Verb),
500 !.
501rest_par([I-L|Rest], [], _, MaxI, MaxI, [I-L|Rest]) :-
502 list_item_prefix(_, L, _),
503 !.
504rest_par([I-L1|LT], ['\n'|Par], BI, MaxI0, MaxI, Rest) :-
505 append(L1, PT, Par),
506 MaxI1 is max(I, MaxI0),
507 rest_par(LT, PT, BI, MaxI1, MaxI, Rest).
508
509
513
([_-L1|LT], Section, LT) :-
515 twiki_section_line(L1, Section),
516 !.
517section_header([0-L1|LT], Section, LT) :-
518 md_section_line(L1, Section),
519 !.
520section_header([_-L1,0-L2|LT], Section, LT) :-
521 md_section_line(L1, L2, Section),
522 !.
523
529
530twiki_section_line([-,-,-|Rest], Section) :-
531 plusses(Rest, Section).
532
533plusses([+, ' '|Rest], h1(Attrs, Content)) :-
534 hdr_attributes(Rest, Attrs, Content).
535plusses([+, +, ' '|Rest], h2(Attrs, Content)) :-
536 hdr_attributes(Rest, Attrs, Content).
537plusses([+, +, +, ' '|Rest], h3(Attrs, Content)) :-
538 hdr_attributes(Rest, Attrs, Content).
539plusses([+, +, +, +, ' '|Rest], h4(Attrs, Content)) :-
540 hdr_attributes(Rest, Attrs, Content).
541
542hdr_attributes(List, Attrs, Content) :-
543 strip_leading_ws(List, List2),
544 ( List2 = ['[',w(Name),']'|List3]
545 -> strip_ws_tokens(List3, Content),
546 Attrs = [class(wiki), id(Name)]
547 ; Attrs = class(wiki),
548 strip_ws_tokens(List, Content)
549 ).
550
554
555md_section_line([#, ' '|Rest], h1(Attrs, Content)) :-
556 md_section_attributes(Rest, Attrs, Content).
557md_section_line([#, #, ' '|Rest], h2(Attrs, Content)) :-
558 md_section_attributes(Rest, Attrs, Content).
559md_section_line([#, #, #, ' '|Rest], h3(Attrs, Content)) :-
560 md_section_attributes(Rest, Attrs, Content).
561md_section_line([#, #, #, #, ' '|Rest], h4(Attrs, Content)) :-
562 md_section_attributes(Rest, Attrs, Content).
563
564md_section_attributes(List, Attrs, Content) :-
565 phrase((tokens(Content), [' '], section_label(Label)), List),
566 !,
567 Attrs = [class(wiki), id(Label)].
568md_section_attributes(Content, Attrs, Content) :-
569 Attrs = [class(wiki)].
570
571section_label(Label) -->
572 [ '{', '#', w(Name) ],
573 label_conts(Cont), ['}'],
574 !,
575 { atomic_list_concat([Name|Cont], Label) }.
576
577label_conts([H|T]) --> label_cont(H), !, label_conts(T).
578label_conts([]) --> [].
579
580label_cont(-) --> [-].
581label_cont(Name) --> [w(Name)].
582
583
584md_section_line(Line1, Line2, Header) :-
585 Line1 \== [],
586 section_underline(Line2, Type),
587 is_list(Line1),
588 phrase(wiki_words(_), Line1), 589 !,
590 ( phrase(labeled_section_line(Title, Attrs), Line1)
591 -> true
592 ; Title = Line1,
593 Attrs = []
594 ),
595 Header =.. [Type, [class(wiki)|Attrs], Title].
596
597section_underline([=,=,=|T], h1) :-
598 maplist(=(=), T),
599 !.
600section_underline([-,-,-|T], h2) :-
601 maplist(=(-), T),
602 !.
603
604labeled_section_line(Title, Attrs) -->
605 tokens(Title), [' '], section_label(Label),
606 !,
607 { Attrs = [id(Label)] }.
608
609
614
615strip_ws_tokens([' '|T0], T) :-
616 !,
617 strip_ws_tokens(T0, T).
618strip_ws_tokens(L0, L) :-
619 append(L, [' '], L0),
620 !.
621strip_ws_tokens(L, L).
622
623
627
628strip_leading_ws([' '|T], T) :- !.
629strip_leading_ws(T, T).
630
631
632 635
640
641tags(Lines, Tags) :-
642 collect_tags(Lines, Tags0),
643 keysort(Tags0, Tags1),
644 pairs_values(Tags1, Tags2),
645 combine_tags(Tags2, Tags).
646
655
656collect_tags([], []).
657collect_tags([Indent-[@,String|L0]|Lines], [Order-tag(Tag,Value)|Tags]) :-
658 tag_name(String, Tag, Order),
659 !,
660 strip_leading_ws(L0, L),
661 rest_tag(Lines, Indent, VT, RestLines),
662 normalise_indentation(VT, VT1),
663 wiki_structure([0-L|VT1], -1, Value0),
664 strip_leading_par(Value0, Value),
665 collect_tags(RestLines, Tags).
666
667
671
672tag_name(w(Name), Tag, Order) :-
673 ( renamed_tag(Name, Tag, Level),
674 tag_order(Tag, Order)
675 -> print_message(Level, pldoc(deprecated_tag(Name, Tag)))
676 ; tag_order(Name, Order)
677 -> Tag = Name
678 ; print_message(warning, pldoc(unknown_tag(Name))),
679 fail
680 ).
681
682
683rest_tag([], _, [], []) :- !.
684rest_tag(Lines, Indent, [], Lines) :-
685 Lines = [Indent-[@,Word|_]|_],
686 tag_name(Word, _, _),
687 !.
688rest_tag([L|Lines0], Indent, [L|VT], Lines) :-
689 rest_tag(Lines0, Indent, VT, Lines).
690
691
695
696renamed_tag(exception, throws, warning).
697renamed_tag(param, arg, silent).
698
699
705
706:- multifile
707 pldoc:tag_order/2. 708
709tag_order(Tag, Order) :-
710 pldoc:tag_order(Tag, Order),
711 !.
712tag_order(arg, 100).
713tag_order(error, 200). 714tag_order(throws, 300).
715tag_order(author, 400).
716tag_order(version, 500).
717tag_order(see, 600).
718tag_order(deprecated, 700).
719tag_order(compat, 800). 720tag_order(copyright, 900).
721tag_order(license, 1000).
722tag_order(bug, 1100).
723tag_order(tbd, 1200).
724
733
734combine_tags([], []).
735combine_tags([tag(arg, V1)|T0], [\args([P1|PL])|Tags]) :-
736 !,
737 arg_tag(V1, P1),
738 arg_tags(T0, PL, T1),
739 combine_tags(T1, Tags).
740combine_tags([tag(Tag,V0)|T0], [\tag(Tag, [V0|Vs])|T]) :-
741 same_tag(Tag, T0, T1, Vs),
742 combine_tags(T1, T).
743
744arg_tag([PT|Descr0], arg(PN, Descr)) :-
745 word_of(PT, PN),
746 strip_leading_ws(Descr0, Descr).
747
748word_of(w(W), W) :- !. 749word_of(W, W).
750
751arg_tags([tag(arg, V1)|T0], [P1|PL], T) :-
752 !,
753 arg_tag(V1, P1),
754 arg_tags(T0, PL, T).
755arg_tags(T, [], T).
756
757same_tag(Tag, [tag(Tag, V)|T0], T, [V|Vs]) :-
758 !,
759 same_tag(Tag, T0, T, Vs).
760same_tag(_, L, L, []).
761
762
763 766
771
772wiki_faces([dt(Class, \term(Text, Term, Bindings)), dd(Descr0)|T0],
773 ArgNames,
774 [dt(Class, \term(Text, Term, Bindings)), dd(Descr)|T]) :-
775 !,
776 varnames(Bindings, VarNames, ArgNames),
777 wiki_faces(Descr0, VarNames, Descr),
778 wiki_faces(T0, ArgNames, T).
779wiki_faces(DOM0, ArgNames, DOM) :-
780 structure_term(DOM0, Functor, Content0),
781 !,
782 wiki_faces_list(Content0, ArgNames, Content),
783 structure_term(DOM, Functor, Content).
784wiki_faces(Verb, _, Verb) :-
785 verbatim_term(Verb),
786 !.
787wiki_faces(Content0, ArgNames, Content) :-
788 assertion(is_list(Content0)),
789 phrase(wiki_faces(Content, ArgNames), Content0),
790 !.
791
792varnames([], List, List).
793varnames([Name=_|T0], [Name|T], List) :-
794 varnames(T0, T, List).
795
796wiki_faces_list([], _, []).
797wiki_faces_list([H0|T0], Args, [H|T]) :-
798 wiki_faces(H0, Args, H),
799 wiki_faces_list(T0, Args, T).
800
806
807structure_term(\tags(Tags), tags, [Tags]) :- !.
808structure_term(\args(Params), args, [Params]) :- !.
809structure_term(arg(Name,Descr), arg(Name), [Descr]) :- !.
810structure_term(\tag(Name,Value), tag(Name), [Value]) :- !.
811structure_term(\include(What,Type,Opts), include(What,Type,Opts), []) :- !.
812structure_term(dl(Att, Args), dl(Att), [Args]) :- !.
813structure_term(dt(Att, Args), dt(Att), [Args]) :- !.
814structure_term(table(Att, Args), table(Att), [Args]) :- !.
815structure_term(td(Att, Args), td(Att), [Args]) :- !.
816structure_term(h1(Att, Args), h1(Att), [Args]) :- !.
817structure_term(h2(Att, Args), h2(Att), [Args]) :- !.
818structure_term(h3(Att, Args), h3(Att), [Args]) :- !.
819structure_term(h4(Att, Args), h4(Att), [Args]) :- !.
820structure_term(hr(Att), hr(Att), []) :- !.
821structure_term(p(Args), p, [Args]) :- !.
822structure_term(Term, Functor, Args) :-
823 structure_term_any(Term, Functor, Args).
824
825structure_term(Term) :-
826 structure_term_any(Term, _Functor, _Args).
827
828structure_term_any(Term, Functor, Args) :-
829 functor(Term, Functor, 1),
830 structure_tag(Functor),
831 !,
832 Term =.. [Functor|Args].
833
834structure_tag(ul).
835structure_tag(ol).
836structure_tag(dl).
837structure_tag(li).
838structure_tag(dt).
839structure_tag(dd).
840structure_tag(table).
841structure_tag(tr).
842structure_tag(td).
843structure_tag(th).
844structure_tag(blockquote).
845structure_tag(center).
846
847
851
852verbatim_term(pre(_,_)).
853verbatim_term(\term(_,_,_)).
854
859
860:- meta_predicate matches(2, -, -, ?, ?). 861
862matches(Goal, Input, Last, List, Rest) :-
863 call(Goal, List, Rest),
864 input(List, Rest, Input, Last).
865
866input([H|T0], Rest, Input, Last) :-
867 ( T0 == Rest
868 -> Input = [H],
869 Last = H
870 ; Input = [H|T],
871 input(T0, Rest, T, Last)
872 ).
873
874
883
884wiki_faces(WithFaces, ArgNames, List, Rest) :-
885 default_faces_options(Options),
886 catch(wiki_faces(WithFaces, ArgNames, Options, List, Rest),
887 pldoc(depth_limit),
888 failed_faces(WithFaces, List, Rest)).
889
890default_faces_options(_{depth:5}).
891
892failed_faces(WithFaces) -->
893 { debug(markdown(overflow), 'Depth limit exceeded', []) },
894 wiki_words(WithFaces).
895
896wiki_faces([EmphTerm|T], ArgNames, Options) -->
897 emphasis_seq(EmphTerm, ArgNames, Options),
898 !,
899 wiki_faces_int(T, ArgNames).
900wiki_faces(Faces, ArgNames, Options) -->
901 wiki_faces_int(Faces, ArgNames, Options).
902
903wiki_faces_int(WithFaces, ArgNames) -->
904 { default_faces_options(Options)
905 },
906 wiki_faces_int(WithFaces, ArgNames, Options).
907
908wiki_faces_int([], _, _) -->
909 [].
910wiki_faces_int(List, ArgNames, Options) -->
911 wiki_face(H, ArgNames, Options),
912 !,
913 { is_list(H)
914 -> append(H, T, List)
915 ; List = [H|T]
916 },
917 wiki_faces(T, ArgNames, Options).
918wiki_faces_int([Before,EmphTerm|T], ArgNames, Options) -->
919 emphasis_before(Before),
920 emphasis_seq(EmphTerm, ArgNames, Options),
921 !,
922 wiki_faces_int(T, ArgNames, Options).
923wiki_faces_int([H|T], ArgNames, Options) -->
924 wiki_face_simple(H, ArgNames, Options),
925 !,
926 wiki_faces_int(T, ArgNames, Options).
927
928next_level(Options0, Options) -->
929 { succ(NewDepth, Options0.depth)
930 -> Options = Options0.put(depth, NewDepth)
931 ; throw(pldoc(depth_limit))
932 }.
933
950
951wiki_face(Out, Args, _) -->
952 prolog:doc_wiki_face(Out, Args),
953 !.
954wiki_face(var(Arg), ArgNames, _) -->
955 [w(Arg)],
956 { memberchk(Arg, ArgNames)
957 },
958 !.
959wiki_face(b(Bold), ArgNames, Options) -->
960 [*,'|'], string(Tokens), ['|',*],
961 !,
962 { phrase(wiki_faces(Bold, ArgNames, Options), Tokens) }.
963wiki_face(i(Italic), ArgNames, Options) -->
964 ['_','|'], string(Tokens), ['|','_'],
965 !,
966 { phrase(wiki_faces(Italic, ArgNames, Options), Tokens) }.
967wiki_face(code(Code), _, _) -->
968 [=], eq_code_words(Words), [=],
969 !,
970 { atomic_list_concat(Words, Code) }.
971wiki_face(code(Code), _, _) -->
972 [=,'|'], wiki_words(Code), ['|',=],
973 !.
974wiki_face(PredRef, _, _) -->
975 ['`'], take_predref(PredRef), ['`'],
976 !.
977wiki_face(\nopredref(Pred), _, _) -->
978 ['`', '`'], take_predref(\predref(Pred)), ['`', '`'],
979 !.
980wiki_face([flag, ' ', \flagref(Flag)], _, _) -->
981 [ w('flag'), ' ', '`', w(Flag), '`' ],
982 { current_prolog_flag(Flag, _) },
983 !.
984wiki_face(code(Code), _, _) -->
985 ['`','`'], wiki_words(Code), ['`','`'],
986 !.
987wiki_face(Code, _, _) -->
988 ( ['`'], code_words(Words), ['`']
989 -> { atomic_list_concat(Words, Text),
990 E = error(_,_),
991 catch(atom_to_term(Text, Term, Vars), E, fail),
992 !,
993 code_face(Text, Term, Vars, Code)
994 }
995 ).
996wiki_face(Face, _, Options) -->
997 [ w(Name) ], arg_list(List),
998 { atomic_list_concat([Name|List], Text),
999 E = error(_,_),
1000 catch(atom_to_term(Text, Term, Vars), E, fail),
1001 term_face(Text, Term, Vars, Face, Options)
1002 },
1003 !.
1004wiki_face(br([]), _, _) -->
1005 [<,w(br),>,'\n'], !.
1006wiki_face(br([]), _, _) -->
1007 [<,w(br),/,>,'\n'], !.
1008 1009wiki_face(_, _, Options) -->
1010 { Options.get(link) == false,
1011 !,
1012 fail
1013 }.
1014wiki_face(PredRef, _, _) -->
1015 take_predref(PredRef),
1016 !.
1017wiki_face(\cite(Citations), _, _) -->
1018 ['['], citations(Citations), [']'].
1019wiki_face(\include(Name, Type, Options), _, _) -->
1020 ['[','['], file_name(Base, Ext), [']',']'],
1021 { autolink_extension(Ext, Type),
1022 !,
1023 file_name_extension(Base, Ext, Name),
1024 resolve_file(Name, Options, [])
1025 },
1026 !.
1027wiki_face(\include(Name, Type, [caption(Caption)|Options]), _, _) -->
1028 ( ['!','['], tokens(100, Caption), [']','(']
1029 -> file_name(Base, Ext), [')'],
1030 { autolink_extension(Ext, Type),
1031 !,
1032 file_name_extension(Base, Ext, Name),
1033 resolve_file(Name, Options, [])
1034 }
1035 ),
1036 !.
1037wiki_face(Link, ArgNames, Options) --> 1038 ( ['[','['], wiki_label(Label, ArgNames, Options), [']','[']
1039 -> wiki_link(Link, [label(Label), relative(true), end(']')]),
1040 [']',']'], !
1041 ).
1042wiki_face(Link, ArgNames, Options) --> 1043 ( ['['], wiki_label(Label, ArgNames, Options), [']','(']
1044 -> wiki_link(Link, [label(Label), relative(true), end(')')]),
1045 [')'], !
1046 ).
1047wiki_face(Link, _ArgNames, _) -->
1048 wiki_link(Link, []),
1049 !.
1050
1051wiki_label(Label, _ArgNames, _Options) -->
1052 image_label(Label).
1053wiki_label(Label, ArgNames, Options) -->
1054 next_level(Options, NOptions),
1055 limit(40, wiki_faces(Label, ArgNames, NOptions.put(link,false))).
1056
1060
1061wiki_face_simple(Word, _, _) -->
1062 [ w(Word) ],
1063 !.
1064wiki_face_simple(SpaceOrPunct, _, _) -->
1065 [ SpaceOrPunct ],
1066 { atomic(SpaceOrPunct) },
1067 !.
1068wiki_face_simple(FT, ArgNames, _) -->
1069 [Structure],
1070 { wiki_faces(Structure, ArgNames, FT)
1071 }.
1072
1073wiki_words([]) --> [].
1074wiki_words([Word|T]) --> [w(Word)], !, wiki_words(T).
1075wiki_words([Punct|T]) --> [Punct], {atomic(Punct)}, wiki_words(T).
1076
1081
1082code_words([]) --> [].
1083code_words([Word|T]) --> [w(Word)], code_words(T).
1084code_words(CodeL) --> ['`','`'], {CodeL = ['`'|T]}, code_words(T).
1085code_words([Punct|T]) --> [Punct], {atomic(Punct)}, code_words(T).
1086
1095
1096eq_code_words([Word]) -->
1097 [ w(Word) ].
1098eq_code_words([Word|T]) -->
1099 [ w(Word) ], eq_code_internals(T, [End]), [w(End)].
1100
1101eq_code_internals(T, T) --> [].
1102eq_code_internals([H|T], Tail) -->
1103 eq_code_internal(H),
1104 eq_code_internals(T, Tail).
1105
1106eq_code_internal(Word) -->
1107 [w(Word)].
1108eq_code_internal(Punct) -->
1109 [Punct],
1110 { eq_code_internal_punct(Punct) }.
1111
1112eq_code_internal_punct('.').
1113eq_code_internal_punct('-').
1114eq_code_internal_punct(':').
1115eq_code_internal_punct('/').
1116
1117
1123
1124code_face(Text, Var, _, Code) :-
1125 var(Var),
1126 !,
1127 Code = var(Text).
1128code_face(Text, _, _, code(Text)).
1129
1130
1134
1135emphasis_seq(EmphTerm, ArgNames, Options) -->
1136 emphasis_start(C),
1137 next_level(Options, NOptions),
1138 matches(limit(100, wiki_faces(Emph, ArgNames, NOptions)), Input, Last),
1139 emphasis_end(C),
1140 { emph_markdown(Last, Input),
1141 emphasis_term(C, Emph, EmphTerm)
1142 },
1143 !.
1144
1145
1152
1153emphasis_term('_', Term, i(Term)).
1154emphasis_term('*', Term, b(Term)).
1155emphasis_term('__', Term, strong(Term)).
1156emphasis_term('**', Term, strong(Term)).
1157
1158emph_markdown(_, [w(_)]) :- !.
1159emph_markdown(Last, Tokens) :-
1160 \+ emphasis_after_sep(Last),
1161 E = error(_,_),
1162 catch(b_getval(pldoc_object, Obj), E, Obj = '??'),
1163 debug(markdown(emphasis), '~q: additionally emphasis: ~p',
1164 [Obj, Tokens]).
1165
1166emphasis_before(Before) -->
1167 [Before],
1168 { emphasis_start_sep(Before) }.
1169
1170emphasis_start_sep('\n').
1171emphasis_start_sep(' ').
1172emphasis_start_sep('<').
1173emphasis_start_sep('{').
1174emphasis_start_sep('(').
1175emphasis_start_sep('[').
1176emphasis_start_sep(',').
1177emphasis_start_sep(':').
1178emphasis_start_sep(';').
1179
1180emphasis_start(Which), [w(Word)] -->
1181 emphasis(Which),
1182 [w(Word)].
1183
1184emphasis(**) --> [*, *].
1185emphasis(*) --> [*].
1186emphasis('__') --> ['_', '_'].
1187emphasis('_') --> ['_'].
1188
1189emphasis_end(Which), [After] -->
1190 emphasis(Which),
1191 [ After ],
1192 !,
1193 { emphasis_close_sep(After) -> true }.
1194emphasis_end(Which) -->
1195 emphasis(Which).
1196
1198
1199emphasis_after_sep('\n').
1200emphasis_after_sep(' ').
1201emphasis_after_sep('(').
1202emphasis_after_sep('[').
1203emphasis_after_sep('<').
1204emphasis_after_sep('=').
1205emphasis_after_sep('+').
1206emphasis_after_sep('\\').
1207emphasis_after_sep('@').
1208
1209emphasis_close_sep('\n'). 1210emphasis_close_sep(' '). 1211emphasis_close_sep(','). 1212emphasis_close_sep('.').
1213emphasis_close_sep('!').
1214emphasis_close_sep('?').
1215emphasis_close_sep(':').
1216emphasis_close_sep(';').
1217emphasis_close_sep(']'). 1218emphasis_close_sep(')'). 1219emphasis_close_sep('}'). 1220emphasis_close_sep(Token) :-
1221 structure_term(Token).
1222
1223
1231
1232arg_list(['('|T]) -->
1233 ['('], arg_list_close(T, 1).
1234
1235arg_list_close(Tokens, Depth) -->
1236 [')'],
1237 !,
1238 ( { Depth == 1 }
1239 -> { Tokens = [')'] }
1240 ; { Depth > 1 }
1241 -> { Tokens = [')'|More],
1242 NewDepth is Depth - 1
1243 },
1244 arg_list_close(More, NewDepth)
1245 ).
1246arg_list_close(['('|T], Depth) -->
1247 ['('], { NewDepth is Depth+1 },
1248 arg_list_close(T, NewDepth).
1249arg_list_close([H|T], Depth) -->
1250 [w(H)],
1251 !,
1252 arg_list_close(T, Depth).
1253arg_list_close([H|T], Depth) -->
1254 [H],
1255 arg_list_close(T, Depth).
1256
1257
1263
1264term_face(_Text, Term, _Vars, \file(Name, FileOptions), Options) :-
1265 ground(Term),
1266 compound(Term),
1267 compound_name_arity(Term, Alias, 1),
1268 user:file_search_path(Alias, _),
1269 existing_file(Term, FileOptions, [], Options),
1270 !,
1271 format(atom(Name), '~q', [Term]).
1272term_face(Text, Term, Vars, Face, _Options) :-
1273 code_face(Text, Term, Vars, Face).
1274
1275untag([], []).
1276untag([w(W)|T0], [W|T]) :-
1277 !,
1278 untag(T0, T).
1279untag([H|T0], [H|T]) :-
1280 untag(T0, T).
1281
1285
1286image_label(\include(Name, image, Options)) -->
1287 file_name(Base, Ext),
1288 { autolink_extension(Ext, image),
1289 file_name_extension(Base, Ext, Name),
1290 resolve_file(Name, Options, RestOptions)
1291 },
1292 file_options(RestOptions).
1293
1294
1295take_predref(\predref(Name/Arity)) -->
1296 [ w(Name), '/' ], arity(Arity),
1297 { functor_name(Name)
1298 }.
1299take_predref(\predref(Module:(Name/Arity))) -->
1300 [ w(Module), ':', w(Name), '/' ], arity(Arity),
1301 { functor_name(Name)
1302 }.
1303take_predref(\predref(Name/Arity)) -->
1304 prolog_symbol_char(S0),
1305 symbol_string(SRest), [ '/' ], arity(Arity),
1306 !,
1307 { atom_chars(Name, [S0|SRest])
1308 }.
1309take_predref(\predref(Name//Arity)) -->
1310 [ w(Name), '/', '/' ], arity(Arity),
1311 { functor_name(Name)
1312 }.
1313take_predref(\predref(Module:(Name//Arity))) -->
1314 [ w(Module), ':', w(Name), '/', '/' ], arity(Arity),
1315 { functor_name(Name)
1316 }.
1317
1322
1323file_options(Options) -->
1324 [;], nv_pairs(Options),
1325 !.
1326file_options([]) -->
1327 [].
1328
1329nv_pairs([H|T]) -->
1330 nv_pair(H),
1331 ( [',']
1332 -> nv_pairs(T)
1333 ; {T=[]}
1334 ).
1335
1336nv_pair(Option) -->
1337 [ w(Name), =,'"'], tokens(Tokens), ['"'],
1338 !,
1339 { untag(Tokens, Atoms),
1340 atomic_list_concat(Atoms, Value0),
1341 ( atom_number(Value0, Value)
1342 -> true
1343 ; Value = Value0
1344 ),
1345 Option =.. [Name,Value]
1346 }.
1347
1348
1361
1362:- multifile
1363 user:url_path/2. 1364
1365wiki_link(\file(Name, FileOptions), Options) -->
1366 file_name(Base, Ext),
1367 { file_name_extension(Base, Ext, Name),
1368 ( autolink_file(Name, _)
1369 ; autolink_extension(Ext, _)
1370 ),
1371 !,
1372 resolve_file(Name, FileOptions, Options)
1373 }.
1374wiki_link(\file(Name, FileOptions), Options) -->
1375 [w(Name)],
1376 { autolink_file(Name, _),
1377 !,
1378 resolve_file(Name, FileOptions, Options)
1379 },
1380 !.
1381wiki_link(a(href(Ref), Label), Options) -->
1382 [ w(Prot),:,/,/], { url_protocol(Prot) },
1383 { option(end(End), Options, space)
1384 },
1385 tokens_no_whitespace(Rest), peek_end_url(End),
1386 !,
1387 { atomic_list_concat([Prot, :,/,/ | Rest], Ref),
1388 option(label(Label), Options, Ref)
1389 }.
1390wiki_link(a(href(Ref), Label), _Options) -->
1391 [<, w(Alias), :],
1392 tokens_no_whitespace(Rest), [>],
1393 { Term = (Alias:Rest),
1394 prolog:url_expansion_hook(Term, Ref, Label), !
1395 }.
1396wiki_link(a(href(Ref), Label), Options) -->
1397 [<, w(Alias), :],
1398 { user:url_path(Alias, _)
1399 },
1400 tokens_no_whitespace(Rest), [>],
1401 { atomic_list_concat(Rest, Local),
1402 ( Local == ''
1403 -> Term =.. [Alias,'.']
1404 ; Term =.. [Alias,Local]
1405 ),
1406 E = error(_,_),
1407 catch(expand_url_path(Term, Ref), E, fail),
1408 option(label(Label), Options, Ref)
1409 }.
1410wiki_link(a(href(Ref), Label), Options) -->
1411 [#, w(First)],
1412 { option(end(End), Options) },
1413 tokens_no_whitespace(Rest),
1414 peek_end_url(End),
1415 !,
1416 { atomic_list_concat([#,First|Rest], Ref),
1417 option(label(Label), Options, Ref)
1418 }.
1419wiki_link(a(href(Ref), Label), Options) -->
1420 [<],
1421 ( { option(relative(true), Options),
1422 Parts = Rest
1423 }
1424 -> tokens_no_whitespace(Rest)
1425 ; { Parts = [Prot, : | Rest]
1426 },
1427 [w(Prot), :], tokens_no_whitespace(Rest)
1428 ),
1429 [>],
1430 !,
1431 { atomic_list_concat(Parts, Ref),
1432 option(label(Label), Options, Ref)
1433 }.
1434
1441
1446
1447file_name(FileBase, Extension) -->
1448 segment(S1),
1449 segments(List),
1450 ['.'], file_extension(Extension),
1451 !,
1452 { atomic_list_concat([S1|List], '/', FileBase) }.
1453file_name(FileBase, Extension) -->
1454 [w(Alias), '('],
1455 { once(user:file_search_path(Alias, _)) },
1456 segment(S1),
1457 segments(List),
1458 [')'],
1459 !,
1460 { atomic_list_concat([S1|List], '/', Base),
1461 Spec =.. [Alias,Base],
1462 absolute_file_name(Spec, Path,
1463 [ access(read),
1464 extensions([pl]),
1465 file_type(prolog),
1466 file_errors(fail)
1467 ]),
1468 file_name_extension(FileBase, Extension, Path)
1469 }.
1470
1471
1472segment(..) -->
1473 ['.','.'],
1474 !.
1475segment(Word) -->
1476 [w(Word)].
1477segment(Dir) -->
1478 [w(Word),'.',w(d)],
1479 { atom_concat(Word, '.d', Dir) }.
1480
1481segments([H|T]) -->
1482 ['/'],
1483 !,
1484 segment(H),
1485 segments(T).
1486segments([]) -->
1487 [].
1488
1489file_extension(Ext) -->
1490 [w(Ext)],
1491 { autolink_extension(Ext, _)
1492 }.
1493
1494
1501
1502resolve_file(Name, FileOptions, Rest) :-
1503 existing_file(Name, FileOptions, Rest, []),
1504 !.
1505resolve_file(_, Options, Options).
1506
1507
1508existing_file(Name, FileOptions, Rest, Options) :-
1509 \+ Options.get(link) == false,
1510 E = error(_,_),
1511 catch(existing_file_p(Name, FileOptions, Rest), E, fail).
1512
1513existing_file_p(Name, FileOptions, Rest) :-
1514 ( nb_current(pldoc_file, RelativeTo),
1515 RelativeTo \== []
1516 -> Extra = [relative_to(RelativeTo)|Extra1]
1517 ; Extra = Extra1
1518 ),
1519 ( compound(Name)
1520 -> Extra1 = [file_type(prolog)]
1521 ; Extra1 = []
1522 ),
1523 absolute_file_name(Name, Path,
1524 [ access(read),
1525 file_errors(fail)
1526 | Extra
1527 ]),
1528 FileOptions = [ absolute_path(Path) | Rest ].
1529
1536
1537arity(Arity) -->
1538 [ w(Word) ],
1539 { E = error(_,_),
1540 catch(atom_number(Word, Arity), E, fail),
1541 Arity >= 0, Arity < 20
1542 }.
1543
1548
1549symbol_string([]) -->
1550 [].
1551symbol_string([H|T]) -->
1552 [H],
1553 { prolog_symbol_char(H) },
1554 symbol_string(T).
1555
1556prolog_symbol_char(C) -->
1557 [C],
1558 { prolog_symbol_char(C) }.
1559
1563
1564prolog_symbol_char(#).
1565prolog_symbol_char($).
1566prolog_symbol_char(&).
1567prolog_symbol_char(*).
1568prolog_symbol_char(+).
1569prolog_symbol_char(-).
1570prolog_symbol_char(.).
1571prolog_symbol_char(/).
1572prolog_symbol_char(:).
1573prolog_symbol_char(<).
1574prolog_symbol_char(=).
1575prolog_symbol_char(>).
1576prolog_symbol_char(?).
1577prolog_symbol_char(@).
1578prolog_symbol_char(\).
1579prolog_symbol_char(^).
1580prolog_symbol_char(~).
1581
1582
1583functor_name(String) :-
1584 sub_atom(String, 0, 1, _, Char),
1585 char_type(Char, lower).
1586
1587url_protocol(http).
1588url_protocol(https).
1589url_protocol(ftp).
1590url_protocol(mailto).
1591
1592peek_end_url(space) -->
1593 peek(Punct, End),
1594 { punct_token(Punct),
1595 space_token(End)
1596 },
1597 !.
1598peek_end_url(space) -->
1599 peek(End),
1600 { space_token(End) },
1601 !.
1602peek_end_url(space, [], []) :- !.
1603peek_end_url(Token) -->
1604 peek(Token),
1605 !.
1606
1607punct_token('.').
1608punct_token('!').
1609punct_token('?').
1610punct_token(',').
1611punct_token(';').
1612
1613space_token(' ') :- !.
1614space_token('\r') :- !.
1615space_token('\n') :- !.
1616space_token(T) :-
1617 \+ atom(T), 1618 \+ T = w(_).
1619
1624
1625autolink_extension(Ext, Type) :-
1626 prolog:doc_autolink_extension(Ext, Type),
1627 !.
1628autolink_extension(Ext, prolog) :-
1629 user:prolog_file_type(Ext,prolog),
1630 !.
1631autolink_extension(txt, wiki).
1632autolink_extension(md, wiki).
1633autolink_extension(gif, image).
1634autolink_extension(png, image).
1635autolink_extension(jpg, image).
1636autolink_extension(jpeg, image).
1637autolink_extension(svg, image).
1638
1643
1644autolink_file('README', wiki).
1645autolink_file('TODO', wiki).
1646autolink_file('ChangeLog', wiki).
1647
1651
1652citations([H|T]) -->
1653 citation(H),
1654 ( [';']
1655 -> citations(T)
1656 ; {T=[]}
1657 ).
1658
1659citation(Atom) -->
1660 [@], wiki_words(Atoms),
1661 { length(Atoms, Len),
1662 Len > 10, !,
1663 fail
1664 ; true
1665 },
1666 end_citation,
1667 !,
1668 { atomic_list_concat(Atoms, Atom)
1669 }.
1670
1671end_citation, [';'] --> [';'].
1672end_citation, ['@'] --> ['@'].
1673end_citation, [']'] --> [']'].
1674
1675
1676 1679
1690
([_-Line|Lines], Header, Lines) :-
1692 phrase(section_line(Header), Line).
1693
1694section_line(\section(Type, Title)) -->
1695 ws, "<", word(Codes), ">", normalise_white_space(TitleCodes),
1696 { atom_codes(Type, Codes),
1697 atom_codes(Title, TitleCodes)
1698 }.
1699
1700 1703
1707
1708tokenize_lines(Lines, TokenLines) :-
1709 tokenize_lines(Lines, -1, TokenLines).
1710
1711tokenize_lines([], _, []) :- !.
1712tokenize_lines(Lines, Indent, [Pre|T]) :-
1713 verbatim(Lines, Indent, Pre, RestLines),
1714 !,
1715 tokenize_lines(RestLines, Indent, T).
1716tokenize_lines([I-H0|T0], Indent0, [I-H|T]) :-
1717 phrase(line_tokens(H), H0),
1718 ( H == []
1719 -> Indent = Indent0
1720 ; Indent = I
1721 ),
1722 tokenize_lines(T0, Indent, T).
1723
1724
1732
1733line_tokens([H|T]) -->
1734 line_token(H),
1735 !,
1736 line_tokens(T).
1737line_tokens([]) -->
1738 [].
1739
1740line_token(T) -->
1741 [C],
1742 ( { code_type(C, space) }
1743 -> ws,
1744 { T = ' ' }
1745 ; { code_type(C, alnum) },
1746 word(Rest),
1747 { atom_codes(W, [C|Rest]),
1748 T = w(W)
1749 }
1750 ; { char_code(T, C) }
1751 ).
1752
1753word([C0|T]) -->
1754 [C0], { code_type(C0, alnum) },
1755 !,
1756 word(T).
1757word([0'_, C1|T]) -->
1758 [0'_, C1], { code_type(C1, alnum) },
1759 !,
1760 word(T).
1761word([]) -->
1762 [].
1763
1764alphas([C0|T]) -->
1765 [C0], { code_type(C0, alpha) },
1766 !,
1767 alphas(T).
1768alphas([]) -->
1769 [].
1770
1790
1791verbatim(Lines, _,
1792 Indent-pre([class(code), ext(Ext)],Pre),
1793 RestLines) :-
1794 skip_empty_lines(Lines, [Indent-FenceLine|CodeLines]),
1795 verbatim_fence(FenceLine, Fence, Ext),
1796 verbatim_body(CodeLines, Indent, [10|PreCodes], [],
1797 [Indent-Fence|RestLines]),
1798 !,
1799 atom_codes(Pre, PreCodes).
1800verbatim([_-[],Indent-Line|Lines], EnvIndent,
1801 Indent-pre(class(code),Pre),
1802 RestLines) :-
1803 EnvIndent >= 0,
1804 Indent >= EnvIndent+4, Indent =< EnvIndent+8,
1805 valid_verbatim_opening(Line),
1806 indented_verbatim_body([Indent-Line|Lines], Indent,
1807 CodeLines, RestLines),
1808 !,
1809 lines_code_text(CodeLines, Indent, [10|PreCodes]),
1810 atom_codes(Pre, PreCodes).
1811
1812verbatim_body(Lines, _, PreT, PreT, Lines).
1813verbatim_body([I-L|Lines], Indent, [10|Pre], PreT, RestLines) :-
1814 PreI is I - Indent,
1815 phrase(pre_indent(PreI), Pre, PreT0),
1816 verbatim_line(L, PreT0, PreT1),
1817 verbatim_body(Lines, Indent, PreT1, PreT, RestLines).
1818
1819verbatim_fence(Line, Fence, '') :-
1820 Line == [0'=,0'=],
1821 !,
1822 Fence = Line.
1823verbatim_fence(Line, Fence, Ext) :-
1824 tilde_fence(Line, Fence, 0, Ext).
1825verbatim_fence(Line, Fence, Ext) :-
1826 md_fence(Line, Fence, 0, Ext).
1827
1828tilde_fence([0'~|T0], [0'~|F0], C0, Ext) :-
1829 !,
1830 C1 is C0+1,
1831 tilde_fence(T0, F0, C1, Ext).
1832tilde_fence(List, [], C, Ext) :-
1833 C >= 3,
1834 ( List == []
1835 -> Ext = ''
1836 ; phrase(tilde_fence_ext(ExtCodes), List)
1837 -> atom_codes(Ext, ExtCodes)
1838 ).
1839
1843
1844tilde_fence_ext(Ext) -->
1845 "{.", !, alphas(Ext), "}".
1846tilde_fence_ext(Ext) -->
1847 "{", alphas(Ext), "}".
1848
1849md_fence([0'`|T0], [0'`|F0], C0, Ext) :-
1850 !,
1851 C1 is C0+1,
1852 md_fence(T0, F0, C1, Ext).
1853md_fence(List, [], C, Ext) :-
1854 C >= 3,
1855 ( List == []
1856 -> Ext = ''
1857 ; phrase(md_fence_ext(ExtCodes), List),
1858 atom_codes(Ext, ExtCodes)
1859 ).
1860
1862md_fence_ext(Ext) -->
1863 tilde_fence_ext(Ext),
1864 !.
1866md_fence_ext(Ext) -->
1867 alphas(Ext).
1868
1874
1875indented_verbatim_body([I-L|T0], Indent, [I-L|T], RestLines) :-
1876 L \== [], I >= Indent,
1877 !,
1878 indented_verbatim_body(T0, Indent, T, RestLines).
1879indented_verbatim_body([I0-[],I-L|T0], Indent, [I0-[],I-L|T], RestLines) :-
1880 I >= Indent,
1881 valid_verbatim_opening(L),
1882 indented_verbatim_body(T0, Indent, T, RestLines).
1883indented_verbatim_body(Lines, _, [], Lines).
1884
1888
1889valid_verbatim_opening([0'||_]) :- !, fail.
1890valid_verbatim_opening(Line) :-
1891 Line \== [],
1892 \+ ( phrase(line_tokens(Tokens), Line),
1893 list_item_prefix(_Type, Tokens, _Rest)
1894 ).
1895
1899
1900lines_code_text([], _, []).
1901lines_code_text([_-[]|T0], Indent, [10|T]) :-
1902 !,
1903 lines_code_text(T0, Indent, T).
1904lines_code_text([I-Line|T0], Indent, [10|T]) :-
1905 PreI is I-Indent,
1906 phrase(pre_indent(PreI), T, T1),
1907 verbatim_line(Line, T1, T2),
1908 lines_code_text(T0, Indent, T2).
1909
1910
1915
1916pre_indent(N) -->
1917 { N > 0,
1918 !,
1919 N2 is N - 1
1920 }, " ",
1921 pre_indent(N2).
1922pre_indent(_) -->
1923 "".
1924
1925verbatim_line(Line, Pre, PreT) :-
1926 append(Line, PreT, Pre).
1927
1928
1929 1932
1939
1940summary_from_lines(Lines, Sentence) :-
1941 skip_empty_lines(Lines, Lines1),
1942 summary2(Lines1, Sentence0),
1943 end_sentence(Sentence0, Sentence).
1944
1945summary2(_, Sentence) :-
1946 Sentence == [],
1947 !. 1948summary2([], []) :- !.
1949summary2([_-[]|_], []) :- !. 1950summary2([_-[0'@|_]|_], []) :- !. 1951summary2([_-L0|Lines], Sentence) :-
1952 phrase(sentence(Sentence, Tail), L0, _),
1953 summary2(Lines, Tail).
1954
1955sentence([C,End], []) -->
1956 [C,End],
1957 { \+ code_type(C, period),
1958 code_type(End, period) 1959 },
1960 space_or_eos,
1961 !.
1962sentence([0' |T0], T) -->
1963 space,
1964 !,
1965 ws,
1966 sentence(T0, T).
1967sentence([H|T0], T) -->
1968 [H],
1969 sentence(T0, T).
1970sentence([0' |T], T) --> 1971 eos.
1972
1973space_or_eos -->
1974 [C],
1975 !,
1976 {code_type(C, space)}.
1977space_or_eos -->
1978 eos.
1979
1984
1985skip_empty_lines([], []).
1986skip_empty_lines([_-[]|Lines0], Lines) :-
1987 !,
1988 skip_empty_lines(Lines0, Lines).
1989skip_empty_lines(Lines, Lines).
1990
1991end_sentence([], []).
1992end_sentence([0'\s], [0'.]) :- !.
1993end_sentence([H|T0], [H|T]) :-
1994 end_sentence(T0, T).
1995
1996
1997 2000
2007
2008indented_lines(Comment, Prefixes, Lines) :-
2009 must_be(codes, Comment),
2010 phrase(split_lines(Prefixes, Lines), Comment),
2011 !.
2012
2013split_lines(_, []) -->
2014 end_of_comment.
2015split_lines(Prefixes, [Indent-L1|Ls]) -->
2016 take_prefix(Prefixes, 0, Indent0),
2017 white_prefix(Indent0, Indent),
2018 take_line(L1),
2019 split_lines(Prefixes, Ls).
2020
2021
2027
-->
2029 eos.
2030end_of_comment -->
2031 ws, stars, "*/".
2032
2033stars --> [].
2034stars --> "*", !, stars.
2035
2036
2041
2042take_prefix(Prefixes, I0, I) -->
2043 { member(Prefix, Prefixes),
2044 string_codes(Prefix, PrefixCodes)
2045 },
2046 prefix(PrefixCodes),
2047 !,
2048 { string_update_linepos(PrefixCodes, I0, I) }.
2049take_prefix(_, I, I) -->
2050 [].
2051
2052prefix([]) --> [].
2053prefix([H|T]) --> [H], prefix(T).
2054
2055white_prefix(I0, I) -->
2056 [C],
2057 { code_type(C, white),
2058 !,
2059 update_linepos(C, I0, I1)
2060 },
2061 white_prefix(I1, I).
2062white_prefix(I, I) -->
2063 [].
2064
2068
2069string_update_linepos([], I, I).
2070string_update_linepos([H|T], I0, I) :-
2071 update_linepos(H, I0, I1),
2072 string_update_linepos(T, I1, I).
2073
2079
2080update_linepos(0'\t, I0, I) :-
2081 !,
2082 I is (I0\/7)+1.
2083update_linepos(0'\b, I0, I) :-
2084 !,
2085 I is max(0, I0-1).
2086update_linepos(0'\r, _, 0) :- !.
2087update_linepos(0'\n, _, 0) :- !.
2088update_linepos(_, I0, I) :-
2089 I is I0 + 1.
2090
2095
2096take_line([]) -->
2097 "\r\n",
2098 !. 2099take_line([]) -->
2100 "\n",
2101 !. 2102take_line(Line) -->
2103 [H], { code_type(H, white) },
2104 !,
2105 take_white(White, WT),
2106 ( nl
2107 -> { Line = [] }
2108 ; { Line = [H|White] },
2109 take_line(WT)
2110 ).
2111take_line([H|T]) -->
2112 [H],
2113 !,
2114 take_line(T).
2115take_line([]) --> 2116 [].
2117
2118take_white([H|T0], T) -->
2119 [H], { code_type(H, white) },
2120 !,
2121 take_white(T0, T).
2122take_white(T, T) -->
2123 [].
2124
2129
2130normalise_indentation(Lines0, Lines) :-
2131 skip_empty_lines(Lines0, Lines1),
2132 Lines1 = [I0-_|Lines2],
2133 !,
2134 smallest_indentation(Lines2, I0, Subtract),
2135 ( Subtract == 0
2136 -> Lines = Lines0
2137 ; maplist(substract_indent(Subtract), Lines0, Lines)
2138 ).
2139normalise_indentation(Lines, Lines).
2140
2141smallest_indentation([], I, I).
2142smallest_indentation([_-[]|T], I0, I) :-
2143 !,
2144 smallest_indentation(T, I0, I).
2145smallest_indentation([X-_|T], I0, I) :-
2146 I1 is min(I0, X),
2147 smallest_indentation(T, I1, I).
2148
2149substract_indent(Subtract, I0-L, I-L) :-
2150 I is max(0,I0-Subtract).
2151
2152
2153 2156
2161
2162strip_leading_par([p(C)|T], L) :-
2163 !,
2164 append(C, T, L).
2165strip_leading_par(L, L).
2166
2167
2168 2171
2175
2176ws -->
2177 [C], {code_type(C, space)},
2178 !,
2179 ws.
2180ws -->
2181 [].
2182
2186
2187space -->
2188 [C],
2189 {code_type(C, space)}.
2190
2194
2195nl -->
2196 "\r\n",
2197 !.
2198nl -->
2199 "\n".
2200
2204
2205peek(H, L, L) :-
2206 L = [H|_].
2207
2208peek(H1, H2, L, L) :-
2209 L = [H1, H2|_].
2210
2216
2217tokens([]) --> [].
2218tokens([H|T]) --> token(H), tokens(T).
2219
2220tokens(_, []) --> [].
2221tokens(C, [H|T]) --> token(H), {succ(C1, C)}, tokens(C1, T).
2222
2228
2229tokens_no_whitespace([]) -->
2230 [].
2231tokens_no_whitespace([Word|T]) -->
2232 [ w(Word) ],
2233 !,
2234 tokens_no_whitespace(T).
2235tokens_no_whitespace([H|T]) -->
2236 [H],
2237 { \+ space_token(H) },
2238 tokens_no_whitespace(T).
2239
2240token(Token) -->
2241 [Token],
2242 { token(Token) }.
2243
2244token(w(_)) :- !.
2245token(Token) :- atom(Token).
2246
2250
2251:- meta_predicate limit(+,2,?,?). 2252
2253limit(Count, Rule, Input, Rest) :-
2254 Count > 0,
2255 State = count(0),
2256 call(Rule, Input, Rest),
2257 arg(1, State, N0),
2258 N is N0+1,
2259 ( N =:= Count
2260 -> !
2261 ; nb_setarg(1, State, N)
2262 ).
2263
2264
2265 2268
2269:- multifile
2270 prolog:message//1. 2271
2272prolog:message(pldoc(deprecated_tag(Name, Tag))) -->
2273 [ 'PlDoc: Deprecated tag @~w (use @~w)'-[Name, Tag]
2274 ].
2275prolog:message(pldoc(unknown_tag(Name))) -->
2276 [ 'PlDoc: unknown tag @~w'-[Name]
2277 ]