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
@@ -6,9 +6,27 @@ require_relative 'indentation_spaces_ruler/indentation_manager'
6
6
  class Tailor
7
7
  module Rulers
8
8
  class IndentationSpacesRuler < Tailor::Ruler
9
- def initialize(config)
10
- super(config)
9
+ def initialize(config, options)
10
+ super(config, options)
11
+ add_lexer_observers(
12
+ :comment,
13
+ :embexpr_beg,
14
+ :embexpr_end,
15
+ :ignored_nl,
16
+ :kw,
17
+ :lbrace,
18
+ :lbracket,
19
+ :lparen,
20
+ :nl,
21
+ :rbrace,
22
+ :rbracket,
23
+ :rparen,
24
+ :tstring_beg,
25
+ :tstring_end
26
+ )
11
27
  @manager = IndentationManager.new(@config)
28
+ @embexpr_beg = false
29
+ @tstring_nesting = []
12
30
  end
13
31
 
14
32
  def comment_update(token, lexed_line, file_text, lineno, column)
@@ -32,16 +50,18 @@ class Tailor
32
50
  end
33
51
 
34
52
  def embexpr_beg_update
35
- @manager.embexpr_beg = true
53
+ @embexpr_beg = true
36
54
  end
37
55
 
56
+ # Due to a Ripper bug (depending on which Ruby version you have), this may
57
+ # or may not get triggered.
58
+ # More info: https://bugs.ruby-lang.org/issues/6211
38
59
  def embexpr_end_update
39
- @manager.embexpr_beg = false
60
+ @embexpr_beg = false
40
61
  end
41
62
 
42
63
  def ignored_nl_update(current_lexed_line, lineno, column)
43
64
  log "indent reasons on entry: #{@manager.indent_reasons}"
44
- stop if @manager.tstring_nesting.size > 0
45
65
 
46
66
  if current_lexed_line.only_spaces?
47
67
  log "Line of only spaces. Moving on."
@@ -52,13 +72,17 @@ class Tailor
52
72
  if @manager.line_ends_with_single_token_indenter?(current_lexed_line)
53
73
  log "Line ends with single-token indent token."
54
74
 
55
- unless @manager.comma_is_part_of_enclosed_statement?(current_lexed_line, lineno)
75
+ unless @manager.in_an_enclosure? &&
76
+ current_lexed_line.ends_with_comma?
56
77
  log "Line-ending single-token indenter found."
57
78
  token_event = current_lexed_line.last_non_line_feed_event
58
79
 
59
80
  unless @manager.line_ends_with_same_as_last token_event
60
- log "Line ends with different type of single-token indenter: #{token_event}"
61
- @manager.add_indent_reason(token_event[1], token_event.last, lineno)
81
+ msg = "Line ends with different type of single-token "
82
+ msg << "indenter: #{token_event}"
83
+ log msg
84
+ @manager.add_indent_reason(token_event[1], token_event.last,
85
+ lineno)
62
86
  end
63
87
  end
64
88
  end
@@ -73,8 +97,13 @@ class Tailor
73
97
  end
74
98
 
75
99
  def kw_update(token, lexed_line, lineno, column)
100
+ if lexed_line.keyword_is_symbol?
101
+ log "Keyword is prefaced by a :, indicating it's really a Symbol."
102
+ return
103
+ end
104
+
76
105
  if token == "end"
77
- @manager.update_for_closing_reason(:on_kw, lexed_line, lineno)
106
+ @manager.update_for_closing_reason(:on_kw, lexed_line)
78
107
  return
79
108
  end
80
109
 
@@ -117,8 +146,8 @@ class Tailor
117
146
  log "last indent reason type: #{@manager.last_indent_reason_type}"
118
147
  log "I think this is a single-token closing line..."
119
148
 
120
- @manager.update_for_closing_reason(@manager.indent_reasons.last[:event_type],
121
- current_lexed_line, lineno)
149
+ @manager.update_for_closing_reason(@manager.indent_reasons.
150
+ last[:event_type], current_lexed_line)
122
151
  end
123
152
 
124
153
  @manager.set_up_line_transition
@@ -131,17 +160,37 @@ class Tailor
131
160
  @manager.transition_lines
132
161
  end
133
162
 
