/
command_literal.rb
186 lines (163 loc) · 4.77 KB
/
command_literal.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# frozen_string_literal: true
module RuboCop
module Cop
module Style
# This cop enforces using `` or %x around command literals.
#
# @example EnforcedStyle: backticks (default)
# # bad
# folders = %x(find . -type d).split
#
# # bad
# %x(
# ln -s foo.example.yml foo.example
# ln -s bar.example.yml bar.example
# )
#
# # good
# folders = `find . -type d`.split
#
# # good
# `
# ln -s foo.example.yml foo.example
# ln -s bar.example.yml bar.example
# `
#
# @example EnforcedStyle: mixed
# # bad
# folders = %x(find . -type d).split
#
# # bad
# `
# ln -s foo.example.yml foo.example
# ln -s bar.example.yml bar.example
# `
#
# # good
# folders = `find . -type d`.split
#
# # good
# %x(
# ln -s foo.example.yml foo.example
# ln -s bar.example.yml bar.example
# )
#
# @example EnforcedStyle: percent_x
# # bad
# folders = `find . -type d`.split
#
# # bad
# `
# ln -s foo.example.yml foo.example
# ln -s bar.example.yml bar.example
# `
#
# # good
# folders = %x(find . -type d).split
#
# # good
# %x(
# ln -s foo.example.yml foo.example
# ln -s bar.example.yml bar.example
# )
#
# @example AllowInnerBackticks: false (default)
# # If `false`, the cop will always recommend using `%x` if one or more
# # backticks are found in the command string.
#
# # bad
# `echo \`ls\``
#
# # good
# %x(echo `ls`)
#
# @example AllowInnerBackticks: true
# # good
# `echo \`ls\``
class CommandLiteral < Base
include ConfigurableEnforcedStyle
extend AutoCorrector
MSG_USE_BACKTICKS = 'Use backticks around command string.'
MSG_USE_PERCENT_X = 'Use `%x` around command string.'
def on_xstr(node)
return if node.heredoc?
if backtick_literal?(node)
check_backtick_literal(node, MSG_USE_PERCENT_X)
else
check_percent_x_literal(node, MSG_USE_BACKTICKS)
end
end
private
def check_backtick_literal(node, message)
return if allowed_backtick_literal?(node)
add_offense(node, message: message) do |corrector|
autocorrect(corrector, node)
end
end
def check_percent_x_literal(node, message)
return if allowed_percent_x_literal?(node)
add_offense(node, message: message) do |corrector|
autocorrect(corrector, node)
end
end
def autocorrect(corrector, node)
return if contains_backtick?(node)
replacement = if backtick_literal?(node)
['%x', ''].zip(preferred_delimiter).map(&:join)
else
%w[` `]
end
corrector.replace(node.loc.begin, replacement.first)
corrector.replace(node.loc.end, replacement.last)
end
def allowed_backtick_literal?(node)
case style
when :backticks
!contains_disallowed_backtick?(node)
when :mixed
node.single_line? && !contains_disallowed_backtick?(node)
end
end
def allowed_percent_x_literal?(node)
case style
when :backticks
contains_disallowed_backtick?(node)
when :mixed
node.multiline? || contains_disallowed_backtick?(node)
when :percent_x
true
end
end
def contains_disallowed_backtick?(node)
!allow_inner_backticks? && contains_backtick?(node)
end
def allow_inner_backticks?
cop_config['AllowInnerBackticks']
end
def contains_backtick?(node)
/`/.match?(node_body(node))
end
def node_body(node)
loc = node.loc
loc.expression.source[loc.begin.length...-loc.end.length]
end
def backtick_literal?(node)
node.loc.begin.source == '`'
end
def preferred_delimiter
(command_delimiter || default_delimiter).chars
end
def command_delimiter
preferred_delimiters_config['%x']
end
def default_delimiter
preferred_delimiters_config['default']
end
def preferred_delimiters_config
config.for_cop('Style/PercentLiteralDelimiters') \
['PreferredDelimiters']
end
end
end
end
end