/
util.rb
149 lines (121 loc) · 4.36 KB
/
util.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
# frozen_string_literal: true
module RuboCop
module Cop
# This module contains a collection of useful utility methods.
module Util
include PathUtil
# Match literal regex characters, not including anchors, character
# classes, alternatives, groups, repetitions, references, etc
LITERAL_REGEX = %r{[\w\s\-,"'!#%&<>=;:`~/]|\\[^AbBdDgGhHkpPRwWXsSzZ0-9]}.freeze
module_function
# This is a bad API
def comment_line?(line_source)
/^\s*#/.match?(line_source)
end
# @deprecated Use `ProcessedSource#line_with_comment?`, `contains_comment?` or similar
def comment_lines?(node)
processed_source[line_range(node)].any? { |line| comment_line?(line) }
end
def line_range(node)
node.first_line..node.last_line
end
def parentheses?(node)
node.loc.respond_to?(:end) && node.loc.end && node.loc.end.is?(')')
end
def add_parentheses(node, corrector)
if !node.respond_to?(:arguments)
corrector.wrap(node, '(', ')')
elsif node.arguments.empty?
corrector.insert_after(node, '()')
else
args_begin = args_begin(node)
corrector.remove(args_begin)
corrector.insert_before(args_begin, '(')
corrector.insert_after(args_end(node), ')')
end
end
def args_begin(node)
loc = node.loc
selector = if node.super_type? || node.yield_type?
loc.keyword
elsif node.def_type? || node.defs_type?
loc.name
else
loc.selector
end
selector.end.resize(1)
end
def args_end(node)
node.loc.expression.end
end
def on_node(syms, sexp, excludes = [], &block)
return to_enum(:on_node, syms, sexp, excludes) unless block
yield sexp if Array(syms).include?(sexp.type)
return if Array(excludes).include?(sexp.type)
sexp.each_child_node { |elem| on_node(syms, elem, excludes, &block) }
end
def begins_its_line?(range)
range.source_line.index(/\S/) == range.column
end
# Returns, for example, a bare `if` node if the given node is an `if`
# with calls chained to the end of it.
def first_part_of_call_chain(node)
while node
case node.type
when :send
node = node.receiver
when :block
node = node.send_node
else
break
end
end
node
end
# If converting a string to Ruby string literal source code, must
# double quotes be used?
def double_quotes_required?(string)
# Double quotes are required for strings which either:
# - Contain single quotes
# - Contain non-printable characters, which must use an escape
# Regex matches IF there is a ' or there is a \\ in the string that is
# not preceded/followed by another \\ (e.g. "\\x34") but not "\\\\".
/'|(?<! \\) \\{2}* \\ (?![\\"])/x.match?(string)
end
def needs_escaping?(string)
double_quotes_required?(escape_string(string))
end
def escape_string(string)
string.inspect[1..-2].tap { |s| s.gsub!(/\\"/, '"') }
end
def to_string_literal(string)
if needs_escaping?(string) && compatible_external_encoding_for?(string)
string.inspect
else
# In a single-quoted strings, double quotes don't need to be escaped
"'#{string.gsub('\"', '"').gsub('\\') { '\\\\' }}'"
end
end
def trim_string_interporation_escape_character(str)
str.gsub(/\\\#\{(.*?)\}/) { "\#{#{Regexp.last_match(1)}}" }
end
def interpret_string_escapes(string)
StringInterpreter.interpret(string)
end
def same_line?(node1, node2)
node1.respond_to?(:loc) && node2.respond_to?(:loc) && node1.loc.line == node2.loc.line
end
def indent(node, offset: 0)
' ' * (node.loc.column + offset)
end
def to_supported_styles(enforced_style)
enforced_style.sub(/^Enforced/, 'Supported').sub('Style', 'Styles')
end
private
def compatible_external_encoding_for?(src)
src = src.dup if RUBY_ENGINE == 'jruby'
src.force_encoding(Encoding.default_external).valid_encoding?
end
end
end
end