syntax_tree 3.6.3 → 4.0.0

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
@@ -1661,15 +1699,13 @@ module SyntaxTree
1661
1699
  q.text(" ") unless power
1662
1700
 
1663
1701
  if operator == :<<
1664
- q.text(operator.to_s)
1665
- q.text(" ")
1702
+ q.text("<< ")
1666
1703
  q.format(right)
1667
1704
  else
1668
1705
  q.group do
1669
- q.text(operator.to_s)
1670
-
1706
+ q.text(operator.name)
1671
1707
  q.indent do
1672
- q.breakable(power ? "" : " ")
1708
+ power ? q.breakable_empty : q.breakable_space
1673
1709
  q.format(right)
1674
1710
  end
1675
1711
  end
@@ -1716,15 +1752,29 @@ module SyntaxTree
1716
1752
  { params: params, locals: locals, location: location, comments: comments }
1717
1753
  end
1718
1754
 
1755
+ # Within the pipes of the block declaration, we don't want any spaces. So
1756
+ # we'll separate the parameters with a comma and space but no breakables.
1757
+ class Separator
1758
+ def call(q)
1759
+ q.text(", ")
1760
+ end
1761
+ end
1762
+
1763
+ # We'll keep a single instance of this separator around for all block vars
1764
+ # to cut down on allocations.
1765
+ SEPARATOR = Separator.new
1766
+
1719
1767
  def format(q)
1720
- q.group(0, "|", "|") do
1768
+ q.text("|")
1769
+ q.group do
1721
1770
  q.remove_breaks(q.format(params))
1722
1771
 
1723
1772
  if locals.any?
1724
1773
  q.text("; ")
1725
- q.seplist(locals, -> { q.text(", ") }) { |local| q.format(local) }
1774
+ q.seplist(locals, SEPARATOR) { |local| q.format(local) }
1726
1775
  end
1727
1776
  end
1777
+ q.text("|")
1728
1778
  end
1729
1779
  end
1730
1780
 
@@ -1816,10 +1866,8 @@ module SyntaxTree
1816
1866
  end_column: end_column
1817
1867
  )
1818
1868
 
1819
- parts = [rescue_clause, else_clause, ensure_clause]
1820
-
1821
1869
  # Here we're going to determine the bounds for the statements
1822
- consequent = parts.compact.first
1870
+ consequent = rescue_clause || else_clause || ensure_clause
1823
1871
  statements.bind(
1824
1872
  start_char,
1825
1873
  start_column,
@@ -1829,7 +1877,7 @@ module SyntaxTree
1829
1877
 
1830
1878
  # Next we're going to determine the rescue clause if there is one
1831
1879
  if rescue_clause
1832
- consequent = parts.drop(1).compact.first
1880
+ consequent = else_clause || ensure_clause
1833
1881
  rescue_clause.bind_end(
1834
1882
  consequent ? consequent.location.start_char : end_char,
1835
1883
  consequent ? consequent.location.start_column : end_column
@@ -1868,26 +1916,26 @@ module SyntaxTree
1868
1916
 
1869
1917
  if rescue_clause
1870
1918
  q.nest(-2) do
1871
- q.breakable(force: true)
1919
+ q.breakable_force
1872
1920
  q.format(rescue_clause)
1873
1921
  end
1874
1922
  end
1875
1923
 
1876
1924
  if else_clause
1877
1925
  q.nest(-2) do
1878
- q.breakable(force: true)
1926
+ q.breakable_force
1879
1927
  q.format(else_keyword)
1880
1928
  end
1881
1929
 
1882
1930
  unless else_clause.empty?
1883
- q.breakable(force: true)
1931
+ q.breakable_force
1884
1932
  q.format(else_clause)
1885
1933
  end
1886
1934
  end
1887
1935
 
1888
1936
  if ensure_clause
1889
1937
  q.nest(-2) do
1890
- q.breakable(force: true)
1938
+ q.breakable_force
1891
1939
  q.format(ensure_clause)
1892
1940
  end
1893
1941
  end
@@ -1955,12 +2003,11 @@ module SyntaxTree
1955
2003
  # If the receiver of this block a Command or CommandCall node, then there
1956
2004
  # are no parentheses around the arguments to that command, so we need to
1957
2005
  # break the block.
1958
- case q.parent
1959
- in { call: Command | CommandCall }
2006
+ case q.parent.call
2007
+ when Command, CommandCall
1960
2008
  q.break_parent
1961
2009
  format_break(q, break_opening, break_closing)
1962
2010
  return
1963
- else
1964
2011
  end
1965
2012
 
1966
2013
  q.group do
@@ -1980,9 +2027,9 @@ module SyntaxTree
1980
2027
  # know for certain we're going to get split over multiple lines
1981
2028
  # anyway.
1982
2029
  case parent
1983
- in Statements | ArgParen
2030
+ when Statements, ArgParen
1984
2031
  break false
1985
- in Command | CommandCall
2032
+ when Command, CommandCall
1986
2033
  true
1987
2034
  else
1988
2035
  false
@@ -1993,8 +2040,8 @@ module SyntaxTree
1993
2040
  # If we're a sibling of a control-flow keyword, then we're going to have to
1994
2041
  # use the do..end bounds.
1995
2042
  def forced_do_end_bounds?(q)
1996
- case q.parent
1997
- in { call: Break | Next | Return | Super }
2043
+ case q.parent.call
2044
+ when Break, Next, Return, Super
1998
2045
  true
1999
2046
  else
2000
2047
  false
@@ -2004,22 +2051,19 @@ module SyntaxTree
2004
2051
  # If we're the predicate of a loop or conditional, then we're going to have
2005
2052
  # to go with the {..} bounds.
2006
2053
  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)
2054
+ previous = nil
2055
+ q.parents.any? do |parent|
2056
+ case parent
2057
+ when Paren, Statements
2058
+ # If we hit certain breakpoints then we know we're safe.
2059
+ return false
2060
+ when If, IfMod, IfOp, Unless, UnlessMod, While, WhileMod, Until,
2061
+ UntilMod
2062
+ return true if parent.predicate == previous
2063
+ end
2011
2064
 
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]
2065
+ previous = parent
2066
+ false
2023
2067
  end
2024
2068
  end
2025
2069
 
@@ -2034,12 +2078,12 @@ module SyntaxTree
2034
2078
 
2035
2079
  unless statements.empty?
2036
2080
  q.indent do
2037
- q.breakable
2081
+ q.breakable_space
2038
2082
  q.format(statements)
2039
2083
  end
2040
2084
  end
2041
2085
 
2042
- q.breakable
2086
+ q.breakable_space
2043
2087
  q.text(closing)
2044
2088
  end
2045
2089
 
@@ -2048,17 +2092,17 @@ module SyntaxTree
2048
2092
  q.format(BlockOpenFormatter.new(opening, block_open), stackable: false)
2049
2093
 
2050
2094
  if node.block_var
2051
- q.breakable
2095
+ q.breakable_space
2052
2096
  q.format(node.block_var)
2053
- q.breakable
2097
+ q.breakable_space
2054
2098
  end
2055
2099
 
2056
2100
  if statements.empty?
2057
2101
  q.text(" ") if opening == "do"
2058
2102
  else
2059
- q.breakable unless node.block_var
2103
+ q.breakable_space unless node.block_var
2060
2104
  q.format(statements)
2061
- q.breakable
2105
+ q.breakable_space
2062
2106
  end
2063
2107
 
2064
2108
  q.text(closing)
@@ -2133,105 +2177,128 @@ module SyntaxTree
2133
2177
  q.group do
2134
2178
  q.text(keyword)
2135
2179
 
2136
- case node.arguments.parts
2137
- in []
2180
+ parts = node.arguments.parts
2181
+ length = parts.length
2182
+
2183
+ if length == 0
2138
2184
  # Here there are no arguments at all, so we're not going to print
2139
2185
  # anything. This would be like if we had:
2140
2186
  #
2141
2187
  # break
2142
2188
  #
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
2189
+ elsif length >= 2
2231
2190
  # If there are multiple arguments, format them all. If the line is
2232
2191
  # going to break into multiple, then use brackets to start and end the
2233
2192
  # expression.
2234
2193
  format_arguments(q, " [", "]")
