edoc_doclet.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 522 行

ERL
522
字号
%% =====================================================================%% This library is free software; you can redistribute it and/or modify%% it under the terms of the GNU Lesser General Public License as%% published by the Free Software Foundation; either version 2 of the%% License, or (at your option) any later version.%%%% This library is distributed in the hope that it will be useful, but%% WITHOUT ANY WARRANTY; without even the implied warranty of%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU%% Lesser General Public License for more details.%%%% You should have received a copy of the GNU Lesser General Public%% License along with this library; if not, write to the Free Software%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307%% USA%%%% $Id$%%%% @copyright 2003-2006 Richard Carlsson%% @author Richard Carlsson <richardc@it.uu.se>%% @see edoc%% @end%% =====================================================================%% @doc Standard doclet module for EDoc.%% Note that this is written so that it is *not* depending on edoc.hrl!%% TODO: copy "doc-files" subdirectories, recursively.%% TODO: generate summary page of TODO-notes%% TODO: generate summary page of deprecated things%% TODO: generate decent indexes over modules, methods, records, etc.-module(edoc_doclet).-export([run/2]).-import(edoc_report, [report/2, warning/2]).%% @headerfile "edoc_doclet.hrl"-include("../include/edoc_doclet.hrl").-define(EDOC_APP, edoc).-define(DEFAULT_FILE_SUFFIX, ".html").-define(INDEX_FILE, "index.html").-define(OVERVIEW_FILE, "overview.edoc").-define(PACKAGE_SUMMARY, "package-summary.html").-define(OVERVIEW_SUMMARY, "overview-summary.html").-define(PACKAGES_FRAME, "packages-frame.html").-define(MODULES_FRAME, "modules-frame.html").-define(STYLESHEET, "stylesheet.css").-define(IMAGE, "erlang.png").-define(NL, "\n").-include("xmerl.hrl").%% Sources is the list of inputs in the order they were found.  Packages%% and Modules are sorted lists of atoms without duplicates. (They%% usually include the data from the edoc-info file in the target%% directory, if it exists.) Note that the "empty package" is never%% included in Packages!%% @spec (Command::doclet_gen() | doclet_toc(), edoc_context()) -> ok%% @doc Main doclet entry point. See the file <a%% href="../include/edoc_doclet.hrl">`edoc_doclet.hrl'</a> for the data%% structures used for passing parameters.%%%% Also see {@link edoc:layout/2} for layout-related options, and%% {@link edoc:get_doc/2} for options related to reading source%% files.%%%% Options:%% <dl>%%  <dt>{@type {file_suffix, string()@}}%%  </dt>%%  <dd>Specifies the suffix used for output files. The default value is%%      `".html"'.%%  </dd>%%  <dt>{@type {hidden, bool()@}}%%  </dt>%%  <dd>If the value is `true', documentation of hidden modules and%%      functions will also be included. The default value is `false'.%%  </dd>%%  <dt>{@type {overview, edoc:filename()@}}%%  </dt>%%  <dd>Specifies the name of the overview-file. By default, this doclet%%      looks for a file `"overview.edoc"' in the target directory.%%  </dd>%%  <dt>{@type {private, bool()@}}%%  </dt>%%  <dd>If the value is `true', documentation of private modules and%%      functions will also be included. The default value is `false'.%%  </dd>%%  <dt>{@type {stylesheet, string()@}}%%  </dt>%%  <dd>Specifies the URI used for referencing the stylesheet. The%%      default value is `"stylesheet.css"'. If an empty string is%%      specified, no stylesheet reference will be generated.%%  </dd>%%  <dt>{@type {stylesheet_file, edoc:filename()@}}%%  </dt>%%  <dd>Specifies the name of the stylesheet file. By default, this%%      doclet uses the file `"stylesheet.css"' in the `priv'%%      subdirectory of the EDoc installation directory. The named file%%      will be copied to the target directory.%%  </dd>%%  <dt>{@type {title, string()@}}%%  </dt>%%  <dd>Specifies the title of the overview-page.%%  </dd>%% </dl>%% INHERIT-OPTIONS: title/2%% INHERIT-OPTIONS: sources/5%% INHERIT-OPTIONS: overview/4%% INHERIT-OPTIONS: copy_stylesheet/2%% INHERIT-OPTIONS: stylesheet/1run(#doclet_gen{}=Cmd, Ctxt) ->    gen(Cmd#doclet_gen.sources,	Cmd#doclet_gen.app,	Cmd#doclet_gen.packages,	Cmd#doclet_gen.modules,	Cmd#doclet_gen.filemap,	Ctxt);run(#doclet_toc{}=Cmd, Ctxt) ->    toc(Cmd#doclet_toc.paths, Ctxt).gen(Sources, App, Packages, Modules, FileMap, Ctxt) ->    Dir = Ctxt#context.dir,    Env = Ctxt#context.env,    Options = Ctxt#context.opts,    Title = title(App, Options),    CSS = stylesheet(Options),    {Modules1, Error} = sources(Sources, Dir, Modules, Env, Options),    modules_frame(Dir, Modules1, Title, CSS),    packages(Packages, Dir, FileMap, Env, Options),    packages_frame(Dir, Packages, Title, CSS),    overview(Dir, Title, Env, Options),    index_file(Dir, length(Packages) > 1, Title),    edoc_lib:write_info_file(App, Packages, Modules1, Dir),    copy_stylesheet(Dir, Options),    copy_image(Dir),    %% handle postponed error during processing of source files    case Error of	true -> exit(error);	false -> ok    end.%% NEW-OPTIONS: title%% DEFER-OPTIONS: run/2title(App, Options) ->    proplists:get_value(title, Options,			if App == ?NO_APP ->				"Overview";			   true ->				io_lib:fwrite("Application: ~s", [App])			end).%% Processing the individual source files.%% NEW-OPTIONS: file_suffix, private, hidden%% INHERIT-OPTIONS: edoc:layout/2%% INHERIT-OPTIONS: edoc:get_doc/3%% DEFER-OPTIONS: run/2sources(Sources, Dir, Modules, Env, Options) ->    Suffix = proplists:get_value(file_suffix, Options,				 ?DEFAULT_FILE_SUFFIX),    Private = proplists:get_bool(private, Options),    Hidden = proplists:get_bool(hidden, Options),    {Ms, E} = lists:foldl(fun (Src, {Set, Error}) ->				  source(Src, Dir, Suffix, Env, Set,					 Private, Hidden, Error, Options)			  end,			  {sets:new(), false}, Sources),    {[M || M <- Modules, sets:is_element(M, Ms)], E}.%% Generating documentation for a source file, adding its name to the%% set if it was successful. Errors are just flagged at this stage,%% allowing all source files to be processed even if some of them fail.source({M, P, Name, Path}, Dir, Suffix, Env, Set, Private, Hidden,       Error, Options) ->    File = filename:join(Path, Name),    case catch {ok, edoc:get_doc(File, Env, Options)} of	{ok, {Module, Doc}} ->	    check_name(Module, M, P, File),	    case ((not is_private(Doc)) orelse Private)		andalso ((not is_hidden(Doc)) orelse Hidden) of		true ->		    Text = edoc:layout(Doc, Options),		    Name1 = packages:last(M) ++ Suffix,		    edoc_lib:write_file(Text, Dir, Name1, P),		    {sets:add_element(Module, Set), Error};		false ->		    {Set, Error}	    end;	R ->	    report("skipping source file '~s': ~W.", [File, R, 15]),	    {Set, true}    end.check_name(M, M0, P0, File) ->    P = list_to_atom(packages:strip_last(M)),    N = packages:last(M),    N0 = packages:last(M0),    case N of	[$? | _] ->	    %% A module name of the form '?...' is assumed to be caused	    %% by the epp_dodger parser when the module declaration has	    %% the form '-module(?MACRO).'; skip the filename check.	    ok;	_ ->	    if N =/= N0 ->		    warning("file '~s' actually contains module '~s'.",			    [File, M]);	       true ->		    ok	    end    end,    if P =/= P0 ->	    warning("file '~s' belongs to package '~s', not '~s'.",		    [File, P, P0]);       true ->	    ok    end.%% Generating the summary files for packages.%% INHERIT-OPTIONS: read_file/4%% INHERIT-OPTIONS: edoc_lib:run_layout/2packages(Packages, Dir, FileMap, Env, Options) ->    lists:foreach(fun (P) ->			  package(P, Dir, FileMap, Env, Options)		  end,		  Packages).package(P, Dir, FileMap, Env, Opts) ->    Tags = case FileMap(P) of	       "" ->		   [];	       File ->		   read_file(File, package, Env, Opts)	   end,    Data = edoc_data:package(P, Tags, Env, Opts),    F = fun (M) ->		M:package(Data, Opts)	end,    Text = edoc_lib:run_layout(F, Opts),    edoc_lib:write_file(Text, Dir, ?PACKAGE_SUMMARY, P).%% Creating an index file, with some frames optional.%% TODO: get rid of frames, or change doctype to Framesetindex_file(Dir, Packages, Title) ->    Frame1 = {frame, [{src,?PACKAGES_FRAME},		      {name,"packagesFrame"},{title,""}],	      []},    Frame2 = {frame, [{src,?MODULES_FRAME},		      {name,"modulesFrame"},{title,""}],	      []},    Frame3 = {frame, [{src,?OVERVIEW_SUMMARY},		      {name,"overviewFrame"},{title,""}],	      []},    Frameset = {frameset, [{cols,"20%,80%"}],		case Packages of		    true ->			[?NL,			 {frameset, [{rows,"30%,70%"}],			  [?NL, Frame1, ?NL, Frame2, ?NL]}			];		    false -> 			[?NL, Frame2, ?NL]		end		++ [?NL, Frame3, ?NL,		    {noframes,		     [?NL,		      {h2, ["This page uses frames"]},		      ?NL,		      {p, ["Your browser does not accept frames.",			   ?NL, br,			   "You should go to the ",			   {a, [{href, ?OVERVIEW_SUMMARY}],			    ["non-frame version"]},			   " instead.", ?NL]},		      ?NL]},		    ?NL]},    XML = xhtml_1(Title, [], Frameset),    Text = xmerl:export_simple([XML], xmerl_html, []),    edoc_lib:write_file(Text, Dir, ?INDEX_FILE).packages_frame(Dir, Ps, Title, CSS) ->    Body = [?NL,	    {h2, [{class, "indextitle"}], ["Packages"]},	    ?NL,	    {table, [{width, "100%"}, {border, 0},		     {summary, "list of packages"}],	     lists:concat(	       [[?NL,		 {tr, [{td, [], [{a, [{href, package_ref(P)},				      {target,"overviewFrame"},				      {class, "package"}],				  [atom_to_list(P)]}]}]}]		|| P <- Ps])},	    ?NL],    XML = xhtml(Title, CSS, Body),    Text = xmerl:export_simple([XML], xmerl_html, []),    edoc_lib:write_file(Text, Dir, ?PACKAGES_FRAME).modules_frame(Dir, Ms, Title, CSS) ->    Body = [?NL,	    {h2, [{class, "indextitle"}], ["Modules"]},	    ?NL,	    {table, [{width, "100%"}, {border, 0},		     {summary, "list of modules"}],	     lists:concat(	       [[?NL,		 {tr, [{td, [],			[{a, [{href, module_ref(M)},			      {target, "overviewFrame"},			      {class, "module"}],			  [atom_to_list(M)]}]}]}]		 || M <- Ms])},	    ?NL],    XML = xhtml(Title, CSS, Body),    Text = xmerl:export_simple([XML], xmerl_html, []),    edoc_lib:write_file(Text, Dir, ?MODULES_FRAME).module_ref(M) ->    edoc_refs:relative_package_path(M, '') ++ ?DEFAULT_FILE_SUFFIX.package_ref(P) ->    edoc_lib:join_uri(edoc_refs:relative_package_path(P, ''),		      ?PACKAGE_SUMMARY).xhtml(Title, CSS, Content) ->    xhtml_1(Title, CSS, {body, [{bgcolor, "white"}], Content}).xhtml_1(Title, CSS, Body) ->    {html, [?NL,	    {head, [?NL, {title, [Title]}, ?NL] ++ CSS},	    ?NL,	    Body,	    ?NL]    }.%% NEW-OPTIONS: overview%% INHERIT-OPTIONS: read_file/4%% INHERIT-OPTIONS: edoc_lib:run_layout/2%% INHERIT-OPTIONS: edoc_extract:file/4%% DEFER-OPTIONS: run/2overview(Dir, Title, Env, Opts) ->    File = proplists:get_value(overview, Opts,			       filename:join(Dir, ?OVERVIEW_FILE)),    Tags = read_file(File, overview, Env, Opts),    Data = edoc_data:overview(Title, Tags, Env, Opts),    F = fun (M) ->		M:overview(Data, Opts)	end,    Text = edoc_lib:run_layout(F, Opts),    edoc_lib:write_file(Text, Dir, ?OVERVIEW_SUMMARY).copy_image(Dir) ->    case code:priv_dir(?EDOC_APP) of	PrivDir when is_list(PrivDir) ->	    From = filename:join(PrivDir, ?IMAGE),	    edoc_lib:copy_file(From, filename:join(Dir, ?IMAGE));	_ ->	    report("cannot find default image file.", []),	    exit(error)    end.%% NEW-OPTIONS: stylesheet_file%% DEFER-OPTIONS: run/2copy_stylesheet(Dir, Options) ->    case proplists:get_value(stylesheet, Options) of	undefined ->	    From = case proplists:get_value(stylesheet_file, Options) of		       File when is_list(File) ->			   File;		       _ ->			   case code:priv_dir(?EDOC_APP) of			       PrivDir when is_list(PrivDir) ->				   filename:join(PrivDir, ?STYLESHEET);			       _ ->				   report("cannot find default "					  "stylesheet file.", []),				   exit(error)			   end		   end,	    edoc_lib:copy_file(From, filename:join(Dir, ?STYLESHEET));	_ ->	    ok    end.%% NEW-OPTIONS: stylesheet%% DEFER-OPTIONS: run/2stylesheet(Options) ->    case proplists:get_value(stylesheet, Options) of	"" ->	    [];	S ->	    Ref = case S of		      undefined ->			  ?STYLESHEET;		      "" ->			  "";    % no stylesheet		      S when is_list(S) ->			  S;		      _ ->			  report("bad value for option 'stylesheet'.",				 []),			  exit(error)		  end,	    [{link, [{rel, "stylesheet"},		     {type, "text/css"},		     {href, Ref},		     {title, "EDoc"}], []},	     ?NL]    end.is_private(E) ->    case get_attrval(private, E) of 	"yes" -> true; 	_ -> false    end.is_hidden(E) ->    case get_attrval(hidden, E) of 	"yes" -> true; 	_ -> false    end.get_attrval(Name, #xmlElement{attributes = As}) ->    case get_attr(Name, As) of	[#xmlAttribute{value = V}] ->	    V;	[] -> ""    end.get_attr(Name, [#xmlAttribute{name = Name} = A | As]) ->    [A | get_attr(Name, As)];get_attr(Name, [_ | As]) ->    get_attr(Name, As);get_attr(_, []) ->    [].%% Read external source file. Fails quietly, returning empty tag list.%% INHERIT-OPTIONS: edoc_extract:file/4read_file(File, Context, Env, Opts) ->    case edoc_extract:file(File, Context, Env, Opts) of	{ok, Tags} ->	    Tags;	{error, _} ->	    []    end.%% TODO: FIXME: meta-level index generation%% Creates a Table of Content from a list of Paths (ie paths to applications)%% and an overview file.-define(EDOC_DIR, "doc").-define(INDEX_DIR, "doc/index").-define(CURRENT_DIR, ".").toc(Paths, Ctxt) ->    Opts = Ctxt#context.opts,    Dir = Ctxt#context.dir,    Env = Ctxt#context.env,    app_index_file(Paths, Dir, Env, Opts).%% TODO: FIXME: it's unclear how much of this is working at all%% NEW-OPTIONS: title%% INHERIT-OPTIONS: overview/4app_index_file(Paths, Dir, Env, Options) ->    Title = proplists:get_value(title, Options,"Overview"),%    Priv = proplists:get_bool(private, Options),    CSS = stylesheet(Options),    Apps1 = [{filename:dirname(A),filename:basename(A)} || A <- Paths],    index_file(Dir, false, Title),    application_frame(Dir, Apps1, Title, CSS),    modules_frame(Dir, [], Title, CSS),    overview(Dir, Title, Env, Options),%    edoc_lib:write_info_file(Prod, [], Modules1, Dir),    copy_stylesheet(Dir, Options).application_frame(Dir, Apps, Title, CSS) ->    Body = [?NL,	    {h2, ["Applications"]},	    ?NL,	    {table, [{width, "100%"}, {border, 0}],	     lists:concat(	       [[{tr, [{td, [], [{a, [{href,app_ref(Path,App)},				      {target,"_top"}],				  [App]}]}]}]		|| {Path,App} <- Apps])},	    ?NL],    XML = xhtml(Title, CSS, Body),    Text = xmerl:export_simple([XML], xmerl_html, []),    edoc_lib:write_file(Text, Dir, ?MODULES_FRAME).app_ref(Path,M) ->    filename:join([Path,M,?EDOC_DIR,?INDEX_FILE]).

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?