rufo 0.0.32 → 0.0.33

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rufo.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Rufo
2
- class Bug < Exception; end
2
+ class Bug < StandardError; end
3
3
 
4
- class SyntaxError < Exception; end
4
+ class SyntaxError < StandardError; end
5
5
 
6
6
  def self.format(code, **options)
7
7
  Formatter.format(code, **options)
@@ -49,6 +49,12 @@ class Rufo::Formatter
49
49
  # The current hash or call or method that has hash-like parameters
50
50
  @current_hash = nil
51
51
 
52
+ @current_type = nil
53
+
54
+ # Are we inside a type body?
55
+ @inside_type_body = false
56
+ @visibility_indent_in_action = {}
57
+
52
58
  # Map lines to commands that start at the begining of a line with the following info:
53
59
  # - line indent
54
60
  # - first param indent
@@ -138,93 +144,167 @@ class Rufo::Formatter
138
144
  @case_when_positions = []
139
145
 
140
146
  # Settings
141
- indent_size options.fetch(:indent_size, 2)
142
- space_after_hash_brace options.fetch(:space_after_hash_brace, :dynamic)
143
- space_after_array_bracket options.fetch(:space_after_array_bracket, :dynamic)
144
- align_comments options.fetch(:align_comments, false)
145
- align_assignments options.fetch(:align_assignments, false)
146
- align_hash_keys options.fetch(:align_hash_keys, false)
147
- align_case_when options.fetch(:align_case_when, false)
148
- align_chained_calls options.fetch(:align_chained_calls, false)
149
- preserve_whitespace options.fetch(:preserve_whitespace, true)
150
- trailing_commas options.fetch(:trailing_commas, :dynamic)
151
- end
152
-
153
- # The indent size (default: 2)
147
+ indent_size options.fetch(:indent_size, 2)
148
+ spaces_inside_hash_brace options.fetch(:spaces_inside_hash_brace, :dynamic)
149
+ spaces_inside_array_bracket options.fetch(:spaces_inside_array_bracket, :dynamic)
150
+ spaces_around_equal options.fetch(:spaces_around_equal, :dynamic)
151
+ spaces_in_ternary options.fetch(:spaces_in_ternary, :dynamic)
152
+ spaces_in_suffix options.fetch(:spaces_in_suffix, :dynamic)
153
+ spaces_in_commands options.fetch(:spaces_in_commands, :dynamic)
154
+ spaces_around_block_brace options.fetch(:spaces_around_block_brace, :dynamic)
155
+ spaces_after_comma options.fetch(:spaces_after_comma, :dynamic)
156
+ spaces_around_hash_arrow options.fetch(:spaces_around_hash_arrow, :dynamic)
157
+ spaces_around_when options.fetch(:spaces_around_when, :dynamic)
158
+ spaces_around_dot options.fetch(:spaces_around_dot, :dynamic)
159
+ spaces_after_lambda_arrow options.fetch(:spaces_after_lambda_arrow, :dynamic)
160
+ spaces_around_unary options.fetch(:spaces_around_unary, :dynamic)
161
+ spaces_around_binary options.fetch(:spaces_around_binary, :dynamic)
162
+ parens_in_def options.fetch(:parens_in_def, :dynamic)
163
+ double_newline_inside_type options.fetch(:double_newline_inside_type, :dynamic)
164
+ visibility_indent options.fetch(:visibility_indent, :dynamic)
165
+ align_comments options.fetch(:align_comments, false)
166
+ align_assignments options.fetch(:align_assignments, false)
167
+ align_hash_keys options.fetch(:align_hash_keys, false)
168
+ align_case_when options.fetch(:align_case_when, false)
169
+ align_chained_calls options.fetch(:align_chained_calls, false)
170
+ trailing_commas options.fetch(:trailing_commas, :dynamic)
171
+ end
172
+
173
+ ### Settings
174
+
154
175
  def indent_size(value)
155
176
  @indent_size = value
156
177
  end
157
178
 
