Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 588 lines (485 sloc) 19.373 kb
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
1 -module(erldocs_core).
2 -export([copy_static_files/1, build/1, dispatch/1]).
3 -export([mapreduce/4, pmapreduce/4, pmapreduce/5]).
4 -include_lib("kernel/include/file.hrl").
5
5c30023 @akaspin better errors
akaspin authored
6 -define(LOG(Str, Args), io:format(Str, Args)).
aabb6ca @akaspin Fix better errors
akaspin authored
7 -define(LOG(Str), io:format(Str)).
5c30023 @akaspin better errors
akaspin authored
8
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
9 %% @doc Copy static files
10 -spec copy_static_files(list()) -> ok.
11 copy_static_files(Conf) ->
12 {ok, ErlDocsCSS} = erldocs_css_dtl:render([]),
13 edoc_lib:write_file(ErlDocsCSS, dest(Conf), "erldocs.css"),
14 {ok, ErlDocsJS} = erldocs_js_dtl:render([]),
15 edoc_lib:write_file(ErlDocsJS, dest(Conf), "erldocs.js"),
16 {ok, Jquery} = jquery_js_dtl:render([]),
17 edoc_lib:write_file(Jquery, dest(Conf), "jquery.js"),
18 ok.
19
20 %% @doc Parses arguments passed to script and calls
21 %% appropriate function.
22 -spec dispatch(list()) -> ok.
23 dispatch(Conf) ->
bacbb00 @dreverri Removed copystatic option since build/1 always copied static files
dreverri authored
24 Start = erlang:now(),
25 build(Conf),
26 Diff = timer:now_diff(erlang:now(), Start),
27 Mins = trunc(Diff * 1.667e-8),
28 log("Woot, finished in ~p Minutes ~p Seconds~n",
29 [Mins, trunc((Diff * 1.0e-6) - (Mins * 60))]).
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
30
31
32 %% @doc Build everything
33 -spec build(list()) -> ok.
34 build(Conf) ->
35 filelib:ensure_dir(dest(Conf)),
36
37 Fun = fun(X, Y) -> build_apps(Conf, X, Y) end,
38 Index = strip_cos(lists:foldl(Fun, [], app_dirs(Conf))),
39
40 ok = module_index(Conf, Index),
41 ok = javascript_index(Conf, Index),
42 ok = copy_static_files(Conf).
43
44 build_apps(Conf, App, Index) ->
45 AppName = bname(App),
7f0e9ef @dreverri Added more logging
dreverri authored
46 log("Building ~s~n", [AppName]),
47 Files = ensure_docsrc(App, Conf),
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
48 Map = fun (F) -> build_file_map(Conf, AppName, F) end,
49 [["app", AppName, AppName, "[application]"] |
50 pmapreduce(Map, fun lists:append/2, [], Files) ++ Index].
51
52 build_file_map(Conf, AppName, File) ->
7f0e9ef @dreverri Added more logging
dreverri authored
53 log("Generating HTML - ~s~n", [bname(File, ".xml")]),
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
54 {Type, _Attr, Content} = read_xml(Conf, File),
55
b380108 @daleharvey Lookup type specs in seperate type specs xml files (fixes #12)
authored
56 TypeSpecsFile = filename:join([dest(Conf), ".xml", "specs_" ++ bname(File)]),
57 TypeSpecs = case read_xml(Conf, TypeSpecsFile) of
58 {error, _, _} -> [];
59 {module, _, Specs} -> strip_whitespace(Specs)
60 end,
61
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
62 case lists:member(Type, buildable()) of
63 false -> [];
64 true ->
65
66 Module = bname(File, ".xml"),
67 Xml = strip_whitespace(Content),
68
69 Sum2 = case Type of
70 erlref ->
71 {modulesummary, [], Sum}
72 = lists:keyfind(modulesummary,1, Xml),
00ce841 @akaspin UTF-8 compatibility.
akaspin authored
73 unicode:characters_to_list(Sum);
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
74 cref ->
75 {libsummary, [], Sum}
76 = lists:keyfind(libsummary, 1, Xml),
77 Sum
78 end,
79
80 Sum1 = lists:flatten(Sum2),
81
82 % strip silly shy characters
83 Funs = get_funs(AppName, Module, lists:keyfind(funcs, 1, Xml)),
84
b380108 @daleharvey Lookup type specs in seperate type specs xml files (fixes #12)
authored
85 ok = render(Type, AppName, Module, Content, TypeSpecs, Conf),
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
86
87 case lists:member({AppName, Module}, ignore()) of
88 true -> [];
89 false -> [ ["mod", AppName, Module, Sum1] | Funs]
90 end
91 end.
92
93 %% @doc strip out the cos* files from the index, who the hell needs them
94 %% anyway
95 -spec strip_cos(list()) -> list().
96 strip_cos(Index) ->
97 [X || X = [_, App |_] <- Index, nomatch == re:run(App, "^cos") ].
98
99 ensure_docsrc(AppDir, Conf) ->
355d423 change I have forgotten
Dale Harvey authored
100
101 % List any doc/src/*.xml files that exist in the source files
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
102 XMLFiles = filelib:wildcard(filename:join([AppDir, "doc", "src", "*.xml"])),
8b97987 @daleharvey Generate type specification xml files
authored
103 HandWritten = [bname(File, ".xml") || File <- XMLFiles],
104
105 ErlFiles = filelib:wildcard(filename:join([AppDir, "*.erl"])) ++
106 filelib:wildcard(filename:join([AppDir, "src", "*.erl"])),
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
107
355d423 change I have forgotten
Dale Harvey authored
108 % Generate any missing module XML
8b97987 @daleharvey Generate type specification xml files
authored
109 SrcFiles = [filename:absname(File) ||
110 File <- ErlFiles,
111 not lists:member(bname(File, ".erl"), HandWritten)],
355d423 change I have forgotten
Dale Harvey authored
112
113 % Output XML files to destination folder
114 % This prevents polluting the source files
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
115 XMLDir = filename:join([dest(Conf), ".xml", bname(AppDir)]),
116 filelib:ensure_dir(XMLDir ++ "/"),
117
b380108 @daleharvey Lookup type specs in seperate type specs xml files (fixes #12)
authored
118 SpecsDest = filename:join([dest(Conf), ".xml"]),
119
8b97987 @daleharvey Generate type specification xml files
authored
120 [ begin
b380108 @daleharvey Lookup type specs in seperate type specs xml files (fixes #12)
authored
121 log("Generating Type Specs - ~s~n", [File]),
000991b @daleharvey Added includes to type spec generator
authored
122 Args = "-I" ++ AppDir ++ "/include -o" ++ SpecsDest ++ " " ++ File,
123 os:cmd("./priv/bin/specs_gen.escript " ++ Args)
8b97987 @daleharvey Generate type specification xml files
authored
124 end || File <- ErlFiles],
125
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
126 %% Return the complete list of XML files
127 XMLFiles ++ tmp_cd(XMLDir, fun() -> gen_docsrc(AppDir, SrcFiles, XMLDir) end).
128
129
130 gen_docsrc(AppDir, SrcFiles, Dest) ->
fd2ff81 @daleharvey Normalise location to source files
authored
131 Opts = [{includes, filelib:wildcard(AppDir ++ "/include")},
132 {sort_functions,false}],
133
7f0e9ef @dreverri Added more logging
dreverri authored
134 lists:foldl(fun(File, Acc) ->
fd2ff81 @daleharvey Normalise location to source files
authored
135 log("Generating XML - ~s~n", [bname(File, ".erl")]),
136 case (catch docb_gen:module(File, Opts)) of
137 ok ->
138 [filename:join([Dest, bname(File, ".erl")]) ++ ".xml"|Acc];
139 Error ->
140 log("Error generating XML (~p): ~p~n", [File, Error]),
141 Acc
142 end
143 end, [], SrcFiles).
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
144
145 %% @doc run a function with the cwd set, ensuring the cwd is reset once
146 %% finished (some dumb functions require to be ran from a particular dir)
147 -spec tmp_cd(list(), fun()) -> term().
148 tmp_cd(Dir, Fun) ->
149
150 {ok, OldDir} = file:get_cwd(),
151
152 ok = filelib:ensure_dir(Dir),
153 ok = file:set_cwd(Dir),
154
155 try
156 Result = Fun(),
157 ok = file:set_cwd(OldDir),
158 Result
159 catch
160 Type:Err ->
161 ok = file:set_cwd(OldDir),
162 throw({Type, Err})
163 end.
164
165
166 module_index(Conf, Index) ->
167
168 log("Creating index.html ...~n"),
169
170 Html = "<h1>Module Index</h1><hr /><br /><div>"
171 ++ xml_to_str(emit_index(Index))
172 ++ "</div>",
173
174 Args = [{base, "./"},
1f29c42 @dreverri Removed name from Module Index
dreverri authored
175 {title, "Module Index"},
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
176 {content, Html},
177 {funs, ""}],
178
179 {ok, Data} = erldocs_dtl:render(Args),
180 ok = file:write_file([dest(Conf), "/index.html"], Data).
181
182 emit_index(L) ->
183 lists:flatmap(
184 fun index_html/1,
185 lists:sort(fun sort_index/2, L)).
186
187 index_html(["app", App, _, _Sum]) ->
188 [{a, [{name, App}]}, {h4, [], [App]}];
189 index_html(["mod", App, Mod, Sum]) ->
190 Url = App ++ "/" ++ Mod ++ ".html",
191 [{p,[], [{a, [{href, Url}], [Mod]}, {br,[],[]}, Sum]}];
192 index_html(_) ->
193 [].
194
195 type_ordering("app") -> 1;
196 type_ordering("mod") -> 2;
197 type_ordering("fun") -> 3.
198
199 index_ordering([Type, App, Mod, _Sum]) ->
200 [string:to_lower(App),
201 type_ordering(Type),
202 string:to_lower(Mod)].
203
204 sort_index(A, B) ->
205 index_ordering(A) =< index_ordering(B).
206
20d4194 @daleharvey Escape apostrophes (fixes #13)
authored
207 html_encode(Str) ->
208 re:replace(Str, "'", "", [{return, list}, global]).
209
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
210 javascript_index(Conf, FIndex) ->
211
212 log("Creating erldocs_index.js ...~n"),
213
214 F = fun([Else, App, NMod, Sum]) ->
00ce841 @akaspin UTF-8 compatibility.
akaspin authored
215 [Else, App, NMod, fmt("~ts", [string:substr(Sum, 1, 50)])]
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
216 end,
217
465e59c @daleharvey Remove command line switch for debug, if its needed, should be runtime
authored
218 Index =
00ce841 @akaspin UTF-8 compatibility.
akaspin authored
219 lists:map(
220 fun([A,B,C,[]]) ->
221 fmt("['~s','~s','~s',[]]", [A,B,C]);
222 ([A,B,C,D]) ->
20d4194 @daleharvey Escape apostrophes (fixes #13)
authored
223 fmt("['~s','~s','~s','~s']", [A,B,C,html_encode(D)])
465e59c @daleharvey Remove command line switch for debug, if its needed, should be runtime
authored
224 end,
00ce841 @akaspin UTF-8 compatibility.
akaspin authored
225 lists:sort(fun sort_index/2, lists:map(F, FIndex))),
465e59c @daleharvey Remove command line switch for debug, if its needed, should be runtime
authored
226
20d4194 @daleharvey Escape apostrophes (fixes #13)
authored
227 Js = re:replace(fmt("var index = [~s];", [string:join(Index, ",")]),
228 "\\n|\\r", "", [{return,list}, global]),
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
229
230 ok = file:write_file([dest(Conf), "/erldocs_index.js"], Js).
231
b380108 @daleharvey Lookup type specs in seperate type specs xml files (fixes #12)
authored
232 render(cref, App, Mod, Xml, Types, Conf) ->
233 render(erlref, App, Mod, Xml, Types, Conf);
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
234
b380108 @daleharvey Lookup type specs in seperate type specs xml files (fixes #12)
authored
235 render(erlref, App, Mod, Xml, Types, Conf) ->
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
236
237 File = filename:join([dest(Conf), App, Mod ++ ".html"]),
238 ok = filelib:ensure_dir(filename:dirname(File) ++ "/"),
239
b380108 @daleharvey Lookup type specs in seperate type specs xml files (fixes #12)
authored
240 Acc = [{ids,[]}, {list, ul}, {functions, []}, {types, Types}],
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
241
b380108 @daleharvey Lookup type specs in seperate type specs xml files (fixes #12)
authored
242 {[_Id, _List, {functions, Funs}, {types, _}], NXml}
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
243 = render(fun tr_erlref/2, Xml, Acc),
244
245 XmlFuns = [{li, [], [{a, [{href, "#"++X}], [X]}]}
246 || X <- lists:reverse(Funs) ],
247
248 Args = [{base, "../"},
32a2790 @dreverri Removed sys.conf; erldocs now accepts list of source paths and one de…
dreverri authored
249 {title, Mod ++ " (" ++ App ++ ") - "},
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
250 {content, xml_to_str(NXml)},
251 {funs, xml_to_str({ul, [{id, "funs"}], XmlFuns})}],
252
253 {ok, Data} = erldocs_dtl:render(Args),
254 ok = file:write_file(File, Data).
255
256 render(Fun, List, Acc) when is_list(List) ->
257 case io_lib:char_list(List) of
258 true ->
259 {Acc, List};
260 false ->
261 F = fun(X, {Ac, L}) ->
262 {NAcc, NEl} = render(Fun, X, Ac),
263 {NAcc, [NEl | L]}
264 end,
265
266 {Ac, L} = lists:foldl(F, {Acc, []}, List),
267 {Ac, lists:reverse(L)}
268 end;
269
270 render(Fun, Element, Acc) ->
271
272 % this is nasty
273 F = fun(ignore, NAcc) ->
274 {NAcc, ""};
275 ({NEl, NAttr, NChild}, NAcc) ->
276 {NNAcc, NNChild} = render(Fun, NChild, NAcc),
277 {NNAcc, {NEl, NAttr, NNChild}};
278 (Else, NAcc) ->
279 {NAcc, Else}
280 end,
281
282 case Fun(Element, Acc) of
283 {El, NAcc} -> F(El, NAcc);
284 El -> F(El, Acc)
285 end.
286
287 get_funs(_App, _Mod, false) ->
288 [];
289 get_funs(App, Mod, {funcs, [], Funs}) ->
290 lists:foldl(
291 fun(X, Acc) -> fun_stuff(App, Mod, X) ++ Acc end,
292 [], Funs).
293
294 fun_stuff(App, Mod, {func, [], Child}) ->
295
296 {fsummary, [], Xml} = lists:keyfind(fsummary, 1, Child),
297 Summary = string:substr(xml_to_str(Xml), 1, 50),
298
299 F = fun({name, [], Name}, Acc) ->
300 case make_name(Name) of
301 ignore -> Acc;
302 NName -> [ ["fun", App, Mod++":"++NName, Summary] | Acc ]
303 end;
c6f4e90 Update for new otp doc layout
Dale Harvey authored
304 ({name, [{name, Name}, {arity, Arity}], []}, Acc) ->
305 [ ["fun", App, Mod++":"++Name++"/"++Arity, Summary] | Acc ];
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
306 (_Else, Acc) -> Acc
307 end,
308
11d8397 backporting fixed for new otp format
Dale Harvey authored
309 lists:foldl(F, [], Child);
310 fun_stuff(_App, _Mod, _Funs) ->
311 [].
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
312
313 make_name(Name) ->
314 Tmp = lists:flatten(Name),
315 case string:chr(Tmp, 40) of
316 0 ->
317 ignore;
318 Pos ->
319 {Name2, Rest2} = lists:split(Pos-1, Tmp),
320 Name3 = lists:last(string:tokens(Name2, ":")),
321 Args = string:substr(Rest2, 2, string:chr(Rest2, 41)-2),
322 NArgs = length(string:tokens(Args, ",")),
323 Name3 ++ "/" ++ integer_to_list(NArgs)
324 end.
325
326 app_dirs(Conf) ->
327 Fun = fun(Path, Acc) ->
328 Acc ++ [X || X <- filelib:wildcard(Path), filelib:is_dir(X)]
329 end,
330 lists:foldl(Fun, [], kf(apps, Conf)).
331
332 add_html("#"++Rest) ->
333 "#"++Rest;
334 add_html(Link) ->
335 case string:tokens(Link, "#") of
336 [Tmp] -> Tmp++".html";
337 [N1, N2] -> lists:flatten([N1, ".html#", N2])
338 end.
339
340 %% Transforms erlang xml format to html
c6f4e90 Update for new otp doc layout
Dale Harvey authored
341 tr_erlref({type_desc, [{variable, Name}], [Desc]}, _Acc) ->
342 {'div', [{class, "type_desc"}], [{code, [], [Name, " = ",Desc]}]};
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
343 tr_erlref({header,[],_Child}, _Acc) ->
344 ignore;
345 tr_erlref({marker, [{id, Marker}], []}, _Acc) ->
346 {span, [{id, Marker}], [" "]};
347 tr_erlref({term,[{id, Term}], _Child}, _Acc) ->
348 Term;
349 tr_erlref({lib,[],Lib}, _Acc) ->
350 {h1, [], [lists:flatten(Lib)]};
351 tr_erlref({module,[],Module}, _Acc) ->
352 {h1, [], [lists:flatten(Module)]};
353 tr_erlref({modulesummary, [], Child}, _Acc) ->
354 {h2, [{class, "modsummary"}], Child};
355 tr_erlref({c, [], Child}, _Acc) ->
356 {code, [], Child};
357 tr_erlref({section, [], Child}, _Acc) ->
358 {'div', [{class, "section"}], Child};
359 tr_erlref({title, [], Child}, _Acc) ->
360 {h4, [], [Child]};
361 tr_erlref({type, [], Child}, _Acc) ->
362 {ul, [{class, "type"}], Child};
363 tr_erlref({v, [], []}, _Acc) ->
364 {li, [], [" "]};
365 tr_erlref({v, [], Child}, _Acc) ->
366 {li, [], [{code, [], Child}]};
367 tr_erlref({seealso, [{marker, Marker}], Child}, _Acc) ->
368 N = case string:tokens(Marker, ":") of
369 [] -> add_html(lists:flatten(Child));
370 [Tmp] -> add_html(Tmp);
371 [Ap | Md] -> "../"++Ap++"/" ++ add_html(lists:flatten(Md))
372 end,
373 {a, [{href, N}, {class, "seealso"}], Child};
374 tr_erlref({desc, [], Child}, _Acc) ->
375 {'div', [{class, "description"}], Child};
376 tr_erlref({description, [], Child}, _Acc) ->
377 {'div', [{class, "description"}], Child};
378 tr_erlref({funcs, [], Child}, _Acc) ->
379 {'div', [{class,"functions"}], [{h4, [], ["Functions"]},
380 {hr, [], []} | Child]};
381 tr_erlref({func, [], Child}, _Acc) ->
382 {'div', [{class,"function"}], Child};
383 tr_erlref({tag, [], Child}, _Acc) ->
384 {dt, [], Child};
385 tr_erlref({taglist, [], Child}, [Ids, _List, Funs]) ->
386 { {dl, [], Child}, [Ids, {list, dl}, Funs] };
387 tr_erlref({input, [], Child}, _Acc) ->
388 {code, [], Child};
389 tr_erlref({item, [], Child}, [_Ids, {list, dl}, _Funs]) ->
390 {dd, [], Child};
391 tr_erlref({item, [], Child}, [_Ids, {list, ul}, _Funs]) ->
392 {li, [], Child};
393 tr_erlref({list, _Type, Child}, [Ids, _List, Funs]) ->
394 { {ul, [], Child}, [Ids, {list, ul}, Funs] };
395 tr_erlref({code, [{type, "none"}], Child}, _Acc) ->
396 {pre, [{class, "sh_erlang"}], Child};
397 tr_erlref({pre, [], Child}, _Acc) ->
398 {pre, [{class, "sh_erlang"}], Child};
399 tr_erlref({note, [], Child}, _Acc) ->
400 {'div', [{class, "note"}], [{h2, [], ["Note!"]} | Child]};
401 tr_erlref({warning, [], Child}, _Acc) ->
402 {'div', [{class, "warning"}], [{h2, [], ["Warning!"]} | Child]};
11d8397 backporting fixed for new otp format
Dale Harvey authored
403 tr_erlref({name, [], [{ret,[],[Ret]}, {nametext,[],[Desc]}]}, _Acc) ->
404 {pre, [], [Ret ++ " " ++ Desc]};
b380108 @daleharvey Lookup type specs in seperate type specs xml files (fixes #12)
authored
405 tr_erlref({name, [{name, Name}, {arity, N}], []}, Acc) ->
406 [{ids, Ids}, List, {functions, Funs}, {types, Types}] = Acc,
407 PName = case find_spec(Name, N, Types) of
408 {ok, Tmp} -> Tmp;
409 _ -> Name ++ "/" ++ N
410 end,
c6f4e90 Update for new otp doc layout
Dale Harvey authored
411 NName = inc_name(Name, Ids, 0),
b380108 @daleharvey Lookup type specs in seperate type specs xml files (fixes #12)
authored
412 { {h3, [{id, Name ++ "/" ++ N}], [PName]},
413 [{ids, [NName | Ids]}, List, {functions, [NName|Funs]}, {types, Types}]};
414 tr_erlref({name, [], Child}, [{ids, Ids}, List, {functions, Funs}, {types, Types}]) ->
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
415 case make_name(Child) of
416 ignore -> ignore;
417 Name ->
418 NName = inc_name(Name, Ids, 0),
419 { {h3, [{id, NName}], [Child]},
b380108 @daleharvey Lookup type specs in seperate type specs xml files (fixes #12)
authored
420 [{ids, [NName | Ids]}, List, {functions, [NName|Funs]}, {types, Types}]}
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
421 end;
422 tr_erlref({fsummary, [], _Child}, _Acc) ->
423 ignore;
424 tr_erlref(Else, _Acc) ->
425 Else.
426
b380108 @daleharvey Lookup type specs in seperate type specs xml files (fixes #12)
authored
427 find_spec(_Name, _Arity, []) ->
428 spec_not_found;
429
430 find_spec(Name, Arity, [{spec, [], Specs} | Rest]) ->
431 {name, _, [SpecName]} = lists:keyfind(name, 1, Specs),
432 {arity, _, [ArityName]} = lists:keyfind(arity, 1, Specs),
433 case SpecName =:= Name andalso ArityName =:= Arity of
434 true ->
435 {contract, _, Contracts} = lists:keyfind(contract, 1, Specs),
436 {clause, _, Clause} = lists:keyfind(clause, 1, Contracts),
437 {head, _, Head} = lists:keyfind(head, 1, Clause),
438 case io_lib:deep_char_list(Head) of
439 true -> {ok, lists:flatten(Head)};
440 false -> invalid_name
441 end;
442 false ->
443 find_spec(Name, Arity, Rest)
444 end;
445 find_spec(Name, Arity, [_ | Rest]) ->
446 find_spec(Name, Arity, Rest).
447
448
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
449 nname(Name, 0) -> Name;
450 nname(Name, Acc) -> Name ++ "-" ++ integer_to_list(Acc).
451
452 inc_name(Name, List, Acc) ->
453 case lists:member(nname(Name, Acc), List) of
454 true -> inc_name(Name, List, Acc+1);
455 false -> nname(Name, Acc)
456 end.
457
458 %% Strips xml children that are entirely whitespace (space, tabs, newlines)
459 strip_whitespace(List) when is_list(List) ->
460 [ strip_whitespace(X) || X <- List, is_whitespace(X) ];
461 strip_whitespace({El,Attr,Children}) ->
462 {El, Attr, strip_whitespace(Children)};
463 strip_whitespace(Else) ->
464 Else.
465
466 is_whitespace(X) when is_tuple(X); is_number(X) ->
467 true;
468 is_whitespace(X) ->
00ce841 @akaspin UTF-8 compatibility.
akaspin authored
469 nomatch == re:run(X, "^[ \n\t]*$", [unicode]). %"
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
470
471 %% rather basic xml to string converter, takes xml of the form
472 %% {tag, [{listof, "attributes"}], ["list of children"]}
473 %% into <tag listof="attributes">list of children</tag>
474 xml_to_str(Xml) ->
475 xml_to_html(Xml).
476
477 xml_to_html({Tag, Attr}) ->
478 %% primarily for cases such as <a name="">
00ce841 @akaspin UTF-8 compatibility.
akaspin authored
479 fmt("<~ts ~ts>", [Tag, atos(Attr)]);
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
480 xml_to_html({Tag, Attr, []}) ->
00ce841 @akaspin UTF-8 compatibility.
akaspin authored
481 fmt("<~ts ~ts />", [Tag, atos(Attr)]);
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
482 xml_to_html({Tag, [], []}) ->
00ce841 @akaspin UTF-8 compatibility.
akaspin authored
483 fmt("<~ts />", [Tag]);
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
484 xml_to_html({Tag, [], Child}) ->
00ce841 @akaspin UTF-8 compatibility.
akaspin authored
485 fmt("<~ts>~ts</~ts>", [Tag, xml_to_html(Child), Tag]);
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
486 xml_to_html({Tag, Attr, Child}) ->
00ce841 @akaspin UTF-8 compatibility.
akaspin authored
487 fmt("<~ts ~ts>~ts</~ts>", [Tag, atos(Attr), xml_to_html(Child), Tag]);
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
488 xml_to_html(List) when is_list(List) ->
489 case io_lib:char_list(List) of
490 true -> htmlchars(List);
491 false -> lists:flatten([ xml_to_html(X) || X <- List])
492 end.
493
494 atos([]) -> "";
495 atos(List) when is_list(List) -> string:join([ atos(X) || X <- List ], " ");
496 atos({Name, Val}) -> atom_to_list(Name) ++ "=\""++Val++"\"".
497
498 %% convert ascii into html characters
499 htmlchars(List) ->
500 htmlchars(List, []).
501
502 htmlchars([], Acc) ->
503 lists:flatten(lists:reverse(Acc));
504
505 htmlchars([$< | Rest], Acc) -> htmlchars(Rest, ["&lt;" | Acc]);
506 htmlchars([$> | Rest], Acc) -> htmlchars(Rest, ["&gt;" | Acc]);
507 htmlchars([160 | Rest], Acc) -> htmlchars(Rest, ["&nbsp;" | Acc]);
508 htmlchars([Else | Rest], Acc) -> htmlchars(Rest, [Else | Acc]).
509
510 %% @doc parse xml file against otp's dtd, need to cd into the
511 %% source directory because files are addressed relative to it
512 -spec read_xml(list(), list()) -> tuple().
513 read_xml(_Conf, XmlFile) ->
514
53da05a @daleharvey Lets use utf8 when we actually fix utf8, for now use latin1 since it …
authored
515 Opts = [{fetch_path, [code:lib_dir(docbuilder, dtd)]},
516 {encoding, "latin1"}],
517 %Opts = [{fetch_path, [code:lib_dir(docbuilder, dtd)]}],
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
518
519 {Xml, _} = xmerl_scan:file(XmlFile, Opts),
520 xmerl_lib:simplify_element(Xml).
521
522 %% lazy shorthand
523 fmt(Format, Args) ->
524 lists:flatten(io_lib:format(Format, Args)).
525
526 log(Str) ->
aabb6ca @akaspin Fix better errors
akaspin authored
527 ?LOG(Str).
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
528 log(Str, Args) ->
5c30023 @akaspin better errors
akaspin authored
529 ?LOG(Str, Args).
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
530
531 %% @doc shorthand for lists:keyfind
532 -spec kf(term(), list()) -> term().
533 kf(Key, Conf) ->
534 {Key, Val} = lists:keyfind(Key, 1, Conf),
535 Val.
536
537 %% @doc path to the destination folder
538 -spec dest(list()) -> list().
539 dest(Conf) ->
32a2790 @dreverri Removed sys.conf; erldocs now accepts list of source paths and one de…
dreverri authored
540 [kf(dest, Conf)].
e086370 @dreverri Switched to ErlyDTL templates
dreverri authored
541
542 bname(Name) ->
543 filename:basename(Name).
544 bname(Name, Ext) ->
545 filename:basename(Name, Ext).
546
547 % List of the type of xml files erldocs can build
548 buildable() ->
549 [ erlref, cref ].
550
551 ignore() ->
552 [{"kernel", "init"},
553 {"kernel", "zlib"},
554 {"kernel", "erlang"},
555 {"kernel", "erl_prim_loader"}].
556
557 -type map_fun(D, R) :: fun((D) -> R).
558 -type reduce_fun(T) :: fun((T, _) -> _).
559
560 -spec pmapreduce(map_fun(T, R), reduce_fun(R), R, [T]) -> [R].
561 pmapreduce(Map, Reduce, Acc0, L) ->
562 pmapreduce(Map, Reduce, Acc0, L, 4).
563
564 -spec pmapreduce(map_fun(T, R), reduce_fun(R), R, [T], pos_integer()) -> [R].
565 pmapreduce(Map, Reduce, Acc0, L, N) ->
566 Keys = [rpc:async_call(node(), ?MODULE, mapreduce,
567 [Map, Reduce, Acc0, Segment])
568 || Segment <- segment(L, N)],
569 mapreduce(fun rpc:yield/1, Reduce, Acc0, Keys).
570
571 -spec mapreduce(map_fun(T, R), reduce_fun(R), R, [T]) -> [R].
572 mapreduce(Map, Reduce, Acc0, L) ->
573 F = fun (Elem, Acc) ->
574 Reduce(Map(Elem), Acc)
575 end,
576 lists:foldl(F, Acc0, lists:reverse(L)).
577
578 -spec segment([T], pos_integer()) -> [[T]].
579 segment(List, Segments) ->
580 segment(List, length(List) div Segments, Segments).
581
582 -spec segment([T], non_neg_integer(), pos_integer()) -> [[T]].
583 segment(List, _N, 1) ->
584 [List];
585 segment(List, N, Segments) ->
586 {Front, Back} = lists:split(N, List),
587 [Front | segment(Back, N, Segments - 1)].
Something went wrong with that request. Please try again.