tailor 1.0.0.alpha2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/.gitignore +1 -0
  2. data/.tailor +10 -2
  3. data/Gemfile.lock +2 -2
  4. data/History.rdoc +20 -0
  5. data/README.rdoc +176 -26
  6. data/features/configurable.feature +19 -39
  7. data/features/horizontal_spacing.feature +3 -2
  8. data/features/indentation.feature +2 -2
  9. data/features/indentation/bad_files_with_no_trailing_newline.feature +9 -8
  10. data/features/indentation/good_files_with_no_trailing_newline.feature +19 -6
  11. data/features/name_detection.feature +2 -2
  12. data/features/support/env.rb +0 -2
  13. data/features/support/file_cases/horizontal_spacing_cases.rb +5 -4
  14. data/features/support/file_cases/indentation_cases.rb +105 -54
  15. data/features/support/file_cases/naming_cases.rb +0 -1
  16. data/features/support/file_cases/vertical_spacing_cases.rb +0 -1
  17. data/features/support/legacy/bad_ternary_colon_spacing.rb +1 -1
  18. data/features/valid_ruby.feature +17 -0
  19. data/features/vertical_spacing.feature +40 -19
  20. data/lib/ext/string_ext.rb +12 -0
  21. data/lib/tailor/cli.rb +7 -5
  22. data/lib/tailor/cli/options.rb +13 -3
  23. data/lib/tailor/composite_observable.rb +17 -2
  24. data/lib/tailor/configuration.rb +83 -72
  25. data/lib/tailor/configuration/style.rb +85 -0
  26. data/lib/tailor/critic.rb +67 -117
  27. data/lib/tailor/formatter.rb +38 -0
  28. data/lib/tailor/formatters/text.rb +35 -10
  29. data/lib/tailor/lexed_line.rb +38 -5
  30. data/lib/tailor/lexer.rb +150 -14
  31. data/lib/tailor/{lexer_constants.rb → lexer/lexer_constants.rb} +9 -7
  32. data/lib/tailor/lexer/token.rb +6 -2
  33. data/lib/tailor/logger.rb +4 -0
  34. data/lib/tailor/problem.rb +8 -73
  35. data/lib/tailor/reporter.rb +1 -1
  36. data/lib/tailor/ruler.rb +67 -6
  37. data/lib/tailor/rulers/allow_camel_case_methods_ruler.rb +9 -1
  38. data/lib/tailor/rulers/allow_hard_tabs_ruler.rb +9 -1
  39. data/lib/tailor/rulers/allow_invalid_ruby_ruler.rb +38 -0
  40. data/lib/tailor/rulers/allow_screaming_snake_case_classes_ruler.rb +9 -2
  41. data/lib/tailor/rulers/allow_trailing_line_spaces_ruler.rb +10 -5
  42. data/lib/tailor/rulers/indentation_spaces_ruler.rb +93 -26
  43. data/lib/tailor/rulers/indentation_spaces_ruler/indentation_manager.rb +128 -84
  44. data/lib/tailor/rulers/max_code_lines_in_class_ruler.rb +9 -5
  45. data/lib/tailor/rulers/max_code_lines_in_method_ruler.rb +9 -5
  46. data/lib/tailor/rulers/max_line_length_ruler.rb +10 -5
  47. data/lib/tailor/rulers/spaces_after_comma_ruler.rb +13 -4
  48. data/lib/tailor/rulers/spaces_after_lbrace_ruler.rb +8 -4
  49. data/lib/tailor/rulers/spaces_after_lbracket_ruler.rb +8 -4
  50. data/lib/tailor/rulers/spaces_after_lparen_ruler.rb +8 -4
  51. data/lib/tailor/rulers/spaces_before_comma_ruler.rb +8 -4
  52. data/lib/tailor/rulers/spaces_before_lbrace_ruler.rb +13 -6
  53. data/lib/tailor/rulers/spaces_before_rbrace_ruler.rb +12 -8
  54. data/lib/tailor/rulers/spaces_before_rbracket_ruler.rb +12 -5
  55. data/lib/tailor/rulers/spaces_before_rparen_ruler.rb +13 -6
  56. data/lib/tailor/rulers/spaces_in_empty_braces_ruler.rb +13 -9
  57. data/lib/tailor/rulers/trailing_newlines_ruler.rb +10 -5
  58. data/lib/tailor/tailorrc.erb +3 -3
  59. data/lib/tailor/version.rb +1 -1
  60. data/m.rb +15 -0
  61. data/spec/spec_helper.rb +0 -1
  62. data/spec/tailor/cli_spec.rb +8 -9
  63. data/spec/tailor/composite_observable_spec.rb +41 -0
  64. data/spec/tailor/configuration/style_spec.rb +197 -0
  65. data/spec/tailor/configuration_spec.rb +52 -33
  66. data/spec/tailor/critic_spec.rb +7 -8
  67. data/spec/tailor/formatter_spec.rb +52 -0
  68. data/spec/tailor/lexed_line_spec.rb +236 -88
  69. data/spec/tailor/lexer_spec.rb +8 -63
  70. data/spec/tailor/problem_spec.rb +14 -46
  71. data/spec/tailor/reporter_spec.rb +8 -8
  72. data/spec/tailor/ruler_spec.rb +1 -1
  73. data/spec/tailor/rulers/indentation_spaces_ruler/indentation_manager_spec.rb +132 -176
  74. data/spec/tailor/rulers/indentation_spaces_ruler_spec.rb +41 -33
  75. data/spec/tailor/rulers/{spaces_after_comma_spec.rb → spaces_after_comma_ruler_spec.rb} +5 -5
  76. data/spec/tailor/rulers/spaces_after_lbrace_ruler_spec.rb +14 -14
  77. data/spec/tailor/rulers/spaces_before_lbrace_ruler_spec.rb +1 -1
  78. data/spec/tailor/rulers/spaces_before_rbrace_ruler_spec.rb +1 -1
  79. data/spec/tailor/version_spec.rb +1 -1
  80. data/spec/tailor_spec.rb +3 -1
  81. data/tailor.gemspec +11 -3
  82. data/uest.rb +9 -0
  83. metadata +66 -41
  84. data/features/step_definitions/spacing/commas_steps.rb +0 -14