2194
+ else
2195
+ # If we get here, then we're formatting a single argument to the flow
2196
+ # control keyword.
2197
+ part = parts.first
2198
+
2199
+ case part
2200
+ when Paren
2201
+ statements = part.contents.body
2202
+
2203
+ if statements.length == 1
2204
+ statement = statements.first
2205
+
2206
+ if statement.is_a?(ArrayLiteral)
2207
+ contents = statement.contents
2208
+
2209
+ if contents && contents.parts.length >= 2
2210
+ # Here we have a single argument that is a set of parentheses
2211
+ # wrapping an array literal that has at least 2 elements.
2212
+ # We're going to print the contents of the array directly.
2213
+ # This would be like if we had:
2214
+ #
2215
+ # break([1, 2, 3])
2216
+ #
2217
+ # which we will print as:
2218
+ #
2219
+ # break 1, 2, 3
2220
+ #
2221
+ q.text(" ")
2222
+ format_array_contents(q, statement)
2223
+ else
2224
+ # Here we have a single argument that is a set of parentheses
2225
+ # wrapping an array literal that has 0 or 1 elements. We're
2226
+ # going to skip the parentheses but print the array itself.
2227
+ # This would be like if we had:
2228
+ #
2229
+ # break([1])
2230
+ #
2231
+ # which we will print as:
2232
+ #
2233
+ # break [1]
2234
+ #
2235
+ q.text(" ")
2236
+ q.format(statement)
2237
+ end
2238
+ elsif skip_parens?(statement)
2239
+ # Here we have a single argument that is a set of parentheses
2240
+ # that themselves contain a single statement. That statement is
2241
+ # a simple value that we can skip the parentheses for. This
2242
+ # would be like if we had:
2243
+ #
2244
+ # break(1)
2245
+ #
2246
+ # which we will print as:
2247
+ #
2248
+ # break 1
2249
+ #
2250
+ q.text(" ")
2251
+ q.format(statement)
2252
+ else
2253
+ # Here we have a single argument that is a set of parentheses.
2254
+ # We're going to print the parentheses themselves as if they
2255
+ # were the set of arguments. This would be like if we had:
2256
+ #
2257
+ # break(foo.bar)
2258
+ #
2259
+ q.format(part)
2260
+ end
2261
+ else
2262
+ q.format(part)
2263
+ end
2264
+ when ArrayLiteral
2265
+ contents = part.contents
2266
+
2267
+ if contents && contents.parts.length >= 2
2268
+ # Here there is a single argument that is an array literal with at
2269
+ # least two elements. We skip directly into the array literal's
2270
+ # elements in order to print the contents. This would be like if
2271
+ # we had:
2272
+ #
2273
+ # break [1, 2, 3]
2274
+ #
2275
+ # which we will print as:
2276
+ #
2277
+ # break 1, 2, 3
2278
+ #
2279
+ q.text(" ")
2280
+ format_array_contents(q, part)
2281
+ else
2282
+ # Here there is a single argument that is an array literal with 0
2283
+ # or 1 elements. In this case we're going to print the array as it
2284
+ # is because skipping the brackets would change the remaining.
2285
+ # This would be like if we had:
2286
+ #
2287
+ # break []
2288
+ # break [1]
2289
+ #
2290
+ q.text(" ")
2291
+ q.format(part)
2292
+ end
2293
+ else
2294
+ # Here there is a single argument that hasn't matched one of our
2295
+ # previous cases. We're going to print the argument as it is. This
2296
+ # would be like if we had:
2297
+ #
2298
+ # break foo
2299
+ #
2300
+ format_arguments(q, "(", ")")
2301
+ end
2235
2302
  end
2236
2303
  end
2237
2304
  end
@@ -2241,29 +2308,34 @@ module SyntaxTree
2241
2308
  def format_array_contents(q, array)
2242
2309
  q.if_break { q.text("[") }
2243
2310
  q.indent do
2244
- q.breakable("")
2311
+ q.breakable_empty
2245
2312
  q.format(array.contents)
2246
2313
  end
2247
- q.breakable("")
2314
+ q.breakable_empty
2248
2315
  q.if_break { q.text("]") }
2249
2316
  end
2250
2317
 
2251
2318
  def format_arguments(q, opening, closing)
2252
2319
  q.if_break { q.text(opening) }
2253
2320
  q.indent do
2254
- q.breakable(" ")
2321
+ q.breakable_space
2255
2322
  q.format(node.arguments)
2256
2323
  end
2257
- q.breakable("")
2324
+ q.breakable_empty
2258
2325
  q.if_break { q.text(closing) }
2259
2326
  end
2260
2327
 
2261
2328
  def skip_parens?(node)
2262
2329
  case node
2263
- in FloatLiteral | Imaginary | Int | RationalLiteral
2264
- true
2265
- in VarRef[value: Const | CVar | GVar | IVar | Kw]
2330
+ when FloatLiteral, Imaginary, Int, RationalLiteral
2266
2331
  true
2332
+ when VarRef
2333
+ case node.value
2334
+ when Const, CVar, GVar, IVar, Kw
2335
+ true
2336
+ else
2337
+ false
2338
+ end
2267
2339
  else
2268
2340
  false
2269
2341
  end
@@ -2326,8 +2398,10 @@ module SyntaxTree
2326
2398
 
2327
2399
  def format(q)
2328
2400
  case operator
2329
- in :"::" | Op[value: "::"]
2401
+ when :"::"
2330
2402
  q.text(".")
2403
+ when Op
2404
+ operator.value == "::" ? q.text(".") : operator.format(q)
2331
2405
  else
2332
2406
  operator.format(q)
2333
2407
  end
@@ -2363,13 +2437,18 @@ module SyntaxTree
2363
2437
  # First, walk down the chain until we get to the point where we're not
2364
2438
  # longer at a chainable node.
2365
2439
  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
2440
+ case (child = children.last)
2441
+ when Call
2442
+ case (receiver = child.receiver)
2443
+ when Call
2444
+ children << receiver
2445
+ when MethodAddBlock
2446
+ receiver.call.is_a?(Call) ? children << receiver : break
2447
+ else
2448
+ break
2449
+ end
2450
+ when MethodAddBlock
2451
+ child.call.is_a?(Call) ? children << child.call : break
2373
2452
  else
2374
2453
  break
2375
2454
  end
@@ -2388,10 +2467,9 @@ module SyntaxTree
2388
2467
  # nodes.
2389
2468
  parent = parents[3] if parent.is_a?(DoBlock)
2390
2469
 
2391
- case parent
2392
- in MethodAddBlock[call: FCall[value: { value: "sig" }]]
2470
+ if parent.is_a?(MethodAddBlock) && parent.call.is_a?(FCall) &&
2471
+ parent.call.value.value == "sig"
2393
2472
  threshold = 2
2394
- else
2395
2473
  end
2396
2474
  end
2397
2475
 
@@ -2434,20 +2512,21 @@ module SyntaxTree
2434
2512
  skip_operator = false
2435
2513
 
2436
2514
  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
2515
+ if child.is_a?(Call)
2516
+ if child.receiver.is_a?(Call) &&
2517
+ (child.receiver.message != :call) &&
2518
+ (child.receiver.message.value == "where") &&
2519
+ (child.message.value == "not")
2520
+ # This is very specialized behavior wherein we group
2521
+ # .where.not calls together because it looks better. For more
2522
+ # information, see
2523
+ # https://github.com/prettier/plugin-ruby/issues/862.
2524
+ else
2525
+ # If we're at a Call node and not a MethodAddBlock node in the
2526
+ # chain then we're going to add a newline so it indents
2527
+ # properly.
2528
+ q.breakable_empty
2529
+ end
2451
2530
  end
2452
2531
 
