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