syntax_tree 2.2.0 → 2.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -1
- data/Gemfile.lock +5 -1
- data/lib/syntax_tree/formatter.rb +8 -1
- data/lib/syntax_tree/node.rb +615 -89
- data/lib/syntax_tree/parser.rb +5 -1
- data/lib/syntax_tree/prettyprint.rb +25 -13
- data/lib/syntax_tree/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5db5f5652e0786475f56ea361b693340998d3f1505d57e2300182d2cc811bcf3
|
4
|
+
data.tar.gz: 3ee5db660541f5355e80fe7d63c4995e30c5089b45286ab1e7ac29ccc62c8bab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9be4902085676d0c4c76eaa29c5b00878916409f2b63a519376ab1a9521cf6204c7aa0a540e61cfa607079ebe17ef42c960c63ab752da300fbdf653be279e4e8
|
7
|
+
data.tar.gz: b2923a9ed6e61e3e14b353b200e49ffe39556081a25fb409cf3af4bd8de4e14aa6229c44c20bb76e67b0efbf63b463cdb0a307ff43975ca5a08a362e6af8aac6
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,27 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [2.3.0] - 2022-04-22
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- [#52](https://github.com/ruby-syntax-tree/syntax_tree/pull/52) - `SyntaxTree::Formatter.format` for formatting an already parsed node.
|
14
|
+
- [#56](https://github.com/ruby-syntax-tree/syntax_tree/pull/56) - `if` and `unless` can now be transformed into ternaries if they're simple enough.
|
15
|
+
- [#56](https://github.com/ruby-syntax-tree/syntax_tree/pull/56) - Nicely format call chains by one indentation.
|
16
|
+
- [#56](https://github.com/ruby-syntax-tree/syntax_tree/pull/56) - Handle trailing operators in call chains when they are necessary because of comments.
|
17
|
+
- [#56](https://github.com/ruby-syntax-tree/syntax_tree/pull/56) - Add some specialized formatting for Sorbet `sig` blocks to make them appear nicer.
|
18
|
+
|
19
|
+
### Changed
|
20
|
+
|
21
|
+
- [#53](https://github.com/ruby-syntax-tree/syntax_tree/pull/53) - Optional keyword arguments on method declarations have a value of `nil` now instead of `false`. This makes it easier to use the visitor.
|
22
|
+
- [#54](https://github.com/ruby-syntax-tree/syntax_tree/pull/54) - Flow control operators can now skip parentheses for simple, individual arguments. e.g., `break(1)` becomes `break 1`.
|
23
|
+
- [#54](https://github.com/ruby-syntax-tree/syntax_tree/pull/54) - Don't allow modifier conditionals to modify ternaries.
|
24
|
+
- [#55](https://github.com/ruby-syntax-tree/syntax_tree/pull/55) - Skip parentheses and brackets on arrays for flow control operators. e.g., `break([1, 2, 3])` becomes `break 1, 2, 3`.
|
25
|
+
- [#56](https://github.com/ruby-syntax-tree/syntax_tree/pull/56) - Don't add parentheses to method calls if you don't need them.
|
26
|
+
- [#56](https://github.com/ruby-syntax-tree/syntax_tree/pull/56) - Format comments on empty parameter sets. e.g., `def foo # bar` should keeps its comment.
|
27
|
+
- [#56](https://github.com/ruby-syntax-tree/syntax_tree/pull/56) - `%s[]` symbols on assignments should not indent to the next line.
|
28
|
+
- [#56](https://github.com/ruby-syntax-tree/syntax_tree/pull/56) - Empty hash and array literals with comments inside of them should be formatted correctly.
|
29
|
+
|
9
30
|
## [2.2.0] - 2022-04-19
|
10
31
|
|
11
32
|
### Added
|
@@ -166,7 +187,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
166
187
|
|
167
188
|
- 🎉 Initial release! 🎉
|
168
189
|
|
169
|
-
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.
|
190
|
+
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.3.0...HEAD
|
191
|
+
[2.3.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.2.0...v2.3.0
|
170
192
|
[2.2.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.1.1...v2.2.0
|
171
193
|
[2.1.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.1.0...v2.1.1
|
172
194
|
[2.1.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.0.1...v2.1.0
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
syntax_tree (2.
|
4
|
+
syntax_tree (2.3.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -17,7 +17,11 @@ GEM
|
|
17
17
|
simplecov_json_formatter (0.1.4)
|
18
18
|
|
19
19
|
PLATFORMS
|
20
|
+
arm64-darwin-21
|
21
|
+
ruby
|
22
|
+
x86_64-darwin-19
|
20
23
|
x86_64-darwin-21
|
24
|
+
x86_64-linux
|
21
25
|
|
22
26
|
DEPENDENCIES
|
23
27
|
bundler
|
@@ -17,6 +17,13 @@ module SyntaxTree
|
|
17
17
|
@quote = "\""
|
18
18
|
end
|
19
19
|
|
20
|
+
def self.format(source, node)
|
21
|
+
formatter = new(source, [])
|
22
|
+
node.format(formatter)
|
23
|
+
formatter.flush
|
24
|
+
formatter.output.join
|
25
|
+
end
|
26
|
+
|
20
27
|
def format(node, stackable: true)
|
21
28
|
stack << node if stackable
|
22
29
|
doc = nil
|
@@ -43,7 +50,7 @@ module SyntaxTree
|
|
43
50
|
# Print all comments that were found after the node.
|
44
51
|
trailing.each do |comment|
|
45
52
|
line_suffix(priority: COMMENT_PRIORITY) do
|
46
|
-
text(" ")
|
53
|
+
comment.inline? ? text(" ") : breakable
|
47
54
|
comment.format(self)
|
48
55
|
break_parent
|
49
56
|
end
|
data/lib/syntax_tree/node.rb
CHANGED
@@ -816,6 +816,29 @@ module SyntaxTree
|
|
816
816
|
end
|
817
817
|
end
|
818
818
|
|
819
|
+
class EmptyWithCommentsFormatter
|
820
|
+
# [LBracket] the opening bracket
|
821
|
+
attr_reader :lbracket
|
822
|
+
|
823
|
+
def initialize(lbracket)
|
824
|
+
@lbracket = lbracket
|
825
|
+
end
|
826
|
+
|
827
|
+
def format(q)
|
828
|
+
q.group do
|
829
|
+
q.text("[")
|
830
|
+
q.indent do
|
831
|
+
lbracket.comments.each do |comment|
|
832
|
+
q.breakable(force: true)
|
833
|
+
comment.format(q)
|
834
|
+
end
|
835
|
+
end
|
836
|
+
q.breakable(force: true)
|
837
|
+
q.text("]")
|
838
|
+
end
|
839
|
+
end
|
840
|
+
end
|
841
|
+
|
819
842
|
# [LBracket] the bracket that opens this array
|
820
843
|
attr_reader :lbracket
|
821
844
|
|
@@ -867,6 +890,11 @@ module SyntaxTree
|
|
867
890
|
return
|
868
891
|
end
|
869
892
|
|
893
|
+
if empty_with_comments?
|
894
|
+
EmptyWithCommentsFormatter.new(lbracket).format(q)
|
895
|
+
return
|
896
|
+
end
|
897
|
+
|
870
898
|
q.group do
|
871
899
|
q.format(lbracket)
|
872
900
|
|
@@ -919,6 +947,12 @@ module SyntaxTree
|
|
919
947
|
q.maxwidth * 2
|
920
948
|
)
|
921
949
|
end
|
950
|
+
|
951
|
+
# If we have an empty array that contains only comments, then we're going
|
952
|
+
# to do some special printing to ensure they get indented correctly.
|
953
|
+
def empty_with_comments?
|
954
|
+
contents.nil? && lbracket.comments.any? && lbracket.comments.none?(&:inline?)
|
955
|
+
end
|
922
956
|
end
|
923
957
|
|
924
958
|
# AryPtn represents matching against an array pattern using the Ruby 2.7+
|
@@ -1042,17 +1076,16 @@ module SyntaxTree
|
|
1042
1076
|
# Determins if the following value should be indented or not.
|
1043
1077
|
module AssignFormatting
|
1044
1078
|
def self.skip_indent?(value)
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
].include?(value.class)
|
1079
|
+
case value
|
1080
|
+
in ArrayLiteral | HashLiteral | Heredoc | Lambda | QSymbols | QWords | Symbols | Words
|
1081
|
+
true
|
1082
|
+
in Call[receiver:]
|
1083
|
+
skip_indent?(receiver)
|
1084
|
+
in DynaSymbol[quote:]
|
1085
|
+
quote.start_with?("%s")
|
1086
|
+
else
|
1087
|
+
false
|
1088
|
+
end
|
1056
1089
|
end
|
1057
1090
|
end
|
1058
1091
|
|
@@ -1552,12 +1585,12 @@ module SyntaxTree
|
|
1552
1585
|
q.text(" ") unless power
|
1553
1586
|
|
1554
1587
|
if operator == :<<
|
1555
|
-
q.text(operator)
|
1588
|
+
q.text(operator.to_s)
|
1556
1589
|
q.text(" ")
|
1557
1590
|
q.format(right)
|
1558
1591
|
else
|
1559
1592
|
q.group do
|
1560
|
-
q.text(operator)
|
1593
|
+
q.text(operator.to_s)
|
1561
1594
|
|
1562
1595
|
q.indent do
|
1563
1596
|
q.breakable(power ? "" : " ")
|
@@ -2042,12 +2075,12 @@ module SyntaxTree
|
|
2042
2075
|
end
|
2043
2076
|
end
|
2044
2077
|
|
2045
|
-
# Formats either a Break or
|
2078
|
+
# Formats either a Break, Next, or Return node.
|
2046
2079
|
class FlowControlFormatter
|
2047
2080
|
# [String] the keyword to print
|
2048
2081
|
attr_reader :keyword
|
2049
2082
|
|
2050
|
-
# [Break | Next] the node being formatted
|
2083
|
+
# [Break | Next | Return] the node being formatted
|
2051
2084
|
attr_reader :node
|
2052
2085
|
|
2053
2086
|
def initialize(keyword, node)
|
@@ -2056,32 +2089,119 @@ module SyntaxTree
|
|
2056
2089
|
end
|
2057
2090
|
|
2058
2091
|
def format(q)
|
2059
|
-
arguments = node.arguments
|
2060
|
-
|
2061
2092
|
q.group do
|
2062
2093
|
q.text(keyword)
|
2063
2094
|
|
2064
|
-
|
2065
|
-
|
2066
|
-
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2072
|
-
|
2073
|
-
|
2074
|
-
|
2075
|
-
|
2076
|
-
|
2077
|
-
|
2078
|
-
|
2095
|
+
case node.arguments.parts
|
2096
|
+
in []
|
2097
|
+
# Here there are no arguments at all, so we're not going to print
|
2098
|
+
# anything. This would be like if we had:
|
2099
|
+
#
|
2100
|
+
# break
|
2101
|
+
#
|
2102
|
+
in [Paren[contents: { body: [ArrayLiteral[contents: { parts: [_, _, *] }] => array] }]]
|
2103
|
+
# Here we have a single argument that is a set of parentheses wrapping
|
2104
|
+
# an array literal that has at least 2 elements. We're going to print
|
2105
|
+
# the contents of the array directly. This would be like if we had:
|
2106
|
+
#
|
2107
|
+
# break([1, 2, 3])
|
2108
|
+
#
|
2109
|
+
# which we will print as:
|
2110
|
+
#
|
2111
|
+
# break 1, 2, 3
|
2112
|
+
#
|
2113
|
+
q.text(" ")
|
2114
|
+
format_array_contents(q, array)
|
2115
|
+
in [Paren[contents: { body: [ArrayLiteral => statement] }]]
|
2116
|
+
# Here we have a single argument that is a set of parentheses wrapping
|
2117
|
+
# an array literal that has 0 or 1 elements. We're going to skip the
|
2118
|
+
# parentheses but print the array itself. This would be like if we
|
2119
|
+
# had:
|
2120
|
+
#
|
2121
|
+
# break([1])
|
2122
|
+
#
|
2123
|
+
# which we will print as:
|
2124
|
+
#
|
2125
|
+
# break [1]
|
2126
|
+
#
|
2127
|
+
q.text(" ")
|
2128
|
+
q.format(statement)
|
2129
|
+
in [Paren[contents: { body: [statement] }]] if skip_parens?(statement)
|
2130
|
+
# Here we have a single argument that is a set of parentheses that
|
2131
|
+
# themselves contain a single statement. That statement is a simple
|
2132
|
+
# value that we can skip the parentheses for. This would be like if we
|
2133
|
+
# had:
|
2134
|
+
#
|
2135
|
+
# break(1)
|
2136
|
+
#
|
2137
|
+
# which we will print as:
|
2138
|
+
#
|
2139
|
+
# break 1
|
2140
|
+
#
|
2141
|
+
q.text(" ")
|
2142
|
+
q.format(statement)
|
2143
|
+
in [Paren => part]
|
2144
|
+
# Here we have a single argument that is a set of parentheses. We're
|
2145
|
+
# going to print the parentheses themselves as if they were the set of
|
2146
|
+
# arguments. This would be like if we had:
|
2147
|
+
#
|
2148
|
+
# break(foo.bar)
|
2149
|
+
#
|
2150
|
+
q.format(part)
|
2151
|
+
in [ArrayLiteral[contents: { parts: [_, _, *] }] => array]
|
2152
|
+
# Here there is a single argument that is an array literal with at
|
2153
|
+
# least two elements. We skip directly into the array literal's
|
2154
|
+
# elements in order to print the contents. This would be like if we
|
2155
|
+
# had:
|
2156
|
+
#
|
2157
|
+
# break [1, 2, 3]
|
2158
|
+
#
|
2159
|
+
# which we will print as:
|
2160
|
+
#
|
2161
|
+
# break 1, 2, 3
|
2162
|
+
#
|
2163
|
+
q.text(" ")
|
2164
|
+
format_array_contents(q, array)
|
2165
|
+
in [ArrayLiteral => part]
|
2166
|
+
# Here there is a single argument that is an array literal with 0 or 1
|
2167
|
+
# elements. In this case we're going to print the array as it is
|
2168
|
+
# because skipping the brackets would change the remaining. This would
|
2169
|
+
# be like if we had:
|
2170
|
+
#
|
2171
|
+
# break []
|
2172
|
+
# break [1]
|
2173
|
+
#
|
2174
|
+
q.text(" ")
|
2175
|
+
q.format(part)
|
2176
|
+
in [_]
|
2177
|
+
# Here there is a single argument that hasn't matched one of our
|
2178
|
+
# previous cases. We're going to print the argument as it is. This
|
2179
|
+
# would be like if we had:
|
2180
|
+
#
|
2181
|
+
# break foo
|
2182
|
+
#
|
2183
|
+
format_arguments(q, "(", ")")
|
2184
|
+
else
|
2185
|
+
# If there are multiple arguments, format them all. If the line is
|
2186
|
+
# going to break into multiple, then use brackets to start and end the
|
2187
|
+
# expression.
|
2188
|
+
format_arguments(q, " [", "]")
|
2079
2189
|
end
|
2080
2190
|
end
|
2081
2191
|
end
|
2082
2192
|
|
2083
2193
|
private
|
2084
2194
|
|
2195
|
+
def format_array_contents(q, array)
|
2196
|
+
q.if_break { q.text("[") }
|
2197
|
+
q.indent do
|
2198
|
+
q.breakable("")
|
2199
|
+
q.format(array.contents)
|
2200
|
+
end
|
2201
|
+
q.breakable("")
|
2202
|
+
q.if_break { q.text("]") }
|
2203
|
+
end
|
2204
|
+
|
2085
2205
|
def format_arguments(q, opening, closing)
|
2086
2206
|
q.if_break { q.text(opening) }
|
2087
2207
|
q.indent do
|
@@ -2091,6 +2211,17 @@ module SyntaxTree
|
|
2091
2211
|
q.breakable("")
|
2092
2212
|
q.if_break { q.text(closing) }
|
2093
2213
|
end
|
2214
|
+
|
2215
|
+
def skip_parens?(node)
|
2216
|
+
case node
|
2217
|
+
in FloatLiteral | Imaginary | Int | RationalLiteral
|
2218
|
+
true
|
2219
|
+
in VarRef[value: Const | CVar | GVar | IVar | Kw]
|
2220
|
+
true
|
2221
|
+
else
|
2222
|
+
false
|
2223
|
+
end
|
2224
|
+
end
|
2094
2225
|
end
|
2095
2226
|
|
2096
2227
|
# Break represents using the +break+ keyword.
|
@@ -2156,6 +2287,181 @@ module SyntaxTree
|
|
2156
2287
|
end
|
2157
2288
|
end
|
2158
2289
|
|
2290
|
+
class CallChainFormatter
|
2291
|
+
# [Call | MethodAddBlock] the top of the call chain
|
2292
|
+
attr_reader :node
|
2293
|
+
|
2294
|
+
def initialize(node)
|
2295
|
+
@node = node
|
2296
|
+
end
|
2297
|
+
|
2298
|
+
def format(q)
|
2299
|
+
children = [node]
|
2300
|
+
threshold = 3
|
2301
|
+
|
2302
|
+
# First, walk down the chain until we get to the point where we're not
|
2303
|
+
# longer at a chainable node.
|
2304
|
+
while true
|
2305
|
+
case children.last
|
2306
|
+
in Call[receiver: Call]
|
2307
|
+
children << children.last.receiver
|
2308
|
+
in Call[receiver: MethodAddBlock[call: Call]]
|
2309
|
+
children << children.last.receiver
|
2310
|
+
in MethodAddBlock[call: Call]
|
2311
|
+
children << children.last.call
|
2312
|
+
else
|
2313
|
+
break
|
2314
|
+
end
|
2315
|
+
end
|
2316
|
+
|
2317
|
+
# Here, we have very specialized behavior where if we're within a sig
|
2318
|
+
# block, then we're going to assume we're creating a Sorbet type
|
2319
|
+
# signature. In that case, we really want the threshold to be lowered so
|
2320
|
+
# that we create method chains off of any two method calls within the
|
2321
|
+
# block. For more details, see
|
2322
|
+
# https://github.com/prettier/plugin-ruby/issues/863.
|
2323
|
+
parents = q.parents.take(4)
|
2324
|
+
if parent = parents[2]
|
2325
|
+
# If we're at a do_block, then we want to go one more level up. This is
|
2326
|
+
# because do blocks have BodyStmt nodes instead of just Statements
|
2327
|
+
# nodes.
|
2328
|
+
parent = parents[3] if parent.is_a?(DoBlock)
|
2329
|
+
|
2330
|
+
case parent
|
2331
|
+
in MethodAddBlock[call: FCall[value: { value: "sig" }]]
|
2332
|
+
threshold = 2
|
2333
|
+
else
|
2334
|
+
end
|
2335
|
+
end
|
2336
|
+
|
2337
|
+
if children.length >= threshold
|
2338
|
+
q.group { q.if_break { format_chain(q, children) }.if_flat { node.format_contents(q) } }
|
2339
|
+
else
|
2340
|
+
node.format_contents(q)
|
2341
|
+
end
|
2342
|
+
end
|
2343
|
+
|
2344
|
+
def format_chain(q, children)
|
2345
|
+
# We're going to have some specialized behavior for if it's an entire
|
2346
|
+
# chain of calls without arguments except for the last one. This is common
|
2347
|
+
# enough in Ruby source code that it's worth the extra complexity here.
|
2348
|
+
empty_except_last =
|
2349
|
+
children.drop(1).all? do |child|
|
2350
|
+
child.is_a?(Call) && child.arguments.nil?
|
2351
|
+
end
|
2352
|
+
|
2353
|
+
# Here, we're going to add all of the children onto the stack of the
|
2354
|
+
# formatter so it's as if we had descending normally into them. This is
|
2355
|
+
# necessary so they can check their parents as normal.
|
2356
|
+
q.stack.concat(children)
|
2357
|
+
q.format(children.last.receiver)
|
2358
|
+
|
2359
|
+
q.group do
|
2360
|
+
if attach_directly?(children.last)
|
2361
|
+
format_child(q, children.pop)
|
2362
|
+
q.stack.pop
|
2363
|
+
end
|
2364
|
+
|
2365
|
+
q.indent do
|
2366
|
+
# We track another variable that checks if you need to move the
|
2367
|
+
# operator to the previous line in case there are trailing comments
|
2368
|
+
# and a trailing operator.
|
2369
|
+
skip_operator = false
|
2370
|
+
|
2371
|
+
while child = children.pop
|
2372
|
+
case child
|
2373
|
+
in Call[receiver: Call[message: { value: "where" }], message: { value: "not" }]
|
2374
|
+
# This is very specialized behavior wherein we group
|
2375
|
+
# .where.not calls together because it looks better. For more
|
2376
|
+
# information, see
|
2377
|
+
# https://github.com/prettier/plugin-ruby/issues/862.
|
2378
|
+
in Call
|
2379
|
+
# If we're at a Call node and not a MethodAddBlock node in the
|
2380
|
+
# chain then we're going to add a newline so it indents properly.
|
2381
|
+
q.breakable("")
|
2382
|
+
else
|
2383
|
+
end
|
2384
|
+
|
2385
|
+
format_child(
|
2386
|
+
q,
|
2387
|
+
child,
|
2388
|
+
skip_comments: children.empty?,
|
2389
|
+
skip_operator: skip_operator,
|
2390
|
+
skip_attached: empty_except_last && children.empty?
|
2391
|
+
)
|
2392
|
+
|
2393
|
+
# If the parent call node has a comment on the message then we need
|
2394
|
+
# to print the operator trailing in order to keep it working.
|
2395
|
+
case children.last
|
2396
|
+
in Call[message: { comments: [_, *] }, operator:]
|
2397
|
+
q.format(CallOperatorFormatter.new(operator))
|
2398
|
+
skip_operator = true
|
2399
|
+
else
|
2400
|
+
skip_operator = false
|
2401
|
+
end
|
2402
|
+
|
2403
|
+
# Pop off the formatter's stack so that it aligns with what would
|
2404
|
+
# have happened if we had been formatting normally.
|
2405
|
+
q.stack.pop
|
2406
|
+
end
|
2407
|
+
end
|
2408
|
+
end
|
2409
|
+
|
2410
|
+
if empty_except_last
|
2411
|
+
case node
|
2412
|
+
in Call
|
2413
|
+
node.format_arguments(q)
|
2414
|
+
in MethodAddBlock[block:]
|
2415
|
+
q.format(block)
|
2416
|
+
end
|
2417
|
+
end
|
2418
|
+
end
|
2419
|
+
|
2420
|
+
def self.chained?(node)
|
2421
|
+
case node
|
2422
|
+
in Call | MethodAddBlock[call: Call]
|
2423
|
+
true
|
2424
|
+
else
|
2425
|
+
false
|
2426
|
+
end
|
2427
|
+
end
|
2428
|
+
|
2429
|
+
private
|
2430
|
+
|
2431
|
+
# For certain nodes, we want to attach directly to the end and don't
|
2432
|
+
# want to indent the first call. So we'll pop off the first children and
|
2433
|
+
# format it separately here.
|
2434
|
+
def attach_directly?(node)
|
2435
|
+
[ArrayLiteral, HashLiteral, Heredoc, If, Unless, XStringLiteral]
|
2436
|
+
.include?(node.receiver.class)
|
2437
|
+
end
|
2438
|
+
|
2439
|
+
def format_child(q, child, skip_comments: false, skip_operator: false, skip_attached: false)
|
2440
|
+
# First, format the actual contents of the child.
|
2441
|
+
case child
|
2442
|
+
in Call
|
2443
|
+
q.group do
|
2444
|
+
q.format(CallOperatorFormatter.new(child.operator)) unless skip_operator
|
2445
|
+
q.format(child.message) if child.message != :call
|
2446
|
+
child.format_arguments(q) unless skip_attached
|
2447
|
+
end
|
2448
|
+
in MethodAddBlock
|
2449
|
+
q.format(child.block) unless skip_attached
|
2450
|
+
end
|
2451
|
+
|
2452
|
+
# If there are any comments on this node then we need to explicitly print
|
2453
|
+
# them out here since we're bypassing the normal comment printing.
|
2454
|
+
if child.comments.any? && !skip_comments
|
2455
|
+
child.comments.each do |comment|
|
2456
|
+
comment.inline? ? q.text(" ") : q.breakable
|
2457
|
+
comment.format(q)
|
2458
|
+
end
|
2459
|
+
|
2460
|
+
q.break_parent
|
2461
|
+
end
|
2462
|
+
end
|
2463
|
+
end
|
2464
|
+
|
2159
2465
|
# Call represents a method call.
|
2160
2466
|
#
|
2161
2467
|
# receiver.message
|
@@ -2219,6 +2525,29 @@ module SyntaxTree
|
|
2219
2525
|
end
|
2220
2526
|
|
2221
2527
|
def format(q)
|
2528
|
+
# If we're at the top of a call chain, then we're going to do some
|
2529
|
+
# specialized printing in case we can print it nicely. We _only_ do this
|
2530
|
+
# at the top of the chain to avoid weird recursion issues.
|
2531
|
+
if !CallChainFormatter.chained?(q.parent) && CallChainFormatter.chained?(receiver)
|
2532
|
+
q.group { q.if_break { CallChainFormatter.new(self).format(q) }.if_flat { format_contents(q) } }
|
2533
|
+
else
|
2534
|
+
format_contents(q)
|
2535
|
+
end
|
2536
|
+
end
|
2537
|
+
|
2538
|
+
def format_arguments(q)
|
2539
|
+
case arguments
|
2540
|
+
in ArgParen
|
2541
|
+
q.format(arguments)
|
2542
|
+
in Args
|
2543
|
+
q.text(" ")
|
2544
|
+
q.format(arguments)
|
2545
|
+
else
|
2546
|
+
# Do nothing if there are no arguments.
|
2547
|
+
end
|
2548
|
+
end
|
2549
|
+
|
2550
|
+
def format_contents(q)
|
2222
2551
|
call_operator = CallOperatorFormatter.new(operator)
|
2223
2552
|
|
2224
2553
|
q.group do
|
@@ -2241,7 +2570,7 @@ module SyntaxTree
|
|
2241
2570
|
q.format(message) if message != :call
|
2242
2571
|
end
|
2243
2572
|
|
2244
|
-
q
|
2573
|
+
format_arguments(q)
|
2245
2574
|
end
|
2246
2575
|
end
|
2247
2576
|
end
|
@@ -2507,7 +2836,7 @@ module SyntaxTree
|
|
2507
2836
|
end
|
2508
2837
|
|
2509
2838
|
alias deconstruct child_nodes
|
2510
|
-
|
2839
|
+
|
2511
2840
|
def deconstruct_keys(keys)
|
2512
2841
|
{ value: value, location: location }
|
2513
2842
|
end
|
@@ -2558,26 +2887,24 @@ module SyntaxTree
|
|
2558
2887
|
def format(q)
|
2559
2888
|
q.group do
|
2560
2889
|
q.format(message)
|
2561
|
-
q.
|
2562
|
-
|
2563
|
-
if align?(self)
|
2564
|
-
q.nest(message.value.length + 1) { q.format(arguments) }
|
2565
|
-
else
|
2566
|
-
q.format(arguments)
|
2567
|
-
end
|
2890
|
+
align(q, self) { q.format(arguments) }
|
2568
2891
|
end
|
2569
2892
|
end
|
2570
2893
|
|
2571
2894
|
private
|
2572
2895
|
|
2573
|
-
def align
|
2896
|
+
def align(q, node, &block)
|
2574
2897
|
case node.arguments
|
2575
2898
|
in Args[parts: [Def | Defs | DefEndless]]
|
2576
|
-
|
2899
|
+
q.text(" ")
|
2900
|
+
yield
|
2901
|
+
in Args[parts: [IfOp]]
|
2902
|
+
yield
|
2577
2903
|
in Args[parts: [Command => command]]
|
2578
|
-
align
|
2904
|
+
align(q, command, &block)
|
2579
2905
|
else
|
2580
|
-
|
2906
|
+
q.text(" ")
|
2907
|
+
q.nest(message.value.length + 1) { yield }
|
2581
2908
|
end
|
2582
2909
|
end
|
2583
2910
|
end
|
@@ -2649,9 +2976,15 @@ module SyntaxTree
|
|
2649
2976
|
q.format(message)
|
2650
2977
|
end
|
2651
2978
|
|
2652
|
-
|
2979
|
+
case arguments
|
2980
|
+
in Args[parts: [IfOp]]
|
2981
|
+
q.if_flat { q.text(" ") }
|
2982
|
+
q.format(arguments)
|
2983
|
+
in Args
|
2653
2984
|
q.text(" ")
|
2654
2985
|
q.nest(argument_alignment(q, doc)) { q.format(arguments) }
|
2986
|
+
else
|
2987
|
+
# If there are no arguments, print nothing.
|
2655
2988
|
end
|
2656
2989
|
end
|
2657
2990
|
end
|
@@ -3037,7 +3370,10 @@ module SyntaxTree
|
|
3037
3370
|
q.group do
|
3038
3371
|
q.text("def ")
|
3039
3372
|
q.format(name)
|
3040
|
-
|
3373
|
+
|
3374
|
+
if !params.is_a?(Params) || !params.empty? || params.comments.any?
|
3375
|
+
q.format(params)
|
3376
|
+
end
|
3041
3377
|
end
|
3042
3378
|
|
3043
3379
|
unless bodystmt.empty?
|
@@ -3257,7 +3593,10 @@ module SyntaxTree
|
|
3257
3593
|
q.format(target)
|
3258
3594
|
q.format(CallOperatorFormatter.new(operator), stackable: false)
|
3259
3595
|
q.format(name)
|
3260
|
-
|
3596
|
+
|
3597
|
+
if !params.is_a?(Params) || !params.empty? || params.comments.any?
|
3598
|
+
q.format(params)
|
3599
|
+
end
|
3261
3600
|
end
|
3262
3601
|
|
3263
3602
|
unless bodystmt.empty?
|
@@ -3792,7 +4131,7 @@ module SyntaxTree
|
|
3792
4131
|
end
|
3793
4132
|
|
3794
4133
|
alias deconstruct child_nodes
|
3795
|
-
|
4134
|
+
|
3796
4135
|
def deconstruct_keys(keys)
|
3797
4136
|
{ value: value, location: location }
|
3798
4137
|
end
|
@@ -3822,7 +4161,7 @@ module SyntaxTree
|
|
3822
4161
|
end
|
3823
4162
|
|
3824
4163
|
alias deconstruct child_nodes
|
3825
|
-
|
4164
|
+
|
3826
4165
|
def deconstruct_keys(keys)
|
3827
4166
|
{ value: value, location: location }
|
3828
4167
|
end
|
@@ -3854,7 +4193,7 @@ module SyntaxTree
|
|
3854
4193
|
end
|
3855
4194
|
|
3856
4195
|
alias deconstruct child_nodes
|
3857
|
-
|
4196
|
+
|
3858
4197
|
def deconstruct_keys(keys)
|
3859
4198
|
{ value: value, location: location }
|
3860
4199
|
end
|
@@ -4002,7 +4341,15 @@ module SyntaxTree
|
|
4002
4341
|
|
4003
4342
|
def format(q)
|
4004
4343
|
q.format(value)
|
4005
|
-
|
4344
|
+
|
4345
|
+
if arguments.is_a?(ArgParen) && arguments.arguments.nil? && !value.is_a?(Const)
|
4346
|
+
# If you're using an explicit set of parentheses on something that looks
|
4347
|
+
# like a constant, then we need to match that in order to maintain valid
|
4348
|
+
# Ruby. For example, you could do something like Foo(), on which we
|
4349
|
+
# would need to keep the parentheses to make it look like a method call.
|
4350
|
+
else
|
4351
|
+
q.format(arguments)
|
4352
|
+
end
|
4006
4353
|
end
|
4007
4354
|
end
|
4008
4355
|
|
@@ -4275,6 +4622,29 @@ module SyntaxTree
|
|
4275
4622
|
# { key => value }
|
4276
4623
|
#
|
4277
4624
|
class HashLiteral < Node
|
4625
|
+
class EmptyWithCommentsFormatter
|
4626
|
+
# [LBrace] the opening brace
|
4627
|
+
attr_reader :lbrace
|
4628
|
+
|
4629
|
+
def initialize(lbrace)
|
4630
|
+
@lbrace = lbrace
|
4631
|
+
end
|
4632
|
+
|
4633
|
+
def format(q)
|
4634
|
+
q.group do
|
4635
|
+
q.text("{")
|
4636
|
+
q.indent do
|
4637
|
+
lbrace.comments.each do |comment|
|
4638
|
+
q.breakable(force: true)
|
4639
|
+
comment.format(q)
|
4640
|
+
end
|
4641
|
+
end
|
4642
|
+
q.breakable(force: true)
|
4643
|
+
q.text("}")
|
4644
|
+
end
|
4645
|
+
end
|
4646
|
+
end
|
4647
|
+
|
4278
4648
|
# [LBrace] the left brace that opens this hash
|
4279
4649
|
attr_reader :lbrace
|
4280
4650
|
|
@@ -4319,7 +4689,18 @@ module SyntaxTree
|
|
4319
4689
|
|
4320
4690
|
private
|
4321
4691
|
|
4692
|
+
# If we have an empty hash that contains only comments, then we're going
|
4693
|
+
# to do some special printing to ensure they get indented correctly.
|
4694
|
+
def empty_with_comments?
|
4695
|
+
assocs.empty? && lbrace.comments.any? && lbrace.comments.none?(&:inline?)
|
4696
|
+
end
|
4697
|
+
|
4322
4698
|
def format_contents(q)
|
4699
|
+
if empty_with_comments?
|
4700
|
+
EmptyWithCommentsFormatter.new(lbrace).format(q)
|
4701
|
+
return
|
4702
|
+
end
|
4703
|
+
|
4323
4704
|
q.format(lbrace)
|
4324
4705
|
|
4325
4706
|
if assocs.empty?
|
@@ -4641,6 +5022,62 @@ module SyntaxTree
|
|
4641
5022
|
end
|
4642
5023
|
end
|
4643
5024
|
|
5025
|
+
# In order for an `if` or `unless` expression to be shortened to a ternary,
|
5026
|
+
# there has to be one and only one consequent clause which is an Else. Both
|
5027
|
+
# the body of the main node and the body of the Else node must have only one
|
5028
|
+
# statement, and that statement must not be on the denied list of potential
|
5029
|
+
# statements.
|
5030
|
+
module Ternaryable
|
5031
|
+
class << self
|
5032
|
+
def call(q, node)
|
5033
|
+
case q.parents.take(2)[1]
|
5034
|
+
in Paren[contents: Statements[body: [node]]]
|
5035
|
+
# If this is a conditional inside of a parentheses as the only
|
5036
|
+
# content, then we don't want to transform it into a ternary.
|
5037
|
+
# Presumably the user wanted it to be an explicit conditional because
|
5038
|
+
# there are parentheses around it. So we'll just leave it in place.
|
5039
|
+
false
|
5040
|
+
else
|
5041
|
+
# Otherwise, we're going to check the conditional for certain cases.
|
5042
|
+
case node
|
5043
|
+
in { predicate: Assign | Command | CommandCall | MAssign | OpAssign }
|
5044
|
+
false
|
5045
|
+
in { statements: { body: [truthy] }, consequent: Else[statements: { body: [falsy] }] }
|
5046
|
+
ternaryable?(truthy) && ternaryable?(falsy)
|
5047
|
+
else
|
5048
|
+
false
|
5049
|
+
end
|
5050
|
+
end
|
5051
|
+
end
|
5052
|
+
|
5053
|
+
private
|
5054
|
+
|
5055
|
+
# Certain expressions cannot be reduced to a ternary without adding
|
5056
|
+
# parentheses around them. In this case we say they cannot be ternaried and
|
5057
|
+
# default instead to breaking them into multiple lines.
|
5058
|
+
def ternaryable?(statement)
|
5059
|
+
# This is a list of nodes that should not be allowed to be a part of a
|
5060
|
+
# ternary clause.
|
5061
|
+
no_ternary = [
|
5062
|
+
Alias, Assign, Break, Command, CommandCall, Heredoc, If, IfMod, IfOp,
|
5063
|
+
Lambda, MAssign, Next, OpAssign, RescueMod, Return, Return0, Super,
|
5064
|
+
Undef, Unless, UnlessMod, Until, UntilMod, VarAlias, VoidStmt, While,
|
5065
|
+
WhileMod, Yield, Yield0, ZSuper
|
5066
|
+
]
|
5067
|
+
|
5068
|
+
# Here we're going to check that the only statement inside the
|
5069
|
+
# statements node is no a part of our denied list of nodes that can be
|
5070
|
+
# ternaries.
|
5071
|
+
#
|
5072
|
+
# If the user is using one of the lower precedence "and" or "or"
|
5073
|
+
# operators, then we can't use a ternary expression as it would break
|
5074
|
+
# the flow control.
|
5075
|
+
!no_ternary.include?(statement.class) &&
|
5076
|
+
!(statement.is_a?(Binary) && %i[and or].include?(statement.operator))
|
5077
|
+
end
|
5078
|
+
end
|
5079
|
+
end
|
5080
|
+
|
4644
5081
|
# Formats an If or Unless node.
|
4645
5082
|
class ConditionalFormatter
|
4646
5083
|
# [String] the keyword associated with this conditional
|
@@ -4655,6 +5092,13 @@ module SyntaxTree
|
|
4655
5092
|
end
|
4656
5093
|
|
4657
5094
|
def format(q)
|
5095
|
+
# If we can transform this node into a ternary, then we're going to print
|
5096
|
+
# a special version that uses the ternary operator if it fits on one line.
|
5097
|
+
if Ternaryable.call(q, node)
|
5098
|
+
format_ternary(q)
|
5099
|
+
return
|
5100
|
+
end
|
5101
|
+
|
4658
5102
|
# If the predicate of the conditional contains an assignment (in which
|
4659
5103
|
# case we can't know for certain that that assignment doesn't impact the
|
4660
5104
|
# statements inside the conditional) then we can't use the modifier form
|
@@ -4664,7 +5108,7 @@ module SyntaxTree
|
|
4664
5108
|
return
|
4665
5109
|
end
|
4666
5110
|
|
4667
|
-
if node.consequent || node.statements.empty?
|
5111
|
+
if node.consequent || node.statements.empty? || contains_conditional?
|
4668
5112
|
q.group { format_break(q, force: true) }
|
4669
5113
|
else
|
4670
5114
|
q.group do
|
@@ -4700,6 +5144,59 @@ module SyntaxTree
|
|
4700
5144
|
q.breakable(force: force)
|
4701
5145
|
q.text("end")
|
4702
5146
|
end
|
5147
|
+
|
5148
|
+
def format_ternary(q)
|
5149
|
+
q.group do
|
5150
|
+
q.if_break do
|
5151
|
+
q.text("#{keyword} ")
|
5152
|
+
q.nest(keyword.length + 1) { q.format(node.predicate) }
|
5153
|
+
|
5154
|
+
q.indent do
|
5155
|
+
q.breakable
|
5156
|
+
q.format(node.statements)
|
5157
|
+
end
|
5158
|
+
|
5159
|
+
q.breakable
|
5160
|
+
q.group do
|
5161
|
+
q.format(node.consequent.keyword)
|
5162
|
+
q.indent do
|
5163
|
+
# This is a very special case of breakable where we want to force
|
5164
|
+
# it into the output but we _don't_ want to explicitly break the
|
5165
|
+
# parent. If a break-parent shows up in the tree, then it's going
|
5166
|
+
# to force it all the way up to the tree, which is going to negate
|
5167
|
+
# the ternary. Maybe this should be an option in prettyprint? As
|
5168
|
+
# in force: :no_break_parent or something.
|
5169
|
+
q.target << PrettyPrint::Breakable.new(" ", 1, force: true)
|
5170
|
+
q.format(node.consequent.statements)
|
5171
|
+
end
|
5172
|
+
end
|
5173
|
+
|
5174
|
+
q.breakable
|
5175
|
+
q.text("end")
|
5176
|
+
end.if_flat do
|
5177
|
+
Parentheses.flat(q) do
|
5178
|
+
q.format(node.predicate)
|
5179
|
+
q.text(" ? ")
|
5180
|
+
|
5181
|
+
statements = [node.statements, node.consequent.statements]
|
5182
|
+
statements.reverse! if keyword == "unless"
|
5183
|
+
|
5184
|
+
q.format(statements[0])
|
5185
|
+
q.text(" : ")
|
5186
|
+
q.format(statements[1])
|
5187
|
+
end
|
5188
|
+
end
|
5189
|
+
end
|
5190
|
+
end
|
5191
|
+
|
5192
|
+
def contains_conditional?
|
5193
|
+
case node
|
5194
|
+
in { statements: { body: [If | IfMod | IfOp | Unless | UnlessMod] } }
|
5195
|
+
true
|
5196
|
+
else
|
5197
|
+
false
|
5198
|
+
end
|
5199
|
+
end
|
4703
5200
|
end
|
4704
5201
|
|
4705
5202
|
# If represents the first clause in an +if+ chain.
|
@@ -4812,7 +5309,7 @@ module SyntaxTree
|
|
4812
5309
|
Yield0, ZSuper
|
4813
5310
|
]
|
4814
5311
|
|
4815
|
-
if force_flat.include?(truthy.class) || force_flat.include?(falsy.class)
|
5312
|
+
if q.parent.is_a?(Paren) || force_flat.include?(truthy.class) || force_flat.include?(falsy.class)
|
4816
5313
|
q.group { format_flat(q) }
|
4817
5314
|
return
|
4818
5315
|
end
|
@@ -5285,7 +5782,7 @@ module SyntaxTree
|
|
5285
5782
|
end
|
5286
5783
|
|
5287
5784
|
alias deconstruct child_nodes
|
5288
|
-
|
5785
|
+
|
5289
5786
|
def deconstruct_keys(keys)
|
5290
5787
|
{ value: value, location: location }
|
5291
5788
|
end
|
@@ -5559,6 +6056,17 @@ module SyntaxTree
|
|
5559
6056
|
end
|
5560
6057
|
|
5561
6058
|
def format(q)
|
6059
|
+
# If we're at the top of a call chain, then we're going to do some
|
6060
|
+
# specialized printing in case we can print it nicely. We _only_ do this
|
6061
|
+
# at the top of the chain to avoid weird recursion issues.
|
6062
|
+
if !CallChainFormatter.chained?(q.parent) && CallChainFormatter.chained?(call)
|
6063
|
+
q.group { q.if_break { CallChainFormatter.new(self).format(q) }.if_flat { format_contents(q) } }
|
6064
|
+
else
|
6065
|
+
format_contents(q)
|
6066
|
+
end
|
6067
|
+
end
|
6068
|
+
|
6069
|
+
def format_contents(q)
|
5562
6070
|
q.format(call)
|
5563
6071
|
q.format(block)
|
5564
6072
|
end
|
@@ -6180,7 +6688,9 @@ module SyntaxTree
|
|
6180
6688
|
q.format(rest) if rest && rest.is_a?(ExcessedComma)
|
6181
6689
|
end
|
6182
6690
|
|
6183
|
-
if [Def, Defs, DefEndless].include?(q.parent.class)
|
6691
|
+
if ![Def, Defs, DefEndless].include?(q.parent.class) || parts.empty?
|
6692
|
+
q.nest(0, &contents)
|
6693
|
+
else
|
6184
6694
|
q.group(0, "(", ")") do
|
6185
6695
|
q.indent do
|
6186
6696
|
q.breakable("")
|
@@ -6188,8 +6698,6 @@ module SyntaxTree
|
|
6188
6698
|
end
|
6189
6699
|
q.breakable("")
|
6190
6700
|
end
|
6191
|
-
else
|
6192
|
-
q.nest(0, &contents)
|
6193
6701
|
end
|
6194
6702
|
end
|
6195
6703
|
end
|
@@ -6410,7 +6918,7 @@ module SyntaxTree
|
|
6410
6918
|
end
|
6411
6919
|
|
6412
6920
|
alias deconstruct child_nodes
|
6413
|
-
|
6921
|
+
|
6414
6922
|
def deconstruct_keys(keys)
|
6415
6923
|
{ value: value, location: location }
|
6416
6924
|
end
|
@@ -6510,7 +7018,7 @@ module SyntaxTree
|
|
6510
7018
|
end
|
6511
7019
|
|
6512
7020
|
alias deconstruct child_nodes
|
6513
|
-
|
7021
|
+
|
6514
7022
|
def deconstruct_keys(keys)
|
6515
7023
|
{ value: value, location: location }
|
6516
7024
|
end
|
@@ -6571,7 +7079,7 @@ module SyntaxTree
|
|
6571
7079
|
end
|
6572
7080
|
|
6573
7081
|
alias deconstruct child_nodes
|
6574
|
-
|
7082
|
+
|
6575
7083
|
def deconstruct_keys(keys)
|
6576
7084
|
{ value: value, location: location }
|
6577
7085
|
end
|
@@ -6596,7 +7104,7 @@ module SyntaxTree
|
|
6596
7104
|
end
|
6597
7105
|
|
6598
7106
|
alias deconstruct child_nodes
|
6599
|
-
|
7107
|
+
|
6600
7108
|
def deconstruct_keys(keys)
|
6601
7109
|
{ value: value, location: location }
|
6602
7110
|
end
|
@@ -6667,7 +7175,7 @@ module SyntaxTree
|
|
6667
7175
|
end
|
6668
7176
|
|
6669
7177
|
alias deconstruct child_nodes
|
6670
|
-
|
7178
|
+
|
6671
7179
|
def deconstruct_keys(keys)
|
6672
7180
|
{ beginning: beginning, parts: parts, location: location }
|
6673
7181
|
end
|
@@ -6700,7 +7208,7 @@ module SyntaxTree
|
|
6700
7208
|
end
|
6701
7209
|
|
6702
7210
|
alias deconstruct child_nodes
|
6703
|
-
|
7211
|
+
|
6704
7212
|
def deconstruct_keys(keys)
|
6705
7213
|
{ value: value, location: location }
|
6706
7214
|
end
|
@@ -6734,7 +7242,7 @@ module SyntaxTree
|
|
6734
7242
|
end
|
6735
7243
|
|
6736
7244
|
alias deconstruct child_nodes
|
6737
|
-
|
7245
|
+
|
6738
7246
|
def deconstruct_keys(keys)
|
6739
7247
|
{ value: value, location: location }
|
6740
7248
|
end
|
@@ -7220,7 +7728,7 @@ module SyntaxTree
|
|
7220
7728
|
@value = value
|
7221
7729
|
@location = location
|
7222
7730
|
end
|
7223
|
-
|
7731
|
+
|
7224
7732
|
def accept(visitor)
|
7225
7733
|
visitor.visit_rparen(self)
|
7226
7734
|
end
|
@@ -7230,7 +7738,7 @@ module SyntaxTree
|
|
7230
7738
|
end
|
7231
7739
|
|
7232
7740
|
alias deconstruct child_nodes
|
7233
|
-
|
7741
|
+
|
7234
7742
|
def deconstruct_keys(keys)
|
7235
7743
|
{ value: value, location: location }
|
7236
7744
|
end
|
@@ -7442,19 +7950,20 @@ module SyntaxTree
|
|
7442
7950
|
comment = parser_comments[comment_index]
|
7443
7951
|
location = comment.location
|
7444
7952
|
|
7445
|
-
if !comment.inline? && (start_char <= location.start_char) &&
|
7446
|
-
|
7447
|
-
parser_comments.delete_at(comment_index)
|
7448
|
-
|
7449
|
-
while (node = body[body_index]) &&
|
7450
|
-
(
|
7451
|
-
node.is_a?(VoidStmt) ||
|
7452
|
-
node.location.start_char < location.start_char
|
7453
|
-
)
|
7953
|
+
if !comment.inline? && (start_char <= location.start_char) && (end_char >= location.end_char) && !comment.ignore?
|
7954
|
+
while (node = body[body_index]) && (node.is_a?(VoidStmt) || node.location.start_char < location.start_char)
|
7454
7955
|
body_index += 1
|
7455
7956
|
end
|
7456
7957
|
|
7457
|
-
body.
|
7958
|
+
if body_index != 0 && body[body_index - 1].location.start_char < location.start_char && body[body_index - 1].location.end_char > location.start_char
|
7959
|
+
# The previous node entirely encapsules the comment, so we don't
|
7960
|
+
# want to attach it here since it will get attached normally. This
|
7961
|
+
# is mostly in the case of hash and array literals.
|
7962
|
+
comment_index += 1
|
7963
|
+
else
|
7964
|
+
parser_comments.delete_at(comment_index)
|
7965
|
+
body.insert(body_index, comment)
|
7966
|
+
end
|
7458
7967
|
else
|
7459
7968
|
comment_index += 1
|
7460
7969
|
end
|
@@ -7485,7 +7994,7 @@ module SyntaxTree
|
|
7485
7994
|
end
|
7486
7995
|
|
7487
7996
|
alias deconstruct child_nodes
|
7488
|
-
|
7997
|
+
|
7489
7998
|
def deconstruct_keys(keys)
|
7490
7999
|
{ parts: parts, location: location }
|
7491
8000
|
end
|
@@ -7782,7 +8291,7 @@ module SyntaxTree
|
|
7782
8291
|
end
|
7783
8292
|
|
7784
8293
|
alias deconstruct child_nodes
|
7785
|
-
|
8294
|
+
|
7786
8295
|
def deconstruct_keys(keys)
|
7787
8296
|
{ value: value, location: location }
|
7788
8297
|
end
|
@@ -7812,7 +8321,7 @@ module SyntaxTree
|
|
7812
8321
|
end
|
7813
8322
|
|
7814
8323
|
alias deconstruct child_nodes
|
7815
|
-
|
8324
|
+
|
7816
8325
|
def deconstruct_keys(keys)
|
7817
8326
|
{ value: value, location: location }
|
7818
8327
|
end
|
@@ -7943,7 +8452,7 @@ module SyntaxTree
|
|
7943
8452
|
end
|
7944
8453
|
|
7945
8454
|
alias deconstruct child_nodes
|
7946
|
-
|
8455
|
+
|
7947
8456
|
def deconstruct_keys(keys)
|
7948
8457
|
{ value: value, location: location }
|
7949
8458
|
end
|
@@ -7972,7 +8481,7 @@ module SyntaxTree
|
|
7972
8481
|
end
|
7973
8482
|
|
7974
8483
|
alias deconstruct child_nodes
|
7975
|
-
|
8484
|
+
|
7976
8485
|
def deconstruct_keys(keys)
|
7977
8486
|
{ value: value, location: location }
|
7978
8487
|
end
|
@@ -8002,7 +8511,7 @@ module SyntaxTree
|
|
8002
8511
|
end
|
8003
8512
|
|
8004
8513
|
alias deconstruct child_nodes
|
8005
|
-
|
8514
|
+
|
8006
8515
|
def deconstruct_keys(keys)
|
8007
8516
|
{ value: value, location: location }
|
8008
8517
|
end
|
@@ -8113,7 +8622,7 @@ module SyntaxTree
|
|
8113
8622
|
end
|
8114
8623
|
|
8115
8624
|
alias deconstruct child_nodes
|
8116
|
-
|
8625
|
+
|
8117
8626
|
def deconstruct_keys(keys)
|
8118
8627
|
{ value: value, location: location }
|
8119
8628
|
end
|
@@ -8191,7 +8700,7 @@ module SyntaxTree
|
|
8191
8700
|
end
|
8192
8701
|
|
8193
8702
|
alias deconstruct child_nodes
|
8194
|
-
|
8703
|
+
|
8195
8704
|
def deconstruct_keys(keys)
|
8196
8705
|
{ value: value, location: location }
|
8197
8706
|
end
|
@@ -8238,9 +8747,26 @@ module SyntaxTree
|
|
8238
8747
|
end
|
8239
8748
|
|
8240
8749
|
def format(q)
|
8241
|
-
q.
|
8750
|
+
parent = q.parents.take(2)[1]
|
8751
|
+
ternary = (parent.is_a?(If) || parent.is_a?(Unless)) && Ternaryable.call(q, parent)
|
8752
|
+
|
8753
|
+
q.text("not")
|
8754
|
+
|
8755
|
+
if parentheses
|
8756
|
+
q.text("(")
|
8757
|
+
elsif ternary
|
8758
|
+
q.if_break { q.text(" ") }.if_flat { q.text("(") }
|
8759
|
+
else
|
8760
|
+
q.text(" ")
|
8761
|
+
end
|
8762
|
+
|
8242
8763
|
q.format(statement) if statement
|
8243
|
-
|
8764
|
+
|
8765
|
+
if parentheses
|
8766
|
+
q.text(")")
|
8767
|
+
elsif ternary
|
8768
|
+
q.if_flat { q.text(")") }
|
8769
|
+
end
|
8244
8770
|
end
|
8245
8771
|
end
|
8246
8772
|
|
@@ -9216,7 +9742,7 @@ module SyntaxTree
|
|
9216
9742
|
end
|
9217
9743
|
|
9218
9744
|
alias deconstruct child_nodes
|
9219
|
-
|
9745
|
+
|
9220
9746
|
def deconstruct_keys(keys)
|
9221
9747
|
{ value: value, location: location }
|
9222
9748
|
end
|
@@ -9245,7 +9771,7 @@ module SyntaxTree
|
|
9245
9771
|
end
|
9246
9772
|
|
9247
9773
|
alias deconstruct child_nodes
|
9248
|
-
|
9774
|
+
|
9249
9775
|
def deconstruct_keys(keys)
|
9250
9776
|
{ parts: parts, location: location }
|
9251
9777
|
end
|
data/lib/syntax_tree/parser.rb
CHANGED
@@ -2079,12 +2079,16 @@ module SyntaxTree
|
|
2079
2079
|
keyword_rest,
|
2080
2080
|
block
|
2081
2081
|
)
|
2082
|
+
# This is to make it so that required keyword arguments
|
2083
|
+
# have a `nil` for the value instead of a `false`.
|
2084
|
+
keywords&.map! { |(key, value)| [key, value || nil] }
|
2085
|
+
|
2082
2086
|
parts = [
|
2083
2087
|
*requireds,
|
2084
2088
|
*optionals&.flatten(1),
|
2085
2089
|
rest,
|
2086
2090
|
*posts,
|
2087
|
-
*keywords&.
|
2091
|
+
*keywords&.flatten(1),
|
2088
2092
|
(keyword_rest if keyword_rest != :nil),
|
2089
2093
|
(block if block != :&)
|
2090
2094
|
].compact
|
@@ -79,7 +79,7 @@ class PrettyPrint
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def pretty_print(q)
|
82
|
-
q.group(2, "align([", "])") do
|
82
|
+
q.group(2, "align#{indent}([", "])") do
|
83
83
|
q.seplist(contents) { |content| q.pp(content) }
|
84
84
|
end
|
85
85
|
end
|
@@ -161,7 +161,7 @@ class PrettyPrint
|
|
161
161
|
end
|
162
162
|
|
163
163
|
def pretty_print(q)
|
164
|
-
q.group(2, "group([", "])") do
|
164
|
+
q.group(2, break? ? "breakGroup([" : "group([", "])") do
|
165
165
|
q.seplist(contents) { |content| q.pp(content) }
|
166
166
|
end
|
167
167
|
end
|
@@ -458,6 +458,10 @@ class PrettyPrint
|
|
458
458
|
IfBreakBuilder.new
|
459
459
|
end
|
460
460
|
|
461
|
+
# Also effectively unnecessary, but here for compatibility.
|
462
|
+
def if_flat
|
463
|
+
end
|
464
|
+
|
461
465
|
# A noop that immediately yields.
|
462
466
|
def indent
|
463
467
|
yield
|
@@ -759,9 +763,7 @@ class PrettyPrint
|
|
759
763
|
|
760
764
|
# This is a linear stack instead of a mutually recursive call defined on
|
761
765
|
# the individual doc nodes for efficiency.
|
762
|
-
while commands.
|
763
|
-
indent, mode, doc = commands.pop
|
764
|
-
|
766
|
+
while (indent, mode, doc = commands.pop)
|
765
767
|
case doc
|
766
768
|
when Text
|
767
769
|
doc.objects.each { |object| buffer << object }
|
@@ -789,10 +791,10 @@ class PrettyPrint
|
|
789
791
|
end
|
790
792
|
end
|
791
793
|
when IfBreak
|
792
|
-
if mode == MODE_BREAK
|
793
|
-
commands << [indent, mode, doc.break_contents]
|
794
|
-
elsif mode == MODE_FLAT
|
795
|
-
commands << [indent, mode, doc.flat_contents]
|
794
|
+
if mode == MODE_BREAK && doc.break_contents.any?
|
795
|
+
commands << [indent, mode, doc.break_contents]
|
796
|
+
elsif mode == MODE_FLAT && doc.flat_contents.any?
|
797
|
+
commands << [indent, mode, doc.flat_contents]
|
796
798
|
end
|
797
799
|
when LineSuffix
|
798
800
|
line_suffixes << [indent, mode, doc.contents, doc.priority]
|
@@ -1011,6 +1013,16 @@ class PrettyPrint
|
|
1011
1013
|
IfBreakBuilder.new(self, doc)
|
1012
1014
|
end
|
1013
1015
|
|
1016
|
+
# This is similar to if_break in that it also inserts an IfBreak node into the
|
1017
|
+
# print tree, however it's starting from the flat contents, and cannot be used
|
1018
|
+
# to build the break contents.
|
1019
|
+
def if_flat
|
1020
|
+
doc = IfBreak.new
|
1021
|
+
target << doc
|
1022
|
+
|
1023
|
+
with_target(doc.flat_contents) { yield }
|
1024
|
+
end
|
1025
|
+
|
1014
1026
|
# Very similar to the #nest method, this indents the nested content by one
|
1015
1027
|
# level by inserting an Indent node into the print tree. The contents of the
|
1016
1028
|
# node are determined by the block.
|
@@ -1116,10 +1128,10 @@ class PrettyPrint
|
|
1116
1128
|
when Group
|
1117
1129
|
commands << [indent, doc.break? ? MODE_BREAK : mode, doc.contents]
|
1118
1130
|
when IfBreak
|
1119
|
-
if mode == MODE_BREAK
|
1120
|
-
commands << [indent, mode, doc.break_contents]
|
1121
|
-
|
1122
|
-
commands << [indent, mode, doc.flat_contents]
|
1131
|
+
if mode == MODE_BREAK && doc.break_contents.any?
|
1132
|
+
commands << [indent, mode, doc.break_contents]
|
1133
|
+
elsif mode == MODE_FLAT && doc.flat_contents.any?
|
1134
|
+
commands << [indent, mode, doc.flat_contents]
|
1123
1135
|
end
|
1124
1136
|
when Breakable
|
1125
1137
|
if mode == MODE_FLAT && !doc.force?
|
data/lib/syntax_tree/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: syntax_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Newton
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-04-
|
11
|
+
date: 2022-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|