%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1997-2009. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% %CopyrightEnd% %% %%%------------------------------------------------------------------ %%% Purpose:Convert Erlang files to html. (Pretty faaast... :-) %%%------------------------------------------------------------------ %-------------------------------------------------------------------- % Some stats (Sparc5@110Mhz): % 4109 lines (erl_parse.erl): 3.00 secs % 1847 lines (application_controller.erl): 0.57 secs % 3160 lines (test_server.erl): 1.00 secs % 1199 lines (ts_estone.erl): 0.35 secs % % Avg: ~4.5e-4s/line, or ~0.45s/1000 lines, or ~2200 lines/sec. %-------------------------------------------------------------------- -module(erl2html2). -export([convert/2]). convert([], _Dest) -> % Fake clause. ok; convert(File, Dest) -> case file:read_file(File) of {ok, Bin} -> Code=binary_to_list(Bin), statistics(runtime), %% The generated code uses the BGCOLOR attribute in the %% BODY tag, which wasn't valid until HTML 3.2. Also, %% good HTML should either override all colour attributes %% or none of them -- *never* just a few. %% %% FIXME: The colours should *really* be set with %% stylesheets... Html0 = ["\n" "\n" "\n" "", File, "\n\n" "\n"], {Html1, Lines} = root(Code, [], 1), Html = [Html0, "
\n", Html1, "
\n", footer(Lines),"\n\n"], file:write_file(Dest, Html); {error, Reason} -> {error, Reason} end. root([], Res, Line) -> {Res, Line}; root([Char0|Code], Res, Line0) -> Char = [Char0], case Char of "-" -> {Match, Line1, NewCode0, AttName} = read_to_char(Line0+1, Code, [], [$(, $.]), {_, Line2, NewCode, Stuff} = read_to_char(Line1, NewCode0, [], $\n), NewRes = [Res,linenum(Line0),"-",AttName, "",Match, Stuff, "\n"], root(NewCode, NewRes, Line2); "%" -> {_, Line, NewCode, Stuff} = read_to_char(Line0+1, Code, [], $\n), NewRes = [Res,linenum(Line0),"%",Stuff,"\n"], root(NewCode, NewRes, Line); "\n" -> root(Code, [Res,linenum(Line0), "\n"], Line0+1); " " -> {_, Line, NewCode, Stuff} = read_to_char(Line0+1, Code, [], $\n), root(NewCode, [Res,linenum(Line0)," ",Stuff, "\n"], Line); "\t" -> {_, Line, NewCode, Stuff} = read_to_char(Line0+1, Code, [], $\n), root(NewCode, [Res,linenum(Line0),"\t",Stuff, "\n"], Line); [Chr|_] when Chr>96, Chr<123 -> %% Assumed to be function/clause start. %% FIXME: This will trivially generate non-unique anchors %% (one for each clause) --- which is illegal HTML. {_, Line1, NewCode0, FName0} = read_to_char(Line0+1, Code, [], $(), {_, Line2, NewCode, Stuff} = read_to_char(Line1,NewCode0, [], $\n), FuncName = [[Chr],FName0], NewRes=[Res,"", linenum(Line0),"",FuncName,"", "(",Stuff, "\n"], root(NewCode, NewRes, Line2); Chr -> {_, Line, NewCode, Stuff} = read_to_char(Line0+1, Code, [], $\n), root(NewCode, [Res,linenum(Line0),Chr,Stuff, "\n"], Line) end. read_to_char(Line0, [], Res, _Chr) -> {nomatch, Line0, [], Res}; read_to_char(Line0, [Char|Code], Res, Chr) -> case Char of Chr -> {Char, Line0, Code, Res}; _ when is_list(Chr) -> case lists:member(Char,Chr) of true -> {Char, Line0, Code, Res}; false -> {Line,NewCode,NewRes} = maybe_convert(Line0,Code,Res,Char), read_to_char(Line, NewCode, NewRes, Chr) end; _ -> {Line,NewCode,NewRes} = maybe_convert(Line0,Code,Res,Char), read_to_char(Line,NewCode, NewRes, Chr) end. maybe_convert(Line0,Code,Res,Chr) -> case Chr of %% Quoted stuff should not have the highlighting like normal code %% FIXME: unbalanced quotes (e.g. in comments) will cause trouble with %% highlighting and line numbering in the rest of the module. $" -> {_, Line1, NewCode, Stuff0} = read_to_char(Line0, Code, [], $"), {Line2,Stuff} = add_linenumbers(Line1,lists:flatten(Stuff0),[]), {Line2,NewCode,[Res,$",Stuff,$"]}; %% These chars have meaning in HTML, and *must* *not* be %% written as themselves. $& -> {Line0, Code, [Res,"&"]}; $< -> {Line0, Code, [Res,"<"]}; $> -> {Line0, Code, [Res,">"]}; %% Everything else is simply copied. OtherChr -> {Line0, Code, [Res,OtherChr]} end. add_linenumbers(Line,[Chr|Chrs],Res) -> case Chr of $\n -> add_linenumbers(Line+1,Chrs,[Res,$\n,linenum(Line)]); _ -> add_linenumbers(Line,Chrs,[Res,Chr]) end; add_linenumbers(Line,[],Res) -> {Line,Res}. %% Make nicely indented line numbers. linenum(Line) -> Num = integer_to_list(Line), A = case Line rem 10 of 0 -> ""; _ -> [] end, Pred = case length(Num) of Length when Length < 5 -> lists:duplicate(5-Length,$\s); _ -> [] end, [A,Pred,integer_to_list(Line),":"]. footer(Lines) -> {_, Time} = statistics(runtime), % io:format("Converted ~p lines in ~.2f Seconds.~n", % [Lines, Time/1000]), S = "The transformation of this file (~p lines) took ~.2f seconds", F = lists:flatten(io_lib:format(S, [Lines, Time/1000])), ["
",F,"
\n"].