163
+ # Since Ripper parses the } in a #{} as :on_rbrace instead of
164
+ # :on_embexpr_end, this works around that by using +@embexpr_beg to track
165
+ # the state of that event. As such, this should only be called from
166
+ # #rbrace_update.
167
+ #
168
+ # @return [Boolean]
169
+ def in_embexpr?
170
+ @embexpr_beg == true
171
+ end
172
+
134
173
  def rbrace_update(current_lexed_line, lineno, column)
174
+ if in_embexpr?
175
+ msg = "Got :rbrace and @embexpr_beg is true. "
176
+ msg << " Must be at an @embexpr_end."
177
+ log msg
178
+ @embexpr_beg = false
179
+ return
180
+ end
181
+
135
182
  if @manager.multi_line_braces?(lineno)
136
183
  log "End of multi-line braces!"
137
184
 
138
185
  if current_lexed_line.only_rbrace?
139
186
  @manager.amount_to_change_this -= 1
140
- log "lonely rbrace. change_this -= 1 -> #{@manager.amount_to_change_this}"
187
+ msg = "lonely rbrace. "
188
+ msg << "change_this -= 1 -> #{@manager.amount_to_change_this}"
189
+ log msg
141
190
  end
142
191
  end
143
192
 
144
- @manager.update_for_closing_reason(:on_rbrace, current_lexed_line, lineno)
193
+ @manager.update_for_closing_reason(:on_rbrace, current_lexed_line)
145
194
  end
146
195
 
147
196
  def rbracket_update(current_lexed_line, lineno, column)
@@ -150,11 +199,13 @@ class Tailor
150
199
 
151
200
  if current_lexed_line.only_rbracket?
152
201
  @manager.amount_to_change_this -= 1
153
- log "lonely rbracket. change_this -= 1 -> #{@manager.amount_to_change_this}"
202
+ msg = "lonely rbracket. "
203
+ msg << "change_this -= 1 -> #{@manager.amount_to_change_this}"
204
+ log msg
154
205
  end
155
206
  end
156
207
 
157
- @manager.update_for_closing_reason(:on_rbracket, current_lexed_line, lineno)
208
+ @manager.update_for_closing_reason(:on_rbracket, current_lexed_line)
158
209
  end
159
210
 
160
211
  def rparen_update(current_lexed_line, lineno, column)
@@ -163,21 +214,36 @@ class Tailor
163
214
 
164
215
  if current_lexed_line.only_rparen?
165
216
  @manager.amount_to_change_this -= 1
166
- log "lonely rparen. change_this -= 1 -> #{@manager.amount_to_change_this}"
217
+ msg = "lonely rparen. "
218
+ msg << "change_this -= 1 -> #{@manager.amount_to_change_this}"
219
+ log msg
167
220
  end
168
221
  end
169
222
 
170
- @manager.update_for_closing_reason(:on_rparen, current_lexed_line, lineno)
223
+ @manager.update_for_closing_reason(:on_rparen, current_lexed_line)
171
224
  end
172
225
 
173
- def tstring_beg_update(lineno)
174
- @manager.tstring_nesting << lineno
226
+ def tstring_beg_update(lexed_line, lineno)
227
+ @tstring_nesting << lineno
228
+ @manager.update_actual_indentation(lexed_line)
229
+ log "tstring_nesting is now: #{@tstring_nesting}"
175
230
  @manager.stop
176
231
  end
177
232
 
178
- def tstring_end_update
179
- @manager.tstring_nesting.pop
180
- @manager.start unless @manager.in_tstring?
233
+ def tstring_end_update(current_line)
234
+ unless @tstring_nesting.empty?
235
+ tstring_start_line = @tstring_nesting.pop
236
+
237
+ if tstring_start_line < current_line
238
+ measure(tstring_start_line, @manager.actual_indentation)
239
+ end
240
+ end
241
+
242
+ @manager.start unless in_tstring?
243
+ end
244
+
245
+ def in_tstring?
246
+ !@tstring_nesting.empty?
181
247
  end
182
248
 
183
249
  # Checks if the line's indentation level is appropriate.
@@ -188,10 +254,11 @@ class Tailor
188
254
  log "Measuring..."
189
255
 
190
256
  if @manager.actual_indentation != @manager.should_be_at
