# frozen_string_literal: truemoduleRuboCopmoduleCopmoduleLint# This cop checks whether the end keywords are aligned properly for do# end blocks.## Three modes are supported through the `EnforcedStyleAlignWith`# configuration parameter:## `start_of_block` : the `end` shall be aligned with the# start of the line where the `do` appeared.## `start_of_line` : the `end` shall be aligned with the# start of the line where the expression started.## `either` (which is the default) : the `end` is allowed to be in either# location. The autofixer will default to `start_of_line`.## @example## # bad## foo.bar# .each do# baz# end## @example## # EnforcedStyleAlignWith: either (default)## # good## variable = lambda do |i|# i# end## @example## # EnforcedStyleAlignWith: start_of_block## # good## foo.bar# .each do# baz# end## @example## # EnforcedStyleAlignWith: start_of_line## # good## foo.bar# .each do# baz# endclassBlockAlignment<CopincludeConfigurableEnforcedStyleMSG='%s is not aligned with %s%s.'.freezedef_node_matcher:block_end_align_target?,<<-PATTERN
{assignment?
splat
and
or
(send _ :<< ...)
(send equal?(%1) !:[] ...)}
PATTERNdefon_block(node)check_block_alignment(start_for_block_node(node),node)enddefstyle_parameter_name'EnforcedStyleAlignWith'endprivatedefstart_for_block_node(block_node)# Which node should we align the 'end' with?result=block_end_align_target(block_node)# In offense message, we want to show the assignment LHS rather than# the entire assignmentresult,=*resultwhileresult.op_asgn_type?||result.masgn_type?resultenddefblock_end_align_target(node)while(parent=node.parent)breakif!parent||!parent.locbreakifparent.loc.line!=node.loc.line&&!parent.masgn_type?breakunlessblock_end_align_target?(parent,node)node=parentendnodeenddefcheck_block_alignment(start_node,block_node)end_loc=block_node.loc.endreturnunlessbegins_its_line?(end_loc)start_loc=start_node.source_rangereturnunlessstart_loc.column!=end_loc.column||style==:start_of_blockdo_source_line_column=compute_do_source_line_column(block_node,end_loc)returnunlessdo_source_line_columnregister_offense(block_node,start_loc,end_loc,do_source_line_column)enddefregister_offense(block_node,start_loc,end_loc,do_source_line_column)error_source_line_column=ifstyle==:start_of_blockdo_source_line_columnelseloc_to_source_line_column(start_loc)endmessage=format_message(start_loc,end_loc,do_source_line_column,error_source_line_column)add_offense(block_node,end_loc,message)enddefformat_message(start_loc,end_loc,do_source_line_column,error_source_line_column)format(MSG,format_source_line_column(loc_to_source_line_column(end_loc)),format_source_line_column(error_source_line_column),alt_start_msg(start_loc,do_source_line_column))enddefcompute_do_source_line_column(node,end_loc)do_loc=node.loc.begin# Actually it's either do or {.# We've found that "end" is not aligned with the start node (which# can be a block, a variable assignment, etc). But we also allow# the "end" to be aligned with the start of the line where the "do"# is, which is a style some people use in multi-line chains of# blocks.match=/\S.*/.match(do_loc.source_line)indentation_of_do_line=match.begin(0)returnunlessend_loc.column!=indentation_of_do_line||style==:start_of_line{source: match[0],line: do_loc.line,column: indentation_of_do_line}enddefloc_to_source_line_column(loc){source: loc.source.lines.to_a.first.chomp,line: loc.line,column: loc.column}enddefalt_start_msg(start_loc,source_line_column)ifstyle!=:either''elsifstart_loc.line==source_line_column[:line]&&start_loc.column==source_line_column[:column]''else' or '+format_source_line_column(source_line_column)endenddefformat_source_line_column(source_line_column)"`#{source_line_column[:source]}` at #{source_line_column[:line]}, "\"#{source_line_column[:column]}"enddefcompute_start_col(ancestor_node,node)ifstyle==:start_of_blockdo_loc=node.loc.beginreturndo_loc.source_line=~/\S/end(ancestor_node||node).source_range.columnenddefautocorrect(node)ancestor_node=start_for_block_node(node)start_col=compute_start_col(ancestor_node,node)loc_end=node.loc.enddelta=start_col-loc_end.columnifdelta>0add_space_before(loc_end,delta)elsifdelta<0remove_space_before(loc_end.begin_pos,-delta)endenddefadd_space_before(loc,delta)->(corrector){corrector.insert_before(loc,' '*delta)}enddefremove_space_before(end_pos,delta)range=range_between(end_pos-delta,end_pos)->(corrector){corrector.remove(range)}endendendendend