2453
2532
  format_child(
@@ -2460,9 +2539,9 @@ module SyntaxTree
2460
2539
 
2461
2540
  # If the parent call node has a comment on the message then we need
2462
2541
  # 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))
2542
+ last_child = children.last
2543
+ if last_child.is_a?(Call) && last_child.message.comments.any?
2544
+ q.format(CallOperatorFormatter.new(last_child.operator))
2466
2545
  skip_operator = true
2467
2546
  else
2468
2547
  skip_operator = false
@@ -2477,18 +2556,22 @@ module SyntaxTree
2477
2556
 
2478
2557
  if empty_except_last
2479
2558
  case node
2480
- in Call
2559
+ when Call
2481
2560
  node.format_arguments(q)
2482
- in MethodAddBlock[block:]
2483
- q.format(block)
2561
+ when MethodAddBlock
2562
+ q.format(node.block)
2484
2563
  end
2485
2564
  end
2486
2565
  end
2487
2566
 
2488
2567
  def self.chained?(node)
2568
+ return false if ENV["STREE_FAST_FORMAT"]
2569
+
2489
2570
  case node
2490
- in Call | MethodAddBlock[call: Call]
2571
+ when Call
2491
2572
  true
2573
+ when MethodAddBlock
2574
+ node.call.is_a?(Call)
2492
2575
  else
2493
2576
  false
2494
2577
  end
@@ -2500,9 +2583,12 @@ module SyntaxTree
2500
2583
  # want to indent the first call. So we'll pop off the first children and
2501
2584
  # format it separately here.
2502
2585
  def attach_directly?(node)
2503
- [ArrayLiteral, HashLiteral, Heredoc, If, Unless, XStringLiteral].include?(
2504
- node.receiver.class
2505
- )
2586
+ case node.receiver
2587
+ when ArrayLiteral, HashLiteral, Heredoc, If, Unless, XStringLiteral
2588
+ true
2589
+ else
2590
+ false
2591
+ end
2506
2592
  end
2507
2593
 
2508
2594
  def format_child(
@@ -2514,7 +2600,7 @@ module SyntaxTree
2514
2600
  )
2515
2601
  # First, format the actual contents of the child.
2516
2602
  case child
2517
- in Call
2603
+ when Call
2518
2604
  q.group do
2519
2605
  unless skip_operator
2520
2606
  q.format(CallOperatorFormatter.new(child.operator))
@@ -2522,7 +2608,7 @@ module SyntaxTree
2522
2608
  q.format(child.message) if child.message != :call
2523
2609
  child.format_arguments(q) unless skip_attached
2524
2610
  end
2525
- in MethodAddBlock
2611
+ when MethodAddBlock
2526
2612
  q.format(child.block) unless skip_attached
2527
2613
  end
2528
2614
 
@@ -2530,7 +2616,7 @@ module SyntaxTree
2530
2616
  # them out here since we're bypassing the normal comment printing.
2531
2617
  if child.comments.any? && !skip_comments
2532
2618
  child.comments.each do |comment|
2533
- comment.inline? ? q.text(" ") : q.breakable
2619
+ comment.inline? ? q.text(" ") : q.breakable_space
2534
2620
  comment.format(q)
2535
2621
  end
2536
2622
 
@@ -2605,8 +2691,8 @@ module SyntaxTree
2605
2691
  # If we're at the top of a call chain, then we're going to do some
2606
2692
  # specialized printing in case we can print it nicely. We _only_ do this
2607
2693
  # at the top of the chain to avoid weird recursion issues.
2608
- if !CallChainFormatter.chained?(q.parent) &&
2609
- CallChainFormatter.chained?(receiver)
2694
+ if CallChainFormatter.chained?(receiver) &&
2695
+ !CallChainFormatter.chained?(q.parent)
2610
2696
  q.group do
2611
2697
  q
2612
2698
  .if_break { CallChainFormatter.new(self).format(q) }
@@ -2617,15 +2703,15 @@ module SyntaxTree
2617
2703
  end
2618
2704
  end
2619
2705
 
2706
+ # Print out the arguments to this call. If there are no arguments, then do
2707
+ #nothing.
2620
2708
  def format_arguments(q)
2621
2709
  case arguments
2622
- in ArgParen
2710
+ when ArgParen
2623
2711
  q.format(arguments)
2624
- in Args
2712
+ when Args
2625
2713
  q.text(" ")
2626
2714
  q.format(arguments)
2627
- else
2628
- # Do nothing if there are no arguments.
2629
2715
  end
2630
2716
  end
2631
2717
 
@@ -2642,7 +2728,7 @@ module SyntaxTree
2642
2728
  q.group do
2643
2729
  q.indent do
2644
2730
  if receiver.comments.any? || call_operator.comments.any?
2645
- q.breakable(force: true)
2731
+ q.breakable_force
2646
2732
  end
2647
2733
 
2648
2734
  if call_operator.comments.empty?
@@ -2719,9 +2805,9 @@ module SyntaxTree
2719
2805
  q.format(value)
2720
2806
  end
2721
2807
 
2722
- q.breakable(force: true)
2808
+ q.breakable_force
2723
2809
  q.format(consequent)
2724
- q.breakable(force: true)
2810
+ q.breakable_force
2725
2811
 
2726
2812
  q.text("end")
2727
2813
  end
@@ -2782,13 +2868,13 @@ module SyntaxTree
2782
2868
  q.format(operator)
2783
2869
 
2784
2870
  case pattern
2785
- in AryPtn | FndPtn | HshPtn
2871
+ when AryPtn, FndPtn, HshPtn
2786
2872
  q.text(" ")
2787
2873
  q.format(pattern)
2788
2874
  else
2789
2875
  q.group do
2790
2876
  q.indent do
2791
- q.breakable
2877
+ q.breakable_space
2792
2878
  q.format(pattern)
2793
2879
  end
2794
2880
  end
@@ -2872,38 +2958,40 @@ module SyntaxTree
2872
2958
  end
2873
2959
 
2874
2960
  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
2961
  if bodystmt.empty?
2888
2962
  q.group do
2889
- declaration.call
2890
- q.breakable(force: true)
2963
+ format_declaration(q)
2964
+ q.breakable_force
2891
2965
  q.text("end")
2892
2966
  end
2893
2967
  else
2894
2968
  q.group do
2895
- declaration.call
2969
+ format_declaration(q)
2896
2970
 
2897
2971
  q.indent do
2898
- q.breakable(force: true)
2972
+ q.breakable_force
2899
2973
  q.format(bodystmt)
2900
2974
  end
2901
2975
 
2902
- q.breakable(force: true)
2976
+ q.breakable_force
2903
2977
  q.text("end")
2904
2978
  end
2905
2979
  end
2906
2980
  end
2981
+
2982
+ private
2983
+
2984
+ def format_declaration(q)
2985
+ q.group do
2986
+ q.text("class ")
2987
+ q.format(constant)
2988
+
2989
+ if superclass
2990
+ q.text(" < ")
2991
+ q.format(superclass)
2992
+ end
2993
+ end
2994
+ end
2907
2995
  end
2908
2996
 
2909
2997
  # Comma represents the use of the , operator.
@@ -2983,15 +3071,31 @@ module SyntaxTree
2983
3071
  private
2984
3072
 
2985
3073
  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)
3074
+ arguments = node.arguments
3075
+
3076
+ if arguments.is_a?(Args)
3077
+ parts = arguments.parts
3078
+
3079
+ if parts.size == 1
3080
+ part = parts.first
3081
+
3082
+ case part
3083
+ when Def, Defs, DefEndless
3084
+ q.text(" ")
3085
+ yield
3086
+ when IfOp
3087
+ q.if_flat { q.text(" ") }
3088
+ yield
3089
+ when Command
3090
+ align(q, part, &block)
3091
+ else
3092
+ q.text(" ")
3093
+ q.nest(message.value.length + 1) { yield }
3094
+ end
3095
+ else
3096
+ q.text(" ")
3097
+ q.nest(message.value.length + 1) { yield }
3098
+ end
2995
3099
  else
2996
3100
  q.text(" ")
2997
3101
  q.nest(message.value.length + 1) { yield }
@@ -3069,7 +3173,7 @@ module SyntaxTree
3069
3173
  if message.comments.any?(&:leading?)
3070
3174
  q.format(CallOperatorFormatter.new(operator), stackable: false)
3071
3175
  q.indent do
3072
- q.breakable("")
3176
+ q.breakable_empty
3073
3177
  q.format(message)
3074
3178
  end
3075
3179
  else
@@ -3078,15 +3182,18 @@ module SyntaxTree
3078
3182
  end
3079
3183
  end
3080
3184
 
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.
3185
+ # Format the arguments for this command call here. If there are no
3186
+ # arguments, then print nothing.
3187
+ if arguments
3188
+ parts = arguments.parts
3189
+
3190
+ if parts.length == 1 && parts.first.is_a?(IfOp)
3191
+ q.if_flat { q.text(" ") }
3192
+ q.format(arguments)
3193
+ else
3194
+ q.text(" ")
3195
+ q.nest(argument_alignment(q, doc)) { q.format(arguments) }
3196
+ end
3090
3197
  end
3091
3198
  end
3092
3199
  end
@@ -3155,7 +3262,7 @@ module SyntaxTree
3155
3262
  end
3156
3263
 
3157
3264
  def ignore?
