class RuboCop::Cop::Layout::EmptyLineBetweenDefs

Checks whether class/module/method definitions are separated by one or more empty lines.

`NumberOfEmptyLines` can be an integer (default is 1) or an array (e.g. [1, 2]) to specify a minimum and maximum number of empty lines permitted.

`AllowAdjacentOneLineDefs` configures whether adjacent one-line definitions are considered an offense.

@example EmptyLineBetweenMethodDefs: true (default)

# checks for empty lines between method definitions.

# bad
def a
end
def b
end

@example

# good
def a
end

def b
end

@example EmptyLineBetweenClassDefs: true (default)

# checks for empty lines between class definitions.

# bad
class A
end
class B
end
def b
end

@example

# good
class A
end

class B
end

def b
end

@example EmptyLineBetweenModuleDefs: true (default)

# checks for empty lines between module definitions.

# bad
module A
end
module B
end
def b
end

@example

# good
module A
end

module B
end

def b
end

@example AllowAdjacentOneLineDefs: true (default)

# good
class ErrorA < BaseError; end
class ErrorB < BaseError; end
class ErrorC < BaseError; end

# good
class ErrorA < BaseError; end

class ErrorB < BaseError; end

class ErrorC < BaseError; end

@example AllowAdjacentOneLineDefs: false

# bad
class ErrorA < BaseError; end
class ErrorB < BaseError; end
class ErrorC < BaseError; end

# good
class ErrorA < BaseError; end

class ErrorB < BaseError; end

class ErrorC < BaseError; end

Constants

MSG

Public Class Methods

autocorrect_incompatible_with() click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 114
def self.autocorrect_incompatible_with
  [Layout::EmptyLines]
end

Public Instance Methods

autocorrect(corrector, prev_def, node, count) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 144
def autocorrect(corrector, prev_def, node, count)
  # finds position of first newline
  end_pos = end_loc(prev_def).end_pos
  source_buffer = end_loc(prev_def).source_buffer
  newline_pos = source_buffer.source.index("\n", end_pos)

  # Handle the case when multiple one-liners are on the same line.
  newline_pos = end_pos + 1 if newline_pos > node.source_range.begin_pos

  if count > maximum_empty_lines
    autocorrect_remove_lines(corrector, newline_pos, count)
  else
    autocorrect_insert_lines(corrector, newline_pos, count)
  end
end
check_defs(nodes) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 130
def check_defs(nodes)
  count = blank_lines_count_between(*nodes)

  return if line_count_allowed?(count)
  return if multiple_blank_lines_groups?(*nodes)
  return if nodes.all?(&:single_line?) && cop_config['AllowAdjacentOneLineDefs']

  correction_node = nodes.last
  location = correction_node.loc.keyword.join(correction_node.loc.name)
  add_offense(location, message: message(correction_node, count: count)) do |corrector|
    autocorrect(corrector, *nodes, count)
  end
end
on_begin(node) click to toggle source

We operate on `begin` nodes, instead of using `OnMethodDef`, so that we can walk over pairs of consecutive nodes and efficiently access a node's predecessor; prev_node ends up doing a linear scan over siblings, so we don't want to call it on each def.

# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 123
def on_begin(node)
  node.children.each_cons(2) do |prev, n|
    nodes = [prev, n]
    check_defs(nodes) if nodes.all? { |def_candidate| candidate?(def_candidate) }
  end
end

Private Instance Methods

allowance_range?() click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 267
def allowance_range?
  minimum_empty_lines != maximum_empty_lines
end
autocorrect_insert_lines(corrector, newline_pos, count) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 251
def autocorrect_insert_lines(corrector, newline_pos, count)
  difference = minimum_empty_lines - count
  where_to_insert = range_between(newline_pos, newline_pos + 1)

  corrector.insert_after(where_to_insert, "\n" * difference)
end
autocorrect_remove_lines(corrector, newline_pos, count) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 244
def autocorrect_remove_lines(corrector, newline_pos, count)
  difference = count - maximum_empty_lines
  range_to_remove = range_between(newline_pos, newline_pos + difference)

  corrector.remove(range_to_remove)
end
blank_lines_count_between(first_def_node, second_def_node) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 208
def blank_lines_count_between(first_def_node, second_def_node)
  lines_between_defs(first_def_node, second_def_node).count(&:blank?)
end
candidate?(node) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 162
def candidate?(node)
  return unless node

  method_candidate?(node) || class_candidate?(node) || module_candidate?(node)
end
class_candidate?(node) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 172
def class_candidate?(node)
  cop_config['EmptyLineBetweenClassDefs'] && node.class_type?
end
def_end(node) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 232
def def_end(node)
  end_loc(node).line
end
def_start(node) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 228
def def_start(node)
  node.loc.keyword.line
end
end_loc(node) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 236
def end_loc(node)
  if (node.def_type? || node.defs_type?) && node.endless?
    node.loc.expression.end
  else
    node.loc.end
  end
end
expected_lines() click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 186
def expected_lines
  if allowance_range?
    "#{minimum_empty_lines..maximum_empty_lines} empty lines"
  else
    lines = maximum_empty_lines == 1 ? 'line' : 'lines'
    "#{maximum_empty_lines} empty #{lines}"
  end
end
line_count_allowed?(count) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 204
def line_count_allowed?(count)
  (minimum_empty_lines..maximum_empty_lines).cover?(count)
end
lines_between_defs(first_def_node, second_def_node) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 220
def lines_between_defs(first_def_node, second_def_node)
  begin_line_num = def_end(first_def_node)
  end_line_num = def_start(second_def_node) - 2
  return [] if end_line_num.negative?

  processed_source.lines[begin_line_num..end_line_num]
end
maximum_empty_lines() click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 216
def maximum_empty_lines
  Array(cop_config['NumberOfEmptyLines']).last
end
message(node, count: nil) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 180
def message(node, count: nil)
  type = node_type(node)

  format(MSG, type: type, expected: expected_lines, actual: count)
end
method_candidate?(node) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 168
def method_candidate?(node)
  cop_config['EmptyLineBetweenMethodDefs'] && (node.def_type? || node.defs_type?)
end
minimum_empty_lines() click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 212
def minimum_empty_lines
  Array(cop_config['NumberOfEmptyLines']).first
end
module_candidate?(node) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 176
def module_candidate?(node)
  cop_config['EmptyLineBetweenModuleDefs'] && node.module_type?
end
multiple_blank_lines_groups?(first_def_node, second_def_node) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 195
def multiple_blank_lines_groups?(first_def_node, second_def_node)
  lines = lines_between_defs(first_def_node, second_def_node)
  blank_start = lines.each_index.select { |i| lines[i].blank? }.max
  non_blank_end = lines.each_index.reject { |i| lines[i].blank? }.min
  return false if blank_start.nil? || non_blank_end.nil?

  blank_start > non_blank_end
end
node_type(node) click to toggle source
# File lib/rubocop/cop/layout/empty_line_between_defs.rb, line 258
def node_type(node)
  case node.type
  when :def, :defs
    :method
  else
    node.type
  end
end