class Parser::Source::Comment::Associator


@api public
@return [Boolean]
* Magic encoding comment.
* Shebang line,
Namely:
Skip file processing directives disguised as comments.
@!attribute skip_directives
@see {associate}
# }
# #<Parser::Source::Comment (string):4:3 “# @see bar”>]
# [#<Parser::Source::Comment (string):3:3 “# Attr stuff”>,
# (send nil :attr_accessor (sym :foo)) =>
# [#<Parser::Source::Comment (string):1:1 “# Class stuff”>],
# (class (const nil :Foo) …) =>
# => {
p Parser::Source::Comment.associate(ast, comments)
CODE
end
attr_accessor :foo
# @see bar
# Attr stuff
class Foo
# Class stuff
ast, comments = Parser::CurrentRuby.parse_with_comments(<<-CODE)
require ‘parser/current’
@example
rdoc-style processing.
location in source code. It may be used, for example, to implement
A processor which associates AST nodes with comments based on their
#

def advance_comment

def advance_comment
  @comment_num += 1
  @current_comment = @comments[@comment_num]
end

def advance_through_directives

def advance_through_directives
  # Skip shebang.
  if @current_comment && @current_comment.text.start_with?('#!'.freeze)
    advance_comment
  end
  # Skip magic comments.
  if @current_comment && @current_comment.text =~ MAGIC_COMMENT_RE
    advance_comment
  end
  # Skip encoding line.
  if @current_comment && @current_comment.text =~ Buffer::ENCODING_RE
    advance_comment
  end
end

def associate

Deprecated:
  • Use {associate_locations}.

Returns:
  • (Hash>) -
def associate
  @map_using = :eql
  do_associate
end

def associate_and_advance_comment(node)

def associate_and_advance_comment(node)
  key = @map_using == :location ? node.location : node
  @mapping[key] << @current_comment
  advance_comment
end

def associate_by_identity

Returns:
  • (Hash>) -
def associate_by_identity
  @map_using = :identity
  do_associate
end

def associate_locations

Returns:
  • (Hash>) -
def associate_locations
  @map_using = :location
  do_associate
end

def children_in_source_order(node)

def children_in_source_order(node)
  if POSTFIX_TYPES.include?(node.type)
    # All these types have either nodes with expressions, or `nil`
    # so a compact will do, but they need to be sorted.
    node.children.compact.sort_by { |child| child.loc.expression.begin_pos }
  else
    node.children.select do |child|
      child.is_a?(AST::Node) && child.loc && child.loc.expression
    end
  end
end

def current_comment_before?(node)

def current_comment_before?(node)
  return false if !@current_comment
  comment_loc = @current_comment.location.expression
  node_loc = node.location.expression
  comment_loc.end_pos <= node_loc.begin_pos
end

def current_comment_before_end?(node)

def current_comment_before_end?(node)
  return false if !@current_comment
  comment_loc = @current_comment.location.expression
  node_loc = node.location.expression
  comment_loc.end_pos <= node_loc.end_pos
end

def current_comment_decorates?(node)

def current_comment_decorates?(node)
  return false if !@current_comment
  @current_comment.location.line == node.location.last_line
end

def do_associate

def do_associate
  @mapping     = Hash.new { |h, k| h[k] = [] }
  @mapping.compare_by_identity if @map_using == :identity
  @comment_num = -1
  advance_comment
  advance_through_directives if @skip_directives
  visit(@ast) if @ast
  @mapping
end

def initialize(ast, comments)

Parameters:
  • comments (Array) --
  • ast (Parser::AST::Node) --
def initialize(ast, comments)
  @ast         = ast
  @comments    = comments
  @skip_directives = true
end

def process_leading_comments(node)

def process_leading_comments(node)
  return if node.type == :begin
  while current_comment_before?(node) # preceding comment
    associate_and_advance_comment(node)
  end
end

def process_trailing_comments(node)

def process_trailing_comments(node)
  while current_comment_before_end?(node)
    associate_and_advance_comment(node) # sparse comment
  end
  while current_comment_decorates?(node)
    associate_and_advance_comment(node) # decorating comment
  end
end

def visit(node)

def visit(node)
  process_leading_comments(node)
  return unless @current_comment
  # If the next comment is beyond the last line of this node, we don't
  # need to iterate over its subnodes
  # (Unless this node is a heredoc... there could be a comment in its body,
  # inside an interpolation)
  node_loc = node.location
  if @current_comment.location.line <= node_loc.last_line ||
     node_loc.is_a?(Map::Heredoc)
    children_in_source_order(node).each { |child| visit(child) }
    process_trailing_comments(node)
  end
end