syntax_tree 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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