Просмотр исходного кода

Bring compile_dir up-to-date.

Fixes #121.
Close #122.
Andreas Stenius 11 лет назад
Родитель
Сommit
26cb8fc36e
4 измененных файлов с 98 добавлено и 65 удалено
  1. 1 1
      README.markdown
  2. 13 4
      src/erlydtl.erl
  3. 83 59
      src/erlydtl_compiler.erl
  4. 1 1
      tests/src/erlydtl_unittests.erl

+ 1 - 1
README.markdown

@@ -173,7 +173,7 @@ created from a directory of templates thusly:
 
 The resulting module will export a function for each template
 appearing in the specified directory. Options is the same as for
-compile/3.
+`compile/3`.
 
 Compiling a helper module can be more efficient than using
 `custom_tags_dir` because the helper functions will be compiled only

+ 13 - 4
src/erlydtl.erl

@@ -39,18 +39,27 @@
 -export([compile/2, compile/3]).
 -export([compile_dir/2, compile_dir/3]).
 
-%% @spec compile( FileOrBinary, Module::atom() ) -> ok | {error, Reason}
+-type error_info() :: {File::list(),
+                       [{Line::integer() | none,
+                         Module::atom(),
+                         ErrorDesc::term()}]}.
+-type errors() :: list(error_info()).
+-type warnings() :: list(error_info()).
+-type ok_ret() :: {ok, Module::atom()} | {ok, Module::atom(), warnings()}.
+-type err_ret() :: error | {error, errors(), warnings()}.
+
+-spec compile( list() | binary(), atom() ) -> {ok, Module::atom()} | error.
 compile(FileOrBinary, Module) ->
     erlydtl_compiler:compile(FileOrBinary, Module).
 
-%% @spec compile( FileOrBinary, Module::atom(), Options ) -> ok | {error, Reason}
+-spec compile( list() | binary(), atom(), list() ) -> ok_ret() | err_ret().
 compile(FileOrBinary, Module, Options) ->
     erlydtl_compiler:compile(FileOrBinary, Module, Options).
 
-%% @spec compile_dir( DirectoryPath::string(), Module::atom() ) -> ok | {error, Reason}
+-spec compile_dir(list() | binary(), atom()) -> {ok, Module::atom()} | error.
 compile_dir(DirectoryPath, Module) ->
     erlydtl_compiler:compile_dir(DirectoryPath, Module).
 
-%% @spec compile_dir( DirectoryPath::string(), Module::atom(), Options ) -> ok | {error, Reason}
+-spec compile_dir(list() | binary(), atom(), list()) -> ok_ret() | err_ret().
 compile_dir(DirectoryPath, Module, Options) ->
     erlydtl_compiler:compile_dir(DirectoryPath, Module, Options).

+ 83 - 59
src/erlydtl_compiler.erl

@@ -60,7 +60,7 @@
 -include("erlydtl_ext.hrl").
 
 compile(FileOrBinary, Module) ->
-    compile(FileOrBinary, Module, [verbose, report_errors]).
+    compile(FileOrBinary, Module, [verbose, report]).
 
 compile(Binary, Module, Options) when is_binary(Binary) ->
     Context = process_opts(undefined, Module, Options),
@@ -72,39 +72,42 @@ compile(File, Module, Options) ->
     compile(Context).
 
 compile_dir(Dir, Module) ->
-    compile_dir(Dir, Module, [verbose, report_errors]).
+    compile_dir(Dir, Module, [verbose, report]).
 
 compile_dir(Dir, Module, Options) ->
-    Context = process_opts(Dir, Module, Options),
+    Context0 = process_opts({dir, Dir}, Module, Options),
     %% Find all files in Dir (recursively), matching the regex (no
     %% files ending in "~").
     Files = filelib:fold_files(Dir, ".+[^~]$", true, fun(F1,Acc1) -> [F1 | Acc1] end, []),
