-
-
Notifications
You must be signed in to change notification settings - Fork 3k
/
closing_heredoc_indentation.rb
123 lines (108 loc) · 2.96 KB
/
closing_heredoc_indentation.rb
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
# frozen_string_literal: true
module RuboCop
module Cop
module Layout
# Checks the indentation of here document closings.
#
# @example
#
# # bad
# class Foo
# def bar
# <<~SQL
# 'Hi'
# SQL
# end
# end
#
# # good
# class Foo
# def bar
# <<~SQL
# 'Hi'
# SQL
# end
# end
#
# # bad
#
# # heredoc contents is before closing heredoc.
# foo arg,
# <<~EOS
# Hi
# EOS
#
# # good
# foo arg,
# <<~EOS
# Hi
# EOS
#
# # good
# foo arg,
# <<~EOS
# Hi
# EOS
#
class ClosingHeredocIndentation < Base
include Heredoc
extend AutoCorrector
SIMPLE_HEREDOC = '<<'
MSG = '`%<closing>s` is not aligned with `%<opening>s`.'
MSG_ARG = '`%<closing>s` is not aligned with `%<opening>s` or ' \
'beginning of method definition.'
def on_heredoc(node)
return if heredoc_type(node) == SIMPLE_HEREDOC ||
opening_indentation(node) == closing_indentation(node) ||
argument_indentation_correct?(node)
message = message(node)
add_offense(node.loc.heredoc_end, message: message) do |corrector|
corrector.replace(node.loc.heredoc_end, indented_end(node))
end
end
private
def opening_indentation(node)
indent_level(heredoc_opening(node))
end
def argument_indentation_correct?(node)
return false unless node.argument? || node.chained?
opening_indentation(
find_node_used_heredoc_argument(node.parent)
) == closing_indentation(node)
end
def closing_indentation(node)
indent_level(heredoc_closing(node))
end
def heredoc_opening(node)
node.source_range.source_line
end
def heredoc_closing(node)
node.loc.heredoc_end.source_line
end
def indented_end(node)
closing_indent = closing_indentation(node)
opening_indent = opening_indentation(node)
closing_text = heredoc_closing(node)
closing_text.gsub(/^\s{#{closing_indent}}/, ' ' * opening_indent)
end
def find_node_used_heredoc_argument(node)
if node.parent&.send_type?
find_node_used_heredoc_argument(node.parent)
else
node
end
end
def message(node)
format(
node.argument? ? MSG_ARG : MSG,
closing: heredoc_closing(node).strip,
opening: heredoc_opening(node).strip
)
end
def indent_level(source_line)
source_line[/\A */].length
end
end
end
end
end