View source with formatted 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              ]).   66
   67/** <module> PlDoc LaTeX backend
   68
   69This  module  translates  the  Herbrand   term  from  the  documentation
   70extracting module doc_wiki.pl into a  LaTeX   document  for  us with the
   71pl.sty LaTeX style file. The function of  this module is very similar to
   72doc_html.pl, providing the HTML backend,  and the implementation follows
   73the same paradigm. The module can
   74
   75        * Generate LaTeX documentation for a Prolog file, both for
   76        printing and embedding in a larger document using
   77        latex_for_file/3.
   78
   79        * Generate LaTeX from a Wiki file using latex_for_wiki_file/3
   80
   81        * Generate LaTeX for a single predicate or a list of predicates
   82        for embedding in a document using latex_for_predicates/3.
   83
   84@tbd See TODO
   85@author Jan Wielemaker
   86*/
   87
   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([]).
  118
  119%!  doc_latex(+Spec, +OutFile, +Options) is det.
  120%
  121%   Process one or  more  objects,  writing   the  LaTeX  output  to
  122%   OutFile.  Spec is one of:
  123%
  124%     - Name/Arity
  125%       Generate documentation for predicate
  126%     - Name//Arity
  127%       Generate documentation for DCG rule
  128%     - File
  129%       If File is a prolog file (as defined by
  130%       user:prolog_file_type/2), process using
  131%       latex_for_file/3, otherwise process using
  132%       latex_for_wiki_file/3.
  133%
  134%   Typically Spec is either a  list  of   filenames  or  a  list of
  135%   predicate indicators.   Defined options are:
  136%
  137%     - stand_alone(+Bool)
  138%       If =true= (default), create a document that can be run
  139%       through LaTeX.  If =false=, produce a document to be
  140%       included in another LaTeX document.
  141%     - public_only(+Bool)
  142%       If =true= (default), only emit documentation for
  143%       exported predicates.
  144%     - section_level(+Level)
  145%       Outermost section level produced. Level is the
  146%       name of a LaTeX section command.  Default is =section=.
  147%     - summary(+File)
  148%       Write summary declarations to the named File.
  149%     - modules(+List)
  150%       If [[Name/Arity]] needs to be resolved, search for the
  151%       predicates in the given modules.
  152%     - module(+Module)
  153%       Same as modules([Module]).
  154
  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(prolog),
  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    ).
  208
  209
  210%!  latex_for_file(+File, +Out, +Options) is det.
  211%
  212%   Generate a LaTeX description of all commented predicates in
  213%   File, writing the LaTeX text to the stream Out. Supports
  214%   the options =stand_alone=, =public_only= and =section_level=.
  215%   See doc_latex/3 for a description of the options.
  216
  217latex_for_file(FileSpec, Out, Options) :-
  218    load_urldefs,
  219    phrase(latex_tokens_for_file(FileSpec, Options), Tokens),
  220    print_latex(Out, Tokens, Options).
  221
  222
  223%!  latex_tokens_for_file(+FileSpec, +Options)//
  224
  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)).
  238
  239
  240%!  latex_for_wiki_file(+File, +Out, +Options) is det.
  241%
  242%   Write a LaTeX translation of  a  Wiki   file  to  the steam Out.
  243%   Supports   the   options   =stand_alone=,    =public_only=   and
  244%   =section_level=.  See  doc_latex/3  for  a  description  of  the
  245%   options.
  246
  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))).
  264
  265
  266%!  latex_for_predicates(+PI:list, +Out, +Options) is det.
  267%
  268%   Generate LaTeX for a list  of   predicate  indicators. This does
  269%   *not*   produce   the    \begin{description}...\end{description}
  270%   environment, just a plain list   of \predicate, etc. statements.
  271%   The current implementation ignores Options.
  272
  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(no_escape(HREF))))
  392    ;   { atom_concat(#,Sec,HREF) }
  393    ->  latex([Content, ' (', cmd(secref(Sec)), ')'])
  394    ;   latex(cmd(href(no_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
  513%!  latex_special(String, Rest)// is semidet.
  514%
  515%   Deals with special sequences of symbols.
  516
  517latex_special(In, Rest) -->
  518    { url_chars(In, Chars, Rest),
  519      special(Chars),
  520      atom_chars(Atom, Chars),
  521      urldef_name(Atom, Name)
  522    },
  523    !,
  524    latex([cmd(Name), latex('{}')]).
  525
  526special(Chars) :-
  527    memberchk(\, Chars),
  528    !.
  529special(Chars) :-
  530    length(Chars, Len),
  531    Len > 1.
  532
  533url_chars([H|T0], [H|T], Rest) :-
  534    urlchar(H),
  535    !,
  536    url_chars(T0, T, Rest).
  537url_chars(L, [], L).
  538
  539
  540%!  latex_arguments(+Args:list)// is det.
  541%
  542%   Write LaTeX command arguments. If  an   argument  is of the form
  543%   opt(Arg) it is written as  [Arg],   Otherwise  it  is written as
  544%   {Arg}. Note that opt([]) is omitted. I think no LaTeX command is
  545%   designed to handle an empty optional argument special.
  546%
  547%   During processing the arguments it asserts fragile/0 to allow is
  548%   taking care of LaTeX fragile   constructs  (i.e. constructs that
  549%   are not allows inside {...}).
  550
  551latex_arguments(List, Out, Tail) :-
  552    asserta(fragile, Ref),
  553    call_cleanup(fragile_list(List, Out, Tail),
  554                 erase(Ref)).
  555
  556fragile_list([]) --> [].
  557fragile_list([opt([])|T]) -->
  558    !,
  559    fragile_list(T).
  560fragile_list([opt(H)|T]) -->
  561    !,
  562    [ '[' ],
  563    latex_arg(H),
  564    [ ']' ],
  565    fragile_list(T).
  566fragile_list([H|T]) -->
  567    [ curl(open) ],
  568    latex_arg(H),
  569    [ curl(close) ],
  570    fragile_list(T).
  571
  572%!  latex_arg(+In)//
  573%
  574%   Write a LaTeX argument.  If  we  can,   we  will  use  a defined
  575%   urldef_name/2.
  576
  577latex_arg(H) -->
  578    { atomic(H),
  579      atom_string(Atom, H),
  580      urldef_name(Atom, Name)
  581    },
  582    !,
  583    latex(cmd(Name)).
  584latex_arg(H) -->
  585    { maplist(atom, H),
  586      atomic_list_concat(H, Atom),
  587      urldef_name(Atom, Name)
  588    },
  589    !,
  590    latex(cmd(Name)).
  591latex_arg(no_escape(Text)) -->
  592    !,
  593    [no_escape(Text)].
  594latex_arg(H) -->
  595    latex(H).
  596
  597attribute(Att, Attrs) :-
  598    is_list(Attrs),
  599    !,
  600    option(Att, Attrs).
  601attribute(Att, One) :-
  602    option(Att, [One]).
  603
  604if_short_list(Content, If, Else, Env) :-
  605    (   short_list(Content)
  606    ->  Env = If
  607    ;   Env = Else
  608    ).
  609
  610%!  short_list(+Content) is semidet.
  611%
  612%   True if Content describes the content of a dl or ul/ol list
  613%   where each elemenent has short content.
  614
  615short_list([]).
  616short_list([_,dd(Content)|T]) :-
  617    !,
  618    short_content(Content),
  619    short_list(T).
  620short_list([_,dd(_, Content)|T]) :-
  621    !,
  622    short_content(Content),
  623    short_list(T).
  624short_list([li(Content)|T]) :-
  625    short_content(Content),
  626    short_list(T).
  627
  628short_content(Content) :-
  629    phrase(latex(Content), Tokens),
  630    summed_string_len(Tokens, 0, Len),
  631    Len < 50.
  632
  633summed_string_len([], Len, Len).
  634summed_string_len([H|T], L0, L) :-
  635    atomic(H),
  636    !,
  637    atom_length(H, AL),
  638    L1 is L0 + AL,
  639    summed_string_len(T, L1, L).
  640summed_string_len([_|T], L0, L) :-
  641    summed_string_len(T, L0, L).
  642
  643
  644%!  latex_section(+Level, +Attributes, +Content)// is det.
  645%
  646%   Emit a LaTeX section,  keeping  track   of  the  desired highest
  647%   section level.
  648%
  649%   @param Level    Desired level, relative to the base-level.  Must
  650%                   be a non-negative integer.
  651
  652latex_section(Level, Attrs, Content) -->
  653    { current_options(Options),
  654      option(section_level(LaTexSection), Options, section),
  655      latex_section_level(LaTexSection, BaseLevel),
  656      FinalLevel is BaseLevel+Level,
  657      (   latex_section_level(SectionCommand, FinalLevel)
  658      ->  Term =.. [SectionCommand, Content]
  659      ;   domain_error(latex_section_level, FinalLevel)
  660      )
  661    },
  662    latex(cmd(Term)),
  663    section_label(Attrs).
  664
  665section_label(Attrs) -->
  666    { is_list(Attrs),
  667      memberchk(id(Name), Attrs),
  668      !,
  669      delete_unsafe_label_chars(Name, SafeName),
  670      atom_concat('sec:', SafeName, Label)
  671    },
  672    latex(cmd(label(Label))).
  673section_label(_) -->
  674    [].
  675
  676latex_section_level(chapter,       0).
  677latex_section_level(section,       1).
  678latex_section_level(subsection,    2).
  679latex_section_level(subsubsection, 3).
  680latex_section_level(paragraph,     4).
  681
  682deepen_section_level(Level0, Level1) :-
  683    latex_section_level(Level0, N),
  684    N1 is N + 1,
  685    latex_section_level(Level1, N1).
  686
  687%!  delete_unsafe_label_chars(+LabelIn, -LabelOut)
  688%
  689%   delete unsafe characters from LabelIn. Currently only deletes _,
  690%   as this appears  commonly  through   filenames,  but  cannot  be
  691%   handled through the LaTeX processing chain.
  692
  693delete_unsafe_label_chars(LabelIn, LabelOut) :-
  694    atom_chars(LabelIn, Chars),
  695    delete(Chars, '_', CharsOut),
  696    atom_chars(LabelOut, CharsOut).
  697
  698
  699                 /*******************************
  700                 *         \ COMMANDS           *
  701                 *******************************/
  702
  703%!  include(+File, +Type, +Options)// is det.
  704%
  705%   Called from [[File]].
  706
  707include(PI, predicate, _) -->
  708    !,
  709    (   {   options(Options)
  710        ->  true
  711        ;   Options = []
  712        },
  713        latex_tokens_for_predicates(PI, Options)
  714    ->  []
  715    ;   latex(cmd(item(['[[', \predref(PI), ']]'])))
  716    ).
  717include(File, Type, Options) -->
  718    { existing_linked_file(File, Path) },
  719    !,
  720    include_file(Path, Type, Options).
  721include(File, _, _) -->
  722    latex(code(['[[', File, ']]'])).
  723
  724include_file(Path, image, Options) -->
  725    { option(caption(Caption), Options) },
  726    !,
  727    latex(cmd(begin(figure, [no_escape(htbp)]))),
  728    latex(cmd(begin(center))),
  729    latex(cmd(includegraphics(Path))),
  730    latex(cmd(end(center))),
  731    latex(cmd(caption(Caption))),
  732    latex(cmd(end(figure))).
  733include_file(Path, image, _) -->
  734    !,
  735    latex(cmd(includegraphics(Path))).
  736include_file(Path, Type, _) -->
  737    { assertion(memberchk(Type, [prolog,wiki])),
  738      current_options(Options0),
  739      select_option(stand_alone(_), Options0, Options1, _),
  740      select_option(section_level(Level0), Options1, Options2, section),
  741      deepen_section_level(Level0, Level),
  742      Options = [stand_alone(false), section_level(Level)|Options2]
  743    },
  744    (   {Type == prolog}
  745    ->  latex_tokens_for_file(Path, Options)
  746    ;   latex_tokens_for_wiki_file(Path, Options)
  747    ).
  748
  749%!  file(+File, +Options)// is det.
  750%
  751%   Called from implicitely linked files.  The HTML version creates
  752%   a hyperlink.  We just name the file.
  753
  754file(File, _Options) -->
  755    { fragile },
  756    !,
  757    latex(cmd(texttt(File))).
  758file(File, _Options) -->
  759    latex(cmd(file(File))).
  760
  761%!  predref(+PI)// is det.
  762%
  763%   Called  from  name/arity  or   name//arity    patterns   in  the
  764%   documentation.
  765
  766predref(Module:Name/Arity) -->
  767    !,
  768    latex(cmd(qpredref(Module, Name, Arity))).
  769predref(Module:Name//Arity) -->
  770    latex(cmd(qdcgref(Module, Name, Arity))).
  771predref(Name/Arity) -->
  772    latex(cmd(predref(Name, Arity))).
  773predref(Name//Arity) -->
  774    latex(cmd(dcgref(Name, Arity))).
  775
  776%!  tags(+Tags:list(Tag)) is det.
  777%
  778%   Emit tag list produced by the   Wiki processor from the @keyword
  779%   commands.
  780
  781tags([\args(Params)|Rest]) -->
  782    !,
  783    args(Params),
  784    tags_list(Rest).
  785tags(List) -->
  786    tags_list(List).
  787
  788tags_list([]) -->
  789    [].
  790tags_list(List) -->
  791    [ nl(2) ],
  792    latex(cmd(begin(tags))),
  793    latex(List),
  794    latex(cmd(end(tags))),
  795    [ nl(2) ].
  796
  797%!  tag(+Tag, +Values:list)// is det.
  798%
  799%   Called from \tag(Name, Values) terms produced by doc_wiki.pl.
  800
  801tag(Tag, [One]) -->
  802    !,
  803    { doc_tag_title(Tag, Title) },
  804    latex([ cmd(tag(Title))
  805          | One
  806          ]).
  807tag(Tag, More) -->
  808    { doc_tag_title(Tag, Title) },
  809    latex([ cmd(mtag(Title)),
  810            \tag_value_list(More)
  811          ]).
  812
  813tag_value_list([H|T]) -->
  814    latex(['- '|H]),
  815    (   { T \== [] }
  816    ->  [latex(' \\\\')],
  817        tag_value_list(T)
  818    ;   []
  819    ).
  820
  821%!  args(+Params:list) is det.
  822%
  823%   Called from \args(List) created by   doc_wiki.pl.  Params is a
  824%   list of arg(Name, Descr).
  825
  826args(Params) -->
  827    latex([ cmd(begin(arguments)),
  828            \arg_list(Params),
  829            cmd(end(arguments))
  830          ]).
  831
  832arg_list([]) -->
  833    [].
  834arg_list([H|T]) -->
  835    argument(H),
  836    arg_list(T).
  837
  838argument(arg(Name,Descr)) -->
  839    [ nl(1) ],
  840    latex(cmd(arg(Name))), [ latex(' & ') ],
  841    latex(Descr), [latex(' \\\\')].
  842
  843%!  file_header(+File, +Options)// is det.
  844%
  845%   Create the file header.
  846
  847file_header(File, Options) -->
  848    { memberchk(file(Title, Comment), Options),
  849      !,
  850      file_synopsis(File, Synopsis)
  851    },
  852    file_title([Synopsis, ': ', Title], File, Options),
  853    { is_structured_comment(Comment, Prefixes),
  854      string_codes(Comment, Codes),
  855      indented_lines(Codes, Prefixes, Lines),
  856      section_comment_header(Lines, _Header, Lines1),
  857      wiki_lines_to_dom(Lines1, [], DOM0),
  858      tags_to_front(DOM0, DOM)
  859    },
  860    latex(DOM),
  861    latex(cmd(vspace('0.7cm'))).
  862file_header(File, Options) -->
  863    { file_synopsis(File, Synopsis)
  864    },
  865    file_title([Synopsis], File, Options).
  866
  867tags_to_front(DOM0, DOM) :-
  868    append(Content, [\tags(Tags)], DOM0),
  869    !,
  870    DOM = [\tags(Tags)|Content].
  871tags_to_front(DOM, DOM).
  872
  873file_synopsis(File, Synopsis) :-
  874    file_name_on_path(File, Term),
  875    unquote_filespec(Term, Unquoted),
  876    format(atom(Synopsis), '~w', [Unquoted]).
  877
  878
  879%!  file_title(+Title:list, +File, +Options)// is det
  880%
  881%   Emit the file-header and manipulation buttons.
  882
  883file_title(Title, File, Options) -->
  884    { option(section_level(Level), Options, section),
  885      Section =.. [Level,Title],
  886      file_base_name(File, BaseExt),
  887      file_name_extension(Base, _, BaseExt),
  888      delete_unsafe_label_chars(Base, SafeBase),
  889      atom_concat('sec:', SafeBase, Label)
  890    },
  891    latex(cmd(Section)),
  892    latex(cmd(label(Label))).
  893
  894
  895%!  objects(+Objects:list, +Options)// is det.
  896%
  897%   Emit the documentation body.
  898
  899objects(Objects, Options) -->
  900    objects(Objects, [body], Options).
  901
  902objects([], Mode, _) -->
  903    pop_mode(body, Mode, _).
  904objects([Obj|T], Mode, Options) -->
  905    object(Obj, Mode, Mode1, Options),
  906    objects(T, Mode1, Options).
  907
  908object(doc(Obj,Pos,Comment), Mode0, Mode, Options) -->
  909    !,
  910    object(Obj, Pos, Comment, Mode0, Mode, Options).
  911object(Obj, Mode0, Mode, Options) -->
  912    { doc_comment(Obj, Pos, _Summary, Comment)
  913    },
  914    !,
  915    object(Obj, Pos, Comment, Mode0, Mode, Options).
  916
  917object(Obj, Pos, Comment, Mode0, Mode, Options) -->
  918    { is_pi(Obj),
  919      !,
  920      is_structured_comment(Comment, Prefixes),
  921      string_codes(Comment, Codes),
  922      indented_lines(Codes, Prefixes, Lines),
  923      strip_module(user:Obj, Module, _),
  924      process_modes(Lines, Module, Pos, Modes, Args, Lines1),
  925      (   private(Obj, Options)
  926      ->  Class = privdef           % private definition
  927      ;   multifile(Obj, Options)
  928      ->  Class = multidef
  929      ;   Class = pubdef            % public definition
  930      ),
  931      (   Obj = Module:_
  932      ->  POptions = [module(Module)|Options]
  933      ;   POptions = Options
  934      ),
  935      DOM = [\pred_dt(Modes, Class, POptions), dd(class=defbody, DOM1)],
  936      wiki_lines_to_dom(Lines1, Args, DOM0),
  937      strip_leading_par(DOM0, DOM1),
  938      assert_documented(Obj)
  939    },
  940    need_mode(description, Mode0, Mode),
  941    latex(DOM).
  942object([Obj|Same], Pos, Comment, Mode0, Mode, Options) -->
  943    !,
  944    object(Obj, Pos, Comment, Mode0, Mode, Options),
  945    { maplist(assert_documented, Same) }.
  946object(Obj, _Pos, _Comment, Mode, Mode, _Options) -->
  947    { debug(pldoc, 'Skipped ~p', [Obj]) },
  948    [].
  949
  950assert_documented(Obj) :-
  951    assert(documented(Obj)).
  952
  953
  954%!  need_mode(+Mode:atom, +Stack:list, -NewStack:list)// is det.
  955%
  956%   While predicates are part of a   description  list, sections are
  957%   not and we therefore  need  to   insert  <dl>...</dl>  into  the
  958%   output. We do so by demanding  an outer environment and push/pop
  959%   the required elements.
  960
  961need_mode(Mode, Stack, Stack) -->
  962    { Stack = [Mode|_] },
  963    !,
  964    [].
  965need_mode(Mode, Stack, Rest) -->
  966    { memberchk(Mode, Stack)
  967    },
  968    !,
  969    pop_mode(Mode, Stack, Rest).
  970need_mode(Mode, Stack, [Mode|Stack]) -->
  971    !,
  972    latex(cmd(begin(Mode))).
  973
  974pop_mode(Mode, Stack, Stack) -->
  975    { Stack = [Mode|_] },
  976    !,
  977    [].
  978pop_mode(Mode, [H|Rest0], Rest) -->
  979    latex(cmd(end(H))),
  980    pop_mode(Mode, Rest0, Rest).
  981
  982
  983%!  pred_dt(+Modes, +Class, Options)// is det.
  984%
  985%   Emit the \predicate{}{}{} header.
  986%
  987%   @param Modes    List as returned by process_modes/5.
  988%   @param Class    One of =privdef= or =pubdef=.
  989%
  990%   @tbd    Determinism
  991
  992pred_dt(Modes, Class, Options) -->
  993    [nl(2)],
  994    pred_dt(Modes, [], _Done, [class(Class)|Options]).
  995
  996pred_dt([], Done, Done, _) -->
  997    [].
  998pred_dt([H|T], Done0, Done, Options) -->
  999    pred_mode(H, Done0, Done1, Options),
 1000    (   {T == []}
 1001    ->  []
 1002    ;   latex(cmd(nodescription)),
 1003        pred_dt(T, Done1, Done, Options)
 1004    ).
 1005
 1006pred_mode(mode(Head,Vars), Done0, Done, Options) -->
 1007    !,
 1008    { bind_vars(Head, Vars) },
 1009    pred_mode(Head, Done0, Done, Options).
 1010pred_mode(Head is Det, Done0, Done, Options) -->
 1011    !,
 1012    anchored_pred_head(Head, Done0, Done, [det(Det)|Options]).
 1013pred_mode(Head, Done0, Done, Options) -->
 1014    anchored_pred_head(Head, Done0, Done, Options).
 1015
 1016bind_vars(Term, Bindings) :-
 1017    bind_vars(Bindings),
 1018    anon_vars(Term).
 1019
 1020bind_vars([]).
 1021bind_vars([Name=Var|T]) :-
 1022    Var = '$VAR'(Name),
 1023    bind_vars(T).
 1024
 1025%!  anon_vars(+Term) is det.
 1026%
 1027%   Bind remaining variables in Term to '$VAR'('_'), so they are
 1028%   printed as '_'.
 1029
 1030anon_vars(Var) :-
 1031    var(Var),
 1032    !,
 1033    Var = '$VAR'('_').
 1034anon_vars(Term) :-
 1035    compound(Term),
 1036    !,
 1037    Term =.. [_|Args],
 1038    maplist(anon_vars, Args).
 1039anon_vars(_).
 1040
 1041
 1042anchored_pred_head(Head, Done0, Done, Options) -->
 1043    { pred_anchor_name(Head, PI, _Name) },
 1044    (   { memberchk(PI, Done0) }
 1045    ->  { Done = Done0 }
 1046    ;   { Done = [PI|Done0] }
 1047    ),
 1048    pred_head(Head, Options).
 1049
 1050
 1051%!  pred_head(+Term, Options) is det.
 1052%
 1053%   Emit a predicate head. The functor is  typeset as a =span= using
 1054%   class =pred= and the arguments and =var= using class =arglist=.
 1055%
 1056%   @tbd Support determinism in operators
 1057
 1058pred_head(//(Head), Options) -->
 1059    !,
 1060    { pred_attributes(Options, Atts),
 1061      Head =.. [Functor|Args],
 1062      length(Args, Arity)
 1063    },
 1064    latex(cmd(dcg(opt(Atts), Functor, Arity, \pred_args(Args, 1)))).
 1065pred_head(Head, _Options) -->                   % Infix operators
 1066    { Head =.. [Functor,Left,Right],
 1067      Functor \== (:),
 1068      is_op_type(Functor, infix), !
 1069    },
 1070    latex(cmd(infixop(Functor, \pred_arg(Left, 1), \pred_arg(Right, 2)))).
 1071pred_head(Head, _Options) -->                   % Prefix operators
 1072    { Head =.. [Functor,Arg],
 1073      is_op_type(Functor, prefix), !
 1074    },
 1075    latex(cmd(prefixop(Functor, \pred_arg(Arg, 1)))).
 1076pred_head(Head, _Options) -->                   % Postfix operators
 1077    { Head =.. [Functor,Arg],
 1078      is_op_type(Functor, postfix), !
 1079    },
 1080    latex(cmd(postfixop(Functor, \pred_arg(Arg, 1)))).
 1081pred_head(M:Head, Options) -->                 % Qualified predicates
 1082    !,
 1083    { pred_attributes(Options, Atts),
 1084      Head =.. [Functor|Args],
 1085      length(Args, Arity)
 1086    },
 1087    latex(cmd(qpredicate(opt(Atts),
 1088                         M,
 1089                         Functor, Arity, \pred_args(Args, 1)))).
 1090pred_head(Head, Options) -->                    % Plain terms
 1091    { pred_attributes(Options, Atts),
 1092      Head =.. [Functor|Args],
 1093      length(Args, Arity)
 1094    },
 1095    latex(cmd(predicate(opt(Atts),
 1096                        Functor, Arity, \pred_args(Args, 1)))).
 1097
 1098%!  pred_attributes(+Options, -Attributes) is det.
 1099%
 1100%   Create a comma-separated list of   predicate attributes, such as
 1101%   determinism, etc.
 1102
 1103pred_attributes(Options, Attrs) :-
 1104    findall(A, pred_att(Options, A), As),
 1105    insert_comma(As, Attrs).
 1106
 1107pred_att(Options, Det) :-
 1108    option(det(Det), Options).
 1109pred_att(Options, private) :-
 1110    option(class(privdef), Options).
 1111pred_att(Options, multifile) :-
 1112    option(class(multidef), Options).
 1113
 1114insert_comma([H1,H2|T0], [H1, ','|T]) :-
 1115    !,
 1116    insert_comma([H2|T0], T).
 1117insert_comma(L, L).
 1118
 1119
 1120:- if(current_predicate(is_dict/1)). 1121dict_kv_pairs([]) --> [].
 1122dict_kv_pairs([H|T]) -->
 1123    dict_kv(H),
 1124    (   { T == [] }
 1125    ->  []
 1126    ;   latex(', '),
 1127        dict_kv_pairs(T)
 1128    ).
 1129
 1130dict_kv(Key-Value) -->
 1131    latex(cmd(key(Key))),
 1132    latex(':'),
 1133    term(Value).
 1134:- endif. 1135
 1136pred_args([], _) -->
 1137    [].
 1138pred_args([H|T], I) -->
 1139    pred_arg(H, I),
 1140    (   {T==[]}
 1141    ->  []
 1142    ;   latex(', '),
 1143        { I2 is I + 1 },
 1144        pred_args(T, I2)
 1145    ).
 1146
 1147pred_arg(Var, I) -->
 1148    { var(Var) },
 1149    !,
 1150    latex(['Arg', I]).
 1151pred_arg(...(Term), I) -->
 1152    !,
 1153    pred_arg(Term, I),
 1154    latex(cmd(ldots)).
 1155pred_arg(Term, I) -->
 1156    { Term =.. [Ind,Arg],
 1157      mode_indicator(Ind)
 1158    },
 1159    !,
 1160    latex([Ind, \pred_arg(Arg, I)]).
 1161pred_arg(Arg:Type, _) -->
 1162    !,
 1163    latex([\argname(Arg), :, \argtype(Type)]).
 1164pred_arg(Arg, _) -->
 1165    { atom(Arg) },
 1166    !,
 1167    argname(Arg).
 1168pred_arg(Arg, _) -->
 1169    argtype(Arg).                   % arbitrary term
 1170
 1171argname('$VAR'(Name)) -->
 1172    !,
 1173    latex(Name).
 1174argname(Name) -->
 1175    !,
 1176    latex(Name).
 1177
 1178argtype(Term) -->
 1179    { format(string(S), '~W',
 1180             [ Term,
 1181               [ quoted(true),
 1182                 numbervars(true)
 1183               ]
 1184             ]) },
 1185    latex(S).
 1186
 1187%!  term(+Text, +Term, +Bindings)// is det.
 1188%
 1189%   Process the \term element as produced by doc_wiki.pl.
 1190%
 1191%   @tbd    Properly merge with pred_head//1
 1192
 1193term(_, Term, Bindings) -->
 1194    { bind_vars(Bindings) },
 1195    term(Term).
 1196
 1197term('$VAR'(Name)) -->
 1198    !,
 1199    latex(cmd(arg(Name))).
 1200term(Compound) -->
 1201    { callable(Compound),
 1202      !,
 1203      Compound =.. [Functor|Args]
 1204    },
 1205    !,
 1206    term_with_args(Functor, Args).
 1207term(Rest) -->
 1208    latex(Rest).
 1209
 1210term_with_args(Functor, [Left, Right]) -->
 1211    { is_op_type(Functor, infix) },
 1212    !,
 1213    latex(cmd(infixterm(Functor, \term(Left), \term(Right)))).
 1214term_with_args(Functor, [Arg]) -->
 1215    { is_op_type(Functor, prefix) },
 1216    !,
 1217    latex(cmd(prefixterm(Functor, \term(Arg)))).
 1218term_with_args(Functor, [Arg]) -->
 1219    { is_op_type(Functor, postfix) },
 1220    !,
 1221    latex(cmd(postfixterm(Functor, \term(Arg)))).
 1222term_with_args(Functor, Args) -->
 1223    latex(cmd(term(Functor, \pred_args(Args, 1)))).
 1224
 1225
 1226%!  termitem(+Text, +Term, +Bindings)// is det.
 1227%
 1228%   Create a termitem or one of its variations.
 1229
 1230termitem(_Text, Term, Bindings) -->
 1231    { bind_vars(Bindings) },
 1232    termitem(Term).
 1233
 1234termitem('$VAR'(Name)) -->
 1235    !,
 1236    latex(cmd(termitem(var(Name), ''))).
 1237:- if(current_predicate(is_dict/1)). 1238termitem(Dict) -->
 1239    { is_dict(Dict),
 1240      !,
 1241      dict_pairs(Dict, Tag, Pairs)
 1242    },
 1243    latex(cmd(dictitem(Tag, \dict_kv_pairs(Pairs)))).
 1244:- endif. 1245termitem(Compound) -->
 1246    { callable(Compound),
 1247      !,
 1248      Compound =.. [Functor|Args]
 1249    },
 1250    !,
 1251    termitem_with_args(Functor, Args).
 1252termitem(Rest) -->
 1253    latex(cmd(termitem(Rest, ''))).
 1254
 1255termitem_with_args(Functor, [Left, Right]) -->
 1256    { is_op_type(Functor, infix) },
 1257    !,
 1258    latex(cmd(infixtermitem(Functor, \term(Left), \term(Right)))).
 1259termitem_with_args(Functor, [Arg]) -->
 1260    { is_op_type(Functor, prefix) },
 1261    !,
 1262    latex(cmd(prefixtermitem(Functor, \term(Arg)))).
 1263termitem_with_args(Functor, [Arg]) -->
 1264    { is_op_type(Functor, postfix) },
 1265    !,
 1266    latex(cmd(postfixtermitem(Functor, \term(Arg)))).
 1267termitem_with_args({}, [Arg]) -->
 1268    !,
 1269    latex(cmd(curltermitem(\argtype(Arg)))).
 1270termitem_with_args(Functor, Args) -->
 1271    latex(cmd(termitem(Functor, \pred_args(Args, 1)))).
 1272
 1273
 1274%!  latex_table(+Attrs, +Content)// is det.
 1275%
 1276%   Emit a table in LaTeX.
 1277
 1278latex_table(_Attrs, Content) -->
 1279    { max_columns(Content, 0, N),
 1280      make_frame(N, l, List),
 1281      atom_chars(Format, ['|'|List])
 1282    },
 1283%       latex(cmd(begin(table, opt(h)))),
 1284    latex(cmd(begin(quote))),
 1285    latex(cmd(begin(tabular, no_escape(Format)))),
 1286    latex(cmd(hline)),
 1287    rows(Content),
 1288    latex(cmd(hline)),
 1289    latex(cmd(end(tabular))),
 1290    latex(cmd(end(quote))).
 1291%       latex(cmd(end(table))).
 1292
 1293max_columns([], C, C).
 1294max_columns([tr(List)|T], C0, C) :-
 1295    length(List, C1),
 1296    C2 is max(C0, C1),
 1297    max_columns(T, C2, C).
 1298
 1299make_frame(0, _, []) :- !.
 1300make_frame(N, C, [C,'|'|T]) :-
 1301    N2 is N - 1,
 1302    make_frame(N2, C, T).
 1303
 1304rows([]) -->
 1305    [].
 1306rows([tr(Content)|T]) -->
 1307    row(Content),
 1308    rows(T).
 1309
 1310row([]) -->
 1311    [ latex(' \\\\'), nl(1) ].
 1312row([td(Content)|T]) -->
 1313    latex(Content),
 1314    (   {T == []}
 1315    ->  []
 1316    ;   [ latex(' & ') ]
 1317    ),
 1318    row(T).
 1319
 1320
 1321                 /*******************************
 1322                 *      SUMMARY PROCESSING      *
 1323                 *******************************/
 1324
 1325%!  latex_summary(+Options)
 1326%
 1327%   If Options contains  summary(+File),  write   a  summary  of all
 1328%   documented predicates to File.
 1329
 1330latex_summary(Options) :-
 1331    option(summary(File), Options),
 1332    !,
 1333    findall(Obj, summary_obj(Obj), Objs),
 1334    maplist(pi_sort_key, Objs, Keyed),
 1335    keysort(Keyed, KSorted),
 1336    pairs_values(KSorted, SortedObj),
 1337    phrase(summarylist(SortedObj, Options), Tokens),
 1338    open(File, write, Out),
 1339    call_cleanup(print_latex(Out, Tokens, Options),
 1340                 close(Out)).
 1341latex_summary(_) :-
 1342    retractall(documented(_)).
 1343
 1344summary_obj(Obj) :-
 1345    documented(Obj),
 1346    pi_head(Obj, Head),
 1347    \+ xref_hook(Head).
 1348
 1349pi_head(M:PI, M:Head) :-
 1350    !,
 1351    pi_head(PI, Head).
 1352pi_head(Name/Arity, Head) :-
 1353    functor(Head, Name, Arity).
 1354pi_head(Name//DCGArity, Head) :-
 1355    Arity is DCGArity+2,
 1356    functor(Head, Name, Arity).
 1357
 1358
 1359pi_sort_key(M:PI, PI-(M:PI)) :- !.
 1360pi_sort_key(PI, PI-PI).
 1361
 1362object_name_arity(_:Term, Type, Name, Arity) :-
 1363    nonvar(Term),
 1364    !,
 1365    object_name_arity(Term, Type, Name, Arity).
 1366object_name_arity(Name/Arity, pred, Name, Arity).
 1367object_name_arity(Name//Arity, dcg, Name, Arity).
 1368
 1369summarylist(Objs, Options) -->
 1370    latex(cmd(begin(summarylist, ll))),
 1371    summary(Objs, Options),
 1372    latex(cmd(end(summarylist))).
 1373
 1374summary([], _) -->
 1375    [].
 1376summary([H|T], Options) -->
 1377    summary_line(H, Options),
 1378    summary(T, Options).
 1379
 1380summary_line(Obj, _Options) -->
 1381    { doc_comment(Obj, _Pos, Summary, _Comment),
 1382      !,
 1383      atom_codes(Summary, Codes),
 1384      phrase(pldoc_wiki:line_tokens(Tokens), Codes), % TBD: proper export
 1385      object_name_arity(Obj, Type, Name, Arity)
 1386    },
 1387    (   {Type == dcg}
 1388    ->  latex(cmd(dcgsummary(Name, Arity, Tokens)))
 1389    ;   { strip_module(Obj, M, _),
 1390          current_op(Pri, Ass, M:Name)
 1391        }
 1392    ->  latex(cmd(oppredsummary(Name, Arity, Ass, Pri, Tokens)))
 1393    ;   latex(cmd(predicatesummary(Name, Arity, Tokens)))
 1394    ).
 1395summary_line(Obj, _Options) -->
 1396    { print_message(warning, pldoc(no_summary_for(Obj)))
 1397    }.
 1398
 1399                 /*******************************
 1400                 *          PRINT TOKENS        *
 1401                 *******************************/
 1402
 1403print_latex(Out, Tokens, Options) :-
 1404    latex_header(Out, Options),
 1405    print_latex_tokens(Tokens, Out),
 1406    latex_footer(Out, Options).
 1407
 1408
 1409%!  print_latex_tokens(+Tokens, +Out)
 1410%
 1411%   Print primitive LaTeX tokens to Output
 1412
 1413print_latex_tokens([], _).
 1414print_latex_tokens([nl(N)|T0], Out) :-
 1415    !,
 1416    max_nl(T0, T, N, NL),
 1417    nl(Out, NL),
 1418    print_latex_tokens(T, Out).
 1419print_latex_tokens([nl_exact(N)|T0], Out) :-
 1420    !,
 1421    nl_exact(T0, T,N, NL),
 1422    nl(Out, NL),
 1423    print_latex_tokens(T, Out).
 1424print_latex_tokens([H|T], Out) :-
 1425    print_latex_token(H, Out),
 1426    print_latex_tokens(T, Out).
 1427
 1428print_latex_token(cmd(Cmd), Out) :-
 1429    !,
 1430    format(Out, '\\~w', [Cmd]).
 1431print_latex_token(curl(open), Out) :-
 1432    !,
 1433    format(Out, '{', []).
 1434print_latex_token(curl(close), Out) :-
 1435    !,
 1436    format(Out, '}', []).
 1437print_latex_token(indent(N), Out) :-
 1438    !,
 1439    format(Out, '~t~*|', [N]).
 1440print_latex_token(nl(N), Out) :-
 1441    !,
 1442    format(Out, '~N', []),
 1443    forall(between(2,N,_), nl(Out)).
 1444print_latex_token(verb(Verb), Out) :-
 1445    is_list(Verb), Verb \== [],
 1446    !,
 1447    atomic_list_concat(Verb, Atom),
 1448    print_latex_token(verb(Atom), Out).
 1449print_latex_token(verb(Verb), Out) :-
 1450    !,
 1451    (   member(C, [$,'|',@,=,'"',^,!]),
 1452        \+ sub_atom(Verb, _, _, _, C)
 1453    ->  atom_replace_char(Verb, '\n', ' ', Verb2),
 1454        format(Out, '\\verb~w~w~w', [C,Verb2,C])
 1455    ;   assertion(fail)
 1456    ).
 1457print_latex_token(code(Code), Out) :-
 1458    !,
 1459    format(Out, '~N\\begin{code}~n', []),
 1460    format(Out, '~w', [Code]),
 1461    format(Out, '~N\\end{code}', []).
 1462print_latex_token(latex(Code), Out) :-
 1463    !,
 1464    write(Out, Code).
 1465print_latex_token(w(Word), Out) :-
 1466    !,
 1467    print_latex(Out, Word).
 1468print_latex_token(no_escape(Text), Out) :-
 1469    !,
 1470    write(Out, Text).
 1471print_latex_token(Rest, Out) :-
 1472    (   atomic(Rest)
 1473    ->  print_latex(Out, Rest)
 1474    ;   %type_error(latex_token, Rest)
 1475        write(Out, Rest)
 1476    ).
 1477
 1478atom_replace_char(In, From, To, Out) :-
 1479    sub_atom(In, _, _, _, From),
 1480    !,
 1481    atom_chars(In, CharsIn),
 1482    replace(CharsIn, From, To, CharsOut),
 1483    atom_chars(Out, CharsOut).
 1484atom_replace_char(In, _, _, In).
 1485
 1486replace([], _, _, []).
 1487replace([H|T0], H, N, [N|T]) :-
 1488    !,
 1489    replace(T0, H, N, T).
 1490replace([H|T0], F, N, [H|T]) :-
 1491    replace(T0, F, N, T).
 1492
 1493
 1494%!  print_latex(+Out, +Text:atomic) is det.
 1495%
 1496%   Print Text, such that it comes out as normal LaTeX text.
 1497
 1498print_latex(Out, String) :-
 1499    atom_string(Atom, String),
 1500    atom_chars(Atom, Chars),
 1501    print_chars(Chars, Out).
 1502
 1503print_chars([], _).
 1504print_chars([H|T], Out) :-
 1505    print_char(H, Out),
 1506    print_chars(T, Out).
 1507
 1508
 1509%!  max_nl(T0, T, M0, M)
 1510%
 1511%   Remove leading sequence of nl(N) and return the maximum of it.
 1512
 1513max_nl([nl(M1)|T0], T, M0, M) :-
 1514    !,
 1515    M2 is max(M1, M0),
 1516    max_nl(T0, T, M2, M).
 1517max_nl([nl_exact(M1)|T0], T, _, M) :-
 1518    !,
 1519    nl_exact(T0, T, M1, M).
 1520max_nl(T, T, M, M).
 1521
 1522nl_exact([nl(_)|T0], T, M0, M) :-
 1523    !,
 1524    max_nl(T0, T, M0, M).
 1525nl_exact([nl_exact(M1)|T0], T, M0, M) :-
 1526    !,
 1527    M2 is max(M1, M0),
 1528    max_nl(T0, T, M2, M).
 1529nl_exact(T, T, M, M).
 1530
 1531
 1532nl(Out, N) :-
 1533    forall(between(1, N, _), nl(Out)).
 1534
 1535
 1536print_char('<', Out) :- !, write(Out, '$<$').
 1537print_char('>', Out) :- !, write(Out, '$>$').
 1538print_char('{', Out) :- !, write(Out, '\\{').
 1539print_char('}', Out) :- !, write(Out, '\\}').
 1540print_char('$', Out) :- !, write(Out, '\\$').
 1541print_char('&', Out) :- !, write(Out, '\\&').
 1542print_char('#', Out) :- !, write(Out, '\\#').
 1543print_char('%', Out) :- !, write(Out, '\\%').
 1544print_char('~', Out) :- !, write(Out, '\\Stilde{}').
 1545print_char('\\',Out) :- !, write(Out, '\\bsl{}').
 1546print_char('^', Out) :- !, write(Out, '\\Shat{}').
 1547print_char('|', Out) :- !, write(Out, '\\Sbar{}').
 1548print_char(C,   Out) :- put_char(Out, C).
 1549
 1550
 1551%!  identifier(+Atom) is semidet.
 1552%
 1553%   True if Atom is (lower, alnum*).
 1554
 1555identifier(Atom) :-
 1556    atom_chars(Atom, [C0|Chars]),
 1557    char_type(C0, lower),
 1558    all_chartype(Chars, alnum).
 1559
 1560all_chartype([], _).
 1561all_chartype([H|T], Type) :-
 1562    char_type(H, Type),
 1563    all_chartype(T, Type).
 1564
 1565
 1566                 /*******************************
 1567                 *    LATEX SPECIAL SEQUENCES   *
 1568                 *******************************/
 1569
 1570%!  urldef_name(?String, ?DefName)
 1571%
 1572%   True if \DefName is  a  urldef   for  String.  UrlDefs are LaTeX
 1573%   sequences that can be used to  represent strings with symbols in
 1574%   fragile environments. Whenever a word can   be  expressed with a
 1575%   urldef, we will  do  this  to   enhance  the  robustness  of the
 1576%   generated LaTeX code.
 1577
 1578:- dynamic
 1579    urldef_name/2,
 1580    urlchar/1,                      % true if C appears in ine of them
 1581    urldefs_loaded/1. 1582
 1583%!  load_urldefs.
 1584%!  load_urldefs(+File)
 1585%
 1586%   Load   =|\urldef|=   definitions   from    File   and   populate
 1587%   urldef_name/2. See =|pldoc.sty|= for details.
 1588
 1589load_urldefs :-
 1590    urldefs_loaded(_),
 1591    !.
 1592load_urldefs :-
 1593    absolute_file_name(library('pldoc/pldoc.sty'), File,
 1594                       [ access(read) ]),
 1595    load_urldefs(File).
 1596
 1597load_urldefs(File) :-
 1598    urldefs_loaded(File),
 1599    !.
 1600load_urldefs(File) :-
 1601    open(File, read, In),
 1602    call_cleanup((   read_line_to_codes(In, L0),
 1603                     process_urldefs(L0, In)),
 1604                 close(In)),
 1605    assert(urldefs_loaded(File)).
 1606
 1607process_urldefs(end_of_file, _) :- !.
 1608process_urldefs(Line, In) :-
 1609    (   phrase(urldef(Name, String), Line)
 1610    ->  assert(urldef_name(String, Name)),
 1611        assert_chars(String)
 1612    ;   true
 1613    ),
 1614    read_line_to_codes(In, L2),
 1615    process_urldefs(L2, In).
 1616
 1617assert_chars(String) :-
 1618    atom_chars(String, Chars),
 1619    (   member(C, Chars),
 1620        \+ urlchar(C),
 1621        assert(urlchar(C)),
 1622        fail
 1623    ;   true
 1624    ).
 1625
 1626urldef(Name, String) -->
 1627    "\\urldef{\\", string(NameS), "}\\satom{", string(StringS), "}",
 1628    ws,
 1629    (   "%"
 1630    ->  string(_)
 1631    ;   []
 1632    ),
 1633    eol,
 1634    !,
 1635    { atom_codes(Name, NameS),
 1636      atom_codes(String, StringS)
 1637    }.
 1638
 1639ws --> [C], { C =< 32 }, !, ws.
 1640ws --> [].
 1641
 1642string([]) --> [].
 1643string([H|T]) --> [H], string(T).
 1644
 1645eol([],[]).
 1646
 1647
 1648                 /*******************************
 1649                 *         HEADER/FOOTER        *
 1650                 *******************************/
 1651
 1652latex_header(Out, Options) :-
 1653    (   option(stand_alone(true), Options, true)
 1654    ->  forall(header(Line), format(Out, '~w~n', [Line]))
 1655    ;   true
 1656    ),
 1657    forall(generated(Line), format(Out, '~w~n', [Line])).
 1658
 1659latex_footer(Out, Options) :-
 1660    (   option(stand_alone(true), Options, true)
 1661    ->  forall(footer(Line), format(Out, '~w~n', [Line]))
 1662    ;   true
 1663    ).
 1664
 1665header('\\documentclass[11pt]{article}').
 1666header('\\usepackage{times}').
 1667header('\\usepackage{pldoc}').
 1668header('\\sloppy').
 1669header('\\makeindex').
 1670header('').
 1671header('\\begin{document}').
 1672
 1673footer('').
 1674footer('\\printindex').
 1675footer('\\end{document}').
 1676
 1677generated('% This LaTeX document was generated using the LaTeX backend of PlDoc,').
 1678generated('% The SWI-Prolog documentation system').
 1679generated('').
 1680
 1681
 1682		 /*******************************
 1683		 *            MESSAGES		*
 1684		 *******************************/
 1685
 1686:- multifile
 1687    prolog:message//1. 1688
 1689prolog:message(pldoc(no_summary_for(Obj))) -->
 1690    [ 'No summary documentation for ~p'-[Obj] ]