View source with raw comments or as raw
    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)  2007-2015, University of Amsterdam
    7                              VU University Amsterdam
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36:- module(pldoc_latex,
   37          [ doc_latex/3,                % +Items, +OutFile, +Options
   38            latex_for_file/3,           % +FileSpec, +Out, +Options
   39            latex_for_wiki_file/3,      % +FileSpec, +Out, +Options
   40            latex_for_predicates/3      % +PI, +Out, +Options
   41          ]).   42:- use_module(library(pldoc)).   43:- use_module(library(readutil)).   44:- use_module(library(error)).   45:- use_module(library(apply)).   46:- use_module(library(option)).   47:- use_module(library(lists)).   48:- use_module(library(debug)).   49:- use_module(pldoc(doc_wiki)).   50:- use_module(pldoc(doc_process)).   51:- use_module(pldoc(doc_modes)).   52:- use_module(library(pairs), [pairs_values/2]).   53:- use_module(library(prolog_source), [file_name_on_path/2]).   54:- use_module(library(prolog_xref), [xref_hook/1]).   55:- use_module(pldoc(doc_html),          % we cannot import all as the
   56              [ doc_file_objects/5,     % \commands have the same name
   57                unquote_filespec/2,
   58                doc_tag_title/2,
   59                existing_linked_file/2,
   60                pred_anchor_name/3,
   61                private/2,
   62                (multifile)/2,
   63                is_pi/1,
   64                is_op_type/2
   65              ]).

PlDoc LaTeX backend

This module translates the Herbrand term from the documentation extracting module doc_wiki.pl into a LaTeX document for us with the pl.sty LaTeX style file. The function of this module is very similar to doc_html.pl, providing the HTML backend, and the implementation follows the same paradigm. The module can

author
- Jan Wielemaker */
To be done
- See TODO
   88:- predicate_options(doc_latex/3, 3,
   89                     [ stand_alone(boolean),
   90                       public_only(boolean),
   91                       section_level(oneof([section,subsection,subsubsection])),
   92                       summary(atom)
   93                     ]).   94:- predicate_options(latex_for_file/3, 3,
   95                     [ stand_alone(boolean),
   96                       public_only(boolean),
   97                       section_level(oneof([section,subsection,subsubsection]))
   98                     ]).   99:- predicate_options(latex_for_predicates/3, 3,
  100                     [                          % no options
  101                     ]).  102:- predicate_options(latex_for_wiki_file/3, 3,
  103                     [ stand_alone(boolean),
  104                       public_only(boolean),
  105                       section_level(oneof([section,subsection,subsubsection]))
  106                     ]).  107
  108
  109:- thread_local
  110    options/1,
  111    documented/1.  112
  113current_options(Options) :-
  114    options(Current),
  115    !,
  116    Options = Current.
  117current_options([]).
 doc_latex(+Spec, +OutFile, +Options) is det
Process one or more objects, writing the LaTeX output to OutFile. Spec is one of:
Name / Arity
Generate documentation for predicate
Name // Arity
Generate documentation for DCG rule
File
If File is a prolog file (as defined by prolog_file_type/2), process using latex_for_file/3, otherwise process using latex_for_wiki_file/3.

Typically Spec is either a list of filenames or a list of predicate indicators. Defined options are:

stand_alone(+Bool)
If true (default), create a document that can be run through LaTeX. If false, produce a document to be included in another LaTeX document.
public_only(+Bool)
If true (default), only emit documentation for exported predicates.
section_level(+Level)
Outermost section level produced. Level is the name of a LaTeX section command. Default is section.
summary(+File)
Write summary declarations to the named File.
modules(+List)
If [[Name/Arity]] needs to be resolved, search for the predicates in the given modules.
module(+Module)
Same as modules([Module]).
  155doc_latex(Spec, OutFile, Options) :-
  156    load_urldefs,
  157    merge_options(Options,
  158                  [ include_reexported(true)
  159                  ],
  160                  Options1),
  161    retractall(documented(_)),
  162    setup_call_cleanup(
  163        asserta(options(Options), Ref),
  164        phrase(process_items(Spec, [body], Options1), Tokens),
  165        erase(Ref)),
  166    setup_call_cleanup(
  167        open(OutFile, write, Out),
  168        print_latex(Out, Tokens, Options1),
  169        close(Out)),
  170    latex_summary(Options).
  171
  172process_items([], Mode, _) -->
  173    !,
  174    pop_mode(body, Mode, _).
  175process_items([H|T], Mode, Options) -->
  176    process_items(H, Mode, Mode1, Options),
  177    process_items(T, Mode1, Options).
  178process_items(Spec, Mode, Options) -->
  179    {Mode = [Mode0|_]},
  180    process_items(Spec, Mode, Mode1, Options),
  181    pop_mode(Mode0, Mode1, _).
  182
  183process_items(PI, Mode0, Mode, Options) -->
  184    { is_pi(PI) },
  185    !,
  186    need_mode(description, Mode0, Mode),
  187    latex_tokens_for_predicates(PI, Options).
  188process_items(FileSpec, Mode0, Mode, Options) -->
  189    {   (   absolute_file_name(FileSpec,
  190                               [ file_type(source),
  191                                 access(read),
  192                                 file_errors(fail)
  193                               ],
  194                               File)
  195        ->  true
  196        ;   absolute_file_name(FileSpec,
  197                               [ access(read)
  198                               ],
  199                               File)
  200        ),
  201        file_name_extension(_Base, Ext, File)
  202    },
  203    need_mode(body, Mode0, Mode),
  204    (   { user:prolog_file_type(Ext, prolog) }
  205    ->  latex_tokens_for_file(File, Options)
  206    ;   latex_tokens_for_wiki_file(File, Options)
  207    ).
 latex_for_file(+File, +Out, +Options) is det
Generate a LaTeX description of all commented predicates in File, writing the LaTeX text to the stream Out. Supports the options stand_alone, public_only and section_level. See doc_latex/3 for a description of the options.
  217latex_for_file(FileSpec, Out, Options) :-
  218    load_urldefs,
  219    phrase(latex_tokens_for_file(FileSpec, Options), Tokens),
  220    print_latex(Out, Tokens, Options).
 latex_tokens_for_file(+FileSpec, +Options)//
  225latex_tokens_for_file(FileSpec, Options, Tokens, Tail) :-
  226    absolute_file_name(FileSpec,
  227                       [ file_type(prolog),
  228                         access(read)
  229                       ],
  230                       File),
  231    doc_file_objects(FileSpec, File, Objects, FileOptions, Options),
  232    asserta(options(Options), Ref),
  233    call_cleanup(phrase(latex([ \file_header(File, FileOptions)
  234                              | \objects(Objects, FileOptions)
  235                              ]),
  236                        Tokens, Tail),
  237                 erase(Ref)).
 latex_for_wiki_file(+File, +Out, +Options) is det
Write a LaTeX translation of a Wiki file to the steam Out. Supports the options stand_alone, public_only and section_level. See doc_latex/3 for a description of the options.
  247latex_for_wiki_file(FileSpec, Out, Options) :-
  248    load_urldefs,
  249    phrase(latex_tokens_for_wiki_file(FileSpec, Options), Tokens),
  250    print_latex(Out, Tokens, Options).
  251
  252latex_tokens_for_wiki_file(FileSpec, Options, Tokens, Tail) :-
  253    absolute_file_name(FileSpec, File,
  254                       [ access(read)
  255                       ]),
  256    read_file_to_codes(File, String, []),
  257    b_setval(pldoc_file, File),
  258    asserta(options(Options), Ref),
  259    call_cleanup((wiki_codes_to_dom(String, [], DOM),
  260                  phrase(latex(DOM), Tokens, Tail)
  261                 ),
  262                 (nb_delete(pldoc_file),
  263                  erase(Ref))).
 latex_for_predicates(+PI:list, +Out, +Options) is det