158
- # Whether to put a space after a hash brace. Valid values are:
159
- #
160
- # * :dynamic: if there's a space, keep it. If not, don't keep it (default)
161
- # * :always: always put a space after a hash brace
162
- # * :never: never put a space after a hash brace
163
- def space_after_hash_brace(value)
179
+ def spaces_inside_hash_brace(value)
180
+ @spaces_inside_hash_brace = dynamic_always_never("spaces_inside_hash_brace", value)
181
+ end
182
+
183
+ def spaces_inside_array_bracket(value)
184
+ @spaces_inside_array_bracket = dynamic_always_never("spaces_inside_array_bracket", value)
185
+ end
186
+
187
+ def spaces_around_equal(value)
188
+ @spaces_around_equal = one_dynamic("spaces_around_equal", value)
189
+ end
190
+
191
+ def spaces_in_ternary(value)
192
+ @spaces_in_ternary = one_dynamic("spaces_in_ternary", value)
193
+ end
194
+
195
+ def spaces_in_suffix(value)
196
+ @spaces_in_suffix = one_dynamic("spaces_in_suffix", value)
197
+ end
198
+
199
+ def spaces_in_commands(value)
200
+ @spaces_in_commands = one_dynamic("spaces_in_commands", value)
201
+ end
202
+
203
+ def spaces_around_block_brace(value)
204
+ @spaces_around_block_brace = one_dynamic("spaces_around_block_brace", value)
205
+ end
206
+
207
+ def spaces_after_comma(value)
208
+ @spaces_after_comma = one_dynamic("spaces_after_comma", value)
209
+ end
210
+
211
+ def spaces_around_hash_arrow(value)
212
+ @spaces_around_hash_arrow = one_dynamic("spaces_around_hash_arrow", value)
213
+ end
214
+
215
+ def spaces_around_when(value)
216
+ @spaces_around_when = one_dynamic("spaces_around_when", value)
217
+ end
218
+
219
+ def spaces_around_dot(value)
220
+ @spaces_around_dot = no_dynamic("spaces_around_dot", value)
221
+ end
222
+
223
+ def spaces_after_lambda_arrow(value)
224
+ @spaces_after_lambda_arrow = no_dynamic("spaces_after_lambda_arrow", value)
225
+ end
226
+
227
+ def spaces_around_unary(value)
228
+ @spaces_around_unary = no_dynamic("spaces_around_unary", value)
229
+ end
230
+
231
+ def spaces_around_binary(value)
232
+ @spaces_around_binary = one_dynamic("spaces_around_binary", value)
233
+ end
234
+
235
+ def parens_in_def(value)
236
+ @parens_in_def = yes_dynamic("parens_in_def", value)
237
+ end
238
+
239
+ def double_newline_inside_type(value)
240
+ @double_newline_inside_type = no_dynamic("double_newline_inside_type", value)
241
+ end
242
+
243
+ def visibility_indent(value)
164
244
  case value
165
- when :dynamic, :always, :never
166
- @space_after_hash_brace = value
245
+ when :indent, :align, :dynamic #, :dedent
246
+ @visibility_indent = value
167
247
  else
168
- raise ArgumentError.new("invalid value for space_after_hash_brace: #{value}. Valid values are: :dynamic, :always, :never")
248
+ raise ArgumentError.new("invalid value for visibility_indent: #{value}. Valid values are: :indent, :align, :dynamic")
169
249
  end
170
250
  end
171
251
 
172
- # Whether to put a space after an array bracket. Valid values are:
173
- #
174
- # * :dynamic: if there's a space, keep it. If not, don't keep it (default)
175
- # * :always: always put a space after an array bracket
176
- # * :never: never put a space after an array bracket
177
- def space_after_array_bracket(value)
252
+ def dynamic_always_never(name, value)
178
253
  case value
179
254
  when :dynamic, :always, :never
180
- @space_after_array_bracket = value
255
+ value
256
+ else
257
+ raise ArgumentError.new("invalid value for #{name}: #{value}. Valid values are: :dynamic, :always, :never")
258
+ end
259
+ end
260
+
261
+ def one_dynamic(name, value)
262
+ case value
263
+ when :one, :dynamic
264
+ value
181
265
  else
182
- raise ArgumentError.new("invalid value for space_after_array_bracket: #{value}. Valid values are: :dynamic, :always, :never")
266
+ raise ArgumentError.new("invalid value for #{name}: #{value}. Valid values are: :one, :dynamic")
267
+ end
268
+ end
269
+
270
+ def no_dynamic(name, value)
271
+ case value
272
+ when :no, :dynamic
273
+ value
274
+ else
275
+ raise ArgumentError.new("invalid value for #{name}: #{value}. Valid values are: :no, :dynamic")
276
+ end
277
+ end
278
+
279
+ def yes_dynamic(name, value)
280
+ case value
281
+ when :yes, :dynamic
282
+ value
283
+ else
284
+ raise ArgumentError.new("invalid value for #{name}: #{value}. Valid values are: :yes, :dynamic")
183
285
  end
184
286
  end
185
287
 
186
- # Whether to align successive comments (default: false)
187
288
  def align_comments(value)
188
289
  @align_comments = value
189
290
  end
190
291
 
191
- # Whether to align successive assignments (default: false)
192
292
  def align_assignments(value)
193
293
  @align_assignments = value
194
294
  end
195
295
 
196
- # Whether to align successive hash keys (default: false)
197
296
  def align_hash_keys(value)
198
297
  @align_hash_keys = value
199
298
  end
200
299
 
201
- # Whether to align successive case when (default: false)
202
300
  def align_case_when(value)
203
301
  @align_case_when = value
204
302
  end
205
303
 
206
- # Whether to align chained calls to the first dot in the first line (default: false)
207
304
  def align_chained_calls(value)
208
305
  @align_chained_calls = value
209
306
  end
210
307
 
211
- # Preserve whitespace after assignments target and values,
212
- # after calls that start with a space, hash arrows and commas.
213
- #
214
- # This allows for manual alignment of some code that would otherwise
215
- # be impossible to automatically format or preserve "beautiful".
216
- #
217
- # If `align_assignments` is true, this doesn't apply to assignments.
218
- # If `align_hash_keys` is true, this doesn't apply to hash keys.
219
- #
220
- # Can also be set to `:YES` to preserve whitespace in many more places,
221
- # in case there's no clear rule in your workplace/project as to when
222
- # to leave spaces or not. This includes spaces (or the absence of them)
223
- # around dots, braces, pipes and hash keys and values.
224
- def preserve_whitespace(value)
225
- @preserve_whitespace = value
226
- end
227
-
228
308
  # Whether to place commas at the end of a multi-line list
