syntax_tree 3.6.3 → 4.0.0

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