@@ -1,9 +1,12 @@
1
1
  require_relative 'logger'
2
- require_relative 'lexer_constants'
2
+ require_relative 'lexer/lexer_constants'
3
3
  require_relative 'lexer/token'
4
4
  require 'ripper'
5
5
 
6
6
  class Tailor
7
+
8
+ # This class provides methods for finding info about the current line. It
9
+ # works off the format that results from {Tailor::Lexer}.
7
10
  class LexedLine < Array
8
11
  include LexerConstants
9
12
 
@@ -97,6 +100,9 @@ class Tailor
97
100
  previous_event.first.last.zero? || previous_event.first.last.nil?
98
101
  end
99
102
 
103
+ # Allows for calling a couple styles of methods:
104
+ # * #ends_with_(.+)? - Allows for checking if the line ends with (.+)
105
+ # * #only_(.+)? - Allows for checking if the line is only spaces and (.+)
100
106
  def method_missing(meth, *args, &blk)
101
107
  if meth.to_s =~ /^ends_with_(.+)\?$/
102
108
  event = "on_#{$1}".to_sym
@@ -145,10 +151,17 @@ class Tailor
145
151
  end
146
152
 
147
153
  # @return [Array] The lexed event that represents the last event in the
148
- # line that's not a +\n+.
154
+ # line that's not a line-feed. Line-feed events are signified by
155
+ # +:on_nl+ and +on_ignored_nl+ events, and by +:on_sp+ events when they
156
+ # equal +"\\\n" (which occurs when a line is broken by a backslash).
149
157
  def last_non_line_feed_event
150
- self.find_all { |e| e[1] != :on_nl && e[1] != :on_ignored_nl }.last ||
151
- [[]]
158
+ events = self.find_all do |e|
159
+ e[1] != :on_nl &&
160
+ e[1] != :on_ignored_nl &&
161
+ e.last != "\\\n"
162
+ end
163
+
164
+ events.last || [[]]
152
165
  end
153
166
 
154
167
  # @return [Fixnum] The length of the line minus the +\n+.
@@ -184,7 +197,7 @@ class Tailor
184
197
  end
