Skip to content

Commit

Permalink
Merge pull request #4579 from josevalim/jv-text-media-shell/OTP-17267
Browse files Browse the repository at this point in the history
Show docs from any textual chunk in the shell
  • Loading branch information
garazdawi authored Mar 11, 2021
2 parents 8910886 + 65e0ba7 commit 3a7873f
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 43 deletions.
9 changes: 5 additions & 4 deletions lib/stdlib/doc/src/shell_docs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,11 @@ Since:
> maps:new().
#{}
</code>
<p>This module can only render EEP-48 documentation of the format
<c>application/erlang+html</c>. For more information about this format see
<seeguide marker="erl_docgen:doc_storage">Documentation Storage</seeguide>
in Erl_Docgen's User's Guide.
<p>This module formats and renders EEP-48 documentation of
the format <c>application/erlang+html</c>. For more information about
this format see <seeguide marker="erl_docgen:doc_storage">Documentation
Storage</seeguide> in Erl_Docgen's User's Guide. It can also render
any other format of "text" type, although those will be rendered as is.
</p>
</description>

Expand Down
22 changes: 13 additions & 9 deletions lib/stdlib/src/c.erl
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,14 @@ c(SrcFile, NewOpts, Filter, BeamFile, Info) ->
-type ht_return() :: h_return() | {error, type_missing}.
-type hcb_return() :: h_return() | {error, callback_missing}.

-define(RENDERABLE_FORMAT(Format),
Format =:= ?NATIVE_FORMAT;
binary_part(Format, 0, 5) =:= <<"text/">>).

