diff --git a/lib/stdlib/doc/src/shell_docs.xml b/lib/stdlib/doc/src/shell_docs.xml index 65a3e37bae16..ed250f7b0360 100644 --- a/lib/stdlib/doc/src/shell_docs.xml +++ b/lib/stdlib/doc/src/shell_docs.xml @@ -54,10 +54,11 @@ Since: > maps:new(). #{} -

This module can only render EEP-48 documentation of the format - application/erlang+html. For more information about this format see - Documentation Storage - in Erl_Docgen's User's Guide. +

This module formats and renders EEP-48 documentation of + the format application/erlang+html. For more information about + this format see Documentation + Storage in Erl_Docgen's User's Guide. It can also render + any other format of "text" type, although those will be rendered as is.

diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl index 6c1b9ca39dd8..80127bc31b5d 100644 --- a/lib/stdlib/src/c.erl +++ b/lib/stdlib/src/c.erl @@ -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}}; @@ -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}}; @@ -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}}; @@ -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}}; @@ -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}}; @@ -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}}; @@ -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}}; @@ -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}}; @@ -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}}; diff --git a/lib/stdlib/src/shell_docs.erl b/lib/stdlib/src/shell_docs.erl index f041bbc5fa81..22dbcc335fb5 100644 --- a/lib/stdlib/src/shell_docs.erl +++ b/lib/stdlib/src/shell_docs.erl @@ -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 @@ -346,7 +346,7 @@ 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; @@ -354,7 +354,7 @@ get_doc(Module, Function, 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(), @@ -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, #{}). @@ -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(), @@ -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(), @@ -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}; @@ -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)). @@ -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)). diff --git a/lib/stdlib/test/shell_docs_SUITE.erl b/lib/stdlib/test/shell_docs_SUITE.erl index 0d0941c7ea97..7768fa563afc 100644 --- a/lib/stdlib/test/shell_docs_SUITE.erl +++ b/lib/stdlib/test/shell_docs_SUITE.erl @@ -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]). @@ -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]}]. @@ -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),