summaryrefslogtreecommitdiff
path: root/rubocop/cop/line_break_around_conditional_block.rb
blob: 8118b314b6312f06cc720a9d33de6ac13196df58 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# frozen_string_literal: true

module RuboCop
  module Cop
    # Ensures a line break around conditional blocks.
    #
    # @example
    #    # bad
    #    do_something
    #    if condition
    #      do_extra_stuff
    #    end
    #    do_something_more
    #
    #    # good
    #    do_something
    #
    #    if condition
    #      do_extra_stuff
    #    end
    #
    #    do_something_more
    #
    #    # bad
    #    do_something
    #    unless condition
    #      do_extra_stuff
    #    end
    #
    #    do_something_more
    #
    #    # good
    #    def a_method
    #      if condition
    #        do_something
    #      end
    #    end
    #
    #    # good
    #    on_block do
    #      if condition
    #        do_something
    #      end
    #    end
    class LineBreakAroundConditionalBlock < RuboCop::Cop::Cop
      include RangeHelp

      MSG = 'Add a line break around conditional blocks'

      def on_if(node)
        # This cop causes errors in haml files, so let's skip those
        return if in_haml?(node)
        return if node.single_line?
        return unless node.if? || node.unless?

        add_offense(node, location: :expression, message: MSG) unless previous_line_valid?(node)
        add_offense(node, location: :expression, message: MSG) unless last_line_valid?(node)
      end

      def autocorrect(node)
        lambda do |corrector|
          line = range_by_whole_lines(node.source_range)
          unless previous_line_valid?(node)
            corrector.insert_before(line, "\n")
          end

          unless last_line_valid?(node)
            corrector.insert_after(line, "\n")
          end
        end
      end

      private

      def previous_line_valid?(node)
        previous_line(node).empty? ||
          start_clause_line?(previous_line(node)) ||
          block_start?(previous_line(node)) ||
          begin_line?(previous_line(node)) ||
          assignment_line?(previous_line(node)) ||
          rescue_line?(previous_line(node))
      end

      def last_line_valid?(node)
        last_line(node).empty? ||
          end_line?(last_line(node)) ||
          end_clause_line?(last_line(node))
      end

      def previous_line(node)
        processed_source[node.loc.line - 2]
      end

      def last_line(node)
        processed_source[node.loc.last_line]
      end

      def start_clause_line?(line)
        line =~ /^\s*(def|=end|#|module|class|if|unless|else|elsif|ensure|when)/
      end

      def end_clause_line?(line)
        line =~ /^\s*(#|rescue|else|elsif|when)/
      end

      def begin_line?(line)
        # an assignment followed by a begin or ust a begin
        line =~ /^\s*(@?(\w|\|+|=|\[|\]|\s)+begin|begin)/
      end

      def assignment_line?(line)
        line =~ /^\s*.*=/
      end

      def rescue_line?(line)
        line =~ /^\s*rescue/
      end

      def block_start?(line)
        line.match(/ (do|{)( \|.*?\|)?\s?$/)
      end

      def end_line?(line)
        line =~ /^\s*(end|})/
      end

      def in_haml?(node)
        node.location.expression.source_buffer.name.end_with?('.haml.rb')
      end
    end
  end
end