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/2 | Main doclet entry point. |
-
-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.
-
-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
-
-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
-
-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}) ->