diff --git a/doc/edown_doclet.md b/doc/edown_doclet.md index 54de337..f2ce9db 100644 --- a/doc/edown_doclet.md +++ b/doc/edown_doclet.md @@ -24,22 +24,22 @@ __Authors:__ Ulf Wiger ([`ulf.wiger@erlang-solutions.com`](mailto:ulf.wiger@erla
run/2Main doclet entry point.
- -

Function Details

+

Function Details

-

run/2

-runfun((Command::doclet_gen() | doclet_toc(), Ctxt::edoc_context()) -> ok) +`run(Command::[doclet_gen()](#type-doclet_gen) | [doclet_toc()](#type-doclet_toc), Ctxt::[edoc_context()](#type-edoc_context)) -> ok` +

+ @@ -150,4 +150,4 @@ will be copied to the target directory. -_Generated by EDoc, Mar 23 2011, 18:36:45._ \ No newline at end of file +_Generated by EDoc, Mar 24 2011, 12:34:30._ \ No newline at end of file diff --git a/doc/edown_layout.md b/doc/edown_layout.md index 9e134f7..b3191b7 100644 --- a/doc/edown_layout.md +++ b/doc/edown_layout.md @@ -29,15 +29,13 @@ The module is intended to be used together with edoc.
markdown/3
module/2The layout function.
overview/2
package/2
type/1
- -

Function Details

+

Function Details

-

markdown/3

@@ -46,10 +44,8 @@ The module is intended to be used together with edoc. `markdown(Title, CSS, Body) -> any()` - -

module/2

@@ -81,6 +77,21 @@ index tables. The default value is 1. +
{pretty_printer, atom()} +
+ + + + +
Specifies how types and specifications are pretty printed. +If the value erl_pp is specified the Erlang pretty printer +(the module erl_pp) will be used. The default is to do +no pretty printing which implies that lines can be very long. +
+ + + +
{stylesheet, string()}
@@ -95,7 +106,7 @@ specified, no stylesheet reference will be generated. -
{sort_functions, bool()} +
{sort_functions, boolean()}
@@ -123,9 +134,7 @@ used for exporting the documentation. See - +__See also:__ [//edoc/edoc:layout/2](http://www.erlang.org/doc/man/edoc.html#layout-2), [edown_doclet:layout/2](edown_doclet.md#layout-2).

overview/2

@@ -135,10 +144,8 @@ __See also:__ [//edoc/edoc:layout/2](http://www.erlang.org/doc/man/edoc.html#lay `overview(E, Options) -> any()` - -

package/2

@@ -147,10 +154,8 @@ __See also:__ [//edoc/edoc:layout/2](http://www.erlang.org/doc/man/edoc.html#lay `package(E, Options) -> any()` - -

type/1

@@ -161,4 +166,4 @@ __See also:__ [//edoc/edoc:layout/2](http://www.erlang.org/doc/man/edoc.html#lay -_Generated by EDoc, Mar 23 2011, 18:36:45._ \ No newline at end of file +_Generated by EDoc, Mar 24 2011, 12:34:30._ \ No newline at end of file diff --git a/doc/edown_lib.md b/doc/edown_lib.md index 9ef99dd..c493064 100644 --- a/doc/edown_lib.md +++ b/doc/edown_lib.md @@ -24,15 +24,13 @@ __Authors:__ Ulf Wiger ([`ulf.wiger@erlang-solutions.com`](mailto:ulf.wiger@erla
export/1
get_attrval/2
redirect_uri/1
- -

Function Details

+

Function Details

-

export/1

@@ -41,10 +39,8 @@ __Authors:__ Ulf Wiger ([`ulf.wiger@erlang-solutions.com`](mailto:ulf.wiger@erla `export(Data) -> any()` - -

get_attrval/2

@@ -53,10 +49,8 @@ __Authors:__ Ulf Wiger ([`ulf.wiger@erlang-solutions.com`](mailto:ulf.wiger@erla `get_attrval(Name, XmlElement) -> any()` - -

redirect_uri/1

@@ -67,4 +61,4 @@ __Authors:__ Ulf Wiger ([`ulf.wiger@erlang-solutions.com`](mailto:ulf.wiger@erla -_Generated by EDoc, Mar 23 2011, 18:36:45._ \ No newline at end of file +_Generated by EDoc, Mar 24 2011, 12:34:30._ \ No newline at end of file diff --git a/doc/edown_xmerl.md b/doc/edown_xmerl.md index a0079fa..a56ccec 100644 --- a/doc/edown_xmerl.md +++ b/doc/edown_xmerl.md @@ -23,15 +23,13 @@ __Authors:__ Ulf Wiger ([`ulf.wiger@erlang-solutions.com`](mailto:ulf.wiger@erla
'#element#'/5
'#root#'/4
'#text#'/1
'#xml-inheritance#'/0
- -

Function Details

+

Function Details

-

'#element#'/5

@@ -40,10 +38,8 @@ __Authors:__ Ulf Wiger ([`ulf.wiger@erlang-solutions.com`](mailto:ulf.wiger@erla `#element#(Tag, Data, Attrs, Parents, E) -> any()` - -

'#root#'/4

@@ -52,10 +48,8 @@ __Authors:__ Ulf Wiger ([`ulf.wiger@erlang-solutions.com`](mailto:ulf.wiger@erla `#root#(Data, Attrs, X3, E) -> any()` - -

'#text#'/1

@@ -64,10 +58,8 @@ __Authors:__ Ulf Wiger ([`ulf.wiger@erlang-solutions.com`](mailto:ulf.wiger@erla `#text#(Text) -> any()` - -

'#xml-inheritance#'/0

@@ -78,4 +70,4 @@ __Authors:__ Ulf Wiger ([`ulf.wiger@erlang-solutions.com`](mailto:ulf.wiger@erla -_Generated by EDoc, Mar 23 2011, 18:36:45._ \ No newline at end of file +_Generated by EDoc, Mar 24 2011, 12:34:30._ \ No newline at end of file diff --git a/rebar.config b/rebar.config index 5230427..e2beeff 100644 --- a/rebar.config +++ b/rebar.config @@ -8,4 +8,5 @@ {clean_files, ["*~","*/*~","ebin/*.beam"]}. {edoc_opts, [{doclet, edown_doclet}, + {src_path, ["src", "test"]}, {app_default,"http://www.erlang.org/doc/man"}]}. diff --git a/src/edown_doclet.erl b/src/edown_doclet.erl index a46ad43..5606d9b 100644 --- a/src/edown_doclet.erl +++ b/src/edown_doclet.erl @@ -104,6 +104,11 @@ %% INHERIT-OPTIONS: stylesheet/1 run(#doclet_gen{}=Cmd, Ctxt) -> + %% dbg:tracer(), + %% dbg:tpl(?MODULE,x), + %% dbg:tpl(edown_layout,x), + %% dbg:tpl(edown_xmerl, x), + %% dbg:p(all,[c]), gen(Cmd#doclet_gen.sources, Cmd#doclet_gen.app, Cmd#doclet_gen.packages, diff --git a/src/edown_layout.erl b/src/edown_layout.erl index 04407ed..f185e4f 100644 --- a/src/edown_layout.erl +++ b/src/edown_layout.erl @@ -56,13 +56,20 @@ %%
Specifies the number of column pairs used for the function %% index tables. The default value is 1. %%
+%%
{@type {pretty_printer, atom()@}} +%%
+%%
Specifies how types and specifications are pretty printed. +%% If the value `erl_pp' is specified the Erlang pretty printer +%% (the module `erl_pp') will be used. The default is to do +%% no pretty printing which implies that lines can be very long. +%%
%%
{@type {stylesheet, string()@}} %%
%%
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. %%
-%%
{@type {sort_functions, bool()@}} +%%
{@type {sort_functions, boolean()@}} %%
%%
If `true', the detailed function descriptions are listed by %% name, otherwise they are listed in the order of occurrence in @@ -94,14 +101,20 @@ module(Element, Options) -> %% % stylesheet = string(), %% % index_columns = integer()} --record(opts, {root, stylesheet, index_columns, sort_functions}). +-record(opts, {root, + stylesheet, + index_columns, + sort_functions, + pretty_printer}). init_opts(Element, Options) -> R = #opts{root = get_attrval(root, Element), index_columns = proplists:get_value(index_columns, Options, 1), sort_functions = proplists:get_value(sort_functions, - Options, true) + Options, true), + pretty_printer = proplists:get_value(pretty_printer, + Options, '') }, case proplists:get_value(stylesheet, Options) of undefined -> @@ -189,10 +202,10 @@ layout_module(#xmlElement{name = module, content = Es}=E, Opts) -> ["Description"]}]} | RestDesc] end - ++ types(lists:sort(Types)) + ++ types(lists:sort(Types), Opts) ++ function_index(SortedFs, Opts#opts.index_columns) - ++ if Opts#opts.sort_functions -> functions(SortedFs); - true -> functions(Functions) + ++ if Opts#opts.sort_functions -> functions(SortedFs, Opts); + true -> functions(Functions, Opts) end %% ++ navigation("bottom") ++ timestamp()), @@ -358,21 +371,23 @@ label_href(Content, F) -> %% %% -functions(Fs) -> - Es = lists:flatmap(fun ({Name, E}) -> function(Name, E) end, Fs), +functions(Fs, Opts) -> + Es = lists:flatmap(fun ({Name, E}) -> function(Name, E, Opts) end, Fs), if Es == [] -> []; true -> - [{a, [{name, ?FUNCTIONS_LABEL}], []}, - {h2, [?FUNCTIONS_TITLE]} | Es] + [?NL, + {h2, [{a, [{name, ?FUNCTIONS_LABEL}], [?FUNCTIONS_TITLE]}]}, + ?NL | Es] end. -function(Name, E=#xmlElement{content = Es}) -> +function(Name, E=#xmlElement{content = Es}, Opts) -> FHead = function_header(Name, E, " *"), - (label_anchor(FHead, E) + %% (label_anchor(FHead, E) + (label_anchor("", E) ++ [{h3, [lists:flatten(FHead)]}, {p, []}, {p, - case typespec(get_content(typespec, Es)) of + case typespec(get_content(typespec, Es), Opts) of [] -> signature(get_content(args, Es), get_attrval(name, E)); @@ -387,7 +402,7 @@ function(Name, E=#xmlElement{content = Es}) -> [] -> []; Rs -> [{p, Rs}] end - ++ throws(Es) + ++ throws(Es, Opts) ++ equiv_p(Es) ++ deprecated(Es, "function") ++ fulldesc(Es) @@ -411,9 +426,10 @@ is_exported(E) -> end. label_anchor(Content, E) -> +%% io:fwrite("label_anchor(~p, ~p)~n", [Content, E]), case get_attrval(label, E) of "" -> Content; - Ref -> [{a, [{name, Ref}], ""}] + Ref -> [{a, [{name, Ref}], Content}] end. %% @@ -452,66 +468,169 @@ returns(Es) -> %% -throws(Es) -> +throws(Es, Opts) -> case get_content(throws, Es) of [] -> []; Es1 -> - [{p, (["throws ", - {'div', [{class,"html"}], {tt, t_utype(get_elem(type, Es1))}}] - ++ local_defs(get_elem(localdef, Es1)))}] + %% Doesn't use format_type; keep it short! + [{p, (["throws ", {tt, t_utype(get_elem(type, Es1))}] + ++ local_defs(get_elem(localdef, Es1), Opts))}, + ?NL] end. %% -typespec([]) -> []; -typespec(Es) -> - [{'div', [{class,"html"}], [{tt, ([t_name(get_elem(erlangName, Es))] - ++ t_utype(get_elem(type, Es)))}]}] - ++ local_defs(get_elem(localdef, Es)). +typespec([], _Opts) -> []; +typespec(Es, Opts) -> + Name = t_name(get_elem(erlangName, Es)), + Defs = get_elem(localdef, Es), + [Type] = get_elem(type, Es), + format_spec(Name, Type, Defs, Opts) ++ local_defs(Defs, Opts). %% %% -types([]) -> []; -types(Ts) -> - Es = lists:flatmap(fun ({Name, E}) -> typedecl(Name, E) end, Ts), - [{h2, [{a, [{name, ?DATA_TYPES_LABEL}], - [?DATA_TYPES_TITLE]}]} | Es]. - -typedecl(Name, E=#xmlElement{content = Es}) -> - Lbl = Name ++ "()", - (label_anchor(Lbl, E) - ++ [{h3, [{class, "typedecl"}], [Lbl]}] - ++ [{'div', [{class,"html"}], typedef(get_content(typedef, Es))}] +types([], _Opts) -> []; +types(Ts, Opts) -> + Es = lists:flatmap(fun ({Name, E}) -> typedecl(Name, E, Opts) end, Ts), + [?NL, + {h2, [{a, [{name, ?DATA_TYPES_LABEL}], + [?DATA_TYPES_TITLE]}]}, + ?NL | Es]. + +typedecl(Name, E=#xmlElement{content = Es}, Opts) -> + ([?NL, {h3, [{class, "typedecl"}], label_anchor([Name, "()"], E)}, ?NL] + ++ [{p, typedef(get_content(typedef, Es), Opts)}, ?NL] ++ fulldesc(Es)). type_name(#xmlElement{content = Es}) -> t_name(get_elem(erlangName, get_content(typedef, Es))). -typedef(Es) -> +typedef(Es, Opts) -> Name = ([t_name(get_elem(erlangName, Es)), "("] - ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])), + ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])), (case get_elem(type, Es) of - [] -> [{b, ["abstract datatype"]}, ": ", {tt, Name}]; - Type -> - [{tt, Name ++ [" = "] ++ t_utype(Type)}] - end - ++ local_defs(get_elem(localdef, Es))). - -local_defs([]) -> []; -local_defs(Es) -> - [{ul, [{class, "definitions"}], - lists:concat([[{li, [{tt, localdef(E)}]}] || E <- Es])}]. - -localdef(E = #xmlElement{content = Es}) -> - (case get_elem(typevar, Es) of - [] -> - T_abs = t_abstype(get_content(abstype, Es)), - label_anchor(T_abs, E) ++ [{tt, T_abs}]; - [V] -> - t_var(V) + [] -> [{b, ["abstract datatype"]}, ": ", {tt, Name}]; + Type -> format_type(Name, Name, Type, [], Opts) end - ++ [" = "] ++ t_utype(get_elem(type, Es))). + ++ local_defs(get_elem(localdef, Es), Opts)). + +local_defs(Es, Opts) -> + local_defs(Es, [], Opts). + +local_defs([], _, _Opts) -> []; +local_defs(Es0, Last, Opts) -> + [E | Es] = lists:reverse(Es0), + [?NL, + {ul, [{class, "definitions"}], + lists:reverse(lists:append([localdef(E1, [], Opts) || E1 <- Es]), + localdef(E, Last, Opts))}]. + +localdef(E = #xmlElement{content = Es}, Last, Opts) -> + Name = case get_elem(typevar, Es) of + [] -> + R = label_anchor(N0 = t_abstype(get_content(abstype, Es)), E), + R; + [V] -> + N0 = t_var(V) + end, + [{li, format_type(Name, N0, get_elem(type, Es), Last, Opts)}]. + +%% Use the default formatting of EDoc, which creates references, and +%% then insert newlines and indentation according to erl_pp (the +%% (fast) Erlang pretty printer). +format_spec(Name, Type, Defs, #opts{pretty_printer = erl_pp}=Opts) -> + try + L = t_clause(Name, Type), + O = pp_clause(Name, Type), + {R, ".\n"} = etypef(L, O), + [{pre, R}] + catch _:_ -> + %% Example: "@spec ... -> record(a)" + format_spec(Name, Type, Defs, Opts#opts{pretty_printer=''}) + end; +format_spec(Sep, Type, Defs, _Opts) -> + %% Very limited formatting. + Br = if Defs =:= [] -> br; true -> [] end, + [{tt, t_clause(Sep, Type)}, Br]. + +t_clause(Name, Type) -> + #xmlElement{content = [#xmlElement{name = 'fun', content = C}]} = Type, + [Name] ++ t_fun(C). + +pp_clause(Pre, Type) -> + Types = ot_utype([Type]), + Atom = lists:duplicate(iolist_size(Pre), $a), + L1 = erl_pp:attribute({attribute,0,spec,{{list_to_atom(Atom),0},[Types]}}), + "-spec " ++ L2 = lists:flatten(L1), + L3 = Pre ++ lists:nthtail(length(Atom), L2), + re:replace(L3, "\n ", "\n", [{return,list},global]). + +format_type(Prefix, Name, Type, Last, #opts{pretty_printer = erl_pp}=Opts) -> + try + L = t_utype(Type), + O = pp_type(Name, Type), + {R, ".\n"} = etypef(L, O), + [{pre, Prefix ++ [" = "] ++ R ++ Last}] + catch _:_ -> + %% Example: "t() = record(a)." + format_type(Prefix, Name, Type, Last, Opts#opts{pretty_printer =''}) + end; +format_type(Prefix, _Name, Type, Last, _Opts) -> + [{tt, Prefix ++ [" = "] ++ t_utype(Type) ++ Last}]. + +pp_type(Prefix, Type) -> + Atom = list_to_atom(lists:duplicate(iolist_size(Prefix), $a)), + L1 = erl_pp:attribute({attribute,0,type,{Atom,ot_utype(Type),[]}}), + {L2,N} = case lists:dropwhile(fun(C) -> C =/= $: end, lists:flatten(L1)) of + ":: " ++ L3 -> {L3,9}; % compensation for extra "()" and ":" + "::\n" ++ L3 -> {"\n"++L3,6} + end, + Ss = lists:duplicate(N, $\s), + re:replace(L2, "\n"++Ss, "\n", [{return,list},global]). + +etypef(L, O0) -> + {R, O} = etypef(L, [], O0, []), + {lists:reverse(R), O}. + +etypef([C | L], St, [C | O], R) -> + etypef(L, St, O, [[C] | R]); +etypef(" "++L, St, O, R) -> + etypef(L, St, O, R); +etypef("", [Cs | St], O, R) -> + etypef(Cs, St, O, R); +etypef("", [], O, R) -> + {R, O}; +etypef(L, St, " "++O, R) -> + etypef(L, St, O, [" " | R]); +etypef(L, St, "\n"++O, R) -> + Ss = lists:takewhile(fun(C) -> C =:= $\s end, O), + etypef(L, St, lists:nthtail(length(Ss), O), ["\n"++Ss | R]); +etypef([{a, HRef, S0} | L], St, O0, R) -> + {S, O} = etypef(S0, app_fix(O0)), + etypef(L, St, O, [{a, HRef, S} | R]); +etypef("="++L, St, "::"++O, R) -> + %% EDoc uses "=" for record field types; Erlang types use "::". + %% Maybe there should be an option for this, possibly affecting + %% other similar discrepancies. + etypef(L, St, O, ["=" | R]); +etypef([Cs | L], St, O, R) -> + etypef(Cs, [L | St], O, R). + +app_fix(L) -> + try + {"//" ++ R1,L2} = app_fix(L, 1), + [App, Mod] = string:tokens(R1, "/"), + "//" ++ atom(App) ++ "/" ++ atom(Mod) ++ L2 + catch _:_ -> L + end. + +app_fix(L, I) -> % a bit slow + {L1, L2} = lists:split(I, L), + case erl_scan:tokens([], L1 ++ ". ", 1) of + {done, {ok,[{atom,_,Atom}|_],_}, _} -> {atom_to_list(Atom), L2}; + _ -> app_fix(L, I+1) + end. fulldesc(Es) -> case get_content(fullDescription, get_content(description, Es)) of @@ -660,9 +779,11 @@ author(E=#xmlElement{}) -> ++ if URI == "" -> []; true -> - [" [", {em, ["web site:"]}, " ", - {tt, [{a, [{href, URI}, {target, "_top"}], [URI]}]}, - "]"] + %% io:fwrite("URI = ~p~n", [URI]), + [" (", {em, ["web site:"]}, " ", + %% {tt, [{a, [{href, URI}, {target, "_top"}], [URI]}]}, + {a, [{href, URI}], [{tt, [URI]}]}, + ")"] end). references(Es) -> @@ -733,11 +854,10 @@ t_type([#xmlElement{name = tuple, content = Es}]) -> t_tuple(Es); t_type([#xmlElement{name = 'fun', content = Es}]) -> ["fun("] ++ t_fun(Es) ++ [")"]; -t_type([#xmlElement{name = record, content = Es}]) -> - t_record(Es); +t_type([E = #xmlElement{name = record, content = Es}]) -> + t_record(E, Es); t_type([E = #xmlElement{name = abstype, content = Es}]) -> - T = t_abstype(Es), - see(E, T); + t_abstype(E, Es); t_type([#xmlElement{name = union, content = Es}]) -> t_union(Es). @@ -778,13 +898,27 @@ t_fun(Es) -> ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [") -> "] ++ t_utype(get_elem(type, Es))). -t_record(Es) -> - ["#"] ++ t_type(get_elem(atom, Es)) ++ ["{"] - ++ seq(fun t_field/1, get_elem(field, Es), ["}"]). +t_record(E, Es) -> + Name = ["#"] ++ t_type(get_elem(atom, Es)), + case get_elem(field, Es) of + [] -> + see(E, [Name, "{}"]); + Fs -> + see(E, Name) ++ ["{"] ++ seq(fun t_field/1, Fs, ["}"]) + end. t_field(#xmlElement{content = Es}) -> t_type(get_elem(atom, Es)) ++ [" = "] ++ t_utype(get_elem(type, Es)). +t_abstype(E, Es) -> + Name = t_name(get_elem(erlangName, Es)), + case get_elem(type, Es) of + [] -> + see(E, [Name, "()"]); + Ts -> + see(E, [Name]) ++ ["("] ++ seq(fun t_utype_elem/1, Ts, [")"]) + end. + t_abstype(Es) -> ([t_name(get_elem(erlangName, Es)), "("] ++ seq(fun t_utype_elem/1, get_elem(type, Es), [")"])). @@ -865,8 +999,9 @@ type(E) -> type(E, []). type(E, Ds) -> - xmerl:export_simple_content(t_utype_elem(E) ++ local_defs(Ds), - ?HTML_EXPORT). + Opts = [], + xmerl:export_simple_content(t_utype_elem(E) ++ local_defs(Ds, Opts), + ?HTML_EXPORT). package(E=#xmlElement{name = package, content = Es}, Options) -> Opts = init_opts(E, Options), @@ -914,6 +1049,144 @@ overview(E=#xmlElement{name = overview, content = Es}, Options) -> _XML = markdown(Title, stylesheet(Opts), Body). %% xmerl:export_simple_content(XML, ?HTML_EXPORT). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% NYTT + +ot_utype([E]) -> + ot_utype_elem(E). + +ot_utype_elem(E=#xmlElement{content = Es}) -> + case get_attrval(name, E) of + "" -> ot_type(Es); + N -> + Name = {var,0,list_to_atom(N)}, + T = ot_type(Es), + case T of + Name -> T; + T -> {ann_type,0,[Name, T]} + end + end. + +ot_type([E=#xmlElement{name = typevar}]) -> + ot_var(E); +ot_type([E=#xmlElement{name = atom}]) -> + ot_atom(E); +ot_type([E=#xmlElement{name = integer}]) -> + ot_integer(E); +ot_type([E=#xmlElement{name = range}]) -> + ot_range(E); +ot_type([E=#xmlElement{name = binary}]) -> + ot_binary(E); +ot_type([E=#xmlElement{name = float}]) -> + ot_float(E); +ot_type([#xmlElement{name = nil}]) -> + ot_nil(); +ot_type([#xmlElement{name = paren, content = Es}]) -> + ot_paren(Es); +ot_type([#xmlElement{name = list, content = Es}]) -> + ot_list(Es); +ot_type([#xmlElement{name = nonempty_list, content = Es}]) -> + ot_nonempty_list(Es); +ot_type([#xmlElement{name = tuple, content = Es}]) -> + ot_tuple(Es); +ot_type([#xmlElement{name = 'fun', content = Es}]) -> + ot_fun(Es); +ot_type([#xmlElement{name = record, content = Es}]) -> + ot_record(Es); +ot_type([#xmlElement{name = abstype, content = Es}]) -> + ot_abstype(Es); +ot_type([#xmlElement{name = union, content = Es}]) -> + ot_union(Es). + +ot_var(E) -> + {var,0,list_to_atom(get_attrval(name, E))}. + +ot_atom(E) -> + {ok, [Atom], _} = erl_scan:string(get_attrval(value, E), 0), + Atom. + +ot_integer(E) -> + {integer,0,list_to_integer(get_attrval(value, E))}. + +ot_range(E) -> + [I1, I2] = string:tokens(get_attrval(value, E), "."), + {type,0,range,[{integer,0,list_to_integer(I1)}, + {integer,0,list_to_integer(I2)}]}. + +ot_binary(E) -> + {Base, Unit} = + case string:tokens(get_attrval(value, E), ",:*><") of + [] -> + {0, 0}; + ["_",B] -> + {list_to_integer(B), 0}; + ["_","_",U] -> + {0, list_to_integer(U)}; + ["_",B,_,"_",U] -> + {list_to_integer(B), list_to_integer(U)} + end, + {type,0,binary,[{integer,0,Base},{integer,0,Unit}]}. + +ot_float(E) -> + {float,0,list_to_float(get_attrval(value, E))}. + +ot_nil() -> + {nil,0}. + +ot_paren(Es) -> + {paren_type,0,[ot_utype(get_elem(type, Es))]}. + +ot_list(Es) -> + {type,0,list,[ot_utype(get_elem(type, Es))]}. + +ot_nonempty_list(Es) -> + {type,0,nonempty_list,[ot_utype(get_elem(type, Es))]}. + +ot_tuple(Es) -> + {type,0,tuple,[ot_utype_elem(E) || E <- Es]}. + +ot_fun(Es) -> + Range = ot_utype(get_elem(type, Es)), + Args = [ot_utype_elem(A) || A <- get_content(argtypes, Es)], + {type,0,'fun',[{type,0,product,Args},Range]}. + +ot_record(Es) -> + {type,0,record,[ot_type(get_elem(atom, Es)) | + [ot_field(F) || F <- get_elem(field, Es)]]}. + +ot_field(#xmlElement{content = Es}) -> + {type,0,field_type, + [ot_type(get_elem(atom, Es)), ot_utype(get_elem(type, Es))]}. + +ot_abstype(Es) -> + ot_name(get_elem(erlangName, Es), + [ot_utype_elem(Elem) || Elem <- get_elem(type, Es)]). + +ot_union(Es) -> + {type,0,union,[ot_utype_elem(E) || E <- Es]}. + +ot_name(Es, T) -> + case ot_name(Es) of + [Mod, ":", Atom] -> + {remote_type,0,[{atom,0,list_to_atom(Mod)}, + {atom,0,list_to_atom(Atom)},T]}; + "tuple" when T =:= [] -> + {type,0,tuple,any}; + Atom -> + {type,0,list_to_atom(Atom),T} + end. + +ot_name([E]) -> + Atom = get_attrval(name, E), + case get_attrval(module, E) of + "" -> Atom; + M -> + case get_attrval(app, E) of + "" -> + [M, ":", Atom]; + A -> + ["//"++A++"/" ++ M, ":", Atom] % EDoc only! + end + end. get_first_sentence([#xmlElement{name = p, content = Es} | Tail]) -> %% Descend into initial paragraph. diff --git a/src/edown_xmerl.erl b/src/edown_xmerl.erl index e7fe1b6..dab405f 100644 --- a/src/edown_xmerl.erl +++ b/src/edown_xmerl.erl @@ -48,15 +48,23 @@ lists:flatten(io_lib:fwrite("~p", [Text])) end. -normalize("\n" ++ [H|T]) when H==$\s; +normalize(S) -> + normalize1(to_string(S)). + +normalize1("\n" ++ [H|T]) when H==$\s; H==$\t -> - normalize("\n" ++ T); -normalize([H|T]) -> - [H|normalize(T)]; -normalize([]) -> + normalize1("\n" ++ T); +normalize1([H|T]) -> + [H|normalize1(T)]; +normalize1([]) -> []. +to_string(S) -> + binary_to_list(iolist_to_binary([S])). +strip(Str) -> lstrip(rstrip(Str)). +lstrip(Str) -> re:replace(Str,"^\\s","",[]). +rstrip(Str) -> re:replace(Str, "\\s\$", []). %% The '#root#' tag is called when the entire structure has been @@ -110,52 +118,53 @@ html_elem(Tag, Data, Attrs, Parents, E) -> end. md_elem(a, Data, Attrs, _Parents, _E) -> - %% io:fwrite("A TAG = ~p~nPs = ~p~n", [Data, _Parents]), + %% io:fwrite("A TAG = ~p~nPs = ~p~n", [_E, _Parents]), case lists:keyfind(href, #xmlAttribute.name, Attrs) of #xmlAttribute{value = HRef} -> - "[" ++ Data ++ "](" ++ HRef ++ ")"; + ["[", Data, "](", HRef, ")"]; false -> case lists:keyfind(name, #xmlAttribute.name, Attrs) of #xmlAttribute{} -> - ["\n", + [ %%"\n", xmerl_lib:start_tag(a,Attrs), Data, - xmerl_lib:end_tag(a), - "\n"] + xmerl_lib:end_tag(a) + %%"\n"] + ] end end; md_elem(img, _Data, Attrs, _Parents, _E) -> #xmlAttribute{value = Src} = lists:keyfind(src,#xmlAttribute.name,Attrs), #xmlAttribute{value = Alt} = lists:keyfind(alt,#xmlAttribute.name,Attrs), - "![" ++ Alt ++ "](" ++ Src ++ ")"; + ["![", Alt, "](", Src, ")"]; md_elem(li, Data, _Attrs, [{ul,_}|_], _E) -> - "* " ++ Data ++ "\n"; + ["* ", strip(Data), "\n"]; md_elem(li, Data, _Attrs, [{ol,_}|_], _E) -> - "1. " ++ Data ++ "\n"; + ["1. ", strip(Data), "\n"]; md_elem(Tag, Data, Attrs, Parents, E) -> case Tag of title -> %% io:fwrite("TITLE = |~s|~n", [Data]), Str = lists:flatten(Data), - Str ++ "\n" ++ [$= || _ <- Str] ++ "\n"; + [Str, "\n", [$= || _ <- to_string(Str)], "\n"]; html -> Data; body -> Data; 'div' -> Data; ul -> Data; ol -> Data; - p -> "\n\n" ++ Data; - b -> "__" ++ no_nl(Data) ++ "__"; - em -> "_" ++ no_nl(Data) ++ "_"; - i -> "_" ++ no_nl(Data) ++ "_"; - tt -> "`" ++ no_nl(Data) ++ "`"; - code -> "`" ++ no_nl(Data) ++ "`"; + p -> ["\n\n", Data]; + b -> ["__", no_nl(Data), "__"]; + em -> ["_", no_nl(Data), "_"]; + i -> ["_", no_nl(Data), "_"]; + tt -> ["`", no_nl(Data), "`"]; + code -> ["`", no_nl(Data), "`"]; dl -> Data; dt -> html_elem(dt, Data, Attrs, Parents, E); dd -> html_elem(dd, Data, Attrs, Parents, E); - h1 -> "\n\n#" ++ no_nl(Data) ++ "#\n"; - h2 -> "\n\n##" ++ no_nl(Data) ++ "##\n"; - h3 -> "\n\n###" ++ no_nl(Data) ++ "##\n"; - h4 -> "\n\n####" ++ no_nl(Data) ++ "##\n"; + h1 -> ["\n\n#", no_nl(Data), "#\n"]; + h2 -> ["\n\n##", no_nl(Data), "##\n"]; + h3 -> ["\n\n###", no_nl(Data), "##\n"]; + h4 -> ["\n\n####", no_nl(Data), "##\n"]; hr -> "---------\n"; head -> []; _ -> @@ -167,13 +176,15 @@ md_elem(Tag, Data, Attrs, Parents, E) -> end. within_html(Tags) -> - lists:any(fun({T,_}) -> needs_html(T) end, Tags). + lists:any(fun({pre,_}) -> true; + ({T,_}) -> needs_html(T) + end, Tags). needs_html(T) -> lists:member(T, [table,'div',h1,h2,h3,h4,dd,dt]). no_nl(S) -> - string:strip([C || C <- lists:flatten(S), + string:strip([C || C <- to_string(S), C =/= $\n], both). %% attr(#xmlAttribute{name = N, value = V}) ->