Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 223 additions & 3 deletions lib/prism/parse_result/newlines.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,117 @@ def visit_lambda_node(node)
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
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

# Permit class nodes to mark newlines within themselves.
#
#: (ClassNode node) -> void
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 module nodes to mark newlines within themselves.
#
#: (ModuleNode node) -> void
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
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

# 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
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

# 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
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

# 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
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

# Mark if nodes as newlines.
#
#: (IfNode node) -> void
Expand Down Expand Up @@ -144,14 +255,26 @@ def newline_flag!(lines) # :nodoc:
class UntilNode < Node
#: (Array[bool] lines) -> void
def newline_flag!(lines) # :nodoc:
predicate.newline_flag!(lines)
if location.start_offset == keyword_loc.start_offset && predicate.is_a?(ParenthesesNode)
# A parenthesized predicate emits its own line event when it is
# compiled at the end of the loop, in addition to this one.
super
else
predicate.newline_flag!(lines)
end
end
end

class WhileNode < Node
#: (Array[bool] lines) -> void
def newline_flag!(lines) # :nodoc:
predicate.newline_flag!(lines)
if location.start_offset == keyword_loc.start_offset && predicate.is_a?(ParenthesesNode)
# A parenthesized predicate emits its own line event when it is
# compiled at the end of the loop, in addition to this one.
super
else
predicate.newline_flag!(lines)
end
end
end

Expand All @@ -162,6 +285,99 @@ def newline_flag!(lines) # :nodoc:
end
end

# The line event for a statement is emitted where its first instruction is
# compiled, so nodes whose first instruction comes from a sub-expression
# delegate their newline flag to that sub-expression: assignments to their
# value, calls to their receiver, and array, hash, and interpolated string
# literals to their first element. Static literals are the exception: they
# are compiled to a single instruction on the first line of the literal, so
# they do not delegate.

class LocalVariableWriteNode < Node
#: (Array[bool] lines) -> void
def newline_flag!(lines) # :nodoc:
value.newline_flag!(lines)
end
end

class InstanceVariableWriteNode < Node
#: (Array[bool] lines) -> void
def newline_flag!(lines) # :nodoc:
value.newline_flag!(lines)
end
end

class ClassVariableWriteNode < Node
#: (Array[bool] lines) -> void
def newline_flag!(lines) # :nodoc:
value.newline_flag!(lines)
end
end

class GlobalVariableWriteNode < Node
#: (Array[bool] lines) -> void
def newline_flag!(lines) # :nodoc:
value.newline_flag!(lines)
end
end

class ConstantWriteNode < Node
#: (Array[bool] lines) -> void
def newline_flag!(lines) # :nodoc:
value.newline_flag!(lines)
end
end

class ConstantPathWriteNode < Node
#: (Array[bool] lines) -> void
def newline_flag!(lines) # :nodoc:
value.newline_flag!(lines)
end
end

class MultiWriteNode < Node
#: (Array[bool] lines) -> void
def newline_flag!(lines) # :nodoc:
value.newline_flag!(lines)
end
end

class CallNode < Node
#: (Array[bool] lines) -> void
def newline_flag!(lines) # :nodoc:
if (receiver = self.receiver)
receiver.newline_flag!(lines)
else
super
end
end
end

class ArrayNode < Node
#: (Array[bool] lines) -> void
def newline_flag!(lines) # :nodoc:
first = elements.first
if first && !static_literal?
first.newline_flag!(lines)
else
super
end
end
end

class HashNode < Node
#: (Array[bool] lines) -> void
def newline_flag!(lines) # :nodoc:
first = elements.first
if first && !static_literal?
first.newline_flag!(lines)
else
super
end
end
end


class InterpolatedMatchLastLineNode < Node
#: (Array[bool] lines) -> void
def newline_flag!(lines) # :nodoc:
Expand All @@ -182,7 +398,11 @@ class InterpolatedStringNode < Node
#: (Array[bool] lines) -> void
def newline_flag!(lines) # :nodoc:
first = parts.first
first.newline_flag!(lines) if first
if first && !static_literal?
first.newline_flag!(lines)
else
super
end
end
end

Expand Down
87 changes: 87 additions & 0 deletions rbi/generated/prism/parse_result/newlines.rbi

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading