syntax_tree 3.6.3 → 4.0.1

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.
@@ -177,10 +177,10 @@ module SyntaxTree
177
177
  q.text("BEGIN ")
178
178
  q.format(lbrace)
179
179
  q.indent do
180
- q.breakable
180
+ q.breakable_space
181
181
  q.format(statements)
182
182
  end
183
- q.breakable
183
+ q.breakable_space
184
184
  q.text("}")
185
185
  end
186
186
  end
@@ -280,10 +280,10 @@ module SyntaxTree
280
280
  q.text("END ")
281
281
  q.format(lbrace)
282
282
  q.indent do
283
- q.breakable
283
+ q.breakable_space
284
284
  q.format(statements)
285
285
  end
286
- q.breakable
286
+ q.breakable_space
287
287
  q.text("}")
288
288
  end
289
289
  end
@@ -327,10 +327,20 @@ module SyntaxTree
327
327
 
328
328
  def format(q)
329
329
  q.text("__END__")
330
- q.breakable(force: true)
330
+ q.breakable_force
331
331
 
332
- separator = -> { q.breakable(indent: false, force: true) }
333
- q.seplist(value.split(/\r?\n/, -1), separator) { |line| q.text(line) }
332
+ first = true
333
+ value.each_line(chomp: true) do |line|
334
+ if first
335
+ first = false
336
+ else
337
+ q.breakable_return
338
+ end
339
+
340
+ q.text(line)
341
+ end
342
+
343
+ q.breakable_return if value.end_with?("\n")
334
344
  end
335
345
  end
336
346
 
@@ -412,7 +422,7 @@ module SyntaxTree
412
422
  q.format(left_argument, stackable: false)
413
423
  q.group do
414
424
  q.nest(keyword.length) do
415
- q.breakable(force: left_argument.comments.any?)
425
+ left_argument.comments.any? ? q.breakable_force : q.breakable_space
416
426
  q.format(AliasArgumentFormatter.new(right), stackable: false)
417
427
  end
418
428
  end
@@ -476,10 +486,10 @@ module SyntaxTree
476
486
 
477
487
  if index
478
488
  q.indent do
479
- q.breakable("")
489
+ q.breakable_empty
480
490
  q.format(index)
481
491
  end
482
- q.breakable("")
492
+ q.breakable_empty
483
493
  end
484
494
 
485
495
  q.text("]")
@@ -537,10 +547,10 @@ module SyntaxTree
537
547
 
538
548
  if index
539
549
  q.indent do
540
- q.breakable("")
550
+ q.breakable_empty
541
551
  q.format(index)
542
552
  end
543
- q.breakable("")
553
+ q.breakable_empty
544
554
  end
545
555
 
546
556
  q.text("]")
@@ -593,25 +603,30 @@ module SyntaxTree
593
603
  return
594
604
  end
595
605
 
596
- q.group(0, "(", ")") do
606
+ q.text("(")
607
+ q.group do
597
608
  q.indent do
598
- q.breakable("")
609
+ q.breakable_empty
599
610
  q.format(arguments)
600
611
  q.if_break { q.text(",") } if q.trailing_comma? && trailing_comma?
601
612
  end
602
- q.breakable("")
613
+ q.breakable_empty
603
614
  end
615
+ q.text(")")
604
616
  end
605
617
 
606
618
  private
607
619
 
608
620
  def trailing_comma?
609
- case arguments
610
- in Args[parts: [*, ArgBlock]]
621
+ return false unless arguments.is_a?(Args)
622
+ parts = arguments.parts
623
+
624
+ if parts.last.is_a?(ArgBlock)
611
625
  # If the last argument is a block, then we can't put a trailing comma
612
626
  # after it without resulting in a syntax error.
613
627
  false
614
- in Args[parts: [Command | CommandCall]]
628
+ elsif (parts.length == 1) && (part = parts.first) &&
629
+ (part.is_a?(Command) || part.is_a?(CommandCall))
615
630
  # If the only argument is a command or command call, then a trailing
616
631
  # comma would be parsed as part of that expression instead of on this
617
632
  # one, so we don't want to add a trailing comma.
@@ -790,6 +805,17 @@ module SyntaxTree
790
805
  # [one, two, three]
791
806
  #
792
807
  class ArrayLiteral < Node
808
+ # It's very common to use seplist with ->(q) { q.breakable_space }. We wrap
809
+ # that pattern into an object to cut down on having to create a bunch of
810
+ # lambdas all over the place.
811
+ class BreakableSpaceSeparator
812
+ def call(q)
813
+ q.breakable_space
814
+ end
815
+ end
816
+
817
+ BREAKABLE_SPACE_SEPARATOR = BreakableSpaceSeparator.new
818
+
793
819
  # Formats an array of multiple simple string literals into the %w syntax.
794
820
  class QWordsFormatter
795
821
  # [Args] the contents of the array
@@ -800,10 +826,11 @@ module SyntaxTree
800
826
  end
801
827
 
802
828
  def format(q)
803
- q.group(0, "%w[", "]") do
829
+ q.text("%w[")
830
+ q.group do
804
831
  q.indent do
805
- q.breakable("")
806
- q.seplist(contents.parts, -> { q.breakable }) do |part|
832
+ q.breakable_empty
833
+ q.seplist(contents.parts, BREAKABLE_SPACE_SEPARATOR) do |part|
807
834
  if part.is_a?(StringLiteral)
808
835
  q.format(part.parts.first)
809
836
  else
@@ -811,8 +838,9 @@ module SyntaxTree
811
838
  end
812
839
  end
813
840
  end
814
- q.breakable("")
841
+ q.breakable_empty
815
842
  end
843
+ q.text("]")
816
844
  end
817
845
  end
818
846
 
@@ -826,15 +854,17 @@ module SyntaxTree
826
854
  end
827
855
 
828
856
  def format(q)
829
- q.group(0, "%i[", "]") do
857
+ q.text("%i[")
858
+ q.group do
830
859
  q.indent do
831
- q.breakable("")
832
- q.seplist(contents.parts, -> { q.breakable }) do |part|
860
+ q.breakable_empty
861
+ q.seplist(contents.parts, BREAKABLE_SPACE_SEPARATOR) do |part|
833
862
  q.format(part.value)
834
863
  end
835
864
  end
836
- q.breakable("")
865
+ q.breakable_empty
837
866
  end
867
+ q.text("]")
838
868
  end
839
869
  end
840
870
 
@@ -861,6 +891,14 @@ module SyntaxTree
861
891
  #
862
892
  # provided the line length was hit between `bar` and `baz`.
863
893
  class VarRefsFormatter
894
+ # The separator for the fill algorithm.
895
+ class Separator
896
+ def call(q)
897
+ q.text(",")
898
+ q.fill_breakable
899
+ end
900
+ end
901
+
864
902
  # [Args] the contents of the array
865
903
  attr_reader :contents
866
904
 
@@ -869,20 +907,16 @@ module SyntaxTree
869
907
  end
870
908
 
871
909
  def format(q)
872
- q.group(0, "[", "]") do
910
+ q.text("[")
911
+ q.group do
873
912
  q.indent do
874
- q.breakable("")
875
-
876
- separator = -> do
877
- q.text(",")
878
- q.fill_breakable
879
- end
880
-
881
- q.seplist(contents.parts, separator) { |part| q.format(part) }
913
+ q.breakable_empty
914
+ q.seplist(contents.parts, Separator.new) { |part| q.format(part) }
882
915
  q.if_break { q.text(",") } if q.trailing_comma?
883
916
  end
884
- q.breakable("")
917
+ q.breakable_empty
885
918
  end
919
+ q.text("]")
886
920
  end
887
921
  end
888
922
 
@@ -902,11 +936,11 @@ module SyntaxTree
902
936
  q.text("[")
903
937
  q.indent do
904
938
  lbracket.comments.each do |comment|
905
- q.breakable(force: true)
939
+ q.breakable_force
906
940
  comment.format(q)
907
941
  end
908
942
  end
909
- q.breakable(force: true)
943
+ q.breakable_force
910
944
  q.text("]")
911
945
  end
912
946
  end
@@ -973,13 +1007,13 @@ module SyntaxTree
973
1007
 
974
1008
  if contents
975
1009
  q.indent do
976
- q.breakable("")
1010
+ q.breakable_empty
977
1011
  q.format(contents)
978
1012
  q.if_break { q.text(",") } if q.trailing_comma?
979
1013
  end
980
1014
  end
981
1015
 
982
- q.breakable("")
1016
+ q.breakable_empty
983
1017
  q.text("]")
984
1018
  end
985
1019
  end
@@ -1127,7 +1161,7 @@ module SyntaxTree
1127
1161
  q.format(constant) if constant
1128
1162
  q.text("[")
1129
1163
  q.indent do
1130
- q.breakable("")
1164
+ q.breakable_empty
1131
1165
 
1132
1166
  parts = [*requireds]
1133
1167
  parts << RestFormatter.new(rest) if rest
@@ -1135,7 +1169,7 @@ module SyntaxTree
1135
1169
 
1136
1170
  q.seplist(parts) { |part| q.format(part) }
1137
1171
  end
1138
- q.breakable("")
1172
+ q.breakable_empty
1139
1173
  q.text("]")
1140
1174
  end
1141
1175
  end
@@ -1145,13 +1179,13 @@ module SyntaxTree
1145
1179
  module AssignFormatting
1146
1180
  def self.skip_indent?(value)
1147
1181
  case value
1148
- in ArrayLiteral | HashLiteral | Heredoc | Lambda | QSymbols | QWords |
1149
- Symbols | Words
1182
+ when ArrayLiteral, HashLiteral, Heredoc, Lambda, QSymbols, QWords,
1183
+ Symbols, Words
1150
1184
  true
1151
- in Call[receiver:]
1152
- skip_indent?(receiver)
1153
- in DynaSymbol[quote:]
1154
- quote.start_with?("%s")
1185
+ when Call
1186
+ skip_indent?(value.receiver)
1187
+ when DynaSymbol
1188
+ value.quote.start_with?("%s")
1155
1189
  else
1156
1190
  false
1157
1191
  end
@@ -1206,7 +1240,7 @@ module SyntaxTree
1206
1240
  q.format(value)
1207
1241
  else
1208
1242
  q.indent do
1209
- q.breakable
1243
+ q.breakable_space
1210
1244
  q.format(value)
1211
1245
  end
1212
1246
  end
@@ -1277,7 +1311,7 @@ module SyntaxTree
1277
1311
  q.format(value)
1278
1312
  else
1279
1313
  q.indent do
1280
- q.breakable
1314
+ q.breakable_space
1281
1315
  q.format(value)
1282
1316
  end
1283
1317
  end
@@ -1404,17 +1438,22 @@ module SyntaxTree
1404
1438
 
1405
1439
  def format_key(q, key)
1406
1440
  case key
1407
- in Label
1441
+ when Label
1408
1442
  q.format(key)
1409
- in SymbolLiteral
1443
+ when SymbolLiteral
1410
1444
  q.format(key.value)
1411
1445
  q.text(":")
1412
- in DynaSymbol[parts: [TStringContent[value: LABEL] => part]]
1413
- q.format(part)
1414
- q.text(":")
1415
- in DynaSymbol
1416
- q.format(key)
1417
- q.text(":")
1446
+ when DynaSymbol
1447
+ parts = key.parts
1448
+
1449
+ if parts.length == 1 && (part = parts.first) &&
1450
+ part.is_a?(TStringContent) && part.value.match?(LABEL)
1451
+ q.format(part)
1452
+ q.text(":")
1453
+ else
1454
+ q.format(key)
1455
+ q.text(":")
1456
+ end
1418
1457
  end