185
198
 
186
199
  # If a trailing comment exists in the line, remove it and the spaces that
187
- # come before it. This is necessary, as {Ripper} doesn't trigger an event
200
+ # come before it. This is necessary, as +Ripper+ doesn't trigger an event
188
201
  # for the end of the line when the line ends with a comment. Without this
189
202
  # observers that key off ending the line will never get triggered, and thus
190
203
  # style won't get checked for that line.
@@ -230,6 +243,26 @@ class Tailor
230
243
  self.none? { |e| e[1] == :on_tstring_beg }
231
244
  end
232
245
 
246
+ # When Ripper lexes a Symbol, it generates one event for :on_symbeg, which
247
+ # is the ':' token, and one for the name of the Symbol. Since your Symbol
248
+ # name can be anything, the second event could be something like "class", in
249
+ # which case :on_kw will get called and probably result in unexpected
250
+ # behavior.
251
+ #
252
+ # This assumes the keyword in question is the last event in the line.
253
+ #
254
+ # @return [Boolean]
255
+ def keyword_is_symbol?
256
+ current_index = self.index(self.last)
257
+ previous_event = self.at(current_index - 1)
258
+
259
+ return false if previous_event.nil?
260
+ return false unless self.last[1] == :on_kw
261
+ return false unless previous_event[1] == :on_symbeg
262
+
263
+ true
264
+ end
265
+
233
266
  #---------------------------------------------------------------------------
234
267
  # Privates!
235
268
  #---------------------------------------------------------------------------
data/lib/tailor/lexer.rb CHANGED
@@ -1,15 +1,16 @@
1
1
  require 'ripper'
2
2
  require_relative 'composite_observable'
3
3
  require_relative 'lexed_line'
4
- require_relative 'lexer_constants'
4
+ require_relative 'lexer/lexer_constants'
5
5
  require_relative 'logger'
6
6
  require_relative 'lexer/token'
7
7
 
8
8
 
9
9
  class Tailor
10
10
 
11
- # https://github.com/svenfuchs/ripper2ruby/blob/303d7ac4dfc2d8dbbdacaa6970fc41ff56b31d82/notes/scanner_events
12
- # https://github.com/ruby/ruby/blob/trunk/test/ripper/test_scanner_events.rb
11
+ # This is what provides the main file parsing for tailor. For every event
12
+ # that's encountered, it calls the appropriate notifier method. Notifier
13
+ # methods are provided by {Tailor::CompositeObservable}.
13
14
  class Lexer < Ripper::Lexer
14
15
  include CompositeObservable
15
16
  include LexerConstants
@@ -31,9 +32,16 @@ class Tailor
31
32
  @added_newline = @file_text != @original_file_text
32
33
  end
33
34
 
34
- def check_added_newline
35
- file_changed
36
- notify_file_observers(count_trailing_newlines(@original_file_text))
35
+ # This kicks off the process of parsing the file and publishing events
36
+ # as the events are discovered.
37
+ def lex
38
+ file_beg_changed
39
+ notify_file_beg_observers(@file_name)
40
+
41
+ super
42
+
43
+ file_end_changed
44
+ notify_file_end_observers(count_trailing_newlines(@original_file_text))
37
45
  end
38
46
 
39
47
  def on_backref(token)
@@ -41,11 +49,18 @@ class Tailor
41
49
  super(token)
42
50
  end
43
51
 
52
+ # Called when the lexer matches the first ` in a `` statement (the second
53
+ # matches :on_tstring_end; this may or may not be a Ruby bug).
54
+ #
55
+ # @param [String] token The token that the lexer matched.
44
56
  def on_backtick(token)
45
57
  log "BACKTICK: '#{token}'"
46
58
  super(token)
47
59
  end
48
60
 
61
+ # Called when the lexer matches a comma.
62
+ #
63
+ # @param [String] token The token that the lexer matched.
49
64
  def on_comma(token)
50
65
  log "COMMA: #{token}"
51
66
  log "Line length: #{current_line_of_text.length}"
@@ -56,6 +71,10 @@ class Tailor
56
71
  super(token)
57
72
  end
58
73
 