3158
- value[1..].strip == "stree-ignore"
3265
+ value.match?(/\A#\s*stree-ignore\s*\z/)
3159
3266
  end
3160
3267
 
3161
3268
  def comments
@@ -3455,12 +3562,12 @@ module SyntaxTree
3455
3562
 
3456
3563
  unless bodystmt.empty?
3457
3564
  q.indent do
3458
- q.breakable(force: true)
3565
+ q.breakable_force
3459
3566
  q.format(bodystmt)
3460
3567
  end
3461
3568
  end
3462
3569
 
3463
- q.breakable(force: true)
3570
+ q.breakable_force
3464
3571
  q.text("end")
3465
3572
  end
3466
3573
  end
@@ -3549,7 +3656,7 @@ module SyntaxTree
3549
3656
  q.text(" =")
3550
3657
  q.group do
3551
3658
  q.indent do
3552
- q.breakable
3659
+ q.breakable_space
3553
3660
  q.format(statement)
3554
3661
  end
3555
3662
  end
@@ -3590,13 +3697,15 @@ module SyntaxTree
3590
3697
  end
3591
3698
 
3592
3699
  def format(q)
3593
- q.group(0, "defined?(", ")") do
3700
+ q.text("defined?(")
3701
+ q.group do
3594
3702
  q.indent do
3595
- q.breakable("")
3703
+ q.breakable_empty
3596
3704
  q.format(value)
3597
3705
  end
3598
- q.breakable("")
3706
+ q.breakable_empty
3599
3707
  end
3708
+ q.text(")")
3600
3709
  end
3601
3710
  end
3602
3711
 
@@ -3678,12 +3787,12 @@ module SyntaxTree
3678
3787
 
3679
3788
  unless bodystmt.empty?
3680
3789
  q.indent do
3681
- q.breakable(force: true)
3790
+ q.breakable_force
3682
3791
  q.format(bodystmt)
3683
3792
  end
3684
3793
  end
3685
3794
 
3686
- q.breakable(force: true)
3795
+ q.breakable_force
3687
3796
  q.text("end")
3688
3797
  end
3689
3798
  end
@@ -3755,15 +3864,18 @@ module SyntaxTree
3755
3864
  end
3756
3865
 
3757
3866
  def format(q)
3758
- space = [If, IfMod, Unless, UnlessMod].include?(q.parent.class)
3759
-
3760
3867
  left = node.left
3761
3868
  right = node.right
3762
3869
 
3763
3870
  q.format(left) if left
3764
- q.text(" ") if space
3765
- q.text(operator)
3766
- q.text(" ") if space
3871
+
3872
+ case q.parent
3873
+ when If, IfMod, Unless, UnlessMod
3874
+ q.text(" #{operator} ")
3875
+ else
3876
+ q.text(operator)
3877
+ end
3878
+
3767
3879
  q.format(right) if right
3768
3880
  end
3769
3881
  end
@@ -3948,19 +4060,30 @@ module SyntaxTree
3948
4060
  def format(q)
3949
4061
  opening_quote, closing_quote = quotes(q)
3950
4062
 
3951
- q.group(0, opening_quote, closing_quote) do
4063
+ q.text(opening_quote)
4064
+ q.group do
3952
4065
  parts.each do |part|
3953
4066
  if part.is_a?(TStringContent)
3954
4067
  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)
4068
+ first = true
4069
+
4070
+ value.each_line(chomp: true) do |line|
4071
+ if first
4072
+ first = false
4073
+ else
4074
+ q.breakable_return
4075
+ end
4076
+
4077
+ q.text(line)
3958
4078
  end
4079
+
4080
+ q.breakable_return if value.end_with?("\n")
3959
4081
  else
3960
4082
  q.format(part)
3961
4083
  end
3962
4084
  end
3963
4085
  end
4086
+ q.text(closing_quote)
3964
4087
  end
3965
4088
 
3966
4089
  private
@@ -4056,7 +4179,7 @@ module SyntaxTree
4056
4179
 
4057
4180
  unless statements.empty?
4058
4181
  q.indent do
4059
- q.breakable(force: true)
4182
+ q.breakable_force
4060
4183
  q.format(statements)
4061
4184
  end
4062
4185
  end
@@ -4126,14 +4249,14 @@ module SyntaxTree
4126
4249
 
4127
4250
  unless statements.empty?
4128
4251
  q.indent do
4129
- q.breakable(force: true)
4252
+ q.breakable_force
4130
4253
  q.format(statements)
4131
4254
  end
4132
4255
  end
4133
4256
 
4134
4257
  if consequent
4135
4258
  q.group do
4136
- q.breakable(force: true)
4259
+ q.breakable_force
4137
4260
  q.format(consequent)
4138
4261
  end
4139
4262
  end
@@ -4329,7 +4452,7 @@ module SyntaxTree
4329
4452
 
4330
4453
  unless statements.empty?
4331
4454
  q.indent do
4332
- q.breakable(force: true)
4455
+ q.breakable_force
4333
4456
  q.format(statements)
4334
4457
  end
4335
4458
  end
@@ -4588,7 +4711,7 @@ module SyntaxTree
4588
4711
  q.text("[")
4589
4712
 
4590
4713
  q.indent do
4591
- q.breakable("")
4714
+ q.breakable_empty
4592
4715
 
4593
4716
  q.text("*")
4594
4717
  q.format(left)
@@ -4601,7 +4724,7 @@ module SyntaxTree
4601
4724
  q.format(right)
4602
4725
  end
4603
4726
 
4604
- q.breakable("")
4727
+ q.breakable_empty
4605
4728
  q.text("]")
4606
4729
  end
4607
4730
  end
@@ -4663,12 +4786,12 @@ module SyntaxTree
4663
4786
 
4664
4787
  unless statements.empty?
4665
4788
  q.indent do
4666
- q.breakable(force: true)
4789
+ q.breakable_force
4667
4790
  q.format(statements)
4668
4791
  end
4669
4792
  end
4670
4793
 
4671
- q.breakable(force: true)
4794
+ q.breakable_force
4672
4795
  q.text("end")
4673
4796
  end
4674
4797
  end
@@ -4731,11 +4854,11 @@ module SyntaxTree
4731
4854
  q.text("{")
4732
4855
  q.indent do
4733
4856
  lbrace.comments.each do |comment|
4734
- q.breakable(force: true)
4857
+ q.breakable_force
4735
4858
  comment.format(q)
4736
4859
  end
4737
4860
  end
4738
- q.breakable(force: true)
4861
+ q.breakable_force
4739
4862
  q.text("}")
4740
4863
  end
4741
4864
  end
@@ -4800,14 +4923,14 @@ module SyntaxTree
4800
4923
  q.format(lbrace)
4801
4924
 
4802
4925
  if assocs.empty?
4803
- q.breakable("")
4926
+ q.breakable_empty
4804
4927
  else
4805
4928
  q.indent do
4806
- q.breakable
4929
+ q.breakable_space
4807
4930
  q.seplist(assocs) { |assoc| q.format(assoc) }
4808
4931
  q.if_break { q.text(",") } if q.trailing_comma?
4809
4932
  end
4810
- q.breakable
4933
+ q.breakable_space
4811
4934
  end
4812
4935
 
4813
4936
  q.text("}")
@@ -4873,22 +4996,34 @@ module SyntaxTree
4873
4996
  }
4874
4997
  end
4875
4998
 
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) }
4999
+ # This is a very specific behavior where you want to force a newline, but
5000
+ # don't want to force the break parent.
5001
+ SEPARATOR = PrettierPrint::Breakable.new(" ", 1, indent: false, force: true)
4880
5002
 
5003
+ def format(q)
4881
5004
  q.group do
4882
5005
  q.format(beginning)
4883
5006
 
4884
5007
  q.line_suffix(priority: Formatter::HEREDOC_PRIORITY) do
4885
5008
  q.group do
4886
- breakable.call
5009
+ q.target << SEPARATOR
4887
5010
 
4888
5011
  parts.each do |part|
4889
5012
  if part.is_a?(TStringContent)
4890
- texts = part.value.split(/\r?\n/, -1)
4891
- q.seplist(texts, breakable) { |text| q.text(text) }
5013
+ value = part.value
5014
+ first = true
5015
+
5016
+ value.each_line(chomp: true) do |line|
5017
+ if first
5018
+ first = false
5019
+ else
5020
+ q.target << SEPARATOR
5021
+ end
5022
+
5023
+ q.text(line)
5024
+ end
5025
+
5026
+ q.target << SEPARATOR if value.end_with?("\n")
4892
5027
  else
4893
5028
  q.format(part)
4894
5029
  end
@@ -5077,18 +5212,7 @@ module SyntaxTree
5077
5212
  def format(q)
5078
5213
  parts = keywords.map { |(key, value)| KeywordFormatter.new(key, value) }
5079
5214
  parts << KeywordRestFormatter.new(keyword_rest) if keyword_rest
5080
-
5081
5215
  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
5216
 
5093
5217
  # If there is a constant, we're going to format to have the constant name
5094
5218
  # first and then use brackets.
@@ -5097,10 +5221,10 @@ module SyntaxTree
5097
5221
  q.format(constant)
5098
5222
  q.text("[")
5099
5223
  q.indent do
