tailor 1.0.0.alpha2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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)