74
+ # Called when the lexer matches a #. The token includes the # as well as
75
+ # the content after it.
76
+ #
77
+ # @param [String] token The token that the lexer matched.
59
78
  def on_comment(token)
60
79
  log "COMMENT: '#{token}'"
61
80
 
@@ -67,6 +86,10 @@ class Tailor
67
86
  super(token)
68
87
  end
69
88
 
89
+ # Called when the lexer matches a constant (including class names, of
90
+ # course).
91
+ #
92
+ # @param [String] token The token that the lexer matched.
70
93
  def on_const(token)
71
94
  log "CONST: '#{token}'"
72
95
 
@@ -78,27 +101,41 @@ class Tailor
78
101
  super(token)
79
102
  end
80
103
 
104
+ # Called when the lexer matches a class variable.
105
+ #
106
+ # @param [String] token The token that the lexer matched.
81
107
  def on_cvar(token)
82
108
  log "CVAR: '#{token}'"
83
109
  super(token)
84
110
  end
85
111
 
112
+ # Called when the lexer matches the content inside a =begin/=end.
113
+ #
114
+ # @param [String] token The token that the lexer matched.
86
115
  def on_embdoc(token)
87
116
  log "EMBDOC: '#{token}'"
88
117
  super(token)
89
118
  end
90
119
 
120
+ # Called when the lexer matches =begin.
121
+ #
122
+ # @param [String] token The token that the lexer matched.
91
123
  def on_embdoc_beg(token)
92
124
  log "EMBDOC_BEG: '#{token}'"
93
125
  super(token)
94
126
  end
95
127
 
128
+ # Called when the lexer matches =end.
129
+ #
130
+ # @param [String] token The token that the lexer matched.
96
131
  def on_embdoc_end(token)
97
132
  log "EMBDOC_BEG: '#{token}'"
98
133
  super(token)
99
134
  end
100
135
 
101
- # Matches the { in an expression embedded in a string.
136
+ # Called when the lexer matches a #{.
137
+ #
138
+ # @param [String] token The token that the lexer matched.
102
139
  def on_embexpr_beg(token)
103
140
  log "EMBEXPR_BEG: '#{token}'"
104
141
  embexpr_beg_changed
@@ -106,6 +143,11 @@ class Tailor
106
143
  super(token)
107
144
  end
108
145
 
146
+ # Called when the lexer matches the } that closes a #{. Note that as of
147
+ # MRI 1.9.3-p125, this never gets called. Logged as a bug and fixed, but
148
+ # not yet released: https://bugs.ruby-lang.org/issues/6211.
149
+ #
150
+ # @param [String] token The token that the lexer matched.
109
151
  def on_embexpr_end(token)
110
152
  log "EMBEXPR_END: '#{token}'"
111
153
  embexpr_end_changed
@@ -118,27 +160,42 @@ class Tailor
118
160
  super(token)
119
161
  end
120
162
 
163
+ # Called when the lexer matches a Float.
164
+ #
165
+ # @param [String] token The token that the lexer matched.
121
166
  def on_float(token)
122
167
  log "FLOAT: '#{token}'"
123
168
  super(token)
124
169
  end
125
170
 
126
- # Global variable
171
+ # Called when the lexer matches a global variable.
172
+ #
173
+ # @param [String] token The token that the lexer matched.
127
174
  def on_gvar(token)
128
175
  log "GVAR: '#{token}'"
129
176
  super(token)
130
177
  end
131
178
 
179
+ # Called when the lexer matches the beginning of a heredoc.
180
+ #
181
+ # @param [String] token The token that the lexer matched.
132
182
  def on_heredoc_beg(token)
133
183
  log "HEREDOC_BEG: '#{token}'"
134
184
  super(token)
135
185
  end
136
186
 
187
+ # Called when the lexer matches the end of a heredoc.
188
+ #
189
+ # @param [String] token The token that the lexer matched.
137
190
  def on_heredoc_end(token)
138
191
  log "HEREDOC_END: '#{token}'"
139
192
  super(token)
140
193
  end
141
194
 
195
+ # Called when the lexer matches an identifier (method name, variable, the
196
+ # text part of a Symbol, etc.).
197
+ #
198
+ # @param [String] token The token that the lexer matched.
142
199
  def on_ident(token)
