Skip to content

Commit

Permalink
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.
  • Loading branch information
kddnewton committed Feb 18, 2024
1 parent f692feb commit 08ec768
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 08ec768

Please sign in to comment.