tailor 0.1.5 → 1.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. data/.gitignore +9 -1
  2. data/.rspec +2 -1
  3. data/.tailor +6 -0
  4. data/Gemfile.lock +47 -78
  5. data/{ChangeLog.rdoc → History.rdoc} +0 -0
  6. data/README.rdoc +157 -24
  7. data/Rakefile +0 -9
  8. data/bin/tailor +16 -69
  9. data/features/configurable.feature +78 -0
  10. data/features/horizontal_spacing.feature +262 -0
  11. data/features/indentation.feature +17 -21
  12. data/features/indentation/bad_files_with_no_trailing_newline.feature +90 -0
  13. data/features/indentation/good_files_with_no_trailing_newline.feature +206 -0
  14. data/features/name_detection.feature +72 -0
  15. data/features/step_definitions/indentation_steps.rb +10 -133
  16. data/features/support/env.rb +7 -15
  17. data/features/support/file_cases/horizontal_spacing_cases.rb +265 -0
  18. data/features/support/file_cases/indentation_cases.rb +972 -0
  19. data/features/support/file_cases/naming_cases.rb +52 -0
  20. data/features/support/file_cases/vertical_spacing_cases.rb +70 -0
  21. data/features/support/hooks.rb +8 -0
  22. data/features/support/{1_file_with_bad_operator_spacing → legacy}/bad_op_spacing.rb +0 -0
  23. data/features/support/{1_file_with_bad_ternary_colon_spacing → legacy}/bad_ternary_colon_spacing.rb +0 -0
  24. data/features/support/{1_long_file_with_indentation/my_project.rb → legacy/long_file_with_indentation.rb} +1 -1
  25. data/features/support/world.rb +14 -0
  26. data/features/vertical_spacing.feature +114 -0
  27. data/lib/ext/string_ext.rb +5 -0
  28. data/lib/tailor.rb +6 -252
  29. data/lib/tailor/cli.rb +49 -0
  30. data/lib/tailor/cli/options.rb +251 -0
  31. data/lib/tailor/composite_observable.rb +56 -0
  32. data/lib/tailor/configuration.rb +263 -0
  33. data/lib/tailor/critic.rb +162 -0
  34. data/lib/tailor/formatters/text.rb +126 -0
  35. data/lib/tailor/lexed_line.rb +246 -0
  36. data/lib/tailor/lexer.rb +428 -0
  37. data/lib/tailor/lexer/token.rb +103 -0
  38. data/lib/tailor/lexer_constants.rb +75 -0
  39. data/lib/tailor/logger.rb +28 -0
  40. data/lib/tailor/problem.rb +100 -0
  41. data/lib/tailor/reporter.rb +48 -0
  42. data/lib/tailor/ruler.rb +39 -0
  43. data/lib/tailor/rulers.rb +7 -0
  44. data/lib/tailor/rulers/allow_camel_case_methods_ruler.rb +30 -0
  45. data/lib/tailor/rulers/allow_hard_tabs_ruler.rb +22 -0
  46. data/lib/tailor/rulers/allow_screaming_snake_case_classes_ruler.rb +32 -0
  47. data/lib/tailor/rulers/allow_trailing_line_spaces_ruler.rb +33 -0
  48. data/lib/tailor/rulers/indentation_spaces_ruler.rb +199 -0
  49. data/lib/tailor/rulers/indentation_spaces_ruler/indentation_manager.rb +362 -0
  50. data/lib/tailor/rulers/max_code_lines_in_class_ruler.rb +84 -0
  51. data/lib/tailor/rulers/max_code_lines_in_method_ruler.rb +84 -0
  52. data/lib/tailor/rulers/max_line_length_ruler.rb +31 -0
  53. data/lib/tailor/rulers/spaces_after_comma_ruler.rb +83 -0
  54. data/lib/tailor/rulers/spaces_after_lbrace_ruler.rb +114 -0
  55. data/lib/tailor/rulers/spaces_after_lbracket_ruler.rb +123 -0
  56. data/lib/tailor/rulers/spaces_after_lparen_ruler.rb +116 -0
  57. data/lib/tailor/rulers/spaces_before_comma_ruler.rb +67 -0
  58. data/lib/tailor/rulers/spaces_before_lbrace_ruler.rb +93 -0
  59. data/lib/tailor/rulers/spaces_before_rbrace_ruler.rb +98 -0
  60. data/lib/tailor/rulers/spaces_before_rbracket_ruler.rb +70 -0
  61. data/lib/tailor/rulers/spaces_before_rparen_ruler.rb +70 -0
  62. data/lib/tailor/rulers/spaces_in_empty_braces_ruler.rb +94 -0
  63. data/lib/tailor/rulers/trailing_newlines_ruler.rb +36 -0
  64. data/lib/tailor/runtime_error.rb +3 -0
  65. data/lib/tailor/tailorrc.erb +88 -0
  66. data/lib/tailor/version.rb +2 -2
  67. data/spec/spec_helper.rb +7 -5
  68. data/spec/tailor/cli_spec.rb +94 -0
  69. data/spec/tailor/configuration_spec.rb +147 -0
  70. data/spec/tailor/critic_spec.rb +63 -0
  71. data/spec/tailor/lexed_line_spec.rb +569 -0
  72. data/spec/tailor/lexer/token_spec.rb +46 -0
  73. data/spec/tailor/lexer_spec.rb +181 -0
  74. data/spec/tailor/options_spec.rb +6 -0
  75. data/spec/tailor/problem_spec.rb +74 -0
  76. data/spec/tailor/reporter_spec.rb +53 -0
  77. data/spec/tailor/ruler_spec.rb +56 -0
  78. data/spec/tailor/rulers/indentation_spaces_ruler/indentation_manager_spec.rb +454 -0
  79. data/spec/tailor/rulers/indentation_spaces_ruler_spec.rb +128 -0
  80. data/spec/tailor/rulers/spaces_after_comma_spec.rb +31 -0
  81. data/spec/tailor/rulers/spaces_after_lbrace_ruler_spec.rb +145 -0
  82. data/spec/tailor/rulers/spaces_before_lbrace_ruler_spec.rb +63 -0
  83. data/spec/tailor/rulers/spaces_before_rbrace_ruler_spec.rb +63 -0
  84. data/spec/tailor/rulers_spec.rb +9 -0
  85. data/spec/tailor/version_spec.rb +6 -0
  86. data/spec/tailor_spec.rb +9 -21
  87. data/tailor.gemspec +22 -35
  88. data/tasks/features.rake +7 -0
  89. data/tasks/roodi.rake +9 -0
  90. data/tasks/roodi_config.yaml +14 -0
  91. data/tasks/spec.rake +16 -0
  92. data/tasks/yard.rake +14 -0
  93. metadata +224 -77
  94. data/features/case_checking.feature +0 -38
  95. data/features/spacing.feature +0 -97
  96. data/features/spacing/commas.feature +0 -44
  97. data/features/step_definitions/case_checking_steps.rb +0 -42
  98. data/features/step_definitions/spacing_steps.rb +0 -156
  99. data/features/support/1_file_with_bad_comma_spacing/bad_comma_spacing.rb +0 -43
  100. data/features/support/1_file_with_bad_curly_brace_spacing/bad_curly_brace_spacing.rb +0 -60
  101. data/features/support/1_file_with_bad_parenthesis/bad_parenthesis.rb +0 -4
  102. data/features/support/1_file_with_bad_square_brackets/bad_square_brackets.rb +0 -62
  103. data/features/support/1_file_with_camel_case_class/camel_case_class.rb +0 -5
  104. data/features/support/1_file_with_camel_case_method/camel_case_method.rb +0 -3
  105. data/features/support/1_file_with_hard_tabs/hard_tab.rb +0 -3
  106. data/features/support/1_file_with_long_lines/long_lines.rb +0 -5
  107. data/features/support/1_file_with_snake_case_class/snake_case_class.rb +0 -5
  108. data/features/support/1_file_with_snake_case_method/snake_case_method.rb +0 -3
  109. data/features/support/1_file_with_trailing_whitespace/trailing_whitespace.rb +0 -5
  110. data/features/support/1_good_simple_file/simple_project.rb +0 -5
  111. data/features/support/common.rb +0 -102
  112. data/features/support/matchers.rb +0 -11
  113. data/lib/tailor/file_line.rb +0 -220
  114. data/lib/tailor/indentation.rb +0 -245
  115. data/lib/tailor/spacing.rb +0 -237
  116. data/spec/file_line_spec.rb +0 -70
  117. data/spec/indentation_spec.rb +0 -259
  118. data/spec/spacing/colon_spacing_spec.rb +0 -71
  119. data/spec/spacing/comma_spacing_spec.rb +0 -159
  120. data/spec/spacing/curly_brace_spacing_spec.rb +0 -257
  121. data/spec/spacing/parentheses_spacing_spec.rb +0 -28
  122. data/spec/spacing/square_bracket_spacing_spec.rb +0 -116
  123. data/spec/spacing_spec.rb +0 -167
  124. data/tasks/metrics.rake +0 -23
