123456789_123456789_123456789_123456789_123456789_

Module: RuboCop::Cop::Layout::EmptyLinesAroundBody

Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Included In:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Macros
Instance Chain:
Defined in: lib/rubocop/cop/mixin/empty_lines_around_body.rb

Overview

Common functionality for checking if presence/absence of empty lines around some kind of body matches the configuration.

Constant Summary

::RuboCop::Cop::ConfigurableEnforcedStyle - Included

SYMBOL_TO_STRING_CACHE

::RuboCop::Cop::RangeHelp - Included

BYTE_ORDER_MARK, NOT_GIVEN

Instance Attribute Summary

Instance Method Summary

::RuboCop::Cop::RangeHelp - Included

#add_range, #column_offset_between,
#contents_range

A range containing only the contents of a literal with delimiters (e.g.

#directions,
#effective_column

Returns the column attribute of the range, except if the range is on the first line and there’s a byte order mark at the beginning of that line, in which case 1 is subtracted from the column value.

#final_pos, #move_pos, #move_pos_str, #range_between, #range_by_whole_lines, #range_with_comments, #range_with_comments_and_lines, #range_with_surrounding_comma, #range_with_surrounding_space, #source_range

::RuboCop::Cop::ConfigurableEnforcedStyle - Included

Instance Method Details

#check(node, body, adjusted_first_line: nil) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 26

def check(node, body, adjusted_first_line: nil)
  return if valid_body_style?(body)
  return if node.single_line?

  first_line = adjusted_first_line || node.source_range.first_line
  last_line = node.source_range.last_line

  case style
  when :empty_lines_except_namespace
    check_empty_lines_except_namespace(body, first_line, last_line)
  when :empty_lines_special
    check_empty_lines_special(body, first_line, last_line)
  else
    check_both(style, first_line, last_line)
  end
end

#check_beginning(style, first_line) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 81

def check_beginning(style, first_line)
  check_source(style, first_line, 'beginning')
end

#check_both(style, first_line, last_line) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 67

def check_both(style, first_line, last_line)
  case style
  when :beginning_only
    check_beginning(:empty_lines, first_line)
    check_ending(:no_empty_lines, last_line)
  when :ending_only
    check_beginning(:no_empty_lines, first_line)
    check_ending(:empty_lines, last_line)
  else
    check_beginning(style, first_line)
    check_ending(style, last_line)
  end
end

#check_deferred_empty_line(body) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 108

def check_deferred_empty_line(body)
  node = first_empty_line_required_child(body)
  return unless node

  line = previous_line_ignoring_comments(node.first_line)
  return if processed_source[line].empty?

  range = source_range(processed_source.buffer, line + 2, 0)

  add_offense(range, message: deferred_message(node)) do |corrector|
    EmptyLineCorrector.correct(corrector, [:empty_lines, range])
  end
end

#check_empty_lines_except_namespace(body, first_line, last_line) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 43

def check_empty_lines_except_namespace(body, first_line, last_line)
  if namespace?(body, with_one_child: true)
    check_both(:no_empty_lines, first_line, last_line)
  else
    check_both(:empty_lines, first_line, last_line)
  end
end

#check_empty_lines_special(body, first_line, last_line) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 51

def check_empty_lines_special(body, first_line, last_line)
  return unless body

  if namespace?(body, with_one_child: true)
    check_both(:no_empty_lines, first_line, last_line)
  else
    if first_child_requires_empty_line?(body)
      check_beginning(:empty_lines, first_line)
    else
      check_beginning(:no_empty_lines, first_line)
      check_deferred_empty_line(body)
    end
    check_ending(:empty_lines, last_line)
  end
end

#check_ending(style, last_line) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 85

def check_ending(style, last_line)
  check_source(style, last_line - 2, 'end')
end

#check_line(style, line, msg) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 98

def check_line(style, line, msg)
  return unless yield(processed_source.lines[line])

  offset = style == :empty_lines && msg.include?('end.') ? 2 : 1
  range = source_range(processed_source.buffer, line + offset, 0)
  add_offense(range, message: msg) do |corrector|
    EmptyLineCorrector.correct(corrector, [style, range])
  end
end

#check_source(style, line_no, desc) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 89

def check_source(style, line_no, desc)
  case style
  when :no_empty_lines
    check_line(style, line_no, message(MSG_EXTRA, desc), &:empty?)
  when :empty_lines
    check_line(style, line_no, message(MSG_MISSING, desc)) { |line| !line.empty? }
  end
end

#constant_definition?(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 20

def_node_matcher :constant_definition?, '{class module}'

#deferred_message(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 159

def deferred_message(node)
  format(MSG_DEFERRED, type: node.type)
end

#empty_line_required?(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 23

def_node_matcher :empty_line_required?,
                 '{def defs class module (send nil? {:private :protected :public})}'

#first_child_requires_empty_line?(body) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 132

def first_child_requires_empty_line?(body)
  if body.begin_type?
    empty_line_required?(body.children.first)
  else
    empty_line_required?(body)
  end
end

#first_empty_line_required_child(body) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 140

def first_empty_line_required_child(body)
  if body.begin_type?
    body.children.find { |child| empty_line_required?(child) }
  elsif empty_line_required?(body)
    body
  end
end

#message(type, desc) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 155

def message(type, desc)
  format(type, kind: self.class::KIND, location: desc)
end

#namespace?(body, with_one_child: false) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 122

def namespace?(body, with_one_child: false)
  if body.begin_type?
    return false if with_one_child

    body.children.all? { |child| constant_definition?(child) }
  else
    constant_definition?(body)
  end
end

#previous_line_ignoring_comments(send_line) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 148

def previous_line_ignoring_comments(send_line)
  (send_line - 2).downto(0) do |line|
    return line unless comment_line?(processed_source[line])
  end
  0
end

#valid_body_style?(body) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/mixin/empty_lines_around_body.rb', line 163

def valid_body_style?(body)
  # When style is `empty_lines`, if the body is empty, we don't enforce
  # the presence OR absence of an empty line
  # But if style is `no_empty_lines`, there must not be an empty line
  body.nil? && style != :no_empty_lines
end