/
align_parameters.rb
112 lines (99 loc) · 3.48 KB
/
align_parameters.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
# encoding: utf-8
module Rubocop
module Cop
class AlignParameters < Cop
ERROR_MESSAGE = 'Align the parameters of a method call if they span ' +
'more than one line.'
def inspect(file, source, tokens, sexp)
@file = file
@tokens = tokens
@token_indexes = {}
@tokens.each_with_index { |t, ix| @token_indexes[t.pos] = ix }
each(:method_add_arg, sexp) do |method_add_arg|
args = get_args(method_add_arg) or next
first_arg, rest_of_args = divide_args(args)
@first_lparen_ix = get_lparen_ix(method_add_arg)
pos_of_1st_arg = position_of(first_arg) or next # Give up.
rest_of_args.each do |arg|
pos = position_of(arg) or next # Give up if no position found.
if pos.lineno != pos_of_1st_arg.lineno
if pos.column != pos_of_1st_arg.column
add_offence(:convention, pos.lineno, ERROR_MESSAGE)
end
end
end
end
end
private
def get_args(method_add_arg)
fcall = method_add_arg[1]
return nil if fcall[0] != :fcall
return nil if fcall[1][0..1] == [:@ident, "lambda"]
arg_paren = method_add_arg[2..-1][0]
return nil if arg_paren[0] != :arg_paren || arg_paren[1].nil?
# A command (call wihtout parentheses) as first parameter
# means there's only one parameter.
return nil if [:command, :command_call].include?(arg_paren[1][0][0])
args_add_block = arg_paren[1]
unless args_add_block[0] == :args_add_block
fail "\n#{@file}: #{method_add_arg}"
end
args_add_block[1].empty? ? [args_add_block[2]] : args_add_block[1]
end
def divide_args(args)
if args[0] == :args_add_star
first_arg = args[1]
rest_of_args = args[2..-1]
else
first_arg = args[0]
rest_of_args = args[1..-1]
end
[first_arg, rest_of_args]
end
def get_lparen_ix(method_add_arg)
method_name_pos = method_add_arg[1][1][-1]
method_name_ix = @token_indexes[method_name_pos]
method_name_ix +
@tokens[method_name_ix..-1].map(&:type).index(:on_lparen)
end
def position_of(sexp)
# Indentation inside a string literal is irrelevant.
return nil if sexp[0] == :string_literal
pos = find_pos_in_sexp(sexp) or return nil # Nil means not found.
ix = find_first_non_whitespace_token(pos) or return nil
@tokens[ix].pos
end
def find_pos_in_sexp(sexp)
return sexp[2] if Position === sexp[2]
sexp.grep(Array).each do |s|
pos = find_pos_in_sexp(s) and return pos
end
nil
end
def find_first_non_whitespace_token(pos)
ix = @token_indexes[pos]
newline_found = false
start_ix = ix.downto(0) do |i|
case @tokens[i].text
when '('
break i + 1 if i == @first_lparen_ix
when "\n"
newline_found = true
when /\t/
# Bail out if tabs are used. Too difficult to calculate column.
return nil
when ','
if newline_found
break i + 1
else
# Bail out if there's a preceding comma on the same line.
return nil
end
end
end
offset = @tokens[start_ix..-1].index { |t| not whitespace?(t) }
start_ix + offset
end
end
end
end