191
- @problems << Problem.new(:indentation, lineno, column,
192
- { actual_indentation: @manager.actual_indentation,
193
- should_be_at: @manager.should_be_at }
194
- )
257
+ msg = "Line is indented to column #{@manager.actual_indentation}, "
258
+ msg << "but should be at #{@manager.should_be_at}."
259
+
260
+ @problems << Problem.new(problem_type, lineno,
261
+ @manager.actual_indentation, msg, @options[:level])
195
262
  end
196
263
  end
197
264
  end
@@ -1,14 +1,30 @@
1
1
  require_relative '../../ruler'
2
2
  require_relative '../../logger'
3
- require_relative '../../lexer_constants'
3
+ require_relative '../../lexer/lexer_constants'
4
4
 
5
5
  class Tailor
6
6
  module Rulers
7
7
  class IndentationSpacesRuler < Tailor::Ruler
8
+
9
+ # Used for managing the state of indentation for some file/text. An
10
+ # object of this type has no knowledge of the file/text itself, but rather
11
+ # just manages indentation expectations based on the object's user's
12
+ # input, somewhat like a state machine.
13
+ #
14
+ # For the sake of talking about indentation expectations, the docs here
15
+ # make mention of 'levels' of indentation. A _level_ here is simply
16
+ # 1 * (number of spaces to indent); so if you've set (number of spaces to
17
+ # indent) to 2, saying something should be indented 1 level, is simply
18
+ # saying that it should be indented 2 spaces.
8
19
  class IndentationManager
9
20
  include Tailor::LexerConstants
10
21
  include Tailor::Logger::Mixin
11
22
 
23
+ # These are event names generated by the {Lexer} that signify
24
+ # indentation level should/could increase by 1.
25
+ ENCLOSERS = Set.new [:on_lbrace, :on_lbracket, :on_lparen]
26
+
27
+ # Look-up table that allows for <tt>OPEN_EVENT_FOR[:on_rbrace]</tt>.
12
28
  OPEN_EVENT_FOR = {
13
29
  on_kw: :on_kw,
14
30
  on_rbrace: :on_lbrace,
@@ -16,25 +32,27 @@ class Tailor
16
32
  on_rparen: :on_lparen
17
33
  }
18
34
 
19
- attr_accessor :amount_to_change_next
35
+ # Allows for updating the indent expectation for the current line.
20
36
  attr_accessor :amount_to_change_this
21
- attr_accessor :embexpr_beg
22
37
 
38
+ # @return [Fixnum] The actual number of characters the current line is
39
+ # indented.
23
40
  attr_reader :actual_indentation
41
+
42
+ # @return [Array<Hash>] Each element represents a reason why code should
43
+ # be indented. Indent levels are not necessarily 1:1 relationship
44
+ # to these reasons (hence the need for this class).
24
45
  attr_reader :indent_reasons
25
- attr_reader :tstring_nesting
26
46
 
47
+ # @param [Fixnum] spaces The number of spaces each level of indentation
48
+ # should move in & out.
27
49
  def initialize(spaces)
28
50
  @spaces = spaces
29
-
30
- log "Setting @proper[:this_line] to 0."
31
51
  @proper = { this_line: 0, next_line: 0 }
32
52
  @actual_indentation = 0
33
-
34
53
  @indent_reasons = []
35
- @tstring_nesting = []
36
-
37
54
  @amount_to_change_this = 0
55
+
38
56
  start
39
57
  end
40
58
 
@@ -44,7 +62,7 @@ class Tailor
44
62
  end
45
63
 
46
64
  # Decreases the indentation expectation for the current line by
47
- # +@spaces+.
65
+ # 1 level.
48
66
  def decrease_this_line
49
67
  if started?
50
68
  @proper[:this_line] -= @spaces
@@ -60,10 +78,8 @@ class Tailor
60
78
  end
61
79
  end
62
80
 
63
-
64
81
  # 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.
82
+ # to change this and next lines, given in +@amount_to_change_this+.
67
83
  def set_up_line_transition
68
84
  log "Amount to change this line: #{@amount_to_change_this}"
69
85
  decrease_this_line if @amount_to_change_this < 0
@@ -133,6 +149,7 @@ class Tailor
133
149
  lexed_line.ends_with_op? ||
134
150
  lexed_line.ends_with_comma? ||
135
151
  lexed_line.ends_with_period? ||
152
+ lexed_line.ends_with_label? ||
136
153
  lexed_line.ends_with_modifier_kw?
137
154
  end
138
155
 
@@ -148,61 +165,37 @@ class Tailor
148
165
  @indent_reasons.last[:event_type] == token_event[1]
