Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Inspect protocol for HTMLTree #547

Merged
merged 8 commits into from
Mar 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions lib/floki/html_tree.ex
Original file line number Diff line number Diff line change
Expand Up @@ -328,4 +328,77 @@ defmodule Floki.HTMLTree do
do_reduce(tree, fun.(head_node, acc), fun)
end
end

defimpl Inspect do
import Inspect.Algebra

def inspect(html_tree, opts) do
open = "#Floki.HTMLTree["
close = "]"
container_opts = [separator: "", break: :flex]

container_doc(
open,
nodes_with_tree(html_tree, html_tree.root_nodes_ids),
close,
opts,
&fun/2,
container_opts
)
end

defp fun({html_tree, %HTMLNode{} = html_node}, opts) do
{open, close, container_opts} = build_node(html_node, opts)

container_doc(
open,
nodes_with_tree(html_tree, html_node.children_nodes_ids),
close,
opts,
&fun/2,
container_opts
)
end

defp fun(%Comment{content: comment}, opts),
do: color(concat(["<!-- ", comment, " -->"]), :comment, opts)

defp fun(%Text{content: text}, opts), do: color(text, :string, opts)

defp nodes_with_tree(html_tree, nodes_ids) do
nodes_ids
|> Enum.reverse()
|> Enum.map(fn node_id ->
with %HTMLNode{} = html_node <- Map.get(html_tree.nodes, node_id) do
{html_tree, html_node}
end
end)
end

defp build_node(%HTMLNode{} = node, opts) do
tag_color = :map
attribute_color = :map

built_attributes =
for {name, value} <- node.attributes do
concat([
color(" #{name}=", attribute_color, opts),
color("\"#{value}\"", :string, opts)
])
end
|> concat()

open =
concat([
color("<#{node.type}", tag_color, opts),
built_attributes,
color(">", tag_color, opts)
])

close = color("</#{node.type}>", tag_color, opts)
container_opts = [separator: "", break: :strict]

{open, close, container_opts}
end
end
end
38 changes: 38 additions & 0 deletions test/floki/html_tree_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,42 @@ defmodule Floki.HTMLTreeTest do
refute Enum.member?(html_tree, %{html_node3 | type: "marquee"})
refute Enum.member?(html_tree, 42)
end

test "inspect works with HTMLTree" do
html_tree = %HTMLTree{
root_nodes_ids: [1],
node_ids: [1],
nodes: %{
1 => %Text{content: "hello world", node_id: 1}
}
}

assert inspect(html_tree) ==
~s|#Floki.HTMLTree[hello world]|
end

test "inspect works with HTMLTree with nested nodes" do
html_tree =
%HTMLTree{
root_nodes_ids: [1],
node_ids: [6, 5, 4, 3, 2, 1],
nodes: %{
1 => %HTMLNode{type: "html", children_nodes_ids: [6, 3, 2], node_id: 1},
2 => %Comment{content: "start of the stack", node_id: 2, parent_node_id: 1},
3 => %HTMLNode{
type: "a",
attributes: [{"class", "link"}],
parent_node_id: 1,
children_nodes_ids: [4],
node_id: 3
},
4 => %HTMLNode{type: "b", parent_node_id: 3, children_nodes_ids: [5], node_id: 4},
5 => %Text{content: "click me", parent_node_id: 4, node_id: 5},
6 => %HTMLNode{type: "span", parent_node_id: 1, node_id: 6}
}
}

assert inspect(html_tree) ==
~s|#Floki.HTMLTree[<html><!-- start of the stack --> <a class=\"link\"><b>click me</b></a> <span></span></html>]|
end
end
Loading