229
309
  #
230
310
  # * :dynamic: if there's a comma, keep it. If not, don't add it (default)
@@ -239,6 +319,8 @@ class Rufo::Formatter
239
319
  end
240
320
  end
241
321
 
322
+ ### Formatter implementation
323
+
242
324
  def format
243
325
  visit @sexp
244
326
  consume_end
@@ -263,7 +345,7 @@ class Rufo::Formatter
263
345
  # Topmost node
264
346
  #
265
347
  # [:program, exps]
266
- visit_exps node[1]
348
+ visit_exps node[1], with_indent: true
267
349
  when :void_stmt
268
350
  # Empty statement
269
351
  #
@@ -391,7 +473,9 @@ class Rufo::Formatter
391
473
  visit_suffix(node, "rescue")
392
474
  when :vcall
393
475
  # [:vcall, exp]
476
+ token_column = current_token_column
394
477
  visit node[1]
478
+ adjust_visibility_indent(node[1], token_column)
395
479
  when :fcall
396
480
  # [:fcall, [:@ident, "foo", [1, 0]]]
397
481
  visit node[1]
@@ -534,7 +618,7 @@ class Rufo::Formatter
534
618
  end
535
619
  end
536
620
 
537
- def visit_exps(exps, with_indent: false, with_lines: true)
621
+ def visit_exps(exps, with_indent: false, with_lines: true, want_trailing_multiline: false)
538
622
  consume_end_of_line(at_prefix: true)
539
623
 
540
624
  line_before_endline = nil
@@ -568,7 +652,7 @@ class Rufo::Formatter
568
652
  if with_lines
569
653
  exp_needs_two_lines = needs_two_lines?(exp)
570
654
 
571
- consume_end_of_line(want_semicolon: !is_last, want_multiline: !is_last, needs_two_lines_on_comment: exp_needs_two_lines)
655
+ consume_end_of_line(want_semicolon: !is_last, want_multiline: !is_last || want_trailing_multiline, needs_two_lines_on_comment: exp_needs_two_lines)
572
656
 
573
657
  # Make sure to put two lines before defs, class and others
574
658
  if !is_last && (exp_needs_two_lines || needs_two_lines?(exps[i + 1])) && @line <= line_before_endline + 1
@@ -758,7 +842,8 @@ class Rufo::Formatter
758
842
  line = @line
759
843
 
760
844
  visit target
761
- consume_space(want_preserve_whitespace: !@align_assignments)
845
+ consume_one_dynamic_space @spaces_around_equal, force_one: @align_assignments
846
+
762
847
  track_assignment
763
848
  consume_op "="
764
849
  visit_assign_value value
@@ -775,7 +860,7 @@ class Rufo::Formatter
775
860
  line = @line
776
861
 
777
862
  visit target
778
- consume_space(want_preserve_whitespace: !@align_assignments)
863
+ consume_one_dynamic_space @spaces_around_equal, force_one: @align_assignments
779
864
 
780
865
  # [:@op, "+=", [1, 2]],
781
866
  check :on_op
@@ -798,12 +883,24 @@ class Rufo::Formatter
798
883
  _, lefts, right = node
799
884
 
800
885
  visit_comma_separated_list lefts
886
+
887
+ first_space = current_token if space?
801
888
  skip_space
802
889
 
803
890
  # A trailing comma can come after the left hand side
804
- consume_token :on_comma if comma?
891
+ if comma?
892
+ consume_token :on_comma
893
+ first_space = current_token if space?
894
+ skip_space
895
+ end
896
+
897
+ if @spaces_around_equal == :one || @align_assignments
898
+ write_space
899
+ elsif first_space
900
+ write_space first_space[2]
901
+ end
805
902
 
806
- consume_space(want_preserve_whitespace: !@align_assignments)
903
+ # consume_space(want_preserve_whitespace: !@align_assignments)
807
904
  track_assignment
808
905
  consume_op "="
809
906
  visit_assign_value right
@@ -813,7 +910,11 @@ class Rufo::Formatter
813
910
  first_space = current_token if space?
814
911
  skip_space
815
912
 
816
- indent_after_space value, sticky: indentable_value?(value), want_space: true, first_space: first_space
913
+ want_space = first_space || @spaces_around_equal == :one
914
+ indent_after_space value, sticky: indentable_value?(value),
915
+ want_space: want_space,
916
+ first_space: first_space,
917
+ preserve_whitespace: @spaces_around_equal == :dynamic && !@align_assignments
817
918
  end
818
919
 
819
920
  def indentable_value?(value)
@@ -883,37 +984,13 @@ class Rufo::Formatter
883
984
  _, cond, then_body, else_body = node
884
985
 
885
986
  visit cond
