diff --git a/.formatter.exs b/.formatter.exs
index 71b9052..ac394c7 100644
--- a/.formatter.exs
+++ b/.formatter.exs
@@ -16,7 +16,6 @@ locals_without_parens = ~w[
details summary menuitem menu
meta link base
area br col embed hr img input keygen param source track wbr
- txt partial
animate animateMotion animateTransform circle clipPath
color-profile defs desc discard ellipse feBlend
@@ -26,13 +25,7 @@ locals_without_parens = ~w[
marker mask mesh meshgradient meshpatch meshrow metadata mpath path pattern polygon
polyline radialGradient rect set solidcolor stop svg switch symbol text
textPath tspan unknown use view
-
- form_for inputs_for
- checkbox color_input checkbox color_input date_input date_select datetime_local_input
- datetime_select email_input file_input hidden_input number_input password_input range_input
- search_input telephone_input textarea text_input time_input time_select url_input
- reset submit phx_label radio_button multiple_select select phx_link phx_button
-]a |> Enum.map(fn e -> {e, :*} end)
+]a |> Enum.flat_map(fn e -> [{e, :*}, {:"#{e}!", :*}] end)
[
inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"],
diff --git a/lib/temple.ex b/lib/temple.ex
index 9d6a253..d6b3399 100644
--- a/lib/temple.ex
+++ b/lib/temple.ex
@@ -29,7 +29,7 @@ defmodule Temple do
# The class attribute also can take a keyword list of classes to conditionally render, based on the boolean result of the value.
- div class: ["text-red-500": false, "text-green-500": true ] do
+ div class: ["text-red-500": false, "text-green-500": true] do
"Alert!"
end
@@ -71,6 +71,34 @@ defmodule Temple do
end
```
+ ## Whitespace Control
+
+ By default, Temple will emit internal whitespace into tags, something like this.
+
+ ```elixir
+ span do
+ "Hello, world!"
+ end
+ ```
+
+ ```html
+
+ Hello, world!
+
+ ```
+
+ If you need to create a "tight" tag, you can call the "bang" version of the desired tag.
+
+ ```elixir
+ span! do
+ "Hello, world!"
+ end
+ ```
+
+ ```html
+ Hello, world!
+ ```
+
## Configuration
### Mode
@@ -151,7 +179,8 @@ defmodule Temple do
markup =
block
|> Parser.parse()
- |> Enum.map(&Temple.Generator.to_eex/1)
+ |> Enum.map(fn parsed -> Temple.Generator.to_eex(parsed, 0) end)
+ |> Enum.intersperse("\n")
|> :erlang.iolist_to_binary()
quote location: :keep do
@@ -163,7 +192,8 @@ defmodule Temple do
quote location: :keep do
unquote(block)
|> Parser.parse()
- |> Enum.map(&Temple.Generator.to_eex/1)
+ |> Enum.map(fn parsed -> Temple.Generator.to_eex(parsed, 0) end)
+ |> Enum.intersperse("\n")
|> :erlang.iolist_to_binary()
end
end
@@ -190,7 +220,8 @@ defmodule Temple do
markup =
block
|> Parser.parse()
- |> Enum.map(&Temple.Generator.to_eex/1)
+ |> Enum.map(fn parsed -> Temple.Generator.to_eex(parsed, 0) end)
+ |> Enum.intersperse("\n")
|> :erlang.iolist_to_binary()
EEx.compile_string(markup, engine: engine, line: __CALLER__.line, file: __CALLER__.file)
diff --git a/lib/temple/generator.ex b/lib/temple/generator.ex
index c346035..c0f7d46 100644
--- a/lib/temple/generator.ex
+++ b/lib/temple/generator.ex
@@ -1,5 +1,5 @@
defprotocol Temple.Generator do
@moduledoc false
- def to_eex(ast)
+ def to_eex(ast, indent \\ 0)
end
diff --git a/lib/temple/parser.ex b/lib/temple/parser.ex
index eb82503..1ddbf05 100644
--- a/lib/temple/parser.ex
+++ b/lib/temple/parser.ex
@@ -58,7 +58,7 @@ defmodule Temple.Parser do
option textarea output progress meter
details summary menuitem menu
html
- ]a
+ ]a |> Enum.flat_map(fn el -> [el, :"#{el}!"] end)
@nonvoid_elements_aliases Enum.map(@nonvoid_elements, fn el ->
Keyword.get(@aliases, el, el)
diff --git a/lib/temple/parser/anonymous_functions.ex b/lib/temple/parser/anonymous_functions.ex
index 8b2065b..dd11ff7 100644
--- a/lib/temple/parser/anonymous_functions.ex
+++ b/lib/temple/parser/anonymous_functions.ex
@@ -32,14 +32,14 @@ defmodule Temple.Parser.AnonymousFunctions do
end
defimpl Temple.Generator do
- def to_eex(%{elixir_ast: {name, _, args}, children: children}) do
+ def to_eex(%{elixir_ast: {name, _, args}, children: children}, indent \\ 0) do
{_do_and_else, args} = Temple.Parser.Utils.split_args(args)
{args, {func, _, [{arrow, _, [[{arg, _, _}], _block]}]}, args2} =
Temple.Parser.Utils.split_on_fn(args, {[], nil, []})
[
- "<%= ",
+ "#{Parser.Utils.indent(indent)}<%= ",
to_string(name),
" ",
Enum.map(args, &Macro.to_string(&1)) |> Enum.join(", "),
@@ -51,16 +51,16 @@ defmodule Temple.Parser.AnonymousFunctions do
to_string(arrow),
" %>",
"\n",
- for(child <- children, do: Temple.Generator.to_eex(child)),
+ for(child <- children, do: Temple.Generator.to_eex(child, indent + 1)),
if Enum.any?(args2) do
[
- "<% end, ",
+ "#{Parser.Utils.indent(indent)}<% end, ",
Enum.map(args2, fn arg -> Macro.to_string(arg) end)
|> Enum.join(", "),
" %>"
]
else
- ["<% end %>", "\n"]
+ ["#{Parser.Utils.indent(indent)}<% end %>", "\n"]
end
]
end
diff --git a/lib/temple/parser/components.ex b/lib/temple/parser/components.ex
index d5f1906..b9913bb 100644
--- a/lib/temple/parser/components.ex
+++ b/lib/temple/parser/components.ex
@@ -2,6 +2,8 @@ defmodule Temple.Parser.Components do
@moduledoc false
@behaviour Temple.Parser
+ alias Temple.Parser
+
defstruct module: nil, assigns: [], children: [], slots: []
@impl Temple.Parser
@@ -88,12 +90,12 @@ defmodule Temple.Parser.Components do
end
defimpl Temple.Generator do
- def to_eex(%{module: module, assigns: assigns, children: children, slots: slots}) do
+ def to_eex(%{module: module, assigns: assigns, children: children, slots: slots}, indent \\ 0) do
component_function = Temple.Config.mode().component_function
renderer = Temple.Config.mode().renderer.(module)
[
- "<%= #{component_function} ",
+ "#{Parser.Utils.indent(indent)}<%= #{component_function} ",
renderer,
", ",
Macro.to_string(assigns),
@@ -102,23 +104,22 @@ defmodule Temple.Parser.Components do
" do %>\n",
if not Enum.empty?(children) do
[
- "<% {:default, _} -> %>\n",
- for(child <- children, do: Temple.Generator.to_eex(child)),
- "\n"
+ "#{Parser.Utils.indent(indent + 1)}<% {:default, _} -> %>\n",
+ for(child <- children, do: Temple.Generator.to_eex(child, indent + 2))
]
else
""
end,
for slot <- slots do
[
- "<% {:",
+ "#{Parser.Utils.indent(indent + 1)}<% {:",
to_string(slot.name),
", ",
"#{Macro.to_string(slot.assigns)}} -> %>\n",
- for(child <- slot.content, do: Temple.Generator.to_eex(child))
+ for(child <- slot.content, do: Temple.Generator.to_eex(child, indent + 2))
]
end,
- "<% end %>"
+ "\n#{Parser.Utils.indent(indent)}<% end %>"
]
else
" %>"
diff --git a/lib/temple/parser/default.ex b/lib/temple/parser/default.ex
index b9b4a2d..dc6e30a 100644
--- a/lib/temple/parser/default.ex
+++ b/lib/temple/parser/default.ex
@@ -15,8 +15,8 @@ defmodule Temple.Parser.Default do
end
defimpl Temple.Generator do
- def to_eex(%{elixir_ast: expression}) do
- ["<%= ", Macro.to_string(expression), " %>\n"]
+ def to_eex(%{elixir_ast: expression}, indent \\ 0) do
+ ["#{Parser.Utils.indent(indent)}<%= ", Macro.to_string(expression), " %>"]
end
end
end
diff --git a/lib/temple/parser/do_expressions.ex b/lib/temple/parser/do_expressions.ex
index 1c31e0c..915f79d 100644
--- a/lib/temple/parser/do_expressions.ex
+++ b/lib/temple/parser/do_expressions.ex
@@ -30,18 +30,23 @@ defmodule Temple.Parser.DoExpressions do
end
defimpl Temple.Generator do
- def to_eex(%{elixir_ast: expression, children: [do_body, else_body]}) do
+ def to_eex(%{elixir_ast: expression, children: [do_body, else_body]}, indent \\ 0) do
[
- "<%= ",
+ "#{Parser.Utils.indent(indent)}<%= ",
Macro.to_string(expression),
" do %>",
"\n",
- for(child <- do_body, do: Temple.Generator.to_eex(child)),
+ for(child <- do_body, do: Temple.Generator.to_eex(child, indent + 1))
+ |> Enum.intersperse("\n"),
if(else_body != nil,
- do: ["<% else %>\n", for(child <- else_body, do: Temple.Generator.to_eex(child))],
+ do: [
+ "#{Parser.Utils.indent(indent)}\n<% else %>\n",
+ for(child <- else_body, do: Temple.Generator.to_eex(child, indent + 1))
+ |> Enum.intersperse("\n")
+ ],
else: ""
),
- "<% end %>"
+ "\n#{Parser.Utils.indent(indent)}<% end %>"
]
end
end
diff --git a/lib/temple/parser/element_list.ex b/lib/temple/parser/element_list.ex
new file mode 100644
index 0000000..1b1260b
--- /dev/null
+++ b/lib/temple/parser/element_list.ex
@@ -0,0 +1,33 @@
+defmodule Temple.Parser.ElementList do
+ @moduledoc false
+
+ @behaviour Temple.Parser
+
+ defstruct children: [], whitespace: :loose
+
+ @impl Temple.Parser
+ def applicable?(asts), do: is_list(asts)
+
+ @impl Temple.Parser
+ def run(asts) do
+ children = Enum.flat_map(asts, &Temple.Parser.parse/1)
+
+ Temple.Ast.new(__MODULE__, children: children)
+ end
+
+ defimpl Temple.Generator do
+ def to_eex(%{children: children, whitespace: whitespace}, indent \\ 0) do
+ child_indent = if whitespace == :loose, do: indent + 1, else: 0
+ self_indent = if whitespace == :loose, do: indent, else: 0
+ whitespace = if whitespace == :tight, do: [], else: ["\n"]
+
+ [
+ whitespace,
+ for(child <- children, do: Temple.Generator.to_eex(child, child_indent))
+ |> Enum.intersperse("\n"),
+ whitespace,
+ Temple.Parser.Utils.indent(self_indent)
+ ]
+ end
+ end
+end
diff --git a/lib/temple/parser/empty.ex b/lib/temple/parser/empty.ex
index 7872fdb..9a76156 100644
--- a/lib/temple/parser/empty.ex
+++ b/lib/temple/parser/empty.ex
@@ -16,7 +16,7 @@ defmodule Temple.Parser.Empty do
end
defimpl Temple.Generator do
- def to_eex(_) do
+ def to_eex(_, _ \\ 0) do
[]
end
end
diff --git a/lib/temple/parser/match.ex b/lib/temple/parser/match.ex
index 53b5973..15a65f4 100644
--- a/lib/temple/parser/match.ex
+++ b/lib/temple/parser/match.ex
@@ -19,8 +19,8 @@ defmodule Temple.Parser.Match do
end
defimpl Temple.Generator do
- def to_eex(%{elixir_ast: elixir_ast}) do
- ["<% ", Macro.to_string(elixir_ast), " %>"]
+ def to_eex(%{elixir_ast: elixir_ast}, indent \\ 0) do
+ ["#{Parser.Utils.indent(indent)}<% ", Macro.to_string(elixir_ast), " %>"]
end
end
end
diff --git a/lib/temple/parser/nonvoid_elements_aliases.ex b/lib/temple/parser/nonvoid_elements_aliases.ex
index 89cb09d..28bca16 100644
--- a/lib/temple/parser/nonvoid_elements_aliases.ex
+++ b/lib/temple/parser/nonvoid_elements_aliases.ex
@@ -2,7 +2,7 @@ defmodule Temple.Parser.NonvoidElementsAliases do
@moduledoc false
@behaviour Temple.Parser
- defstruct name: nil, attrs: [], children: []
+ defstruct name: nil, attrs: [], children: [], meta: %{}
alias Temple.Parser
@@ -23,18 +23,34 @@ defmodule Temple.Parser.NonvoidElementsAliases do
children = Temple.Parser.parse(do_and_else[:do])
- Temple.Ast.new(__MODULE__, name: to_string(name), attrs: args, children: children)
+ Temple.Ast.new(__MODULE__,
+ name: to_string(name) |> String.replace_suffix("!", ""),
+ attrs: args,
+ children:
+ Temple.Ast.new(Temple.Parser.ElementList,
+ children: children,
+ whitespace: whitespace(to_string(name))
+ )
+ )
+ end
+
+ defp whitespace(name) do
+ if String.ends_with?(name, "!") do
+ :tight
+ else
+ :loose
+ end
end
defimpl Temple.Generator do
- def to_eex(%{name: name, attrs: attrs, children: children}) do
+ def to_eex(%{name: name, attrs: attrs, children: children}, indent \\ 0) do
[
- "<",
+ "#{Parser.Utils.indent(indent)}<",
name,
Temple.Parser.Utils.compile_attrs(attrs),
- ">\n",
- for(child <- children, do: Temple.Generator.to_eex(child)),
- "\n",
+ ">",
+ Temple.Generator.to_eex(children, indent),
+ "",
name,
">"
]
diff --git a/lib/temple/parser/right_arrow.ex b/lib/temple/parser/right_arrow.ex
index ef01f72..3be2015 100644
--- a/lib/temple/parser/right_arrow.ex
+++ b/lib/temple/parser/right_arrow.ex
@@ -18,12 +18,12 @@ defmodule Temple.Parser.RightArrow do
end
defimpl Temple.Generator do
- def to_eex(%{elixir_ast: elixir_ast, children: children}) do
+ def to_eex(%{elixir_ast: elixir_ast, children: children}, indent \\ 0) do
[
- "<% ",
+ "#{Parser.Utils.indent(indent)}<% ",
Macro.to_string(elixir_ast),
" -> %>\n",
- for(child <- children, do: Temple.Generator.to_eex(child))
+ for(child <- children, do: Temple.Generator.to_eex(child, indent + 1))
]
end
end
diff --git a/lib/temple/parser/slot.ex b/lib/temple/parser/slot.ex
index 57484ad..287992d 100644
--- a/lib/temple/parser/slot.ex
+++ b/lib/temple/parser/slot.ex
@@ -1,6 +1,7 @@
defmodule Temple.Parser.Slot do
@moduledoc false
@behaviour Temple.Parser
+ alias Temple.Parser.Utils
defstruct name: nil, args: []
@@ -26,15 +27,15 @@ defmodule Temple.Parser.Slot do
end
defimpl Temple.Generator do
- def to_eex(%{name: name, args: args}) do
+ def to_eex(%{name: name, args: args}, indent \\ 0) do
render_block_function = Temple.Config.mode().render_block_function
[
- "<%= #{render_block_function}(@inner_block, {:",
+ "#{Utils.indent(indent)}<%= #{render_block_function}(@inner_block, {:",
to_string(name),
", ",
Macro.to_string(quote(do: Enum.into(unquote(args), %{}))),
- "}) %>"
+ "}) %>\n"
]
end
end
diff --git a/lib/temple/parser/text.ex b/lib/temple/parser/text.ex
index 4bab474..3642d5c 100644
--- a/lib/temple/parser/text.ex
+++ b/lib/temple/parser/text.ex
@@ -16,8 +16,8 @@ defmodule Temple.Parser.Text do
end
defimpl Temple.Generator do
- def to_eex(%{text: text}) do
- [text, "\n"]
+ def to_eex(%{text: text}, indent \\ 0) do
+ [Parser.Utils.indent(indent), text]
end
end
end
diff --git a/lib/temple/parser/utils.ex b/lib/temple/parser/utils.ex
index 9d8837e..bbe05e3 100644
--- a/lib/temple/parser/utils.ex
+++ b/lib/temple/parser/utils.ex
@@ -113,4 +113,8 @@ defmodule Temple.Parser.Utils do
def pop_compact?(args) do
Keyword.pop(args, :compact, false)
end
+
+ def indent(level) do
+ String.duplicate(" ", level * 2)
+ end
end
diff --git a/lib/temple/parser/void_elements_aliases.ex b/lib/temple/parser/void_elements_aliases.ex
index 7abd6b9..71ae88d 100644
--- a/lib/temple/parser/void_elements_aliases.ex
+++ b/lib/temple/parser/void_elements_aliases.ex
@@ -2,6 +2,8 @@ defmodule Temple.Parser.VoidElementsAliases do
@moduledoc false
@behaviour Temple.Parser
+ alias Temple.Parser.Utils
+
defstruct name: nil, attrs: []
@impl Temple.Parser
@@ -28,12 +30,12 @@ defmodule Temple.Parser.VoidElementsAliases do
end
defimpl Temple.Generator do
- def to_eex(%{name: name, attrs: attrs}) do
+ def to_eex(%{name: name, attrs: attrs}, indent \\ 0) do
[
- "<",
+ "#{Utils.indent(indent)}<",
to_string(name),
Temple.Parser.Utils.compile_attrs(attrs),
- ">\n"
+ ">"
]
end
end
diff --git a/test/component_test.exs b/test/component_test.exs
index bf0926d..9750f31 100644
--- a/test/component_test.exs
+++ b/test/component_test.exs
@@ -18,7 +18,19 @@ defmodule Temple.ComponentTest do
end
assert evaluate_template(result) ==
- ~s{
Hello, world
}
+ ~s"""
+
+ Hello, world
+
+
+
+
+
+
+
+ """
end
test "function components can accept local assigns" do
@@ -34,7 +46,16 @@ defmodule Temple.ComponentTest do
end
assert evaluate_template(result) ==
- ~s{
Hello, world
I'm a component!
}
+ ~s"""
+
+ Hello, world
+
+
+
+ I'm a component!
+
+
+ """
end
test "function components can use other components" do
@@ -50,8 +71,21 @@ defmodule Temple.ComponentTest do
end
assert evaluate_template(result) == ~s"""
-
outer!
-
inner!
+
+
+
+ outer!
+
+
+
+
+
+
+
+ inner!
+
+
+
"""
end
@@ -63,7 +97,14 @@ defmodule Temple.ComponentTest do
end
end
- assert evaluate_template(result) == ~s{
doo doo
}
+ assert evaluate_template(result) == ~s"""
+
+
+ doo doo
+
+
+
+ """
end
test "components can be void elements" do
@@ -72,7 +113,11 @@ defmodule Temple.ComponentTest do
c Temple.Components.VoidComponent, foo: :bar
end
- assert evaluate_template(result) == ~s{
bar
}
+ assert evaluate_template(result) == ~s"""
+
+ bar
+
+ """
end
test "components can have named slots" do
@@ -94,6 +139,22 @@ defmodule Temple.ComponentTest do
end
assert evaluate_template(result, assigns) ==
- ~s{
the value is Header
}
+ ~s"""
+
+
+
+ the value is Header
+
+
+
+
+
+
+
+
+
+ """
end
end
diff --git a/test/parser/anonymous_functions_test.exs b/test/parser/anonymous_functions_test.exs
index d5dfa06..6564304 100644
--- a/test/parser/anonymous_functions_test.exs
+++ b/test/parser/anonymous_functions_test.exs
@@ -7,9 +7,9 @@ defmodule Temple.Parser.AnonymousFunctionsTest do
test "returns true when the node contains an anonymous function as an argument to a function" do
raw_asts = [
quote do
- form_for changeset, Routes.foo_path(conn, :create), fn form ->
+ form_for(changeset, Routes.foo_path(conn, :create), fn form ->
Does.something!(form)
- end
+ end)
end
]
@@ -47,9 +47,9 @@ defmodule Temple.Parser.AnonymousFunctionsTest do
raw_ast =
quote do
- form_for changeset, Routes.foo_path(conn, :create), fn form ->
+ form_for(changeset, Routes.foo_path(conn, :create), fn form ->
unquote(expected_child)
- end
+ end)
end
ast = AnonymousFunctions.run(raw_ast)
@@ -69,9 +69,9 @@ defmodule Temple.Parser.AnonymousFunctionsTest do
test "emits eex" do
raw_ast =
quote do
- form_for changeset, Routes.foo_path(conn, :create), fn form ->
+ form_for(changeset, Routes.foo_path(conn, :create), fn form ->
Does.something!(form)
- end
+ end)
end
result =
diff --git a/test/parser/components_test.exs b/test/parser/components_test.exs
index c7c66fc..224f87e 100644
--- a/test/parser/components_test.exs
+++ b/test/parser/components_test.exs
@@ -187,7 +187,12 @@ defmodule Temple.Parser.ComponentsTest do
|> Temple.Generator.to_eex()
assert result |> :erlang.iolist_to_binary() ==
- ~s|<%= Temple.Component.__component__ SomeModule, [foo: :bar] do %>\n<% {:default, _} -> %>\nI'm a component!\n<% end %>|
+ ~s"""
+ <%= Temple.Component.__component__ SomeModule, [foo: :bar] do %>
+ <% {:default, _} -> %>
+ I'm a component!
+ <% end %>
+ """
end
test "emits eex for void component with slots" do
@@ -208,7 +213,14 @@ defmodule Temple.Parser.ComponentsTest do
|> Temple.Generator.to_eex()
assert result |> :erlang.iolist_to_binary() ==
- ~s|<%= Temple.Component.__component__ SomeModule, [foo: :bar] do %>\n<% {:foo, %{form: form}} -> %>\n
+ <% end %>
+ """
end
test "emits eex for nonvoid component with slots" do
@@ -233,7 +245,18 @@ defmodule Temple.Parser.ComponentsTest do
|> Temple.Generator.to_eex()
assert result |> :erlang.iolist_to_binary() ==
- ~s|<%= Temple.Component.__component__ SomeModule, [foo: :bar] do %>\n<% {:default, _} -> %>\n
\ninner content\n\n
\n<% {:foo, %{form: form}} -> %>
\nin the slot\n\n
<% end %>|
+ ~s"""
+ <%= Temple.Component.__component__ SomeModule, [foo: :bar] do %>
+ <% {:default, _} -> %>
+
+ inner content
+
+ <% {:foo, %{form: form}} -> %>
+
+ in the slot
+
+ <% end %>
+ """
end
test "emits eex for void component" do
diff --git a/test/parser/default_test.exs b/test/parser/default_test.exs
index 80359a5..7c2ffd8 100644
--- a/test/parser/default_test.exs
+++ b/test/parser/default_test.exs
@@ -2,6 +2,7 @@ defmodule Temple.Parser.DefaultTest do
use ExUnit.Case, async: true
alias Temple.Parser.Default
+ alias Temple.Support.Utils
describe "applicable?/1" do
test "returns true when the node is an elixir expression" do
@@ -35,8 +36,11 @@ defmodule Temple.Parser.DefaultTest do
end
|> Default.run()
|> Temple.Generator.to_eex()
+ |> Utils.iolist_to_binary()
- assert result |> :erlang.iolist_to_binary() == ~s|<%= Foo.bar!(baz) %>\n|
+ assert result == ~s"""
+ <%= Foo.bar!(baz) %>
+ """
end
end
end
diff --git a/test/parser/do_expressions_test.exs b/test/parser/do_expressions_test.exs
index ae86538..6592fc5 100644
--- a/test/parser/do_expressions_test.exs
+++ b/test/parser/do_expressions_test.exs
@@ -2,6 +2,7 @@ defmodule Temple.Parser.DoExpressionsTest do
use ExUnit.Case, async: true
alias Temple.Parser.DoExpressions
+ alias Temple.Support.Utils
describe "applicable?/1" do
test "returns true when the node contains a do expression" do
@@ -47,9 +48,14 @@ defmodule Temple.Parser.DoExpressionsTest do
end
|> DoExpressions.run()
|> Temple.Generator.to_eex()
+ |> Utils.iolist_to_binary()
- assert result |> :erlang.iolist_to_binary() ==
- ~s|<%= for(big <- boys) do %>\nbob\n<% end %>|
+ assert result ==
+ ~s"""
+ <%= for(big <- boys) do %>
+ bob
+ <% end %>
+ """
end
test "emits eex for that includes in else clause" do
@@ -65,9 +71,17 @@ defmodule Temple.Parser.DoExpressionsTest do
end
|> DoExpressions.run()
|> Temple.Generator.to_eex()
+ |> Utils.iolist_to_binary()
- assert result |> :erlang.iolist_to_binary() ==
- ~s|<%= if(foo?) do %>\nbob\nbobby\n<% else %>\ncarol\n<% end %>|
+ assert result ==
+ ~s"""
+ <%= if(foo?) do %>
+ bob
+ bobby
+ <% else %>
+ carol
+ <% end %>
+ """
end
test "emits eex for a case expression" do
@@ -80,9 +94,15 @@ defmodule Temple.Parser.DoExpressionsTest do
end
|> DoExpressions.run()
|> Temple.Generator.to_eex()
+ |> Utils.iolist_to_binary()
- assert result |> :erlang.iolist_to_binary() ==
- ~s|<%= case(foo?) do %>\n<% :bing -> %>\n<%= :bong %>\n<% end %>|
+ assert result ==
+ ~s"""
+ <%= case(foo?) do %>
+ <% :bing -> %>
+ <%= :bong %>
+ <% end %>
+ """
end
end
end
diff --git a/test/parser/nonvoid_elements_aliases_test.exs b/test/parser/nonvoid_elements_aliases_test.exs
index da0f236..22411a2 100644
--- a/test/parser/nonvoid_elements_aliases_test.exs
+++ b/test/parser/nonvoid_elements_aliases_test.exs
@@ -2,6 +2,8 @@ defmodule Temple.Parser.NonvoidElementsAliasesTest do
use ExUnit.Case, async: true
alias Temple.Parser.NonvoidElementsAliases
+ alias Temple.Parser.ElementList
+ alias Temple.Support.Utils
describe "applicable?/1" do
test "returns true when the node is a nonvoid element or alias" do
@@ -63,19 +65,25 @@ defmodule Temple.Parser.NonvoidElementsAliasesTest do
assert %NonvoidElementsAliases{
name: "div",
attrs: [class: "foo", id: {:var, [], _}],
- children: [
- %NonvoidElementsAliases{
- name: "select",
- children: [
- %NonvoidElementsAliases{
- name: "option",
+ children: %ElementList{
+ children: [
+ %NonvoidElementsAliases{
+ name: "select",
+ children: %ElementList{
children: [
- %Temple.Parser.Text{text: "foo"}
+ %NonvoidElementsAliases{
+ name: "option",
+ children: %ElementList{
+ children: [
+ %Temple.Parser.Text{text: "foo"}
+ ]
+ }
+ }
]
}
- ]
- }
- ]
+ }
+ ]
+ }
} = ast
end
end
@@ -94,9 +102,44 @@ defmodule Temple.Parser.NonvoidElementsAliasesTest do
end
|> NonvoidElementsAliases.run()
|> Temple.Generator.to_eex()
+ |> Utils.iolist_to_binary()
- assert result |> :erlang.iolist_to_binary() ==
- ~s|
>\n\n
|
+ assert result ==
+ ~s"""
+
>
+
+
+ """
+ end
+
+ test "produce 'tight' markup" do
+ result =
+ quote do
+ div class: "foo", id: var do
+ select__ do
+ option! do
+ "foo"
+ end
+ end
+ end
+ end
+ |> NonvoidElementsAliases.run()
+ |> Temple.Generator.to_eex()
+ |> :erlang.iolist_to_binary()
+ |> Kernel.<>("\n")
+
+ assert result ==
+ ~s"""
+
>
+
+
+ """
end
end
end
diff --git a/test/parser/right_arrow_test.exs b/test/parser/right_arrow_test.exs
index 4031ecb..7ee66ef 100644
--- a/test/parser/right_arrow_test.exs
+++ b/test/parser/right_arrow_test.exs
@@ -2,6 +2,7 @@ defmodule Temple.Parser.RightArrowTest do
use ExUnit.Case, async: true
alias Temple.Parser.RightArrow
+ alias Temple.Support.Utils
describe "applicable?/1" do
test "returns true when the node contains a right arrow" do
@@ -70,9 +71,13 @@ defmodule Temple.Parser.RightArrowTest do
|> List.first()
|> RightArrow.run()
|> Temple.Generator.to_eex()
+ |> Utils.iolist_to_binary()
- assert result |> :erlang.iolist_to_binary() ==
- ~s|<% :bing -> %>\n<%= :bong %>\n|
+ assert result ==
+ ~s"""
+ <% :bing -> %>
+ <%= :bong %>
+ """
end
end
end
diff --git a/test/parser/slot_test.exs b/test/parser/slot_test.exs
index 9b01e1e..78b015c 100644
--- a/test/parser/slot_test.exs
+++ b/test/parser/slot_test.exs
@@ -42,7 +42,9 @@ defmodule Temple.Parser.SlotTest do
|> Temple.Generator.to_eex()
assert result |> :erlang.iolist_to_binary() ==
- ~s|<%= Temple.Component.__render_block__(@inner_block, {:header, Enum.into([value: Form.form_for(changeset, action)], %{})}) %>|
+ ~s"""
+ <%= Temple.Component.__render_block__(@inner_block, {:header, Enum.into([value: Form.form_for(changeset, action)], %{})}) %>
+ """
end
end
end
diff --git a/test/parser/temple_namespace_nonvoid_test.exs b/test/parser/temple_namespace_nonvoid_test.exs
index de508ae..81df4ab 100644
--- a/test/parser/temple_namespace_nonvoid_test.exs
+++ b/test/parser/temple_namespace_nonvoid_test.exs
@@ -3,6 +3,7 @@ defmodule Temple.Parser.TempleNamespaceNonvoidTest do
alias Temple.Parser.NonvoidElementsAliases
alias Temple.Parser.TempleNamespaceNonvoid
+ alias Temple.Support.Utils
describe "applicable?/1" do
test "returns true when the node is a Temple aliased nonvoid element" do
@@ -50,7 +51,10 @@ defmodule Temple.Parser.TempleNamespaceNonvoidTest do
assert %NonvoidElementsAliases{
name: "div",
attrs: [class: "foo", id: {:var, [], _}],
- children: [%Temple.Parser.Text{text: "foo"}]
+ children: %Temple.Parser.ElementList{
+ children: [%Temple.Parser.Text{text: "foo"}],
+ whitespace: :loose
+ }
} = ast
end
end
@@ -65,9 +69,14 @@ defmodule Temple.Parser.TempleNamespaceNonvoidTest do
end
|> TempleNamespaceNonvoid.run()
|> Temple.Generator.to_eex()
+ |> Utils.iolist_to_binary()
- assert result |> :erlang.iolist_to_binary() ==
- ~s|
>\nfoo\n\n
|
+ assert result ==
+ ~s"""
+
>
+ foo
+
+ """
end
end
end
diff --git a/test/parser/temple_namespace_void_test.exs b/test/parser/temple_namespace_void_test.exs
index 1ca3c2d..19e8d2d 100644
--- a/test/parser/temple_namespace_void_test.exs
+++ b/test/parser/temple_namespace_void_test.exs
@@ -3,6 +3,7 @@ defmodule Temple.Parser.TempleNamespaceVoidTest do
alias Temple.Parser.TempleNamespaceVoid
alias Temple.Parser.VoidElementsAliases
+ alias Temple.Support.Utils
describe "applicable?/1" do
test "returns true when the node is a Temple aliased nonvoid element" do
@@ -58,8 +59,9 @@ defmodule Temple.Parser.TempleNamespaceVoidTest do
end
|> TempleNamespaceVoid.run()
|> Temple.Generator.to_eex()
+ |> Utils.iolist_to_binary()
- assert result |> :erlang.iolist_to_binary() == ~s|\n|
+ assert result == ~s|\n|
end
end
end
diff --git a/test/parser/text_test.exs b/test/parser/text_test.exs
index 16be3b3..432df1c 100644
--- a/test/parser/text_test.exs
+++ b/test/parser/text_test.exs
@@ -2,6 +2,7 @@ defmodule Temple.Parser.TextTest do
use ExUnit.Case, async: true
alias Temple.Parser.Text
+ alias Temple.Support.Utils
describe "applicable?/1" do
test "returns true when the node is a string literal" do
@@ -35,8 +36,9 @@ defmodule Temple.Parser.TextTest do
"string literal"
|> Text.run()
|> Temple.Generator.to_eex()
+ |> Utils.iolist_to_binary()
- assert result |> :erlang.iolist_to_binary() == ~s|string literal\n|
+ assert result == ~s|string literal\n|
end
end
end
diff --git a/test/parser/void_elements_aliases_test.exs b/test/parser/void_elements_aliases_test.exs
index a71d1e8..a2a898a 100644
--- a/test/parser/void_elements_aliases_test.exs
+++ b/test/parser/void_elements_aliases_test.exs
@@ -2,6 +2,7 @@ defmodule Temple.Parser.VoidElementsAliasesTest do
use ExUnit.Case, async: true
alias Temple.Parser.VoidElementsAliases
+ alias Temple.Support.Utils
describe "applicable?/1" do
test "returns true when the node is a nonvoid element or alias" do
@@ -63,8 +64,9 @@ defmodule Temple.Parser.VoidElementsAliasesTest do
end
|> VoidElementsAliases.run()
|> Temple.Generator.to_eex()
+ |> Utils.iolist_to_binary()
- assert result |> :erlang.iolist_to_binary() == ~s|\n|
+ assert result == ~s|\n|
end
end
end
diff --git a/test/support/utils.ex b/test/support/utils.ex
index 3a8c028..e47adf0 100644
--- a/test/support/utils.ex
+++ b/test/support/utils.ex
@@ -33,4 +33,20 @@ defmodule Temple.Support.Utils do
|> elem(0)
|> Phoenix.HTML.safe_to_string()
end
+
+ @doc """
+ Converts an iolist to a binary and appends a new line.
+ """
+ def iolist_to_binary(iolist) do
+ iolist
+ |> :erlang.iolist_to_binary()
+ |> append_new_line()
+ end
+
+ @doc """
+ Appends a new line to a string.
+ """
+ def append_new_line(string) do
+ string <> "\n"
+ end
end
diff --git a/test/temple_test.exs b/test/temple_test.exs
index 4ca8cc9..fecbd14 100644
--- a/test/temple_test.exs
+++ b/test/temple_test.exs
@@ -11,7 +11,12 @@ defmodule TempleTest do
end
end
- assert result == ~s{
}
+ assert result == ~s"""
+
+
+
+
+ """
end
test "renders void element" do
@@ -32,7 +37,12 @@ defmodule TempleTest do
end
end
- assert result == ~s{
hifoo
}
+ assert result == ~s"""
+
+ hi
+ foo
+
+ """
end
test "renders a variable text node as eex" do
@@ -43,7 +53,11 @@ defmodule TempleTest do
end
end
- assert result == ~s{
<%= foo %>
}
+ assert result == ~s"""
+
+ <%= foo %>
+
+ """
end
test "renders an assign text node as eex" do
@@ -54,7 +68,11 @@ defmodule TempleTest do
end
end
- assert result == ~s{
<%= @foo %>
}
+ assert result == ~s"""
+
+ <%= @foo %>
+
+ """
end
test "renders a match expression" do
@@ -67,7 +85,12 @@ defmodule TempleTest do
end
end
- assert result == ~s{<% x = 420 %>
blaze it
}
+ assert result == ~s"""
+ <% x = 420 %>
+
+ blaze it
+
+ """
end
test "renders a non-match expression" do
@@ -80,7 +103,12 @@ defmodule TempleTest do
end
end
- assert result == ~s{<%= IO.inspect(:foo) %>
+ """
end
test "renders an expression in attr as eex" do
@@ -102,7 +130,12 @@ defmodule TempleTest do
end
assert result ==
- ~s|
x end))} %>>
|
+ ~s"""
+
x end))} %>>
+
+
+
+ """
end
test "renders a for comprehension as eex" do
@@ -113,7 +146,12 @@ defmodule TempleTest do
end
end
- assert result == ~s{<%= for(x <- 1..5) do %><% end %>}
+ assert result == ~s"""
+ <%= for(x <- 1..5) do %>
+
+
+ <% end %>
+ """
end
test "renders an if expression as eex" do
@@ -124,7 +162,12 @@ defmodule TempleTest do
end
end
- assert result == ~s{<%= if(true == false) do %><% end %>}
+ assert result == ~s"""
+ <%= if(true == false) do %>
+
+
+ <% end %>
+ """
end
test "renders an if/else expression as eex" do
@@ -138,7 +181,15 @@ defmodule TempleTest do
end
assert result ==
- ~s{<%= if(true == false) do %><% else %><% end %>}
+ ~s"""
+ <%= if(true == false) do %>
+
+
+ <% else %>
+
+
+ <% end %>
+ """
end
test "renders an unless expression as eex" do
@@ -149,7 +200,12 @@ defmodule TempleTest do
end
end
- assert result == ~s{<%= unless(true == false) do %><% end %>}
+ assert result == ~s"""
+ <%= unless(true == false) do %>
+
+
+ <% end %>
+ """
end
test "renders a case expression as eex" do
@@ -161,14 +217,12 @@ defmodule TempleTest do
end
end
- expected =
- ~S"""
- <%= case(@foo) do %>
+ expected = ~S"""
+ <%= case(@foo) do %>
<% :baz -> %>
- <%= some_component(form: @form) %>
- <% end %>
- """
- |> String.trim()
+ <%= some_component(form: @form) %>
+ <% end %>
+ """
assert result == expected
end
@@ -176,70 +230,99 @@ defmodule TempleTest do
test "renders multiline anonymous function with 1 arg before the function" do
result =
temple do
- form_for Routes.user_path(@conn, :create), fn f ->
+ form_for(Routes.user_path(@conn, :create), fn f ->
"Name: "
- text_input f, :name
- end
+ text_input(f, :name)
+ end)
end
assert result ==
- ~s{<%= form_for Routes.user_path(@conn, :create), fn f -> %>Name: <%= text_input(f, :name) %><% end %>}
+ ~s"""
+ <%= form_for Routes.user_path(@conn, :create), fn f -> %>
+ Name:
+ <%= text_input(f, :name) %>
+ <% end %>
+ """
end
test "renders multiline anonymous functions with 2 args before the function" do
result =
temple do
- form_for @changeset, Routes.user_path(@conn, :create), fn f ->
+ form_for(@changeset, Routes.user_path(@conn, :create), fn f ->
"Name: "
- text_input f, :name
- end
+ text_input(f, :name)
+ end)
end
assert result ==
- ~s{<%= form_for @changeset, Routes.user_path(@conn, :create), fn f -> %>Name: <%= text_input(f, :name) %><% end %>}
+ ~s"""
+ <%= form_for @changeset, Routes.user_path(@conn, :create), fn f -> %>
+ Name:
+ <%= text_input(f, :name) %>
+ <% end %>
+ """
end
test "renders multiline anonymous functions with complex nested children" do
result =
temple do
- form_for @changeset, Routes.user_path(@conn, :create), fn f ->
+ form_for(@changeset, Routes.user_path(@conn, :create), fn f ->
div do
"Name: "
- text_input f, :name
+ text_input(f, :name)
end
- end
+ end)
end
assert result ==
- ~s{<%= form_for @changeset, Routes.user_path(@conn, :create), fn f -> %>
Name: <%= text_input(f, :name) %>
<% end %>}
+ ~s"""
+ <%= form_for @changeset, Routes.user_path(@conn, :create), fn f -> %>
+
+ Name:
+ <%= text_input(f, :name) %>
+
+ <% end %>
+ """
end
test "renders multiline anonymous function with 3 arg before the function" do
result =
temple do
- form_for @changeset, Routes.user_path(@conn, :create), [foo: :bar], fn f ->
+ form_for(@changeset, Routes.user_path(@conn, :create), [foo: :bar], fn f ->
"Name: "
- text_input f, :name
- end
+ text_input(f, :name)
+ end)
end
assert result ==
- ~s{<%= form_for @changeset, Routes.user_path(@conn, :create), [foo: :bar], fn f -> %>Name: <%= text_input(f, :name) %><% end %>}
+ ~s"""
+ <%= form_for @changeset, Routes.user_path(@conn, :create), [foo: :bar], fn f -> %>
+ Name:
+ <%= text_input(f, :name) %>
+ <% end %>
+ """
end
test "renders multiline anonymous function with 1 arg before the function and 1 arg after" do
result =
temple do
- form_for @changeset,
- fn f ->
- "Name: "
- text_input f, :name
- end,
- foo: :bar
+ form_for(
+ @changeset,
+ fn f ->
+ "Name: "
+ text_input(f, :name)
+ end,
+ foo: :bar
+ )
end
assert result ==
- ~s{<%= form_for @changeset, fn f -> %>Name: <%= text_input(f, :name) %><% end, [foo: :bar] %>}
+ ~s"""
+ <%= form_for @changeset, fn f -> %>
+ Name:
+ <%= text_input(f, :name) %>
+ <% end, [foo: :bar] %>
+ """
end
test "tags prefixed with Temple. should be interpreted as temple tags" do
@@ -252,7 +335,13 @@ defmodule TempleTest do
end
end
- assert result == ~s{
bob
}
+ assert result == ~s"""
+
+
+ bob
+
+
+ """
end
test "can pass do as an arg instead of a block" do
@@ -267,7 +356,17 @@ defmodule TempleTest do
end
assert result ==
- ~s{
Hello, world
Hello, world
Hello, world
}
+ ~s"""
+
+ Hello, world
+
+
+ Hello, world
+
+
+ Hello, world
+
+ """
end
test "for with 2 generators" do
@@ -280,7 +379,16 @@ defmodule TempleTest do
end
assert result ==
- ~s{<%= for(x <- 1..5, y <- 6..10) do %>
<%= x %>
<%= y %>
<% end %>}
+ ~s"""
+ <%= for(x <- 1..5, y <- 6..10) do %>
+
+ <%= x %>
+
+
+ <%= y %>
+
+ <% end %>
+ """
end
test "can pass an expression as assigns" do
@@ -292,7 +400,15 @@ defmodule TempleTest do
end
assert result ==
- ~s{}
+ ~s"""
+
+ """
end
test "can pass a variable as assigns" do
@@ -304,7 +420,11 @@ defmodule TempleTest do
end
assert result ==
- ~s{}
+ ~s"""
+
+ """
end
test "can pass a function as assigns" do
@@ -316,7 +436,11 @@ defmodule TempleTest do
end
assert result ==
- ~s{}
+ ~s"""
+
+ """
end
test "hr tag works" do
@@ -334,7 +458,23 @@ defmodule TempleTest do
end
assert evaluate_template(result, assigns) ==
- ~s{
foo
foo
bar
bar
}
+ ~s"""
+
+ foo
+
+
+
+ foo
+
+
+
+ bar
+
+
+
+ bar
+
+ """
end
test "boolean attributes" do
@@ -363,6 +503,10 @@ defmodule TempleTest do
end
end
- assert evaluate_template(result, assigns) == ~s{
+ """
end
end
diff --git a/test/whitespace_test.exs b/test/whitespace_test.exs
new file mode 100644
index 0000000..c663026
--- /dev/null
+++ b/test/whitespace_test.exs
@@ -0,0 +1,46 @@
+defmodule Temple.WhitespaceTest do
+ use ExUnit.Case, async: true
+
+ import Temple
+
+ alias Temple.Support.Utils
+
+ test "only emits a single new line" do
+ result =
+ temple do
+ div class: "hello" do
+ span id: "foo" do
+ "Howdy, "
+ end
+
+ div class: "hi" do
+ "Jim Bob"
+ end
+
+ c WhoaNelly, foo: "bar" do
+ slot :silver do
+ "esketit"
+ end
+ end
+ end
+ end
+ |> Utils.append_new_line()
+
+ expected = ~s"""
+