aboutsummaryrefslogtreecommitdiffstats
path: root/src/asciideck_attributes_pass.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/asciideck_attributes_pass.erl')
-rw-r--r--src/asciideck_attributes_pass.erl112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/asciideck_attributes_pass.erl b/src/asciideck_attributes_pass.erl
new file mode 100644
index 0000000..393b57d
--- /dev/null
+++ b/src/asciideck_attributes_pass.erl
@@ -0,0 +1,112 @@
+%% Copyright (c) 2017-2018, Loïc Hoguin <[email protected]>
+%%
+%% Permission to use, copy, modify, and/or distribute this software for any
+%% purpose with or without fee is hereby granted, provided that the above
+%% copyright notice and this permission notice appear in all copies.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+%% The purpose of this pass is to apply attributes to
+%% their corresponding blocks. For macros the attributes
+%% are already applied. For inline elements the inline
+%% pass is taking care of it.
+-module(asciideck_attributes_pass).
+
+-export([run/1]).
+
+run([]) ->
+ [];
+%% A block identifier is an alternative way of specifying
+%% the id attribute for a block.
+run([{block_id, #{id := ID}, <<>>, _}|Tail0]) ->
+ Tail = apply_attributes(Tail0, #{<<"id">> => ID}),
+ run(Tail);
+%% A block title is ultimately treated as an attribute
+%% for the following block.
+run([{block_title, _, Title, _}|Tail0]) ->
+ Tail = apply_attributes(Tail0, #{<<"title">> => Title}),
+ run(Tail);
+run([{attribute_list, Attrs, <<>>, _}|Tail0]) ->
+ Tail = apply_attributes(Tail0, Attrs),
+ run(Tail);
+run([Block|Tail]) ->
+ [Block|run(Tail)].
+
+%% Find the next block to apply the attributes.
+apply_attributes([], _) ->
+ [];
+apply_attributes(AST=[Element0={Type, Attrs0, Content, Ann}|Tail], Attrs) ->
+ case can_apply(Type) of
+ drop ->
+ AST;
+ skip ->
+ [Element0|apply_attributes(Tail, Attrs)];
+ apply ->
+ Element = {Type, maps:merge(Attrs0, Attrs), Content, Ann},
+ [Element|Tail]
+ end.
+
+%% Block macros already come with a mandatory attribute list.
+%% Just to play it safe we drop the attributes for now.
+can_apply(block_macro) -> drop;
+%% If we hit a list item continuation, drop the attributes for now.
+can_apply(list_item_continuation) -> drop;
+%% We skip attribute lists and alike and let it sort itself out.
+can_apply(block_id) -> skip;
+can_apply(attribute_list) -> skip;
+can_apply(block_title) -> skip;
+%% Everything else is a block.
+can_apply(_) -> apply.
+
+-ifdef(TEST).
+attribute_list_test() ->
+ AST0 = [
+ {attribute_list, #{
+ 0 => <<"width=400">>,
+ <<"width">> => <<"400">>
+ }, <<>>, #{line => 1}},
+ {listing_block, #{}, <<"Hello!">>, #{line => 2}}
+ ],
+ AST = [
+ {listing_block, #{
+ 0 => <<"width=400">>,
+ <<"width">> => <<"400">>
+ }, <<"Hello!">>, #{line => 2}}
+ ],
+ AST = run(AST0),
+ ok.
+
+block_id_test() ->
+ AST0 = [
+ {block_id, #{
+ id => <<"cowboy_req">>
+ }, <<>>, #{line => 1}},
+ {listing_block, #{}, <<"Hello!">>, #{line => 2}}
+ ],
+ AST = [
+ {listing_block, #{
+ <<"id">> => <<"cowboy_req">>
+ }, <<"Hello!">>, #{line => 2}}
+ ],
+ AST = run(AST0),
+ ok.
+
+block_title_test() ->
+ AST0 = [
+ {block_title, #{}, <<"Title">>, #{line => 1}},
+ {listing_block, #{}, <<"Hello!">>, #{line => 2}}
+ ],
+ AST = [
+ {listing_block, #{
+ <<"title">> => <<"Title">>
+ }, <<"Hello!">>, #{line => 2}}
+ ],
+ AST = run(AST0),
+ ok.
+-endif.