886
- consume_space(want_preserve_whitespace: true)
987
+ consume_one_dynamic_space @spaces_in_ternary
887
988
  consume_op "?"
888
-
889
- first_space = current_token if space?
890
-
891
- skip_space
892
- if newline? || comment?
893
- consume_end_of_line
894
- write_indent(next_indent)
895
- elsif first_space && @preserve_whitespace
896
- write_space first_space[2]
897
- else
898
- consume_space
899
- end
900
-
989
+ consume_one_dynamic_space_or_newline @spaces_in_ternary
901
990
  visit then_body
902
- consume_space(want_preserve_whitespace: true)
991
+ consume_one_dynamic_space @spaces_in_ternary
903
992
  consume_op ":"
904
-
905
- first_space = current_token if space?
906
- skip_space
907
-
908
- if newline? || comment?
909
- consume_end_of_line
910
- write_indent(next_indent)
911
- elsif first_space && @preserve_whitespace
912
- write_space first_space[2]
913
- else
914
- consume_space
915
- end
916
-
993
+ consume_one_dynamic_space_or_newline @spaces_in_ternary
917
994
  visit else_body
918
995
  end
919
996
 
@@ -930,10 +1007,9 @@ class Rufo::Formatter
930
1007
  end
931
1008
 
932
1009
  visit body
933
-
934
- consume_space(want_preserve_whitespace: true)
1010
+ consume_one_dynamic_space @spaces_in_suffix
935
1011
  consume_keyword(suffix)
936
- consume_space(want_preserve_whitespace: true)
1012
+ consume_one_dynamic_space_or_newline @spaces_in_suffix
937
1013
  visit cond
938
1014
  end
939
1015
 
@@ -944,7 +1020,7 @@ class Rufo::Formatter
944
1020
  @dot_column = nil
945
1021
  visit obj
946
1022
 
947
- first_space = current_token if space? && @preserve_whitespace == :YES
1023
+ first_space = current_token if space? && @spaces_around_dot == :dynamic
948
1024
  skip_space
949
1025
 
950
1026
  if newline? || comment?
@@ -973,7 +1049,7 @@ class Rufo::Formatter
973
1049
  consume_call_dot
974
1050
 
975
1051
  first_space = nil
976
- first_space = current_token if space? && @preserve_whitespace == :YES
1052
+ first_space = current_token if space? && @spaces_around_dot == :dynamic
977
1053
  skip_space
978
1054
 
979
1055
  if newline? || comment?
@@ -1177,7 +1253,7 @@ class Rufo::Formatter
1177
1253
 
1178
1254
  visit receiver
1179
1255
 
1180
- first_space = current_token if space? && @preserve_whitespace == :YES
1256
+ first_space = current_token if space? && @spaces_around_dot == :dynamic
1181
1257
 
1182
1258
  line = @line
1183
1259
  skip_space_or_newline
@@ -1213,11 +1289,11 @@ class Rufo::Formatter
1213
1289
  write " \\"
1214
1290
  write_line
1215
1291
  write_indent(next_indent)
1216
- elsif first_space && @preserve_whitespace
1292
+ elsif first_space && @spaces_in_commands == :dynamic
1217
1293
  write_space first_space[2]
1218
1294
  skip_space_or_newline
1219
1295
  else
1220
- consume_space
1296
+ consume_space if @spaces_in_commands == :one
1221
1297
  end
1222
1298
  end
1223
1299
 
@@ -1304,11 +1380,7 @@ class Rufo::Formatter
1304
1380
  visit call
1305
1381
 
1306
1382
  if block[0] == :brace_block
1307
- if space? || !@preserve_whitespace
1308
- consume_space
1309
- else
1310
- consume_space unless @preserve_whitespace == :YES
1311
- end
1383
+ consume_one_dynamic_space @spaces_around_block_brace
1312
1384
  else
1313
1385
  consume_space
1314
1386
  end
@@ -1324,11 +1396,7 @@ class Rufo::Formatter
1324
1396
  if void_exps?(body)
1325
1397
  consume_token :on_lbrace
1326
1398
  consume_block_args args
1327
- if space?
1328
- consume_space
1329
- else
1330
- skip_space_or_newline
1331
- end
1399
+ consume_one_dynamic_space_no_more_than_one @spaces_around_block_brace
1332
1400
  consume_token :on_rbrace
1333
1401
  return
1334
1402
  end
@@ -1338,12 +1406,15 @@ class Rufo::Formatter
1338
1406
  # If the whole block fits into a single line, use braces
1339
1407
  if current_token_line == closing_brace_token[0][0]
1340
1408
  consume_token :on_lbrace
1341
-
1342
1409
  consume_block_args args
1343
-
1344
- consume_space unless !space? && @preserve_whitespace == :YES
1410
+ consume_one_dynamic_space_no_more_than_one @spaces_around_block_brace
1345
1411
  visit_exps body, with_lines: false
1346
- consume_space unless !space? && @preserve_whitespace == :YES
1412
+
1413
+ while semicolon?
1414
+ next_token
1415
+ end
1416
+
1417
+ consume_one_dynamic_space_no_more_than_one @spaces_around_block_brace
1347
1418
 