1419
1458
  end
1420
1459
  end
@@ -1424,8 +1463,7 @@ module SyntaxTree
1424
1463
  def format_key(q, key)
1425
1464
  case key
1426
1465
  when Label
1427
- q.text(":")
1428
- q.text(key.value.chomp(":"))
1466
+ q.text(":#{key.value.chomp(":")}")
1429
1467
  when DynaSymbol
1430
1468
  q.text(":")
1431
1469
  q.format(key)
@@ -1544,12 +1582,12 @@ module SyntaxTree
1544
1582
 
1545
1583
  unless bodystmt.empty?
1546
1584
  q.indent do
1547
- q.breakable(force: true) unless bodystmt.statements.empty?
1585
+ q.breakable_force unless bodystmt.statements.empty?
1548
1586
  q.format(bodystmt)
1549
1587
  end
1550
1588
  end
1551
1589
 
1552
- q.breakable(force: true)
1590
+ q.breakable_force
1553
1591
  q.text("end")
1554
1592
  end
1555
1593
  end
@@ -1592,10 +1630,10 @@ module SyntaxTree
1592
1630
  q.text("^(")
1593
1631
  q.nest(1) do
1594
1632
  q.indent do
1595
- q.breakable("")
1633
+ q.breakable_empty
1596
1634
  q.format(statement)
1597
1635
  end
1598
- q.breakable("")
1636
+ q.breakable_empty
1599
1637
  q.text(")")
1600
1638
  end
1601
1639
  end
@@ -1613,6 +1651,20 @@ module SyntaxTree
1613
1651
  # array << value
1614
1652
  #
1615
1653
  class Binary < Node
1654
+ # Since Binary's operator is a symbol, it's better to use the `name` method
1655
+ # than to allocate a new string every time. This is a tiny performance
1656
+ # optimization, but enough that it shows up in the profiler. Adding this in
1657
+ # for older Ruby versions.
1658
+ unless :+.respond_to?(:name)
1659
+ using Module.new {
1660
+ refine Symbol do
1661
+ def name
1662
+ to_s.freeze
1663
+ end
1664
+ end
1665
+ }
1666
+ end
1667
+
1616
1668
  # [untyped] the left-hand side of the expression
1617
1669
  attr_reader :left
1618
1670
 
@@ -1661,15 +1713,13 @@ module SyntaxTree
1661
1713
  q.text(" ") unless power
1662
1714
 
1663
1715
  if operator == :<<
1664
- q.text(operator.to_s)
1665
- q.text(" ")
1716
+ q.text("<< ")
1666
1717
  q.format(right)
1667
1718
  else
1668
1719
  q.group do
1669
- q.text(operator.to_s)
1670
-
1720
+ q.text(operator.name)
1671
1721
  q.indent do
1672
- q.breakable(power ? "" : " ")
1722
+ power ? q.breakable_empty : q.breakable_space
1673
1723
  q.format(right)
1674
1724
  end
1675
1725
  end
@@ -1716,15 +1766,29 @@ module SyntaxTree
1716
1766
  { params: params, locals: locals, location: location, comments: comments }
1717
1767
  end
1718
1768
 
1769
+ # Within the pipes of the block declaration, we don't want any spaces. So
1770
+ # we'll separate the parameters with a comma and space but no breakables.
1771
+ class Separator
1772
+ def call(q)
1773
+ q.text(", ")
1774
+ end
1775
+ end
1776
+
1777
+ # We'll keep a single instance of this separator around for all block vars
1778
+ # to cut down on allocations.
1779
+ SEPARATOR = Separator.new
1780
+
1719
1781
  def format(q)
1720
- q.group(0, "|", "|") do
1782
+ q.text("|")
1783
+ q.group do
1721
1784
  q.remove_breaks(q.format(params))
1722
1785
 
1723
1786
  if locals.any?
1724
1787
  q.text("; ")
1725
- q.seplist(locals, -> { q.text(", ") }) { |local| q.format(local) }
1788
+ q.seplist(locals, SEPARATOR) { |local| q.format(local) }
1726
1789
  end
1727
1790
  end
1791
+ q.text("|")
1728
1792
  end
1729
1793
  end
1730
1794
 
@@ -1816,10 +1880,8 @@ module SyntaxTree
1816
1880
  end_column: end_column
1817
1881
  )
1818
1882
 
1819
- parts = [rescue_clause, else_clause, ensure_clause]
1820
-
1821
1883
  # Here we're going to determine the bounds for the statements
