Skip to content

Commit

Permalink
[ruby/prism] Split up comments between leading and trailing
Browse files Browse the repository at this point in the history
Also make them lazy to allocate the array, and also expose ParseResult#encoding.

ruby/prism@08ec7683ae
  • Loading branch information
kddnewton authored and matzbot committed Feb 18, 2024
1 parent 07c774e commit 792804e
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 18 deletions.
54 changes: 50 additions & 4 deletions lib/prism/parse_result.rb
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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?
Expand Down
35 changes: 22 additions & 13 deletions lib/prism/parse_result/comments.rb
Expand Up @@ -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

Expand All @@ -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

Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion test/prism/magic_comment_test.rb
Expand Up @@ -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
Expand Down

0 comments on commit 792804e

Please sign in to comment.