5100
- q.breakable("")
5101
- contents.call
5224
+ q.breakable_empty
5225
+ format_contents(q, parts, nested)
5102
5226
  end
5103
- q.breakable("")
5227
+ q.breakable_empty
5104
5228
  q.text("]")
5105
5229
  end
5106
5230
  return
@@ -5115,7 +5239,7 @@ module SyntaxTree
5115
5239
  # If there's only one pair, then we'll just print the contents provided
5116
5240
  # we're not inside another pattern.
5117
5241
  if !nested && parts.size == 1
5118
- contents.call
5242
+ format_contents(q, parts, nested)
5119
5243
  return
5120
5244
  end
5121
5245
 
@@ -5124,18 +5248,31 @@ module SyntaxTree
5124
5248
  q.group do
5125
5249
  q.text("{")
5126
5250
  q.indent do
5127
- q.breakable
5128
- contents.call
5251
+ q.breakable_space
5252
+ format_contents(q, parts, nested)
5129
5253
  end
5130
5254
 
5131
5255
  if q.target_ruby_version < Gem::Version.new("2.7.3")
5132
5256
  q.text(" }")
5133
5257
  else
5134
- q.breakable
5258
+ q.breakable_space
5135
5259
  q.text("}")
5136
5260
  end
5137
5261
  end
5138
5262
  end
5263
+
5264
+ private
5265
+
5266
+ def format_contents(q, parts, nested)
5267
+ q.group { q.seplist(parts) { |part| q.format(part, stackable: false) } }
5268
+
5269
+ # If there isn't a constant, and there's a blank keyword_rest, then we
5270
+ # have an plain ** that needs to have a `then` after it in order to
5271
+ # parse correctly on the next parse.
5272
+ if !constant && keyword_rest && keyword_rest.value.nil? && !nested
5273
+ q.text(" then")
5274
+ end
5275
+ end
5139
5276
  end
5140
5277
 
5141
5278
  # The list of nodes that represent patterns inside of pattern matching so that
@@ -5188,8 +5325,12 @@ module SyntaxTree
5188
5325
  queue = [parent]
5189
5326
 
5190
5327
  while (node = queue.shift)
5191
- return true if [Assign, MAssign, OpAssign].include?(node.class)
5192
- queue += node.child_nodes.compact
5328
+ case node
5329
+ when Assign, MAssign, OpAssign
5330
+ return true
5331
+ else
5332
+ node.child_nodes.each { |child| queue << child if child }
5333
+ end
5193
5334
  end
5194
5335
 
5195
5336
  false
@@ -5204,28 +5345,36 @@ module SyntaxTree
5204
5345
  module Ternaryable
5205
5346
  class << self
5206
5347
  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
5348
+ return false if ENV["STREE_FAST_FORMAT"]
5349
+
5350
+ # If this is a conditional inside of a parentheses as the only content,
5351
+ # then we don't want to transform it into a ternary. Presumably the user
5352
+ # wanted it to be an explicit conditional because there are parentheses
5353
+ # around it. So we'll just leave it in place.
5354
+ grandparent = q.grandparent
5355
+ if grandparent.is_a?(Paren) && (body = grandparent.contents.body) &&
5356
+ body.length == 1 && body.first == node
5357
+ return false
5228
5358
  end
5359
+
5360
+ # Otherwise, we'll check the type of predicate. For certain nodes we
5361
+ # want to force it to not be a ternary, like if the predicate is an
5362
+ # assignment because it's hard to read.
5363
+ case node.predicate
5364
+ when Assign, Command, CommandCall, MAssign, OpAssign
5365
+ return false
5366
+ when Not
5367
+ return false unless node.predicate.parentheses?
5368
+ end
5369
+
5370
+ # If there's no Else, then this can't be represented as a ternary.
5371
+ return false unless node.consequent.is_a?(Else)
5372
+
5373
+ truthy_body = node.statements.body
5374
+ falsy_body = node.consequent.statements.body
5375
+
5376
+ (truthy_body.length == 1) && ternaryable?(truthy_body.first) &&
5377
+ (falsy_body.length == 1) && ternaryable?(falsy_body.first)
5229
5378
  end
5230
5379
 
5231
5380
  private
@@ -5234,24 +5383,23 @@ module SyntaxTree
5234
5383
  # parentheses around them. In this case we say they cannot be ternaried
5235
5384
  # and default instead to breaking them into multiple lines.
5236
5385
  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))
5386
+ case statement
5387
+ when Alias, Assign, Break, Command, CommandCall, Heredoc, If, IfMod,
5388
+ IfOp, Lambda, MAssign, Next, OpAssign, RescueMod, Return, Return0,
5389
+ Super, Undef, Unless, UnlessMod, Until, UntilMod, VarAlias,
5390
+ VoidStmt, While, WhileMod, Yield, Yield0, ZSuper
5391
+ # This is a list of nodes that should not be allowed to be a part of a
5392
+ # ternary clause.
5393
+ false
5394
+ when Binary
5395
+ # If the user is using one of the lower precedence "and" or "or"
5396
+ # operators, then we can't use a ternary expression as it would break
5397
+ # the flow control.
5398
+ operator = statement.operator
5399
+ operator != :and && operator != :or
5400
+ else
5401
+ true
5402
+ end
5255
5403
  end
5256
5404
  end
5257
5405
  end
@@ -5311,17 +5459,17 @@ module SyntaxTree
5311
5459
 
5312
5460
  unless node.statements.empty?
5313
5461
  q.indent do
5314
- q.breakable(force: force)
5462
+ force ? q.breakable_force : q.breakable_space
5315
5463
  q.format(node.statements)
5316
5464
  end
5317
5465
  end
5318
5466
 
5319
5467
  if node.consequent
5320
- q.breakable(force: force)
5468
+ force ? q.breakable_force : q.breakable_space
5321
5469
  q.format(node.consequent)
5322
5470
  end
5323
5471
 
5324
- q.breakable(force: force)
5472
+ force ? q.breakable_force : q.breakable_space
5325
5473
  q.text("end")
5326
5474
  end
5327
5475
 
@@ -5333,11 +5481,11 @@ module SyntaxTree
5333
5481
  q.nest(keyword.length + 1) { q.format(node.predicate) }
5334
5482
 
5335
5483
  q.indent do
5336
- q.breakable
5484
+ q.breakable_space
5337
5485
  q.format(node.statements)
5338
5486
  end
5339
5487
 
5340
- q.breakable
5488
+ q.breakable_space
5341
5489
  q.group do
5342
5490
  q.format(node.consequent.keyword)
5343
5491
  q.indent do
@@ -5351,7 +5499,7 @@ module SyntaxTree
5351
5499
  end
5352
5500
  end
5353
5501
 
5354
- q.breakable
5502
+ q.breakable_space
5355
5503
  q.text("end")
5356
5504
  end
5357
5505
  .if_flat do
@@ -5371,8 +5519,11 @@ module SyntaxTree
5371
5519
  end
5372
5520
 
5373
5521
  def contains_conditional?
5374
- case node
5375
- in statements: { body: [If | IfMod | IfOp | Unless | UnlessMod] }
5522
+ statements = node.statements.body
5523
+ return false if statements.length != 1
5524
+
5525
+ case statements.first
5526
+ when If, IfMod, IfOp, Unless, UnlessMod
5376
5527
  true
5377
5528
  else
5378
5529
  false
@@ -5507,19 +5658,19 @@ module SyntaxTree
5507
5658
  q.nest("if ".length) { q.format(predicate) }
5508
5659
 
5509
5660
  q.indent do
5510
- q.breakable
5661
+ q.breakable_space
5511
5662
  q.format(truthy)
5512
5663
  end
5513
5664
 
5514
- q.breakable
5665
+ q.breakable_space
5515
5666
  q.text("else")
5516
5667
 
5517
5668
  q.indent do
5518
- q.breakable
5669
+ q.breakable_space
5519
5670
  q.format(falsy)
5520
5671
  end
5521
5672
 
5522
- q.breakable
5673
+ q.breakable_space
5523
5674
  q.text("end")
5524
5675
  end
5525
5676
  end
@@ -5529,11 +5680,11 @@ module SyntaxTree
5529
5680
  q.text(" ?")
5530
5681
 
5531
5682
  q.indent do
5532
- q.breakable
5683
+ q.breakable_space
5533
5684
  q.format(truthy)
5534
5685
  q.text(" :")
5535
5686
 
5536
- q.breakable
5687
+ q.breakable_space
5537
5688
  q.format(falsy)
5538
5689
  end
5539
5690
  end
@@ -5566,10 +5717,10 @@ module SyntaxTree
5566
5717
  q.text("#{keyword} ")