1348
1419
  consume_token :on_rbrace
1349
1420
  return
@@ -1387,15 +1458,7 @@ class Rufo::Formatter
1387
1458
 
1388
1459
  def consume_block_args(args)
1389
1460
  if args
1390
- if space?
1391
- consume_space(want_preserve_whitespace: @preserve_whitespace == :YES)
1392
- else
1393
- if @preserve_whitespace == :YES
1394
- skip_space
1395
- else
1396
- consume_space
1397
- end
1398
- end
1461
+ consume_one_dynamic_space_no_more_than_one @spaces_around_block_brace
1399
1462
  # + 1 because of |...|
1400
1463
  # ^
1401
1464
  indent(@column + 1) do
@@ -1522,9 +1585,13 @@ class Rufo::Formatter
1522
1585
  # [:bodystmt, body, rescue_body, else_body, ensure_body]
1523
1586
  _, body, rescue_body, else_body, ensure_body = node
1524
1587
 
1588
+ inside_type_body = @inside_type_body
1589
+ current_type = @current_type
1590
+ @inside_type_body = false
1591
+
1525
1592
  line = @line
1526
1593
 
1527
- indent_body body
1594
+ indent_body body, want_multiline: inside_type_body && @double_newline_inside_type == :dynamic
1528
1595
 
1529
1596
  while rescue_body
1530
1597
  # [:rescue, type, name, body, more_rescue]
@@ -1566,6 +1633,11 @@ class Rufo::Formatter
1566
1633
  indent_body ensure_body[1]
1567
1634
  end
1568
1635
 
1636
+ if inside_type_body && current_type && @visibility_indent_in_action[current_type]
1637
+ @indent -= @indent_size
1638
+ @visibility_indent_in_action.delete current_type
1639
+ end
1640
+
1569
1641
  write_indent if @line != line
1570
1642
  consume_keyword "end"
1571
1643
  end
@@ -1752,7 +1824,7 @@ class Rufo::Formatter
1752
1824
  consume_end_of_line(want_multiline: false)
1753
1825
  write_indent
1754
1826
  end
1755
- elsif first_space && @preserve_whitespace
1827
+ elsif first_space && @spaces_after_comma == :dynamic
1756
1828
  write_space first_space[2]
1757
1829
  skip_space_or_newline
1758
1830
  else
@@ -1799,10 +1871,12 @@ class Rufo::Formatter
1799
1871
 
1800
1872
  consume_op_or_keyword op
1801
1873
 
1802
- has_space = space?
1874
+ setting = op == :not ? @spaces_in_commands : @spaces_around_unary
1803
1875
 
1804
- if has_space
1805
- consume_space(want_preserve_whitespace: @preserve_whitespace)
1876
+ first_space = space?
1877
+ consume_space = first_space && setting == :dynamic
1878
+ if consume_space
1879
+ consume_space(want_preserve_whitespace: true)
1806
1880
  else
1807
1881
  skip_space_or_newline
1808
1882
  end
@@ -1810,15 +1884,17 @@ class Rufo::Formatter
1810
1884
  if op == :not
1811
1885
  has_paren = current_token_kind == :on_lparen
1812
1886
 
1813
- if has_paren && !has_space
1887
+ if has_paren && !first_space
1814
1888
  write "("
1815
1889
  next_token
1816
1890
  skip_space_or_newline
1891
+ elsif !has_paren && !consume_space
1892
+ write_space
1817
1893
  end
1818
1894
 
1819
1895
  visit exp
1820
1896
 
1821
- if has_paren && !has_space
1897
+ if has_paren && !first_space
1822
1898
  skip_space_or_newline
1823
1899
  check :on_rparen
1824
1900
  write ")"
@@ -1851,11 +1927,7 @@ class Rufo::Formatter
1851
1927
  token_column = current_token_column
1852
1928
 
1853
1929
  visit left
1854
- if space?
1855
- needs_space = true
1856
- else
1857
- needs_space = op != :* && op != :/ && op != :**
1858
- end
1930
+ needs_space = space?
1859
1931
 
1860
1932
  has_backslash, first_space = skip_space_backslash
1861
1933
  if has_backslash
@@ -1863,14 +1935,33 @@ class Rufo::Formatter
1863
1935
  write " \\"
1864
1936
  write_line
1865
1937
  write_indent(next_indent)
1866
- elsif @preserve_whitespace && first_space
1938
+ elsif first_space && @spaces_around_binary == :dynamic
1867
1939
  write_space first_space[2]
1868
1940
  else
1869
1941
  write_space if needs_space
1870
1942
  end
1871
1943
 
1872
1944
  consume_op_or_keyword op
1873
- indent_after_space right, want_space: needs_space, needed_indent: needed_indent, token_column: token_column, base_column: base_column
1945
+
1946
+ first_space = nil
1947
+ first_space = current_token if space?
1948
+ skip_space
1949
+
1950
+ if newline? || comment?
1951
+ indent_after_space right,
1952
+ want_space: needs_space,
1953
+ needed_indent: needed_indent,
1954
+ token_column: token_column,
1955
+ base_column: base_column,
1956
+ preserve_whitespace: @spaces_around_binary == :dynamic
1957
+ else
1958
+ if @spaces_around_binary == :one
1959
+ write " " if needs_space
1960
+ elsif first_space
1961
+ write_space first_space[2]
1962
+ end
1963
+ visit right
1964
+ end
1874
1965
  end
