Skip to content

Commit 39f4313

Browse files
Implement Inspect protocol for HTMLTree (#547)
* WIP Implement Inspect protocol * Fix formatting * Traverse tree only once * Change params of fun/2 recursion * Make things work * Fix formatting * Address code review improvements * Apply suggestions from code review
1 parent d1fd549 commit 39f4313

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed

lib/floki/html_tree.ex

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,4 +328,77 @@ defmodule Floki.HTMLTree do
328328
do_reduce(tree, fun.(head_node, acc), fun)
329329
end
330330
end
331+
332+
defimpl Inspect do
333+
import Inspect.Algebra
334+
335+
def inspect(html_tree, opts) do
336+
open = "#Floki.HTMLTree["
337+
close = "]"
338+
container_opts = [separator: "", break: :flex]
339+
340+
container_doc(
341+
open,
342+
nodes_with_tree(html_tree, html_tree.root_nodes_ids),
343+
close,
344+
opts,
345+
&fun/2,
346+
container_opts
347+
)
348+
end
349+
350+
defp fun({html_tree, %HTMLNode{} = html_node}, opts) do
351+
{open, close, container_opts} = build_node(html_node, opts)
352+
353+
container_doc(
354+
open,
355+
nodes_with_tree(html_tree, html_node.children_nodes_ids),
356+
close,
357+
opts,
358+
&fun/2,
359+
container_opts
360+
)
361+
end
362+
363+
defp fun(%Comment{content: comment}, opts),
364+
do: color(concat(["<!-- ", comment, " -->"]), :comment, opts)
365+
366+
defp fun(%Text{content: text}, opts), do: color(text, :string, opts)
367+
368+
defp nodes_with_tree(html_tree, nodes_ids) do
369+
nodes_ids
370+
|> Enum.reverse()
371+
|> Enum.map(fn node_id ->
372+
with %HTMLNode{} = html_node <- Map.get(html_tree.nodes, node_id) do
373+
{html_tree, html_node}
374+
end
375+
end)
376+
end
377+
378+
defp build_node(%HTMLNode{} = node, opts) do
379+
tag_color = :map
380+
attribute_color = :map
381+
382+
built_attributes =
383+
for {name, value} <- node.attributes do
384+
concat([
385+
color(" #{name}=", attribute_color, opts),
386+
color("\"#{value}\"", :string, opts)
387+
])
388+
end
389+
|> concat()
390+
391+
open =
392+
concat([
393+
color("<#{node.type}", tag_color, opts),
394+
built_attributes,
395+
color(">", tag_color, opts)
396+
])
397+
398+
close = color("</#{node.type}>", tag_color, opts)
399+
container_opts = [separator: "", break: :strict]
400+
401+
{open, close, container_opts}
402+
end
403+
end
331404
end

test/floki/html_tree_test.exs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,4 +256,42 @@ defmodule Floki.HTMLTreeTest do
256256
refute Enum.member?(html_tree, %{html_node3 | type: "marquee"})
257257
refute Enum.member?(html_tree, 42)
258258
end
259+
260+
test "inspect works with HTMLTree" do
261+
html_tree = %HTMLTree{
262+
root_nodes_ids: [1],
263+
node_ids: [1],
264+
nodes: %{
265+
1 => %Text{content: "hello world", node_id: 1}
266+
}
267+
}
268+
269+
assert inspect(html_tree) ==
270+
~s|#Floki.HTMLTree[hello world]|
271+
end
272+
273+
test "inspect works with HTMLTree with nested nodes" do
274+
html_tree =
275+
%HTMLTree{
276+
root_nodes_ids: [1],
277+
node_ids: [6, 5, 4, 3, 2, 1],
278+
nodes: %{
279+
1 => %HTMLNode{type: "html", children_nodes_ids: [6, 3, 2], node_id: 1},
280+
2 => %Comment{content: "start of the stack", node_id: 2, parent_node_id: 1},
281+
3 => %HTMLNode{
282+
type: "a",
283+
attributes: [{"class", "link"}],
284+
parent_node_id: 1,
285+
children_nodes_ids: [4],
286+
node_id: 3
287+
},
288+
4 => %HTMLNode{type: "b", parent_node_id: 3, children_nodes_ids: [5], node_id: 4},
289+
5 => %Text{content: "click me", parent_node_id: 4, node_id: 5},
290+
6 => %HTMLNode{type: "span", parent_node_id: 1, node_id: 6}
291+
}
292+
}
293+
294+
assert inspect(html_tree) ==
295+
~s|#Floki.HTMLTree[<html><!-- start of the stack --> <a class=\"link\"><b>click me</b></a> <span></span></html>]|
296+
end
259297
end

0 commit comments

Comments
 (0)