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

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

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