5567
5718
  q.nest(keyword.length + 1) { q.format(node.predicate) }
5568
5719
  q.indent do
5569
- q.breakable
5720
+ q.breakable_space
5570
5721
  q.format(node.statement)
5571
5722
  end
5572
- q.breakable
5723
+ q.breakable_space
5573
5724
  q.text("end")
5574
5725
  end
5575
5726
 
@@ -5720,13 +5871,13 @@ module SyntaxTree
5720
5871
 
5721
5872
  unless statements.empty?
5722
5873
  q.indent do
5723
- q.breakable(force: true)
5874
+ q.breakable_force
5724
5875
  q.format(statements)
5725
5876
  end
5726
5877
  end
5727
5878
 
5728
5879
  if consequent
5729
- q.breakable(force: true)
5880
+ q.breakable_force
5730
5881
  q.format(consequent)
5731
5882
  end
5732
5883
  end
@@ -5830,11 +5981,15 @@ module SyntaxTree
5830
5981
  # [String] the value of the keyword
5831
5982
  attr_reader :value
5832
5983
 
5984
+ # [Symbol] the symbol version of the value
5985
+ attr_reader :name
5986
+
5833
5987
  # [Array[ Comment | EmbDoc ]] the comments attached to this node
5834
5988
  attr_reader :comments
5835
5989
 
5836
5990
  def initialize(value:, location:, comments: [])
5837
5991
  @value = value
5992
+ @name = value.to_sym
5838
5993
  @location = location
5839
5994
  @comments = comments
5840
5995
  end
@@ -6013,7 +6168,8 @@ module SyntaxTree
6013
6168
  end
6014
6169
 
6015
6170
  def format(q)
6016
- q.group(0, "->") do
6171
+ q.text("->")
6172
+ q.group do
6017
6173
  if params.is_a?(Paren)
6018
6174
  q.format(params) unless params.contents.empty?
6019
6175
  elsif params.empty? && params.comments.any?
@@ -6039,10 +6195,10 @@ module SyntaxTree
6039
6195
 
6040
6196
  unless statements.empty?
6041
6197
  q.indent do
6042
- q.breakable
6198
+ q.breakable_space
6043
6199
  q.format(statements)
6044
6200
  end
6045
- q.breakable
6201
+ q.breakable_space
6046
6202
  end
6047
6203
 
6048
6204
  q.text("}")
@@ -6051,12 +6207,12 @@ module SyntaxTree
6051
6207
 
6052
6208
  unless statements.empty?
6053
6209
  q.indent do
6054
- q.breakable
6210
+ q.breakable_space
6055
6211
  q.format(statements)
6056
6212
  end
6057
6213
  end
6058
6214
 
6059
- q.breakable
6215
+ q.breakable_space
6060
6216
  q.text("end")
6061
6217
  end
6062
6218
  end
@@ -6123,7 +6279,7 @@ module SyntaxTree
6123
6279
 
6124
6280
  if locals.any?
6125
6281
  q.text("; ")
6126
- q.seplist(locals, -> { q.text(", ") }) { |local| q.format(local) }
6282
+ q.seplist(locals, BlockVar::SEPARATOR) { |local| q.format(local) }
6127
6283
  end
6128
6284
  end
6129
6285
  end
@@ -6277,7 +6433,7 @@ module SyntaxTree
6277
6433
  q.group { q.format(target) }
6278
6434
  q.text(" =")
6279
6435
  q.indent do
6280
- q.breakable
6436
+ q.breakable_space
6281
6437
  q.format(value)
6282
6438
  end
6283
6439
  end
@@ -6323,8 +6479,8 @@ module SyntaxTree
6323
6479
  # If we're at the top of a call chain, then we're going to do some
6324
6480
  # specialized printing in case we can print it nicely. We _only_ do this
6325
6481
  # at the top of the chain to avoid weird recursion issues.
6326
- if !CallChainFormatter.chained?(q.parent) &&
6327
- CallChainFormatter.chained?(call)
6482
+ if CallChainFormatter.chained?(call) &&
6483
+ !CallChainFormatter.chained?(q.parent)
6328
6484
  q.group do
6329
6485
  q
6330
6486
  .if_break { CallChainFormatter.new(self).format(q) }
@@ -6431,15 +6587,17 @@ module SyntaxTree
6431
6587
  q.format(contents)
6432
6588
  q.text(",") if comma
6433
6589
  else
6434
- q.group(0, "(", ")") do
6590
+ q.text("(")
6591
+ q.group do
6435
6592
  q.indent do
6436
- q.breakable("")
6593
+ q.breakable_empty
6437
6594
  q.format(contents)
6438
6595
  end
6439
6596
 
6440
6597
  q.text(",") if comma
6441
- q.breakable("")
6598
+ q.breakable_empty
6442
6599
  end
6600
+ q.text(")")
6443
6601
  end
6444
6602
  end
6445
6603
  end
@@ -6486,33 +6644,35 @@ module SyntaxTree
6486
6644
  end
6487
6645
 
6488
6646
  def format(q)
6489
- declaration = -> do
6490
- q.group do
6491
- q.text("module ")
6492
- q.format(constant)
6493
- end
6494
- end
6495
-
6496
6647
  if bodystmt.empty?
6497
6648
  q.group do
6498
- declaration.call
6499
- q.breakable(force: true)
6649
+ format_declaration(q)
6650
+ q.breakable_force
6500
6651
  q.text("end")
6501
6652
  end
6502
6653
  else
6503
6654
  q.group do
6504
- declaration.call
6655
+ format_declaration(q)
6505
6656
 
6506
6657
  q.indent do
6507
- q.breakable(force: true)
6658
+ q.breakable_force
6508
6659
  q.format(bodystmt)
6509
6660
  end
6510
6661
 
6511
- q.breakable(force: true)
6662
+ q.breakable_force
6512
6663
  q.text("end")
6513
6664
  end
6514
6665
  end
6515
6666
  end
6667
+
6668
+ private
6669
+
6670
+ def format_declaration(q)
6671
+ q.group do
6672
+ q.text("module ")
6673
+ q.format(constant)
6674
+ end
6675
+ end
6516
6676
  end
6517
6677
 
6518
6678
  # MRHS represents the values that are being assigned on the right-hand side of
@@ -6610,11 +6770,15 @@ module SyntaxTree
6610
6770
  # [String] the operator
6611
6771
  attr_reader :value
6612
6772
 
6773
+ # [Symbol] the symbol version of the value
6774
+ attr_reader :name
6775
+
6613
6776
  # [Array[ Comment | EmbDoc ]] the comments attached to this node
6614
6777
  attr_reader :comments
6615
6778
 
6616
6779
  def initialize(value:, location:, comments: [])
6617
6780
  @value = value
6781
+ @name = value.to_sym
6618
6782
  @location = location
6619
6783
  @comments = comments
6620
6784
  end
@@ -6696,7 +6860,7 @@ module SyntaxTree
6696
6860
  q.format(value)
6697
6861
  else
6698
6862
  q.indent do
6699
- q.breakable
6863
+ q.breakable_space
6700
6864
  q.format(value)
6701
6865
  end
6702
6866
  end
@@ -6767,10 +6931,10 @@ module SyntaxTree
6767
6931
 
6768
6932
  q.text("(")
6769
6933
  q.indent do
6770
- q.breakable("")
6934
+ q.breakable_empty
6771
6935
  yield
6772
6936
  end
6773
- q.breakable("")
6937
+ q.breakable_empty
6774
6938
  q.text(")")
6775
6939
  end
6776
6940
  end
@@ -6962,23 +7126,35 @@ module SyntaxTree
6962
7126
  parts << KeywordRestFormatter.new(keyword_rest) if keyword_rest
6963
7127
  parts << block if block
6964
7128
 
6965
- contents = -> do
6966
- q.seplist(parts) { |part| q.format(part) }
6967
- q.format(rest) if rest.is_a?(ExcessedComma)
7129
+ if parts.empty?
7130
+ q.nest(0) { format_contents(q, parts) }
7131
+ return
6968
7132
  end
6969
7133
 
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
7134
+ case q.parent
7135
+ when Def, Defs, DefEndless
7136
+ q.nest(0) do
7137
+ q.text("(")
7138
+ q.group do
7139
+ q.indent do
7140
+ q.breakable_empty
7141
+ format_contents(q, parts)
7142
+ end
7143
+ q.breakable_empty
6977
7144
  end
6978
- q.breakable("")
7145
+ q.text(")")
6979
7146
  end
7147
+ else
7148
+ q.nest(0) { format_contents(q, parts) }
6980
7149
  end
6981
7150
  end
