edoc_layout.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 851 行 · 第 1/2 页
ERL
851 行
%% =====================================================================%% 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$%%%% @author Richard Carlsson <richardc@it.uu.se>%% @copyright 2001-2006 Richard Carlsson%% @see edoc%% @end%% =====================================================================%% @doc The standard HTML layout module for EDoc. See the {@link edoc}%% module for details on usage.%% Note that this is written so that it is *not* depending on edoc.hrl!-module(edoc_layout).-export([module/2, package/2, overview/2, type/1]).-import(edoc_report, [report/2]).-include("xmerl.hrl").-define(HTML_EXPORT, xmerl_html).-define(DEFAULT_XML_EXPORT, ?HTML_EXPORT).-define(OVERVIEW_SUMMARY, "overview-summary.html").-define(STYLESHEET, "stylesheet.css").-define(NL, "\n").-define(DESCRIPTION_TITLE, "Description").-define(DESCRIPTION_LABEL, "description").-define(DATA_TYPES_TITLE, "Data Types").-define(DATA_TYPES_LABEL, "types").-define(FUNCTION_INDEX_TITLE, "Function Index").-define(FUNCTION_INDEX_LABEL, "index").-define(FUNCTIONS_TITLE, "Function Details").-define(FUNCTIONS_LABEL, "functions").%% @doc The layout function.%%%% Options to the standard layout:%% <dl>%% <dt>{@type {index_columns, integer()@}}%% </dt>%% <dd>Specifies the number of column pairs used for the function%% index tables. The default value is 1.%% </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 {xml_export, Module::atom()@}}%% </dt>%% <dd>Specifies an {@link //xmerl. `xmerl'} callback module to be%% used for exporting the documentation. See {@link%% //xmerl/xmerl:export_simple/3} for details.%% </dd>%% </dl>%%%% @see edoc:layout/2%% NEW-OPTIONS: xml_export, index_columns, stylesheetmodule(Element, Options) -> XML = layout_module(Element, init_opts(Element, Options)), Export = proplists:get_value(xml_export, Options, ?DEFAULT_XML_EXPORT), xmerl:export_simple(XML, Export, []).% Put layout options in a data structure for easier access.%% %Commented out until it can be made private%% %@type opts() = #opts{root = string(),%% % stylesheet = string(),%% % index_columns = integer()}-record(opts, {root, stylesheet, index_columns}).init_opts(Element, Options) -> R = #opts{root = get_attrval(root, Element), index_columns = proplists:get_value(index_columns, Options, 1) }, case proplists:get_value(stylesheet, Options) of undefined -> S = edoc_lib:join_uri(R#opts.root, ?STYLESHEET), R#opts{stylesheet = S}; "" -> R; % don't use any stylesheet S when is_list(S) -> R#opts{stylesheet = S}; _ -> report("bad value for option `stylesheet'.", []), exit(error) end.%% =====================================================================%% XML-BASED LAYOUT ENGINE%% =====================================================================%% We assume that we have expanded XML data.%% <!ELEMENT module (behaviour*, description?, author*, copyright?,%% version?, since?, deprecated?, see*, reference*,%% todo?, typedecls?, functions)>%% <!ATTLIST module%% name CDATA #REQUIRED%% private NMTOKEN(yes | no) #IMPLIED%% root CDATA #IMPLIED>%% <!ELEMENT behaviour (#PCDATA)>%% <!ATTLIST behaviour%% href CDATA #IMPLIED>%% <!ELEMENT description (briefDescription, fullDescription?)>%% <!ELEMENT briefDescription (#PCDATA)>%% <!ELEMENT fullDescription (#PCDATA)>%% <!ELEMENT author EMPTY>%% <!ATTLIST author%% name CDATA #REQUIRED%% email CDATA #IMPLIED%% website CDATA #IMPLIED>%% <!ELEMENT version (#PCDATA)>%% <!ELEMENT since (#PCDATA)>%% <!ELEMENT copyright (#PCDATA)>%% <!ELEMENT deprecated (description)>%% <!ELEMENT see (#PCDATA)>%% <!ATTLIST see%% name CDATA #REQUIRED%% href CDATA #IMPLIED>%% <!ELEMENT reference (#PCDATA)>%% <!ELEMENT todo (#PCDATA)>%% <!ELEMENT typedecls (typedecl+)>%% <!ELEMENT functions (function+)>layout_module(#xmlElement{name = module, content = Es}=E, Opts) -> Name = get_attrval(name, E), Title = io_lib:format("Module ~s", [Name]), Desc = get_content(description, Es), ShortDesc = get_content(briefDescription, Desc), FullDesc = get_content(fullDescription, Desc), Functions = [E || E <- get_content(functions, Es)], SortedFs = lists:sort([{function_name(E), E} || E <- Functions]), Types = get_content(typedecls, Es), SortedTs = lists:sort([{type_name(E), E} || E <- Types]), Body = (navigation("top") ++ [?NL, hr, ?NL, ?NL, {h1, [Title]}, ?NL] ++ doc_index(FullDesc, Functions, Types) ++ ShortDesc ++ [?NL] ++ copyright(Es) ++ deprecated(Es, "module") ++ [?NL] ++ version(Es) ++ since(Es) ++ behaviours(Es, Name) ++ authors(Es) ++ references(Es) ++ sees(Es) ++ todos(Es) ++ if FullDesc == [] -> []; true -> [?NL, {h2, [{a, [{name, "description"}], ["Description"]}]} | FullDesc] end ++ types(SortedTs) ++ function_index(SortedFs, Opts#opts.index_columns) ++ functions(SortedFs) ++ [hr, ?NL] ++ navigation("bottom") ++ timestamp()), xhtml(Title, stylesheet(Opts), Body).timestamp() -> [?NL, {p, [{i, [io_lib:fwrite("Generated by EDoc, ~s, ~s.", [edoc_lib:datestr(date()), edoc_lib:timestr(time())]) ]}]}, ?NL]. stylesheet(Opts) -> case Opts#opts.stylesheet of undefined -> []; CSS -> [{link, [{rel, "stylesheet"}, {type, "text/css"}, {href, CSS}, {title, "EDoc"}], []}, ?NL] end.navigation(Where) -> [?NL, {'div', [{class, "navbar"}], [{a, [{name, "#navbar_" ++ Where}], []}, {table, [{width, "100%"}, {border,0}, {cellspacing, 0}, {cellpadding, 2}, {summary, "navigation bar"}], [{tr, [{td, [{a, [{href, ?OVERVIEW_SUMMARY}, {target,"overviewFrame"}], ["Overview"]}]}, {td, [{a, [{href, "http://www.erlang.org/"}], [{img, [{src, "erlang.png"}, {align, "right"}, {border, 0}, {alt, "erlang logo"}], []}]} ]} ]} ]} ]} ].doc_index(FullDesc, Functions, Types) -> case doc_index_rows(FullDesc, Functions, Types) of [] -> []; Rs -> [{ul, [{class, "index"}], [{li, [{a, [{href, local_label(R)}], [T]}]} || {T, R} <- Rs]}] end.doc_index_rows(FullDesc, Functions, Types) -> (if FullDesc == [] -> []; true -> [{?DESCRIPTION_TITLE, ?DESCRIPTION_LABEL}] end ++ if Types == [] -> []; true -> [{?DATA_TYPES_TITLE, ?DATA_TYPES_LABEL}] end ++ if Functions == [] -> []; true -> [{?FUNCTION_INDEX_TITLE, ?FUNCTION_INDEX_LABEL}, {?FUNCTIONS_TITLE, ?FUNCTIONS_LABEL}] end).function_index(Fs, Cols) -> case function_index_rows(Fs, Cols, []) of [] -> []; Rows -> [?NL, {h2, [{a, [{name, ?FUNCTION_INDEX_LABEL}], [?FUNCTION_INDEX_TITLE]}]}, ?NL, {table, [{width, "100%"}, {border, 1}, {cellspacing,0}, {cellpadding,2}, {summary, "function index"}], Rows}, ?NL] end.function_index_rows(Fs, Cols, Title) -> Rows = (length(Fs) + (Cols - 1)) div Cols, (if Title == [] -> []; true -> [{tr, [{th, [{colspan, Cols * 2}, {align, left}], [Title]}]}, ?NL] end ++ lists:flatmap(fun index_row/1, edoc_lib:transpose(edoc_lib:segment(Fs, Rows)))).index_row(Fs) -> [{tr, lists:flatmap(fun index_col/1, Fs)}, ?NL].index_col({Name, F=#xmlElement{content = Es}}) -> [{td, [{valign, "top"}], label_href(function_header(Name, F, "*"), F)}, {td, index_desc(Es)}].index_desc(Es) -> Desc = get_content(description, Es), (case get_content(deprecated, Es) of [] -> []; _ -> ["(", {em, ["Deprecated"]}, ".) "] end ++ case get_content(briefDescription, Desc) of [] -> equiv(Es); % no description at all if no equiv ShortDesc -> ShortDesc end).label_href(Content, F) -> case get_attrval(label, F) of "" -> Content; Ref -> [{a, [{href, local_label(Ref)}], Content}] end.%% <!ELEMENT function (args, typespec?, returns?, throws?, equiv?,%% description?, since?, deprecated?, see*, todo?)>%% <!ATTLIST function%% name CDATA #REQUIRED%% arity CDATA #REQUIRED%% exported NMTOKEN(yes | no) #REQUIRED%% label CDATA #IMPLIED>%% <!ELEMENT args (arg*)>%% <!ELEMENT equiv (expr, see?)>%% <!ELEMENT expr (#PCDATA)>functions(Fs) -> Es = lists:flatmap(fun ({Name, E}) -> function(Name, E) end, Fs), if Es == [] -> []; true -> [?NL, {h2, [{a, [{name, ?FUNCTIONS_LABEL}], [?FUNCTIONS_TITLE]}]}, ?NL | Es] end.function(Name, E=#xmlElement{content = Es}) -> ([?NL, {h3, [{class, "function"}], label_anchor(function_header(Name, E, " *"), E)}, ?NL] ++ [{'div', [{class, "spec"}], [?NL, {p, case typespec(get_content(typespec, Es)) of [] -> signature(get_content(args, Es), get_attrval(name, E)); Spec -> Spec end}, ?NL] ++ case params(get_content(args, Es)) of [] -> []; Ps -> [{p, Ps}, ?NL] end ++ case returns(get_content(returns, Es)) of [] -> []; Rs -> [{p, Rs}, ?NL] end}] ++ throws(Es) ++ equiv_p(Es) ++ deprecated(Es, "function") ++ fulldesc(Es) ++ since(Es) ++ sees(Es) ++ todos(Es)).function_name(E) -> atom(get_attrval(name, E)) ++ "/" ++ get_attrval(arity, E).function_header(Name, E, Private) -> case is_exported(E) of true -> [Name]; false -> [Name, Private] end.is_exported(E) -> case get_attrval(exported, E) of "yes" -> true; _ -> false end.label_anchor(Content, E) -> case get_attrval(label, E) of "" -> Content; Ref -> [{a, [{name, Ref}], Content}] end.%% <!ELEMENT args (arg*)>%% <!ELEMENT arg (argName, description?)>%% <!ELEMENT argName (#PCDATA)>%% This is currently only done for functions without type spec.signature(Es, Name) -> [{tt, [Name, "("] ++ seq(fun arg/1, Es) ++ [") -> term()"]}].arg(#xmlElement{content = Es}) -> [get_text(argName, Es)].%% parameter and return value descriptions (if any)params(Es) -> As = [{get_text(argName, Es1), get_content(fullDescription, get_content(description, Es1))} || #xmlElement{content = Es1} <- Es], As1 = [A || A <- As, element(2, A) /= []], if As1 == [] -> []; true -> [ { [{tt, [A]}, ": "] ++ D ++ [br, ?NL] } || {A, D} <- As1] end.returns(Es) -> case get_content(fullDescription, get_content(description, Es)) of [] -> []; D -> ["returns: "] ++ D end.%% <!ELEMENT throws (type, localdef*)>throws(Es) -> case get_content(throws, Es) of [] -> []; Es1 -> [{p, (["throws ", {tt, t_utype(get_elem(type, Es1))}] ++ local_defs(get_elem(localdef, Es1)))}, ?NL] end.%% <!ELEMENT typespec (erlangName, type, localdef*)>typespec([]) -> [];typespec(Es) -> [{tt, ([t_name(get_elem(erlangName, Es))] ++ t_utype(get_elem(type, Es)))}] ++ local_defs(get_elem(localdef, Es)).
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?