143
200
  log "IDENT: '#{token}'"
144
201
  l_token = Tailor::Lexer::Token.new(token)
@@ -163,18 +220,23 @@ class Tailor
163
220
  super(token)
164
221
  end
165
222
 
223
+ # Called when the lexer matches an Integer.
224
+ #
225
+ # @param [String] token The token that the lexer matched.
166
226
  def on_int(token)
167
227
  log "INT: '#{token}'"
168
228
  super(token)
169
229
  end
170
230
 
171
- # Instance variable
231
+ # Called when the lexer matches an instance variable.
232
+ #
233
+ # @param [String] token The token that the lexer matched.
172
234
  def on_ivar(token)
173
235
  log "IVAR: '#{token}'"
174
236
  super(token)
175
237
  end
176
238
 
177
- # Called when the lexer matches a Ruby keyword
239
+ # Called when the lexer matches a Ruby keyword.
178
240
  #
179
241
  # @param [String] token The token that the lexer matched.
180
242
  def on_kw(token)
@@ -194,13 +256,20 @@ class Tailor
194
256
  super(token)
195
257
  end
196
258
 
259
+ # Called when the lexer matches a label (the first part in a non-rocket
260
+ # style Hash).
261
+ #
262
+ # Example:
263
+ # one: 1 # Matches one:
264
+ #
265
+ # @param [String] token The token that the lexer matched.
197
266
  def on_label(token)
198
267
  log "LABEL: '#{token}'"
199
268
  super(token)
200
269
  end
201
270
 
202
271
  # Called when the lexer matches a {. Note a #{ match calls
203
- # {on_embexpr_beg}.
272
+ # +#on_embexpr_beg+.
204
273
  #
205
274
  # @param [String] token The token that the lexer matched.
206
275
  def on_lbrace(token)
@@ -222,6 +291,9 @@ class Tailor
222
291
  super(token)
223
292
  end
224
293
 
294
+ # Called when the lexer matches a (.
295
+ #
296
+ # @param [String] token The token that the lexer matched.
225
297
  def on_lparen(token)
226
298
  log "LPAREN: '#{token}'"
227
299
  lparen_changed
@@ -240,12 +312,17 @@ class Tailor
240
312
  super(token)
241
313
  end
242
314
 
243
- # Operators
315
+ # Called when the lexer matches an operator.
316
+ #
317
+ # @param [String] token The token that the lexer matched.
244
318
  def on_op(token)
245
319
  log "OP: '#{token}'"
246
320
  super(token)
247
321
  end
248
322
 
323
+ # Called when the lexer matches a period.
324
+ #
325
+ # @param [String] token The token that the lexer matched.
249
326
  def on_period(token)
250
327
  log "PERIOD: '#{token}'"
251
328
 
@@ -255,6 +332,10 @@ class Tailor
255
332
  super(token)
256
333
  end
257
334
 
335
+ # Called when the lexer matches '%w'. Statement is ended by a
336
+ # +:on_words_end+.
337
+ #
338
+ # @param [String] token The token that the lexer matched.
258
339
  def on_qwords_beg(token)
259
340
  log "QWORDS_BEG: '#{token}'"
260
341
  super(token)
@@ -286,16 +367,25 @@ class Tailor
286
367
  super(token)
287
368
  end
288
369
 
370
+ # Called when the lexer matches the beginning of a Regexp.
371
+ #
372
+ # @param [String] token The token that the lexer matched.
289
373
  def on_regexp_beg(token)
290
374
  log "REGEXP_BEG: '#{token}'"
291
375
  super(token)
292
376
  end
293
377
 
378
+ # Called when the lexer matches the end of a Regexp.
379
+ #
380
+ # @param [String] token The token that the lexer matched.
294
381
  def on_regexp_end(token)
295
382
  log "REGEXP_END: '#{token}'"
296
383
  super(token)
297
384
  end
298
385
 
386
+ # Called when the lexer matches a ).
387
+ #
388
+ # @param [String] token The token that the lexer matched.
299
389
  def on_rparen(token)
300
390
  log "RPAREN: '#{token}'"
301
391
 
@@ -306,68 +396,114 @@ class Tailor
306
396
  super(token)
307
397
  end
308
398
 