7151
+
7152
+ private
7153
+
7154
+ def format_contents(q, parts)
7155
+ q.seplist(parts) { |part| q.format(part) }
7156
+ q.format(rest) if rest.is_a?(ExcessedComma)
7157
+ end
6982
7158
  end
6983
7159
 
6984
7160
  # Paren represents using balanced parentheses in a couple places in a Ruby
@@ -7029,12 +7205,12 @@ module SyntaxTree
7029
7205
 
7030
7206
  if contents && (!contents.is_a?(Params) || !contents.empty?)
7031
7207
  q.indent do
7032
- q.breakable("")
7208
+ q.breakable_empty
7033
7209
  q.format(contents)
7034
7210
  end
7035
7211
  end
7036
7212
 
7037
- q.breakable("")
7213
+ q.breakable_empty
7038
7214
  q.text(")")
7039
7215
  end
7040
7216
  end
@@ -7108,7 +7284,7 @@ module SyntaxTree
7108
7284
  # We're going to put a newline on the end so that it always has one unless
7109
7285
  # it ends with the special __END__ syntax. In that case we want to
7110
7286
  # replicate the text exactly so we will just let it be.
7111
- q.breakable(force: true) unless statements.body.last.is_a?(EndContent)
7287
+ q.breakable_force unless statements.body.last.is_a?(EndContent)
7112
7288
  end
7113
7289
  end
7114
7290
 
@@ -7160,15 +7336,18 @@ module SyntaxTree
7160
7336
  closing = Quotes.matching(opening[2])
7161
7337
  end
7162
7338
 
7163
- q.group(0, opening, closing) do
7339
+ q.text(opening)
7340
+ q.group do
7164
7341
  q.indent do
7165
- q.breakable("")
7166
- q.seplist(elements, -> { q.breakable }) do |element|
7167
- q.format(element)
7168
- end
7342
+ q.breakable_empty
7343
+ q.seplist(
7344
+ elements,
7345
+ ArrayLiteral::BREAKABLE_SPACE_SEPARATOR
7346
+ ) { |element| q.format(element) }
7169
7347
  end
7170
- q.breakable("")
7348
+ q.breakable_empty
7171
7349
  end
7350
+ q.text(closing)
7172
7351
  end
7173
7352
  end
7174
7353
 
@@ -7251,15 +7430,18 @@ module SyntaxTree
7251
7430
  closing = Quotes.matching(opening[2])
7252
7431
  end
7253
7432
 
7254
- q.group(0, opening, closing) do
7433
+ q.text(opening)
7434
+ q.group do
7255
7435
  q.indent do
7256
- q.breakable("")
7257
- q.seplist(elements, -> { q.breakable }) do |element|
7258
- q.format(element)
7259
- end
7436
+ q.breakable_empty
7437
+ q.seplist(
7438
+ elements,
7439
+ ArrayLiteral::BREAKABLE_SPACE_SEPARATOR
7440
+ ) { |element| q.format(element) }
7260
7441
  end
7261
- q.breakable("")
7442
+ q.breakable_empty
7262
7443
  end
7444
+ q.text(closing)
7263
7445
  end
7264
7446
  end
7265
7447
 
@@ -7781,13 +7963,13 @@ module SyntaxTree
7781
7963
 
7782
7964
  unless statements.empty?
7783
7965
  q.indent do
7784
- q.breakable(force: true)
7966
+ q.breakable_force
7785
7967
  q.format(statements)
7786
7968
  end
7787
7969
  end
7788
7970
 
7789
7971
  if consequent
7790
- q.breakable(force: true)
7972
+ q.breakable_force
7791
7973
  q.format(consequent)
7792
7974
  end
7793
7975
  end
@@ -7835,19 +8017,21 @@ module SyntaxTree
7835
8017
  end
7836
8018
 
7837
8019
  def format(q)
7838
- q.group(0, "begin", "end") do
8020
+ q.text("begin")
8021
+ q.group do
7839
8022
  q.indent do
7840
- q.breakable(force: true)
8023
+ q.breakable_force
7841
8024
  q.format(statement)
7842
8025
  end
7843
- q.breakable(force: true)
8026
+ q.breakable_force
7844
8027
  q.text("rescue StandardError")
7845
8028
  q.indent do
7846
- q.breakable(force: true)
8029
+ q.breakable_force
7847
8030
  q.format(value)
7848
8031
  end
7849
- q.breakable(force: true)
8032
+ q.breakable_force
7850
8033
  end
8034
+ q.text("end")
7851
8035
  end
7852
8036
  end
7853
8037
 
@@ -8066,14 +8250,16 @@ module SyntaxTree
8066
8250
  end
8067
8251
 
8068
8252
  def format(q)
8069
- q.group(0, "class << ", "end") do
8253
+ q.text("class << ")
8254
+ q.group do
8070
8255
  q.format(target)
8071
8256
  q.indent do
8072
- q.breakable(force: true)
8257
+ q.breakable_force
8073
8258
  q.format(bodystmt)
8074
8259
  end
8075
- q.breakable(force: true)
8260
+ q.breakable_force
8076
8261
  end
8262
+ q.text("end")
8077
8263
  end
8078
8264
  end
8079
8265
 
@@ -8179,30 +8365,26 @@ module SyntaxTree
8179
8365
  end
8180
8366
  end
8181
8367
 
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|
8368
+ previous = nil
8369
+ body.each do |statement|
8189
8370
  next if statement.is_a?(VoidStmt)
8190
8371
 
8191
8372
  if line.nil?
8192
8373
  q.format(statement)
8193
8374
  elsif (statement.location.start_line - line) > 1
8194
- q.breakable(force: true)
8195
- q.breakable(force: true)
8375
+ q.breakable_force
8376
+ q.breakable_force
8196
8377
  q.format(statement)
8197
- elsif access_controls[statement] || access_controls[body[index - 1]]
8198
- q.breakable(force: true)
8199
- q.breakable(force: true)
8378
+ elsif (statement.is_a?(VCall) && statement.access_control?) ||
8379
+ (previous.is_a?(VCall) && previous.access_control?)
8380
+ q.breakable_force
8381
+ q.breakable_force
8200
8382
  q.format(statement)
8201
8383
  elsif statement.location.start_line != line
8202
- q.breakable(force: true)
8384
+ q.breakable_force
8203
8385
  q.format(statement)
8204
8386
  elsif !q.parent.is_a?(StringEmbExpr)
8205
- q.breakable(force: true)
8387
+ q.breakable_force
8206
8388
  q.format(statement)
8207
8389
  else
8208
8390
  q.text("; ")
@@ -8210,6 +8392,7 @@ module SyntaxTree
8210
8392
  end
8211
8393
 
8212
8394
  line = statement.location.end_line
8395
+ previous = statement
8213
8396
  end
8214
8397
  end
8215
8398
 
@@ -8327,7 +8510,7 @@ module SyntaxTree
8327
8510
  q.format(left)
8328
8511
  q.text(" \\")
8329
8512
  q.indent do
8330
- q.breakable(force: true)
8513
+ q.breakable_force
8331
8514
  q.format(right)
8332
8515
  end
8333
8516
  end
@@ -8413,15 +8596,21 @@ module SyntaxTree
8413
8596
  # same line in the source, then we're going to leave them in place and
8414
8597
  # assume that's the way the developer wanted this expression
8415
8598
  # represented.
8416
- q.remove_breaks(q.group(0, '#{', "}") { q.format(statements) })
8599
+ q.remove_breaks(
8600
+ q.group do
8601
+ q.text('#{')
8602
+ q.format(statements)
8603
+ q.text("}")
8604
+ end
8605
+ )
8417
8606
  else
8418
8607
  q.group do
8419
8608
  q.text('#{')
8420
8609
  q.indent do
8421
- q.breakable("")
8610
+ q.breakable_empty
8422
8611
  q.format(statements)
8423
8612
  end
8424
- q.breakable("")
8613
+ q.breakable_empty
8425
8614
  q.text("}")
8426
8615
  end
8427
8616
  end
@@ -8479,19 +8668,30 @@ module SyntaxTree
8479
8668
  [quote, quote]
8480
8669
  end
8481
8670
 
8482
- q.group(0, opening_quote, closing_quote) do
8671
+ q.text(opening_quote)
8672
+ q.group do
8483
8673
  parts.each do |part|
8484
8674
  if part.is_a?(TStringContent)
8485
8675
  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)
8676
+ first = true
8677
+
8678
+ value.each_line(chomp: true) do |line|
8679
+ if first
8680
+ first = false
8681
+ else
8682
+ q.breakable_return
8683
+ end
8684
+
8685
+ q.text(line)
8489
8686
  end