1875
1966
 
1876
1967
  def consume_op_or_keyword(op)
@@ -1890,21 +1981,24 @@ class Rufo::Formatter
1890
1981
  # [:bodystmt, body, nil, nil, nil]]
1891
1982
  _, name, superclass, body = node
1892
1983
 
1893
- consume_keyword "class"
1894
- skip_space_or_newline
1895
- write_space
1896
- visit name
1897
-
1898
- if superclass
1984
+ push_type(node) do
1985
+ consume_keyword "class"
1899
1986
  skip_space_or_newline
1900
1987
  write_space
1901
- consume_op "<"
1902
- skip_space_or_newline
1903
- write_space
1904
- visit superclass
1905
- end
1988
+ visit name
1906
1989
 
1907
- visit body
1990
+ if superclass
1991
+ skip_space_or_newline
1992
+ write_space
1993
+ consume_op "<"
1994
+ skip_space_or_newline
1995
+ write_space
1996
+ visit superclass
1997
+ end
1998
+
1999
+ @inside_type_body = true
2000
+ visit body
2001
+ end
1908
2002
  end
1909
2003
 
1910
2004
  def visit_module(node)
@@ -1913,11 +2007,15 @@ class Rufo::Formatter
1913
2007
  # [:bodystmt, body, nil, nil, nil]]
1914
2008
  _, name, body = node
1915
2009
 
1916
- consume_keyword "module"
1917
- skip_space_or_newline
1918
- write_space
1919
- visit name
1920
- visit body
2010
+ push_type(node) do
2011
+ consume_keyword "module"
2012
+ skip_space_or_newline
2013
+ write_space
2014
+ visit name
2015
+
2016
+ @inside_type_body = true
2017
+ visit body
2018
+ end
1921
2019
  end
1922
2020
 
1923
2021
  def visit_def(node)
@@ -2004,9 +2102,14 @@ class Rufo::Formatter
2004
2102
  next_token
2005
2103
  end
2006
2104
  elsif !empty_params?(params)
2007
- write "("
2105
+ if @parens_in_def == :yes
2106
+ write "("
2107
+ else
2108
+ write_space
2109
+ end
2110
+
2008
2111
  visit params
2009
- write ")"
2112
+ write ")" if @parens_in_def == :yes
2010
2113
  skip_space
2011
2114
  end
2012
2115
 
@@ -2137,7 +2240,7 @@ class Rufo::Formatter
2137
2240
  if newline? || comment?
2138
2241
  consume_end_of_line
2139
2242
  write_indent
2140
- elsif first_space && @preserve_whitespace
2243
+ elsif first_space && @spaces_after_comma == :dynamic
2141
2244
  write_space first_space[2]
2142
2245
  skip_space_or_newline
2143
2246
  else
@@ -2293,10 +2396,7 @@ class Rufo::Formatter
2293
2396
  arrow = symbol || !(key[0] == :@label || key[0] == :dyna_symbol)
2294
2397
 
2295
2398
  visit key
2296
-
2297
- unless @preserve_whitespace == :YES && !space? && !newline? && !comment?
2298
- consume_space(want_preserve_whitespace: @preserve_whitespace)
2299
- end
2399
+ consume_one_dynamic_space @spaces_around_hash_arrow
2300
2400
 
2301
2401
  track_hash_key
2302
2402
 
@@ -2304,10 +2404,7 @@ class Rufo::Formatter
2304
2404
  # or `"label": value`
2305
2405
  if arrow
2306
2406
  consume_op "=>"
2307
-
2308
- unless @preserve_whitespace == :YES && !space? && !newline? && !comment?
2309
- consume_space(want_preserve_whitespace: !@align_hash_keys)
2310
- end
2407
+ consume_one_dynamic_space @spaces_around_hash_arrow, force_one: @align_hash_keys
2311
2408
  end
2312
2409
 
2313
2410
  visit value
@@ -2416,12 +2513,16 @@ class Rufo::Formatter
2416
2513
  # [:sclass, target, body]
2417
2514
  _, target, body = node
2418
2515
 
2419
- consume_keyword "class"
2420
- consume_space
2421
- consume_op "<<"
2422
- consume_space
2423
- visit target
2424
- visit body
2516
+ push_type(node) do
2517
+ consume_keyword "class"
2518
+ consume_space
2519
+ consume_op "<<"
2520
+ consume_space
2521
+ visit target
2522
+
2523
+ @inside_type_body = true
2524
+ visit body
2525
+ end
2425
2526
  end
2426
2527
 
2427
2528
  def visit_setter(node)
@@ -2436,7 +2537,7 @@ class Rufo::Formatter
2436
2537
 
2437
2538
  visit receiver
2438
2539
 
2439
- first_space = current_token if space? && @preserve_whitespace == :YES
2540
+ first_space = current_token if space? && @spaces_around_dot == :dynamic
2440
2541
 