-spec h(module()) -> h_return().
h(Module) ->
case code:get_doc(Module) of
{ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
{ok, #docs_v1{ format = Format } = Docs} when ?RENDERABLE_FORMAT(Format) ->
format_docs(shell_docs:render(Module, Docs));
{ok, #docs_v1{ format = Enc }} ->
{error, {unknown_format, Enc}};
Expand All @@ -172,7 +176,7 @@ h(Module) ->
-spec h(module(),function()) -> hf_return().
h(Module,Function) ->
case code:get_doc(Module) of
{ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
{ok, #docs_v1{ format = Format } = Docs} when ?RENDERABLE_FORMAT(Format) ->
format_docs(shell_docs:render(Module, Function, Docs));
{ok, #docs_v1{ format = Enc }} ->
{error, {unknown_format, Enc}};
Expand All @@ -183,7 +187,7 @@ h(Module,Function) ->
-spec h(module(),function(),arity()) -> hf_return().
h(Module,Function,Arity) ->
case code:get_doc(Module) of
{ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
{ok, #docs_v1{ format = Format } = Docs} when ?RENDERABLE_FORMAT(Format) ->
format_docs(shell_docs:render(Module, Function, Arity, Docs));
{ok, #docs_v1{ format = Enc }} ->
{error, {unknown_format, Enc}};
Expand All @@ -194,7 +198,7 @@ h(Module,Function,Arity) ->
-spec ht(module()) -> h_return().
ht(Module) ->
case code:get_doc(Module) of
{ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
{ok, #docs_v1{ format = Format } = Docs} when ?RENDERABLE_FORMAT(Format) ->
format_docs(shell_docs:render_type(Module, Docs));
{ok, #docs_v1{ format = Enc }} ->
{error, {unknown_format, Enc}};
Expand All @@ -205,7 +209,7 @@ ht(Module) ->
-spec ht(module(),Type :: atom()) -> ht_return().
ht(Module,Type) ->
case code:get_doc(Module) of
{ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
{ok, #docs_v1{ format = Format } = Docs} when ?RENDERABLE_FORMAT(Format) ->
format_docs(shell_docs:render_type(Module, Type, Docs));
{ok, #docs_v1{ format = Enc }} ->
{error, {unknown_format, Enc}};
Expand All @@ -217,7 +221,7 @@ ht(Module,Type) ->
ht_return().
ht(Module,Type,Arity) ->
case code:get_doc(Module) of
{ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
{ok, #docs_v1{ format = Format } = Docs} when ?RENDERABLE_FORMAT(Format) ->
format_docs(shell_docs:render_type(Module, Type, Arity, Docs));
{ok, #docs_v1{ format = Enc }} ->
{error, {unknown_format, Enc}};
Expand All @@ -228,7 +232,7 @@ ht(Module,Type,Arity) ->
-spec hcb(module()) -> h_return().
hcb(Module) ->
case code:get_doc(Module) of
{ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
{ok, #docs_v1{ format = Format } = Docs} when ?RENDERABLE_FORMAT(Format) ->
format_docs(shell_docs:render_callback(Module, Docs));
{ok, #docs_v1{ format = Enc }} ->
{error, {unknown_format, Enc}};
Expand All @@ -239,7 +243,7 @@ hcb(Module) ->
-spec hcb(module(),Callback :: atom()) -> hcb_return().
hcb(Module,Callback) ->
case code:get_doc(Module) of
{ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
{ok, #docs_v1{ format = Format } = Docs} when ?RENDERABLE_FORMAT(Format) ->
format_docs(shell_docs:render_callback(Module, Callback, Docs));
{ok, #docs_v1{ format = Enc }} ->
{error, {unknown_format, Enc}};
Expand All @@ -251,7 +255,7 @@ hcb(Module,Callback) ->
hcb_return().
hcb(Module,Callback,Arity) ->
case code:get_doc(Module) of
{ok, #docs_v1{ format = ?NATIVE_FORMAT } = Docs} ->
{ok, #docs_v1{ format = Format } = Docs} when ?RENDERABLE_FORMAT(Format) ->
format_docs(shell_docs:render_callback(Module, Callback, Arity, Docs));
{ok, #docs_v1{ format = Enc }} ->
{error, {unknown_format, Enc}};
Expand Down
59 changes: 32 additions & 27 deletions lib/stdlib/src/shell_docs.erl
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,8 @@ trim_last([],_What) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec get_doc(Module :: module()) -> chunk_elements().
get_doc(Module) ->
{ok, #docs_v1{ module_doc = ModuleDoc } } = code:get_doc(Module),
get_local_doc(Module, ModuleDoc).
{ok, #docs_v1{ module_doc = ModuleDoc } = D } = code:get_doc(Module),
get_local_doc(Module, ModuleDoc, D).

-spec get_doc(Module :: module(), Function, Arity) ->
[{{Function,Arity}, Anno, Signature, chunk_elements(), Metadata}] when
Expand All @@ -346,15 +346,15 @@ get_doc(Module) ->
Signature :: [binary()],
Metadata :: #{}.
get_doc(Module, Function, Arity) ->
{ok, #docs_v1{ docs = Docs } } = code:get_doc(Module),
{ok, #docs_v1{ docs = Docs } = D } = code:get_doc(Module),
FnFunctions =
lists:filter(fun({{function, F, A},_Anno,_Sig,_Doc,_Meta}) ->
F =:= Function andalso A =:= Arity;
(_) ->
false
end, Docs),

[{F,A,S,get_local_doc({F,A},D),M} || {F,A,S,D,M} <- FnFunctions].
[{F,A,S,get_local_doc({F,A},Dc,D),M} || {F,A,S,Dc,M} <- FnFunctions].

-spec render(Module, Docs) -> unicode:chardata() when
Module :: module(),
Expand All @@ -375,7 +375,7 @@ render(Module, #docs_v1{ } = D) when is_atom(Module) ->
render(Module, #docs_v1{ module_doc = ModuleDoc } = D, Config)
when is_atom(Module), is_map(Config) ->
render_headers_and_docs([[{h2,[],[<<"\t",(atom_to_binary(Module))/binary>>]}]],
get_local_doc(Module, ModuleDoc), D, Config);
get_local_doc(Module, ModuleDoc, D), D, Config);
render(_Module, Function, #docs_v1{ } = D) ->
render(_Module, Function, D, #{}).

Expand Down Expand Up @@ -430,14 +430,14 @@ render(Module, Function, Arity, #docs_v1{ docs = Docs } = D, Config)
Signature :: [binary()],
Metadata :: #{}.
get_type_doc(Module, Type, Arity) ->
{ok, #docs_v1{ docs = Docs } } = code:get_doc(Module),
{ok, #docs_v1{ docs = Docs } = D } = code:get_doc(Module),
FnFunctions =
lists:filter(fun({{type, T, A},_Anno,_Sig,_Doc,_Meta}) ->
T =:= Type andalso A =:= Arity;
(_) ->
false
end, Docs),
[{F,A,S,get_local_doc(F, D),M} || {F,A,S,D,M} <- FnFunctions].
[{F,A,S,get_local_doc(F, Dc, D),M} || {F,A,S,Dc,M} <- FnFunctions].

-spec render_type(Module, Docs) -> unicode:chardata() when
Module :: module(),
Expand Down Expand Up @@ -501,14 +501,14 @@ render_type(_Module, Type, Arity, #docs_v1{ docs = Docs } = D, Config) ->
Signature :: [binary()],
Metadata :: #{}.
get_callback_doc(Module, Callback, Arity) ->
{ok, #docs_v1{ docs = Docs } } = code:get_doc(Module),
{ok, #docs_v1{ docs = Docs } = D } = code:get_doc(Module),
FnFunctions =
lists:filter(fun({{callback, T, A},_Anno,_Sig,_Doc,_Meta}) ->
T =:= Callback andalso A =:= Arity;
(_) ->
false
end, Docs),
[{F,A,S,get_local_doc(F, D),M} || {F,A,S,D,M} <- FnFunctions].
[{F,A,S,get_local_doc(F, Dc, D),M} || {F,A,S,Dc,M} <- FnFunctions].

-spec render_callback(Module, Docs) -> unicode:chardata() when
Module :: module(),
Expand Down Expand Up @@ -562,23 +562,28 @@ render_callback(_Module, Callback, Arity, #docs_v1{ docs = Docs } = D, Config) -
end, Docs), D, Config).

%% Get the docs in the correct locale if it exists.
get_local_doc(MissingMod, Docs) when is_atom(MissingMod) ->
get_local_doc(atom_to_binary(MissingMod), Docs);
get_local_doc({F,A}, Docs) ->
get_local_doc(unicode:characters_to_binary(io_lib:format("~tp/~p",[F,A])), Docs);
get_local_doc(_Missing, #{ <<"en">> := Docs }) ->
get_local_doc(MissingMod, Docs, D) when is_atom(MissingMod) ->
get_local_doc(atom_to_binary(MissingMod), Docs, D);
get_local_doc({F,A}, Docs, D) ->
get_local_doc(unicode:characters_to_binary(io_lib:format("~tp/~p",[F,A])), Docs, D);
get_local_doc(_Missing, #{ <<"en">> := Docs }, D) ->
%% English if it exists
normalize(Docs);
get_local_doc(_Missing, ModuleDoc) when map_size(ModuleDoc) > 0 ->
normalize_format(Docs, D);
get_local_doc(_Missing, ModuleDoc, D) when map_size(ModuleDoc) > 0 ->
%% Otherwise take first alternative found
normalize(maps:get(hd(maps:keys(ModuleDoc)), ModuleDoc));
get_local_doc(Missing, hidden) ->
normalize_format(maps:get(hd(maps:keys(ModuleDoc)), ModuleDoc), D);
get_local_doc(Missing, hidden, _D) ->
[{p,[],[<<"The documentation for ">>,Missing,
<<" is hidden. This probably means that it is internal "
"and not to be used by other applications.">>]}];
get_local_doc(Missing, None) when None =:= none; None =:= #{} ->
get_local_doc(Missing, None, _D) when None =:= none; None =:= #{} ->
[{p,[],[<<"There is no documentation for ">>,Missing]}].

normalize_format(Docs, #docs_v1{ format = ?NATIVE_FORMAT }) ->
normalize(Docs);
normalize_format(Docs, #docs_v1{ format = <<"text/", _/binary>> }) when is_binary(Docs) ->
[{pre, [], [Docs]}].

%%% Functions for rendering reference documentation
render_function([], _D, _Config) ->
{error,function_missing};
Expand All @@ -599,13 +604,13 @@ render_function(FDocs, #docs_v1{ docs = Docs } = D, Config) ->
Doc =/= #{}
end, Members) of
{value, {_,_,_,Doc,_Meta}} ->
render_headers_and_docs(Signatures, get_local_doc({F,A},Doc), D, Config);
render_headers_and_docs(Signatures, get_local_doc({F,A},Doc,D), D, Config);
false ->
case lists:keyfind(Group, 1, Docs) of
false ->
render_headers_and_docs(Signatures, get_local_doc({F,A},none), D, Config);
render_headers_and_docs(Signatures, get_local_doc({F,A},none,D), D, Config);
{_,_,_,Doc,_} ->
render_headers_and_docs(Signatures, get_local_doc({F,A},Doc), D, Config)
render_headers_and_docs(Signatures, get_local_doc({F,A},Doc,D), D, Config)
end
end
end, maps:to_list(Grouping)).
Expand Down Expand Up @@ -682,12 +687,12 @@ render_signature_listing(Module, Type, #docs_v1{ docs = Docs } = D, Config) ->
{br,[],[]}, Hdr], D, Config)
end.

render_typecb_docs([], _D) ->
render_typecb_docs([], _C) ->
{error,type_missing};
render_typecb_docs(TypeCBs, #config{} = D) when is_list(TypeCBs) ->
[render_typecb_docs(TypeCB, D) || TypeCB <- TypeCBs];
render_typecb_docs({{_,F,A},_,_Sig,Docs,_Meta} = TypeCB, #config{} = D) ->
render_headers_and_docs(render_signature(TypeCB), get_local_doc({F,A},Docs), D).
render_typecb_docs(TypeCBs, #config{} = C) when is_list(TypeCBs) ->
[render_typecb_docs(TypeCB, C) || TypeCB <- TypeCBs];
render_typecb_docs({{_,F,A},_,_Sig,Docs,_Meta} = TypeCB, #config{docs = D} = C) ->
render_headers_and_docs(render_signature(TypeCB), get_local_doc({F,A},Docs,D), C).
render_typecb_docs(Docs, D, Config) ->
render_typecb_docs(Docs, init_config(D, Config)).

Expand Down
25 changes: 22 additions & 3 deletions lib/stdlib/test/shell_docs_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
%%
-module(shell_docs_SUITE).
-export([all/0, suite/0, groups/0, init_per_suite/1, end_per_suite/1,
init_per_group/2, end_per_group/2]).
init_per_group/2, end_per_group/2]).

-export([render/1, render_smoke/1, links/1, normalize/1, render_prop/1]).
-export([render/1, render_smoke/1, links/1, normalize/1, render_prop/1,
render_non_native/1]).

-export([render_all/1, update_render/0, update_render/1]).

Expand All @@ -32,7 +33,7 @@ suite() ->
[{timetrap,{minutes,10}}].

all() ->
[render_smoke, render, links, normalize, {group, prop}].
[render_smoke, render, render_non_native, links, normalize, {group, prop}].

groups() ->
[{prop,[],[render_prop]}].
Expand Down Expand Up @@ -235,6 +236,24 @@ b2a(Bin) ->
{ok,[{A,_}],_} -> A
end.

%% Test rendering of non-native modules
render_non_native(_Config) ->
Docs = #docs_v1{
anno = erl_anno:new(13),
beam_language = not_erlang,
format = <<"text/asciidoc">>,
module_doc = #{<<"en">> => <<"This is\n\npure text">>},
docs= []
},

<<"\n\tnot_an_erlang_module\n\n"
" This is\n"
" \n"
" pure text\n">> =
unicode:characters_to_binary(shell_docs:render(not_an_erlang_module, Docs, #{})),

ok.

%% Testing functions
render_all(Dir) ->
file:make_dir(Dir),
Expand Down

0 comments on commit 3a7873f

Please sign in to comment.