-    {ParserResults, ParserErrors}
+    {ParserResults,
+     #dtl_context{ errors=#error_info{ list=ParserErrors } }=Context1}
         = lists:foldl(
-            fun (File, {ResultAcc, ErrorAcc}) ->
+            fun (File, {ResultAcc, Ctx}) ->
                     case filename:basename(File) of
                         "."++_ ->
-                            {ResultAcc, ErrorAcc};
+                            {ResultAcc, Ctx};
                         _ ->
                             FilePath = filename:absname(File),
                             case filelib:is_dir(FilePath) of
                                 true ->
-                                    {ResultAcc, ErrorAcc};
+                                    {ResultAcc, Ctx};
                                 false ->
-                                    case parse(FilePath, Context) of
-                                        up_to_date -> {ResultAcc, ErrorAcc};
+                                    case parse(FilePath, Ctx) of
+                                        up_to_date -> {ResultAcc, Ctx};
                                         {ok, DjangoParseTree, CheckSum} ->
-                                            {[{File, DjangoParseTree, CheckSum}|ResultAcc], ErrorAcc};
-                                        Err -> {ResultAcc, [Err|ErrorAcc]}
+                                            {[{File, DjangoParseTree, CheckSum}|ResultAcc], Ctx};
+                                        {error, Reason} -> {ResultAcc, add_error(Reason, Ctx)}
                                     end
                             end
                     end
-            end, {[], []}, Files),
-    case ParserErrors of
-        [] ->
-            compile_multiple_to_binary(Dir, ParserResults, Context);
-        [Error|_] -> Error %% just the first error?
-    end.
+            end,
+            {[], Context0},
+            Files),
+    Context2 = if length(ParserErrors) == 0 ->
+                       compile_multiple_to_binary(Dir, ParserResults, Context1);
+                  true -> Context1
+               end,
+    collect_result(Context2).
 
 parse(Data) ->
     parse(Data, #dtl_context{}).
@@ -147,14 +150,14 @@ process_opts(File, Module, Options0) ->
                      filename:join(
                        [proplists:get_value(out_dir, Options1, ""),
                         Module]);
-                 {dir, Dir} -> Dir;
+                 {dir, Dir} -> filename:absname(Dir);
                  _ -> File
              end,
-    Options = [{compiler_options, [{source, lists:concat([Source, ".erl"])}]}
+    Options = [{compiler_options, [{source, Source}]}
                |compiler_opts(Options1, [])],
     case File of
-        {dir, Dir1} ->
-            init_context([], Dir1, Module, Options);
+        {dir, _} ->
+            init_context([], Source, Module, Options);
         _ ->
             init_context([Source], filename:dirname(Source), Module, Options)
     end.
@@ -258,40 +261,53 @@ do_compile(#dtl_context{ bin=Binary }=Context) ->
         {error, Reason} -> add_error(Reason, Context)
     end.
 
-compile_multiple_to_binary(Dir, ParserResults, Context) ->
-    MatchAst = options_match_ast(Context),
-    {Functions, {AstInfo, _}}
+compile_multiple_to_binary(Dir, ParserResults, Context0) ->
+    MatchAst = options_match_ast(Context0),
+    {Functions,
+     {AstInfo, _,
+      #dtl_context{ errors=#error_info{ list=Errors } }=Context1}}
         = lists:mapfoldl(
-            fun({File, DjangoParseTree, CheckSum}, {AstInfo, TreeWalker}) ->
-                    FilePath = full_path(File, Context#dtl_context.doc_root),
-                    {{BodyAst, BodyInfo}, TreeWalker1} = with_dependency(
-                                                           {FilePath, CheckSum},
-                                                           body_ast(DjangoParseTree, Context, TreeWalker)),
-                    FunctionName = filename:rootname(filename:basename(File)),
-                    Function1 = erl_syntax:function(
-                                  erl_syntax:atom(FunctionName),
-                                  [erl_syntax:clause(
-                                     [erl_syntax:variable("_Variables")],
-                                     none,
-                                     [erl_syntax:application(
-                                        none, erl_syntax:atom(FunctionName),
-                                        [erl_syntax:variable("_Variables"), erl_syntax:list([])])
-                                     ])
-                                  ]),
-                    Function2 = erl_syntax:function(
-                                  erl_syntax:atom(FunctionName),
-                                  [erl_syntax:clause(
-                                     [erl_syntax:variable("_Variables"),
-                                      erl_syntax:variable("RenderOptions")],
-                                     none,
-                                     MatchAst ++ [BodyAst])
-                                  ]),
-                    {{FunctionName, Function1, Function2}, {merge_info(AstInfo, BodyInfo), TreeWalker1}}
+            fun({File, DjangoParseTree, CheckSum}, {AstInfo, TreeWalker, Ctx}) ->
+                    try
+                        FilePath = full_path(File, Ctx#dtl_context.doc_root),
+                        {{BodyAst, BodyInfo}, TreeWalker1} = with_dependency(
+                                                               {FilePath, CheckSum},
+                                                               body_ast(DjangoParseTree, Ctx, TreeWalker)),
+                        FunctionName = filename:rootname(filename:basename(File)),
+                        Function1 = erl_syntax:function(
+                                      erl_syntax:atom(FunctionName),
+                                      [erl_syntax:clause(
+                                         [erl_syntax:variable("_Variables")],
+                                         none,
+                                         [erl_syntax:application(
+                                            none, erl_syntax:atom(FunctionName),
+                                            [erl_syntax:variable("_Variables"), erl_syntax:list([])])
+                                         ])
+                                      ]),
+                        Function2 = erl_syntax:function(
+                                      erl_syntax:atom(FunctionName),
+                                      [erl_syntax:clause(
+                                         [erl_syntax:variable("_Variables"),
+                                          erl_syntax:variable("RenderOptions")],
+                                         none,
+                                         MatchAst ++ [BodyAst])
+                                      ]),
+                        {{FunctionName, Function1, Function2}, {merge_info(AstInfo, BodyInfo), TreeWalker1, Ctx}}
+                    catch
+                        throw:Error ->
+                            {error, {AstInfo, TreeWalker, add_error(Error, Ctx)}}
+                    end
             end,
-            {#ast_info{}, init_treewalker(Context)},
+            {#ast_info{},
+             init_treewalker(Context0),
+             Context0},
             ParserResults),
-    Forms = custom_forms(Dir, Context#dtl_context.module, Functions, AstInfo),
-    compile_forms(Forms, Context).
+    if length(Errors) == 0 ->
+            Forms = custom_forms(Dir, Context1#dtl_context.module, Functions, AstInfo),
+            compile_forms(Forms, Context1);
+       true ->
+            Context1
+    end.
 
 compile_to_binary(DjangoParseTree, CheckSum, Context) ->
     try body_ast(DjangoParseTree, Context, init_treewalker(Context)) of
@@ -374,7 +390,7 @@ maybe_debug_template(Forms, Context) ->
             print("Compiler options: ~p~n", [Options], Context),
             try
                 Source = erl_prettypr:format(erl_syntax:form_list(Forms)),
-                File = proplists:get_value(source, Options),
+                File = lists:concat([proplists:get_value(source, Options), ".erl"]),
                 io:format("Saving template source to: ~s.. ~p~n",
                           [File, file:write_file(File, Source)])
             catch
@@ -1872,11 +1888,16 @@ call_ast(Module, Variable, AstInfo, TreeWalker) ->
 print(Fmt, Args, #dtl_context{ verbose = true }) -> io:format(Fmt, Args);
 print(_Fmt, _Args, _Context) -> ok.
 
+get_current_file(#dtl_context{ parse_trail=[File|_] }) -> File;
+get_current_file(#dtl_context{ doc_root=Root }) -> Root.
 
 add_error(Error, #dtl_context{
-                    errors=#error_info{ report=Report, list=Es }=Ei,
-                    parse_trail=[File|_] }=Context) ->
-    Item = get_error_item(Report, "", File, Error),
+                    errors=#error_info{ report=Report, list=Es }=Ei
+                   }=Context) ->
+    Item = get_error_item(
+             Report, "",
+             get_current_file(Context),
+             Error),
     Context#dtl_context{
       errors=Ei#error_info{ list=[Item|Es] }
      }.
@@ -1889,9 +1910,12 @@ add_errors(Errors, Context) ->
 add_warning(Warning, #dtl_context{ warnings=warnings_as_errors }=Context) ->
     add_error(Warning, Context);
 add_warning(Warning, #dtl_context{
-                        warnings=#error_info{ report=Report, list=Ws }=Wi,
-                        parse_trail=[File|_] }=Context) ->
-    Item = get_error_item(Report, "Warning: ", File, Warning),
+                        warnings=#error_info{ report=Report, list=Ws }=Wi
+                       }=Context) ->
+    Item = get_error_item(
+             Report, "Warning: ",
+             get_current_file(Context),
+             Warning),
     Context#dtl_context{
       warnings=Wi#error_info{ list=[Item|Ws] }
      }.

+ 1 - 1
tests/src/erlydtl_unittests.erl

@@ -281,7 +281,7 @@ tests() ->
                <<"{% for outer in list %}{% for inner in outer %}({{ forloop.parentloop.counter0 }}, {{ forloop.counter0 }})\n{% endfor %}{% endfor %}">>,
                [{'list', [["One", "two"], ["One", "two"]]}], [], [], <<"(0, 0)\n(0, 1)\n(1, 0)\n(1, 1)\n">>,
                %% the warnings we get from the erlang compiler still needs some care..
-               [error_info([no_out_dir]), {"/erlydtl_running_test.erl", [{0, erl_lint, {unused_var, 'Var_inner/1_1:31'}}]}]},
+               [error_info("/erlydtl_running_test", [{0, erl_lint, {unused_var, 'Var_inner/1_1:31'}}, no_out_dir])]},
               {"If changed",
                <<"{% for x in list %}{% ifchanged %}{{ x }}\n{% endifchanged %}{% endfor %}">>,
                [{'list', ["one", "two", "two", "three", "three", "three"]}], <<"one\ntwo\nthree\n">>},