class Prism::ParseResult::Comments - Documentation for Ruby 3.3

# class Prism::ParseResult::Comments

When we’ve parsed the source, we have both the syntax tree and the list of comments that we found in the source. This class is responsible for walking the tree and finding the nearest location to attach each comment.

It does this by first finding the nearest locations to each comment. Locations can either come from nodes directly or from location fields on nodes. For example, a ‘ClassNode` has an overall location encompassing the entire class, but it also has a location for the `class` keyword.

Once the nearest locations are found, it determines which one to attach to. If it’s a trailing comment (a comment on the same line as other source code), it will favor attaching to the nearest location that occurs before the comment. Otherwise it will favor attaching to the nearest location that is after the comment.

### Attributes

parse_result[R]

The parse result that we are attaching comments to.

### Public Class Methods

new(parse_result) click to toggle source

Create a new `Comments` object that will attach comments to the given parse result.

    # File lib/prism/parse_result/comments.rb, line 78
    def initialize(parse_result)
      @parse_result = parse_result
    end

### Public Instance Methods

attach!() click to toggle source

Attach the comments to their respective locations in the tree by mutating the parse result.

    # File lib/prism/parse_result/comments.rb, line 84
    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
      end
    end

### Private Instance Methods

nearest_targets(node, comment) click to toggle source

Responsible for finding the nearest targets to the given comment within the context of the given encapsulating node.

    # File lib/prism/parse_result/comments.rb, line 103
    def nearest_targets(node, comment)
      comment_start = comment.location.start_offset
      comment_end = comment.location.end_offset

      targets = []
      node.comment_targets.map do |value|
        case value
        when StatementsNode
          targets.concat(value.body.map { |node| NodeTarget.new(node) })
        when Node
          targets << NodeTarget.new(value)
        when Location
          targets << LocationTarget.new(value)
        end
      end

      targets.sort_by!(&:start_offset)
      preceding = nil
      following = nil

      left = 0
      right = targets.length

      # This is a custom binary search that finds the nearest nodes to the
      # given comment. When it finds a node that completely encapsulates the
      # comment, it recurses downward into the tree.
      while left < right
        middle = (left + right) / 2
        target = targets[middle]

        target_start = target.start_offset
        target_end = target.end_offset

        if target.encloses?(comment)
          # The comment is completely contained by this target. Abandon the
          # binary search at this level.
          return nearest_targets(target.node, comment)
        end

        if target_end <= comment_start
          # This target falls completely before the comment. Because we will
          # never consider this target or any targets before it again, this
          # target must be the closest preceding target we have encountered so
          # far.
          preceding = target
          left = middle + 1
          next
        end

        if comment_end <= target_start
          # This target falls completely after the comment. Because we will
          # never consider this target or any targets after it again, this
          # target must be the closest following target we have encountered so
          # far.
          following = target
          right = middle
          next
        end

        # This should only happen if there is a bug in this parser.
        raise "Comment location overlaps with a target location"
      end

      [preceding, NodeTarget.new(node), following]
    end