class Prism::ParseResult::Comments
当我们解析完源代码时,我们既有语法树,也有源代码中找到的注释列表。这个类的职责是遍历语法树,找到每个注释要附加的最近位置。
它通过首先找到每个注释的最近位置来实现这一点。位置可以直接来自节点,也可以来自节点上的位置字段。例如,一个 ‘ClassNode` 拥有一个包含整个类的总体位置,但它也拥有 `class` 关键字的位置。
一旦找到最近的位置,它会确定要附加到哪个位置。如果它是一个尾随注释(与其它源代码在同一行的注释),它将倾向于附加到注释之前出现的最近位置。否则,它将倾向于附加到注释之后的最近位置。
属性
parse_result[R]
我们要将注释附加到的解析结果。
公共类方法
new(parse_result) 点击切换源代码
创建一个新的 Comments
对象,该对象会将注释附加到给定的解析结果。
# File prism/parse_result/comments.rb, line 86 def initialize(parse_result) @parse_result = parse_result end
公共实例方法
attach!() 点击切换源代码
通过改变解析结果,将注释附加到树中各自的位置。
# File prism/parse_result/comments.rb, line 92 def attach! parse_result.comments.each do |comment| preceding, enclosing, following = nearest_targets(parse_result.value, comment) if comment.trailing? if preceding preceding.trailing_comment(comment) else (following || enclosing || NodeTarget.new(parse_result.value)).leading_comment(comment) end else # If a comment exists on its own line, prefer a leading comment. if following following.leading_comment(comment) elsif preceding preceding.trailing_comment(comment) else (enclosing || NodeTarget.new(parse_result.value)).leading_comment(comment) end end end end
私有实例方法
nearest_targets(node, comment) 点击切换源代码
负责在给定封装节点的上下文中查找给定注释的最近目标。
# File prism/parse_result/comments.rb, line 119 def nearest_targets(node, comment) comment_start = comment.location.start_offset comment_end = comment.location.end_offset targets = [] #: Array[_Target] 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 #: _Target? following = nil #: _Target? 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) # @type var target: NodeTarget # 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