Packages
surface
0.9.1
0.12.3
0.12.2
0.12.1
0.12.0
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.1
0.1.0
0.1.0-alpha.2
0.1.0-alpha.1
0.1.0-alpha.0
A component based library for Phoenix LiveView
Current section
Files
Jump to
Current section
Files
lib/surface/formatter/phases/spaces_to_newlines.ex
defmodule Surface.Formatter.Phases.SpacesToNewlines do
@moduledoc """
In a variety of scenarios, converts :space nodes to :newline nodes.
(Below, "element" means an HTML element or a Surface component.)
1. If an element contains other elements as children, surround it with newlines.
1. If there is a space after an opening tag or before a closing tag, convert it to a newline.
1. If there is a closing tag on its own line, ensure there's a newline before the next sibling node.
""" && false
@behaviour Surface.Formatter.Phase
alias Surface.{Formatter, Formatter.Phase}
def run(nodes, _opts) do
nodes
|> ensure_newlines_surrounding_elements_with_element_children()
|> convert_spaces_to_newlines_around_edge_children()
|> move_siblings_after_lone_closing_tag_to_new_line()
end
# If an element has an element as a child, ensure it's surrounded by newlines, not spaces
defp ensure_newlines_surrounding_elements_with_element_children(nodes, accumulated \\ [])
defp ensure_newlines_surrounding_elements_with_element_children(
[:space, {_, _, children, _} = element, :space | rest],
accumulated
) do
whitespace =
if Enum.any?(children, &Formatter.is_element?/1) do
:newline
else
:space
end
ensure_newlines_surrounding_elements_with_element_children(
rest,
accumulated ++ [whitespace, element, whitespace]
)
end
defp ensure_newlines_surrounding_elements_with_element_children(
[{_, _, children, _} = element, :space | rest],
accumulated
) do
whitespace =
if Enum.any?(children, &Formatter.is_element?/1) do
:newline
else
:space
end
ensure_newlines_surrounding_elements_with_element_children(
rest,
accumulated ++ [element, whitespace]
)
end
defp ensure_newlines_surrounding_elements_with_element_children([node | rest], accumulated) do
ensure_newlines_surrounding_elements_with_element_children(rest, accumulated ++ [node])
end
defp ensure_newlines_surrounding_elements_with_element_children([], accumulated) do
Phase.transform_element_children(
accumulated,
&ensure_newlines_surrounding_elements_with_element_children/1
)
end
# If there is a space before the first child / after the last, convert it to a newline
defp convert_spaces_to_newlines_around_edge_children(nodes) do
# If there is a space before the first child, and it's an element, convert it to a newline
nodes =
case nodes do
[:space, element | rest] ->
[:newline, element | rest]
_ ->
nodes
end
# If there is a space before the first child, and it's an element, convert it to a newline
nodes =
case Enum.reverse(nodes) do
[:space, _element | _rest] ->
Enum.slice(nodes, 0..-2) ++ [:newline]
_ ->
nodes
end
nodes
|> Phase.transform_element_children(&convert_spaces_to_newlines_around_edge_children/1)
end
# Basically makes sure that this
#
# <p>
# Foo
# </p> <p>Hello</p>
#
# turns into this
#
# <p>
# Foo
# </p>
# <p>Hello</p>
defp move_siblings_after_lone_closing_tag_to_new_line(nodes, accumulated \\ [])
defp move_siblings_after_lone_closing_tag_to_new_line(
[{_, _, children, _} = element, :space | rest],
accumulated
) do
if Enum.any?(children, &(&1 == :newline)) do
move_siblings_after_lone_closing_tag_to_new_line(
rest,
accumulated ++ [element, :newline]
)
else
move_siblings_after_lone_closing_tag_to_new_line(
rest,
accumulated ++ [element, :space]
)
end
end
defp move_siblings_after_lone_closing_tag_to_new_line([node | rest], accumulated) do
move_siblings_after_lone_closing_tag_to_new_line(rest, accumulated ++ [node])
end
defp move_siblings_after_lone_closing_tag_to_new_line([], accumulated) do
Phase.transform_element_children(
accumulated,
&move_siblings_after_lone_closing_tag_to_new_line/1
)
end
end