class SyntaxTree::Statements
all comments get printed appropriately.
propagate that onto void_stmt nodes inside the stmts in order to make sure
stmts nodes will report back down the location information. We then
need to have the right location information, so all of the parent node of
have some special handling in order to handle empty statement lists. They
Normally we would just track those as a node that has an array body, but we
Everything that has a block of code inside of it has a list of statements.
def ===(other)
def ===(other) other.is_a?(Statements) && ArrayMatch.call(body, other.body) end
def accept(visitor)
def accept(visitor) visitor.visit_statements(self) end
def attach_comments(parser, start_char, end_char)
found while this statements list was being parsed and add them into the
As efficiently as possible, gather up all of the comments that have been
def attach_comments(parser, start_char, end_char) parser_comments = parser.comments comment_index = 0 body_index = 0 while comment_index < parser_comments.size comment = parser_comments[comment_index] location = comment.location if !comment.inline? && (start_char <= location.start_char) && (end_char >= location.end_char) && !comment.ignore? while (node = body[body_index]) && ( node.is_a?(VoidStmt) || node.location.start_char < location.start_char ) body_index += 1 end if body_index != 0 && body[body_index - 1].location.start_char < location.start_char && body[body_index - 1].location.end_char > location.start_char # The previous node entirely encapsules the comment, so we don't # want to attach it here since it will get attached normally. This # is mostly in the case of hash and array literals. comment_index += 1 else parser_comments.delete_at(comment_index) body.insert(body_index, comment) end else comment_index += 1 end end end
def bind(parser, start_char, start_column, end_char, end_column)
def bind(parser, start_char, start_column, end_char, end_column) @location = Location.new( start_line: location.start_line, start_char: start_char, start_column: start_column, end_line: location.end_line, end_char: end_char, end_column: end_column ) if (void_stmt = body[0]).is_a?(VoidStmt) location = void_stmt.location location = Location.new( start_line: location.start_line, start_char: start_char, start_column: start_column, end_line: location.end_line, end_char: start_char, end_column: end_column ) body[0] = VoidStmt.new(location: location) end attach_comments(parser, start_char, end_char) end
def bind_end(end_char, end_column)
def bind_end(end_char, end_column) @location = Location.new( start_line: location.start_line, start_char: location.start_char, start_column: location.start_column, end_line: location.end_line, end_char: end_char, end_column: end_column ) end
def child_nodes
def child_nodes body end
def copy(body: nil, location: nil)
def copy(body: nil, location: nil) node = Statements.new( body: body || self.body, location: location || self.location ) node.comments.concat(comments.map(&:copy)) node end
def deconstruct_keys(_keys)
def deconstruct_keys(_keys) { body: body, location: location, comments: comments } end
def empty?
def empty? body.all? do |statement| statement.is_a?(VoidStmt) && statement.comments.empty? end end
def format(q)
def format(q) line = nil # This handles a special case where you've got a block of statements where # the only value is a comment. In that case a lot of nodes like # brace_block will attempt to format as a single line, but since that # wouldn't work with a comment, we intentionally break the parent group. if body.length == 2 void_stmt, comment = body if void_stmt.is_a?(VoidStmt) && comment.is_a?(Comment) q.format(comment) q.break_parent return end end previous = nil body.each do |statement| next if statement.is_a?(VoidStmt) if line.nil? q.format(statement) elsif (statement.location.start_line - line) > 1 q.breakable_force q.breakable_force q.format(statement) elsif (statement.is_a?(VCall) && statement.access_control?) || (previous.is_a?(VCall) && previous.access_control?) q.breakable_force q.breakable_force q.format(statement) elsif statement.location.start_line != line q.breakable_force q.format(statement) elsif !q.parent.is_a?(StringEmbExpr) q.breakable_force q.format(statement) else q.text("; ") q.format(statement) end line = statement.location.end_line previous = statement end end
def initialize(body:, location:)
def initialize(body:, location:) @body = body @location = location @comments = [] end