class Ruby::Prism::ParseResult::Newlines

The :line tracepoint event gets fired whenever the Ruby VM encounters an expression on a new line. The types of expressions that can trigger this event are:

  • if statements

  • unless statements

  • nodes that are children of statements lists

In order to keep track of the newlines, we have a list of offsets that come back from the parser. We assign these offsets to the first nodes that we find in the tree that are on those lines.

Note that the logic in this file should be kept in sync with the Java MarkNewlinesVisitor, since that visitor is responsible for marking the newlines for JRuby/TruffleRuby.

This file is autoloaded only when mark_newlines! is called, so the re-opening of the various nodes in this file will only be performed in that case. We do that to avoid storing the extra @newline instance variable on every node if we don’t need it.

Public Class Methods

Create a new Newlines visitor with the given newline offsets.

: (Integer lines) -> void

# File lib/ruby/prism/parse_result/newlines.rb, line 36
def initialize(lines)
  @lines = Array.new(1 + lines, false)
end

Public Instance Methods

Permit block nodes to mark newlines within themselves.

: (BlockNode node) -> void

Calls superclass method
# File lib/ruby/prism/parse_result/newlines.rb, line 43
def visit_block_node(node)
  old_lines = @lines
  @lines = Array.new(old_lines.size, false)

  begin
    super(node)
  ensure
    @lines = old_lines
  end
end

Permit class nodes to mark newlines within themselves.

: (ClassNode node) -> void

Calls superclass method
# File lib/ruby/prism/parse_result/newlines.rb, line 88
def visit_class_node(node)
  old_lines = @lines
  @lines = Array.new(old_lines.size, false)

  begin
    super(node)
  ensure
    @lines = old_lines
  end
end

Permit def nodes to mark newlines within themselves. The body of an endless method definition never emits newline events, so in that case mark every line as already seen while visiting it instead. Nested scopes (blocks, lambdas, etc.) reset the lines and emit events again.

: (DefNode node) -> void

Calls superclass method
# File lib/ruby/prism/parse_result/newlines.rb, line 74
def visit_def_node(node)
  old_lines = @lines
  @lines = Array.new(old_lines.size, !node.equal_loc.nil?)

  begin
    super(node)
  ensure
    @lines = old_lines
  end
end

Statements inside string interpolation do not emit newline events, so mark every line as already seen while visiting them. Nested scopes (blocks, lambdas, defs, etc.) reset the lines and emit events again.

: (EmbeddedStatementsNode node) -> void

Calls superclass method
# File lib/ruby/prism/parse_result/newlines.rb, line 132
def visit_embedded_statements_node(node)
  old_lines = @lines
  @lines = Array.new(old_lines.size, true)

  begin
    super(node)
  ensure
    @lines = old_lines
  end
end

Mark if nodes as newlines.

: (IfNode node) -> void

Calls superclass method
# File lib/ruby/prism/parse_result/newlines.rb, line 182
def visit_if_node(node)
  node.newline_flag!(@lines)
  super(node)
end

Permit lambda nodes to mark newlines within themselves.

: (LambdaNode node) -> void

Calls superclass method
# File lib/ruby/prism/parse_result/newlines.rb, line 57
def visit_lambda_node(node)
  old_lines = @lines
  @lines = Array.new(old_lines.size, false)

  begin
    super(node)
  ensure
    @lines = old_lines
  end
end

Permit module nodes to mark newlines within themselves.

: (ModuleNode node) -> void

Calls superclass method
# File lib/ruby/prism/parse_result/newlines.rb, line 102
def visit_module_node(node)
  old_lines = @lines
  @lines = Array.new(old_lines.size, false)

  begin
    super(node)
  ensure
    @lines = old_lines
  end
end

Permit singleton class nodes to mark newlines within themselves.

: (SingletonClassNode node) -> void

Calls superclass method
# File lib/ruby/prism/parse_result/newlines.rb, line 116
def visit_singleton_class_node(node)
  old_lines = @lines
  @lines = Array.new(old_lines.size, false)

  begin
    super(node)
  ensure
    @lines = old_lines
  end
end

Permit statements lists to mark newlines within themselves.

: (StatementsNode node) -> void

Calls superclass method
# File lib/ruby/prism/parse_result/newlines.rb, line 198
def visit_statements_node(node)
  node.body.each do |child|
    child.newline_flag!(@lines)
  end
  super(node)
end

Mark unless nodes as newlines.

: (UnlessNode node) -> void

Calls superclass method
# File lib/ruby/prism/parse_result/newlines.rb, line 190
def visit_unless_node(node)
  node.newline_flag!(@lines)
  super(node)
end

The predicate of an until loop is compiled at the end of the loop, after the body, so any statements it contains (from parentheses) emit their line events again even if the lines were already seen.

: (UntilNode node) -> void

# File lib/ruby/prism/parse_result/newlines.rb, line 166
def visit_until_node(node)
  old_lines = @lines
  @lines = Array.new(old_lines.size, false)

  begin
    visit(node.predicate)
  ensure
    @lines = old_lines
  end

  visit(node.statements)
end

The predicate of a while loop is compiled at the end of the loop, after the body, so any statements it contains (from parentheses) emit their line events again even if the lines were already seen.

: (WhileNode node) -> void

# File lib/ruby/prism/parse_result/newlines.rb, line 148
def visit_while_node(node)
  old_lines = @lines
  @lines = Array.new(old_lines.size, false)

  begin
    visit(node.predicate)
  ensure
    @lines = old_lines
  end

  visit(node.statements)
end