Permalink
Browse files

Support `{{ block.super }}` in block tags (fixes #18)

  • Loading branch information...
1 parent c1eebb2 commit d572bfc9bbb47b446b5bafa5a55c025c6931452a @kaos kaos committed Feb 27, 2014
@@ -59,10 +59,10 @@
-import(erlydtl_compiler_utils,
[unescape_string_literal/1, full_path/2, push_scope/2,
- restore_scope/2, begin_scope/1, begin_scope/3, end_scope/4,
- print/3, get_current_file/1, add_errors/2, add_warnings/2,
- merge_info/2, call_extension/3, init_treewalker/1,
- resolve_variable/2, resolve_variable/3,
+ restore_scope/2, begin_scope/1, begin_scope/2, end_scope/4,
+ empty_scope/0, print/3, get_current_file/1, add_errors/2,
+ add_warnings/2, merge_info/2, call_extension/3,
+ init_treewalker/1, resolve_variable/2, resolve_variable/3,
reset_parse_trail/2]).
-include_lib("merl/include/merl.hrl").
@@ -502,9 +502,10 @@ body_ast([{'extends', {string_literal, _Pos, String}} | ThisParseTree], #treewal
BlockDict = lists:foldl(
fun ({block, {identifier, _, Name}, Contents}, Dict) ->
dict:store(Name, Contents, Dict);
- (_, Dict) ->
- Dict
- end, dict:new(), ThisParseTree),
+ (_, Dict) -> Dict
+ end,
+ dict:new(),
+ ThisParseTree),
{Info, TreeWalker1} = with_dependency(
{File, CheckSum},
body_ast(
@@ -525,17 +526,28 @@ body_ast([{'extends', {string_literal, _Pos, String}} | ThisParseTree], #treewal
body_ast(DjangoParseTree, TreeWalker) ->
- {ScopeId, TreeWalkerScope} = begin_scope(TreeWalker),
+ body_ast(DjangoParseTree, empty_scope(), TreeWalker).
+
+body_ast(DjangoParseTree, BodyScope, TreeWalker) ->
+ {ScopeId, TreeWalkerScope} = begin_scope(BodyScope, TreeWalker),
{AstInfoList, TreeWalker1} =
lists:mapfoldl(
fun ({'autoescape', {identifier, _, OnOrOff}, Contents}, #treewalker{ context=Context }=TW) ->
body_ast(Contents, TW#treewalker{ context=Context#dtl_context{auto_escape = OnOrOff} });
- ({'block', {identifier, _, Name}, Contents}, #treewalker{ context=Context }=TW) ->
- Block = case dict:find(Name, Context#dtl_context.block_dict) of
- {ok, ChildBlock} -> ChildBlock;
- _ -> Contents
- end,
- body_ast(Block, TW);
+ ({'block', {identifier, Pos, Name}, Contents}, #treewalker{ context=Context }=TW) ->
+ {Block, BlockScope} =
+ case dict:find(Name, Context#dtl_context.block_dict) of
+ {ok, ChildBlock} ->
+ {{ContentsAst, _ContentsInfo}, _ContentsTW} = body_ast(Contents, TW),
+ {ChildBlock,
+ create_scope(
+ [{block, ?Q("[{super, _@ContentsAst}]")}],
+ Pos, TW)
+ };
+ _ ->
+ {Contents, empty_scope()}
+ end,
+ body_ast(Block, BlockScope, TW);
({'blocktrans', Args, Contents}, TW) ->
blocktrans_ast(Args, Contents, TW);
({'call', {identifier, _, Name}}, TW) ->
@@ -1144,8 +1156,8 @@ scope_as(VarName, Contents, TreeWalker) ->
{{ContentsAst, ContentsInfo}, TreeWalker1} = body_ast(Contents, TreeWalker),
VarAst = merl:var(lists:concat(["Var_", VarName])),
{Id, TreeWalker2} = begin_scope(
- [{VarName, VarAst}],
- [?Q("_@VarAst = _@ContentsAst")],
+ {[{VarName, VarAst}],
+ [?Q("_@VarAst = _@ContentsAst")]},
TreeWalker1),
{{Id, ContentsInfo}, TreeWalker2}.
@@ -1154,10 +1166,10 @@ regroup_ast(ListVariable, GrouperVariable, LocalVarName, TreeWalker) ->
LocalVarAst = merl:var(lists:concat(["Var_", LocalVarName])),
{Id, TreeWalker2} = begin_scope(
- [{LocalVarName, LocalVarAst}],
- [?Q("_@LocalVarAst = erlydtl_runtime:regroup(_@ListAst, _@regroup)",
- [{regroup, regroup_filter(GrouperVariable, [])}])
- ],
+ {[{LocalVarName, LocalVarAst}],
+ [?Q("_@LocalVarAst = erlydtl_runtime:regroup(_@ListAst, _@regroup)",
+ [{regroup, regroup_filter(GrouperVariable, [])}])
+ ]},
TreeWalker1),
{{Id, ListInfo}, TreeWalker2}.
@@ -1371,3 +1383,20 @@ call_ast(Module, Variable, AstInfo, TreeWalker) ->
" {error, Reason} -> io_lib:format(\"error: ~p\", [Reason])",
"end"]),
with_dependencies(Module:dependencies(), {{Ast, AstInfo}, TreeWalker}).
+
+create_scope(Vars, VarScope) ->
+ {Scope, Values} =
+ lists:foldl(
+ fun ({Name, Value}, {VarAcc, ValueAcc}) ->
+ NameAst = merl:var(lists:concat(["_Var_", Name, VarScope])),
+ {[{Name, NameAst}|VarAcc],
+ [?Q("_@NameAst = _@Value")|ValueAcc]
+ }
+ end,
+ empty_scope(),
+ Vars),
+ {Scope, [Values]}.
+
+create_scope(Vars, {Row, Col}, #treewalker{ context=Context }) ->
+ Level = length(Context#dtl_context.local_scopes),
+ create_scope(Vars, lists:concat(["/", Level, "_", Row, ":", Col])).
@@ -62,8 +62,9 @@
push_scope/2,
restore_scope/2,
begin_scope/1,
- begin_scope/3,
- end_scope/4
+ begin_scope/2,
+ end_scope/4,
+ empty_scope/0
]).
-include("erlydtl_ext.hrl").
@@ -208,15 +209,17 @@ restore_scope(#treewalker{ context=Target }, Context) ->
restore_scope(#dtl_context{ local_scopes=Scopes }, Context) ->
Context#dtl_context{ local_scopes=Scopes }.
-begin_scope(TreeWalker) -> begin_scope([], [], TreeWalker).
+begin_scope(TreeWalker) -> begin_scope(empty_scope(), TreeWalker).
-begin_scope(Scope, Values, TreeWalker) ->
+begin_scope({Scope, Values}, TreeWalker) ->
Id = make_ref(),
{Id, push_scope({Id, Scope, Values}, TreeWalker)}.
end_scope(Fun, Id, AstList, TreeWalker) ->
close_scope(Fun, Id, AstList, TreeWalker).
+empty_scope() -> {[], []}.
+
reset_parse_trail(ParseTrail, #treewalker{ context=Context }=TreeWalker) ->
TreeWalker#treewalker{ context=reset_parse_trail(ParseTrail, Context) };
reset_parse_trail(ParseTrail, Context) ->
@@ -323,9 +326,14 @@ close_scope(Fun, Id, AstList, TreeWalker) ->
merge_scopes(Id, #treewalker{ context=Context }=TreeWalker) ->
{Values, Scopes} = merge_scopes(Id, Context#dtl_context.local_scopes, []),
- {Values, TreeWalker#treewalker{ context=Context#dtl_context{ local_scopes = Scopes } }}.
+ {lists:reverse(Values),
+ TreeWalker#treewalker{
+ context=Context#dtl_context{
+ local_scopes = Scopes
+ } }}.
merge_scopes(Id, [{Id, _Scope, []}|Scopes], Acc) -> {Acc, Scopes};
+merge_scopes(Id, [{Id, _Scope, Values}|Scopes], Acc) -> {[{Id, Values}|Acc], Scopes};
merge_scopes(Id, [{_ScopeId, _Scope, []}|Scopes], Acc) ->
merge_scopes(Id, Scopes, Acc);
merge_scopes(Id, [{ScopeId, _Scope, Values}|Scopes], Acc) ->
@@ -339,6 +347,8 @@ split_ast(Id, AstList) ->
split_ast(_Split, [], {Pre, Acc}) ->
{Pre, lists:reverse(Acc), []};
+split_ast(_Split, [], Acc) ->
+ {[], lists:reverse(Acc), []};
split_ast(Split, [Split|Rest], {Pre, Acc}) ->
{Pre, lists:reverse(Acc), Rest};
split_ast(Split, [Split|Rest], Acc) ->
View
@@ -0,0 +1,11 @@
+base-barstring
+
+base template
+
+extending title: "base title"
+
+more of base template
+
+replacing the base content - variable: test-barstring after variable. Was: base content
+
+end of base template
View
@@ -0,0 +1,3 @@
+{% extends "base" %}
+{% block title %}extending title: "{{ block.super }}"{% endblock %}
+{% block content %}replacing the base content - variable: {{ test_var }} after variable. Was: {{ block.super }}{% endblock %}
@@ -48,7 +48,8 @@ test_list() ->
"custom_tag2", "custom_tag3", "custom_tag4", "custom_call",
"include_template", "include_path", "ssi", "extends_path",
"extends_path2", "trans", "extends2", "extends3",
- "recursive_block", "extend_recursive_block", "missing"
+ "recursive_block", "extend_recursive_block", "missing",
+ "block_super"
].
setup_compile("for_list_preset") ->
@@ -102,6 +103,11 @@ setup("autoescape") ->
setup("extends") ->
RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
{ok, RenderVars};
+setup("include_template") -> setup("extends");
+setup("include_path") -> setup("extends");
+setup("extends_path") -> setup("extends");
+setup("extends_path2") -> setup("extends");
+setup("block_super") -> setup("extends");
setup("filters") ->
RenderVars = [
{date_var1, {1975,7,24}},
@@ -157,18 +163,6 @@ setup("cycle") ->
RenderVars = [{test, [integer_to_list(X) || X <- lists:seq(1, 20)]},
{a, "Apple"}, {b, "Banana"}, {c, "Cherry"}],
{ok, RenderVars};
-setup("include_template") ->
- RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
- {ok, RenderVars};
-setup("include_path") ->
- RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
- {ok, RenderVars};
-setup("extends_path") ->
- RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
- {ok, RenderVars};
-setup("extends_path2") ->
- RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
- {ok, RenderVars};
setup("trans") ->
RenderVars = [{locale, "reverse"}],
{ok, RenderVars};
@@ -185,8 +179,6 @@ setup("custom_tag4") ->
setup("ssi") ->
RenderVars = [{path, filename:absname(filename:join(["tests", "input", "ssi_include.html"]))}],
{ok, RenderVars};
-
-
%%--------------------------------------------------------------------
%% Custom tags
%%--------------------------------------------------------------------
@@ -311,7 +303,11 @@ test_render(Name, Module) ->
fun(F) -> file:write_file(F, Data) end),
{error, io_lib:format(
"Expected output does not match rendered output~n"
- "==Expected==~n~p~n--Actual--~n~p~n==End==~n",
+ " ==Expected==~n"
+ "~s~n"
+ " --Actual--~n"
+ "~s~n"
+ " ==End==~n",
[RenderResult, Data])}
end;
true ->

0 comments on commit d572bfc

Please sign in to comment.