149
166
  end
150
167
 
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.
168
+ # Determines if the current spot in the file is enclosed in braces,
169
+ # brackets, or parens.
155
170
  #
156
- # @param [LexedLine] lexed_line
157
- # @param [Fixnum] lineno
158
171
  # @return [Boolean]
159
- def comma_is_part_of_enclosed_statement?(lexed_line, lineno)
172
+ def in_an_enclosure?
160
173
  return false if @indent_reasons.empty?
161
174
 
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?
175
+ i_reasons = @indent_reasons.dup
176
+ log "i reasons: #{i_reasons}"
177
177
 
178
- kw_token = d_tokens.reverse.find do |t|
179
- ['{', '[', '('].none? { |e| t[:token] == e }
178
+ until ENCLOSERS.include? i_reasons.last[:event_type]
179
+ i_reasons.pop
180
+ break if i_reasons.empty?
180
181
  end
181
182
 
182
- return false if kw_token.nil?
183
+ return false if i_reasons.empty?
183
184
 
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}
185
+ i_reasons.last[:event_type] == :on_lbrace ||
186
+ i_reasons.last[:event_type] == :on_lbracket ||
187
+ i_reasons.last[:event_type] == :on_lparen
204
188
  end
205
189
 
190
+ # Adds to the list of reasons to indent the next line, then increases
191
+ # the expectation for the next line by +@spaces+.
192
+ #
193
+ # @param [Symbol] event_type The event type that caused the reason for
194
+ # indenting.
195
+ # @param [Tailor::Token,String] token The token that caused the reason
196
+ # for indenting.
197
+ # @param [Fixnum] lineno The line number the reason for indenting was
198
+ # discovered on.
206
199
  def add_indent_reason(event_type, token, lineno)
