diff --git a/lib/syntax_tree/node.rb b/lib/syntax_tree/node.rb index 85956c8c..58335b00 100644 --- a/lib/syntax_tree/node.rb +++ b/lib/syntax_tree/node.rb @@ -4813,7 +4813,7 @@ class Heredoc < Node # [HeredocBeg] the opening of the heredoc attr_reader :beginning - # [String] the ending of the heredoc + # [HeredocEnd] the ending of the heredoc attr_reader :ending # [Integer] how far to dedent the heredoc @@ -4847,7 +4847,7 @@ def accept(visitor) end def child_nodes - [beginning, *parts] + [beginning, *parts, ending] end alias deconstruct child_nodes @@ -4883,7 +4883,7 @@ def format(q) end end - q.text(ending) + q.format(ending) end end end @@ -4929,6 +4929,45 @@ def format(q) end end + # HeredocEnd represents the closing declaration of a heredoc. + # + # <<~DOC + # contents + # DOC + # + # In the example above the HeredocEnd node represents the closing DOC. + class HeredocEnd < Node + # [String] the closing declaration of the heredoc + attr_reader :value + + # [Array[ Comment | EmbDoc ]] the comments attached to this node + attr_reader :comments + + def initialize(value:, location:, comments: []) + @value = value + @location = location + @comments = comments + end + + def accept(visitor) + visitor.visit_heredoc_end(self) + end + + def child_nodes + [] + end + + alias deconstruct child_nodes + + def deconstruct_keys(_keys) + { value: value, location: location, comments: comments } + end + + def format(q) + q.text(value) + end + end + # HshPtn represents matching against a hash pattern using the Ruby 2.7+ # pattern matching syntax. # diff --git a/lib/syntax_tree/parser.rb b/lib/syntax_tree/parser.rb index fdffbeb9..0f8332b1 100644 --- a/lib/syntax_tree/parser.rb +++ b/lib/syntax_tree/parser.rb @@ -1640,9 +1640,19 @@ def on_heredoc_dedent(string, width) def on_heredoc_end(value) heredoc = @heredocs[-1] + location = + Location.token( + line: lineno, + char: char_pos, + column: current_column, + size: value.size + 1 + ) + + heredoc_end = HeredocEnd.new(value: value.chomp, location: location) + @heredocs[-1] = Heredoc.new( beginning: heredoc.beginning, - ending: value.chomp, + ending: heredoc_end, dedent: heredoc.dedent, parts: heredoc.parts, location: diff --git a/lib/syntax_tree/visitor.rb b/lib/syntax_tree/visitor.rb index 348a05a2..e3b52077 100644 --- a/lib/syntax_tree/visitor.rb +++ b/lib/syntax_tree/visitor.rb @@ -194,6 +194,9 @@ class Visitor < BasicVisitor # Visit a HeredocBeg node. alias visit_heredoc_beg visit_child_nodes + # Visit a HeredocEnd node. + alias visit_heredoc_end visit_child_nodes + # Visit a HshPtn node. alias visit_hshptn visit_child_nodes diff --git a/lib/syntax_tree/visitor/field_visitor.rb b/lib/syntax_tree/visitor/field_visitor.rb index 1cc74f3d..6c5c6139 100644 --- a/lib/syntax_tree/visitor/field_visitor.rb +++ b/lib/syntax_tree/visitor/field_visitor.rb @@ -497,6 +497,10 @@ def visit_heredoc_beg(node) visit_token(node, "heredoc_beg") end + def visit_heredoc_end(node) + visit_token(node, "heredoc_end") + end + def visit_hshptn(node) node(node, "hshptn") do field("constant", node.constant) if node.constant diff --git a/test/node_test.rb b/test/node_test.rb index ffd00fa5..30776f9d 100644 --- a/test/node_test.rb +++ b/test/node_test.rb @@ -546,6 +546,17 @@ def test_heredoc_beg assert_node(HeredocBeg, source, at: at, &:beginning) end + def test_heredoc_end + source = <<~SOURCE + <<~HEREDOC + contents + HEREDOC + SOURCE + + at = location(lines: 3..3, chars: 22..31, columns: 0..9) + assert_node(HeredocEnd, source, at: at, &:ending) + end + def test_hshptn source = <<~SOURCE case value