Class: RuboCop::Cop::Layout::EmptyLineBetweenDefs
| Relationships & Source Files | |
| Super Chains via Extension / Inclusion / Inheritance | |
|
Class Chain:
self,
::RuboCop::Cop::AutoCorrector,
::RuboCop::Cop::Base,
::RuboCop::ExcludeLimit,
NodePattern::Macros,
RuboCop::AST::Sexp
|
|
|
Instance Chain:
self,
::RuboCop::Cop::RangeHelp,
::RuboCop::Cop::Base,
::RuboCop::Cop::AutocorrectLogic,
::RuboCop::Cop::IgnoredNode,
::RuboCop::Util,
RuboCop::AST::Sexp
|
|
| Inherits: |
RuboCop::Cop::Base
|
| Defined in: | lib/rubocop/cop/layout/empty_line_between_defs.rb |
Overview
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.
Constant Summary
-
MSG =
# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 118'Expected %<expected>s between %<type>s definitions; found %<actual>d.'
::RuboCop::Cop::Base - Inherited
EMPTY_OFFENSES, RESTRICT_ON_SEND
::RuboCop::Cop::RangeHelp - Included
Class Attribute Summary
::RuboCop::Cop::AutoCorrector - Extended
::RuboCop::Cop::Base - Inherited
| .gem_requirements, .lint?, | |
| .support_autocorrect? | Returns if class supports autocorrect. |
| .support_multiple_source? | Override if your cop should be called repeatedly for multiple investigations Between calls to |
Class Method Summary
::RuboCop::Cop::Base - Inherited
| .autocorrect_incompatible_with | List of cops that should not try to autocorrect at the same time as this cop. |
| .badge | Naming. |
| .callbacks_needed, .cop_name, .department, | |
| .documentation_url | Returns a url to view this cops documentation online. |
| .exclude_from_registry | Call for abstract Cop classes. |
| .inherited, | |
| .joining_forces | Override and return the Force class(es) you need to join. |
| .match? | Returns true if the cop name or the cop namespace matches any of the given names. |
| .new, | |
| .requires_gem | Register a version requirement for the given gem name. |
| .restrict_on_send | |
::RuboCop::ExcludeLimit - Extended
| exclude_limit | Sets up a configuration option to have an exclude limit tracked. |
| transform | |
Instance Attribute Summary
- #allowance_range? ⇒ Boolean readonly private
::RuboCop::Cop::Base - Inherited
::RuboCop::Cop::AutocorrectLogic - Included
Instance Method Summary
- #autocorrect(corrector, prev_def, node, count)
- #check_defs(nodes)
-
#on_begin(node)
We operate on
beginnodes, instead of usingOnMethodDef, 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. - #autocorrect_insert_lines(corrector, newline_pos, count) private
- #autocorrect_remove_lines(corrector, newline_pos, count) private
- #blank_lines_count_between(first_def_node, second_def_node) private
- #candidate?(node) ⇒ Boolean private
- #class_candidate?(node) ⇒ Boolean private
- #def_end(node) private
- #def_location(correction_node) private
- #def_start(node) private
- #empty_line_between_macros private
- #end_loc(node) private
- #expected_lines private
- #line_count_allowed?(count) ⇒ Boolean private
- #lines_between_defs(first_def_node, second_def_node) private
- #macro_candidate?(node) ⇒ Boolean private
- #maximum_empty_lines private
- #message(node, count: nil) private
- #method_candidate?(node) ⇒ Boolean private
- #minimum_empty_lines private
- #module_candidate?(node) ⇒ Boolean private
- #multiple_blank_lines_groups?(first_def_node, second_def_node) ⇒ Boolean private
- #node_type(node) private
-
#trailing_heredoc_end(node, end_location)
private
For an endless method whose body is a heredoc (e.g.
def a = <<~TEXT), the node’s source range ends at the heredoc opening line, before the heredoc body.
::RuboCop::Cop::RangeHelp - Included
| #add_range, | |
| #arguments_range | A range containing the first to the last argument of a method call or method definition. |
| #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::Base - Inherited
| #add_global_offense | Adds an offense that has no particular location. |
| #add_offense | Adds an offense on the specified range (or node with an expression) Unless that offense is disabled for this range, a corrector will be yielded to provide the cop the opportunity to autocorrect the offense. |
| #begin_investigation | Called before any investigation. |
| #callbacks_needed, | |
| #cop_config | Configuration Helpers. |
| #cop_name, #excluded_file?, | |
| #external_dependency_checksum | This method should be overridden when a cop’s behavior depends on state that lives outside of these locations: |
| #inspect, | |
| #message | Gets called if no message is specified when calling |
| #name | Alias for Base#cop_name. |
| #offenses, | |
| #on_investigation_end | Called after all on_… |
| #on_new_investigation | Called before all on_… |
| #on_other_file | Called instead of all on_… |
| #parse | There should be very limited reasons for a Cop to do it’s own parsing. |
| #parser_engine, | |
| #ready | Called between investigations. |
| #relevant_file?, | |
| #target_gem_version | Returns a gems locked versions (i.e. |
| #target_rails_version, #target_ruby_version, #annotate, #apply_correction, #attempt_correction, | |
| #callback_argument | Reserved for Cop::Cop. |
| #complete_investigation | Called to complete an investigation. |
| #correct, #current_corrector, | |
| #current_offense_locations | Reserved for Commissioner: |
| #current_offenses, #currently_disabled_lines, #custom_severity, #default_severity, #disable_uncorrectable, #enabled_line?, #file_name_matches_any?, #find_message, #find_severity, #matches_absolute_include_pattern?, #range_for_original, #range_from_node_or_range, | |
| #reset_investigation | Actually private methods. |
| #use_corrector | |
::RuboCop::Cop::AutocorrectLogic - Included
::RuboCop::Cop::IgnoredNode - Included
Constructor Details
This class inherits a constructor from RuboCop::Cop::Base
Class Method Details
.autocorrect_incompatible_with
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 120
def self.autocorrect_incompatible_with [Layout::EmptyLines] end
Instance Attribute Details
#allowance_range? ⇒ Boolean (readonly, private)
[ GitHub ]
# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 318
def allowance_range? minimum_empty_lines != maximum_empty_lines end
Instance Method Details
#autocorrect(corrector, prev_def, node, count)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 151
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. begin_pos = node.source_range.begin_pos newline_pos = begin_pos - 1 if newline_pos > begin_pos if count > maximum_empty_lines autocorrect_remove_lines(corrector, newline_pos, count) else autocorrect_insert_lines(corrector, newline_pos, count) end end
#autocorrect_insert_lines(corrector, newline_pos, count) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 300
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 293
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 242
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) ⇒ Boolean (private)
# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 180
def candidate?(node) return false unless node method_candidate?(node) || class_candidate?(node) || module_candidate?(node) || macro_candidate?(node) end
#check_defs(nodes)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 136
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 = def_location(correction_node) add_offense(location, message: (correction_node, count: count)) do |corrector| autocorrect(corrector, *nodes, count) end end
#class_candidate?(node) ⇒ Boolean (private)
# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 206
def class_candidate?(node) cop_config['EmptyLineBetweenClassDefs'] && node.class_type? end
#def_end(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 272
def def_end(node) end_loc(node).line end
#def_location(correction_node) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 170
def def_location(correction_node) if correction_node.any_block_type? correction_node.source_range.join(correction_node.children.first.source_range) elsif correction_node.send_type? correction_node.source_range else correction_node.loc.keyword.join(correction_node.loc.name) end end
#def_start(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 262
def def_start(node) node = node.send_node if node.any_block_type? if node.send_type? node.source_range.line else node.loc.keyword.line end end
#empty_line_between_macros (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 187
def empty_line_between_macros @empty_line_between_macros ||= cop_config.fetch('DefLikeMacros', []).map(&:to_sym).freeze end
#end_loc(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 276
def end_loc(node) end_location = node.source_range.end trailing_heredoc_end(node, end_location) || end_location end
#expected_lines (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 220
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) ⇒ Boolean (private)
# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 238
def line_count_allowed?(count) (minimum_empty_lines..maximum_empty_lines).cover?(count) end
#lines_between_defs(first_def_node, second_def_node) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 254
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
#macro_candidate?(node) ⇒ Boolean (private)
# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 191
def macro_candidate?(node) macro_candidate = if node.any_block_type? node.send_node elsif node.send_type? node end return false unless macro_candidate macro_candidate.macro? && empty_line_between_macros.include?(macro_candidate.method_name) end
#maximum_empty_lines (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 250
def maximum_empty_lines Array(cop_config['NumberOfEmptyLines']).last end
#message(node, count: nil) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 214
def (node, count: nil) type = node_type(node) format(MSG, type: type, expected: expected_lines, actual: count) end
#method_candidate?(node) ⇒ Boolean (private)
# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 202
def method_candidate?(node) cop_config['EmptyLineBetweenMethodDefs'] && node.any_def_type? end
#minimum_empty_lines (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 246
def minimum_empty_lines Array(cop_config['NumberOfEmptyLines']).first end
#module_candidate?(node) ⇒ Boolean (private)
# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 210
def module_candidate?(node) cop_config['EmptyLineBetweenModuleDefs'] && node.module_type? end
#multiple_blank_lines_groups?(first_def_node, second_def_node) ⇒ Boolean (private)
# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 229
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 307
def node_type(node) case node.type when :def, :defs :method when :numblock, :itblock :block else node.type end end
#on_begin(node)
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 129
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
#trailing_heredoc_end(node, end_location) (private)
For an endless method whose body is a heredoc (e.g. def a = <<~TEXT), the
node’s source range ends at the heredoc opening line, before the heredoc body.
Use the heredoc’s closing delimiter so the def’s real end is located after the
heredoc and blank-line insertion does not land inside the heredoc body.
# File 'lib/rubocop/cop/layout/empty_line_between_defs.rb', line 285
def trailing_heredoc_end(node, end_location) heredocs = node.each_descendant(:any_str).select(&:heredoc?) return if heredocs.empty? heredoc_end = heredocs.map { |heredoc| heredoc.loc.heredoc_end }.max_by(&:end_pos) heredoc_end if heredoc_end.end_pos > end_location.end_pos end