1822
- consequent = parts.compact.first
1884
+ consequent = rescue_clause || else_clause || ensure_clause
1823
1885
  statements.bind(
1824
1886
  start_char,
1825
1887
  start_column,
@@ -1829,7 +1891,7 @@ module SyntaxTree
1829
1891
 
1830
1892
  # Next we're going to determine the rescue clause if there is one
1831
1893
  if rescue_clause
1832
- consequent = parts.drop(1).compact.first
1894
+ consequent = else_clause || ensure_clause
1833
1895
  rescue_clause.bind_end(
1834
1896
  consequent ? consequent.location.start_char : end_char,
1835
1897
  consequent ? consequent.location.start_column : end_column
@@ -1868,26 +1930,26 @@ module SyntaxTree
1868
1930
 
1869
1931
  if rescue_clause
1870
1932
  q.nest(-2) do
1871
- q.breakable(force: true)
1933
+ q.breakable_force
1872
1934
  q.format(rescue_clause)
1873
1935
  end
1874
1936
  end
1875
1937
 
1876
1938
  if else_clause
1877
1939
  q.nest(-2) do
1878
- q.breakable(force: true)
1940
+ q.breakable_force
1879
1941
  q.format(else_keyword)
1880
1942
  end
1881
1943
 
1882
1944
  unless else_clause.empty?
1883
- q.breakable(force: true)
1945
+ q.breakable_force
1884
1946
  q.format(else_clause)
1885
1947
  end
1886
1948
  end
1887
1949
 
1888
1950
  if ensure_clause
1889
1951
  q.nest(-2) do
1890
- q.breakable(force: true)
1952
+ q.breakable_force
1891
1953
  q.format(ensure_clause)
1892
1954
  end
1893
1955
  end
@@ -1955,12 +2017,11 @@ module SyntaxTree
1955
2017
  # If the receiver of this block a Command or CommandCall node, then there
1956
2018
  # are no parentheses around the arguments to that command, so we need to
1957
2019
  # break the block.
1958
- case q.parent
1959
- in { call: Command | CommandCall }
2020
+ case q.parent.call
2021
+ when Command, CommandCall
1960
2022
  q.break_parent
1961
2023
  format_break(q, break_opening, break_closing)
1962
2024
  return
1963
- else
1964
2025
  end
1965
2026
 
1966
2027
  q.group do
@@ -1980,9 +2041,9 @@ module SyntaxTree
1980
2041
  # know for certain we're going to get split over multiple lines
1981
2042
  # anyway.
1982
2043
  case parent
1983
- in Statements | ArgParen
2044
+ when Statements, ArgParen
1984
2045
  break false
1985
- in Command | CommandCall
2046
+ when Command, CommandCall
1986
2047
  true
1987
2048
  else
1988
2049
  false
@@ -1993,8 +2054,8 @@ module SyntaxTree
1993
2054
  # If we're a sibling of a control-flow keyword, then we're going to have to
1994
2055
  # use the do..end bounds.
1995
2056
  def forced_do_end_bounds?(q)
1996
- case q.parent
1997
- in { call: Break | Next | Return | Super }
2057
+ case q.parent.call
2058
+ when Break, Next, Return, Super
1998
2059
  true
1999
2060
  else
2000
2061
  false
@@ -2004,22 +2065,19 @@ module SyntaxTree
2004
2065
  # If we're the predicate of a loop or conditional, then we're going to have
2005
2066
  # to go with the {..} bounds.
2006
2067
  def forced_brace_bounds?(q)
2007
- parents = q.parents.to_a
2008
- parents.each_with_index.any? do |parent, index|
2009
- # If we hit certain breakpoints then we know we're safe.
2010
- break false if [Paren, Statements].include?(parent.class)
2068
+ previous = nil
2069
+ q.parents.any? do |parent|
2070
+ case parent
2071
+ when Paren, Statements
2072
+ # If we hit certain breakpoints then we know we're safe.
2073
+ return false
2074
+ when If, IfMod, IfOp, Unless, UnlessMod, While, WhileMod, Until,
2075
+ UntilMod
2076
+ return true if parent.predicate == previous
2077
+ end
2011
2078
 
2012
- [
2013
- If,
2014
- IfMod,
2015
- IfOp,
2016
- Unless,
2017
- UnlessMod,
2018
- While,
2019
- WhileMod,
2020
- Until,
2021
- UntilMod
2022
- ].include?(parent.class) && parent.predicate == parents[index - 1]
2079
+ previous = parent
2080
+ false
2023
2081
  end
2024
2082
  end
2025
2083
 
@@ -2034,12 +2092,12 @@ module SyntaxTree
2034
2092
 
2035
2093
  unless statements.empty?
2036
2094
  q.indent do
2037
- q.breakable
2095
+ q.breakable_space
2038
2096
  q.format(statements)
2039
2097
  end
2040
2098
  end
2041
2099
 
2042
- q.breakable
2100
+ q.breakable_space
2043
2101
  q.text(closing)
2044
2102
  end
2045
2103
 
@@ -2048,17 +2106,17 @@ module SyntaxTree
2048
2106
  q.format(BlockOpenFormatter.new(opening, block_open), stackable: false)
2049
2107
 
2050
2108
  if node.block_var
2051
- q.breakable
2109
+ q.breakable_space
2052
2110
  q.format(node.block_var)
2053
- q.breakable
2111
+ q.breakable_space
2054
2112
  end
2055
2113
 
2056
2114
  if statements.empty?
2057
2115
  q.text(" ") if opening == "do"
2058
2116
  else
2059
- q.breakable unless node.block_var
2117
+ q.breakable_space unless node.block_var
2060
2118
  q.format(statements)
2061
- q.breakable
2119
+ q.breakable_space
2062
2120
  end
2063
2121
 
2064
2122
  q.text(closing)
@@ -2133,105 +2191,128 @@ module SyntaxTree
2133
2191
  q.group do
2134
2192
  q.text(keyword)
2135
2193
 
2136
- case node.arguments.parts
2137
- in []
2194
+ parts = node.arguments.parts
2195
+ length = parts.length
2196
+
2197
+ if length == 0
2138
2198
  # Here there are no arguments at all, so we're not going to print
2139
2199
  # anything. This would be like if we had:
2140
2200
  #
2141
2201
  # break
2142
2202
  #
2143
- in [
2144
- Paren[
2145
- contents: {
2146
- body: [ArrayLiteral[contents: { parts: [_, _, *] }] => array] }
2147
- ]
2148
- ]
2149
- # Here we have a single argument that is a set of parentheses wrapping
2150
- # an array literal that has at least 2 elements. We're going to print
2151
- # the contents of the array directly. This would be like if we had:
2152
- #
2153
- # break([1, 2, 3])
2154
- #
2155
- # which we will print as:
2156
- #
2157
- # break 1, 2, 3
2158
- #
2159
- q.text(" ")
2160
- format_array_contents(q, array)
2161
- in [Paren[contents: { body: [ArrayLiteral => statement] }]]
2162
- # Here we have a single argument that is a set of parentheses wrapping
2163
- # an array literal that has 0 or 1 elements. We're going to skip the
2164
- # parentheses but print the array itself. This would be like if we
2165
- # had:
2166
- #
2167
- # break([1])
2168
- #
2169
- # which we will print as:
2170
- #
2171
- # break [1]
2172
- #
2173
- q.text(" ")
2174
- q.format(statement)
2175
- in [Paren[contents: { body: [statement] }]] if skip_parens?(statement)
2176
- # Here we have a single argument that is a set of parentheses that
2177
- # themselves contain a single statement. That statement is a simple
2178
- # value that we can skip the parentheses for. This would be like if we
2179
- # had:
2180
- #
2181
- # break(1)
2182
- #
2183
- # which we will print as:
2184
- #
2185
- # break 1
2186
- #
2187
- q.text(" ")
2188
- q.format(statement)
2189
- in [Paren => part]
2190
- # Here we have a single argument that is a set of parentheses. We're
2191
- # going to print the parentheses themselves as if they were the set of
2192
- # arguments. This would be like if we had:
2193
- #
2194
- # break(foo.bar)
2195
- #
2196
- q.format(part)
2197
- in [ArrayLiteral[contents: { parts: [_, _, *] }] => array]
2198
- # Here there is a single argument that is an array literal with at
2199
- # least two elements. We skip directly into the array literal's
2200
- # elements in order to print the contents. This would be like if we
2201
- # had:
2202
- #
2203
- # break [1, 2, 3]
2204
- #
2205
- # which we will print as:
2206
- #
2207
- # break 1, 2, 3
2208
- #
2209
- q.text(" ")
2210
- format_array_contents(q, array)
2211
- in [ArrayLiteral => part]
2212
- # Here there is a single argument that is an array literal with 0 or 1
2213
- # elements. In this case we're going to print the array as it is
2214
- # because skipping the brackets would change the remaining. This would
2215
- # be like if we had:
2216
- #
2217
- # break []
2218
- # break [1]
2219
- #
2220
- q.text(" ")
2221
- q.format(part)
2222
- in [_]
2223
- # Here there is a single argument that hasn't matched one of our
2224
- # previous cases. We're going to print the argument as it is. This
2225
- # would be like if we had:
2226
- #
2227
- # break foo
2228
- #
2229
- format_arguments(q, "(", ")")
2230
- else
2203
+ elsif length >= 2
2231
2204
  # If there are multiple arguments, format them all. If the line is
2232
2205
  # going to break into multiple, then use brackets to start and end the
2233
2206
  # expression.
2234
2207
  format_arguments(q, " [", "]")
2208
+ else
2209
+ # If we get here, then we're formatting a single argument to the flow
2210
+ # control keyword.
2211
+ part = parts.first
2212
+
2213
+ case part
2214
+ when Paren
2215
+ statements = part.contents.body
2216
+
2217
+ if statements.length == 1
2218
+ statement = statements.first
2219
+
2220
+ if statement.is_a?(ArrayLiteral)
2221
+ contents = statement.contents
2222
+
2223
+ if contents && contents.parts.length >= 2
2224
+ # Here we have a single argument that is a set of parentheses
2225
+ # wrapping an array literal that has at least 2 elements.
2226
+ # We're going to print the contents of the array directly.
2227
+ # This would be like if we had:
2228
+ #
2229
+ # break([1, 2, 3])
2230
+ #
2231
+ # which we will print as:
2232
+ #
2233
+ # break 1, 2, 3
2234
+ #
2235
+ q.text(" ")
2236
+ format_array_contents(q, statement)
2237
+ else
2238
+ # Here we have a single argument that is a set of parentheses
2239
+ # wrapping an array literal that has 0 or 1 elements. We're
2240
+ # going to skip the parentheses but print the array itself.
2241
+ # This would be like if we had:
2242
+ #
2243
+ # break([1])
2244
+ #
2245
+ # which we will print as:
2246
+ #
2247
+ # break [1]
2248
+ #
2249
+ q.text(" ")
2250
+ q.format(statement)
2251
+ end
2252
+ elsif skip_parens?(statement)
2253
+ # Here we have a single argument that is a set of parentheses
2254
+ # that themselves contain a single statement. That statement is
2255
+ # a simple value that we can skip the parentheses for. This
2256
+ # would be like if we had:
2257
+ #
2258
+ # break(1)
2259
+ #
2260
+ # which we will print as:
2261
+ #
2262
+ # break 1
2263
+ #
2264
+ q.text(" ")
2265
+ q.format(statement)
2266
+ else
2267
+ # Here we have a single argument that is a set of parentheses.
2268
+ # We're going to print the parentheses themselves as if they
2269
+ # were the set of arguments. This would be like if we had:
2270
+ #
2271
+ # break(foo.bar)
2272
+ #
2273
+ q.format(part)
2274
+ end
2275
+ else
2276
+ q.format(part)
2277
+ end
2278
+ when ArrayLiteral
2279
+ contents = part.contents
2280
+
2281
+ if contents && contents.parts.length >= 2
2282
+ # Here there is a single argument that is an array literal with at
2283
+ # least two elements. We skip directly into the array literal's
2284
+ # elements in order to print the contents. This would be like if
2285
+ # we had:
2286
+ #
2287
+ # break [1, 2, 3]
2288
+ #
2289
+ # which we will print as:
2290
+ #
2291
+ # break 1, 2, 3
2292
+ #
2293
+ q.text(" ")
2294
+ format_array_contents(q, part)
2295
+ else
2296
+ # Here there is a single argument that is an array literal with 0
2297
+ # or 1 elements. In this case we're going to print the array as it
2298
+ # is because skipping the brackets would change the remaining.
2299
+ # This would be like if we had:
2300
+ #
2301
+ # break []
2302
+ # break [1]
2303
+ #
2304
+ q.text(" ")
2305
+ q.format(part)
2306
+ end
2307
+ else
2308
+ # Here there is a single argument that hasn't matched one of our
2309
+ # previous cases. We're going to print the argument as it is. This
2310
+ # would be like if we had:
2311
+ #
2312
+ # break foo
2313
+ #
2314
+ format_arguments(q, "(", ")")
2315
+ end
2235
2316
  end
2236
2317
  end
2237
2318
  end
@@ -2241,29 +2322,34 @@ module SyntaxTree
2241
2322
  def format_array_contents(q, array)
2242
2323
  q.if_break { q.text("[") }
2243
2324
  q.indent do
2244
- q.breakable("")
2325
+ q.breakable_empty
2245
2326
  q.format(array.contents)
2246
2327
  end
2247
- q.breakable("")
2328
+ q.breakable_empty
2248
2329
  q.if_break { q.text("]") }
2249
2330
  end
2250
2331
 
2251
2332
  def format_arguments(q, opening, closing)
2252
2333
  q.if_break { q.text(opening) }
2253
2334
  q.indent do
2254
- q.breakable(" ")
2335
+ q.breakable_space
2255
2336
  q.format(node.arguments)
2256
2337
  end
2257
- q.breakable("")
2338
+ q.breakable_empty
2258
2339
  q.if_break { q.text(closing) }
2259
2340
  end
2260
2341
 
2261
2342
  def skip_parens?(node)
2262
2343
  case node
2263
- in FloatLiteral | Imaginary | Int | RationalLiteral
2264
- true
2265
- in VarRef[value: Const | CVar | GVar | IVar | Kw]
2344
+ when FloatLiteral, Imaginary, Int, RationalLiteral
2266
2345
  true
2346
+ when VarRef
2347
+ case node.value
2348
+ when Const, CVar, GVar, IVar, Kw
2349
+ true
2350
+ else
2351
+ false
2352
+ end
2267
2353
  else
2268
2354
  false
2269
2355
  end
@@ -2326,8 +2412,10 @@ module SyntaxTree
2326
2412
 
2327
2413
  def format(q)
2328
2414
  case operator
2329
- in :"::" | Op[value: "::"]
2415
+ when :"::"
2330
2416
  q.text(".")
2417
+ when Op
2418
+ operator.value == "::" ? q.text(".") : operator.format(q)
2331
2419
  else
2332
2420
  operator.format(q)
2333
2421
  end
@@ -2363,13 +2451,18 @@ module SyntaxTree
2363
2451
  # First, walk down the chain until we get to the point where we're not
2364
2452
  # longer at a chainable node.
2365
2453
  loop do
2366
- case children.last
2367
- in Call[receiver: Call]
2368
- children << children.last.receiver
2369
- in Call[receiver: MethodAddBlock[call: Call]]
2370
- children << children.last.receiver
2371
- in MethodAddBlock[call: Call]
2372
- children << children.last.call
2454
+ case (child = children.last)
2455
+ when Call
2456
+ case (receiver = child.receiver)
2457
+ when Call
2458
+ children << receiver
2459
+ when MethodAddBlock
2460
+ receiver.call.is_a?(Call) ? children << receiver : break
2461
+ else
2462
+ break
2463
+ end
2464
+ when MethodAddBlock
2465
+ child.call.is_a?(Call) ? children << child.call : break
2373
2466
  else
2374
2467
  break
2375
2468
  end
@@ -2388,10 +2481,9 @@ module SyntaxTree
2388
2481
  # nodes.
2389
2482
  parent = parents[3] if parent.is_a?(DoBlock)
2390
2483
 
2391
- case parent
2392
- in MethodAddBlock[call: FCall[value: { value: "sig" }]]
2484
+ if parent.is_a?(MethodAddBlock) && parent.call.is_a?(FCall) &&
2485
+ parent.call.value.value == "sig"
2393
2486
  threshold = 2
2394
- else
2395
2487
  end
2396
2488
  end
2397
2489
 
@@ -2434,20 +2526,21 @@ module SyntaxTree
2434
2526
  skip_operator = false
2435
2527
 
2436
2528
  while (child = children.pop)
2437
- case child
2438
- in Call[
2439
- receiver: Call[message: { value: "where" }],
2440
- message: { value: "not" }
2441
- ]
2442
- # This is very specialized behavior wherein we group
2443
- # .where.not calls together because it looks better. For more
2444
- # information, see
2445
- # https://github.com/prettier/plugin-ruby/issues/862.
2446
- in Call
2447
- # If we're at a Call node and not a MethodAddBlock node in the
2448
- # chain then we're going to add a newline so it indents properly.
2449
- q.breakable("")
2450
- else
2529
+ if child.is_a?(Call)
2530
+ if child.receiver.is_a?(Call) &&
2531
+ (child.receiver.message != :call) &&
2532
+ (child.receiver.message.value == "where") &&
2533
+ (child.message.value == "not")
2534
+ # This is very specialized behavior wherein we group
2535
+ # .where.not calls together because it looks better. For more
2536
+ # information, see
2537
+ # https://github.com/prettier/plugin-ruby/issues/862.
2538
+ else
2539
+ # If we're at a Call node and not a MethodAddBlock node in the
2540
+ # chain then we're going to add a newline so it indents
2541
+ # properly.
2542
+ q.breakable_empty
2543
+ end
2451
2544
  end
2452
2545
 
2453
2546
  format_child(
@@ -2460,9 +2553,9 @@ module SyntaxTree
2460
2553
 
2461
2554
  # If the parent call node has a comment on the message then we need
2462
2555
  # to print the operator trailing in order to keep it working.
2463
- case children.last
2464
- in Call[message: { comments: [_, *] }, operator:]
2465
- q.format(CallOperatorFormatter.new(operator))
2556
+ last_child = children.last
2557
+ if last_child.is_a?(Call) && last_child.message.comments.any?
2558
+ q.format(CallOperatorFormatter.new(last_child.operator))
2466
2559
  skip_operator = true
2467
2560
  else
2468
2561
  skip_operator = false
@@ -2477,18 +2570,22 @@ module SyntaxTree
2477
2570
 
2478
2571
  if empty_except_last
2479
2572
  case node
2480
- in Call
2573
+ when Call
2481
2574
  node.format_arguments(q)
2482
- in MethodAddBlock[block:]
2483
- q.format(block)
2575
+ when MethodAddBlock
2576
+ q.format(node.block)
2484
2577
  end
2485
2578
  end
2486
2579
  end
2487
2580
 
2488
2581
  def self.chained?(node)
2582
+ return false if ENV["STREE_FAST_FORMAT"]
2583
+
2489
2584
  case node
2490
- in Call | MethodAddBlock[call: Call]
2585
+ when Call
2491
2586
  true
2587
+ when MethodAddBlock
2588
+ node.call.is_a?(Call)
2492
2589
  else
2493
2590
  false
2494
2591
  end
@@ -2500,9 +2597,12 @@ module SyntaxTree
2500
2597
  # want to indent the first call. So we'll pop off the first children and
2501
2598
  # format it separately here.
2502
2599
  def attach_directly?(node)
2503
- [ArrayLiteral, HashLiteral, Heredoc, If, Unless, XStringLiteral].include?(
2504
- node.receiver.class
2505
- )
2600
+ case node.receiver
2601
+ when ArrayLiteral, HashLiteral, Heredoc, If, Unless, XStringLiteral
2602
+ true
2603
+ else
2604
+ false
2605
+ end
2506
2606
  end
2507
2607
 
2508
2608
  def format_child(
@@ -2514,7 +2614,7 @@ module SyntaxTree
2514
2614
  )
2515
2615
  # First, format the actual contents of the child.
2516
2616
  case child
2517
- in Call
2617
+ when Call
2518
2618
  q.group do
2519
2619
  unless skip_operator
2520
2620
  q.format(CallOperatorFormatter.new(child.operator))
@@ -2522,7 +2622,7 @@ module SyntaxTree
2522
2622
  q.format(child.message) if child.message != :call
2523
2623
  child.format_arguments(q) unless skip_attached
2524
2624
  end
2525
- in MethodAddBlock
2625
+ when MethodAddBlock
2526
2626
  q.format(child.block) unless skip_attached
2527
2627
  end
2528
2628
 
@@ -2530,7 +2630,7 @@ module SyntaxTree
2530
2630
  # them out here since we're bypassing the normal comment printing.
2531
2631
  if child.comments.any? && !skip_comments
2532
2632
  child.comments.each do |comment|
2533
- comment.inline? ? q.text(" ") : q.breakable
2633
+ comment.inline? ? q.text(" ") : q.breakable_space
2534
2634
  comment.format(q)
2535
2635
  end
2536
2636
 
@@ -2605,8 +2705,8 @@ module SyntaxTree
2605
2705
  # If we're at the top of a call chain, then we're going to do some
2606
2706
  # specialized printing in case we can print it nicely. We _only_ do this
2607
2707
  # at the top of the chain to avoid weird recursion issues.
2608
- if !CallChainFormatter.chained?(q.parent) &&
2609
- CallChainFormatter.chained?(receiver)
2708
+ if CallChainFormatter.chained?(receiver) &&
2709
+ !CallChainFormatter.chained?(q.parent)
2610
2710
  q.group do
2611
2711
  q
2612
2712
  .if_break { CallChainFormatter.new(self).format(q) }
@@ -2617,15 +2717,15 @@ module SyntaxTree
2617
2717
  end
2618
2718
  end
2619
2719
 
2720
+ # Print out the arguments to this call. If there are no arguments, then do
2721
+ #nothing.
2620
2722
  def format_arguments(q)
2621
2723
  case arguments
2622
- in ArgParen
2724
+ when ArgParen
2623
2725
  q.format(arguments)
2624
- in Args
2726
+ when Args
2625
2727
  q.text(" ")
2626
2728
  q.format(arguments)
2627
- else
2628
- # Do nothing if there are no arguments.
2629
2729
  end
2630
2730
  end
2631
2731
 
@@ -2642,7 +2742,7 @@ module SyntaxTree
2642
2742
  q.group do
2643
2743
  q.indent do
2644
2744
  if receiver.comments.any? || call_operator.comments.any?
2645
- q.breakable(force: true)
2745
+ q.breakable_force
2646
2746
  end
2647
2747
 
2648
2748
  if call_operator.comments.empty?
@@ -2719,9 +2819,9 @@ module SyntaxTree
2719
2819
  q.format(value)
2720
2820
  end
2721
2821
 
2722
- q.breakable(force: true)
2822
+ q.breakable_force
2723
2823
  q.format(consequent)
2724
- q.breakable(force: true)
2824
+ q.breakable_force
2725
2825
 
2726
2826
  q.text("end")
2727
2827
  end
@@ -2782,13 +2882,13 @@ module SyntaxTree
2782
2882
  q.format(operator)
2783
2883
 
2784
2884
  case pattern
2785
- in AryPtn | FndPtn | HshPtn
2885
+ when AryPtn, FndPtn, HshPtn
2786
2886
  q.text(" ")
2787
2887
  q.format(pattern)
2788
2888
  else
2789
2889
  q.group do
2790
2890
  q.indent do
2791
- q.breakable
2891
+ q.breakable_space
2792
2892
  q.format(pattern)
2793
2893
  end
2794
2894
  end
@@ -2872,38 +2972,40 @@ module SyntaxTree
2872
2972
  end
2873
2973
 
2874
2974
  def format(q)
2875
- declaration = -> do
2876
- q.group do
2877
- q.text("class ")
2878
- q.format(constant)
2879
-
2880
- if superclass
2881
- q.text(" < ")
2882
- q.format(superclass)
2883
- end
2884
- end
2885
- end
2886
-
2887
2975
  if bodystmt.empty?
2888
2976
  q.group do
2889
- declaration.call
2890
- q.breakable(force: true)
2977
+ format_declaration(q)
2978
+ q.breakable_force
2891
2979
  q.text("end")
2892
2980
  end
2893
2981
  else
2894
2982
  q.group do
2895
- declaration.call
2983
+ format_declaration(q)
2896
2984
 
2897
2985
  q.indent do
2898
- q.breakable(force: true)
2986
+ q.breakable_force
2899
2987
  q.format(bodystmt)
2900
2988
  end
2901
2989
 
2902
- q.breakable(force: true)
2990
+ q.breakable_force
2903
2991
  q.text("end")
2904
2992
  end
2905
2993
  end
2906
2994
  end
2995
+
2996
+ private
2997
+
2998
+ def format_declaration(q)
2999
+ q.group do
3000
+ q.text("class ")
3001
+ q.format(constant)
3002
+
3003
+ if superclass
3004
+ q.text(" < ")
3005
+ q.format(superclass)
3006
+ end
3007
+ end
3008
+ end
2907
3009
  end
2908
3010
 
2909
3011
  # Comma represents the use of the , operator.
@@ -2983,15 +3085,31 @@ module SyntaxTree
2983
3085
  private
2984
3086
 
2985
3087
  def align(q, node, &block)
2986
- case node.arguments
2987
- in Args[parts: [Def | Defs | DefEndless]]
2988
- q.text(" ")
2989
- yield
2990
- in Args[parts: [IfOp]]
2991
- q.if_flat { q.text(" ") }
2992
- yield
2993
- in Args[parts: [Command => command]]
2994
- align(q, command, &block)
3088
+ arguments = node.arguments
3089
+
3090
+ if arguments.is_a?(Args)
3091
+ parts = arguments.parts
3092
+
3093
+ if parts.size == 1
3094
+ part = parts.first
3095
+
3096
+ case part
3097
+ when Def, Defs, DefEndless
3098
+ q.text(" ")
3099
+ yield
3100
+ when IfOp
3101
+ q.if_flat { q.text(" ") }
3102
+ yield
3103
+ when Command
3104
+ align(q, part, &block)
3105
+ else
3106
+ q.text(" ")
3107
+ q.nest(message.value.length + 1) { yield }
3108
+ end
3109
+ else
3110
+ q.text(" ")
3111
+ q.nest(message.value.length + 1) { yield }
3112
+ end
2995
3113
  else
2996
3114
  q.text(" ")
2997
3115
  q.nest(message.value.length + 1) { yield }
@@ -3069,7 +3187,7 @@ module SyntaxTree
3069
3187
  if message.comments.any?(&:leading?)
3070
3188
  q.format(CallOperatorFormatter.new(operator), stackable: false)
3071
3189
  q.indent do
3072
- q.breakable("")
3190
+ q.breakable_empty
3073
3191
  q.format(message)
3074
3192
  end
3075
3193
  else
@@ -3078,15 +3196,18 @@ module SyntaxTree
3078
3196
  end
3079
3197
  end
3080
3198
 
3081
- case arguments
3082
- in Args[parts: [IfOp]]
3083
- q.if_flat { q.text(" ") }
3084
- q.format(arguments)
3085
- in Args
3086
- q.text(" ")
3087
- q.nest(argument_alignment(q, doc)) { q.format(arguments) }
3088
- else
3089
- # If there are no arguments, print nothing.
3199
+ # Format the arguments for this command call here. If there are no
3200
+ # arguments, then print nothing.
3201
+ if arguments
3202
+ parts = arguments.parts
3203
+
3204
+ if parts.length == 1 && parts.first.is_a?(IfOp)
3205
+ q.if_flat { q.text(" ") }
3206
+ q.format(arguments)
3207
+ else
3208
+ q.text(" ")
3209
+ q.nest(argument_alignment(q, doc)) { q.format(arguments) }
3210
+ end
3090
3211
  end
3091
3212
  end
3092
3213
  end
@@ -3155,7 +3276,7 @@ module SyntaxTree
3155
3276
  end
3156
3277
 
3157
3278
  def ignore?
3158
- value[1..].strip == "stree-ignore"
3279
+ value.match?(/\A#\s*stree-ignore\s*\z/)
3159
3280
  end
3160
3281
 
3161
3282
  def comments
@@ -3455,12 +3576,12 @@ module SyntaxTree
3455
3576
 
3456
3577
  unless bodystmt.empty?
3457
3578
  q.indent do
3458
- q.breakable(force: true)
3579
+ q.breakable_force
3459
3580
  q.format(bodystmt)
3460
3581
  end
3461
3582
  end
3462
3583
 
3463
- q.breakable(force: true)
3584
+ q.breakable_force
3464
3585
  q.text("end")
3465
3586
  end
3466
3587
  end
@@ -3549,7 +3670,7 @@ module SyntaxTree
3549
3670
  q.text(" =")
3550
3671
  q.group do
3551
3672
  q.indent do
3552
- q.breakable
3673
+ q.breakable_space
3553
3674
  q.format(statement)
3554
3675
  end
3555
3676
  end
@@ -3590,13 +3711,15 @@ module SyntaxTree
3590
3711
  end
3591
3712
 
3592
3713
  def format(q)
3593
- q.group(0, "defined?(", ")") do
3714
+ q.text("defined?(")
3715
+ q.group do
3594
3716
  q.indent do
3595
- q.breakable("")
3717
+ q.breakable_empty
3596
3718
  q.format(value)
3597
3719
  end
3598
- q.breakable("")
3720
+ q.breakable_empty
3599
3721
  end
3722
+ q.text(")")
3600
3723
  end
3601
3724
  end
3602
3725
 
@@ -3678,12 +3801,12 @@ module SyntaxTree
3678
3801
 
3679
3802
  unless bodystmt.empty?
3680
3803
  q.indent do
3681
- q.breakable(force: true)
3804
+ q.breakable_force
3682
3805
  q.format(bodystmt)
3683
3806
  end
3684
3807
  end
3685
3808
 
3686
- q.breakable(force: true)
3809
+ q.breakable_force
3687
3810
  q.text("end")
3688
3811
  end
3689
3812
  end
@@ -3755,15 +3878,18 @@ module SyntaxTree
3755
3878
  end
3756
3879
 
3757
3880
  def format(q)
3758
- space = [If, IfMod, Unless, UnlessMod].include?(q.parent.class)
3759
-
3760
3881
  left = node.left
3761
3882
  right = node.right
3762
3883
 
3763
3884
  q.format(left) if left
3764
- q.text(" ") if space
3765
- q.text(operator)
3766
- q.text(" ") if space
3885
+
3886
+ case q.parent
3887
+ when If, IfMod, Unless, UnlessMod
3888
+ q.text(" #{operator} ")
3889
+ else
3890
+ q.text(operator)
3891
+ end
3892
+
3767
3893
  q.format(right) if right
3768
3894
  end
3769
3895
  end
@@ -3948,19 +4074,30 @@ module SyntaxTree
3948
4074
  def format(q)
3949
4075
  opening_quote, closing_quote = quotes(q)
3950
4076
 
3951
- q.group(0, opening_quote, closing_quote) do
4077
+ q.text(opening_quote)
4078
+ q.group do
3952
4079
  parts.each do |part|
3953
4080
  if part.is_a?(TStringContent)
3954
4081
  value = Quotes.normalize(part.value, closing_quote)
3955
- separator = -> { q.breakable(force: true, indent: false) }
3956
- q.seplist(value.split(/\r?\n/, -1), separator) do |text|
3957
- q.text(text)
4082
+ first = true
4083
+
4084
+ value.each_line(chomp: true) do |line|
4085
+ if first
4086
+ first = false
4087
+ else
4088
+ q.breakable_return
4089
+ end
4090
+
4091
+ q.text(line)
3958
4092
  end
4093
+
4094
+ q.breakable_return if value.end_with?("\n")
3959
4095
  else
3960
4096
  q.format(part)
3961
4097
  end
3962
4098
  end
3963
4099
  end
4100
+ q.text(closing_quote)
3964
4101
  end
3965
4102
 
3966
4103
  private
@@ -4056,7 +4193,7 @@ module SyntaxTree
4056
4193
 
4057
4194
  unless statements.empty?
4058
4195
  q.indent do
4059
- q.breakable(force: true)
4196
+ q.breakable_force
4060
4197
  q.format(statements)
4061
4198
  end
4062
4199
  end
@@ -4126,14 +4263,14 @@ module SyntaxTree
4126
4263
 
4127
4264
  unless statements.empty?
4128
4265
  q.indent do
4129
- q.breakable(force: true)
4266
+ q.breakable_force
4130
4267
  q.format(statements)
4131
4268
  end
4132
4269
  end
4133
4270
 
4134
4271
  if consequent
4135
4272
  q.group do
4136
- q.breakable(force: true)
4273
+ q.breakable_force
4137
4274
  q.format(consequent)
4138
4275
  end
4139
4276
  end
@@ -4329,7 +4466,7 @@ module SyntaxTree
4329
4466
 
4330
4467
  unless statements.empty?
4331
4468
  q.indent do
4332
- q.breakable(force: true)
4469
+ q.breakable_force
4333
4470
  q.format(statements)
4334
4471
  end
4335
4472
  end
@@ -4588,7 +4725,7 @@ module SyntaxTree
4588
4725
  q.text("[")
4589
4726
 
4590
4727
  q.indent do
4591
- q.breakable("")
4728
+ q.breakable_empty
4592
4729
 
4593
4730
  q.text("*")
4594
4731
  q.format(left)
@@ -4601,7 +4738,7 @@ module SyntaxTree
4601
4738
  q.format(right)
4602
4739
  end
4603
4740
 
4604
- q.breakable("")
4741
+ q.breakable_empty
4605
4742
  q.text("]")
4606
4743
  end
4607
4744
  end
@@ -4663,12 +4800,12 @@ module SyntaxTree
4663
4800
 
4664
4801
  unless statements.empty?
4665
4802
  q.indent do
4666
- q.breakable(force: true)
4803
+ q.breakable_force
4667
4804
  q.format(statements)
4668
4805
  end
4669
4806
  end
4670
4807
 
4671
- q.breakable(force: true)
4808
+ q.breakable_force
4672
4809
  q.text("end")
4673
4810
  end
4674
4811
  end
@@ -4731,11 +4868,11 @@ module SyntaxTree
4731
4868
  q.text("{")
4732
4869
  q.indent do
4733
4870
  lbrace.comments.each do |comment|
4734
- q.breakable(force: true)
4871
+ q.breakable_force
4735
4872
  comment.format(q)
4736
4873
  end
4737
4874
  end
4738
- q.breakable(force: true)
4875
+ q.breakable_force
4739
4876
  q.text("}")
4740
4877
  end
4741
4878
  end
@@ -4800,14 +4937,14 @@ module SyntaxTree
4800
4937
  q.format(lbrace)
4801
4938
 
4802
4939
  if assocs.empty?
4803
- q.breakable("")
4940
+ q.breakable_empty
4804
4941
  else
4805
4942
  q.indent do
4806
- q.breakable
4943
+ q.breakable_space
4807
4944
  q.seplist(assocs) { |assoc| q.format(assoc) }
4808
4945
  q.if_break { q.text(",") } if q.trailing_comma?
4809
4946
  end
4810
- q.breakable
4947
+ q.breakable_space
4811
4948
  end
4812
4949
 
4813
4950
  q.text("}")
@@ -4873,22 +5010,34 @@ module SyntaxTree
4873
5010
  }
4874
5011
  end
4875
5012
 
4876
- def format(q)
4877
- # This is a very specific behavior where you want to force a newline, but
4878
- # don't want to force the break parent.
4879
- breakable = -> { q.breakable(indent: false, force: :skip_break_parent) }
5013
+ # This is a very specific behavior where you want to force a newline, but
5014
+ # don't want to force the break parent.
5015
+ SEPARATOR = PrettierPrint::Breakable.new(" ", 1, indent: false, force: true)
4880
5016
 
5017
+ def format(q)
4881
5018
  q.group do
4882
5019
  q.format(beginning)
4883
5020
 
4884
5021
  q.line_suffix(priority: Formatter::HEREDOC_PRIORITY) do
4885
5022
  q.group do
4886
- breakable.call
5023
+ q.target << SEPARATOR
4887
5024
 
4888
5025
  parts.each do |part|
4889
5026
  if part.is_a?(TStringContent)
4890
- texts = part.value.split(/\r?\n/, -1)
4891
- q.seplist(texts, breakable) { |text| q.text(text) }
5027
+ value = part.value
5028
+ first = true
5029
+
5030
+ value.each_line(chomp: true) do |line|
5031
+ if first
5032
+ first = false
5033
+ else
5034
+ q.target << SEPARATOR
5035
+ end
5036
+
5037
+ q.text(line)
5038
+ end
5039
+
5040
+ q.target << SEPARATOR if value.end_with?("\n")
4892
5041
  else
4893
5042
  q.format(part)
4894
5043
  end
@@ -5077,18 +5226,7 @@ module SyntaxTree
5077
5226
  def format(q)
5078
5227
  parts = keywords.map { |(key, value)| KeywordFormatter.new(key, value) }
5079
5228
  parts << KeywordRestFormatter.new(keyword_rest) if keyword_rest
5080
-
5081
5229
  nested = PATTERNS.include?(q.parent.class)
5082
- contents = -> do
5083
- q.group { q.seplist(parts) { |part| q.format(part, stackable: false) } }
5084
-
5085
- # If there isn't a constant, and there's a blank keyword_rest, then we
5086
- # have an plain ** that needs to have a `then` after it in order to
5087
- # parse correctly on the next parse.
5088
- if !constant && keyword_rest && keyword_rest.value.nil? && !nested
5089
- q.text(" then")
5090
- end
5091
- end
5092
5230
 
5093
5231
  # If there is a constant, we're going to format to have the constant name
5094
5232
  # first and then use brackets.
@@ -5097,10 +5235,10 @@ module SyntaxTree
5097
5235
  q.format(constant)
5098
5236
  q.text("[")
5099
5237
  q.indent do
5100
- q.breakable("")
5101
- contents.call
5238
+ q.breakable_empty
5239
+ format_contents(q, parts, nested)
5102
5240
  end
5103
- q.breakable("")
5241
+ q.breakable_empty
5104
5242
  q.text("]")
5105
5243
  end
5106
5244
  return
@@ -5115,7 +5253,7 @@ module SyntaxTree
5115
5253
  # If there's only one pair, then we'll just print the contents provided
5116
5254
  # we're not inside another pattern.
5117
5255
  if !nested && parts.size == 1
5118
- contents.call
5256
+ format_contents(q, parts, nested)
5119
5257
  return
5120
5258
  end
5121
5259
 
@@ -5124,18 +5262,31 @@ module SyntaxTree
5124
5262
  q.group do
5125
5263
  q.text("{")
5126
5264
  q.indent do
5127
- q.breakable
5128
- contents.call
5265
+ q.breakable_space
5266
+ format_contents(q, parts, nested)
5129
5267
  end
5130
5268
 
5131
5269
  if q.target_ruby_version < Gem::Version.new("2.7.3")
5132
5270
  q.text(" }")
5133
5271
  else
5134
- q.breakable
5272
+ q.breakable_space
5135
5273
  q.text("}")
5136
5274
  end
5137
5275
  end
5138
5276
  end
5277
+
5278
+ private
5279
+
5280
+ def format_contents(q, parts, nested)
5281
+ q.group { q.seplist(parts) { |part| q.format(part, stackable: false) } }
5282
+
5283
+ # If there isn't a constant, and there's a blank keyword_rest, then we
5284
+ # have an plain ** that needs to have a `then` after it in order to
5285
+ # parse correctly on the next parse.
5286
+ if !constant && keyword_rest && keyword_rest.value.nil? && !nested
5287
+ q.text(" then")
5288
+ end
5289
+ end
5139
5290
  end
5140
5291
 
5141
5292
  # The list of nodes that represent patterns inside of pattern matching so that
@@ -5188,8 +5339,12 @@ module SyntaxTree
5188
5339
  queue = [parent]
5189
5340
 
5190
5341
  while (node = queue.shift)
5191
- return true if [Assign, MAssign, OpAssign].include?(node.class)
5192
- queue += node.child_nodes.compact
5342
+ case node
5343
+ when Assign, MAssign, OpAssign
5344
+ return true
5345
+ else
5346
+ node.child_nodes.each { |child| queue << child if child }
5347
+ end
5193
5348
  end
5194
5349
 
5195
5350
  false
@@ -5204,28 +5359,36 @@ module SyntaxTree
5204
5359
  module Ternaryable
5205
5360
  class << self
5206
5361
  def call(q, node)
5207
- case q.parents.take(2)[1]
5208
- in Paren[contents: Statements[body: [node]]]
5209
- # If this is a conditional inside of a parentheses as the only
5210
- # content, then we don't want to transform it into a ternary.
5211
- # Presumably the user wanted it to be an explicit conditional because
5212
- # there are parentheses around it. So we'll just leave it in place.
5213
- false
5214
- else
5215
- # Otherwise, we're going to check the conditional for certain cases.
5216
- case node
5217
- in predicate: Assign | Command | CommandCall | MAssign | OpAssign
5218
- false
5219
- in predicate: Not[parentheses: false]
5220
- false
5221
- in {
5222
- statements: { body: [truthy] },
5223
- consequent: Else[statements: { body: [falsy] }] }
5224
- ternaryable?(truthy) && ternaryable?(falsy)
5225
- else
5226
- false
5227
- end
5362
+ return false if ENV["STREE_FAST_FORMAT"]
5363
+
5364
+ # If this is a conditional inside of a parentheses as the only content,
5365
+ # then we don't want to transform it into a ternary. Presumably the user
5366
+ # wanted it to be an explicit conditional because there are parentheses
5367
+ # around it. So we'll just leave it in place.
5368
+ grandparent = q.grandparent
5369
+ if grandparent.is_a?(Paren) && (body = grandparent.contents.body) &&
5370
+ body.length == 1 && body.first == node
5371
+ return false
5372
+ end
5373
+
5374
+ # Otherwise, we'll check the type of predicate. For certain nodes we
5375
+ # want to force it to not be a ternary, like if the predicate is an
5376
+ # assignment because it's hard to read.
5377
+ case node.predicate
5378
+ when Assign, Command, CommandCall, MAssign, OpAssign
5379
+ return false
5380
+ when Not
5381
+ return false unless node.predicate.parentheses?
5228
5382
  end
5383
+
5384
+ # If there's no Else, then this can't be represented as a ternary.
5385
+ return false unless node.consequent.is_a?(Else)
5386
+
5387
+ truthy_body = node.statements.body
5388
+ falsy_body = node.consequent.statements.body
5389
+
5390
+ (truthy_body.length == 1) && ternaryable?(truthy_body.first) &&
5391
+ (falsy_body.length == 1) && ternaryable?(falsy_body.first)
5229
5392
  end
5230
5393
 
5231
5394
  private
@@ -5234,24 +5397,23 @@ module SyntaxTree
5234
5397
  # parentheses around them. In this case we say they cannot be ternaried
5235
5398
  # and default instead to breaking them into multiple lines.
5236
5399
  def ternaryable?(statement)
5237
- # This is a list of nodes that should not be allowed to be a part of a
5238
- # ternary clause.
5239
- no_ternary = [
5240
- Alias, Assign, Break, Command, CommandCall, Heredoc, If, IfMod, IfOp,
5241
- Lambda, MAssign, Next, OpAssign, RescueMod, Return, Return0, Super,
5242
- Undef, Unless, UnlessMod, Until, UntilMod, VarAlias, VoidStmt, While,
5243
- WhileMod, Yield, Yield0, ZSuper
5244
- ]
5245
-
5246
- # Here we're going to check that the only statement inside the
5247
- # statements node is no a part of our denied list of nodes that can be
5248
- # ternaries.
5249
- #
5250
- # If the user is using one of the lower precedence "and" or "or"
5251
- # operators, then we can't use a ternary expression as it would break
5252
- # the flow control.
5253
- !no_ternary.include?(statement.class) &&
5254
- !(statement.is_a?(Binary) && %i[and or].include?(statement.operator))
5400
+ case statement
5401
+ when Alias, Assign, Break, Command, CommandCall, Heredoc, If, IfMod,
5402
+ IfOp, Lambda, MAssign, Next, OpAssign, RescueMod, Return, Return0,
5403
+ Super, Undef, Unless, UnlessMod, Until, UntilMod, VarAlias,
5404
+ VoidStmt, While, WhileMod, Yield, Yield0, ZSuper
5405
+ # This is a list of nodes that should not be allowed to be a part of a
5406
+ # ternary clause.
5407
+ false
5408
+ when Binary
5409
+ # If the user is using one of the lower precedence "and" or "or"
5410
+ # operators, then we can't use a ternary expression as it would break
5411
+ # the flow control.
5412
+ operator = statement.operator
5413
+ operator != :and && operator != :or
5414
+ else
5415
+ true
5416
+ end
5255
5417
  end
5256
5418
  end
5257
5419
  end
@@ -5311,17 +5473,17 @@ module SyntaxTree
5311
5473
 
5312
5474
  unless node.statements.empty?
5313
5475
  q.indent do
5314
- q.breakable(force: force)
5476
+ force ? q.breakable_force : q.breakable_space
5315
5477
  q.format(node.statements)
5316
5478
  end
5317
5479
  end
5318
5480
 
5319
5481
  if node.consequent
5320
- q.breakable(force: force)
5482
+ force ? q.breakable_force : q.breakable_space
5321
5483
  q.format(node.consequent)
5322
5484
  end
5323
5485
 
5324
- q.breakable(force: force)
5486
+ force ? q.breakable_force : q.breakable_space
5325
5487
  q.text("end")
5326
5488
  end
5327
5489
 
@@ -5333,11 +5495,11 @@ module SyntaxTree
5333
5495
  q.nest(keyword.length + 1) { q.format(node.predicate) }
5334
5496
 
5335
5497
  q.indent do
5336
- q.breakable
5498
+ q.breakable_space
5337
5499
  q.format(node.statements)
5338
5500
  end
5339
5501
 
5340
- q.breakable
5502
+ q.breakable_space
5341
5503
  q.group do
5342
5504
  q.format(node.consequent.keyword)
5343
5505
  q.indent do
@@ -5351,7 +5513,7 @@ module SyntaxTree
5351
5513
  end
5352
5514
  end
5353
5515
 
5354
- q.breakable
5516
+ q.breakable_space
5355
5517
  q.text("end")
5356
5518
  end
5357
5519
  .if_flat do
@@ -5371,8 +5533,11 @@ module SyntaxTree
5371
5533
  end
5372
5534
 
5373
5535
  def contains_conditional?
5374
- case node
5375
- in statements: { body: [If | IfMod | IfOp | Unless | UnlessMod] }
5536
+ statements = node.statements.body
5537
+ return false if statements.length != 1
5538
+
5539
+ case statements.first
5540
+ when If, IfMod, IfOp, Unless, UnlessMod
5376
5541
  true
5377
5542
  else
5378
5543
  false
@@ -5507,19 +5672,19 @@ module SyntaxTree
5507
5672
  q.nest("if ".length) { q.format(predicate) }
5508
5673
 
5509
5674
  q.indent do
5510
- q.breakable
5675
+ q.breakable_space
5511
5676
  q.format(truthy)
5512
5677
  end
5513
5678
 
5514
- q.breakable
5679
+ q.breakable_space
5515
5680
  q.text("else")
5516
5681
 
5517
5682
  q.indent do
5518
- q.breakable
5683
+ q.breakable_space
5519
5684
  q.format(falsy)
5520
5685
  end
5521
5686
 
5522
- q.breakable
5687
+ q.breakable_space
5523
5688
  q.text("end")
5524
5689
  end
5525
5690
  end
@@ -5529,11 +5694,11 @@ module SyntaxTree
5529
5694
  q.text(" ?")
5530
5695
 
5531
5696
  q.indent do
5532
- q.breakable
5697
+ q.breakable_space
5533
5698
  q.format(truthy)
5534
5699
  q.text(" :")
5535
5700
 
5536
- q.breakable
5701
+ q.breakable_space
5537
5702
  q.format(falsy)
5538
5703
  end
5539
5704
  end
@@ -5566,10 +5731,10 @@ module SyntaxTree
5566
5731
  q.text("#{keyword} ")
5567
5732
  q.nest(keyword.length + 1) { q.format(node.predicate) }
5568
5733
  q.indent do
5569
- q.breakable
5734
+ q.breakable_space
5570
5735
  q.format(node.statement)
5571
5736
  end
5572
- q.breakable
5737
+ q.breakable_space
5573
5738
  q.text("end")
5574
5739
  end
5575
5740
 
@@ -5720,13 +5885,13 @@ module SyntaxTree
5720
5885
 
5721
5886
  unless statements.empty?
5722
5887
  q.indent do
5723
- q.breakable(force: true)
5888
+ q.breakable_force
5724
5889
  q.format(statements)
5725
5890
  end
5726
5891
  end
5727
5892
 
5728
5893
  if consequent
5729
- q.breakable(force: true)
5894
+ q.breakable_force
5730
5895
  q.format(consequent)
5731
5896
  end
5732
5897
  end
@@ -5830,11 +5995,15 @@ module SyntaxTree
5830
5995
  # [String] the value of the keyword
5831
5996
  attr_reader :value
5832
5997
 
5998
+ # [Symbol] the symbol version of the value
5999
+ attr_reader :name
6000
+
5833
6001
  # [Array[ Comment | EmbDoc ]] the comments attached to this node
5834
6002
  attr_reader :comments
5835
6003
 
5836
6004
  def initialize(value:, location:, comments: [])
5837
6005
  @value = value
6006
+ @name = value.to_sym
5838
6007
  @location = location
5839
6008
  @comments = comments
5840
6009
  end
@@ -6013,7 +6182,8 @@ module SyntaxTree
6013
6182
  end
6014
6183
 
6015
6184
  def format(q)
6016
- q.group(0, "->") do
6185
+ q.text("->")
6186
+ q.group do
6017
6187
  if params.is_a?(Paren)
6018
6188
  q.format(params) unless params.contents.empty?
6019
6189
  elsif params.empty? && params.comments.any?
@@ -6039,10 +6209,10 @@ module SyntaxTree
6039
6209
 
6040
6210
  unless statements.empty?
6041
6211
  q.indent do
6042
- q.breakable
6212
+ q.breakable_space
6043
6213
  q.format(statements)
6044
6214
  end
6045
- q.breakable
6215
+ q.breakable_space
6046
6216
  end
6047
6217
 
6048
6218
  q.text("}")
@@ -6051,12 +6221,12 @@ module SyntaxTree
6051
6221
 
6052
6222
  unless statements.empty?
6053
6223
  q.indent do
6054
- q.breakable
6224
+ q.breakable_space
6055
6225
  q.format(statements)
6056
6226
  end
6057
6227
  end
6058
6228
 
6059
- q.breakable
6229
+ q.breakable_space
6060
6230
  q.text("end")
6061
6231
  end
6062
6232
  end
@@ -6123,7 +6293,7 @@ module SyntaxTree
6123
6293
 
6124
6294
  if locals.any?
6125
6295
  q.text("; ")
6126
- q.seplist(locals, -> { q.text(", ") }) { |local| q.format(local) }
6296
+ q.seplist(locals, BlockVar::SEPARATOR) { |local| q.format(local) }
6127
6297
  end
6128
6298
  end
6129
6299
  end
@@ -6277,7 +6447,7 @@ module SyntaxTree
6277
6447
  q.group { q.format(target) }
6278
6448
  q.text(" =")
6279
6449
  q.indent do
6280
- q.breakable
6450
+ q.breakable_space
6281
6451
  q.format(value)
6282
6452
  end
6283
6453
  end
@@ -6323,8 +6493,8 @@ module SyntaxTree
6323
6493
  # If we're at the top of a call chain, then we're going to do some
6324
6494
  # specialized printing in case we can print it nicely. We _only_ do this
6325
6495
  # at the top of the chain to avoid weird recursion issues.
6326
- if !CallChainFormatter.chained?(q.parent) &&
6327
- CallChainFormatter.chained?(call)
6496
+ if CallChainFormatter.chained?(call) &&
6497
+ !CallChainFormatter.chained?(q.parent)
6328
6498
  q.group do
6329
6499
  q
6330
6500
  .if_break { CallChainFormatter.new(self).format(q) }
@@ -6431,15 +6601,17 @@ module SyntaxTree
6431
6601
  q.format(contents)
6432
6602
  q.text(",") if comma
6433
6603
  else
6434
- q.group(0, "(", ")") do
6604
+ q.text("(")
6605
+ q.group do
6435
6606
  q.indent do
6436
- q.breakable("")
6607
+ q.breakable_empty
6437
6608
  q.format(contents)
6438
6609
  end
6439
6610
 
6440
6611
  q.text(",") if comma
6441
- q.breakable("")
6612
+ q.breakable_empty
6442
6613
  end
6614
+ q.text(")")
6443
6615
  end
6444
6616
  end
6445
6617
  end
@@ -6486,33 +6658,35 @@ module SyntaxTree
6486
6658
  end
6487
6659
 
6488
6660
  def format(q)
6489
- declaration = -> do
6490
- q.group do
6491
- q.text("module ")
6492
- q.format(constant)
6493
- end
6494
- end
6495
-
6496
6661
  if bodystmt.empty?
6497
6662
  q.group do
6498
- declaration.call
6499
- q.breakable(force: true)
6663
+ format_declaration(q)
6664
+ q.breakable_force
6500
6665
  q.text("end")
6501
6666
  end
6502
6667
  else
6503
6668
  q.group do
6504
- declaration.call
6669
+ format_declaration(q)
6505
6670
 
6506
6671
  q.indent do
6507
- q.breakable(force: true)
6672
+ q.breakable_force
6508
6673
  q.format(bodystmt)
6509
6674
  end
6510
6675
 
6511
- q.breakable(force: true)
6676
+ q.breakable_force
6512
6677
  q.text("end")
6513
6678
  end
6514
6679
  end
6515
6680
  end
6681
+
6682
+ private
6683
+
6684
+ def format_declaration(q)
6685
+ q.group do
6686
+ q.text("module ")
6687
+ q.format(constant)
6688
+ end
6689
+ end
6516
6690
  end
6517
6691
 
6518
6692
  # MRHS represents the values that are being assigned on the right-hand side of
@@ -6610,11 +6784,15 @@ module SyntaxTree
6610
6784
  # [String] the operator
6611
6785
  attr_reader :value
6612
6786
 
6787
+ # [Symbol] the symbol version of the value
6788
+ attr_reader :name
6789
+
6613
6790
  # [Array[ Comment | EmbDoc ]] the comments attached to this node
6614
6791
  attr_reader :comments
6615
6792
 
6616
6793
  def initialize(value:, location:, comments: [])
6617
6794
  @value = value
6795
+ @name = value.to_sym
6618
6796
  @location = location
6619
6797
  @comments = comments
6620
6798
  end
@@ -6696,7 +6874,7 @@ module SyntaxTree
6696
6874
  q.format(value)
6697
6875
  else
6698
6876
  q.indent do
6699
- q.breakable
6877
+ q.breakable_space
6700
6878
  q.format(value)
6701
6879
  end
6702
6880
  end
@@ -6767,10 +6945,10 @@ module SyntaxTree
6767
6945
 
6768
6946
  q.text("(")
6769
6947
  q.indent do
6770
- q.breakable("")
6948
+ q.breakable_empty
6771
6949
  yield
6772
6950
  end
6773
- q.breakable("")
6951
+ q.breakable_empty
6774
6952
  q.text(")")
6775
6953
  end
6776
6954
  end
@@ -6962,23 +7140,35 @@ module SyntaxTree
6962
7140
  parts << KeywordRestFormatter.new(keyword_rest) if keyword_rest
6963
7141
  parts << block if block
6964
7142
 
6965
- contents = -> do
6966
- q.seplist(parts) { |part| q.format(part) }
6967
- q.format(rest) if rest.is_a?(ExcessedComma)
7143
+ if parts.empty?
7144
+ q.nest(0) { format_contents(q, parts) }
7145
+ return
6968
7146
  end
6969
7147
 
6970
- if ![Def, Defs, DefEndless].include?(q.parent.class) || parts.empty?
6971
- q.nest(0, &contents)
6972
- else
6973
- q.group(0, "(", ")") do
6974
- q.indent do
6975
- q.breakable("")
6976
- contents.call
7148
+ case q.parent
7149
+ when Def, Defs, DefEndless
7150
+ q.nest(0) do
7151
+ q.text("(")
7152
+ q.group do
7153
+ q.indent do
7154
+ q.breakable_empty
7155
+ format_contents(q, parts)
7156
+ end
7157
+ q.breakable_empty
6977
7158
  end
6978
- q.breakable("")
7159
+ q.text(")")
6979
7160
  end
7161
+ else
7162
+ q.nest(0) { format_contents(q, parts) }
6980
7163
  end
6981
7164
  end
7165
+
7166
+ private
7167
+
7168
+ def format_contents(q, parts)
7169
+ q.seplist(parts) { |part| q.format(part) }
7170
+ q.format(rest) if rest.is_a?(ExcessedComma)
7171
+ end
6982
7172
  end
6983
7173
 
6984
7174
  # Paren represents using balanced parentheses in a couple places in a Ruby
@@ -7029,12 +7219,12 @@ module SyntaxTree
7029
7219
 
7030
7220
  if contents && (!contents.is_a?(Params) || !contents.empty?)
7031
7221
  q.indent do
7032
- q.breakable("")
7222
+ q.breakable_empty
7033
7223
  q.format(contents)
7034
7224
  end
7035
7225
  end
7036
7226
 
7037
- q.breakable("")
7227
+ q.breakable_empty
7038
7228
  q.text(")")
7039
7229
  end
7040
7230
  end
@@ -7108,7 +7298,7 @@ module SyntaxTree
7108
7298
  # We're going to put a newline on the end so that it always has one unless
7109
7299
  # it ends with the special __END__ syntax. In that case we want to
7110
7300
  # replicate the text exactly so we will just let it be.
7111
- q.breakable(force: true) unless statements.body.last.is_a?(EndContent)
7301
+ q.breakable_force unless statements.body.last.is_a?(EndContent)
7112
7302
  end
7113
7303
  end
7114
7304
 
@@ -7160,15 +7350,18 @@ module SyntaxTree
7160
7350
  closing = Quotes.matching(opening[2])
7161
7351
  end
7162
7352
 
7163
- q.group(0, opening, closing) do
7353
+ q.text(opening)
7354
+ q.group do
7164
7355
  q.indent do
7165
- q.breakable("")
7166
- q.seplist(elements, -> { q.breakable }) do |element|
7167
- q.format(element)
7168
- end
7356
+ q.breakable_empty
7357
+ q.seplist(
7358
+ elements,
7359
+ ArrayLiteral::BREAKABLE_SPACE_SEPARATOR
7360
+ ) { |element| q.format(element) }
7169
7361
  end
7170
- q.breakable("")
7362
+ q.breakable_empty
7171
7363
  end
7364
+ q.text(closing)
7172
7365
  end
7173
7366
  end
7174
7367
 
@@ -7251,15 +7444,18 @@ module SyntaxTree
7251
7444
  closing = Quotes.matching(opening[2])
7252
7445
  end
7253
7446
 
7254
- q.group(0, opening, closing) do
7447
+ q.text(opening)
7448
+ q.group do
7255
7449
  q.indent do
7256
- q.breakable("")
7257
- q.seplist(elements, -> { q.breakable }) do |element|
7258
- q.format(element)
7259
- end
7450
+ q.breakable_empty
7451
+ q.seplist(
7452
+ elements,
7453
+ ArrayLiteral::BREAKABLE_SPACE_SEPARATOR
7454
+ ) { |element| q.format(element) }
7260
7455
  end
7261
- q.breakable("")
7456
+ q.breakable_empty
7262
7457
  end
7458
+ q.text(closing)
7263
7459
  end
7264
7460
  end
7265
7461
 
@@ -7781,13 +7977,13 @@ module SyntaxTree
7781
7977
 
7782
7978
  unless statements.empty?
7783
7979
  q.indent do
7784
- q.breakable(force: true)
7980
+ q.breakable_force
7785
7981
  q.format(statements)
7786
7982
  end
7787
7983
  end
7788
7984
 
7789
7985
  if consequent
7790
- q.breakable(force: true)
7986
+ q.breakable_force
7791
7987
  q.format(consequent)
7792
7988
  end
7793
7989
  end
@@ -7835,19 +8031,21 @@ module SyntaxTree
7835
8031
  end
7836
8032
 
7837
8033
  def format(q)
7838
- q.group(0, "begin", "end") do
8034
+ q.text("begin")
8035
+ q.group do
7839
8036
  q.indent do
7840
- q.breakable(force: true)
8037
+ q.breakable_force
7841
8038
  q.format(statement)
7842
8039
  end
7843
- q.breakable(force: true)
8040
+ q.breakable_force
7844
8041
  q.text("rescue StandardError")
7845
8042
  q.indent do
7846
- q.breakable(force: true)
8043
+ q.breakable_force
7847
8044
  q.format(value)
7848
8045
  end
7849
- q.breakable(force: true)
8046
+ q.breakable_force
7850
8047
  end
8048
+ q.text("end")
7851
8049
  end
7852
8050
  end
7853
8051
 
@@ -8066,14 +8264,16 @@ module SyntaxTree
8066
8264
  end
8067
8265
 
8068
8266
  def format(q)
8069
- q.group(0, "class << ", "end") do
8267
+ q.text("class << ")
8268
+ q.group do
8070
8269
  q.format(target)
8071
8270
  q.indent do
8072
- q.breakable(force: true)
8271
+ q.breakable_force
8073
8272
  q.format(bodystmt)
8074
8273
  end
8075
- q.breakable(force: true)
8274
+ q.breakable_force
8076
8275
  end
8276
+ q.text("end")
8077
8277
  end
8078
8278
  end
8079
8279
 
@@ -8179,30 +8379,26 @@ module SyntaxTree
8179
8379
  end
8180
8380
  end
8181
8381
 
8182
- access_controls =
8183
- Hash.new do |hash, node|
8184
- hash[node] = node.is_a?(VCall) &&
8185
- %w[private protected public].include?(node.value.value)
8186
- end
8187
-
8188
- body.each_with_index do |statement, index|
8382
+ previous = nil
8383
+ body.each do |statement|
8189
8384
  next if statement.is_a?(VoidStmt)
8190
8385
 
8191
8386
  if line.nil?
8192
8387
  q.format(statement)
8193
8388
  elsif (statement.location.start_line - line) > 1
8194
- q.breakable(force: true)
8195
- q.breakable(force: true)
8389
+ q.breakable_force
8390
+ q.breakable_force
8196
8391
  q.format(statement)
8197
- elsif access_controls[statement] || access_controls[body[index - 1]]
8198
- q.breakable(force: true)
8199
- q.breakable(force: true)
8392
+ elsif (statement.is_a?(VCall) && statement.access_control?) ||
8393
+ (previous.is_a?(VCall) && previous.access_control?)
8394
+ q.breakable_force
8395
+ q.breakable_force
8200
8396
  q.format(statement)
8201
8397
  elsif statement.location.start_line != line
8202
- q.breakable(force: true)
8398
+ q.breakable_force
8203
8399
  q.format(statement)
8204
8400
  elsif !q.parent.is_a?(StringEmbExpr)
8205
- q.breakable(force: true)
8401
+ q.breakable_force
8206
8402
  q.format(statement)
8207
8403
  else
8208
8404
  q.text("; ")
@@ -8210,6 +8406,7 @@ module SyntaxTree
8210
8406
  end
8211
8407
 
8212
8408
  line = statement.location.end_line
8409
+ previous = statement
8213
8410
  end
8214
8411
  end
8215
8412
 
@@ -8327,7 +8524,7 @@ module SyntaxTree
8327
8524
  q.format(left)
8328
8525
  q.text(" \\")
8329
8526
  q.indent do
8330
- q.breakable(force: true)
8527
+ q.breakable_force
8331
8528
  q.format(right)
8332
8529
  end
8333
8530
  end
@@ -8413,15 +8610,21 @@ module SyntaxTree
8413
8610
  # same line in the source, then we're going to leave them in place and
8414
8611
  # assume that's the way the developer wanted this expression
8415
8612
  # represented.
8416
- q.remove_breaks(q.group(0, '#{', "}") { q.format(statements) })
8613
+ q.remove_breaks(
8614
+ q.group do
8615
+ q.text('#{')
8616
+ q.format(statements)
8617
+ q.text("}")
8618
+ end
8619
+ )
8417
8620
  else
8418
8621
  q.group do
8419
8622
  q.text('#{')
8420
8623
  q.indent do
8421
- q.breakable("")
8624
+ q.breakable_empty
8422
8625
  q.format(statements)
8423
8626
  end
8424
- q.breakable("")
8627
+ q.breakable_empty
8425
8628
  q.text("}")
8426
8629
  end
8427
8630
  end
@@ -8479,19 +8682,30 @@ module SyntaxTree
8479
8682
  [quote, quote]
8480
8683
  end
8481
8684
 
8482
- q.group(0, opening_quote, closing_quote) do
8685
+ q.text(opening_quote)
8686
+ q.group do
8483
8687
  parts.each do |part|
8484
8688
  if part.is_a?(TStringContent)
8485
8689
  value = Quotes.normalize(part.value, closing_quote)
8486
- separator = -> { q.breakable(force: true, indent: false) }
8487
- q.seplist(value.split(/\r?\n/, -1), separator) do |text|
8488
- q.text(text)
8690
+ first = true
8691
+
8692
+ value.each_line(chomp: true) do |line|
8693
+ if first
8694
+ first = false
8695
+ else
8696
+ q.breakable_return
8697
+ end
8698
+
8699
+ q.text(line)
8489
8700
  end
8701
+
8702
+ q.breakable_return if value.end_with?("\n")
8490
8703
  else
8491
8704
  q.format(part)
8492
8705
  end
8493
8706
  end
8494
8707
  end
8708
+ q.text(closing_quote)
8495
8709
  end
8496
8710
  end
8497
8711
 
@@ -8698,15 +8912,18 @@ module SyntaxTree
8698
8912
  closing = Quotes.matching(opening[2])
8699
8913
  end
8700
8914
 
8701
- q.group(0, opening, closing) do
8915
+ q.text(opening)
8916
+ q.group do
8702
8917
  q.indent do
8703
- q.breakable("")
8704
- q.seplist(elements, -> { q.breakable }) do |element|
8705
- q.format(element)
8706
- end
8918
+ q.breakable_empty
8919
+ q.seplist(
8920
+ elements,
8921
+ ArrayLiteral::BREAKABLE_SPACE_SEPARATOR
8922
+ ) { |element| q.format(element) }
8707
8923
  end
8708
- q.breakable("")
8924
+ q.breakable_empty
8709
8925
  end
8926
+ q.text(closing)
8710
8927
  end
8711
8928
  end
8712
8929
 
@@ -9000,6 +9217,7 @@ module SyntaxTree
9000
9217
 
9001
9218
  # [boolean] whether or not parentheses were used
9002
9219
  attr_reader :parentheses
9220
+ alias parentheses? parentheses
9003
9221
 
9004
9222
  # [Array[ Comment | EmbDoc ]] the comments attached to this node
9005
9223
  attr_reader :comments
@@ -9031,27 +9249,26 @@ module SyntaxTree
9031
9249
  end
9032
9250
 
9033
9251
  def format(q)
9034
- parent = q.parents.take(2)[1]
9035
- ternary =
9036
- (parent.is_a?(If) || parent.is_a?(Unless)) &&
9037
- Ternaryable.call(q, parent)
9038
-
9039
9252
  q.text("not")
9040
9253
 
9041
9254
  if parentheses
9042
9255
  q.text("(")
9043
- elsif ternary
9044
- q.if_break { q.text(" ") }.if_flat { q.text("(") }
9045
- else
9046
- q.text(" ")
9047
- end
9048
-
9049
- q.format(statement) if statement
9050
-
9051
- if parentheses
9256
+ q.format(statement) if statement
9052
9257
  q.text(")")
9053
- elsif ternary
9054
- q.if_flat { q.text(")") }
9258
+ else
9259
+ grandparent = q.grandparent
9260
+ ternary =
9261
+ (grandparent.is_a?(If) || grandparent.is_a?(Unless)) &&
9262
+ Ternaryable.call(q, grandparent)
9263
+
9264
+ if ternary
9265
+ q.if_break { q.text(" ") }.if_flat { q.text("(") }
9266
+ q.format(statement) if statement
9267
+ q.if_flat { q.text(")") } if ternary
9268
+ else
9269
+ q.text(" ")
9270
+ q.format(statement) if statement
9271
+ end
9055
9272
  end
9056
9273
  end
9057
9274
  end
@@ -9316,10 +9533,10 @@ module SyntaxTree
9316
9533
  q.text("#{keyword} ")
9317
9534
  q.nest(keyword.length + 1) { q.format(node.predicate) }
9318
9535
  q.indent do
9319
- q.breakable("")
9536
+ q.breakable_empty
9320
9537
  q.format(statements)
9321
9538
  end
9322
- q.breakable("")
9539
+ q.breakable_empty
9323
9540
  q.text("end")
9324
9541
  end
9325
9542
  end
@@ -9372,7 +9589,7 @@ module SyntaxTree
9372
9589
  q.group do
9373
9590
  q.text(keyword)
9374
9591
  q.nest(keyword.length) { q.format(predicate) }
9375
- q.breakable(force: true)
9592
+ q.breakable_force
9376
9593
  q.text("end")
9377
9594
  end
9378
9595
  else
@@ -9572,6 +9789,29 @@ module SyntaxTree
9572
9789
  def format(q)
9573
9790
  q.format(value)
9574
9791
  end
9792
+
9793
+ # Oh man I hate this so much. Basically, ripper doesn't provide enough
9794
+ # functionality to actually know where pins are within an expression. So we
9795
+ # have to walk the tree ourselves and insert more information. In doing so,
9796
+ # we have to replace this node by a pinned node when necessary.
9797
+ #
9798
+ # To be clear, this method should just not exist. It's not good. It's a
9799
+ # place of shame. But it's necessary for now, so I'm keeping it.
9800
+ def pin(parent)
9801
+ replace = PinnedVarRef.new(value: value, location: location)
9802
+
9803
+ parent
9804
+ .deconstruct_keys([])
9805
+ .each do |key, value|
9806
+ if value == self
9807
+ parent.instance_variable_set(:"@#{key}", replace)
9808
+ break
9809
+ elsif value.is_a?(Array) && (index = value.index(self))
9810
+ parent.public_send(key)[index] = replace
9811
+ break
9812
+ end
9813
+ end
9814
+ end
9575
9815
  end
9576
9816
 
9577
9817
  # PinnedVarRef represents a pinned variable reference within a pattern
@@ -9653,6 +9893,10 @@ module SyntaxTree
9653
9893
  def format(q)
9654
9894
  q.format(value)
9655
9895
  end
9896
+
9897
+ def access_control?
9898
+ @access_control ||= %w[private protected public].include?(value.value)
9899
+ end
9656
9900
  end
9657
9901
 
9658
9902
  # VoidStmt represents an empty lexical block of code.
@@ -9742,6 +9986,22 @@ module SyntaxTree
9742
9986
  }
9743
9987
  end
9744
9988
 
9989
+ # We have a special separator here for when clauses which causes them to
9990
+ # fill as much of the line as possible as opposed to everything breaking
9991
+ # into its own line as soon as you hit the print limit.
9992
+ class Separator
9993
+ def call(q)
9994
+ q.group do
9995
+ q.text(",")
9996
+ q.breakable_space
9997
+ end
9998
+ end
9999
+ end
10000
+
10001
+ # We're going to keep a single instance of this separator around so we don't
10002
+ # have to allocate a new one every time we format a when clause.
10003
+ SEPARATOR = Separator.new
10004
+
9745
10005
  def format(q)
9746
10006
  keyword = "when "
9747
10007
 
@@ -9752,8 +10012,7 @@ module SyntaxTree
9752
10012
  if arguments.comments.any?
9753
10013
  q.format(arguments)
9754
10014
  else
9755
- separator = -> { q.group { q.comma_breakable } }
9756
- q.seplist(arguments.parts, separator) { |part| q.format(part) }
10015
+ q.seplist(arguments.parts, SEPARATOR) { |part| q.format(part) }
9757
10016
  end
9758
10017
 
9759
10018
  # Very special case here. If you're inside of a when clause and the
@@ -9768,13 +10027,13 @@ module SyntaxTree
9768
10027
 
9769
10028
  unless statements.empty?
9770
10029
  q.indent do
9771
- q.breakable(force: true)
10030
+ q.breakable_force
9772
10031
  q.format(statements)
9773
10032
  end
9774
10033
  end
9775
10034
 
9776
10035
  if consequent
9777
- q.breakable(force: true)
10036
+ q.breakable_force
9778
10037
  q.format(consequent)
9779
10038
  end
9780
10039
  end
@@ -9829,7 +10088,7 @@ module SyntaxTree
9829
10088
  q.group do
9830
10089
  q.text(keyword)
9831
10090
  q.nest(keyword.length) { q.format(predicate) }
9832
- q.breakable(force: true)
10091
+ q.breakable_force
9833
10092
  q.text("end")
9834
10093
  end
9835
10094
  else
@@ -9995,15 +10254,18 @@ module SyntaxTree
9995
10254
  closing = Quotes.matching(opening[2])
9996
10255
  end
9997
10256
 
9998
- q.group(0, opening, closing) do
10257
+ q.text(opening)
10258
+ q.group do
9999
10259
  q.indent do
10000
- q.breakable("")
10001
- q.seplist(elements, -> { q.breakable }) do |element|
10002
- q.format(element)
10003
- end
10260
+ q.breakable_empty
10261
+ q.seplist(
10262
+ elements,
10263
+ ArrayLiteral::BREAKABLE_SPACE_SEPARATOR
10264
+ ) { |element| q.format(element) }
10004
10265
  end
10005
- q.breakable("")
10266
+ q.breakable_empty
10006
10267
  end
10268
+ q.text(closing)
10007
10269
  end
10008
10270
  end
10009
10271
 
@@ -10147,10 +10409,10 @@ module SyntaxTree
10147
10409
  else
10148
10410
  q.if_break { q.text("(") }.if_flat { q.text(" ") }
10149
10411
  q.indent do
10150
- q.breakable("")
10412
+ q.breakable_empty
10151
10413
  q.format(arguments)
10152
10414
  end
10153
- q.breakable("")
10415
+ q.breakable_empty
10154
10416
  q.if_break { q.text(")") }
10155
10417
  end
10156
10418
  end