diff --git a/lib/slime/parser.ex b/lib/slime/parser.ex index d1d71a6..f1ef64e 100644 --- a/lib/slime/parser.ex +++ b/lib/slime/parser.ex @@ -57,14 +57,14 @@ defmodule Slime.Parser do end end - @inline_tag_regex ~r/\A(?(?:[\.#]?[\w-]+)++):\W*(?.*)/ + @inline_tag_regex ~r/\A(?(?:[\.#]?[\w-]+)++):[^\w\.#]*(?.*)/ def parse_line(line) do case strip_line(line) do {_indentation, ""} -> if Application.get_env(:slime, :keep_lines), do: {:prev, ""}, else: nil {indentation, line} -> - [tag, inline_tag] = + [tag, rest] = case Regex.run(@inline_tag_regex, line, capture: :all_but_first) do nil -> [line, nil] match -> match @@ -72,10 +72,10 @@ defmodule Slime.Parser do parse_tag = fn (tag) -> tag |> String.first |> parse_line(tag) end tag = parse_tag.(tag) - tag = if inline_tag do - inline_tag = parse_tag.(inline_tag) + tag = if rest do + {0, rest} = parse_line(rest) {tag_name, attrs} = tag - {tag_name, [{:children, [inline_tag]} | attrs]} + {tag_name, Keyword.put(attrs, :children, [rest])} else tag end diff --git a/lib/slime/tree.ex b/lib/slime/tree.ex index 356d20b..9cabce5 100644 --- a/lib/slime/tree.ex +++ b/lib/slime/tree.ex @@ -26,10 +26,7 @@ defmodule Slime.Tree do |> Enum.map(&to_branch/1) filter = make_child_filter(indentation) {children, rem} = Enum.split_while(t, filter) - split_children? = existing == [] && children != [] && - Application.get_env(:slime, :keep_lines) - sep = if split_children?, do: [%TextNode{content: ""}], else: [] - children_tree = existing ++ sep ++ build_tree(children) + children_tree = children |> build_tree |> append_to(existing) attrs = Keyword.put(attrs, :children, children_tree) branch = to_branch({tag, attrs}) tree = build_tree(rem) @@ -45,6 +42,20 @@ defmodule Slime.Tree do end end + defp append_to([], existing), do: existing + defp append_to(children, []), do: sep() ++ children + defp append_to(children, existing = [%TextNode{}]), do: existing ++ children + defp append_to(children, [html_node = %HTMLNode{children: node_children}]) do + case node_children do + [%TextNode{}] -> [html_node] ++ children + _ -> [%{html_node|children: append_to(children, node_children)}] + end + end + + defp sep() do + Application.get_env(:slime, :keep_lines) && [%TextNode{content: ""}] || [] + end + defp to_branch(%{} = branch), do: branch defp to_branch(text) when is_binary(text) do %TextNode{content: text} diff --git a/test/parser_test.exs b/test/parser_test.exs index 1f37646..6d5eada 100644 --- a/test/parser_test.exs +++ b/test/parser_test.exs @@ -18,6 +18,30 @@ defmodule ParserTest do ] end + test "parses inline nesting" do + parsed = [".row: .col-lg-12: p Hello World"] |> Parser.parse_lines + assert parsed == [ + {0, {"div", + children: [ + {"div", + children: [ + {"p", + attributes: [], + children: ["Hello World"], + spaces: %{}, + close: false} + ], + attributes: [class: "col-lg-12"], + spaces: %{}, + close: false} + ], + attributes: [class: "row"], + spaces: %{}, + close: false} + } + ] + end + test "parses css classes with dashes" do {_, {"div", opts}} = ".my-css-class test" |> Parser.parse_line diff --git a/test/renderer_test.exs b/test/renderer_test.exs index da4a169..cecd7ee 100644 --- a/test/renderer_test.exs +++ b/test/renderer_test.exs @@ -98,6 +98,45 @@ defmodule RendererTest do """ |> String.replace("\n", "") end + test "render nested inline html" do + slime = ~s""" + .row: .col-lg-12: p Hello World + """ + + assert render(slime) == """ +
+
+

Hello World

+
+
+ """ |> String.replace("\n", "") + end + + test "render mixed nesting" do + slime = ~s""" + .wrap: .row: .col-lg-12 + .box: p One + .box: p Two + p Three + """ + + assert render(slime) == """ +
+
+
+
+

One

+
+
+

Two

+
+
+
+
+

Three

+ """ |> String.replace("\n", "") + end + test "render closed tag (ending with /)" do assert render(~s(img src="image.png"/)) == ~s() end diff --git a/test/tree_test.exs b/test/tree_test.exs index f53d3d4..f8c2301 100644 --- a/test/tree_test.exs +++ b/test/tree_test.exs @@ -31,4 +31,56 @@ defmodule TreeTest do assert parsed == expected end + + test "creates tree with mixed nesting" do + expected = [ + %HTMLNode{ + tag: :div, + attributes: [class: ~w(wrap)], + children: [ + %HTMLNode{ + tag: :div, + attributes: [class: ~w(row)], + children: [ + %HTMLNode{ + tag: :div, + attributes: [class: ~w(col-lg-12)], + children: [ + %HTMLNode{ + tag: :div, + attributes: [class: ~w(box)], + children: [ + %HTMLNode{ + tag: :p, + attributes: [], + children: [%TextNode{content: "One"}]}]}, + %HTMLNode{ + tag: :p, + attributes: [], + children: [%TextNode{content: "Two"}]}]}]}]}, + %HTMLNode{ + tag: :p, + attributes: [], + children: [%TextNode{content: "Three"}]}] + + parsed = [ + {0, {:div, + attributes: [class: ~w(wrap)], + children: [ + {:div, + attributes: [class: ~w(row)], + children: [ + {:div, + attributes: [class: ~w(col-lg-12)], + children: []}]}]}}, + {2, {:div, + attributes: [class: ~w(box)], + children: [ + {:p, attributes: [], children: ~w(One)}]}}, + {2, {:p, attributes: [], children: ~w(Two)}}, + {0, {:p, attributes: [], children: ~w(Three)}} + ] |> Tree.build_tree + + assert parsed == expected + end end