Browse Source

New option for 0-based list access (fixes #156)

Andreas Stenius 11 years ago
parent
commit
9e8134213b

+ 1 - 0
NEWS.md

@@ -6,3 +6,4 @@ Standards](http://www.gnu.org/prep/standards/html_node/NEWS-File.html#NEWS-File)
 
 ## master (upcoming release)
 
+* New option for 0-based list access (#156) (see README for details).

+ 9 - 0
README.markdown

@@ -178,6 +178,11 @@ Options is a proplist possibly containing:
   custom tags and filters. `Module` should implement the
   `erlydtl_library` behaviour (see [Custom tags and filters] below).
 
+* `lists_0_based` - **Compatibility warning** Defaults to `false`,
+  giving 1-based list access, as is common practice in Erlang. Set it
+  to `true` to get 1-based access as in Django, or to `defer` to not
+  decide until render time, using the render option `lists_0_based`.
+
 * `locale` **deprecated** - The same as {blocktrans_locales, [Val]}.
 
 * `no_env` - Do not read additional options from the OS environment
@@ -284,6 +289,10 @@ Same as `render/1`, but with the following options:
   for blocktrans variable interpolation should be wrapped to `{{` and
   `}}`.
 
+* `lists_0_based` - If the compile option `lists_0_based` was set to
+  `defer`, pass this option (or set it to true, `{lists_0_based,
+  true}`) to get 0-based list indexing when rendering the template.
+
 * `locale` - A string specifying the current locale, for use with the
   `blocktrans_fun` compile-time option.
 

+ 2 - 1
include/erlydtl_ext.hrl

@@ -32,7 +32,8 @@
           all_options = [],
           errors = #error_info{},
           warnings = #error_info{},
-          bin = undefined
+          bin = undefined,
+          lists_0_based = false
          }).
 
 -record(ast_info, {

+ 7 - 3
src/erlydtl_beam_compiler.erl

@@ -1118,13 +1118,17 @@ resolve_variable_ast(VarTuple, FinderFunction, TreeWalker) ->
 
 resolve_variable_ast1({attribute, {{_, Pos, Attr}, Variable}}, {Runtime, Finder}=FinderFunction, TreeWalker) ->
     {{VarAst, VarInfo}, TreeWalker1} = resolve_variable_ast(Variable, FinderFunction, TreeWalker),
+    #treewalker{ context=#dtl_context{ lists_0_based = Lists0Based } } = TreeWalker,
     FileName = get_current_file(TreeWalker1),
     {{?Q(["'@Runtime@':'@Finder@'(",
           "  _@Attr@, _@VarAst,",
-          "  [{filename, _@FileName@},",
-          "   {pos, _@Pos@},",
+          "  [",
+          "   {lists_0_based, _@Lists0Based@},",
+          "   {render_options, RenderOptions},",
           "   {record_info, _RecordInfo},",
-          "   {render_options, RenderOptions}])"]),
+          "   {filename, _@FileName@},",
+          "   {pos, _@Pos@}",
+          "  ])"]),
       VarInfo},
      TreeWalker1};
 

+ 2 - 1
src/erlydtl_compiler.erl

@@ -253,7 +253,8 @@ init_context(ParseTrail, DefDir, Module, Options) ->
            record_info = [{R, lists:zip(I, lists:seq(2, length(I) + 1))}
                           || {R, I} <- proplists:get_value(record_info, Options, Ctx#dtl_context.record_info)],
            errors = init_error_info(errors, Ctx#dtl_context.errors, Options),
-           warnings = init_error_info(warnings, Ctx#dtl_context.warnings, Options)
+           warnings = init_error_info(warnings, Ctx#dtl_context.warnings, Options),
+           lists_0_based = proplists:get_value(lists_0_based, Options, Ctx#dtl_context.lists_0_based)
           },
     Context = load_libraries(proplists:get_value(default_libraries, Options, []), Context0),
     case call_extension(Context, init_context, [Context]) of

+ 15 - 0
src/erlydtl_runtime.erl

@@ -17,9 +17,24 @@ find_value(Key, Data, Options) when is_atom(Key), is_tuple(Data) ->
             end;
         _ -> find_value(Key, Data)
     end;
+find_value(Key, Data, Options) when is_integer(Key), is_list(Data) ->
+    find_value(adjust_index(Key, 1, lists_0_based, Options), Data);
 find_value(Key, Data, _Options) ->
     find_value(Key, Data).
 
+adjust_index(Key, Off, Opt, Options) when is_list(Options) ->
+    case proplists:get_value(Opt, Options) of
+        defer ->
+            adjust_index(
+              Key, Off, Opt,
+              proplists:get_value(render_options, Options));
+        true ->
+            Key + Off;
+        _ ->
+            Key
+    end;
+adjust_index(Key, _Off, _Opt, _Options) -> Key.
+
 find_value(_, undefined) ->
     undefined;
 find_value(Key, Fun) when is_function(Fun, 1) ->

+ 13 - 1
test/erlydtl_test_defs.erl

@@ -108,9 +108,21 @@ all_test_defs() ->
        {"Index tuple variable",
         <<"{{ var1.2 }}">>, [{var1, {a, b, c}}],
         <<"b">>},
-       {"Index all elements of list",
+       {"Index all elements of list (default, 1-based)",
         <<"{{ var1.1 }},{{ var1.2 }},{{ var1.3 }}.">>,
         [{var1, [a, b, c]}],
+        <<"a,b,c.">>},
+       {"Index all elements 0-based (selected at compile time)",
+        <<"{{ var1.0 }},{{ var1.1 }},{{ var1.2 }}.">>,
+        [{var1, [a, b, c]}], [], [lists_0_based],
+        <<"a,b,c.">>},
+       {"Index all elements 0-based (selected at render time)",
+        <<"{{ var1.0 }},{{ var1.1 }},{{ var1.2 }}.">>,
+        [{var1, [a, b, c]}], [lists_0_based], [{lists_0_based, defer}],
+        <<"a,b,c.">>},
+       {"Index all elements 1-based (selected at render time)",
+        <<"{{ var1.1 }},{{ var1.2 }},{{ var1.3 }}.">>,
+        [{var1, [a, b, c]}], [], [{lists_0_based, defer}],
         <<"a,b,c.">>}
       ]},
      {"now",