Generate LaTeX for a list of predicate indicators. This does not produce the \begin{description}...\end{description} environment, just a plain list of \predicate, etc. statements. The current implementation ignores Options.
  273latex_for_predicates(Spec, Out, Options) :-
  274    load_urldefs,
  275    phrase(latex_tokens_for_predicates(Spec, Options), Tokens),
  276    print_latex(Out, [nl_exact(0)|Tokens], Options).
  277
  278latex_tokens_for_predicates([], _Options) --> !.
  279latex_tokens_for_predicates([H|T], Options) -->
  280    !,
  281    latex_tokens_for_predicates(H, Options),
  282    latex_tokens_for_predicates(T, Options).
  283latex_tokens_for_predicates(PI, Options) -->
  284    { generic_pi(PI),
  285      !,
  286      (   doc_comment(PI, Pos, _Summary, Comment)
  287      ->  true
  288      ;   Comment = ''
  289      )
  290    },
  291    object(PI, Pos, Comment, [description], _, Options).
  292latex_tokens_for_predicates(Spec, Options) -->
  293    { findall(PI, documented_pi(Spec, PI, Options), List),
  294      (   List == []
  295      ->  print_message(warning, pldoc(no_predicates_from(Spec)))
  296      ;   true
  297      )
  298    },
  299    latex_tokens_for_predicates(List, Options).
  300
  301documented_pi(Spec, PI, Options) :-
  302    option(modules(List), Options),
  303    member(M, List),
  304    generalise_spec(Spec, PI, M),
  305    doc_comment(PI, _Pos, _Summary, _Comment),
  306    !.
  307documented_pi(Spec, PI, Options) :-
  308    option(module(M), Options),
  309    generalise_spec(Spec, PI, M),
  310    doc_comment(PI, _Pos, _Summary, _Comment),
  311    !.
  312documented_pi(Spec, PI, _Options) :-
  313    generalise_spec(Spec, PI, _),
  314    doc_comment(PI, _Pos, _Summary, _Comment).
  315
  316generic_pi(Module:Name/Arity) :-
  317    atom(Module), atom(Name), integer(Arity),
  318    !.
  319generic_pi(Module:Name//Arity) :-
  320    atom(Module), atom(Name), integer(Arity).
  321
  322generalise_spec(Name/Arity, M:Name/Arity, M).
  323generalise_spec(Name//Arity, M:Name//Arity, M).
  324
  325
  326                 /*******************************
  327                 *       LATEX PRODUCTION       *
  328                 *******************************/
  329
  330:- thread_local
  331    fragile/0.                      % provided when in fragile mode
  332
  333latex([]) -->
  334    !,
  335    [].
  336latex(Atomic) -->
  337    { string(Atomic),
  338      atom_string(Atom, Atomic),
  339      sub_atom(Atom, 0, _, 0, 'LaTeX')
  340    },
  341    !,
  342    [ latex('\\LaTeX{}') ].
  343latex(Atomic) -->                       % can this actually happen?
  344    { atomic(Atomic),
  345      !,
  346      atom_string(Atom, Atomic),
  347      findall(x, sub_atom(Atom, _, _, _, '\n'), Xs),
  348      length(Xs, Lines)
  349    },
  350    (   {Lines == 0}
  351    ->  [ Atomic ]
  352    ;   [ nl(Lines) ]
  353    ).
  354latex(List) -->
  355    latex_special(List, Rest),
  356    !,
  357    latex(Rest).
  358latex(w(Word)) -->
  359    [ Word ].
  360latex([H|T]) -->
  361    !,
  362    (   latex(H)
  363    ->  latex(T)
  364    ;   { print_message(error, latex(failed(H))) },
  365        latex(T)
  366    ).
  367
  368% high level commands
  369latex(h1(Attrs, Content)) -->
  370    latex_section(0, Attrs, Content).
  371latex(h2(Attrs, Content)) -->
  372    latex_section(1, Attrs, Content).
  373latex(h3(Attrs, Content)) -->
  374    latex_section(2, Attrs, Content).
  375latex(h4(Attrs, Content)) -->
  376    latex_section(3, Attrs, Content).
  377latex(p(Content)) -->
  378    [ nl_exact(2) ],
  379    latex(Content).
  380latex(blockquote(Content)) -->
  381    latex(cmd(begin(quote))),
  382    latex(Content),
  383    latex(cmd(end(quote))).
  384latex(center(Content)) -->
  385    latex(cmd(begin(center))),
  386    latex(Content),
  387    latex(cmd(end(center))).
  388latex(a(Attrs, Content)) -->
  389    { attribute(href(HREF), Attrs) },
  390    (   {HREF == Content}
  391    ->  latex(cmd(url(url_escape(HREF))))
  392    ;   { atom_concat(#,Sec,HREF) }
  393    ->  latex([Content, ' (', cmd(secref(Sec)), ')'])
  394    ;   latex(cmd(href(url_escape(HREF), Content)))
  395    ).
  396latex(br(_)) -->
  397    latex(latex(\\)).
  398latex(hr(_)) -->
  399    latex(cmd(hrule)).
  400latex(code(CodeList)) -->
  401    { is_list(CodeList),
  402      !,
  403      atomic_list_concat(CodeList, Atom)
  404    },
  405    (   {fragile}
  406    ->  latex(cmd(const(Atom)))
  407    ;   [ verb(Atom) ]
  408    ).
  409latex(code(Code)) -->
  410    { identifier(Code) },
  411    !,
  412    latex(cmd(const(Code))).
  413latex(code(Code)) -->
  414    (   {fragile}
  415    ->  latex(cmd(const(Code)))
  416    ;   [ verb(Code) ]
  417    ).
  418latex(b(Code)) -->
  419    latex(cmd(textbf(Code))).
  420latex(strong(Code)) -->
  421    latex(cmd(textbf(Code))).
  422latex(i(Code)) -->
  423    latex(cmd(textit(Code))).
  424latex(var(Var)) -->
  425    latex(cmd(arg(Var))).
  426latex(pre(_Class, Code)) -->
  427    [ nl_exact(2), code(Code), nl_exact(2) ].
  428latex(ul(Content)) -->
  429    { if_short_list(Content, shortlist, itemize, Env) },
  430    latex(cmd(begin(Env))),
  431    latex(Content),
  432    latex(cmd(end(Env))).
  433latex(ol(Content)) -->
  434    latex(cmd(begin(enumerate))),
  435    latex(Content),
  436    latex(cmd(end(enumerate))).
  437latex(li(Content)) -->
  438    latex(cmd(item)),
  439    latex(Content).
  440latex(dl(_, Content)) -->
  441    latex(cmd(begin(description))),
  442    latex(Content),
  443    latex(cmd(end(description))).
  444latex(dd(_, Content)) -->
  445    latex(Content).
  446latex(dd(Content)) -->
  447    latex(Content).
  448latex(dt(class=term, \term(Text, Term, Bindings))) -->
  449    termitem(Text, Term, Bindings).
  450latex(dt(Content)) -->
  451    latex(cmd(item(opt(Content)))).
  452latex(table(Attrs, Content)) -->
  453    latex_table(Attrs, Content).
  454latex(\Cmd, List, Tail) :-
  455    call(Cmd, List, Tail).
  456
  457% low level commands
  458latex(latex(Text)) -->
  459    [ latex(Text) ].
  460latex(cmd(Term)) -->
  461    { Term =.. [Cmd|Args] },
  462    indent(Cmd),
  463    [ cmd(Cmd) ],
  464    latex_arguments(Args),
  465    outdent(Cmd).
  466
  467indent(begin) --> !,           [ nl(2) ].
  468indent(end) --> !,             [ nl_exact(1) ].
  469indent(section) --> !,         [ nl(2) ].
  470indent(subsection) --> !,      [ nl(2) ].
  471indent(subsubsection) --> !,   [ nl(2) ].
  472indent(item) --> !,            [ nl(1), indent(4) ].
  473indent(definition) --> !,      [ nl(1), indent(4) ].
  474indent(tag) --> !,             [ nl(1), indent(4) ].
  475indent(termitem) --> !,        [ nl(1), indent(4) ].
  476indent(prefixtermitem) --> !,  [ nl(1), indent(4) ].
  477indent(infixtermitem) --> !,   [ nl(1), indent(4) ].
  478indent(postfixtermitem) --> !, [ nl(1), indent(4) ].
  479indent(predicate) --> !,       [ nl(1), indent(4) ].
  480indent(dcg) --> !,             [ nl(1), indent(4) ].
  481indent(infixop) --> !,         [ nl(1), indent(4) ].
  482indent(prefixop) --> !,        [ nl(1), indent(4) ].
  483indent(postfixop) --> !,       [ nl(1), indent(4) ].
  484indent(predicatesummary) --> !,[ nl(1) ].
  485indent(dcgsummary) --> !,      [ nl(1) ].
  486indent(oppredsummary) --> !,   [ nl(1) ].
  487indent(hline) --> !,           [ nl(1) ].
  488indent(_) -->                  [].
  489
  490outdent(begin) --> !,           [ nl_exact(1) ].
  491outdent(end) --> !,             [ nl(2) ].
  492outdent(item) --> !,            [ ' ' ].
  493outdent(tag) --> !,             [ nl(1) ].
  494outdent(termitem) --> !,        [ nl(1) ].
  495outdent(prefixtermitem) --> !,  [ nl(1) ].
  496outdent(infixtermitem) --> !,   [ nl(1) ].
  497outdent(postfixtermitem) --> !, [ nl(1) ].
  498outdent(definition) --> !,      [ nl(1) ].
  499outdent(section) --> !,         [ nl(2) ].
  500outdent(subsection) --> !,      [ nl(2) ].
  501outdent(subsubsection) --> !,   [ nl(2) ].
  502outdent(predicate) --> !,       [ nl(1) ].
  503outdent(dcg) --> !,             [ nl(1) ].
  504outdent(infixop) --> !,         [ nl(1) ].
  505outdent(prefixop) --> !,        [ nl(1) ].
  506outdent(postfixop) --> !,       [ nl(1) ].
  507outdent(predicatesummary) --> !,[ nl(1) ].
  508outdent(dcgsummary) --> !,      [ nl(1) ].
  509outdent(oppredsummary) --> !,   [ nl(1) ].
  510outdent(hline) --> !,           [ nl(1) ].
  511outdent(_) -->                  [].
  512
  513fragile_latex(Content, In, Out) :-
  514    setup_call_cleanup(
  515        asserta(fragile, Ref),
  516        phrase(latex(Content), In, Out),
  517        erase(Ref)).
 latex_special(String, Rest)// is semidet
Deals with special sequences of symbols.
  523latex_special(In, Rest) -->
  524    { url_chars(In, Chars, Rest),
  525      special(Chars),
  526      atom_chars(Atom, Chars),
  527      urldef_name(Atom, Name)
  528    },
  529    !,
  530    latex([cmd(Name), latex('{}')]).
  531
  532special(Chars) :-
  533    memberchk(\, Chars),
  534    !.
  535special(Chars) :-
  536    length(Chars, Len),
  537    Len > 1.
  538
  539url_chars([H|T0], [H|T], Rest) :-
  540    urlchar(H),
  541    !,
  542    url_chars(T0, T, Rest).
  543url_chars(L, [], L).
 latex_arguments(+Args:list)// is det
Write LaTeX command arguments. If an argument is of the form opt(Arg) it is written as [Arg], Otherwise it is written as {Arg}. Note that opt([]) is omitted. I think no LaTeX command is designed to handle an empty optional argument special.

During processing the arguments it asserts fragile/0 to allow is taking care of LaTeX fragile constructs (i.e. constructs that are not allows inside {...}).

  557latex_arguments(List, Out, Tail) :-
  558    asserta(fragile, Ref),
  559    call_cleanup(fragile_list(List, Out, Tail),
  560                 erase(Ref)).
  561
  562fragile_list([]) --> [].
  563fragile_list([opt([])|T]) -->
  564    !,
  565    fragile_list(T).
  566fragile_list([opt(H)|T]) -->
  567    !,
  568    [ '[' ],
  569    latex_arg(H),
  570    [ ']' ],
  571    fragile_list(T).
  572fragile_list([H|T]) -->
  573    [ curl(open) ],
  574    latex_arg(H),
  575    [ curl(close) ],
  576    fragile_list(T).
 latex_arg(+In)//
Write a LaTeX argument. If we can, we will use a defined urldef_name/2.
  583latex_arg(H) -->
  584    { atomic(H),
  585      atom_string(Atom, H),
  586      urldef_name(Atom, Name)
  587    },
  588    !,
  589    latex(cmd(Name)).
  590latex_arg(H) -->
  591    { maplist(atom, H),
  592      atomic_list_concat(H, Atom),
  593      urldef_name(Atom, Name)
  594    },
  595    !,
  596    latex(cmd(Name)).
  597latex_arg(no_escape(Text)) -->
  598    !,
  599    [no_escape(Text)].
  600latex_arg(url_escape(Text)) -->
  601    !,
  602    [url_escape(Text)].
  603latex_arg(H) -->
  604    latex(H).
  605
  606attribute(Att, Attrs) :-
  607    is_list(Attrs),
  608    !,
  609    option(Att, Attrs).
  610attribute(Att, One) :-
  611    option(Att, [One]).
  612
  613if_short_list(Content, If, Else, Env) :-
  614    (   short_list(Content)
  615    ->  Env = If
  616    ;   Env = Else
  617    ).
 short_list(+Content) is semidet
True if Content describes the content of a dl or ul/ol list where each elemenent has short content.
  624short_list([]).
  625short_list([_,dd(Content)|T]) :-
  626    !,
  627    short_content(Content),
  628    short_list(T).
  629short_list([_,dd(_, Content)|T]) :-
  630    !,
  631    short_content(Content),
  632    short_list(T).
  633short_list([li(Content)|T]) :-
  634    short_content(Content),
  635    short_list(T).
  636
  637short_content(Content) :-
  638    phrase(latex(Content), Tokens),
  639    summed_string_len(Tokens, 0, Len),
  640    Len < 50.
  641
  642summed_string_len([], Len, Len).
  643summed_string_len([H|T], L0, L) :-
  644    atomic(H),
  645    !,
  646    atom_length(H, AL),
  647    L1 is L0 + AL,
  648    summed_string_len(T, L1, L).
  649summed_string_len([_|T], L0, L) :-
  650    summed_string_len(T, L0, L).
 latex_section(+Level, +Attributes, +Content)// is det
Emit a LaTeX section, keeping track of the desired highest section level.
Arguments:
Level- Desired level, relative to the base-level. Must be a non-negative integer.
  661latex_section(Level, Attrs, Content) -->
  662    { current_options(Options),
  663      option(section_level(LaTexSection), Options, section),
  664      latex_section_level(LaTexSection, BaseLevel),
  665      FinalLevel is BaseLevel+Level,
  666      (   latex_section_level(SectionCommand, FinalLevel)
  667      ->  Term =.. [SectionCommand, Content]
  668      ;   domain_error(latex_section_level, FinalLevel)
  669      )
  670    },
  671    latex(cmd(Term)),
  672    section_label(Attrs).
  673
  674section_label(Attrs) -->
  675    { is_list(Attrs),
  676      memberchk(id(Name), Attrs),
  677      !,
  678      delete_unsafe_label_chars(Name, SafeName),
  679      atom_concat('sec:', SafeName, Label)
  680    },
  681    latex(cmd(label(Label))).
  682section_label(_) -->
  683    [].
  684
  685latex_section_level(chapter,       0).
  686latex_section_level(section,       1).
  687latex_section_level(subsection,    2).
  688latex_section_level(subsubsection, 3).
  689latex_section_level(paragraph,     4).
  690
  691deepen_section_level(Level0, Level1) :-
  692    latex_section_level(Level0, N),
  693    N1 is N + 1,
  694    latex_section_level(Level1, N1).
 delete_unsafe_label_chars(+LabelIn, -LabelOut)
delete unsafe characters from LabelIn. Currently only deletes _, as this appears commonly through filenames, but cannot be handled through the LaTeX processing chain.
  702delete_unsafe_label_chars(LabelIn, LabelOut) :-
  703    atom_chars(LabelIn, Chars),
  704    delete(Chars, '_', CharsOut),
  705    atom_chars(LabelOut, CharsOut).
  706
  707
  708                 /*******************************
  709                 *         \ COMMANDS           *
  710                 *******************************/
 include(+File, +Type, +Options)// is det
Called from [[File]].
  716include(PI, predicate, _) -->
  717    !,
  718    (   {   options(Options)
  719        ->  true
  720        ;   Options = []
  721        },
  722        latex_tokens_for_predicates(PI, Options)
  723    ->  []
  724    ;   latex(cmd(item(['[[', \predref(PI), ']]'])))
  725    ).
  726include(File, Type, Options) -->
  727    { existing_linked_file(File, Path) },
  728    !,
  729    include_file(Path, Type, Options).
  730include(File, _, _) -->
  731    latex(code(['[[', File, ']]'])).
  732
  733include_file(Path, image, Options) -->
  734    { option(caption(Caption), Options) },
  735    !,
  736    latex(cmd(begin(figure, [no_escape(htbp)]))),
  737    latex(cmd(begin(center))),
  738    latex(cmd(includegraphics(Path))),
  739    latex(cmd(end(center))),
  740    latex(cmd(caption(Caption))),
  741    latex(cmd(end(figure))).
  742include_file(Path, image, _) -->
  743    !,
  744    latex(cmd(includegraphics(Path))).
  745include_file(Path, Type, _) -->
  746    { assertion(memberchk(Type, [prolog,wiki])),
  747      current_options(Options0),
  748      select_option(stand_alone(_), Options0, Options1, _),
  749      select_option(section_level(Level0), Options1, Options2, section),
  750      deepen_section_level(Level0, Level),
  751      Options = [stand_alone(false), section_level(Level)|Options2]
  752    },
  753    (   {Type == prolog}
  754    ->  latex_tokens_for_file(Path, Options)
  755    ;   latex_tokens_for_wiki_file(Path, Options)
  756    ).
 file(+File, +Options)// is det
Called from implicitely linked files. The HTML version creates a hyperlink. We just name the file.
  763file(File, _Options) -->
  764    { fragile },
  765    !,
  766    latex(cmd(texttt(File))).
  767file(File, _Options) -->
  768    latex(cmd(file(File))).
 predref(+PI)// is det
Called from name/arity or name//arity patterns in the documentation.
  775predref(Module:Name/Arity) -->
  776    !,
  777    latex(cmd(qpredref(Module, Name, Arity))).
  778predref(Module:Name//Arity) -->
  779    latex(cmd(qdcgref(Module, Name, Arity))).
  780predref(Name/Arity) -->
  781    latex(cmd(predref(Name, Arity))).
  782predref(Name//Arity) -->
  783    latex(cmd(dcgref(Name, Arity))).
 nopredref(+PI)//
Called from name/arity.
  789nopredref(Name/Arity) -->
  790    latex(cmd(nopredref(Name, Arity))).
 flagref(+Flag)//
Reference to a Prolog flag
  796flagref(Flag) -->
  797    latex(cmd(prologflag(Flag))).
 cite(+Citations) is det
Emit a \cite{Citations} command
  803cite(Citations) -->
  804    { atomic_list_concat(Citations, ',', Atom) },
  805    latex(cmd(cite(Atom))).
 tags(+Tags:list(Tag)) is det
Emit tag list produced by the Wiki processor from the @keyword commands.
  812tags([\args(Params)|Rest]) -->
  813    !,
  814    args(Params),
  815    tags_list(Rest).
  816tags(List) -->
  817    tags_list(List).
  818
  819tags_list([]) -->
  820    [].
  821tags_list(List) -->
  822    [ nl(2) ],
  823    latex(cmd(begin(tags))),
  824    latex(List),
  825    latex(cmd(end(tags))),
  826    [ nl(2) ].
 tag(+Tag, +Values:list)// is det
Called from \tag(Name, Values) terms produced by doc_wiki.pl.
  832tag(Tag, [One]) -->
  833    !,
  834    { doc_tag_title(Tag, Title) },
  835    latex([ cmd(tag(Title))
  836          | One
  837          ]).
  838tag(Tag, More) -->
  839    { doc_tag_title(Tag, Title) },
  840    latex([ cmd(mtag(Title)),
  841            \tag_value_list(More)
  842          ]).
  843
  844tag_value_list([H|T]) -->
  845    latex(['- '|H]),
  846    (   { T \== [] }
  847    ->  [latex(' \\\\')],
  848        tag_value_list(T)
  849    ;   []
  850    ).
 args(+Params:list) is det
Called from \args(List) created by doc_wiki.pl. Params is a list of arg(Name, Descr).
  857args(Params) -->
  858    latex([ cmd(begin(arguments)),
  859            \arg_list(Params),
  860            cmd(end(arguments))
  861          ]).
  862
  863arg_list([]) -->
  864    [].
  865arg_list([H|T]) -->
  866    argument(H),
  867    arg_list(T).
  868
  869argument(arg(Name,Descr)) -->
  870    [ nl(1) ],
  871    latex(cmd(arg(Name))), [ latex(' & ') ],
  872    latex(Descr), [latex(' \\\\')].
 file_header(+File, +Options)// is det
Create the file header.
  878file_header(File, Options) -->
  879    { memberchk(file(Title, Comment), Options),
  880      !,
  881      file_synopsis(File, Synopsis, Options)
  882    },
  883    file_title([Synopsis, ': ', Title], File, Options),
  884    { is_structured_comment(Comment, Prefixes),
  885      string_codes(Comment, Codes),
  886      indented_lines(Codes, Prefixes, Lines),
  887      section_comment_header(Lines, _Header, Lines1),
  888      wiki_lines_to_dom(Lines1, [], DOM0),
  889      tags_to_front(DOM0, DOM)
  890    },
  891    latex(DOM),
  892    latex(cmd(vspace('0.7cm'))).
  893file_header(File, Options) -->
  894    { file_synopsis(File, Synopsis, Options)
  895    },
  896    file_title([Synopsis], File, Options).
  897
  898tags_to_front(DOM0, DOM) :-
  899    append(Content, [\tags(Tags)], DOM0),
  900    !,
  901    DOM = [\tags(Tags)|Content].
  902tags_to_front(DOM, DOM).
  903
  904file_synopsis(_File, Synopsis, Options) :-
  905    option(file_synopsis(Synopsis), Options),
  906    !.
  907file_synopsis(File, Synopsis, _) :-
  908    file_name_on_path(File, Term),
  909    unquote_filespec(Term, Unquoted),
  910    format(atom(Synopsis), '~w', [Unquoted]).
 file_title(+Title:list, +File, +Options)// is det
Emit the file-header and manipulation buttons.
  917file_title(Title, File, Options) -->
  918    { option(section_level(Level), Options, section),
  919      Section =.. [Level,Title],
  920      file_base_name(File, BaseExt),
  921      file_name_extension(Base, _, BaseExt),
  922      (   option(label(Seclabel), Options)
  923      ->  true
  924      ;   delete_unsafe_label_chars(Base, Seclabel)
  925      ),
  926      atom_concat('sec:', Seclabel, Label)
  927    },
  928    latex(cmd(Section)),
  929    latex(cmd(label(Label))).
 objects(+Objects:list, +Options)// is det
Emit the documentation body.
  936objects(Objects, Options) -->
  937    objects(Objects, [body], Options).
  938
  939objects([], Mode, _) -->
  940    pop_mode(body, Mode, _).
  941objects([Obj|T], Mode, Options) -->
  942    object(Obj, Mode, Mode1, Options),
  943    objects(T, Mode1, Options).
  944
  945object(doc(Obj,Pos,Comment), Mode0, Mode, Options) -->
  946    !,
  947    object(Obj, Pos, Comment, Mode0, Mode, Options).
  948object(Obj, Mode0, Mode, Options) -->
  949    { doc_comment(Obj, Pos, _Summary, Comment)
  950    },
  951    !,
  952    object(Obj, Pos, Comment, Mode0, Mode, Options).
  953
  954object(Obj, Pos, Comment, Mode0, Mode, Options) -->
  955    { is_pi(Obj),
  956      !,
  957      is_structured_comment(Comment, Prefixes),
  958      string_codes(Comment, Codes),
  959      indented_lines(Codes, Prefixes, Lines),
  960      strip_module(user:Obj, Module, _),
  961      process_modes(Lines, Module, Pos, Modes, Args, Lines1),
  962      (   private(Obj, Options)
  963      ->  Class = privdef           % private definition
  964      ;   multifile(Obj, Options)
  965      ->  Class = multidef
  966      ;   Class = pubdef            % public definition
  967      ),
  968      (   Obj = Module:_
  969      ->  POptions = [module(Module)|Options]
  970      ;   POptions = Options
  971      ),
  972      DOM = [\pred_dt(Modes, Class, POptions), dd(class=defbody, DOM1)],
  973      wiki_lines_to_dom(Lines1, Args, DOM0),
  974      strip_leading_par(DOM0, DOM1),
  975      assert_documented(Obj)
  976    },
  977    need_mode(description, Mode0, Mode),
  978    latex(DOM).
  979object([Obj|Same], Pos, Comment, Mode0, Mode, Options) -->
  980    !,
  981    object(Obj, Pos, Comment, Mode0, Mode, Options),
  982    { maplist(assert_documented, Same) }.
  983object(Obj, _Pos, _Comment, Mode, Mode, _Options) -->
  984    { debug(pldoc, 'Skipped ~p', [Obj]) },
  985    [].
  986
  987assert_documented(Obj) :-
  988    assert(documented(Obj)).
 need_mode(+Mode:atom, +Stack:list, -NewStack:list)// is det
While predicates are part of a description list, sections are not and we therefore need to insert <dl>...</dl> into the output. We do so by demanding an outer environment and push/pop the required elements.
  998need_mode(Mode, Stack, Stack) -->
  999    { Stack = [Mode|_] },
 1000    !,
 1001    [].
 1002need_mode(Mode, Stack, Rest) -->
 1003    { memberchk(Mode, Stack)
 1004    },
 1005    !,
 1006    pop_mode(Mode, Stack, Rest).
 1007need_mode(Mode, Stack, [Mode|Stack]) -->
 1008    !,
 1009    latex(cmd(begin(Mode))).
 1010
 1011pop_mode(Mode, Stack, Stack) -->
 1012    { Stack = [Mode|_] },
 1013    !,
 1014    [].
 1015pop_mode(Mode, [H|Rest0], Rest) -->
 1016    latex(cmd(end(H))),
 1017    pop_mode(Mode, Rest0, Rest).
 pred_dt(+Modes, +Class, Options)// is det
Emit the \predicate{}{}{} header.
Arguments:
Modes- List as returned by process_modes/5.
Class- One of privdef or pubdef.
To be done
- Determinism
 1029pred_dt(Modes, Class, Options) -->
 1030    [nl(2)],
 1031    pred_dt(Modes, [], _Done, [class(Class)|Options]).
 1032
 1033pred_dt([], Done, Done, _) -->
 1034    [].
 1035pred_dt([H|T], Done0, Done, Options) -->
 1036    pred_mode(H, Done0, Done1, Options),
 1037    (   {T == []}
 1038    ->  []
 1039    ;   latex(cmd(nodescription)),
 1040        pred_dt(T, Done1, Done, Options)
 1041    ).
 1042
 1043pred_mode(mode(Head,Vars), Done0, Done, Options) -->
 1044    !,
 1045    { bind_vars(Head, Vars) },
 1046    pred_mode(Head, Done0, Done, Options).
 1047pred_mode(Head is Det, Done0, Done, Options) -->
 1048    !,
 1049    anchored_pred_head(Head, Done0, Done, [det(Det)|Options]).
 1050pred_mode(Head, Done0, Done, Options) -->
 1051    anchored_pred_head(Head, Done0, Done, Options).
 1052
 1053bind_vars(Term, Bindings) :-
 1054    bind_vars(Bindings),
 1055    anon_vars(Term).
 1056
 1057bind_vars([]).
 1058bind_vars([Name=Var|T]) :-
 1059    Var = '$VAR'(Name),
 1060    bind_vars(T).
 anon_vars(+Term) is det
Bind remaining variables in Term to '$VAR'('_'), so they are printed as '_'.
 1067anon_vars(Var) :-
 1068    var(Var),
 1069    !,
 1070    Var = '$VAR'('_').
 1071anon_vars(Term) :-
 1072    compound(Term),
 1073    !,
 1074    Term =.. [_|Args],
 1075    maplist(anon_vars, Args).
 1076anon_vars(_).
 1077
 1078
 1079anchored_pred_head(Head, Done0, Done, Options) -->
 1080    { pred_anchor_name(Head, PI, _Name) },
 1081    (   { memberchk(PI, Done0) }
 1082    ->  { Done = Done0 }
 1083    ;   { Done = [PI|Done0] }
 1084    ),
 1085    pred_head(Head, Options).
 pred_head(+Term, Options) is det
Emit a predicate head. The functor is typeset as a span using class pred and the arguments and var using class arglist.
To be done
- Support determinism in operators
 1095pred_head(//(Head), Options) -->
 1096    !,
 1097    { pred_attributes(Options, Atts),
 1098      Head =.. [Functor|Args],
 1099      length(Args, Arity)
 1100    },
 1101    latex(cmd(dcg(opt(Atts), Functor, Arity, \pred_args(Args, 1)))).
 1102pred_head(Head, _Options) -->                   % Infix operators
 1103    { Head =.. [Functor,Left,Right],
 1104      Functor \== (:),
 1105      is_op_type(Functor, infix), !
 1106    },
 1107    latex(cmd(infixop(Functor, \pred_arg(Left, 1), \pred_arg(Right, 2)))).
 1108pred_head(Head, _Options) -->                   % Prefix operators
 1109    { Head =.. [Functor,Arg],
 1110      is_op_type(Functor, prefix), !
 1111    },
 1112    latex(cmd(prefixop(Functor, \pred_arg(Arg, 1)))).
 1113pred_head(Head, _Options) -->                   % Postfix operators
 1114    { Head =.. [Functor,Arg],
 1115      is_op_type(Functor, postfix), !
 1116    },
 1117    latex(cmd(postfixop(Functor, \pred_arg(Arg, 1)))).
 1118pred_head(M:Head, Options) -->                 % Qualified predicates
 1119    !,
 1120    { pred_attributes(Options, Atts),
 1121      Head =.. [Functor|Args],
 1122      length(Args, Arity)
 1123    },
 1124    latex(cmd(qpredicate(opt(Atts),
 1125                         M,
 1126                         Functor, Arity, \pred_args(Args, 1)))).
 1127pred_head(Head, Options) -->                    % Plain terms
 1128    { pred_attributes(Options, Atts),
 1129      Head =.. [Functor|Args],
 1130      length(Args, Arity)
 1131    },
 1132    latex(cmd(predicate(opt(Atts),
 1133                        Functor, Arity, \pred_args(Args, 1)))).
 pred_attributes(+Options, -Attributes) is det
Create a comma-separated list of predicate attributes, such as determinism, etc.
 1140pred_attributes(Options, Attrs) :-
 1141    findall(A, pred_att(Options, A), As),
 1142    insert_comma(As, Attrs).
 1143
 1144pred_att(Options, Det) :-
 1145    option(det(Det), Options).
 1146pred_att(Options, private) :-
 1147    option(class(privdef), Options).
 1148pred_att(Options, multifile) :-
 1149    option(class(multidef), Options).
 1150
 1151insert_comma([H1,H2|T0], [H1, ','|T]) :-
 1152    !,
 1153    insert_comma([H2|T0], T).
 1154insert_comma(L, L).
 1155
 1156
 1157:- if(current_predicate(is_dict/1)). 1158dict_kv_pairs([]) --> [].
 1159dict_kv_pairs([H|T]) -->
 1160    dict_kv(H),
 1161    (   { T == [] }
 1162    ->  []
 1163    ;   latex(', '),
 1164        dict_kv_pairs(T)
 1165    ).
 1166
 1167dict_kv(Key-Value) -->
 1168    latex(cmd(key(Key))),
 1169    latex(':'),
 1170    term(Value).
 1171:- endif. 1172
 1173pred_args([], _) -->
 1174    [].
 1175pred_args([H|T], I) -->
 1176    pred_arg(H, I),
 1177    (   {T==[]}
 1178    ->  []
 1179    ;   latex(', '),
 1180        { I2 is I + 1 },
 1181        pred_args(T, I2)
 1182    ).
 1183
 1184pred_arg(Var, I) -->
 1185    { var(Var) },
 1186    !,
 1187    latex(['Arg', I]).
 1188pred_arg(...(Term), I) -->
 1189    !,
 1190    pred_arg(Term, I),
 1191    latex(cmd(ldots)).
 1192pred_arg(Term, I) -->
 1193    { Term =.. [Ind,Arg],
 1194      mode_indicator(Ind)
 1195    },
 1196    !,
 1197    latex([Ind, \pred_arg(Arg, I)]).
 1198pred_arg(Arg:Type, _) -->
 1199    !,
 1200    latex([\argname(Arg), :, \argtype(Type)]).
 1201pred_arg(Arg, _) -->
 1202    { atom(Arg) },
 1203    !,
 1204    argname(Arg).
 1205pred_arg(Arg, _) -->
 1206    argtype(Arg).                   % arbitrary term
 1207
 1208argname('$VAR'(Name)) -->
 1209    !,
 1210    latex(Name).
 1211argname(Name) -->
 1212    !,
 1213    latex(Name).
 1214
 1215argtype(Term) -->
 1216    { format(string(S), '~W',
 1217             [ Term,
 1218               [ quoted(true),
 1219                 numbervars(true)
 1220               ]
 1221             ]) },
 1222    latex(S).
 term(+Text, +Term, +Bindings)// is det
Process the \term element as produced by doc_wiki.pl.
To be done
- Properly merge with pred_head//1
 1230term(_, Term, Bindings) -->
 1231    { bind_vars(Bindings) },
 1232    term(Term).
 1233
 1234term('$VAR'(Name)) -->
 1235    !,
 1236    latex(cmd(arg(Name))).
 1237term(Compound) -->
 1238    { callable(Compound),
 1239      !,
 1240      Compound =.. [Functor|Args]
 1241    },
 1242    !,
 1243    term_with_args(Functor, Args).
 1244term(Rest) -->
 1245    latex(Rest).
 1246
 1247term_with_args(Functor, [Left, Right]) -->
 1248    { is_op_type(Functor, infix) },
 1249    !,
 1250    latex(cmd(infixterm(Functor, \term(Left), \term(Right)))).
 1251term_with_args(Functor, [Arg]) -->
 1252    { is_op_type(Functor, prefix) },
 1253    !,
 1254    latex(cmd(prefixterm(Functor, \term(Arg)))).
 1255term_with_args(Functor, [Arg]) -->
 1256    { is_op_type(Functor, postfix) },
 1257    !,
 1258    latex(cmd(postfixterm(Functor, \term(Arg)))).
 1259term_with_args(Functor, Args) -->
 1260    latex(cmd(term(Functor, \pred_args(Args, 1)))).
 termitem(+Text, +Term, +Bindings)// is det
Create a termitem or one of its variations.
 1267termitem(_Text, Term, Bindings) -->
 1268    { bind_vars(Bindings) },
 1269    termitem(Term).
 1270
 1271termitem('$VAR'(Name)) -->
 1272    !,
 1273    latex(cmd(termitem(var(Name), ''))).
 1274:- if(current_predicate(is_dict/1)). 1275termitem(Dict) -->
 1276    { is_dict(Dict),
 1277      !,
 1278      dict_pairs(Dict, Tag, Pairs)
 1279    },
 1280    latex(cmd(dictitem(Tag, \dict_kv_pairs(Pairs)))).
 1281:- endif. 1282termitem(Compound) -->
 1283    { callable(Compound),
 1284      !,
 1285      Compound =.. [Functor|Args]
 1286    },
 1287    !,
 1288    termitem_with_args(Functor, Args).
 1289termitem(Rest) -->
 1290    latex(cmd(termitem(Rest, ''))).
 1291
 1292termitem_with_args(Functor, [Left, Right]) -->
 1293    { is_op_type(Functor, infix) },
 1294    !,
 1295    latex(cmd(infixtermitem(Functor, \term(Left), \term(Right)))).
 1296termitem_with_args(Functor, [Arg]) -->
 1297    { is_op_type(Functor, prefix) },
 1298    !,
 1299    latex(cmd(prefixtermitem(Functor, \term(Arg)))).
 1300termitem_with_args(Functor, [Arg]) -->
 1301    { is_op_type(Functor, postfix) },
 1302    !,
 1303    latex(cmd(postfixtermitem(Functor, \term(Arg)))).
 1304termitem_with_args({}, [Arg]) -->
 1305    !,
 1306    latex(cmd(curltermitem(\argtype(Arg)))).
 1307termitem_with_args(Functor, Args) -->
 1308    latex(cmd(termitem(Functor, \pred_args(Args, 1)))).
 latex_table(+Attrs, +Content)// is det
Emit a table in LaTeX.
 1315latex_table(_Attrs, Content) -->
 1316    { max_columns(Content, 0, _, -, Wittness),
 1317      col_align(Wittness, 1, Content, Align),
 1318      atomics_to_string(Align, '|', S0),
 1319      atomic_list_concat(['|',S0,'|'], Format)
 1320    },
 1321    latex(cmd(begin(quote))),
 1322    latex(cmd(begin(tabulary,
 1323                    no_escape('0.9\\textwidth'),
 1324                    no_escape(Format)))),
 1325    latex(cmd(hline)),
 1326    rows(Content),
 1327    latex(cmd(hline)),
 1328    latex(cmd(end(tabulary))),
 1329    latex(cmd(end(quote))).
 1330
 1331max_columns([], C, C, W, W).
 1332max_columns([tr(List)|T], C0, C, _, W) :-
 1333    length(List, C1),
 1334    C1 >= C0,		% take last as wittness to avoid getting the header
 1335    !,
 1336    max_columns(T, C1, C, List, W).
 1337max_columns([_|T], C0, C, W0, W) :-
 1338    max_columns(T, C0, C, W0, W).
 1339
 1340col_align([], _, _, []).
 1341col_align([CH|CT], Col, Rows, [AH|AT]) :-
 1342    (   member(tr(Cells), Rows),
 1343        nth1(Col, Cells, Cell),
 1344        auto_par(Cell)
 1345    ->  Wrap = auto
 1346    ;   Wrap = false
 1347    ),
 1348    col_align(CH, Wrap, AH),
 1349    Col1 is Col+1,
 1350    col_align(CT, Col1, Rows, AT).
 1351
 1352col_align(td(class=Class,_), Wrap, Align) :-
 1353    align_class(Class, Wrap, Align),
 1354    !.
 1355col_align(_, auto, 'L') :- !.
 1356col_align(_, false, 'l').
 1357
 1358align_class(left,   auto, 'L').
 1359align_class(center, auto, 'C').
 1360align_class(right,  auto, 'R').
 1361align_class(left,   false, 'l').
 1362align_class(center, false, 'c').
 1363align_class(right,  false, 'r').
 1364
 1365rows([]) -->
 1366    [].
 1367rows([tr(Content)|T]) -->
 1368    row(Content),
 1369    rows(T).
 1370
 1371row([]) -->
 1372    [ latex(' \\\\'), nl(1) ].
 1373row([td(_Attrs, Content)|T]) -->
 1374    !,
 1375    row([td(Content)|T]).
 1376row([td(Content)|T]) -->
 1377    fragile_latex(Content),
 1378    (   {T == []}
 1379    ->  []
 1380    ;   [ latex(' & ') ]
 1381    ),
 1382    row(T).
 1383row([th(Content)|T]) -->
 1384    fragile_latex(cmd(textbf(Content))),
 1385    (   {T == []}
 1386    ->  []
 1387    ;   [ latex(' & ') ]
 1388    ),
 1389    row(T).
 auto_par(+Content) is semidet
True when cell Content is a good candidate for auto-wrapping.
 1395auto_par(Content) :-
 1396    phrase(html_text(Content), Words),
 1397    length(Words, WC),
 1398    WC > 1,
 1399    atomics_to_string(Words, Text),
 1400    string_length(Text, Width),
 1401    Width > 15.
 1402
 1403html_text([]) -->
 1404    !.
 1405html_text([H|T]) -->
 1406    !,
 1407    html_text(H),
 1408    html_text(T).
 1409html_text(\predref(Name/Arity)) -->
 1410    !,
 1411    { format(string(S), '~q/~q', [Name, Arity]) },
 1412    [S].
 1413html_text(Compound) -->
 1414    { compound(Compound),
 1415      !,
 1416      functor(Compound, _Name, Arity),
 1417      arg(Arity, Compound, Content)
 1418    },
 1419    html_text(Content).
 1420html_text(Word) -->
 1421    [Word].
 1422
 1423
 1424
 1425
 1426                 /*******************************
 1427                 *      SUMMARY PROCESSING      *
 1428                 *******************************/
 latex_summary(+Options)
If Options contains summary(+File), write a summary of all documented predicates to File.
 1435latex_summary(Options) :-
 1436    option(summary(File), Options),
 1437    !,
 1438    findall(Obj, summary_obj(Obj), Objs),
 1439    maplist(pi_sort_key, Objs, Keyed),
 1440    keysort(Keyed, KSorted),
 1441    pairs_values(KSorted, SortedObj),
 1442    phrase(summarylist(SortedObj, Options), Tokens),
 1443    open(File, write, Out),
 1444    call_cleanup(print_latex(Out, Tokens, Options),
 1445                 close(Out)).
 1446latex_summary(_) :-
 1447    retractall(documented(_)).
 1448
 1449summary_obj(Obj) :-
 1450    documented(Obj),
 1451    pi_head(Obj, Head),
 1452    \+ xref_hook(Head).
 1453
 1454pi_head(M:PI, M:Head) :-
 1455    !,
 1456    pi_head(PI, Head).
 1457pi_head(Name/Arity, Head) :-
 1458    functor(Head, Name, Arity).
 1459pi_head(Name//DCGArity, Head) :-
 1460    Arity is DCGArity+2,
 1461    functor(Head, Name, Arity).
 1462
 1463
 1464pi_sort_key(M:PI, PI-(M:PI)) :- !.
 1465pi_sort_key(PI, PI-PI).
 1466
 1467object_name_arity(_:Term, Type, Name, Arity) :-
 1468    nonvar(Term),
 1469    !,
 1470    object_name_arity(Term, Type, Name, Arity).
 1471object_name_arity(Name/Arity, pred, Name, Arity).
 1472object_name_arity(Name//Arity, dcg, Name, Arity).
 1473
 1474summarylist(Objs, Options) -->
 1475    latex(cmd(begin(summarylist, ll))),
 1476    summary(Objs, Options),
 1477    latex(cmd(end(summarylist))).
 1478
 1479summary([], _) -->
 1480    [].
 1481summary([H|T], Options) -->
 1482    summary_line(H, Options),
 1483    summary(T, Options).
 1484
 1485summary_line(Obj, _Options) -->
 1486    { doc_comment(Obj, _Pos, Summary, _Comment),
 1487      !,
 1488      atom_codes(Summary, Codes),
 1489      phrase(pldoc_wiki:line_tokens(Tokens), Codes), % TBD: proper export
 1490      object_name_arity(Obj, Type, Name, Arity)
 1491    },
 1492    (   {Type == dcg}
 1493    ->  latex(cmd(dcgsummary(Name, Arity, Tokens)))
 1494    ;   { strip_module(Obj, M, _),
 1495          current_op(Pri, Ass, M:Name)
 1496        }
 1497    ->  latex(cmd(oppredsummary(Name, Arity, Ass, Pri, Tokens)))
 1498    ;   latex(cmd(predicatesummary(Name, Arity, Tokens)))
 1499    ).
 1500summary_line(Obj, _Options) -->
 1501    { print_message(warning, pldoc(no_summary_for(Obj)))
 1502    }.
 1503
 1504                 /*******************************
 1505                 *          PRINT TOKENS        *
 1506                 *******************************/
 1507
 1508print_latex(Out, Tokens, Options) :-
 1509    latex_header(Out, Options),
 1510    print_latex_tokens(Tokens, Out),
 1511    latex_footer(Out, Options).
 print_latex_tokens(+Tokens, +Out)
Print primitive LaTeX tokens to Output
 1518print_latex_tokens([], _).
 1519print_latex_tokens([nl(N)|T0], Out) :-
 1520    !,
 1521    max_nl(T0, T, N, NL),
 1522    nl(Out, NL),
 1523    print_latex_tokens(T, Out).
 1524print_latex_tokens([nl_exact(N)|T0], Out) :-
 1525    !,
 1526    nl_exact(T0, T,N, NL),
 1527    nl(Out, NL),
 1528    print_latex_tokens(T, Out).
 1529print_latex_tokens([H|T], Out) :-
 1530    print_latex_token(H, Out),
 1531    print_latex_tokens(T, Out).
 1532
 1533print_latex_token(cmd(Cmd), Out) :-
 1534    !,
 1535    format(Out, '\\~w', [Cmd]).
 1536print_latex_token(curl(open), Out) :-
 1537    !,
 1538    format(Out, '{', []).
 1539print_latex_token(curl(close), Out) :-
 1540    !,
 1541    format(Out, '}', []).
 1542print_latex_token(indent(N), Out) :-
 1543    !,
 1544    format(Out, '~t~*|', [N]).
 1545print_latex_token(nl(N), Out) :-
 1546    !,
 1547    format(Out, '~N', []),
 1548    forall(between(2,N,_), nl(Out)).
 1549print_latex_token(verb(Verb), Out) :-
 1550    is_list(Verb), Verb \== [],
 1551    !,
 1552    atomic_list_concat(Verb, Atom),
 1553    print_latex_token(verb(Atom), Out).
 1554print_latex_token(verb(Verb), Out) :-
 1555    !,
 1556    (   member(C, [$,'|',@,=,'"',^,!]),
 1557        \+ sub_atom(Verb, _, _, _, C)
 1558    ->  atom_replace_char(Verb, '\n', ' ', Verb2),
 1559        format(Out, '\\verb~w~w~w', [C,Verb2,C])
 1560    ;   assertion(fail)
 1561    ).
 1562print_latex_token(code(Code), Out) :-
 1563    !,
 1564    format(Out, '~N\\begin{code}~n', []),
 1565    format(Out, '~w', [Code]),
 1566    format(Out, '~N\\end{code}', []).
 1567print_latex_token(latex(Code), Out) :-
 1568    !,
 1569    write(Out, Code).
 1570print_latex_token(w(Word), Out) :-
 1571    !,
 1572    print_latex(Out, Word).
 1573print_latex_token(no_escape(Text), Out) :-
 1574    !,
 1575    write(Out, Text).
 1576print_latex_token(url_escape(Text), Out) :-
 1577    !,
 1578    print_url(Out, Text).
 1579print_latex_token(Rest, Out) :-
 1580    (   atomic(Rest)
 1581    ->  print_latex(Out, Rest)
 1582    ;   %type_error(latex_token, Rest)
 1583        write(Out, Rest)
 1584    ).
 1585
 1586atom_replace_char(In, From, To, Out) :-
 1587    sub_atom(In, _, _, _, From),
 1588    !,
 1589    atom_chars(In, CharsIn),
 1590    replace(CharsIn, From, To, CharsOut),
 1591    atom_chars(Out, CharsOut).
 1592atom_replace_char(In, _, _, In).
 1593
 1594replace([], _, _, []).
 1595replace([H|T0], H, N, [N|T]) :-
 1596    !,
 1597    replace(T0, H, N, T).
 1598replace([H|T0], F, N, [H|T]) :-
 1599    replace(T0, F, N, T).
 print_latex(+Out, +Text:atomic) is det
Print Text, such that it comes out as normal LaTeX text.
 1606print_latex(Out, String) :-
 1607    atom_string(Atom, String),
 1608    atom_chars(Atom, Chars),
 1609    print_chars(Chars, Out).
 1610
 1611print_chars([], _).
 1612print_chars([H|T], Out) :-
 1613    print_char(H, Out),
 1614    print_chars(T, Out).
 1615
 1616
 1617print_url(Out, String) :-
 1618    string_chars(String, Chars),
 1619    print_url_chars(Chars, Out).
 1620
 1621print_url_chars([], _).
 1622print_url_chars([H|T], Out) :-
 1623    print_url_char(H, Out),
 1624    print_url_chars(T, Out).
 1625
 1626print_url_char('#', Out) :- !, write(Out, '\\#').
 1627print_url_char(C,   Out) :- put_char(Out, C).
 max_nl(T0, T, M0, M)
Remove leading sequence of nl(N) and return the maximum of it.
 1634max_nl([nl(M1)|T0], T, M0, M) :-
 1635    !,
 1636    M2 is max(M1, M0),
 1637    max_nl(T0, T, M2, M).
 1638max_nl([nl_exact(M1)|T0], T, _, M) :-
 1639    !,
 1640    nl_exact(T0, T, M1, M).
 1641max_nl(T, T, M, M).
 1642
 1643nl_exact([nl(_)|T0], T, M0, M) :-
 1644    !,
 1645    max_nl(T0, T, M0, M).
 1646nl_exact([nl_exact(M1)|T0], T, M0, M) :-
 1647    !,
 1648    M2 is max(M1, M0),
 1649    max_nl(T0, T, M2, M).
 1650nl_exact(T, T, M, M).
 1651
 1652
 1653nl(Out, N) :-
 1654    forall(between(1, N, _), nl(Out)).
 print_char(+Char, +Out) is det
Write Char in LaTeX format to Out. This escapes characters for LaTeX where necessary.
 1662print_char('<', Out) :- !, write(Out, '$<$').
 1663print_char('>', Out) :- !, write(Out, '$>$').
 1664print_char('{', Out) :- !, write(Out, '\\{').
 1665print_char('}', Out) :- !, write(Out, '\\}').
 1666print_char('$', Out) :- !, write(Out, '\\$').
 1667print_char('&', Out) :- !, write(Out, '\\&').
 1668print_char('#', Out) :- !, write(Out, '\\#').
 1669print_char('%', Out) :- !, write(Out, '\\%').
 1670print_char('~', Out) :- !, write(Out, '\\Stilde{}').
 1671print_char('\\',Out) :- !, write(Out, '\\bsl{}').
 1672print_char('^', Out) :- !, write(Out, '\\Shat{}').
 1673print_char('|', Out) :- !, write(Out, '\\Sbar{}').
 1674print_char(C,   Out) :- decompose_char(C, Out), !.
 1675print_char(C,   Out) :- put_char(Out, C).
 decompose_char(+Char) is semidet
Deal with diacritics. Relies on Unicode decomposition, where a character with diacritics becomes the plain character, followed by a composing diacritics mark.
 1683:- if(exists_source(library(unicode))). 1684:- use_module(library(unicode)). 1685decompose_char(Char, Out) :-
 1686    char_code(Char, Code),
 1687    Code > 128,
 1688    unicode_map(Char, Decomposed, [decompose]),
 1689    atom_codes(Decomposed, [C,D]),
 1690    diacritic_cmd(D, Cmd),
 1691    format(Out, '\\~w~c', [Cmd, C]).
 1692:- else. 1693decompose_char(_,_) :-
 1694    fail.
 1695:- endif. 1696
 1697diacritic_cmd(768, '`').
 1698diacritic_cmd(769, '\'').
 1699diacritic_cmd(770, '~').
 1700diacritic_cmd(771, '=').
 1701diacritic_cmd(774, 'v').
 1702diacritic_cmd(775, '.').
 1703diacritic_cmd(776, '"').
 1704diacritic_cmd(785, 'u').
 1705diacritic_cmd(807, 'c').
 1706diacritic_cmd(808, 'k').
 identifier(+Atom) is semidet
True if Atom is (lower, alnum*).
 1712identifier(Atom) :-
 1713    atom_chars(Atom, [C0|Chars]),
 1714    char_type(C0, lower),
 1715    all_chartype(Chars, alnum).
 1716
 1717all_chartype([], _).
 1718all_chartype([H|T], Type) :-
 1719    char_type(H, Type),
 1720    all_chartype(T, Type).
 1721
 1722
 1723                 /*******************************
 1724                 *    LATEX SPECIAL SEQUENCES   *
 1725                 *******************************/
 urldef_name(?String, ?DefName)
True if \DefName is a urldef for String. UrlDefs are LaTeX sequences that can be used to represent strings with symbols in fragile environments. Whenever a word can be expressed with a urldef, we will do this to enhance the robustness of the generated LaTeX code.
 1735:- dynamic
 1736    urldef_name/2,
 1737    urlchar/1,                      % true if C appears in ine of them
 1738    urldefs_loaded/1.
 load_urldefs
 load_urldefs(+File)
Load \urldef definitions from File and populate urldef_name/2. See pldoc.sty for details.
 1746load_urldefs :-
 1747    urldefs_loaded(_),
 1748    !.
 1749load_urldefs :-
 1750    absolute_file_name(library('pldoc/pldoc.sty'), File,
 1751                       [ access(read) ]),
 1752    load_urldefs(File).
 1753
 1754load_urldefs(File) :-
 1755    urldefs_loaded(File),
 1756    !.
 1757load_urldefs(File) :-
 1758    open(File, read, In),
 1759    call_cleanup((   read_line_to_codes(In, L0),
 1760                     process_urldefs(L0, In)),
 1761                 close(In)),
 1762    assert(urldefs_loaded(File)).
 1763
 1764process_urldefs(end_of_file, _) :- !.
 1765process_urldefs(Line, In) :-
 1766    (   phrase(urldef(Name, String), Line)
 1767    ->  assert(urldef_name(String, Name)),
 1768        assert_chars(String)
 1769    ;   true
 1770    ),
 1771    read_line_to_codes(In, L2),
 1772    process_urldefs(L2, In).
 1773
 1774assert_chars(String) :-
 1775    atom_chars(String, Chars),
 1776    (   member(C, Chars),
 1777        \+ urlchar(C),
 1778        assert(urlchar(C)),
 1779        fail
 1780    ;   true
 1781    ).
 1782
 1783urldef(Name, String) -->
 1784    "\\urldef{\\", string(NameS), "}\\satom{", string(StringS), "}",
 1785    ws,
 1786    (   "%"
 1787    ->  string(_)
 1788    ;   []
 1789    ),
 1790    eol,
 1791    !,
 1792    { atom_codes(Name, NameS),
 1793      atom_codes(String, StringS)
 1794    }.
 1795
 1796ws --> [C], { C =< 32 }, !, ws.
 1797ws --> [].
 1798
 1799string([]) --> [].
 1800string([H|T]) --> [H], string(T).
 1801
 1802eol([],[]).
 1803
 1804
 1805                 /*******************************
 1806                 *         HEADER/FOOTER        *
 1807                 *******************************/
 1808
 1809latex_header(Out, Options) :-
 1810    (   option(stand_alone(true), Options, true)
 1811    ->  forall(header(Line), format(Out, '~w~n', [Line]))
 1812    ;   true
 1813    ),
 1814    forall(generated(Line), format(Out, '~w~n', [Line])).
 1815
 1816latex_footer(Out, Options) :-
 1817    (   option(stand_alone(true), Options, true)
 1818    ->  forall(footer(Line), format(Out, '~w~n', [Line]))
 1819    ;   true
 1820    ).
 1821
 1822header('\\documentclass[11pt]{article}').
 1823header('\\usepackage{times}').
 1824header('\\usepackage{pldoc}').
 1825header('\\sloppy').
 1826header('\\makeindex').
 1827header('').
 1828header('\\begin{document}').
 1829
 1830footer('').
 1831footer('\\printindex').
 1832footer('\\end{document}').
 1833
 1834generated('% This LaTeX document was generated using the LaTeX backend of PlDoc,').
 1835generated('% The SWI-Prolog documentation system').
 1836generated('').
 1837
 1838
 1839		 /*******************************
 1840		 *            MESSAGES		*
 1841		 *******************************/
 1842
 1843:- multifile
 1844    prolog:message//1. 1845
 1846prolog:message(pldoc(no_summary_for(Obj))) -->
 1847    [ 'No summary documentation for ~p'-[Obj] ]