View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2006-2017, 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_http,
   37          [ doc_enable/1,               % +Boolean
   38            doc_server/1,               % ?Port
   39            doc_server/2,               % ?Port, +Options
   40            doc_browser/0,
   41            doc_browser/1               % +What
   42          ]).   43:- use_module(library(pldoc)).   44:- use_module(library(http/thread_httpd)).   45:- use_module(library(http/http_parameters)).   46:- use_module(library(http/html_write)).   47:- use_module(library(http/mimetype)).   48:- use_module(library(dcg/basics)).   49:- use_module(library(http/http_dispatch)).   50:- use_module(library(http/http_hook)).   51:- use_module(library(http/http_path)).   52:- use_module(library(http/http_wrapper)).   53:- use_module(library(uri)).   54:- use_module(library(debug)).   55:- use_module(library(lists)).   56:- use_module(library(url)).   57:- use_module(library(socket)).   58:- use_module(library(option)).   59:- use_module(library(error)).   60:- use_module(library(www_browser)).   61:- use_module(pldoc(doc_process)).   62:- use_module(pldoc(doc_htmlsrc)).   63:- use_module(pldoc(doc_html)).   64:- use_module(pldoc(doc_index)).   65:- use_module(pldoc(doc_search)).   66:- use_module(pldoc(doc_man)).   67:- use_module(pldoc(doc_wiki)).   68:- use_module(pldoc(doc_util)).   69:- use_module(pldoc(doc_access)).   70:- use_module(pldoc(doc_pack)).

Documentation server

The module library(pldoc/http) provides an embedded HTTP documentation server that allows for browsing the documentation of all files loaded after library(pldoc) has been loaded. */

   79:- dynamic
   80    doc_server_port/1,
   81    doc_enabled/0.   82
   83http:location(pldoc, root(pldoc), []).
   84http:location(pldoc_man, pldoc(refman), []).
   85http:location(pldoc_pkg, pldoc(package), []).
   86http:location(pldoc_resource, Path, []) :-
   87    http_location_by_id(pldoc_resource, Path).
 doc_enable(+Boolean)
Actually activate the PlDoc server. Merely loading the server does not do so to avoid incidental loading in a user HTTP server making the documentation available.
   95doc_enable(true) :-
   96    (   doc_enabled
   97    ->  true
   98    ;   assertz(doc_enabled)
   99    ).
  100doc_enable(false) :-
  101    retractall(doc_enabled).
 doc_server(?Port) is det
 doc_server(?Port, +Options) is det
Start a documentation server in the current Prolog process. The server is started in a seperate thread. Options are handed to http_server/2. In addition, the following options are recognised:
allow(HostOrIP)
Allow connections from HostOrIP. If HostOrIP is an atom it is matched to the hostname. It if starts with a ., suffix match is done, matching the domain. Finally it can be a term ip(A,B,C,D). See tcp_host_to_address/2 for details.
deny(HostOrIP)
See allow(HostOrIP).
edit(Bool)
Allow editing from localhost connections? Default: true.

The predicate doc_server/1 is defined as below, which provides a good default for development.

doc_server(Port) :-
        doc_server(Port,
                   [ allow(localhost)
                   ]).
See also
- doc_browser/1
  137doc_server(Port) :-
  138    doc_server(Port,
  139               [ allow(localhost),
  140                 allow(ip(127,0,0,1)) % Windows ip-->host often fails
  141               ]).
  142
  143doc_server(Port, _) :-
  144    doc_enable(true),
  145    catch(doc_current_server(Port), _, fail),
  146    !.
  147doc_server(Port, Options) :-
  148    doc_enable(true),
  149    prepare_editor,
  150    host_access_options(Options, ServerOptions),
  151    http_absolute_location(pldoc('.'), Entry, []),
  152    merge_options(ServerOptions,
  153                  [ port(Port),
  154                    entry_page(Entry)
  155                  ], HTTPOptions),
  156    http_server(http_dispatch, HTTPOptions),
  157    assertz(doc_server_port(Port)).
 doc_current_server(-Port) is det