2441
2542
  skip_space
2442
2543
 
@@ -2455,7 +2556,7 @@ class Rufo::Formatter
2455
2556
  consume_call_dot
2456
2557
 
2457
2558
  first_space = nil
2458
- first_space = current_token if space? && @preserve_whitespace == :YES
2559
+ first_space = current_token if space? && @spaces_around_dot == :dynamic
2459
2560
  skip_space
2460
2561
 
2461
2562
  if newline? || comment?
@@ -2523,7 +2624,7 @@ class Rufo::Formatter
2523
2624
  write "->"
2524
2625
  next_token
2525
2626
 
2526
- if space? && @preserve_whitespace
2627
+ if space? && @spaces_after_lambda_arrow == :dynamic
2527
2628
  consume_space(want_preserve_whitespace: true)
2528
2629
  else
2529
2630
  skip_space_or_newline
@@ -2558,9 +2659,9 @@ class Rufo::Formatter
2558
2659
  if current_token_line == closing_brace_token[0][0]
2559
2660
  consume_token :on_tlambeg
2560
2661
 
2561
- consume_space unless !space? && @preserve_whitespace == :YES
2662
+ consume_space unless !space? && @spaces_around_block_brace == :dynamic
2562
2663
  visit_exps body, with_lines: false
2563
- consume_space unless !space? && @preserve_whitespace == :YES
2664
+ consume_space unless !space? && @spaces_around_block_brace == :dynamic
2564
2665
 
2565
2666
  consume_token :on_rbrace
2566
2667
  return
@@ -2606,7 +2707,7 @@ class Rufo::Formatter
2606
2707
  has_space = space?
2607
2708
 
2608
2709
  if has_space
2609
- consume_space(want_preserve_whitespace: @preserve_whitespace)
2710
+ consume_space(want_preserve_whitespace: @spaces_in_commands == :dynamic)
2610
2711
  else
2611
2712
  skip_space_or_newline
2612
2713
  end
@@ -2656,7 +2757,7 @@ class Rufo::Formatter
2656
2757
  skip_space
2657
2758
 
2658
2759
  if inside_hash
2659
- case @space_after_hash_brace
2760
+ case @spaces_inside_hash_brace
2660
2761
  when :never
2661
2762
  needs_final_space = false
2662
2763
  when :always
@@ -2665,7 +2766,7 @@ class Rufo::Formatter
2665
2766
  end
2666
2767
 
2667
2768
  if inside_array
2668
- case @space_after_array_bracket
2769
+ case @spaces_inside_array_bracket
2669
2770
  when :never
2670
2771
  needs_final_space = false
2671
2772
  when :always
@@ -2740,7 +2841,7 @@ class Rufo::Formatter
2740
2841
  consume_end_of_line
2741
2842
  write_indent(needed_indent)
2742
2843
  end
2743
- elsif !is_last && first_space && @preserve_whitespace
2844
+ elsif !is_last && first_space && @spaces_after_comma == :dynamic
2744
2845
  write_space first_space[2]
2745
2846
  else
2746
2847
  write_space unless is_last
@@ -2895,7 +2996,7 @@ class Rufo::Formatter
2895
2996
  # [:when, conds, body, next_exp]
2896
2997
  _, conds, body, next_exp = node
2897
2998
 
2898
- preserve_whitespace = @preserve_whitespace && !@align_case_when
2999
+ preserve_whitespace = @spaces_around_when == :dynamic && !@align_case_when
2899
3000
 
2900
3001
  consume_keyword "when"
2901
3002
  consume_space(want_preserve_whitespace: preserve_whitespace)
@@ -2982,7 +3083,7 @@ class Rufo::Formatter
2982
3083
  def consume_space(want_preserve_whitespace: false)
2983
3084
  first_space = current_token if space?
2984
3085
  skip_space
2985
- if want_preserve_whitespace && @preserve_whitespace && !newline? && !comment? && first_space
3086
+ if want_preserve_whitespace && !newline? && !comment? && first_space
2986
3087
  write_space first_space[2] unless @output[-1] == " "
2987
3088
  skip_space_or_newline
2988
3089
  else
@@ -2991,6 +3092,45 @@ class Rufo::Formatter
2991
3092
  end
2992
3093
  end
2993
3094
 
3095
+ def consume_one_dynamic_space(setting, force_one: false)
3096
+ if setting == :one || force_one
3097
+ consume_space
3098
+ else
3099
+ if space?
3100
+ consume_space(want_preserve_whitespace: true)
3101
+ elsif newline?
3102
+ next_token
3103
+ if space?
3104
+ write_space current_token[2]
3105
+ end
3106
+ skip_space_or_newline
3107
+ else
3108
+ skip_space_or_newline
3109
+ end
3110
+ end
3111
+ end
3112
+
3113
+ def consume_one_dynamic_space_no_more_than_one(setting)
3114
+ if setting == :one
3115
+ consume_space
3116
+ else
3117
+ consume_space if space?
3118
+ end
3119
+ end
3120
+
3121
+ def consume_one_dynamic_space_or_newline(setting)
3122
+ first_space = current_token if space?
3123
+ skip_space
3124
+ if newline? || comment?
3125
+ consume_end_of_line
3126
+ write_indent(next_indent)
3127
+ elsif first_space && setting == :dynamic
3128
+ write_space first_space[2]
3129
+ else
3130
+ consume_space if setting == :one
3131
+ end
3132
+ end
3133
+
2994
3134
  def skip_space