399
+ # Called when the lexer matches a ;.
400
+ #
401
+ # @param [String] token The token that the lexer matched.
309
402
  def on_semicolon(token)
310
403
  log "SEMICOLON: '#{token}'"
311
404
  super(token)
312
405
  end
313
406
 
407
+ # Called when the lexer matches any type of space character.
408
+ #
409
+ # @param [String] token The token that the lexer matched.
314
410
  def on_sp(token)
315
411
  log "SP: '#{token}'; size: #{token.size}"
316
412
  l_token = Tailor::Lexer::Token.new(token)
317
413
  sp_changed
318
414
  notify_sp_observers(l_token, lineno, column)
415
+
416
+ # Deal with lines that end with \
417
+ if token == "\\\n"
418
+ current_line = LexedLine.new(super, lineno)
419
+ ignored_nl_changed
420
+ notify_ignored_nl_observers(current_line, lineno, column)
421
+ end
319
422
  super(token)
320
423
  end
321
424
 
425
+ # Called when the lexer matches the : at the beginning of a Symbol.
426
+ #
427
+ # @param [String] token The token that the lexer matched.
322
428
  def on_symbeg(token)
323
429
  log "SYMBEG: '#{token}'"
324
430
  super(token)
325
431
  end
326
432
 
433
+ # Called when the lexer matches the -> as a lambda.
434
+ #
435
+ # @param [String] token The token that the lexer matched.
327
436
  def on_tlambda(token)
328
437
  log "TLAMBDA: '#{token}'"
329
438
  super(token)
330
439
  end
331
440
 
441
+ # Called when the lexer matches the { that represents the beginning of a
442
+ # -> lambda.
443
+ #
444
+ # @param [String] token The token that the lexer matched.
332
445
  def on_tlambeg(token)
333
446
  log "TLAMBEG: '#{token}'"
334
447
  super(token)
335
448
  end
336
449
 
450
+ # Called when the lexer matches the beginning of a String.
451
+ #
452
+ # @param [String] token The token that the lexer matched.
337
453
  def on_tstring_beg(token)
338
454
  log "TSTRING_BEG: '#{token}'"
455
+ current_line = LexedLine.new(super, lineno)
339
456
  tstring_beg_changed
340
- notify_tstring_beg_observers(lineno)
457
+ notify_tstring_beg_observers(current_line, lineno)
341
458
  super(token)
342
459
  end
343
460
 
461
+ # Called when the lexer matches the content of any String.
462
+ #
463
+ # @param [String] token The token that the lexer matched.
344
464
  def on_tstring_content(token)
345
465
  log "TSTRING_CONTENT: '#{token}'"
346
466
  super(token)
347
467
  end
348
468
 
469
+ # Called when the lexer matches the end of a String.
470
+ #
471
+ # @param [String] token The token that the lexer matched.
349
472
  def on_tstring_end(token)
350
473
  log "TSTRING_END: '#{token}'"
351
474
  tstring_end_changed
352
- notify_tstring_end_observers
475
+ notify_tstring_end_observers(lineno)
353
476
  super(token)
354
477
  end
355
478
 
479
+ # Called when the lexer matches '%W'.
480
+ #
481
+ # @param [String] token The token that the lexer matched.
356
482
  def on_words_beg(token)
357
483
  log "WORDS_BEG: '#{token}'"
358
484
  super(token)
359
485
  end
360
486
 
487
+ # Called when the lexer matches the separators in a %w or %W (by default,
488
+ # this is a single space).
489
+ #
490
+ # @param [String] token The token that the lexer matched.
361
491
  def on_words_sep(token)
362
492
  log "WORDS_SEP: '#{token}'"
363
493
  super(token)
364
494
  end
365
495
 
496
+ # Called when the lexer matches __END__.
497
+ #
498
+ # @param [String] token The token that the lexer matched.
366
499
  def on___end__(token)
367
500
  log "__END__: '#{token}'"
368
501
  super(token)
369
502
  end
370
503
 
504
+ # Called when the lexer matches CHAR.
505
+ #
506
+ # @param [String] token The token that the lexer matched.
371
507
  def on_CHAR(token)
372
508
  log "CHAR: '#{token}'"
373
509
  super(token)