207
200
  @indent_reasons << {
208
201
  event_type: event_type,
@@ -216,6 +209,14 @@ class Tailor
216
209
  @indent_reasons.each { |r| log r.to_s }
217
210
  end
218
211
 
212
+ # An "opening reason" is a reason for indenting that also has a "closing
213
+ # reason", such as a +def+, +{+, +[+, +(+.
214
+ #
215
+ # @param [Symbol] event_type The event type that is the opening reason.
216
+ # @param [Tailor::Token,String] token The token that is the opening
217
+ # reasons.
218
+ # @param [Fixnum] lineno The line number the opening reason was found
219
+ # on.
219
220
  def update_for_opening_reason(event_type, token, lineno)
220
221
  if token.modifier_keyword?
221
222
  log "Found modifier in line: '#{token}'"
@@ -232,6 +233,14 @@ class Tailor
232
233
  add_indent_reason(event_type, token, lineno)
233
234
  end
234
235
 
236
+ # A "continuation reason" is a reason for indenting & outdenting that's
237
+ # not an opening or closing reason, such as +elsif+, +rescue+, +when+
238
+ # (in a +case+ statement), etc.
239
+ #
240
+ # @param [Symbol] event_type The event type that is the opening reason.
241
+ # @param [Tailor::LexedLine] lexed_line
242
+ # @param [Fixnum] lineno The line number the opening reason was found
243
+ # on.
235
244
  def update_for_continuation_reason(token, lexed_line, lineno)
236
245
  d_tokens = @indent_reasons.dup
237
246
  d_tokens.pop
@@ -247,22 +256,24 @@ class Tailor
247
256
 
248
257
  last_reason_line = @indent_reasons.find { |r| r[:lineno] == lineno }
249
258
 
250
- if last_reason_line.nil?
251
- @proper[:next_line] = @indent_reasons.last[:should_be_at] + @spaces
259
+ @proper[:next_line] = if last_reason_line.nil?
260
+ if @indent_reasons.empty?
261
+ @spaces
262
+ else
263
+ @indent_reasons.last[:should_be_at] + @spaces
264
+ end
252
265
  else
253
- @proper[:next_line] = @indent_reasons.last[:should_be_at] - @spaces
266
+ @indent_reasons.last[:should_be_at] - @spaces
254
267
  end
255
268
  end
256
269
 
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
-
270
+ # A "closing reason" is a reason for indenting that also has an "opening
271
+ # reason", such as a +end+, +}+, +]+, +)+.
272
+ #
273
+ # @param [Symbol] event_type The event type that is the closing reason.
274
+ # @param [Tailor::Token,String] token The token that is the closing
275
+ # reason.
276
+ def update_for_closing_reason(event_type, lexed_line)
266
277
  remove_continuation_keywords
267
278
  remove_appropriate_reason(event_type)
268
279
 
@@ -284,12 +295,14 @@ class Tailor
284
295
  end
285
296
  end
286
297
 
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
298
+ # Removes the last matching opening reason reason of +event_type+ from
299
+ # the list of indent reasons.
300
+ #
301
+ # @param [Symbol] closing_event_type The closing event for which to find
302
+ # the matching opening event to remove from the list of indent
303
+ # reasons.
304
+ def remove_appropriate_reason(closing_event_type)
305
+ if last_opening_event = last_opening_event(closing_event_type)
293
306
  r_index = @indent_reasons.reverse.index(last_opening_event)
294
307
  index = @indent_reasons.size - r_index - 1
295
308
  tmp_reasons = []
@@ -299,30 +312,64 @@ class Tailor
299
312
  end
300
313
 
301
314
  @indent_reasons.replace(tmp_reasons)
315
+ elsif last_single_token_event
316
+ log "Just popped off reason: #{@indent_reasons.pop}"
302
317
  else
303
- @indent_reasons.pop
318
+ log "Couldn't find a matching opening reason to pop off...'"
319
+ return
304
320
  end
305
321
 
306
322
  log "Removed indent reason; it's now:"
307
323
  @indent_reasons.each { |r| log r.to_s }
308
324
  end
309
325
 
326
+ # A "single-token" event is one that that causes indentation
327
+ # expectations to increase. They don't have have a paired closing
328
+ # reason like opening reasons. Instead, they're determined to be done
329
+ # with their indenting when an :on_ignored_nl occurs. Single-token
330
+ # events are operators and commas (commas that aren't used as
331
+ # separators in {, [, ( events).
332
+ def last_single_token_event
333
+ return nil if @indent_reasons.empty?
334
+
335
+ @indent_reasons.reverse.find do |r|
336
+ !ENCLOSERS.include? r[:event_type] &&
337
+ r[:event_type] != :on_kw
338
+ end
339
+ end
340
+
341
+
342
+ # Returns the last matching opening event that corresponds to the
343
+ # +closing_event_type+.
344
+ #
345
+ # @param [Symbol] closing_event_type The closing event for which to
346
+ # find its associated opening event.
347
+ def last_opening_event(closing_event_type)
348
+ return nil if @indent_reasons.empty?
349
+
350
+ @indent_reasons.reverse.find do |r|
351
+ r[:event_type] == OPEN_EVENT_FOR[closing_event_type]
352
+ end
353
+ end
354
+
310
355
  def last_indent_reason_type
311
356
  return if @indent_reasons.empty?
312
357
 
313
358
  @indent_reasons.last[:event_type]
314
359
  end
315
360
 
361
+ # Removes all continuation keywords from the list of
362
+ # indentation reasons.
316
363
  def remove_continuation_keywords
317
364
  return if @indent_reasons.empty?
318
365
 
319
366
  while CONTINUATION_KEYWORDS.include?(@indent_reasons.last[:token])
320
- @indent_reasons.pop
367
+ log "Just popped off continuation reason: #{@indent_reasons.pop}"
321
368
  end
322
369
  end
323
370
 
324
- # Overriding to be able to call #multi_line_brackets?,
325
- # #multi_line_braces?, and #multi_line_parens?, where each takes a
371
+ # Overriding to be able to call +#multi_line_brackets?+,
372
+ # +#multi_line_braces?+, and +#multi_line_parens?+, where each takes a
326
373
  # single parameter, which is the lineno.
327
374
  #
328
375
  # @return [Boolean]
@@ -342,6 +389,7 @@ class Tailor
342
389
  t[:token] == token
343
390
  end
344
391
 
392
+ log "#{meth} called, but no #{$1} were found." if tokens.empty?
345
393
  return false if tokens.empty?
346
394
 
347
395
  token_on_this_line = tokens.find { |t| t[:lineno] == lineno }
@@ -352,10 +400,6 @@ class Tailor
352
400
  super(meth, *args, &blk)
353
401
  end
354
402
  end
355
-
356
- def in_tstring?
357
- !@tstring_nesting.empty?
358
- end
359
403
  end
360
404
  end
361
405
  end