diff --git a/.rubocop.yml b/.rubocop.yml
index 0672d768a..62881c7cf 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -116,6 +116,7 @@ Metrics/BlockLength:
Metrics/ParameterLists:
Exclude:
+ - lib/herb/ast/node.rb
- lib/herb/ast/nodes.rb
- lib/herb/errors.rb
- lib/herb/engine/validators/security_validator.rb
@@ -178,3 +179,7 @@ Security/MarshalLoad:
Lint/UnderscorePrefixedVariableName:
Exclude:
- test/engine/secure_compiler_test.rb
+
+Lint/UnusedMethodArgument:
+ Exclude:
+ - lib/herb/errors.rb
diff --git a/lib/herb/ast/node.rb b/lib/herb/ast/node.rb
index e1e5e4270..e1b5768c6 100644
--- a/lib/herb/ast/node.rb
+++ b/lib/herb/ast/node.rb
@@ -46,8 +46,15 @@ def inspect_errors(prefix: " ")
"├── errors: #{inspect_array(errors, item_name: "error", prefix: prefix)}"
end
- #: (Array[Herb::AST::Node|Herb::Errors::Error], ?item_name: String, ?prefix: String) -> String
- def inspect_array(array, item_name: "item", prefix: " ")
+ #: (
+ #| Array[Herb::AST::Node|Herb::Errors::Error],
+ #| ?item_name: String,
+ #| ?prefix: String,
+ #| ?indent: Integer,
+ #| ?depth: Integer,
+ #| ?depth_limit: Integer
+ #| ) -> String
+ def inspect_array(array, item_name: "item", prefix: " ", indent: 0, depth: 0, depth_limit: 25)
output = +""
if array.any?
@@ -55,10 +62,12 @@ def inspect_array(array, item_name: "item", prefix: " ")
output += "\n"
items = array.map { |item|
+ kwargs = { indent: indent, depth: depth, depth_limit: depth_limit }
+
if array.last == item
- "└── #{item.tree_inspect.gsub(/^/, " ").lstrip}"
+ "└── #{item.tree_inspect(**kwargs).gsub(/^/, " ").lstrip}"
else
- "├── #{item.tree_inspect.gsub(/^/, "│ ")}".gsub("├── │ ", "├──")
+ "├── #{item.tree_inspect(**kwargs).gsub(/^/, "│ ")}".gsub("├── │ ", "├──")
end
}
@@ -71,8 +80,8 @@ def inspect_array(array, item_name: "item", prefix: " ")
output
end
- #: (?Integer) -> String
- def tree_inspect(_indent = 0)
+ #: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect(indent: 0, depth: 0, depth_limit: 25)
raise NotImplementedError
end
diff --git a/sig/herb/ast/node.rbs b/sig/herb/ast/node.rbs
index c89780470..7d52e7498 100644
--- a/sig/herb/ast/node.rbs
+++ b/sig/herb/ast/node.rbs
@@ -27,11 +27,18 @@ module Herb
# : (?prefix: String) -> String
def inspect_errors: (?prefix: String) -> String
- # : (Array[Herb::AST::Node|Herb::Errors::Error], ?item_name: String, ?prefix: String) -> String
- def inspect_array: (Array[Herb::AST::Node | Herb::Errors::Error], ?item_name: String, ?prefix: String) -> String
-
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (
+ # | Array[Herb::AST::Node|Herb::Errors::Error],
+ # | ?item_name: String,
+ # | ?prefix: String,
+ # | ?indent: Integer,
+ # | ?depth: Integer,
+ # | ?depth_limit: Integer
+ # | ) -> String
+ def inspect_array: (untyped array, ?item_name: untyped, ?prefix: untyped, ?indent: untyped, ?depth: untyped, ?depth_limit: untyped) -> untyped
+
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
# : (Visitor) -> void
def accept: (Visitor) -> void
diff --git a/sig/herb/ast/nodes.rbs b/sig/herb/ast/nodes.rbs
index 47ad6767b..a79e16e1f 100644
--- a/sig/herb/ast/nodes.rbs
+++ b/sig/herb/ast/nodes.rbs
@@ -25,8 +25,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class LiteralNode < Node
@@ -52,8 +52,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class HTMLOpenTagNode < Node
@@ -87,8 +87,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class HTMLCloseTagNode < Node
@@ -120,8 +120,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class HTMLElementNode < Node
@@ -157,8 +157,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class HTMLAttributeValueNode < Node
@@ -190,8 +190,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class HTMLAttributeNameNode < Node
@@ -217,8 +217,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class HTMLAttributeNode < Node
@@ -248,8 +248,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class HTMLTextNode < Node
@@ -275,8 +275,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class HTMLCommentNode < Node
@@ -306,8 +306,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class HTMLDoctypeNode < Node
@@ -337,8 +337,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class XMLDeclarationNode < Node
@@ -368,8 +368,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class CDATANode < Node
@@ -399,8 +399,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class WhitespaceNode < Node
@@ -426,8 +426,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBContentNode < Node
@@ -463,8 +463,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBEndNode < Node
@@ -494,8 +494,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBElseNode < Node
@@ -527,8 +527,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBIfNode < Node
@@ -564,8 +564,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBBlockNode < Node
@@ -599,8 +599,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBWhenNode < Node
@@ -632,8 +632,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBCaseNode < Node
@@ -671,8 +671,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBCaseMatchNode < Node
@@ -710,8 +710,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBWhileNode < Node
@@ -745,8 +745,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBUntilNode < Node
@@ -780,8 +780,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBForNode < Node
@@ -815,8 +815,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBRescueNode < Node
@@ -850,8 +850,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBEnsureNode < Node
@@ -883,8 +883,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBBeginNode < Node
@@ -924,8 +924,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBUnlessNode < Node
@@ -961,8 +961,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBYieldNode < Node
@@ -992,8 +992,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBInNode < Node
@@ -1025,8 +1025,8 @@ module Herb
# : () -> String
def inspect: () -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
end
end
diff --git a/sig/herb/errors.rbs b/sig/herb/errors.rbs
index 28ba337a5..92d7f4d00 100644
--- a/sig/herb/errors.rbs
+++ b/sig/herb/errors.rbs
@@ -24,8 +24,8 @@ module Herb
# : (?untyped) -> String
def to_json: (?untyped) -> String
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class UnexpectedError < Error
@@ -46,8 +46,8 @@ module Herb
# : () -> serialized_unexpected_error
def to_hash: () -> serialized_unexpected_error
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class UnexpectedTokenError < Error
@@ -66,8 +66,8 @@ module Herb
# : () -> serialized_unexpected_token_error
def to_hash: () -> serialized_unexpected_token_error
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class MissingOpeningTagError < Error
@@ -84,8 +84,8 @@ module Herb
# : () -> serialized_missing_opening_tag_error
def to_hash: () -> serialized_missing_opening_tag_error
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class MissingClosingTagError < Error
@@ -102,8 +102,8 @@ module Herb
# : () -> serialized_missing_closing_tag_error
def to_hash: () -> serialized_missing_closing_tag_error
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class TagNamesMismatchError < Error
@@ -122,8 +122,8 @@ module Herb
# : () -> serialized_tag_names_mismatch_error
def to_hash: () -> serialized_tag_names_mismatch_error
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class QuotesMismatchError < Error
@@ -142,8 +142,8 @@ module Herb
# : () -> serialized_quotes_mismatch_error
def to_hash: () -> serialized_quotes_mismatch_error
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class VoidElementClosingTagError < Error
@@ -164,8 +164,8 @@ module Herb
# : () -> serialized_void_element_closing_tag_error
def to_hash: () -> serialized_void_element_closing_tag_error
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class UnclosedElementError < Error
@@ -182,8 +182,8 @@ module Herb
# : () -> serialized_unclosed_element_error
def to_hash: () -> serialized_unclosed_element_error
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class RubyParseError < Error
@@ -204,8 +204,8 @@ module Herb
# : () -> serialized_ruby_parse_error
def to_hash: () -> serialized_ruby_parse_error
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class ERBControlFlowScopeError < Error
@@ -222,8 +222,8 @@ module Herb
# : () -> serialized_erb_control_flow_scope_error
def to_hash: () -> serialized_erb_control_flow_scope_error
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
class MissingERBEndTagError < Error
@@ -240,8 +240,8 @@ module Herb
# : () -> serialized_missingerb_end_tag_error
def to_hash: () -> serialized_missingerb_end_tag_error
- # : (?Integer) -> String
- def tree_inspect: (?Integer) -> String
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
end
end
end
diff --git a/templates/lib/herb/ast/nodes.rb.erb b/templates/lib/herb/ast/nodes.rb.erb
index f02c14ea1..3fa363070 100644
--- a/templates/lib/herb/ast/nodes.rb.erb
+++ b/templates/lib/herb/ast/nodes.rb.erb
@@ -57,13 +57,19 @@ module Herb
tree_inspect.rstrip.gsub(/\s+$/, "")
end
- #: (?Integer) -> String
- def tree_inspect(indent = 0)
+ #: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect(indent: 0, depth: 0, depth_limit: 10)
output = +""
output += white("@ #{bold(yellow(node_name.to_s))} #{dimmed("(location: #{location.tree_inspect})")}")
output += "\n"
+ if depth >= depth_limit
+ output += dimmed("└── [depth limit reached ...]\n\n")
+
+ return output.gsub(/^/, " " * indent)
+ end
+
output += inspect_errors(prefix: "<%= node.fields.any? ? "│ " : " " %>")
<%- node.fields.each do |field| -%>
@@ -95,7 +101,7 @@ module Herb
<%- else -%>
output += "│ └── "
<%- end -%>
- output += <%= field.name %>.tree_inspect(indent).gsub(/^/, " " * (indent + 1)).lstrip.gsub(/^/, "<%= prefix %>").delete_prefix("<%= prefix %>")
+ output += <%= field.name %>.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, " " * (indent + 1)).lstrip.gsub(/^/, "<%= prefix %>").delete_prefix("<%= prefix %>")
<%- if false -%>
<%- unless node.fields.last == field -%>
output += "\n"
@@ -107,7 +113,7 @@ module Herb
end
<%- when Herb::Template::ArrayField -%>
output += white("<%= symbol %> <%= field.name %>: ")
- output += inspect_array(<%= field.name %>, prefix: "<%= (node.fields.last == field) ? " " : "│ " %>")
+ output += inspect_array(<%= field.name %>, prefix: "<%= (node.fields.last == field) ? " " : "│ " %>", indent: indent, depth: depth + 1, depth_limit: depth_limit)
<%- else -%>
<%- raise "Unhandled type: #{field.class}" -%>
<%- end -%>
diff --git a/templates/lib/herb/errors.rb.erb b/templates/lib/herb/errors.rb.erb
index 8fe497990..da4a8474a 100644
--- a/templates/lib/herb/errors.rb.erb
+++ b/templates/lib/herb/errors.rb.erb
@@ -37,8 +37,8 @@ module Herb
to_hash.to_json(state)
end
- #: (?Integer) -> String
- def tree_inspect(_indent = 0)
+ #: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect(indent: 0, depth: 0, depth_limit: 25)
raise NotImplementedError
end
end
@@ -74,8 +74,8 @@ module Herb
}) #: Herb::serialized_<%= error.human %>
end
- #: (?Integer) -> String
- def tree_inspect(indent = 0)
+ #: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
+ def tree_inspect(indent: 0, depth: 0, depth_limit: 25)
output = +""
output += white("@ #{bold(red(error_name))} #{dimmed("(location: #{location.tree_inspect})\n")}")
diff --git a/test/ast/tree_inspect_test.rb b/test/ast/tree_inspect_test.rb
new file mode 100644
index 000000000..1a8b3e36f
--- /dev/null
+++ b/test/ast/tree_inspect_test.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require_relative "../test_helper"
+
+module AST
+ class TreeInspectTest < Minitest::Spec
+ include SnapshotUtils
+
+ test "deeply nested document" do
+ assert_parsed_snapshot(<<~ERB)
+
+ ERB
+ end
+
+ test "deeply nested with attributes" do
+ assert_parsed_snapshot(<<~ERB)
+
+ ERB
+ end
+ end
+end
diff --git a/test/snapshots/ast/tree_inspect_test/test_0001_deeply_nested_document_4593d80d72ab12fb3c2b889b019dd0cb.txt b/test/snapshots/ast/tree_inspect_test/test_0001_deeply_nested_document_4593d80d72ab12fb3c2b889b019dd0cb.txt
new file mode 100644
index 000000000..1b5a82dc2
--- /dev/null
+++ b/test/snapshots/ast/tree_inspect_test/test_0001_deeply_nested_document_4593d80d72ab12fb3c2b889b019dd0cb.txt
@@ -0,0 +1,272 @@
+---
+source: "AST::TreeInspectTest#test_0001_deeply nested document"
+input: |2-
+
+---
+@ DocumentNode (location: (1:0)-(26:0))
+└── children: (2 items)
+ ├── @ HTMLElementNode (location: (1:0)-(25:6))
+ │ ├── open_tag:
+ │ │ └── @ HTMLOpenTagNode (location: (1:0)-(1:5))
+ │ │ ├── tag_opening: "<" (location: (1:0)-(1:1))
+ │ │ ├── tag_name: "div" (location: (1:1)-(1:4))
+ │ │ ├── tag_closing: ">" (location: (1:4)-(1:5))
+ │ │ ├── children: []
+ │ │ └── is_void: false
+ │ │
+ │ ├── tag_name: "div" (location: (1:1)-(1:4))
+ │ ├── body: (3 items)
+ │ │ ├── @ HTMLTextNode (location: (1:5)-(2:2))
+ │ │ │ └── content: "\n "
+ │ │ │
+ │ │ ├── @ HTMLElementNode (location: (2:2)-(24:8))
+ │ │ │ ├── open_tag:
+ │ │ │ │ └── @ HTMLOpenTagNode (location: (2:2)-(2:7))
+ │ │ │ │ ├── tag_opening: "<" (location: (2:2)-(2:3))
+ │ │ │ │ ├── tag_name: "div" (location: (2:3)-(2:6))
+ │ │ │ │ ├── tag_closing: ">" (location: (2:6)-(2:7))
+ │ │ │ │ ├── children: []
+ │ │ │ │ └── is_void: false
+ │ │ │ │
+ │ │ │ ├── tag_name: "div" (location: (2:3)-(2:6))
+ │ │ │ ├── body: (3 items)
+ │ │ │ │ ├── @ HTMLTextNode (location: (2:7)-(3:4))
+ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │
+ │ │ │ │ ├── @ HTMLElementNode (location: (3:4)-(23:10))
+ │ │ │ │ │ ├── open_tag:
+ │ │ │ │ │ │ └── @ HTMLOpenTagNode (location: (3:4)-(3:9))
+ │ │ │ │ │ │ ├── tag_opening: "<" (location: (3:4)-(3:5))
+ │ │ │ │ │ │ ├── tag_name: "div" (location: (3:5)-(3:8))
+ │ │ │ │ │ │ ├── tag_closing: ">" (location: (3:8)-(3:9))
+ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ └── is_void: false
+ │ │ │ │ │ │
+ │ │ │ │ │ ├── tag_name: "div" (location: (3:5)-(3:8))
+ │ │ │ │ │ ├── body: (3 items)
+ │ │ │ │ │ │ ├── @ HTMLTextNode (location: (3:9)-(4:6))
+ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │
+ │ │ │ │ │ │ ├── @ HTMLElementNode (location: (4:6)-(22:12))
+ │ │ │ │ │ │ │ ├── open_tag:
+ │ │ │ │ │ │ │ │ └── @ HTMLOpenTagNode (location: (4:6)-(4:11))
+ │ │ │ │ │ │ │ │ ├── tag_opening: "<" (location: (4:6)-(4:7))
+ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (4:7)-(4:10))
+ │ │ │ │ │ │ │ │ ├── tag_closing: ">" (location: (4:10)-(4:11))
+ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ └── is_void: false
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (4:7)-(4:10))
+ │ │ │ │ │ │ │ ├── body: (3 items)
+ │ │ │ │ │ │ │ │ ├── @ HTMLTextNode (location: (4:11)-(5:8))
+ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ ├── @ HTMLElementNode (location: (5:8)-(21:14))
+ │ │ │ │ │ │ │ │ │ ├── open_tag:
+ │ │ │ │ │ │ │ │ │ │ └── @ HTMLOpenTagNode (location: (5:8)-(5:13))
+ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "<" (location: (5:8)-(5:9))
+ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (5:9)-(5:12))
+ │ │ │ │ │ │ │ │ │ │ ├── tag_closing: ">" (location: (5:12)-(5:13))
+ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ └── is_void: false
+ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (5:9)-(5:12))
+ │ │ │ │ │ │ │ │ │ ├── body: (3 items)
+ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLTextNode (location: (5:13)-(6:10))
+ │ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLElementNode (location: (6:10)-(20:16))
+ │ │ │ │ │ │ │ │ │ │ │ ├── open_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLOpenTagNode (location: (6:10)-(6:15))
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "<" (location: (6:10)-(6:11))
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (6:11)-(6:14))
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_closing: ">" (location: (6:14)-(6:15))
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ │ │ └── is_void: false
+ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (6:11)-(6:14))
+ │ │ │ │ │ │ │ │ │ │ │ ├── body: (3 items)
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLTextNode (location: (6:15)-(7:12))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLElementNode (location: (7:12)-(19:18))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── open_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLOpenTagNode (location: (7:12)-(7:17))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "<" (location: (7:12)-(7:13))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (7:13)-(7:16))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_closing: ">" (location: (7:16)-(7:17))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── is_void: false
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (7:13)-(7:16))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── body: (3 items)
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLTextNode (location: (7:17)-(8:14))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLElementNode (location: (8:14)-(18:20))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── open_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLOpenTagNode (location: (8:14)-(8:19))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "<" (location: (8:14)-(8:15))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (8:15)-(8:18))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_closing: ">" (location: (8:18)-(8:19))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── is_void: false
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (8:15)-(8:18))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── body: (3 items)
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLTextNode (location: (8:19)-(9:16))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLElementNode (location: (9:16)-(17:22))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── open_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLOpenTagNode (location: (9:16)-(9:21))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── [depth limit reached ...]
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (9:17)-(9:20))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── body: (3 items)
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLTextNode (location: (9:21)-(10:18))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── [depth limit reached ...]
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLElementNode (location: (10:18)-(16:24))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── [depth limit reached ...]
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLTextNode (location: (16:24)-(17:16))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── [depth limit reached ...]
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── close_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLCloseTagNode (location: (17:16)-(17:22))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── [depth limit reached ...]
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── is_void: false
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── source: "HTML"
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLTextNode (location: (17:22)-(18:14))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── close_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLCloseTagNode (location: (18:14)-(18:20))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "" (location: (18:14)-(18:16))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (18:16)-(18:19))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── tag_closing: ">" (location: (18:19)-(18:20))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── is_void: false
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── source: "HTML"
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLTextNode (location: (18:20)-(19:12))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── close_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLCloseTagNode (location: (19:12)-(19:18))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "" (location: (19:12)-(19:14))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (19:14)-(19:17))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── tag_closing: ">" (location: (19:17)-(19:18))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── is_void: false
+ │ │ │ │ │ │ │ │ │ │ │ │ │ └── source: "HTML"
+ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLTextNode (location: (19:18)-(20:10))
+ │ │ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ ├── close_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLCloseTagNode (location: (20:10)-(20:16))
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "" (location: (20:10)-(20:12))
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (20:12)-(20:15))
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ │ │ └── tag_closing: ">" (location: (20:15)-(20:16))
+ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ ├── is_void: false
+ │ │ │ │ │ │ │ │ │ │ │ └── source: "HTML"
+ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ └── @ HTMLTextNode (location: (20:16)-(21:8))
+ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ ├── close_tag:
+ │ │ │ │ │ │ │ │ │ │ └── @ HTMLCloseTagNode (location: (21:8)-(21:14))
+ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "" (location: (21:8)-(21:10))
+ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (21:10)-(21:13))
+ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ └── tag_closing: ">" (location: (21:13)-(21:14))
+ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ ├── is_void: false
+ │ │ │ │ │ │ │ │ │ └── source: "HTML"
+ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ └── @ HTMLTextNode (location: (21:14)-(22:6))
+ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ ├── close_tag:
+ │ │ │ │ │ │ │ │ └── @ HTMLCloseTagNode (location: (22:6)-(22:12))
+ │ │ │ │ │ │ │ │ ├── tag_opening: "" (location: (22:6)-(22:8))
+ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (22:8)-(22:11))
+ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ └── tag_closing: ">" (location: (22:11)-(22:12))
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ ├── is_void: false
+ │ │ │ │ │ │ │ └── source: "HTML"
+ │ │ │ │ │ │ │
+ │ │ │ │ │ │ └── @ HTMLTextNode (location: (22:12)-(23:4))
+ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │
+ │ │ │ │ │ ├── close_tag:
+ │ │ │ │ │ │ └── @ HTMLCloseTagNode (location: (23:4)-(23:10))
+ │ │ │ │ │ │ ├── tag_opening: "" (location: (23:4)-(23:6))
+ │ │ │ │ │ │ ├── tag_name: "div" (location: (23:6)-(23:9))
+ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ └── tag_closing: ">" (location: (23:9)-(23:10))
+ │ │ │ │ │ │
+ │ │ │ │ │ ├── is_void: false
+ │ │ │ │ │ └── source: "HTML"
+ │ │ │ │ │
+ │ │ │ │ └── @ HTMLTextNode (location: (23:10)-(24:2))
+ │ │ │ │ └── content: "\n "
+ │ │ │ │
+ │ │ │ ├── close_tag:
+ │ │ │ │ └── @ HTMLCloseTagNode (location: (24:2)-(24:8))
+ │ │ │ │ ├── tag_opening: "" (location: (24:2)-(24:4))
+ │ │ │ │ ├── tag_name: "div" (location: (24:4)-(24:7))
+ │ │ │ │ ├── children: []
+ │ │ │ │ └── tag_closing: ">" (location: (24:7)-(24:8))
+ │ │ │ │
+ │ │ │ ├── is_void: false
+ │ │ │ └── source: "HTML"
+ │ │ │
+ │ │ └── @ HTMLTextNode (location: (24:8)-(25:0))
+ │ │ └── content: "\n"
+ │ │
+ │ ├── close_tag:
+ │ │ └── @ HTMLCloseTagNode (location: (25:0)-(25:6))
+ │ │ ├── tag_opening: "" (location: (25:0)-(25:2))
+ │ │ ├── tag_name: "div" (location: (25:2)-(25:5))
+ │ │ ├── children: []
+ │ │ └── tag_closing: ">" (location: (25:5)-(25:6))
+ │ │
+ │ ├── is_void: false
+ │ └── source: "HTML"
+ │
+ └── @ HTMLTextNode (location: (25:6)-(26:0))
+ └── content: "\n"
\ No newline at end of file
diff --git a/test/snapshots/ast/tree_inspect_test/test_0002_deeply_nested_with_attributes_f06a9fd72ce823160a92ff8ec5433c9e.txt b/test/snapshots/ast/tree_inspect_test/test_0002_deeply_nested_with_attributes_f06a9fd72ce823160a92ff8ec5433c9e.txt
new file mode 100644
index 000000000..57ff62007
--- /dev/null
+++ b/test/snapshots/ast/tree_inspect_test/test_0002_deeply_nested_with_attributes_f06a9fd72ce823160a92ff8ec5433c9e.txt
@@ -0,0 +1,266 @@
+---
+source: "AST::TreeInspectTest#test_0002_deeply nested with attributes"
+input: |2-
+
+---
+@ DocumentNode (location: (1:0)-(20:0))
+└── children: (2 items)
+ ├── @ HTMLElementNode (location: (1:0)-(19:6))
+ │ ├── open_tag:
+ │ │ └── @ HTMLOpenTagNode (location: (1:0)-(1:5))
+ │ │ ├── tag_opening: "<" (location: (1:0)-(1:1))
+ │ │ ├── tag_name: "div" (location: (1:1)-(1:4))
+ │ │ ├── tag_closing: ">" (location: (1:4)-(1:5))
+ │ │ ├── children: []
+ │ │ └── is_void: false
+ │ │
+ │ ├── tag_name: "div" (location: (1:1)-(1:4))
+ │ ├── body: (3 items)
+ │ │ ├── @ HTMLTextNode (location: (1:5)-(2:2))
+ │ │ │ └── content: "\n "
+ │ │ │
+ │ │ ├── @ HTMLElementNode (location: (2:2)-(18:8))
+ │ │ │ ├── open_tag:
+ │ │ │ │ └── @ HTMLOpenTagNode (location: (2:2)-(2:7))
+ │ │ │ │ ├── tag_opening: "<" (location: (2:2)-(2:3))
+ │ │ │ │ ├── tag_name: "div" (location: (2:3)-(2:6))
+ │ │ │ │ ├── tag_closing: ">" (location: (2:6)-(2:7))
+ │ │ │ │ ├── children: []
+ │ │ │ │ └── is_void: false
+ │ │ │ │
+ │ │ │ ├── tag_name: "div" (location: (2:3)-(2:6))
+ │ │ │ ├── body: (3 items)
+ │ │ │ │ ├── @ HTMLTextNode (location: (2:7)-(3:4))
+ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │
+ │ │ │ │ ├── @ HTMLElementNode (location: (3:4)-(17:10))
+ │ │ │ │ │ ├── open_tag:
+ │ │ │ │ │ │ └── @ HTMLOpenTagNode (location: (3:4)-(3:9))
+ │ │ │ │ │ │ ├── tag_opening: "<" (location: (3:4)-(3:5))
+ │ │ │ │ │ │ ├── tag_name: "div" (location: (3:5)-(3:8))
+ │ │ │ │ │ │ ├── tag_closing: ">" (location: (3:8)-(3:9))
+ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ └── is_void: false
+ │ │ │ │ │ │
+ │ │ │ │ │ ├── tag_name: "div" (location: (3:5)-(3:8))
+ │ │ │ │ │ ├── body: (3 items)
+ │ │ │ │ │ │ ├── @ HTMLTextNode (location: (3:9)-(4:6))
+ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │
+ │ │ │ │ │ │ ├── @ HTMLElementNode (location: (4:6)-(16:12))
+ │ │ │ │ │ │ │ ├── open_tag:
+ │ │ │ │ │ │ │ │ └── @ HTMLOpenTagNode (location: (4:6)-(4:11))
+ │ │ │ │ │ │ │ │ ├── tag_opening: "<" (location: (4:6)-(4:7))
+ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (4:7)-(4:10))
+ │ │ │ │ │ │ │ │ ├── tag_closing: ">" (location: (4:10)-(4:11))
+ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ └── is_void: false
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (4:7)-(4:10))
+ │ │ │ │ │ │ │ ├── body: (3 items)
+ │ │ │ │ │ │ │ │ ├── @ HTMLTextNode (location: (4:11)-(5:8))
+ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ ├── @ HTMLElementNode (location: (5:8)-(15:14))
+ │ │ │ │ │ │ │ │ │ ├── open_tag:
+ │ │ │ │ │ │ │ │ │ │ └── @ HTMLOpenTagNode (location: (5:8)-(5:13))
+ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "<" (location: (5:8)-(5:9))
+ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (5:9)-(5:12))
+ │ │ │ │ │ │ │ │ │ │ ├── tag_closing: ">" (location: (5:12)-(5:13))
+ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ └── is_void: false
+ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (5:9)-(5:12))
+ │ │ │ │ │ │ │ │ │ ├── body: (3 items)
+ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLTextNode (location: (5:13)-(6:10))
+ │ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLElementNode (location: (6:10)-(14:16))
+ │ │ │ │ │ │ │ │ │ │ │ ├── open_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLOpenTagNode (location: (6:10)-(6:15))
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "<" (location: (6:10)-(6:11))
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (6:11)-(6:14))
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_closing: ">" (location: (6:14)-(6:15))
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ │ │ └── is_void: false
+ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (6:11)-(6:14))
+ │ │ │ │ │ │ │ │ │ │ │ ├── body: (3 items)
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLTextNode (location: (6:15)-(7:12))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLElementNode (location: (7:12)-(13:18))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── open_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLOpenTagNode (location: (7:12)-(7:17))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "<" (location: (7:12)-(7:13))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (7:13)-(7:16))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_closing: ">" (location: (7:16)-(7:17))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── is_void: false
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (7:13)-(7:16))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── body: (3 items)
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLTextNode (location: (7:17)-(8:14))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLElementNode (location: (8:14)-(12:20))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── open_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLOpenTagNode (location: (8:14)-(8:19))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "<" (location: (8:14)-(8:15))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (8:15)-(8:18))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_closing: ">" (location: (8:18)-(8:19))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── is_void: false
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (8:15)-(8:18))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── body: (3 items)
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLTextNode (location: (8:19)-(9:16))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLElementNode (location: (9:16)-(11:22))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── open_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLOpenTagNode (location: (9:16)-(9:72))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── [depth limit reached ...]
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (9:17)-(9:20))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── body: (3 items)
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLTextNode (location: (9:72)-(10:18))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── [depth limit reached ...]
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── @ HTMLElementNode (location: (10:18)-(10:45))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── [depth limit reached ...]
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLTextNode (location: (10:45)-(11:16))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── [depth limit reached ...]
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── close_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLCloseTagNode (location: (11:16)-(11:22))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── [depth limit reached ...]
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── is_void: false
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── source: "HTML"
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLTextNode (location: (11:22)-(12:14))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── close_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLCloseTagNode (location: (12:14)-(12:20))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "" (location: (12:14)-(12:16))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (12:16)-(12:19))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── tag_closing: ">" (location: (12:19)-(12:20))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── is_void: false
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── source: "HTML"
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLTextNode (location: (12:20)-(13:12))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── close_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLCloseTagNode (location: (13:12)-(13:18))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "" (location: (13:12)-(13:14))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (13:14)-(13:17))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── tag_closing: ">" (location: (13:17)-(13:18))
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── is_void: false
+ │ │ │ │ │ │ │ │ │ │ │ │ │ └── source: "HTML"
+ │ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLTextNode (location: (13:18)-(14:10))
+ │ │ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ ├── close_tag:
+ │ │ │ │ │ │ │ │ │ │ │ │ └── @ HTMLCloseTagNode (location: (14:10)-(14:16))
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "" (location: (14:10)-(14:12))
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (14:12)-(14:15))
+ │ │ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ │ │ └── tag_closing: ">" (location: (14:15)-(14:16))
+ │ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ │ ├── is_void: false
+ │ │ │ │ │ │ │ │ │ │ │ └── source: "HTML"
+ │ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ │ └── @ HTMLTextNode (location: (14:16)-(15:8))
+ │ │ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ ├── close_tag:
+ │ │ │ │ │ │ │ │ │ │ └── @ HTMLCloseTagNode (location: (15:8)-(15:14))
+ │ │ │ │ │ │ │ │ │ │ ├── tag_opening: "" (location: (15:8)-(15:10))
+ │ │ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (15:10)-(15:13))
+ │ │ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ │ │ └── tag_closing: ">" (location: (15:13)-(15:14))
+ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ │ ├── is_void: false
+ │ │ │ │ │ │ │ │ │ └── source: "HTML"
+ │ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ │ └── @ HTMLTextNode (location: (15:14)-(16:6))
+ │ │ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ ├── close_tag:
+ │ │ │ │ │ │ │ │ └── @ HTMLCloseTagNode (location: (16:6)-(16:12))
+ │ │ │ │ │ │ │ │ ├── tag_opening: "" (location: (16:6)-(16:8))
+ │ │ │ │ │ │ │ │ ├── tag_name: "div" (location: (16:8)-(16:11))
+ │ │ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ │ │ └── tag_closing: ">" (location: (16:11)-(16:12))
+ │ │ │ │ │ │ │ │
+ │ │ │ │ │ │ │ ├── is_void: false
+ │ │ │ │ │ │ │ └── source: "HTML"
+ │ │ │ │ │ │ │
+ │ │ │ │ │ │ └── @ HTMLTextNode (location: (16:12)-(17:4))
+ │ │ │ │ │ │ └── content: "\n "
+ │ │ │ │ │ │
+ │ │ │ │ │ ├── close_tag:
+ │ │ │ │ │ │ └── @ HTMLCloseTagNode (location: (17:4)-(17:10))
+ │ │ │ │ │ │ ├── tag_opening: "" (location: (17:4)-(17:6))
+ │ │ │ │ │ │ ├── tag_name: "div" (location: (17:6)-(17:9))
+ │ │ │ │ │ │ ├── children: []
+ │ │ │ │ │ │ └── tag_closing: ">" (location: (17:9)-(17:10))
+ │ │ │ │ │ │
+ │ │ │ │ │ ├── is_void: false
+ │ │ │ │ │ └── source: "HTML"
+ │ │ │ │ │
+ │ │ │ │ └── @ HTMLTextNode (location: (17:10)-(18:2))
+ │ │ │ │ └── content: "\n "
+ │ │ │ │
+ │ │ │ ├── close_tag:
+ │ │ │ │ └── @ HTMLCloseTagNode (location: (18:2)-(18:8))
+ │ │ │ │ ├── tag_opening: "" (location: (18:2)-(18:4))
+ │ │ │ │ ├── tag_name: "div" (location: (18:4)-(18:7))
+ │ │ │ │ ├── children: []
+ │ │ │ │ └── tag_closing: ">" (location: (18:7)-(18:8))
+ │ │ │ │
+ │ │ │ ├── is_void: false
+ │ │ │ └── source: "HTML"
+ │ │ │
+ │ │ └── @ HTMLTextNode (location: (18:8)-(19:0))
+ │ │ └── content: "\n"
+ │ │
+ │ ├── close_tag:
+ │ │ └── @ HTMLCloseTagNode (location: (19:0)-(19:6))
+ │ │ ├── tag_opening: "" (location: (19:0)-(19:2))
+ │ │ ├── tag_name: "div" (location: (19:2)-(19:5))
+ │ │ ├── children: []
+ │ │ └── tag_closing: ">" (location: (19:5)-(19:6))
+ │ │
+ │ ├── is_void: false
+ │ └── source: "HTML"
+ │
+ └── @ HTMLTextNode (location: (19:6)-(20:0))
+ └── content: "\n"
\ No newline at end of file