edoc_data.erl

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

ERL
533
字号
%% =====================================================================%% 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$%%%% @private%% @copyright 2003 Richard Carlsson%% @author Richard Carlsson <richardc@csd.uu.se>%% @see edoc%% @end %% =====================================================================%% @doc Building the EDoc external data structure. See the file%% <a href="../priv/edoc.dtd">`edoc.dtd'</a> for details.-module(edoc_data).-export([module/4, package/4, overview/4, type/2]).-include("edoc.hrl").%% TODO: report multiple definitions of the same type in the same module.%% TODO: check that variables in @equiv are found in the signature%% TODO: copy types from target (if missing) when using @equiv%% <!ELEMENT module (description?, author*, copyright?, version?,%%                   since?, deprecated?, see*, reference*, todo?,%%                   behaviour*, callbacks?, typedecls?, functions)>%% <!ATTLIST module%%   name CDATA #REQUIRED%%   private NMTOKEN(yes | no) #IMPLIED%%   hidden NMTOKEN(yes | no) #IMPLIED%%   root 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 behaviour (#PCDATA)>%% <!ATTLIST behaviour%%   href CDATA #IMPLIED>%% <!ELEMENT callbacks (callback+)>%% <!ELEMENT typedecls (typedecl+)>%% <!ELEMENT typedecl (typedef, description?)>%% <!ELEMENT functions (function+)>%% NEW-OPTIONS: private, hidden, todo%% DEFER-OPTIONS: edoc_extract:source/4module(Module, Entries, Env, Opts) ->    Name = atom_to_list(Module#module.name),    HeaderEntry = get_entry(module, Entries),    HeaderTags = HeaderEntry#entry.data,    AllTags = get_all_tags(Entries),    Functions = function_filter(Entries, Opts),    Out = {module, ([{name, Name},		     {root, Env#env.root}]		    ++ case is_private(HeaderTags) of			   true -> [{private, "yes"}];			   false -> []		       end		    ++ case is_hidden(HeaderTags) of			   true -> [{hidden, "yes"}];			   false -> []		       end),	   (behaviours(Module#module.attributes, Env)	    ++ get_doc(HeaderTags)	    ++ authors(HeaderTags)	    ++ get_version(HeaderTags)	    ++ get_since(HeaderTags)	    ++ get_copyright(HeaderTags)	    ++ get_deprecated(HeaderTags)	    ++ sees(HeaderTags, Env)	    ++ references(HeaderTags)	    ++ todos(HeaderTags, Opts)	    ++ [{typedecls, types(AllTags, Env)},		{functions, functions(Functions, Env, Opts)}		| callbacks(Functions, Module, Env, Opts)])	  },    xmerl_lib:expand_element(Out).get_all_tags(Es) ->    lists:flatmap(fun (#entry{data = Ts}) -> Ts end, Es).is_private(Ts) ->    get_tags(private, Ts) =/= [].description([]) ->    [];description(Desc) ->    ShortDesc = edoc_lib:get_first_sentence(Desc),    [{description,      [{briefDescription, ShortDesc},       {fullDescription, Desc}]}].types(Tags, Env) ->    [{typedecl, [{label, edoc_types:to_label(Def)}],      [edoc_types:to_xml(Def, Env)] ++ description(Doc)}     || #tag{name = type, data = {Def, Doc}} <- Tags].functions(Es, Env, Opts) ->    [function(N, As, Export, Ts, Env, Opts)     || #entry{name = {_,_}=N, args = As, export = Export, data = Ts}	    <- Es].function_filter(Es, Opts) ->    Private = proplists:get_bool(private, Opts),    Hidden = proplists:get_bool(hidden, Opts),    [E || E <- Es, function_filter(E, Private, Hidden)].%% Note that only entries whose names have the form {_,_} are functions.function_filter(#entry{name = {_,_}, export = Export, data = Ts},		Private, Hidden) ->    ((Export andalso not is_private(Ts)) orelse Private)	andalso ((not is_hidden(Ts)) orelse Hidden);function_filter(_, _, _) ->    false.is_hidden(Ts) ->    get_tags(hidden, Ts) =/= [].callbacks(Es, Module, Env, Opts) ->    case lists:any(fun (#entry{name = {behaviour_info, 1}}) -> true;		       (_) -> false		   end,		   Es) of	true ->	    case catch (Module#module.name):behaviour_info(callbacks) of		{'EXIT', _} -> [];		Fs ->		    Fs1 = [{F,A} || {F,A} <- Fs, is_atom(F), is_integer(A)],		    if Fs1 == [] ->			    [];		       true ->			    [{callbacks,			      [callback(F, Env, Opts) || F <- Fs1]}]		    end	    end;	false -> []    end.%% <!ELEMENT callback EMPTY>%% <!ATTLIST callback%%   name CDATA #REQUIRED%%   arity CDATA #REQUIRED>callback({N, A}, _Env, _Opts) ->    {callback, [{name, atom_to_list(N)},		{arity, integer_to_list(A)}],     []}.%% <!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 arg (argName, description?)>%% <!ELEMENT argName (#PCDATA)>%% <!ELEMENT returns (description)>%% <!ELEMENT throws (type, localdef*)>%% <!ELEMENT equiv (expr, see?)>%% <!ELEMENT expr (#PCDATA)>function({N, A}, As, Export, Ts, Env, Opts) ->    {Args, Ret, Spec} = signature(Ts, As, Env),    {function, [{name, atom_to_list(N)},		{arity, integer_to_list(A)},      		{exported, case Export of			       true -> "yes";			       false -> "no"			   end},		{label, edoc_refs:to_label(edoc_refs:function(N, A))}],     [{args, [{arg, [{argName, [atom_to_list(A)]}] ++ description(D)}	      || {A, D} <- Args]}]     ++ Spec     ++ case Ret of	    [] -> [];	    _ -> [{returns, description(Ret)}]	end     ++ get_throws(Ts, Env)     ++ get_equiv(Ts, Env)     ++ get_doc(Ts)     ++ get_since(Ts)     ++ get_deprecated(Ts, N, A, Env)     ++ sees(Ts, Env)     ++ todos(Ts, Opts)    }.get_throws(Ts, Env) ->    case get_tags(throws, Ts) of	[Throws] ->	    Type = Throws#tag.data,	    [edoc_types:to_xml(Type, Env)];	[] ->	    []    end.get_equiv(Ts, Env) ->    case get_tags(equiv, Ts) of	[Equiv] ->	    Expr = Equiv#tag.data,	    See = case get_expr_ref(Equiv#tag.data) of		      none -> [];		      Ref ->			  [see(Ref, [edoc_refs:to_string(Ref)], Env)]		  end,	    [{equiv, [{expr, [erl_prettypr:format(Expr)]} | See]}];	[] ->	    []    end.get_doc(Ts) ->    case get_tags(doc, Ts) of	[T] ->	    description(T#tag.data);	[] ->	    []    end.get_copyright(Ts) ->    get_pcdata_tag(copyright, Ts).get_version(Ts) ->    get_pcdata_tag(version, Ts).get_since(Ts) ->    get_pcdata_tag(since, Ts).get_pcdata_tag(Tag, Ts) ->    case get_tags(Tag, Ts) of	[T] ->	    [{Tag, [T#tag.data]}];	[] ->	    []    end.%% Deprecation declarations for xref:%%%%   -deprecated(Info).%%       Info = Spec | [Spec]%%       Spec = module | {F,A} | {F,A,Details}}%%       Details = next_version | next_major_release | eventually%%                 (EXTENSION: | string() | {M1,F1,A1}}%% TODO: use info from '-deprecated(...)' (xref-)declarations.get_deprecated(Ts) ->    case get_tags(deprecated, Ts) of	[T] ->	    [{deprecated, description(T#tag.data)}];	[] ->	    []    end.get_deprecated(Ts, F, A, Env) ->    case get_deprecated(Ts) of	[] ->	    M = Env#env.module,	    case erl_internal:obsolete(M, F, A) of		{true, {M1, F1, A1}} ->		    Text = if M =:= M1 ->				   io_lib:fwrite("~w/~w", [F1, A1]);			      true ->				   io_lib:fwrite("~w:~w/~w",						 [M1, F1, A1])			   end,		    Ref = if M =:= M1 ->				  edoc_refs:function(F1, A1);			     true ->				  edoc_refs:function(M1, F1, A1)			  end,		    Desc = ["Use ",			    {a, href(Ref, Env), [{code, [Text]}]},			    " instead."],		    [{deprecated, description(Desc)}];		{true, Text} ->		    [{deprecated, description([Text])}];		_ ->		    []	    end;	Es ->	    Es    end.get_expr_ref(Expr) ->    case catch {ok, erl_syntax_lib:analyze_application(Expr)} of	{ok, {F, A}} when is_atom(F), is_integer(A) ->	    edoc_refs:function(F, A); 	{ok, {M, {F, A}}} when is_atom(M), is_atom(F), is_integer(A) -> 	    edoc_refs:function(M, F, A);	_ ->	    none    end.authors(Ts) ->    [author(Info) || #tag{data = Info} <- get_tags(author, Ts)].%% <!ATTLIST author%%   name CDATA #REQUIRED%%   email CDATA #IMPLIED%%   website CDATA #IMPLIED>author({Name, Mail, URI}) ->    %% At least one of Name and Mail must be nonempty in the tag.    {author, ([{name, if Name =:= "" -> Mail;			 true -> Name		      end}]	      ++ if Mail =:= "" ->			 case lists:member($@, Name) of			     true -> [{email, Name}];			     false -> []			 end;		    true -> [{email, Mail}]		 end	      ++ if URI =:= "" -> [];		    true -> [{website, URI}]		 end), []}.behaviours(As, Env) ->    [{behaviour, href(edoc_refs:module(B), Env), [atom_to_list(B)]}     || {behaviour, B} <- As, is_atom(B)].sees(Tags, Env) ->    Ts = get_tags(see, Tags),    Rs = lists:keysort(1, [Data || #tag{data = Data} <- Ts]),    [see(Ref, XML, Env) || {Ref, XML} <- Rs].see(Ref, [], Env) ->    see(Ref, [edoc_refs:to_string(Ref)], Env);see(Ref, XML, Env) ->    {see, [{name, edoc_refs:to_string(Ref)}] ++ href(Ref, Env), XML}.href(Ref, Env) ->    [{href, edoc_refs:get_uri(Ref, Env)}]	++ case edoc_refs:is_top(Ref, Env) of	       true ->		   [{target, "_top"}];	       false ->		   []	   end.references(Tags) ->    [{reference, XML} || #tag{data = XML} <- get_tags(reference, Tags)].todos(Tags, Opts) ->    case proplists:get_bool(todo, Opts) of	true ->	    [{todo, XML} || #tag{data = XML} <- get_tags('todo', Tags)];	false ->	    []    end.signature(Ts, As, Env) ->    case get_tags(spec, Ts) of	[T] ->	    Spec = T#tag.data,	    R = merge_returns(Spec, Ts),	    As0 = edoc_types:arg_names(Spec),	    Ds0 = edoc_types:arg_descs(Spec),	    %% choose names in spec before names in code	    P = dict:from_list(params(Ts)),	    As1 = merge_args(As0, As, Ds0, P),	    %% check_params(As1, P),	    Spec1 = edoc_types:set_arg_names(Spec, [A || {A,_} <- As1]),	    {As1, R, [edoc_types:to_xml(Spec1, Env)]};	[] ->	    S = sets:new(),	    {fix_argnames(As, S, 1), [], []}    end.params(Ts) ->    [T#tag.data || T <- get_tags(param, Ts)].%% check_params(As, P) ->%%     case dict:keys(P) -- [N || {N,_} <- As] of%% 	[] -> ok;%% 	Ps -> error  %% TODO: report @param declarations with no match%%     end.merge_returns(Spec, Ts) ->    case get_tags(return, Ts) of	[] ->	    case edoc_types:range_desc(Spec) of		"" -> [];		Txt -> [Txt]	    end;	[T] -> T#tag.data    end.%% Names are chosen from the first list (the specification) if possible.%% Descriptions specified with @param (in P dict) override descriptions%% from the spec (in Ds).merge_args(As, As1, Ds, P) ->    merge_args(As, As1, Ds, [], P, sets:new(), 1).merge_args(['_' | As], ['_' | As1], [D | Ds], Rs, P, S, N) ->    merge_args(As, As1, Ds, Rs, P, S, N, make_name(N, S), D);merge_args(['_' | As], [A | As1], [D | Ds], Rs, P, S, N) ->    merge_args(As, As1, Ds, Rs, P, S, N, A, D);merge_args([A | As], [_ | As1], [D | Ds], Rs, P, S, N) ->    merge_args(As, As1, Ds, Rs, P, S, N, A, D);merge_args([], [], [], Rs, _P, _S, _N) ->    lists:reverse(Rs).merge_args(As, As1, Ds, Rs, P, S, N, A, D0) ->    D = case dict:find(A, P) of	    {ok, D1} -> D1;	    error when D0 =:= [] -> [];  % no description	    error -> [D0]  % a simple-xml text element	end,    merge_args(As, As1, Ds, [{A, D} | Rs], P,	       sets:add_element(A, S), N + 1).fix_argnames(['_' | As], S, N) ->    A = make_name(N, S),    [A | fix_argnames(As, sets:add_element(A, S), N + 1)];fix_argnames([A | As], S, N) ->    [A | fix_argnames(As, sets:add_element(A, S), N + 1)];fix_argnames([], _S, _N) ->    [].make_name(N, S) ->    make_name(N, S, "X").make_name(N, S, Base) ->    A = list_to_atom(Base ++ integer_to_list(N)),    case sets:is_element(A, S) of 	true -> 	    make_name(N, S, Base ++ "x"); 	false -> 	    A    end.get_entry(Name, [#entry{name = Name} = E | _Es]) -> E;get_entry(Name, [_ | Es]) -> get_entry(Name, Es).get_tags(Tag, [#tag{name = Tag} = T | Ts]) -> [T | get_tags(Tag, Ts)];get_tags(Tag, [_ | Ts]) -> get_tags(Tag, Ts);get_tags(_, []) -> [].%% ---------------------------------------------------------------------type(T, Env) ->    xmerl_lib:expand_element({type, [edoc_types:to_xml(T, Env)]}).%% <!ELEMENT package (description?, author*, copyright?, version?,%% 		   since?, deprecated?, see*, reference*, todo?,%% 		   modules)>%% <!ATTLIST package%%   name CDATA #REQUIRED%%   root CDATA #IMPLIED>%% <!ELEMENT modules (module+)>package(Package, Tags, Env, Opts) ->    Env1 = Env#env{package = Package,		   root = edoc_refs:relative_package_path('', Package)},    xmerl_lib:expand_element(package_1(Package, Tags, Env1, Opts)).package_1(Package, Tags, Env, Opts) ->    {package, [{root, Env#env.root}],     ([{packageName, [atom_to_list(Package)]}]      ++ get_doc(Tags)      ++ authors(Tags)      ++ get_copyright(Tags)      ++ get_version(Tags)      ++ get_since(Tags)      ++ get_deprecated(Tags)      ++ sees(Tags, Env)      ++ references(Tags)      ++ todos(Tags, Opts))    }.%% <!ELEMENT overview (title, description?, author*, copyright?, version?,%%                     since?, see*, reference*, todo?, packages, modules)>%% <!ATTLIST overview%%   root CDATA #IMPLIED>%% <!ELEMENT title (#PCDATA)>overview(Title, Tags, Env, Opts) ->    Env1 = Env#env{package = '',		   root = ""},    xmerl_lib:expand_element(overview_1(Title, Tags, Env1, Opts)).overview_1(Title, Tags, Env, Opts) ->    {overview, [{root, Env#env.root}],     ([{title, [get_title(Tags, Title)]}]      ++ get_doc(Tags)      ++ authors(Tags)      ++ get_copyright(Tags)      ++ get_version(Tags)      ++ get_since(Tags)      ++ sees(Tags, Env)      ++ references(Tags)      ++ todos(Tags, Opts))    }.get_title(Ts, Default) ->    case get_tags(title, Ts) of	[T] ->	    T#tag.data;	[] ->	    Default    end.

⌨️ 快捷键说明

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