From 792804e32f7aaa1008acd01068df3c0b85cd8ffe Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Sun, 18 Feb 2024 15:14:46 -0500 Subject: [PATCH] [ruby/prism] Split up comments between leading and trailing Also make them lazy to allocate the array, and also expose ParseResult#encoding. https://github.com/ruby/prism/commit/08ec7683ae --- lib/prism/parse_result.rb | 54 +++++++++++++++++++++++++++--- lib/prism/parse_result/comments.rb | 35 ++++++++++++------- test/prism/magic_comment_test.rb | 2 +- 3 files changed, 73 insertions(+), 18 deletions(-) diff --git a/lib/prism/parse_result.rb b/lib/prism/parse_result.rb index 8ba4e195e6236e..88f068e62acbe9 100644 --- a/lib/prism/parse_result.rb +++ b/lib/prism/parse_result.rb @@ -21,6 +21,12 @@ def initialize(source, start_line = 1, offsets = []) @offsets = offsets # set after parsing is done end + # Returns the encoding of the source code, which is set by parameters to the + # parser or by the encoding magic comment. + def encoding + source.encoding + end + # Perform a byteslice on the source code using the given byte offset and # byte length. def slice(byte_offset, length) @@ -108,16 +114,46 @@ class Location # The length of this location in bytes. attr_reader :length - # The list of comments attached to this location - attr_reader :comments - # Create a new location object with the given source, start byte offset, and # byte length. def initialize(source, start_offset, length) @source = source @start_offset = start_offset @length = length - @comments = [] + + # These are used to store comments that are associated with this location. + # They are initialized to `nil` to save on memory when there are no + # comments to be attached and/or the comment-related APIs are not used. + @leading_comments = nil + @trailing_comments = nil + end + + # These are the comments that are associated with this location that exist + # before the start of this location. + def leading_comments + @leading_comments ||= [] + end + + # Attach a comment to the leading comments of this location. + def leading_comment(comment) + leading_comments << comment + end + + # These are the comments that are associated with this location that exist + # after the end of this location. + def trailing_comments + @trailing_comments ||= [] + end + + # Attach a comment to the trailing comments of this location. + def trailing_comment(comment) + trailing_comments << comment + end + + # Returns all comments that are associated with this location (both leading + # and trailing comments). + def comments + (@leading_comments || []).concat(@trailing_comments || []) end # Create a new location object with the given options. @@ -268,6 +304,11 @@ def initialize(location) def deconstruct_keys(keys) { location: location } end + + # Returns the content of the comment by slicing it from the source code. + def slice + location.slice + end end # InlineComment objects are the most common. They correspond to comments in @@ -437,6 +478,11 @@ def deconstruct_keys(keys) { value: value, comments: comments, magic_comments: magic_comments, data_loc: data_loc, errors: errors, warnings: warnings } end + # Returns the encoding of the source code that was parsed. + def encoding + source.encoding + end + # Returns true if there were no errors during parsing and false if there # were. def success? diff --git a/lib/prism/parse_result/comments.rb b/lib/prism/parse_result/comments.rb index 7a3a47de50b06c..26775b7f766418 100644 --- a/lib/prism/parse_result/comments.rb +++ b/lib/prism/parse_result/comments.rb @@ -39,8 +39,12 @@ def encloses?(comment) comment.location.end_offset <= end_offset end - def <<(comment) - node.location.comments << comment + def leading_comment(comment) + node.location.leading_comment(comment) + end + + def trailing_comment(comment) + node.location.trailing_comment(comment) end end @@ -65,8 +69,12 @@ def encloses?(comment) false end - def <<(comment) - location.comments << comment + def leading_comment(comment) + location.leading_comment(comment) + end + + def trailing_comment(comment) + location.trailing_comment(comment) end end @@ -84,15 +92,16 @@ def initialize(parse_result) def attach! parse_result.comments.each do |comment| preceding, enclosing, following = nearest_targets(parse_result.value, comment) - target = - if comment.trailing? - preceding || following || enclosing || NodeTarget.new(parse_result.value) - else - # If a comment exists on its own line, prefer a leading comment. - following || preceding || enclosing || NodeTarget.new(parse_result.value) - end - - target << comment + + if comment.trailing? + preceding&.trailing_comment(comment) || + (following || enclosing || NodeTarget.new(parse_result.value)).leading_comment(comment) + else + # If a comment exists on its own line, prefer a leading comment. + following&.leading_comment(comment) || + preceding&.trailing_comment(comment) || + (enclosing || NodeTarget.new(parse_result.value)).leading_comment(comment) + end end end diff --git a/test/prism/magic_comment_test.rb b/test/prism/magic_comment_test.rb index 812109f2bbcf9d..5e232c2d004cd1 100644 --- a/test/prism/magic_comment_test.rb +++ b/test/prism/magic_comment_test.rb @@ -30,7 +30,7 @@ class MagicCommentTest < TestCase def assert_magic_comment(example) expected = Ripper.new(example).tap(&:parse).encoding - actual = Prism.parse(example).source.source.encoding + actual = Prism.parse(example).encoding assert_equal expected, actual end end