8687
+
8688
+ q.breakable_return if value.end_with?("\n")
8490
8689
  else
8491
8690
  q.format(part)
8492
8691
  end
8493
8692
  end
8494
8693
  end
8694
+ q.text(closing_quote)
8495
8695
  end
8496
8696
  end
8497
8697
 
@@ -8698,15 +8898,18 @@ module SyntaxTree
8698
8898
  closing = Quotes.matching(opening[2])
8699
8899
  end
8700
8900
 
8701
- q.group(0, opening, closing) do
8901
+ q.text(opening)
8902
+ q.group do
8702
8903
  q.indent do
8703
- q.breakable("")
8704
- q.seplist(elements, -> { q.breakable }) do |element|
8705
- q.format(element)
8706
- end
8904
+ q.breakable_empty
8905
+ q.seplist(
8906
+ elements,
8907
+ ArrayLiteral::BREAKABLE_SPACE_SEPARATOR
8908
+ ) { |element| q.format(element) }
8707
8909
  end
8708
- q.breakable("")
8910
+ q.breakable_empty
8709
8911
  end
8912
+ q.text(closing)
8710
8913
  end
8711
8914
  end
8712
8915
 
@@ -9000,6 +9203,7 @@ module SyntaxTree
9000
9203
 
9001
9204
  # [boolean] whether or not parentheses were used
9002
9205
  attr_reader :parentheses
9206
+ alias parentheses? parentheses
9003
9207
 
9004
9208
  # [Array[ Comment | EmbDoc ]] the comments attached to this node
9005
9209
  attr_reader :comments
@@ -9031,27 +9235,26 @@ module SyntaxTree
9031
9235
  end
9032
9236
 
9033
9237
  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
9238
  q.text("not")
9040
9239
 
9041
9240
  if parentheses
9042
9241
  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
9242
+ q.format(statement) if statement
9052
9243
  q.text(")")
9053
- elsif ternary
9054
- q.if_flat { q.text(")") }
9244
+ else
9245
+ grandparent = q.grandparent
9246
+ ternary =
9247
+ (grandparent.is_a?(If) || grandparent.is_a?(Unless)) &&
9248
+ Ternaryable.call(q, grandparent)
9249
+
9250
+ if ternary
9251
+ q.if_break { q.text(" ") }.if_flat { q.text("(") }
9252
+ q.format(statement) if statement
9253
+ q.if_flat { q.text(")") } if ternary
9254
+ else
9255
+ q.text(" ")
9256
+ q.format(statement) if statement
9257
+ end
9055
9258
  end
9056
9259
  end
9057
9260
  end
@@ -9316,10 +9519,10 @@ module SyntaxTree
9316
9519
  q.text("#{keyword} ")
9317
9520
  q.nest(keyword.length + 1) { q.format(node.predicate) }
9318
9521
  q.indent do
9319
- q.breakable("")
9522
+ q.breakable_empty
9320
9523
  q.format(statements)
9321
9524
  end
9322
- q.breakable("")
9525
+ q.breakable_empty
9323
9526
  q.text("end")
9324
9527
  end
9325
9528
  end
@@ -9372,7 +9575,7 @@ module SyntaxTree
9372
9575
  q.group do
9373
9576
  q.text(keyword)
9374
9577
  q.nest(keyword.length) { q.format(predicate) }
9375
- q.breakable(force: true)
9578
+ q.breakable_force
9376
9579
  q.text("end")
9377
9580
  end
9378
9581
  else
@@ -9572,6 +9775,29 @@ module SyntaxTree
9572
9775
  def format(q)
9573
9776
  q.format(value)
9574
9777
  end
9778
+
9779
+ # Oh man I hate this so much. Basically, ripper doesn't provide enough
9780
+ # functionality to actually know where pins are within an expression. So we
9781
+ # have to walk the tree ourselves and insert more information. In doing so,
9782
+ # we have to replace this node by a pinned node when necessary.
9783
+ #
9784
+ # To be clear, this method should just not exist. It's not good. It's a
9785
+ # place of shame. But it's necessary for now, so I'm keeping it.
9786
+ def pin(parent)
9787
+ replace = PinnedVarRef.new(value: value, location: location)
9788
+
9789
+ parent
9790
+ .deconstruct_keys([])
9791
+ .each do |key, value|
9792
+ if value == self
9793
+ parent.instance_variable_set(:"@#{key}", replace)
9794
+ break
9795
+ elsif value.is_a?(Array) && (index = value.index(self))
9796
+ parent.public_send(key)[index] = replace
9797
+ break
9798
+ end
9799
+ end
9800
+ end
9575
9801
  end
9576
9802
 
9577
9803
  # PinnedVarRef represents a pinned variable reference within a pattern
@@ -9653,6 +9879,10 @@ module SyntaxTree
9653
9879
  def format(q)
9654
9880
  q.format(value)
9655
9881
  end
9882
+
9883
+ def access_control?
9884
+ @access_control ||= %w[private protected public].include?(value.value)
9885
+ end
9656
9886
  end
9657
9887
 
9658
9888
  # VoidStmt represents an empty lexical block of code.
@@ -9742,6 +9972,22 @@ module SyntaxTree
9742
9972
  }
9743
9973
  end
9744
9974
 
9975
+ # We have a special separator here for when clauses which causes them to
9976
+ # fill as much of the line as possible as opposed to everything breaking
9977
+ # into its own line as soon as you hit the print limit.
9978
+ class Separator
9979
+ def call(q)
9980
+ q.group do
9981
+ q.text(",")
9982
+ q.breakable_space
9983
+ end
9984
+ end
9985
+ end
9986
+
9987
+ # We're going to keep a single instance of this separator around so we don't
9988
+ # have to allocate a new one every time we format a when clause.
9989
+ SEPARATOR = Separator.new
9990
+
9745
9991
  def format(q)
9746
9992
  keyword = "when "
9747
9993
 
@@ -9752,8 +9998,7 @@ module SyntaxTree
9752
9998
  if arguments.comments.any?
9753
9999
  q.format(arguments)
9754
10000
  else
9755
- separator = -> { q.group { q.comma_breakable } }
9756
- q.seplist(arguments.parts, separator) { |part| q.format(part) }
10001
+ q.seplist(arguments.parts, SEPARATOR) { |part| q.format(part) }
9757
10002
  end
9758
10003
 
9759
10004
  # Very special case here. If you're inside of a when clause and the
@@ -9768,13 +10013,13 @@ module SyntaxTree
9768
10013
 
9769
10014
  unless statements.empty?
9770
10015
  q.indent do
9771
- q.breakable(force: true)
10016
+ q.breakable_force
9772
10017
  q.format(statements)
9773
10018
  end
9774
10019
  end
9775
10020
 
9776
10021
  if consequent
9777
- q.breakable(force: true)
10022
+ q.breakable_force
9778
10023
  q.format(consequent)
9779
10024
  end
9780
10025
  end
@@ -9829,7 +10074,7 @@ module SyntaxTree
9829
10074
  q.group do
9830
10075
  q.text(keyword)
9831
10076
  q.nest(keyword.length) { q.format(predicate) }
9832
- q.breakable(force: true)
10077
+ q.breakable_force
9833
10078
  q.text("end")
9834
10079
  end
9835
10080
  else
@@ -9995,15 +10240,18 @@ module SyntaxTree
9995
10240
  closing = Quotes.matching(opening[2])
9996
10241
  end
9997
10242
 
9998
- q.group(0, opening, closing) do
10243
+ q.text(opening)
10244
+ q.group do
9999
10245
  q.indent do
10000
- q.breakable("")
10001
- q.seplist(elements, -> { q.breakable }) do |element|
10002
- q.format(element)
10003
- end
10246
+ q.breakable_empty
10247
+ q.seplist(
10248
+ elements,
10249
+ ArrayLiteral::BREAKABLE_SPACE_SEPARATOR
10250
+ ) { |element| q.format(element) }
10004
10251
  end
10005
- q.breakable("")
10252
+ q.breakable_empty
10006
10253
  end
10254
+ q.text(closing)
10007
10255
  end
10008
10256
  end
10009
10257
 
@@ -10147,10 +10395,10 @@ module SyntaxTree
10147
10395
  else
10148
10396
  q.if_break { q.text("(") }.if_flat { q.text(" ") }
10149
10397
  q.indent do
10150
- q.breakable("")
10398
+ q.breakable_empty
10151
10399
  q.format(arguments)
10152
10400
  end
10153
- q.breakable("")
10401
+ q.breakable_empty
10154
10402
  q.if_break { q.text(")") }
10155
10403
  end
10156
10404
  end