rufo 0.0.32 → 0.0.33

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.
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