2995
3135
  next_token while space?
2996
3136
  end
@@ -3318,7 +3458,7 @@ class Rufo::Formatter
3318
3458
  end
3319
3459
  end
3320
3460
 
3321
- def indent_body(exps, force_multiline: false)
3461
+ def indent_body(exps, force_multiline: false, want_multiline: false)
3322
3462
  skip_space
3323
3463
 
3324
3464
  has_semicolon = semicolon?
@@ -3370,7 +3510,7 @@ class Rufo::Formatter
3370
3510
  end
3371
3511
 
3372
3512
  indent do
3373
- consume_end_of_line(want_multiline: false)
3513
+ consume_end_of_line(want_multiline: want_multiline)
3374
3514
  end
3375
3515
 
3376
3516
  if keyword?("then")
@@ -3384,7 +3524,7 @@ class Rufo::Formatter
3384
3524
  skip_space_or_newline
3385
3525
  else
3386
3526
  indent do
3387
- visit_exps exps, with_indent: true
3527
+ visit_exps exps, with_indent: true, want_trailing_multiline: want_multiline
3388
3528
  end
3389
3529
  write_line unless @last_was_newline
3390
3530
  end
@@ -3423,7 +3563,7 @@ class Rufo::Formatter
3423
3563
  @column += indent
3424
3564
  end
3425
3565
 
3426
- def indent_after_space(node, sticky: false, want_space: true, first_space: nil, needed_indent: next_indent, token_column: nil, base_column: nil)
3566
+ def indent_after_space(node, sticky: false, want_space: true, first_space: nil, needed_indent: next_indent, token_column: nil, base_column: nil, preserve_whitespace:)
3427
3567
  first_space = current_token if space?
3428
3568
 
3429
3569
  skip_space
@@ -3447,7 +3587,7 @@ class Rufo::Formatter
3447
3587
  end
3448
3588
  else
3449
3589
  if want_space
3450
- if first_space && @preserve_whitespace
3590
+ if first_space && preserve_whitespace
3451
3591
  write_space first_space[2]
3452
3592
  else
3453
3593
  write_space
@@ -3528,6 +3668,64 @@ class Rufo::Formatter
3528
3668
  node.size == 1 && node[0].size == 1 && node[0][0] == :void_stmt
3529
3669
  end
3530
3670
 
3671
+ def adjust_visibility_indent(node, base_column)
3672
+ return if @visibility_indent == :align
3673
+
3674
+ case node[1]
3675
+ when "private", "protected", "public"
3676
+ # OK
3677
+ else
3678
+ return
3679
+ end
3680
+
3681
+ i = @tokens.size - 1
3682
+
3683
+ # First, skip spaces until a newline or comment
3684
+ while i >= 0
3685
+ token = @tokens[i]
3686
+ case token[1]
3687
+ when :on_sp
3688
+ i -= 1
3689
+ next
3690
+ when :on_nl, :on_ignored_nl, :on_comment
3691
+ i -= 1
3692
+ break
3693
+ else
3694
+ return
3695
+ end
3696
+ end
3697
+
3698
+ if @visibility_indent_in_action[@current_type]
3699
+ last_newline_index = @output.rindex("\n")
3700
+ if last_newline_index
3701
+ # Remove extra indent if we are indenting inside private/protected/public
3702
+ # and we just found another one.
3703
+ @output = "#{@output[0..last_newline_index]}#{@output[last_newline_index + 1 + @indent_size..-1]}"
3704
+ @indent -= @indent_size
3705
+ @visibility_indent_in_action.delete @current_type
3706
+ end
3707
+ end
3708
+
3709
+ # Now we skip all spaces and newlines
3710
+ while i >= 0
3711
+ token = @tokens[i]
3712
+ case token[1]
3713
+ when :on_sp, :on_nl, :on_ignored_nl
3714
+ i -= 1
3715
+ next
3716
+ else
3717
+ break
3718
+ end
3719
+ end
3720
+
3721
+ return if i < 0
3722
+
3723
+ if @visibility_indent == :indent || base_column + @indent_size == @tokens[i][0][1]
3724
+ @indent += @indent_size
3725
+ @visibility_indent_in_action[@current_type] = true
3726
+ end
3727
+ end
3728
+
3531
3729
  def find_closing_brace_token
3532
3730
  count = 0
3533
3731
  i = @tokens.size - 1
@@ -3620,6 +3818,13 @@ class Rufo::Formatter
3620
3818
  @current_hash = old_hash
3621
3819
  end
3622
3820
 
3821
+ def push_type(node)
3822
+ old_type = @current_type
3823
+ @current_type = node
3824
+ yield
3825
+ @current_type = old_type
3826
+ end
3827
+
3623
3828
  def dedent_calls
3624
3829
  return if @line_to_call_info.empty?
3625
3830