syntax_tree 0.1.0 → 1.0.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 +72 -1
- data/Gemfile.lock +2 -2
- data/README.md +7 -0
- data/exe/stree +4 -80
- data/lib/syntax_tree/cli.rb +237 -0
- data/lib/syntax_tree/prettyprint.rb +19 -9
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree.rb +950 -430
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3684dd6ee3c63a0d12bd9688dcd5c31a508e5b56cbed0d9939843014dcb035c
|
4
|
+
data.tar.gz: 7b229ace8aa66b761b7a2d2b9ad6560a819e953b67d787ac3c9dc2cc224a36fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74efe38ca5ab0e3bfcd6fd8ac8809d88e0d4fc87d84178a188601c7afc50c06c18c85f2669f504f1d84635fb39a367fcbf3cdbdae1eb45a5bf79fc97f2447e78
|
7
|
+
data.tar.gz: 7b186fc7133a6f2806d73de62c15de8ae39682f7f317a7368f9a3a5877dc1d2f5f69824b08a0f970c8983f92fce8f21c824db20f9f46eecbb6786deacb46560a
|
data/CHANGELOG.md
CHANGED
@@ -6,11 +6,82 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [1.0.0]
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- The ability to "check" formatting by formatting the output of the first format.
|
14
|
+
- Comments can now be attached to the `case` keyword.
|
15
|
+
- Remove escaped forward slashes from regular expression literals when converting to `%r`.
|
16
|
+
- Allow arrays of `CHAR` nodes to be converted to `QWords` under certain conditions.
|
17
|
+
- Allow `HashLiteral` opening braces to have trailing comments.
|
18
|
+
- Add parentheses if `Yield` breaks onto multiple lines.
|
19
|
+
- Ensure all nodes that could have heredocs nested know about their end lines.
|
20
|
+
- Ensure comments on assignment after the `=` before the value keep their place.
|
21
|
+
- Trailing comments on parameters with no parentheses now do not force a break.
|
22
|
+
- Allow `ArrayLiteral` opening brackets to have trailing comments.
|
23
|
+
- Allow different line suffix nodes to have different priorities.
|
24
|
+
- Better support for encoding by properly reading encoding magic comments.
|
25
|
+
- Support singleton single-line method definitions.
|
26
|
+
- Support `stree-ignore` comments to ignore formatting nodes.
|
27
|
+
- Add special formatting for arrays of `VarRef` nodes whose sum width is greater than 2 * the maximum width.
|
28
|
+
- Better output formatting for the CLI.
|
29
|
+
|
30
|
+
### Changed
|
31
|
+
|
32
|
+
- Force a break if a block is attached to a `Command` or `CommandCall` node.
|
33
|
+
- Don't indent `CommandCall` arguments if they don't fit aligned.
|
34
|
+
- Force a break in `Call` nodes if there are comments on the receiver.
|
35
|
+
- Do not change block bounds if inside of a `Command` or `CommandCall` node.
|
36
|
+
- Handle empty parentheses inside method calls.
|
37
|
+
- Skip indentation for special array literals on assignment nodes.
|
38
|
+
- Ensure a final breakable is inserted when converting an `ArrayLiteral` to a `QSymbols`.
|
39
|
+
- Fix up the `doc_width` calculation for `CommandCall` nodes.
|
40
|
+
- Ensure parameters inside a lambda literal when there are no parentheses are grouped.
|
41
|
+
- Ensure when converting an `ArrayLiteral` to a `QWords` that the strings do not contain `[`.
|
42
|
+
- Stop looking for parent `Command` or `CommandCall` nodes in blocks once you hit `Statements`.
|
43
|
+
- Ensure nested `Lambda` nodes get their correct bounds.
|
44
|
+
- Ensure we do not change block bounds within control flow constructs.
|
45
|
+
- Ensure parentheses are added around keywords changing to their modifier forms.
|
46
|
+
- Allow conditionals to take modifier form if they are using the `then` keyword with a `VoidStmt`.
|
47
|
+
- `UntilMod` and `WhileMod` nodes that wrap a `Begin` should be forced into their modifier forms.
|
48
|
+
- Ensure `For` loops keep their trailing commas.
|
49
|
+
- Replicate content for `__END__` keyword exactly.
|
50
|
+
- Keep block `If`, `Unless`, `While`, and `Until` forms if there is an assignment in the predicate.
|
51
|
+
- Force using braces if the block is within the predicate of a conditional or loop.
|
52
|
+
- Allow for the possibility that `CommandCall` nodes might not have arguments.
|
53
|
+
- Explicitly handle `?"` so that it formats properly.
|
54
|
+
- Check that a block is within the predicate in a more relaxed way.
|
55
|
+
- Ensure the `Return` breaks with brackets and not parentheses.
|
56
|
+
- Ensure trailing comments on parameter declarations are consistent.
|
57
|
+
- Make `Command` and `CommandCall` aware that their arguments could exceed their normal expected bounds because of heredocs.
|
58
|
+
- Only unescape forward slashes in regular expressions if converting from slash bounds to `%r` bounds.
|
59
|
+
- Allow `When` nodes to grab trailing comments away from their statements lists.
|
60
|
+
- Allow flip-flop operators to be formatted correctly within `IfMod` and `UnlessMod` nodes.
|
61
|
+
- Allow `IfMod` and `UnlessMod` to know about heredocs moving their bounds.
|
62
|
+
- Properly handle breaking parameters when there are no parentheses.
|
63
|
+
- Properly handle trailing operators in call chains with attached comments.
|
64
|
+
- Force using braces if the block is within the predicate of a ternary.
|
65
|
+
- Properly handle trailing comments after a `then` operator on a `When` or `In` clause.
|
66
|
+
- Ensure nested `HshPtn` nodes use braces.
|
67
|
+
- Force using braces if the block is within a `Binary` within the predicate of a loop or conditional.
|
68
|
+
- Make sure `StringLiteral` and `StringEmbExpr` know that they can be extended by heredocs.
|
69
|
+
- Ensure `Int` nodes with preceding unary `+` get formatted properly.
|
70
|
+
- Properly handle byte-order mark column offsets at the beginnings of files.
|
71
|
+
- Ensure `Words`, `Symbols`, `QWords`, and `QSymbols` properly format when their contents contain brackets.
|
72
|
+
- Ensure ternaries being broken out into `if`...`else`...`end` get wrapped in parentheses if necessary.
|
73
|
+
|
74
|
+
### Removed
|
75
|
+
|
76
|
+
- The `AccessCtrl` node in favor of just formatting correctly when you hit a `Statements` node.
|
77
|
+
- The `MethodAddArg` node is removed in favor of an optional `arguments` field on `Call` and `FCall`.
|
78
|
+
|
9
79
|
## [0.1.0] - 2021-11-16
|
10
80
|
|
11
81
|
### Added
|
12
82
|
|
13
83
|
- 🎉 Initial release! 🎉
|
14
84
|
|
15
|
-
[unreleased]: https://github.com/kddnewton/syntax_tree/compare/
|
85
|
+
[unreleased]: https://github.com/kddnewton/syntax_tree/compare/v1.0.0...HEAD
|
86
|
+
[1.0.0]: https://github.com/kddnewton/syntax_tree/compare/v0.1.0...v1.0.0
|
16
87
|
[0.1.0]: https://github.com/kddnewton/syntax_tree/compare/8aa1f5...v0.1.0
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
syntax_tree (
|
4
|
+
syntax_tree (1.0.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -10,7 +10,7 @@ GEM
|
|
10
10
|
benchmark-ips (2.9.2)
|
11
11
|
docile (1.4.0)
|
12
12
|
minitest (5.14.4)
|
13
|
-
parser (3.0.3.
|
13
|
+
parser (3.0.3.2)
|
14
14
|
ast (~> 2.4.1)
|
15
15
|
rake (13.0.6)
|
16
16
|
ruby_parser (3.18.1)
|
data/README.md
CHANGED
@@ -49,6 +49,13 @@ class MyClass
|
|
49
49
|
...
|
50
50
|
```
|
51
51
|
|
52
|
+
or
|
53
|
+
|
54
|
+
```sh
|
55
|
+
$ stree write program.rb
|
56
|
+
program.rb 1ms
|
57
|
+
```
|
58
|
+
|
52
59
|
## Development
|
53
60
|
|
54
61
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/exe/stree
CHANGED
@@ -1,84 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
4
|
+
$:.unshift(File.expand_path("../lib", __dir__))
|
5
|
+
require "syntax_tree"
|
6
|
+
require "syntax_tree/cli"
|
5
7
|
|
6
|
-
|
7
|
-
stree MDOE FILE
|
8
|
-
|
9
|
-
MODE: one of "a", "ast", "d", "doc", "f", "format", "w", or "write"
|
10
|
-
FILE: one or more paths to files to parse
|
11
|
-
EOF
|
12
|
-
|
13
|
-
if ARGV.length < 2
|
14
|
-
warn(help)
|
15
|
-
exit(1)
|
16
|
-
end
|
17
|
-
|
18
|
-
module SyntaxTree::CLI
|
19
|
-
class AST
|
20
|
-
def run(filepath)
|
21
|
-
pp SyntaxTree.parse(File.read(filepath))
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class Doc
|
26
|
-
def run(filepath)
|
27
|
-
formatter = SyntaxTree::Formatter.new([])
|
28
|
-
SyntaxTree.parse(File.read(filepath)).format(formatter)
|
29
|
-
pp formatter.groups.first
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class Format
|
34
|
-
def run(filepath)
|
35
|
-
puts SyntaxTree.format(File.read(filepath))
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
class Write
|
40
|
-
def run(filepath)
|
41
|
-
File.write(filepath, SyntaxTree.format(File.read(filepath)))
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
mode =
|
47
|
-
case ARGV.shift
|
48
|
-
when "a", "ast"
|
49
|
-
SyntaxTree::CLI::AST.new
|
50
|
-
when "d", "doc"
|
51
|
-
SyntaxTree::CLI::Doc.new
|
52
|
-
when "f", "format"
|
53
|
-
SyntaxTree::CLI::Format.new
|
54
|
-
when "w", "write"
|
55
|
-
SyntaxTree::CLI::Write.new
|
56
|
-
else
|
57
|
-
warn(help)
|
58
|
-
exit(1)
|
59
|
-
end
|
60
|
-
|
61
|
-
queue = Queue.new
|
62
|
-
ARGV.each { |pattern| Dir[pattern].each { |filepath| queue << filepath } }
|
63
|
-
|
64
|
-
if queue.size <= 1
|
65
|
-
filepath = queue.shift
|
66
|
-
mode.run(filepath) if File.file?(filepath)
|
67
|
-
return
|
68
|
-
end
|
69
|
-
|
70
|
-
count = [8, queue.size].min
|
71
|
-
threads =
|
72
|
-
count.times.map do
|
73
|
-
Thread.new do
|
74
|
-
loop do
|
75
|
-
filepath = queue.shift
|
76
|
-
break if filepath == :exit
|
77
|
-
|
78
|
-
mode.run(filepath) if File.file?(filepath)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
count.times { queue << :exit }
|
84
|
-
threads.each(&:join)
|
8
|
+
exit(SyntaxTree::CLI.run(ARGV))
|
@@ -0,0 +1,237 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SyntaxTree
|
4
|
+
module CLI
|
5
|
+
# A utility wrapper around colored strings in the output.
|
6
|
+
class Color
|
7
|
+
attr_reader :value, :code
|
8
|
+
|
9
|
+
def initialize(value, code)
|
10
|
+
@value = value
|
11
|
+
@code = code
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"\033[#{code}m#{value}\033[0m"
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.gray(value)
|
19
|
+
new(value, "38;5;102")
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.red(value)
|
23
|
+
new(value, "1;31")
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.yellow(value)
|
27
|
+
new(value, "33")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# The parent action class for the CLI that implements the basics.
|
32
|
+
class Action
|
33
|
+
def run(filepath, source)
|
34
|
+
end
|
35
|
+
|
36
|
+
def success
|
37
|
+
end
|
38
|
+
|
39
|
+
def failure
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# An action of the CLI that prints out the AST for the given source.
|
44
|
+
class AST < Action
|
45
|
+
def run(filepath, source)
|
46
|
+
pp SyntaxTree.parse(source)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# An action of the CLI that ensures that the filepath is formatted as
|
51
|
+
# expected.
|
52
|
+
class Check < Action
|
53
|
+
class UnformattedError < StandardError
|
54
|
+
end
|
55
|
+
|
56
|
+
def run(filepath, source)
|
57
|
+
raise UnformattedError if source != SyntaxTree.format(source)
|
58
|
+
rescue
|
59
|
+
warn("[#{Color.yellow("warn")}] #{filepath}")
|
60
|
+
raise
|
61
|
+
end
|
62
|
+
|
63
|
+
def success
|
64
|
+
puts("All files matched expected format.")
|
65
|
+
end
|
66
|
+
|
67
|
+
def failure
|
68
|
+
warn("The listed files did not match the expected format.")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# An action of the CLI that formats the source twice to check if the first
|
73
|
+
# format is not idempotent.
|
74
|
+
class Debug < Action
|
75
|
+
class NonIdempotentFormatError < StandardError
|
76
|
+
end
|
77
|
+
|
78
|
+
def run(filepath, source)
|
79
|
+
warning = "[#{Color.yellow("warn")}] #{filepath}"
|
80
|
+
formatted = SyntaxTree.format(source)
|
81
|
+
|
82
|
+
if formatted != SyntaxTree.format(formatted)
|
83
|
+
raise NonIdempotentFormatError
|
84
|
+
end
|
85
|
+
rescue
|
86
|
+
warn(warning)
|
87
|
+
raise
|
88
|
+
end
|
89
|
+
|
90
|
+
def success
|
91
|
+
puts("All files can be formatted idempotently.")
|
92
|
+
end
|
93
|
+
|
94
|
+
def failure
|
95
|
+
warn("The listed files could not be formatted idempotently.")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# An action of the CLI that prints out the doc tree IR for the given source.
|
100
|
+
class Doc < Action
|
101
|
+
def run(filepath, source)
|
102
|
+
formatter = Formatter.new([])
|
103
|
+
SyntaxTree.parse(source).format(formatter)
|
104
|
+
pp formatter.groups.first
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# An action of the CLI that formats the input source and prints it out.
|
109
|
+
class Format < Action
|
110
|
+
def run(filepath, source)
|
111
|
+
puts SyntaxTree.format(source)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# An action of the CLI that formats the input source and writes the
|
116
|
+
# formatted output back to the file.
|
117
|
+
class Write < Action
|
118
|
+
def run(filepath, source)
|
119
|
+
print filepath
|
120
|
+
start = Time.now
|
121
|
+
|
122
|
+
formatted = SyntaxTree.format(source)
|
123
|
+
File.write(filepath, formatted)
|
124
|
+
|
125
|
+
color = source == formatted ? Color.gray(filepath) : filepath
|
126
|
+
delta = ((Time.now - start) * 1000).round
|
127
|
+
|
128
|
+
puts "\r#{color} #{delta}ms"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# The help message displayed if the input arguments are not correctly
|
133
|
+
# ordered or formatted.
|
134
|
+
HELP = <<~HELP
|
135
|
+
stree MODE FILE
|
136
|
+
|
137
|
+
MODE: ast | check | debug | doc | format | write
|
138
|
+
FILE: one or more paths to files to parse
|
139
|
+
HELP
|
140
|
+
|
141
|
+
class << self
|
142
|
+
# Run the CLI over the given array of strings that make up the arguments
|
143
|
+
# passed to the invocation.
|
144
|
+
def run(argv)
|
145
|
+
if argv.length < 2
|
146
|
+
warn(HELP)
|
147
|
+
return 1
|
148
|
+
end
|
149
|
+
|
150
|
+
arg, *patterns = argv
|
151
|
+
action =
|
152
|
+
case arg
|
153
|
+
when "a", "ast"
|
154
|
+
AST.new
|
155
|
+
when "c", "check"
|
156
|
+
Check.new
|
157
|
+
when "debug"
|
158
|
+
Debug.new
|
159
|
+
when "doc"
|
160
|
+
Doc.new
|
161
|
+
when "f", "format"
|
162
|
+
Format.new
|
163
|
+
when "w", "write"
|
164
|
+
Write.new
|
165
|
+
else
|
166
|
+
warn(HELP)
|
167
|
+
return 1
|
168
|
+
end
|
169
|
+
|
170
|
+
errored = false
|
171
|
+
patterns.each do |pattern|
|
172
|
+
Dir.glob(pattern).each do |filepath|
|
173
|
+
next unless File.file?(filepath)
|
174
|
+
source = source_for(filepath)
|
175
|
+
|
176
|
+
begin
|
177
|
+
action.run(filepath, source)
|
178
|
+
rescue ParseError => error
|
179
|
+
warn("Error: #{error.message}")
|
180
|
+
lines = source.lines
|
181
|
+
|
182
|
+
maximum = [error.lineno + 3, lines.length].min
|
183
|
+
digits = Math.log10(maximum).ceil
|
184
|
+
|
185
|
+
([error.lineno - 3, 0].max...maximum).each do |line_index|
|
186
|
+
line_number = line_index + 1
|
187
|
+
|
188
|
+
if line_number == error.lineno
|
189
|
+
part1 = Color.red(">")
|
190
|
+
part2 = Color.gray("%#{digits}d |" % line_number)
|
191
|
+
warn("#{part1} #{part2} #{lines[line_index]}")
|
192
|
+
|
193
|
+
part3 = Color.gray(" %#{digits}s |" % " ")
|
194
|
+
warn("#{part3} #{" " * error.column}#{Color.red("^")}")
|
195
|
+
else
|
196
|
+
prefix = Color.gray(" %#{digits}d |" % line_number)
|
197
|
+
warn("#{prefix} #{lines[line_index]}")
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
errored = true
|
202
|
+
rescue Check::UnformattedError, Debug::NonIdempotentFormatError
|
203
|
+
errored = true
|
204
|
+
rescue => error
|
205
|
+
warn(error.message)
|
206
|
+
warn(error.backtrace)
|
207
|
+
errored = true
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
if errored
|
213
|
+
action.failure
|
214
|
+
1
|
215
|
+
else
|
216
|
+
action.success
|
217
|
+
0
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
private
|
222
|
+
|
223
|
+
# Returns the source from the given filepath taking into account any
|
224
|
+
# potential magic encoding comments.
|
225
|
+
def source_for(filepath)
|
226
|
+
encoding =
|
227
|
+
File.open(filepath, "r") do |file|
|
228
|
+
header = file.readline
|
229
|
+
header += file.readline if header.start_with?("#!")
|
230
|
+
Ripper.new(header).tap(&:parse).encoding
|
231
|
+
end
|
232
|
+
|
233
|
+
File.read(filepath, encoding: encoding)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -213,9 +213,12 @@ class PrettyPrint
|
|
213
213
|
# constantly check where the line ends to avoid accidentally printing some
|
214
214
|
# content after a line suffix node.
|
215
215
|
class LineSuffix
|
216
|
-
|
216
|
+
DEFAULT_PRIORITY = 1
|
217
217
|
|
218
|
-
|
218
|
+
attr_reader :priority, :contents
|
219
|
+
|
220
|
+
def initialize(priority: DEFAULT_PRIORITY, contents: [])
|
221
|
+
@priority = priority
|
219
222
|
@contents = contents
|
220
223
|
end
|
221
224
|
|
@@ -741,10 +744,17 @@ class PrettyPrint
|
|
741
744
|
|
742
745
|
# This is a separate command stack that includes the same kind of triplets
|
743
746
|
# as the commands variable. It is used to keep track of things that should
|
744
|
-
# go at the end of printed lines once the other doc nodes are
|
745
|
-
#
|
747
|
+
# go at the end of printed lines once the other doc nodes are accounted for.
|
748
|
+
# Typically this is used to implement comments.
|
746
749
|
line_suffixes = []
|
747
750
|
|
751
|
+
# This is a special sort used to order the line suffixes by both the
|
752
|
+
# priority set on the line suffix and the index it was in the original
|
753
|
+
# array.
|
754
|
+
line_suffix_sort = ->(line_suffix) do
|
755
|
+
[-line_suffix.last, -line_suffixes.index(line_suffix)]
|
756
|
+
end
|
757
|
+
|
748
758
|
# This is a linear stack instead of a mutually recursive call defined on
|
749
759
|
# the individual doc nodes for efficiency.
|
750
760
|
while commands.any?
|
@@ -783,7 +793,7 @@ class PrettyPrint
|
|
783
793
|
commands << [indent, mode, doc.flat_contents] if doc.flat_contents
|
784
794
|
end
|
785
795
|
when LineSuffix
|
786
|
-
line_suffixes << [indent, mode, doc.contents]
|
796
|
+
line_suffixes << [indent, mode, doc.contents, doc.priority]
|
787
797
|
when Breakable
|
788
798
|
if mode == MODE_FLAT
|
789
799
|
if doc.force?
|
@@ -804,7 +814,7 @@ class PrettyPrint
|
|
804
814
|
# to flush them now, as we are about to add a newline.
|
805
815
|
if line_suffixes.any?
|
806
816
|
commands << [indent, mode, doc]
|
807
|
-
commands += line_suffixes.
|
817
|
+
commands += line_suffixes.sort_by(&line_suffix_sort)
|
808
818
|
line_suffixes = []
|
809
819
|
next
|
810
820
|
end
|
@@ -838,7 +848,7 @@ class PrettyPrint
|
|
838
848
|
end
|
839
849
|
|
840
850
|
if commands.empty? && line_suffixes.any?
|
841
|
-
commands += line_suffixes.
|
851
|
+
commands += line_suffixes.sort_by(&line_suffix_sort)
|
842
852
|
line_suffixes = []
|
843
853
|
end
|
844
854
|
end
|
@@ -1012,8 +1022,8 @@ class PrettyPrint
|
|
1012
1022
|
|
1013
1023
|
# Inserts a LineSuffix node into the print tree. The contents of the node are
|
1014
1024
|
# determined by the block.
|
1015
|
-
def line_suffix
|
1016
|
-
doc = LineSuffix.new
|
1025
|
+
def line_suffix(priority: LineSuffix::DEFAULT_PRIORITY)
|
1026
|
+
doc = LineSuffix.new(priority: priority)
|
1017
1027
|
target << doc
|
1018
1028
|
|
1019
1029
|
with_target(doc.contents) { yield }
|
data/lib/syntax_tree/version.rb
CHANGED