syntax_tree 3.6.3 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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