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)  2006-2014, 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_index,
   37	  [ doc_for_dir/2,              % +Dir, +Options
   38	    dir_index//2,               % +Dir, +Options, //
   39	    object_summaries//3,        % +Objs, +Section, +Options, //
   40	    file_index_header//2,       % +File, +Options, //
   41	    doc_links//2,               % +Directory, +Options, //
   42	    doc_file_href/2,            % +File, -HREF
   43	    places_menu//1,             % +Dir, //
   44	    source_directory/1          % ?Directory
   45	  ]).   46:- use_module(doc_process).   47:- use_module(doc_html).   48:- use_module(doc_wiki).   49:- use_module(doc_search).   50:- use_module(doc_util).   51
   52:- if(exists_source(library(doc_http))).   53:- use_module(library(http/http_dispatch)).   54:- use_module(library(doc_http), []).   55:- endif.   56
   57:- use_module(library(http/html_write)).   58:- use_module(library(http/html_head)).   59:- use_module(library(readutil)).   60:- use_module(library(url)).   61:- use_module(library(option)).   62:- use_module(library(lists)).   63:- include(hooks).   64
   65/** <module> Create indexes
   66*/
   67
   68:- predicate_options(dir_index//2, 2,
   69		     [ directory(atom),
   70		       edit(boolean),
   71		       files(list),
   72		       members(list),
   73		       qualify(boolean),
   74		       title(atom),
   75		       if(oneof([true,loaded])),
   76		       recursive(boolean),
   77		       secref_style(oneof([number, title, number_title])),
   78		       pass_to(doc_links/4, 2)
   79		     ]).   80:- predicate_options(doc_links//2, 2,
   81		     [ files(list),
   82		       pass_to(pldoc_search:search_form/3, 1)
   83		     ]).   84:- predicate_options(file_index_header//2, 2,
   85		     [ directory(any),
   86		       files(list),
   87		       qualify(boolean),
   88		       secref_style(oneof([number, title, number_title])),
   89		       pass_to(pldoc_html:edit_button/4, 2),
   90		       pass_to(pldoc_html:source_button/4, 2)
   91		     ]).   92:- predicate_options(object_summaries//3, 3,
   93		     [ edit(boolean),
   94		       files(list),
   95		       module(atom),
   96		       public(list),
   97		       qualify(boolean),
   98		       secref_style(oneof([number, title, number_title]))
   99		     ]).  100:- predicate_options(doc_for_dir/2, 2, [pass_to(dir_index/4, 2)]).  101
  102%!  doc_for_dir(+Dir, +Options) is det.
  103%
  104%   Write summary index for all files  in   Dir  to  Out. The result
  105%   consists of the =README= file  (if   any),  a  table holding with
  106%   links to objects and summary  sentences   and  finaly the =TODO=
  107%   file (if any).
  108
  109doc_for_dir(DirSpec, Options) :-
  110    absolute_file_name(DirSpec,
  111		       [ file_type(directory),
  112			 access(read)
  113		       ],
  114		       Dir),
  115    (   option(title(Title), Options)
  116    ->  true
  117    ;   file_base_name(Dir, Title)
  118    ),
  119    doc_write_page(
  120	pldoc(dir_index),
  121	title(Title),
  122	\dir_index(Dir, Options),
  123	Options).
  124
  125:- html_meta doc_write_page(+, html, html, +).  126
  127doc_write_page(Style, Head, Body, Options) :-
  128    option(files(_), Options),
  129    !,
  130    phrase(page(Style, Head, Body), HTML),
  131    print_html(HTML).
  132doc_write_page(Style, Head, Body, _) :-
  133    reply_html_page(Style, Head, Body).
  134
  135
  136%!  dir_index(+Dir, +Options)//
  137%
  138%   Create an index for all Prolog files appearing in Dir or in
  139%   any directory contained in Dir.  Options:
  140%
  141%     * members(+Members)
  142%     Documented members.  See doc_files.pl
  143%     * title(+Title)
  144%     Title to use for the index page
  145
  146dir_index(Dir, Options) -->
  147    { dir_source_files(Dir, Files0, Options),
  148      sort(Files0, Files),
  149      maplist(ensure_doc_objects, Files),
  150      directory_file_path(Dir, 'index.html', File),
  151      b_setval(pldoc_file, File)    % for predref
  152    },
  153    html([ \doc_resources(Options),
  154	   \doc_links(Dir, Options),
  155	   \dir_header(Dir, Options),
  156	   \subdir_links(Dir, Options),
  157	   h2(class([wiki,plfiles]), 'Prolog files'),
  158	   table(class(summary),
  159		 \file_indices(Files, [directory(Dir)|Options])),
  160	   \dir_footer(Dir, Options)
  161	 ]).
  162
  163%!  dir_source_files(+Dir, -Files, +Options) is det
  164%
  165%   Create a list of source-files to be documented as part of Dir.
  166
  167dir_source_files(_, Files, Options) :-
  168    option(members(Members), Options),
  169    !,
  170    findall(F, member(file(F,_Doc), Members), Files).
  171dir_source_files(Dir, Files, Options) :-
  172    directory_source_files(Dir, Files, Options).
  173
  174%!  subdir_links(+Dir, +Options)// is det.
  175%
  176%   Create links to subdirectories
  177
  178subdir_links(Dir, Options) -->
  179    { option(members(Members), Options),
  180      findall(SubDir, member(directory(SubDir, _, _, _), Members), SubDirs),
  181      SubDirs \== []
  182    },
  183    html([ h2(class([wiki,subdirs]), 'Sub directories'),
  184	   table(class(subdirs),
  185		 \subdir_link_rows(SubDirs, Dir))
  186	 ]).
  187subdir_links(_, _) --> [].
  188
  189subdir_link_rows([], _) --> [].
  190subdir_link_rows([H|T], Dir) -->
  191    subdir_link_row(H, Dir),
  192    subdir_link_rows(T, Dir).
  193
  194subdir_link_row(Dir, From) -->
  195    { directory_file_path(Dir, 'index.html', Index),
  196      relative_file_name(Index, From, Link),
  197      file_base_name(Dir, Base)
  198    },
  199    html(tr(td(a([class(subdir), href(Link)], ['[dir] ', Base])))).
  200
  201%!  dir_header(+Dir, +Options)// is det.
  202%
  203%   Create header for directory.  Options:
  204%
  205%     * readme(File)
  206%     Include File as introduction to the directory header.
  207
  208dir_header(Dir, Options) -->
  209    wiki_file(Dir, readme, Options),
  210    !.
  211dir_header(Dir, Options) -->
  212    { (   option(title(Title), Options)
  213      ->  true
  214      ;   file_base_name(Dir, Title)
  215      )
  216    },
  217    html(h1(class=dir, Title)).
  218
  219%!  dir_footer(+Dir, +Options)// is det.
  220%
  221%   Create footer for directory. The footer contains the =TODO= file
  222%   if provided.  Options:
  223%
  224%     * todo(File)
  225%     Include File as TODO file in the footer.
  226
  227dir_footer(Dir, Options) -->
  228    wiki_file(Dir, todo, Options),
  229    !.
  230dir_footer(_, _) -->
  231    [].
  232
  233%!  wiki_file(+Dir, +Type, +Options)// is semidet.
  234%
  235%   Include text from a Wiki text-file.
  236
  237wiki_file(Dir, Type, Options) -->
  238    { (   Opt =.. [Type,WikiFile],
  239	  option(Opt, Options)
  240      ->  true
  241      ;   directory_files(Dir, Files),
  242	  member(File, Files),
  243	  wiki_file_type(Type, Pattern),
  244	  downcase_atom(File, Pattern),
  245	  directory_file_path(Dir, File, WikiFile)
  246      ),
  247      access_file(WikiFile, read),
  248      !,
  249      read_file_to_codes(WikiFile, String, []),
  250      wiki_codes_to_dom(String, [], DOM)
  251    },
  252    pldoc_html:html(DOM).
  253
  254%!  wiki_file_type(+Category, -File) is nondet.
  255%
  256%   Declare file pattern names that are included for README and TODO
  257%   for a directory. Files are matched case-insensitively.
  258
  259wiki_file_type(readme, 'readme').
  260wiki_file_type(readme, 'readme.md').
  261wiki_file_type(readme, 'readme.txt').
  262wiki_file_type(todo,   'todo').
  263wiki_file_type(todo,   'todo.md').
  264wiki_file_type(todo,   'todo.txt').
  265
  266%!  file_indices(+Files, +Options)// is det.
  267%
  268%   Provide a file-by-file index of the   contents of each member of
  269%   Files.
  270
  271file_indices([], _) -->
  272    [].
  273file_indices([H|T], Options) -->
  274    file_index(H, Options),
  275    file_indices(T, Options).
  276
  277%!  file_index(+File, +Options)// is det.
  278%
  279%   Create an index for File.
  280
  281file_index(File, Options) -->
  282    { doc_summaries(File, Objs0),
  283      module_info(File, ModuleOptions, Options),
  284      doc_hide_private(Objs0, Objs1, ModuleOptions),
  285      sort(Objs1, Objs)
  286    },
  287    html([ \file_index_header(File, Options)
  288	 | \object_summaries(Objs, File, ModuleOptions)
  289	 ]).
  290
  291doc_summaries(File, Objects) :-
  292    xref_current_source(FileSpec),
  293    xref_option(FileSpec, comments(collect)),
  294    !,
  295    Pos = File:0,
  296    findall(doc(Obj,Pos,Summary),
  297	    xref_doc_summary(Obj, Pos, Summary), Objects).
  298doc_summaries(File, Objects) :-
  299    Pos = File:_Line,
  300    findall(doc(Obj,Pos,Summary),
  301	    doc_comment(Obj, Pos, Summary, _), Objects).
  302
  303xref_doc_summary(M:Name/Arity, File:_, Summary) :-
  304    xref_comment(File, Head, Summary, _Comment),
  305    xref_module(File, Module),
  306    strip_module(Module:Head, M, Plain),
  307    functor(Plain, Name, Arity).
  308
  309%!  file_index_header(+File, +Options)// is det.
  310%
  311%   Create an entry in a summary-table for File.
  312
  313file_index_header(File, Options) -->
  314    prolog:doc_file_index_header(File, Options),
  315    !.
  316file_index_header(File, Options) -->
  317    { (   option(directory(Dir), Options),
  318	  directory_file_path(Dir, Label, File)
  319      ->  true
  320      ;   file_base_name(File, Label)
  321      ),
  322      doc_file_href(File, HREF, Options)
  323    },
  324    html(tr(th([colspan(3), class(file)],
  325	       [ span(style('float:left'), a(href(HREF), Label)),
  326		 \file_module_title(File),
  327		 span(style('float:right'),
  328		      [ \source_button(File, Options),
  329			\edit_button(File, Options)
  330		      ])
  331	       ]))).
  332
  333file_module_title(File) -->
  334    { (   module_property(M, file(File))
  335      ;   xref_module(File, M)
  336      ),
  337      doc_comment(M:module(Title), _, _, _)
  338    },
  339    !,
  340    html([&(nbsp), ' -- ', Title]).
  341file_module_title(_) -->
  342    [].
  343
  344
  345%!  doc_file_href(+File, -HREF, +Options) is det.
  346%
  347%   HREF is reference to documentation of File.
  348
  349doc_file_href(File, HREF, Options) :-
  350    option(directory(Dir), Options),
  351    atom_concat(Dir, Local0, File),
  352    atom_concat(/, Local, Local0),
  353    !,
  354    (   option(files(Map), Options),        % generating files
  355	memberchk(file(File, DocFile), Map)
  356    ->  file_base_name(DocFile, HREF)
  357    ;   HREF = Local
  358    ).
  359doc_file_href(File, HREF, _) :-
  360    doc_file_href(File, HREF).
  361
  362
  363
  364%!  doc_file_href(+Path, -HREF) is det.
  365%
  366%   Create a /doc HREF from Path.  There   are  some nasty things we
  367%   should take care of.
  368%
  369%           * Windows paths may start with =|L:|= (mapped to =|/L:|=)
  370%           * Paths may contain spaces and other weird stuff
  371
  372doc_file_href(File0, HREF) :-
  373    insert_alias(File0, File),
  374    ensure_slash_start(File, SlashFile),
  375    http_location([path(SlashFile)], Escaped),
  376    http_location_by_id(pldoc_doc, DocRoot),
  377    atom_concat(DocRoot, Escaped, HREF).
  378
  379
  380%!  ensure_slash_start(+File0, -File) is det.
  381%
  382%   Ensure  File  starts  with  a  /.    This  maps  C:/foobar  into
  383%   /C:/foobar, so our paths start with /doc/ again ...
  384
  385ensure_slash_start(File, File) :-
  386    sub_atom(File, 0, _, _, /),
  387    !.
  388ensure_slash_start(File0, File) :-
  389    atom_concat(/, File0, File).
  390
  391
  392%!  object_summaries(+Objects, +Section, +Options)// is det.
  393%
  394%   Create entries in a summary table for Objects.
  395
  396object_summaries(Objects, Section, Options) -->
  397    { tag_pub_priv(Objects, Tagged, Options),
  398      keysort(Tagged, Ordered)
  399    },
  400    obj_summaries(Ordered, Section, Options).
  401
  402obj_summaries([], _, _) -->
  403    [].
  404obj_summaries([_Tag-H|T], Section, Options) -->
  405    object_summary(H, Section, Options),
  406    obj_summaries(T, Section, Options).
  407
  408tag_pub_priv([], [], _).
  409tag_pub_priv([H|T0], [Tag-H|T], Options) :-
  410    (   private(H, Options)
  411    ->  Tag = z_private
  412    ;   Tag = a_public
  413    ),
  414    tag_pub_priv(T0, T, Options).
  415
  416
  417%!  object_summary(+Object, +Section, +Options)// is det
  418%
  419%   Create a summary for Object.  Summary consists of a link to
  420%   the Object and a summary text as a table-row.
  421%
  422%   @tbd    Hacky interface.  Do we demand Summary to be in Wiki?
  423
  424object_summary(q(_Q,Obj), Section, Options) -->
  425    { nonvar(Obj) },
  426    !,
  427    object_summary(Obj, Section, Options).
  428object_summary(doc(Obj, _Pos, _Summary), wiki, Options) -->
  429    !,
  430    html(tr(class(wiki),
  431	    [ td(colspan(3), \object_ref(Obj, Options))
  432	    ])).
  433object_summary(doc(Obj, _Pos, Summary), _Section, Options) -->
  434    !,
  435    (   { string_codes(Summary, Codes),
  436	  wiki_codes_to_dom(Codes, [], DOM0),
  437	  strip_leading_par(DOM0, DOM),
  438	  (   private(Obj, Options)
  439	  ->  Class = private               % private definition
  440	  ;   Class = public                % public definition
  441	  )
  442	}
  443    ->  html(tr(class(Class),
  444		[ td(\object_ref(Obj, Options)),
  445		  td(class(summary), DOM),
  446		  td([align(right)],
  447		     span(style('white-space: nowrap'),
  448			  [ \object_source_button(Obj, Options),
  449			    \object_edit_button(Obj, Options)
  450			  ]))
  451		]))
  452    ;   []
  453    ).
  454object_summary(Obj, Section, Options) -->
  455    { prolog:doc_object_summary(Obj, _Cat, Section, Summary)
  456    },
  457    !,
  458    object_summary(doc(Obj, _, Summary), Section, Options).
  459object_summary(_, _, _) -->
  460    [].
  461
  462
  463		 /*******************************
  464		 *          NAVIGATION          *
  465		 *******************************/
  466
  467%!  doc_links(+Directory, +Options)// is det.
  468%
  469%   Provide overview links and search facilities.
  470
  471doc_links(_Directory, Options) -->
  472    { option(files(_), Options), !
  473    }.
  474doc_links(Directory, Options) -->
  475    prolog:doc_links(Directory, Options),
  476    !,
  477    { option(html_resources(Resoures), Options, pldoc) },
  478    html_requires(Resoures).
  479doc_links(Directory, Options) -->
  480    {   (   Directory == ''
  481	->  working_directory(Dir, Dir)
  482	;   Dir = Directory
  483	),
  484	option(html_resources(Resoures), Options, pldoc)
  485    },
  486    html([ \html_requires(Resoures),
  487	   div(class(navhdr),
  488	       [ div(class(jump),
  489		      div([ \places_menu(Dir),
  490			    \plversion
  491			  ])),
  492		 div(class(search), \search_form(Options)),
  493		 br(clear(right))
  494	       ])
  495	 ]).
  496
  497
  498%!  version// is det.
  499%
  500%   Prolog version
  501
  502plversion -->
  503    { current_prolog_flag(version_data, swi(Major, Minor, Patch, _))
  504    },
  505    !,
  506    html(a([ class(prolog_version),
  507	     href('http://www.swi-prolog.org')
  508	   ],
  509	   [' SWI-Prolog ', Major, '.', Minor, '.', Patch])).
  510
  511plversion -->
  512    { current_prolog_flag(version_data, yap(Major, Minor, Patch, _))
  513    },
  514    html(a([ class(prolog_version),
  515	     href('http://www.dcc.fc.up.pt/~vsc')
  516	   ],
  517	   [' YAP ', Major, '.', Minor, '.', Patch])).
  518
  519
  520%!  places_menu(Current)// is det
  521%
  522%   Create a =select= menu with entries for all loaded directories
  523
  524places_menu(Dir) -->
  525    prolog:doc_places_menu(Dir),
  526    !.
  527places_menu(Dir) -->
  528    { findall(D, source_directory(D), List),
  529      sort(List, Dirs)
  530    },
  531    html(form([ action(location_by_id(go_place))
  532	      ],
  533	      [ input([type(submit), value('Go')]),
  534		select(name(place),
  535		       \packs_source_dirs(Dirs, Dir))
  536	      ])).
  537
  538packs_source_dirs(Dirs, Dir) -->
  539    packs_link,
  540    source_dirs(Dirs, Dir).
  541
  542source_dirs([], _) -->
  543    [].
  544source_dirs([H|T], WD) -->
  545    { (   H == WD
  546      ->  Attrs = [selected]
  547      ;   Attrs = []
  548      ),
  549      format(string(IndexFile), '~w/index.html', [H]),
  550      doc_file_href(IndexFile, HREF),
  551      format(string(Call), 'document.location=\'~w\';', [HREF])
  552    },
  553    html(option([onClick(Call)|Attrs], H)),
  554    source_dirs(T, WD).
  555
  556packs_link -->
  557    { pack_property(_,_),
  558      !,
  559      http_link_to_id(pldoc_pack, [], HREF),
  560      format(atom(Call), 'document.location=\'~w\';', [HREF])
  561    },
  562    html(option([ class(packs),
  563		  onClick(Call),
  564		  value(':packs:')
  565		],
  566		'List extension packs')).
  567packs_link -->
  568    [].
  569
  570%!  source_directory(+Dir) is semidet.
  571%!  source_directory(-Dir) is nondet.
  572%
  573%   True if Dir is a directory  from   which  we  have loaded Prolog
  574%   sources.
  575
  576source_directory(Dir) :-
  577    (   ground(Dir)
  578    ->  '$time_source_file'(File, _Time1, _System1),
  579	file_directory_name(File, Dir), !
  580    ;   '$time_source_file'(File, _Time2, _System2),
  581	file_directory_name(File, Dir)
  582    )