@@ -0,0 +1,362 @@
1
+ require_relative '../../ruler'
2
+ require_relative '../../logger'
3
+ require_relative '../../lexer_constants'
4
+
5
+ class Tailor
6
+ module Rulers
7
+ class IndentationSpacesRuler < Tailor::Ruler
8
+ class IndentationManager
9
+ include Tailor::LexerConstants
10
+ include Tailor::Logger::Mixin
11
+
12
+ OPEN_EVENT_FOR = {
13
+ on_kw: :on_kw,
14
+ on_rbrace: :on_lbrace,
15
+ on_rbracket: :on_lbracket,
16
+ on_rparen: :on_lparen
17
+ }
18
+
19
+ attr_accessor :amount_to_change_next
20
+ attr_accessor :amount_to_change_this
21
+ attr_accessor :embexpr_beg
22
+
23
+ attr_reader :actual_indentation
24
+ attr_reader :indent_reasons
25
+ attr_reader :tstring_nesting
26
+
27
+ def initialize(spaces)
28
+ @spaces = spaces
29
+
30
+ log "Setting @proper[:this_line] to 0."
31
+ @proper = { this_line: 0, next_line: 0 }
32
+ @actual_indentation = 0
33
+
34
+ @indent_reasons = []
35
+ @tstring_nesting = []
36
+
37
+ @amount_to_change_this = 0
38
+ start
39
+ end
40
+
41
+ # @return [Fixnum] The indent level the file should currently be at.
42
+ def should_be_at
43
+ @proper[:this_line]
44
+ end
45
+
46
+ # Decreases the indentation expectation for the current line by
47
+ # +@spaces+.
48
+ def decrease_this_line
49
+ if started?
50
+ @proper[:this_line] -= @spaces
51
+
52
+ if @proper[:this_line] < 0
53
+ @proper[:this_line] = 0
54
+ end
55
+
56
+ log "@proper[:this_line] = #{@proper[:this_line]}"
57
+ log "@proper[:next_line] = #{@proper[:next_line]}"
58
+ else
59
+ log "#decrease_this_line called, but checking is stopped."
60
+ end
61
+ end
62
+
63
+
64
+ # Sets up expectations in +@proper+ based on the number of +/- reasons
65
+ # to change this and next lines, given in +@amount_to_change_this+ and
66
+ # +@amount_to_change_next+, respectively.
67
+ def set_up_line_transition
68
+ log "Amount to change this line: #{@amount_to_change_this}"
69
+ decrease_this_line if @amount_to_change_this < 0
70
+ end
71
+
72
+ # Should be called just before moving to the next line. This sets the
73
+ # expectation set in +@proper[:next_line]+ to
74
+ # +@proper[:this_line]+.
75
+ def transition_lines
76
+ if started?
77
+ log "Resetting change_this to 0."
78
+ @amount_to_change_this = 0
79
+ log "Setting @proper[:this_line] = that of :next_line"
80
+ @proper[:this_line] = @proper[:next_line]
81
+ log "Transitioning @proper[:this_line] to #{@proper[:this_line]}"
82
+ else
83
+ log "Skipping #transition_lines; checking is stopped."
84
+ end
85
+ end
86
+
87
+ # Starts the process of increasing/decreasing line indentation
88
+ # expectations.
89
+ def start
90
+ log "Starting indentation ruling."
91
+ log "Next check should be at #{should_be_at}"
92
+ @do_measurement = true
93
+ end
94
+
95
+ # Tells if the indentation checking process is on.
96
+ #
97
+ # @return [Boolean] +true+ if it's started; +false+ if not.
98
+ def started?
99
+ @do_measurement
100
+ end
101
+
102
+ # Stops the process of increasing/decreasing line indentation
103
+ # expectations.
104
+ def stop
105
+ if started?
106
+ msg = "Stopping indentation ruling. Should be: #{should_be_at}; "
107
+ msg << "actual: #{@actual_indentation}"
108
+ log msg
109
+ end
110
+
111
+ @do_measurement = false
112
+ end
113
+
114
+ # Updates +@actual_indentation+ based on the given lexed_line_output.
115
+ #
116
+ # @param [Array] lexed_line_output The lexed output for the current line.
117
+ def update_actual_indentation(lexed_line_output)
118
+ if lexed_line_output.end_of_multi_line_string?
119
+ log "Found end of multi-line string."
120
+ return
121
+ end
122
+
123
+ first_non_space_element = lexed_line_output.first_non_space_element
124
+ @actual_indentation = first_non_space_element.first.last
125
+ log "Actual indentation: #{@actual_indentation}"
126
+ end
127
+
128
+ # Checks if the current line ends with an operator, comma, or period.
129
+ #
130
+ # @param [LexedLine] lexed_line
131
+ # @return [Boolean]
132
+ def line_ends_with_single_token_indenter?(lexed_line)
133
+ lexed_line.ends_with_op? ||
134
+ lexed_line.ends_with_comma? ||
135
+ lexed_line.ends_with_period? ||
136
+ lexed_line.ends_with_modifier_kw?
137
+ end
138
+
139
+ # Checks to see if the last token in @single_tokens is the same as the
140
+ # one in +token_event+.
141
+ #
142
+ # @param [Array] token_event A single event (probably extracted from a
143
+ # {LexedLine}).
144
+ # @return [Boolean]
145
+ def line_ends_with_same_as_last(token_event)
146
+ return false if @indent_reasons.empty?
147
+
148
+ @indent_reasons.last[:event_type] == token_event[1]
149
+ end
150
+
151
+ # Checks to see if +lexed_line+ ends with a comma, and if it is in the
152
+ # middle of an enclosed statement (unclosed braces, brackets, parens).
153
+ # You don't want to update indentation expectations for this comma if
154
+ # you've already done so for the start of the enclosed statement.
155
+ #
156
+ # @param [LexedLine] lexed_line
157
+ # @param [Fixnum] lineno
158
+ # @return [Boolean]
159
+ def comma_is_part_of_enclosed_statement?(lexed_line, lineno)
160
+ return false if @indent_reasons.empty?
161
+
162
+ lexed_line.ends_with_comma? &&
163
+ @indent_reasons.last[:event_type] == :on_lbrace ||
164
+ @indent_reasons.last[:event_type] == :on_lbracket ||
165
+ @indent_reasons.last[:event_type] == :on_lparen
166
+ end
167
+
168
+ # Checks if indentation level got increased on this line because of a
169
+ # keyword and if it got increased on this line because of a
170
+ # single-token indenter.
171
+ #
172
+ # @param [Fixnum] lineno
173
+ # @return [Boolean]
174
+ def keyword_and_single_token_line?(lineno)
175
+ d_tokens = @indent_reasons.find_all { |t| t[:lineno] == lineno }
176
+ return false if d_tokens.empty?
177
+
178
+ kw_token = d_tokens.reverse.find do |t|
179
+ ['{', '[', '('].none? { |e| t[:token] == e }
180
+ end
181
+
182
+ return false if kw_token.nil?
183
+
184
+ s_token = @indent_reasons.reverse.find { |t| t[:lineno] == lineno }
185
+ return false unless s_token
186
+
187
+ true
188
+ end
189
+
190
+ def single_token_start_line
191
+ return if @indent_reasons.empty?
192
+
193
+ @indent_reasons.first[:lineno]
194
+ end
195
+
196
+ def double_token_start_line
197
+ return if @indent_reasons.empty?
198
+
199
+ @indent_reasons.last[:lineno]
200
+ end
201
+
202
+ def double_tokens_in_line(lineno)
203
+ @indent_reasons.find_all { |t| t[:lineno] == lineno}
204
+ end
205
+
206
+ def add_indent_reason(event_type, token, lineno)
207
+ @indent_reasons << {
208
+ event_type: event_type,
209
+ token: token,
210
+ lineno: lineno,
211
+ should_be_at: @proper[:this_line]
212
+ }
213
+
214
+ @proper[:next_line] = @indent_reasons.last[:should_be_at] + @spaces
215
+ log "Added indent reason; it's now:"
216
+ @indent_reasons.each { |r| log r.to_s }
217
+ end
218
+
219
+ def update_for_opening_reason(event_type, token, lineno)
220
+ if token.modifier_keyword?
221
+ log "Found modifier in line: '#{token}'"
222
+ return
223
+ end
224
+
225
+ log "Token '#{token}' not used as a modifier."
226
+
227
+ if token.do_is_for_a_loop?
228
+ log "Found keyword loop using optional 'do'"
229
+ return
230
+ end
231
+
232
+ add_indent_reason(event_type, token, lineno)
233
+ end
234
+
235
+ def update_for_continuation_reason(token, lexed_line, lineno)
236
+ d_tokens = @indent_reasons.dup
237
+ d_tokens.pop
238
+ on_line_token = d_tokens.find { |t| t[:lineno] == lineno }
239
+ log "online token: #{on_line_token}"
240
+
241
+ if on_line_token.nil? && lexed_line.to_s =~ /^\s*#{token}/
242
+ @proper[:this_line] -= @spaces unless @proper[:this_line].zero?
243
+ msg = "Continuation keyword: '#{token}'. "
244
+ msg << "change_this -= 1 -> #{@proper[:this_line]}"
245
+ log msg
246
+ end
247
+
248
+ last_reason_line = @indent_reasons.find { |r| r[:lineno] == lineno }
249
+
250
+ if last_reason_line.nil?
251
+ @proper[:next_line] = @indent_reasons.last[:should_be_at] + @spaces
252
+ else
253
+ @proper[:next_line] = @indent_reasons.last[:should_be_at] - @spaces
254
+ end
255
+ end
256
+
257
+ def update_for_closing_reason(event_type, lexed_line, lineno)
258
+ if event_type == :on_rbrace && @embexpr_beg == true
259
+ msg = "Got :rbrace and @embexpr_beg is true. "
260
+ msg << " Must be at an @embexpr_end."
261
+ log msg
262
+ @embexpr_beg = false
263
+ return
264
+ end
265
+
266
+ remove_continuation_keywords
267
+ remove_appropriate_reason(event_type)
268
+
269
+ @proper[:next_line] = if @indent_reasons.empty?
270
+ 0
271
+ else
272
+ @indent_reasons.last[:should_be_at] + @spaces
273
+ end
274
+
275
+ log "Updated :next after closing; it's now #{@proper[:next_line]}"
276
+
277
+ meth = "only_#{event_type.to_s.sub("^on_", '')}?"
278
+
279
+ if lexed_line.send(meth.to_sym) || lexed_line.to_s =~ /^\s*end\n?$/
280
+ @proper[:this_line] = @proper[:this_line] - @spaces
281
+ msg = "End multi-line statement. "
282
+ msg < "change_this -= 1 -> #{@proper[:this_line]}."
283
+ log msg
284
+ end
285
+ end
286
+
287
+ def remove_appropriate_reason(event_type)
288
+ last_opening_event = @indent_reasons.reverse.find do |r|
289
+ r[:event_type] == OPEN_EVENT_FOR[event_type]
290
+ end
291
+
292
+ if last_opening_event
293
+ r_index = @indent_reasons.reverse.index(last_opening_event)
294
+ index = @indent_reasons.size - r_index - 1
295
+ tmp_reasons = []
296
+
297
+ @indent_reasons.each_with_index do |r, i|
298
+ tmp_reasons << r unless i == index
299
+ end
300
+
301
+ @indent_reasons.replace(tmp_reasons)
302
+ else
303
+ @indent_reasons.pop
304
+ end
305
+
306
+ log "Removed indent reason; it's now:"
307
+ @indent_reasons.each { |r| log r.to_s }
308
+ end
309
+
310
+ def last_indent_reason_type
311
+ return if @indent_reasons.empty?
312
+
313
+ @indent_reasons.last[:event_type]
314
+ end
315
+
316
+ def remove_continuation_keywords
317
+ return if @indent_reasons.empty?
318
+
319
+ while CONTINUATION_KEYWORDS.include?(@indent_reasons.last[:token])
320
+ @indent_reasons.pop
321
+ end
322
+ end
323
+
324
+ # Overriding to be able to call #multi_line_brackets?,
325
+ # #multi_line_braces?, and #multi_line_parens?, where each takes a
326
+ # single parameter, which is the lineno.
327
+ #
328
+ # @return [Boolean]
329
+ def method_missing(meth, *args, &blk)
330
+ if meth.to_s =~ /^multi_line_(.+)\?$/
331
+ token = case $1
332
+ when "brackets" then '['
333
+ when "braces" then '{'
334
+ when "parens" then '('
335
+ else
336
+ super(meth, *args, &blk)
337
+ end
338
+
339
+ lineno = args.first
340
+
341
+ tokens = @indent_reasons.find_all do |t|
342
+ t[:token] == token
343
+ end
344
+
345
+ return false if tokens.empty?
346
+
347
+ token_on_this_line = tokens.find { |t| t[:lineno] == lineno }
348
+ return true if token_on_this_line.nil?
349
+
350
+ false
351
+ else
352
+ super(meth, *args, &blk)
353
+ end
354
+ end
355
+
356
+ def in_tstring?
357
+ !@tstring_nesting.empty?
358
+ end
359
+ end
360
+ end
361
+ end
362
+ end
@@ -0,0 +1,84 @@
1
+ require_relative '../ruler'
2
+ require_relative '../lexer_constants'
3
+
4
+ class Tailor
5
+ module Rulers
6
+ class MaxCodeLinesInClassRuler < Tailor::Ruler
7
+ include LexerConstants
8
+
9
+ def initialize(config)
10
+ super(config)
11
+ @class_start_lines = []
12
+ @kw_start_lines = []
13
+ @end_last_class = false
14
+ end
15
+
16
+ def ignored_nl_update(lexed_line, lineno, column)
17
+ return if @class_start_lines.empty?
18
+ return if lexed_line.only_spaces?
19
+ return if lexed_line.comment_line?
20
+
21
+ @class_start_lines.each do |line|
22
+ line[:count] += 1
23
+ log "Class from line #{line[:lineno]} now at #{line[:count]} lines."
24
+ end
25
+
26
+ if @end_last_class
27
+ measure(@class_start_lines.last[:count],
28
+ @class_start_lines.last[:lineno],
29
+ @class_start_lines.last[:column])
30
+ @class_start_lines.pop
31
+ @end_last_class = false
32
+ end
33
+ end
34
+
35
+ def kw_update(token, lexed_line, lineno, column)
36
+ if token == "class" || token == "module"
37
+ @class_start_lines << { lineno: lineno, column: column, count: 0 }
38
+ log "Class start lines: #{@class_start_lines}"
39
+ end
40
+
41
+ unless token.modifier_keyword? ||
42
+ !token.keyword_to_indent? ||
43
+ token.do_is_for_a_loop? ||
44
+ token.continuation_keyword?
45
+ @kw_start_lines << lineno
46
+ log "Keyword start lines: #{@kw_start_lines}"
47
+ end
48
+
49
+ if token == "end"
50
+ log "Got 'end' of class/module."
51
+
52
+ unless @class_start_lines.empty?
53
+ if @class_start_lines.last[:lineno] == @kw_start_lines.last
54
+ msg = "Class/module from line #{@class_start_lines.last[:lineno]}"
55
+ msg << " was #{@class_start_lines.last[:count]} lines long."
56
+ log msg
57
+ @end_last_class = true
58
+ end
59
+ end
60
+
61
+ @kw_start_lines.pop
62
+ log "End of keyword statement. Keywords: #{@kw_start_lines}"
63
+ end
64
+ end
65
+
66
+ def nl_update(lexed_line, lineno, column)
67
+ ignored_nl_update(lexed_line, lineno, column)
68
+ end
69
+
70
+ # Checks to see if the actual count of code lines in the class is greater
71
+ # than the value in +@config+.
72
+ #
73
+ # @param [Fixnum] actual_count The number of code lines found.
74
+ # @param [Fixnum] lineno The line the potential problem is on.
75
+ # @param [Fixnum] column The column the potential problem is on.
76
+ def measure(actual_count, lineno, column)
77
+ if actual_count > @config
78
+ @problems << Problem.new(:code_lines_in_class, lineno, column,
79
+ { actual_count: actual_count, should_be_at: @config })
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,84 @@
1
+ require_relative '../ruler'
2
+ require_relative '../lexer_constants'
3
+
4
+ class Tailor
5
+ module Rulers
6
+ class MaxCodeLinesInMethodRuler < Tailor::Ruler
7
+ include LexerConstants
8
+
9
+ def initialize(config)
10
+ super(config)
11
+ @method_start_lines = []
12
+ @kw_start_lines = []
13
+ @end_last_method = false
14
+ end
15
+
16
+ def ignored_nl_update(lexed_line, lineno, column)
17
+ return if @method_start_lines.empty?
18
+ return if lexed_line.only_spaces?
19
+ return if lexed_line.comment_line?
20
+
21
+ @method_start_lines.each do |line|
22
+ line[:count] += 1
23
+ log "Method from line #{line[:lineno]} now at #{line[:count]} lines."
24
+ end
25
+
26
+ if @end_last_method
27
+ measure(@method_start_lines.last[:count],
28
+ @method_start_lines.last[:lineno],
29
+ @method_start_lines.last[:column])
30
+ @method_start_lines.pop
31
+ @end_last_method = false
32
+ end
33
+ end
34
+
35
+ def kw_update(token, lexed_line, lineno, column)
36
+ if token == "def"
37
+ @method_start_lines << { lineno: lineno, column: column, count: 0 }
38
+ log "Method start lines: #{@method_start_lines}"
39
+ end
40
+
41
+ unless token.modifier_keyword? ||
42
+ !token.keyword_to_indent? ||
43
+ token.do_is_for_a_loop? ||
44
+ token.continuation_keyword?
45
+ @kw_start_lines << lineno
46
+ log "Keyword start lines: #{@kw_start_lines}"
47
+ end
48
+
49
+ if token == "end"
50
+ log "Got 'end' of method."
51
+
52
+ unless @method_start_lines.empty?
53
+ if @method_start_lines.last[:lineno] == @kw_start_lines.last
54
+ #msg = "Method from line #{@method_start_lines.last[:lineno]}"
55
+ #msg << " was #{@method_start_lines.last[:count]} lines long."
56
+ #log msg
57
+ @end_last_method = true
58
+ end
59
+ end
60
+
61
+ @kw_start_lines.pop
62
+ log "End of keyword statement. Keywords: #{@kw_start_lines}"
63
+ end
64
+ end
65
+
66
+ def nl_update(lexed_line, lineno, column)
67
+ ignored_nl_update(lexed_line, lineno, column)
68
+ end
69
+
70
+ # Checks to see if the actual count of code lines in the method is greater
71
+ # than the value in +@config+.
72
+ #
73
+ # @param [Fixnum] actual_count The number of code lines found.
74
+ # @param [Fixnum] lineno The line the potential problem is on.
75
+ # @param [Fixnum] column The column the potential problem is on.
76
+ def measure(actual_count, lineno, column)
77
+ if actual_count > @config
78
+ @problems << Problem.new(:code_lines_in_method, lineno, column,
79
+ { actual_count: actual_count, should_be_at: @config })
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end