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 6 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
74 changes: 74 additions & 0 deletions lib/floki/html_tree.ex
Original file line number Diff line number Diff line change
Expand Up @@ -328,4 +328,78 @@ 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 = "%HTMLTree{"
close = "}"
container_opts = [separator: "", break: :flex]

container_doc(
open,
[{html_tree, html_tree.root_nodes_ids}],
close,
opts,
&fun/2,
container_opts
)
end

defp fun({html_tree, nodes_ids}, opts) do
nodes_ids
|> Enum.reverse()
|> Enum.map(fn node_id ->
root = Map.get(html_tree.nodes, node_id)

{open, close, container_opts} = build_tree(root, opts)

if match?(%HTMLNode{}, root) do
container_doc(
open,
[{html_tree, root.children_nodes_ids}],
close,
opts,
&fun/2,
container_opts
)
else
open
end
end)
|> concat()
end

defp build_tree(%Comment{content: comment}, _opts),
do: {comment, "", [separator: "", break: :strict]}

defp build_tree(%Text{content: text}, _opts), do: {text, "", [separator: "", break: :strict]}

defp build_tree(%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: [2, 1],
node_ids: [2, 1],
nodes: %{
1 => %Text{content: "hello", node_id: 1},
2 => %Text{content: " world", node_id: 2}
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After thinking for a while, I believe the parser won't produce such tree, because the Text would be collapsed. So I would change this to one Text` node, or maybe adding a tag in between.


assert inspect(html_tree) ==
~s|%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|%HTMLTree{<html>start of the stack<a class="link"><b>click me</b></a><span></span></html>}|
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We forgot to format the comment. The suggestions I made is changing that. But the output here will be different.

PS: try to add a |> dbg() to line 292 and see the beauty of the output :D

end
end
Loading