TCP/IP port of the documentation server. Fails if no server is running. Note that in the current infrastructure we can easily be embedded into another Prolog HTTP server. If we are not started from doc_server/2, we return the port of a running HTTP server.
Errors
- existence_error(http_server, pldoc)
To be done
- Trap destruction of the server.
  170doc_current_server(Port) :-
  171    (   doc_server_port(P)
  172    ->  Port = P
  173    ;   http_current_server(_:_, P)
  174    ->  Port = P
  175    ;   existence_error(http_server, pldoc)
  176    ).
 doc_browser is det
 doc_browser(+What) is semidet
Open user's default browser on the documentation server.
  183doc_browser :-
  184    doc_browser([]).
  185doc_browser(Spec) :-
  186    catch(doc_current_server(Port),
  187          error(existence_error(http_server, pldoc), _),
  188          doc_server(Port)),
  189    browser_url(Spec, Request),
  190    format(string(URL), 'http://localhost:~w~w', [Port, Request]),
  191    www_open_url(URL).
  192
  193browser_url([], Root) :-
  194    !,
  195    http_location_by_id(pldoc_root, Root).
  196browser_url(Name, URL) :-
  197    atom(Name),
  198    !,
  199    browser_url(Name/_, URL).
  200browser_url(Name//Arity, URL) :-
  201    must_be(atom, Name),
  202    integer(Arity),
  203    !,
  204    PredArity is Arity+2,
  205    browser_url(Name/PredArity, URL).
  206browser_url(Name/Arity, URL) :-
  207    !,
  208    must_be(atom, Name),
  209    (   predicate(Name, Arity, _, _, _)
  210    ->  format(string(S), '~q/~w', [Name, Arity]),
  211        http_link_to_id(pldoc_man, [predicate=S], URL)
  212    ;   browser_url(_:Name/Arity, URL)
  213    ).
  214browser_url(Spec, URL) :-
  215    !,
  216    Spec = M:Name/Arity,
  217    doc_comment(Spec, _Pos, _Summary, _Comment),
  218    !,
  219    (   var(M)
  220    ->  format(string(S), '~q/~w', [Name, Arity])
  221    ;   format(string(S), '~q:~q/~w', [M, Name, Arity])
  222    ),
  223    http_link_to_id(pldoc_object, [object=S], URL).
 prepare_editor
Start XPCE as edit requests comming from the document server can only be handled if XPCE is running.
  230prepare_editor :-
  231    current_prolog_flag(editor, pce_emacs),
  232    !,
  233    start_emacs.
  234prepare_editor.
  235
  236
  237                 /*******************************
  238                 *          USER REPLIES        *
  239                 *******************************/
  240
  241:- http_handler(pldoc(.),          pldoc_root,
  242                [ prefix,
  243                  authentication(pldoc(read)),
  244                  condition(doc_enabled)
  245                ]).  246:- http_handler(pldoc('index.html'), pldoc_index,   []).  247:- http_handler(pldoc(file),       pldoc_file,     []).  248:- http_handler(pldoc(place),      go_place,       []).  249:- http_handler(pldoc(edit),       pldoc_edit,
  250                [authentication(pldoc(edit))]).  251:- http_handler(pldoc(doc),        pldoc_doc,      [prefix]).  252:- http_handler(pldoc(man),        pldoc_man,      []).  253:- http_handler(pldoc(doc_for),    pldoc_object,   [id(pldoc_doc_for)]).  254:- http_handler(pldoc(search),     pldoc_search,   []).  255:- http_handler(pldoc('res/'),     pldoc_resource, [prefix]).
 pldoc_root(+Request)
Reply using the index-page of the Prolog working directory. There are various options for the start directory. For example we could also use the file or directory of the file that would be edited using edit/0.
  265pldoc_root(Request) :-
  266    http_parameters(Request,
  267                    [ empty(Empty, [ oneof([true,false]),
  268                                     default(false)
  269                                   ])
  270                    ]),
  271    pldoc_root(Request, Empty).
  272
  273pldoc_root(Request, false) :-
  274    http_location_by_id(pldoc_root, Root),
  275    memberchk(path(Path), Request),
  276    Root \== Path,
  277    !,
  278    existence_error(http_location, Path).
  279pldoc_root(_Request, false) :-
  280    working_directory(Dir0, Dir0),
  281    allowed_directory(Dir0),
  282    !,
  283    ensure_slash_end(Dir0, Dir1),
  284    doc_file_href(Dir1, Ref0),
  285    atom_concat(Ref0, 'index.html', Index),
  286    throw(http_reply(see_other(Index))).
  287pldoc_root(Request, _) :-
  288    pldoc_index(Request).
 pldoc_index(+Request)
HTTP handle for /index.html, providing an overall overview of the available documentation.
  296pldoc_index(_Request) :-
  297    reply_html_page(pldoc(index),
  298                    title('SWI-Prolog documentation'),
  299                    [ \doc_links('', []),
  300                       h1('SWI-Prolog documentation'),
  301                      \man_overview([])
  302                    ]).
 pldoc_file(+Request)
Hander for /file?file=File, providing documentation for File.
  309pldoc_file(Request) :-
  310    http_parameters(Request,
  311                    [ file(File, [])
  312                    ]),
  313    (   source_file(File)
  314    ->  true
  315    ;   throw(http_reply(forbidden(File)))
  316    ),
  317    doc_for_file(File, []).
 pldoc_edit(+Request)
HTTP handler that starts the user's default editor on the host running the server. This handler can only accessed if the browser connection originates from localhost. The call can edit files using the file attribute or a predicate if both name and arity is given and optionally module.
  327pldoc_edit(Request) :-
  328    http:authenticate(pldoc(edit), Request, _),
  329    http_parameters(Request,
  330                    [ file(File,
  331                           [ optional(true),
  332                             description('Name of the file to edit')
  333                           ]),
  334                      line(Line,
  335                           [ optional(true),
  336                             integer,
  337                             description('Line in the file')
  338                           ]),
  339                      name(Name,
  340                           [ optional(true),
  341                             description('Name of a Prolog predicate to edit')
  342                           ]),
  343                      arity(Arity,
  344                            [ integer,
  345                              optional(true),
  346                              description('Arity of a Prolog predicate to edit')
  347                            ]),
  348                      module(Module,
  349                             [ optional(true),
  350                               description('Name of a Prolog module to search for predicate')
  351                             ])
  352                    ]),
  353    (   atom(File)
  354    ->  allowed_file(File)
  355    ;   true
  356    ),
  357    (   atom(File), integer(Line)
  358    ->  Edit = file(File, line(Line))
  359    ;   atom(File)
  360    ->  Edit = file(File)
  361    ;   atom(Name), integer(Arity)
  362    ->  (   atom(Module)
  363        ->  Edit = (Module:Name/Arity)
  364        ;   Edit = (Name/Arity)
  365        )
  366    ),
  367    edit(Edit),
  368    format('Content-type: text/plain~n~n'),
  369    format('Started ~q~n', [edit(Edit)]).
  370pldoc_edit(_Request) :-
  371    http_location_by_id(pldoc_edit, Location),
  372    throw(http_reply(forbidden(Location))).
 go_place(+Request)
HTTP handler to handle the places menu.
  379go_place(Request) :-
  380    http_parameters(Request,
  381                    [ place(Place, [])
  382                    ]),
  383    places(Place).
  384
  385places(':packs:') :-
  386    !,
  387    http_link_to_id(pldoc_pack, [], HREF),
  388    throw(http_reply(moved(HREF))).
  389places(Dir0) :-
  390    expand_alias(Dir0, Dir),
  391    (   allowed_directory(Dir)
  392    ->  format(string(IndexFile), '~w/index.html', [Dir]),
  393        doc_file_href(IndexFile, HREF),
  394        throw(http_reply(moved(HREF)))
  395    ;   throw(http_reply(forbidden(Dir)))
  396    ).
 allowed_directory(+Dir) is semidet
True if we are allowed to produce and index for Dir.
  403allowed_directory(Dir) :-
  404    source_directory(Dir),
  405    !.
  406allowed_directory(Dir) :-
  407    working_directory(CWD, CWD),
  408    same_file(CWD, Dir).
  409allowed_directory(Dir) :-
  410    prolog:doc_directory(Dir).
 allowed_file(+File) is semidet
True if we are allowed to serve File. Currently means we have predicates loaded from File or the directory must be allowed.
  418allowed_file(File) :-
  419    source_file(_, File),
  420    !.
  421allowed_file(File) :-
  422    absolute_file_name(File, Canonical),
  423    file_directory_name(Canonical, Dir),
  424    allowed_directory(Dir).
 pldoc_resource(+Request)
Handler for /res/File, serving CSS, JS and image files.
  431pldoc_resource(Request) :-
  432    http_location_by_id(pldoc_resource, ResRoot),
  433    memberchk(path(Path), Request),
  434    atom_concat(ResRoot, File, Path),
  435    file(File, Local),
  436    http_reply_file(pldoc(Local), [], Request).
  437
  438file('pldoc.css',     'pldoc.css').
  439file('pllisting.css', 'pllisting.css').
  440file('pldoc.js',      'pldoc.js').
  441file('edit.png',      'edit.png').
  442file('editpred.png',  'editpred.png').
  443file('up.gif',        'up.gif').
  444file('source.png',    'source.png').
  445file('public.png',    'public.png').
  446file('private.png',   'private.png').
  447file('reload.png',    'reload.png').
  448file('favicon.ico',   'favicon.ico').
  449file('h1-bg.png',     'h1-bg.png').
  450file('h2-bg.png',     'h2-bg.png').
  451file('pub-bg.png',    'pub-bg.png').
  452file('priv-bg.png',   'priv-bg.png').
  453file('multi-bg.png',  'multi-bg.png').
 pldoc_doc(+Request)
Handler for /doc/Path

Reply documentation of a file. Path is the absolute path of the file for which to return the documentation. Extension is either none, the Prolog extension or the HTML extension.

Note that we reply with pldoc.css if the file basename is pldoc.css to allow for a relative link from any directory.

  467pldoc_doc(Request) :-
  468    memberchk(path(ReqPath), Request),
  469    http_location_by_id(pldoc_doc, Me),
  470    atom_concat(Me, AbsFile0, ReqPath),
  471    (   sub_atom(ReqPath, _, _, 0, /)
  472    ->  atom_concat(ReqPath, 'index.html', File),
  473        throw(http_reply(moved(File)))
  474    ;   clean_path(AbsFile0, AbsFile1),
  475        expand_alias(AbsFile1, AbsFile),
  476        is_absolute_file_name(AbsFile)
  477    ->  documentation(AbsFile, Request)
  478    ).
  479
  480documentation(Path, Request) :-
  481    file_base_name(Path, Base),
  482    file(_, Base),                         % serve pldoc.css, etc.
  483    !,
  484    http_reply_file(pldoc(Base), [], Request).
  485documentation(Path, Request) :-
  486    file_name_extension(_, Ext, Path),
  487    autolink_extension(Ext, image),
  488    http_reply_file(Path, [unsafe(true)], Request).
  489documentation(Path, Request) :-
  490    Index = '/index.html',
  491    sub_atom(Path, _, _, 0, Index),
  492    atom_concat(Dir, Index, Path),
  493    exists_directory(Dir),                 % Directory index
  494    !,
  495    (   allowed_directory(Dir)
  496    ->  edit_options(Request, EditOptions),
  497        doc_for_dir(Dir, EditOptions)
  498    ;   throw(http_reply(forbidden(Dir)))
  499    ).
  500documentation(File, Request) :-
  501    wiki_file(File, WikiFile),
  502    !,
  503    (   allowed_file(WikiFile)
  504    ->  true
  505    ;   throw(http_reply(forbidden(File)))
  506    ),
  507    edit_options(Request, Options),
  508    doc_for_wiki_file(WikiFile, Options).
  509documentation(Path, Request) :-
  510    pl_file(Path, File),
  511    !,
  512    (   allowed_file(File)
  513    ->  true
  514    ;   throw(http_reply(forbidden(File)))
  515    ),
  516    doc_reply_file(File, Request).
  517documentation(Path, _) :-
  518    throw(http_reply(not_found(Path))).
  519
  520:- public
  521    doc_reply_file/2.  522
  523doc_reply_file(File, Request) :-
  524    http_parameters(Request,
  525                    [ public_only(Public),
  526                      reload(Reload),
  527                      show(Show),
  528                      format_comments(FormatComments)
  529                    ],
  530                    [ attribute_declarations(param)
  531                    ]),
  532    (   exists_file(File)
  533    ->  true
  534    ;   throw(http_reply(not_found(File)))
  535    ),
  536    (   Reload == true,
  537        source_file(File)
  538    ->  load_files(File, [if(changed), imports([])])
  539    ;   true
  540    ),
  541    edit_options(Request, EditOptions),
  542    (   Show == src
  543    ->  format('Content-type: text/html~n~n', []),
  544        source_to_html(File, stream(current_output),
  545                       [ skin(src_skin(Request, Show, FormatComments)),
  546                         format_comments(FormatComments)
  547                       ])
  548    ;   Show == raw
  549    ->  http_reply_file(File,
  550                        [ unsafe(true), % is already validated
  551                          mime_type(text/plain)
  552                        ], Request)
  553    ;   doc_for_file(File,
  554                     [ public_only(Public),
  555                       source_link(true)
  556                     | EditOptions
  557                     ])
  558    ).
  559
  560
  561:- public src_skin/5.                   % called through source_to_html/3.
  562
  563src_skin(Request, _Show, FormatComments, header, Out) :-
  564    memberchk(request_uri(ReqURI), Request),
  565    negate(FormatComments, AltFormatComments),
  566    replace_parameters(ReqURI, [show(raw)], RawLink),
  567    replace_parameters(ReqURI, [format_comments(AltFormatComments)], CmtLink),
  568    phrase(html(div(class(src_formats),
  569                    [ 'View source with ',
  570                      a(href(CmtLink), \alt_view(AltFormatComments)),
  571                      ' or as ',
  572                      a(href(RawLink), raw)
  573                    ])), Tokens),
  574    print_html(Out, Tokens).
  575
  576alt_view(true) -->
  577    html('formatted comments').
  578alt_view(false) -->
  579    html('raw comments').
  580
  581negate(true, false).
  582negate(false, true).
  583
  584replace_parameters(ReqURI, Extra, URI) :-
  585    uri_components(ReqURI, C0),
  586    uri_data(search, C0, Search0),
  587    (   var(Search0)
  588    ->  uri_query_components(Search, Extra)
  589    ;   uri_query_components(Search0, Form0),
  590        merge_options(Extra, Form0, Form),
  591        uri_query_components(Search, Form)
  592    ),
  593    uri_data(search, C0, Search, C),
  594    uri_components(URI, C).
 edit_options(+Request, -Options) is det
Return edit(true) in Options if the connection is from the localhost.
  602edit_options(Request, [edit(true)]) :-
  603    catch(http:authenticate(pldoc(edit), Request, _), _, fail),
  604    !.
  605edit_options(_, []).
 pl_file(+File, -PlFile) is semidet
  610pl_file(File, PlFile) :-
  611    file_name_extension(Base, html, File),
  612    !,
  613    absolute_file_name(Base,
  614                       PlFile,
  615                       [ file_errors(fail),
  616                         file_type(prolog),
  617                         access(read)
  618                       ]).
  619pl_file(File, File).
 wiki_file(+File, -TxtFile) is semidet
True if TxtFile is an existing file that must be served as wiki file.
  626wiki_file(File, TxtFile) :-
  627    file_name_extension(_, Ext, File),
  628    wiki_file_extension(Ext),
  629    !,
  630    TxtFile = File.
  631wiki_file(File, TxtFile) :-
  632    file_base_name(File, Base),
  633    autolink_file(Base, wiki),
  634    !,
  635    TxtFile = File.
  636wiki_file(File, TxtFile) :-
  637    file_name_extension(Base, html, File),
  638    wiki_file_extension(Ext),
  639    file_name_extension(Base, Ext, TxtFile),
  640    access_file(TxtFile, read).
  641
  642wiki_file_extension(md).
  643wiki_file_extension(txt).
 clean_path(+AfterDoc, -AbsPath)
Restore the path, Notably deals Windows issues
  650clean_path(Path0, Path) :-
  651    current_prolog_flag(windows, true),
  652    sub_atom(Path0, 2, _, _, :),
  653    !,
  654    sub_atom(Path0, 1, _, 0, Path).
  655clean_path(Path, Path).
 pldoc_man(+Request)
Handler for /man, offering one of the parameters:
predicate=PI
providing documentation from the manual on the predicate PI.
function=PI
providing documentation from the manual on the function PI.
CAPI=F
providing documentation from the manual on the C-function F.
  669pldoc_man(Request) :-
  670    http_parameters(Request,
  671                    [ predicate(PI, [optional(true)]),
  672                      function(Fun, [optional(true)]),
  673                      'CAPI'(F,     [optional(true)]),
  674                      section(Sec,  [optional(true)])
  675                    ]),
  676    (   ground(PI)
  677    ->  atom_pi(PI, Obj)
  678    ;   ground(Fun)
  679    ->  atomic_list_concat([Name,ArityAtom], /, Fun),
  680        atom_number(ArityAtom, Arity),
  681        Obj = f(Name/Arity)
  682    ;   ground(F)
  683    ->  Obj = c(F)
  684    ;   ground(Sec)
  685    ->  atom_concat('sec:', Sec, SecID),
  686        Obj = section(SecID)
  687    ),
  688    man_title(Obj, Title),
  689    reply_html_page(
  690        pldoc(object(Obj)),
  691        title(Title),
  692        \man_page(Obj, [])).
  693
  694man_title(f(Obj), Title) :-
  695    !,
  696    format(atom(Title), 'SWI-Prolog -- function ~w', [Obj]).
  697man_title(c(Obj), Title) :-
  698    !,
  699    format(atom(Title), 'SWI-Prolog -- API-function ~w', [Obj]).
  700man_title(section(_Id), Title) :-
  701    !,
  702    format(atom(Title), 'SWI-Prolog -- Manual', []).
  703man_title(Obj, Title) :-
  704    format(atom(Title), 'SWI-Prolog -- ~w', [Obj]).
 pldoc_object(+Request)
Handler for /doc_for?object=Term, Provide documentation for the given term.
  711pldoc_object(Request) :-
  712    http_parameters(Request,
  713                    [ object(Atom, []),
  714                      header(Header, [default(true)])
  715                    ]),
  716    atom_to_term(Atom, Obj, _),
  717    (   prolog:doc_object_title(Obj, Title)
  718    ->  true
  719    ;   Title = Atom
  720    ),
  721    edit_options(Request, EditOptions),
  722    reply_html_page(
  723        pldoc(object(Obj)),
  724        title(Title),
  725        \object_page(Obj, [header(Header)|EditOptions])).
 pldoc_search(+Request)
Search the collected PlDoc comments and Prolog manual.
  732pldoc_search(Request) :-
  733    http_parameters(Request,
  734                    [ for(For,
  735                          [ optional(true),
  736                            description('String to search for')
  737                          ]),
  738                      page(Page,
  739                           [ integer,
  740                             default(1),
  741                             description('Page of search results to view')
  742                           ]),
  743                      in(In,
  744                         [ oneof([all,app,noapp,man,lib,pack,wiki]),
  745                           default(all),
  746                           description('Search everying, application only or manual only')
  747                         ]),
  748                      match(Match,
  749                            [ oneof([name,summary]),
  750                              default(summary),
  751                              description('Match only the name or also the summary')
  752                            ]),
  753                      resultFormat(Format,
  754                                   [ oneof(long,summary),
  755                                     default(summary),
  756                                     description('Return full documentation or summary-lines')
  757                                   ])
  758                    ]),
  759    edit_options(Request, EditOptions),
  760    format(string(Title), 'Prolog search -- ~w', [For]),
  761    reply_html_page(pldoc(search(For)),
  762                    title(Title),
  763                    \search_reply(For,
  764                                  [ resultFormat(Format),
  765                                    search_in(In),
  766                                    search_match(Match),
  767                                    page(Page)
  768                                  | EditOptions
  769                                  ])).
  770
  771
  772                 /*******************************
  773                 *     HTTP PARAMETER TYPES     *
  774                 *******************************/
  775
  776:- public
  777    param/2.                        % used in pack documentation server
  778
  779param(public_only,
  780      [ boolean,
  781        default(true),
  782        description('If true, hide private predicates')
  783      ]).
  784param(reload,
  785      [ boolean,
  786        default(false),
  787        description('Reload the file and its documentation')
  788      ]).
  789param(show,
  790      [ oneof([doc,src,raw]),
  791        default(doc),
  792        description('How to show the file')
  793      ]).
  794param(format_comments,
  795      [ boolean,
  796        default(true),
  797        description('If true, use PlDoc for rendering structured comments')
  798      ])