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