syntax_tree 0.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/CHANGELOG.md +98 -1
- data/Gemfile.lock +5 -4
- data/README.md +8 -1
- data/exe/stree +4 -80
- data/lib/syntax_tree/cli.rb +244 -0
- data/lib/syntax_tree/prettyprint.rb +23 -11
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree.rb +1405 -590
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4797ffd100da9b050f154cd0ac4a235b342c395e49747ee51574eb0e0f9fcf1a
|
4
|
+
data.tar.gz: 52babf707ae1bf42abaeb51b350644fc50f61fa1b74482abb776e863d2ec48dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62e28b7e6b25e81bdfb70817c02152bd640395149c1ac75fa553154ebb636c9c913dbf2ebda7a65f9687dd1bb69d51e0e3d8f5ffba172d0b1eadb5849e64369a
|
7
|
+
data.tar.gz: 6233b01960317929ce137305a08f45d3d756d741b5f6b6d037f2499377a53922f222543f433912cc853ee31726e2306527fbe29c75ad757e6125207b7476956c
|
data/.github/workflows/main.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -6,11 +6,108 @@ 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.2.0] - 2022-01-09
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- Support for Ruby 3.1 syntax, including: blocks without names, hash keys without values, endless methods without parentheses, and new argument forwarding.
|
14
|
+
- Support for pinned expressions and variables within pattern matching.
|
15
|
+
- Support endless ranges as the final argument to a `when` clause.
|
16
|
+
|
17
|
+
## [1.1.1] - 2021-12-09
|
18
|
+
|
19
|
+
### Added
|
20
|
+
|
21
|
+
- [#7](https://github.com/kddnewton/syntax_tree/issues/7) Better formatting for hashes and arrays that are values in hashes.
|
22
|
+
- [#9](https://github.com/kddnewton/syntax_tree/issues/9) Special handling for RSpec matchers when nesting `CommandCall` nodes.
|
23
|
+
- [#10](https://github.com/kddnewton/syntax_tree/issues/10) Force the maintaining of the modifier forms of conditionals and loops if the statement includes an assignment. Also, for the maintaining of the block form of conditionals and loops if the predicate includes an assignment.
|
24
|
+
|
25
|
+
## [1.1.0] - 2021-12-08
|
26
|
+
|
27
|
+
### Added
|
28
|
+
|
29
|
+
- Better handling for formatting files with errors.
|
30
|
+
- Colorize the output snippet using IRB.
|
31
|
+
|
32
|
+
## [1.0.0] - 2021-12-08
|
33
|
+
|
34
|
+
### Added
|
35
|
+
|
36
|
+
- The ability to "check" formatting by formatting the output of the first format.
|
37
|
+
- Comments can now be attached to the `case` keyword.
|
38
|
+
- Remove escaped forward slashes from regular expression literals when converting to `%r`.
|
39
|
+
- Allow arrays of `CHAR` nodes to be converted to `QWords` under certain conditions.
|
40
|
+
- Allow `HashLiteral` opening braces to have trailing comments.
|
41
|
+
- Add parentheses if `Yield` breaks onto multiple lines.
|
42
|
+
- Ensure all nodes that could have heredocs nested know about their end lines.
|
43
|
+
- Ensure comments on assignment after the `=` before the value keep their place.
|
44
|
+
- Trailing comments on parameters with no parentheses now do not force a break.
|
45
|
+
- Allow `ArrayLiteral` opening brackets to have trailing comments.
|
46
|
+
- Allow different line suffix nodes to have different priorities.
|
47
|
+
- Better support for encoding by properly reading encoding magic comments.
|
48
|
+
- Support singleton single-line method definitions.
|
49
|
+
- Support `stree-ignore` comments to ignore formatting nodes.
|
50
|
+
- Add special formatting for arrays of `VarRef` nodes whose sum width is greater than 2 * the maximum width.
|
51
|
+
- Better output formatting for the CLI.
|
52
|
+
|
53
|
+
### Changed
|
54
|
+
|
55
|
+
- Force a break if a block is attached to a `Command` or `CommandCall` node.
|
56
|
+
- Don't indent `CommandCall` arguments if they don't fit aligned.
|
57
|
+
- Force a break in `Call` nodes if there are comments on the receiver.
|
58
|
+
- Do not change block bounds if inside of a `Command` or `CommandCall` node.
|
59
|
+
- Handle empty parentheses inside method calls.
|
60
|
+
- Skip indentation for special array literals on assignment nodes.
|
61
|
+
- Ensure a final breakable is inserted when converting an `ArrayLiteral` to a `QSymbols`.
|
62
|
+
- Fix up the `doc_width` calculation for `CommandCall` nodes.
|
63
|
+
- Ensure parameters inside a lambda literal when there are no parentheses are grouped.
|
64
|
+
- Ensure when converting an `ArrayLiteral` to a `QWords` that the strings do not contain `[`.
|
65
|
+
- Stop looking for parent `Command` or `CommandCall` nodes in blocks once you hit `Statements`.
|
66
|
+
- Ensure nested `Lambda` nodes get their correct bounds.
|
67
|
+
- Ensure we do not change block bounds within control flow constructs.
|
68
|
+
- Ensure parentheses are added around keywords changing to their modifier forms.
|
69
|
+
- Allow conditionals to take modifier form if they are using the `then` keyword with a `VoidStmt`.
|
70
|
+
- `UntilMod` and `WhileMod` nodes that wrap a `Begin` should be forced into their modifier forms.
|
71
|
+
- Ensure `For` loops keep their trailing commas.
|
72
|
+
- Replicate content for `__END__` keyword exactly.
|
73
|
+
- Keep block `If`, `Unless`, `While`, and `Until` forms if there is an assignment in the predicate.
|
74
|
+
- Force using braces if the block is within the predicate of a conditional or loop.
|
75
|
+
- Allow for the possibility that `CommandCall` nodes might not have arguments.
|
76
|
+
- Explicitly handle `?"` so that it formats properly.
|
77
|
+
- Check that a block is within the predicate in a more relaxed way.
|
78
|
+
- Ensure the `Return` breaks with brackets and not parentheses.
|
79
|
+
- Ensure trailing comments on parameter declarations are consistent.
|
80
|
+
- Make `Command` and `CommandCall` aware that their arguments could exceed their normal expected bounds because of heredocs.
|
81
|
+
- Only unescape forward slashes in regular expressions if converting from slash bounds to `%r` bounds.
|
82
|
+
- Allow `When` nodes to grab trailing comments away from their statements lists.
|
83
|
+
- Allow flip-flop operators to be formatted correctly within `IfMod` and `UnlessMod` nodes.
|
84
|
+
- Allow `IfMod` and `UnlessMod` to know about heredocs moving their bounds.
|
85
|
+
- Properly handle breaking parameters when there are no parentheses.
|
86
|
+
- Properly handle trailing operators in call chains with attached comments.
|
87
|
+
- Force using braces if the block is within the predicate of a ternary.
|
88
|
+
- Properly handle trailing comments after a `then` operator on a `When` or `In` clause.
|
89
|
+
- Ensure nested `HshPtn` nodes use braces.
|
90
|
+
- Force using braces if the block is within a `Binary` within the predicate of a loop or conditional.
|
91
|
+
- Make sure `StringLiteral` and `StringEmbExpr` know that they can be extended by heredocs.
|
92
|
+
- Ensure `Int` nodes with preceding unary `+` get formatted properly.
|
93
|
+
- Properly handle byte-order mark column offsets at the beginnings of files.
|
94
|
+
- Ensure `Words`, `Symbols`, `QWords`, and `QSymbols` properly format when their contents contain brackets.
|
95
|
+
- Ensure ternaries being broken out into `if`...`else`...`end` get wrapped in parentheses if necessary.
|
96
|
+
|
97
|
+
### Removed
|
98
|
+
|
99
|
+
- The `AccessCtrl` node in favor of just formatting correctly when you hit a `Statements` node.
|
100
|
+
- The `MethodAddArg` node is removed in favor of an optional `arguments` field on `Call` and `FCall`.
|
101
|
+
|
9
102
|
## [0.1.0] - 2021-11-16
|
10
103
|
|
11
104
|
### Added
|
12
105
|
|
13
106
|
- 🎉 Initial release! 🎉
|
14
107
|
|
15
|
-
[unreleased]: https://github.com/kddnewton/syntax_tree/compare/
|
108
|
+
[unreleased]: https://github.com/kddnewton/syntax_tree/compare/v1.2.0...HEAD
|
109
|
+
[1.2.0]: https://github.com/kddnewton/syntax_tree/compare/v1.1.1...v1.2.0
|
110
|
+
[1.1.1]: https://github.com/kddnewton/syntax_tree/compare/v1.1.0...v1.1.1
|
111
|
+
[1.1.0]: https://github.com/kddnewton/syntax_tree/compare/v1.0.0...v1.1.0
|
112
|
+
[1.0.0]: https://github.com/kddnewton/syntax_tree/compare/v0.1.0...v1.0.0
|
16
113
|
[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.2.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -9,8 +9,8 @@ GEM
|
|
9
9
|
ast (2.4.2)
|
10
10
|
benchmark-ips (2.9.2)
|
11
11
|
docile (1.4.0)
|
12
|
-
minitest (5.
|
13
|
-
parser (3.0.
|
12
|
+
minitest (5.15.0)
|
13
|
+
parser (3.1.0.0)
|
14
14
|
ast (~> 2.4.1)
|
15
15
|
rake (13.0.6)
|
16
16
|
ruby_parser (3.18.1)
|
@@ -26,6 +26,7 @@ GEM
|
|
26
26
|
|
27
27
|
PLATFORMS
|
28
28
|
x86_64-darwin-19
|
29
|
+
x86_64-darwin-21
|
29
30
|
x86_64-linux
|
30
31
|
|
31
32
|
DEPENDENCIES
|
@@ -40,4 +41,4 @@ DEPENDENCIES
|
|
40
41
|
syntax_tree!
|
41
42
|
|
42
43
|
BUNDLED WITH
|
43
|
-
2.2.
|
44
|
+
2.2.31
|
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
[![Build Status](https://github.com/kddnewton/syntax_tree/workflows/Main/badge.svg)](https://github.com/kddnewton/syntax_tree/actions)
|
4
4
|
[![Gem Version](https://img.shields.io/gem/v/syntax_tree.svg)](https://rubygems.org/gems/syntax_tree)
|
5
5
|
|
6
|
-
A fast
|
6
|
+
A fast Ruby parser and formatter with only standard library dependencies.
|
7
7
|
|
8
8
|
## Installation
|
9
9
|
|
@@ -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,244 @@
|
|
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 StandardError
|
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 StandardError
|
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
|
+
rescue StandardError
|
130
|
+
puts "\r#{filepath}"
|
131
|
+
raise
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# The help message displayed if the input arguments are not correctly
|
136
|
+
# ordered or formatted.
|
137
|
+
HELP = <<~HELP
|
138
|
+
stree MODE FILE
|
139
|
+
|
140
|
+
MODE: ast | check | debug | doc | format | write
|
141
|
+
FILE: one or more paths to files to parse
|
142
|
+
HELP
|
143
|
+
|
144
|
+
class << self
|
145
|
+
# Run the CLI over the given array of strings that make up the arguments
|
146
|
+
# passed to the invocation.
|
147
|
+
def run(argv)
|
148
|
+
if argv.length < 2
|
149
|
+
warn(HELP)
|
150
|
+
return 1
|
151
|
+
end
|
152
|
+
|
153
|
+
arg, *patterns = argv
|
154
|
+
action =
|
155
|
+
case arg
|
156
|
+
when "a", "ast"
|
157
|
+
AST.new
|
158
|
+
when "c", "check"
|
159
|
+
Check.new
|
160
|
+
when "debug"
|
161
|
+
Debug.new
|
162
|
+
when "doc"
|
163
|
+
Doc.new
|
164
|
+
when "f", "format"
|
165
|
+
Format.new
|
166
|
+
when "w", "write"
|
167
|
+
Write.new
|
168
|
+
else
|
169
|
+
warn(HELP)
|
170
|
+
return 1
|
171
|
+
end
|
172
|
+
|
173
|
+
errored = false
|
174
|
+
patterns.each do |pattern|
|
175
|
+
Dir.glob(pattern).each do |filepath|
|
176
|
+
next unless File.file?(filepath)
|
177
|
+
source = SyntaxTree.read(filepath)
|
178
|
+
|
179
|
+
begin
|
180
|
+
action.run(filepath, source)
|
181
|
+
rescue ParseError => error
|
182
|
+
warn("Error: #{error.message}")
|
183
|
+
|
184
|
+
if error.lineno
|
185
|
+
highlight_error(error, source)
|
186
|
+
else
|
187
|
+
warn(error.message)
|
188
|
+
warn(error.backtrace)
|
189
|
+
end
|
190
|
+
|
191
|
+
errored = true
|
192
|
+
rescue Check::UnformattedError, Debug::NonIdempotentFormatError
|
193
|
+
errored = true
|
194
|
+
rescue => error
|
195
|
+
warn(error.message)
|
196
|
+
warn(error.backtrace)
|
197
|
+
errored = true
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
if errored
|
203
|
+
action.failure
|
204
|
+
1
|
205
|
+
else
|
206
|
+
action.success
|
207
|
+
0
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
private
|
212
|
+
|
213
|
+
# Highlights a snippet from a source and parse error.
|
214
|
+
def highlight_error(error, source)
|
215
|
+
lines = source.lines
|
216
|
+
|
217
|
+
maximum = [error.lineno + 3, lines.length].min
|
218
|
+
digits = Math.log10(maximum).ceil
|
219
|
+
|
220
|
+
([error.lineno - 3, 0].max...maximum).each do |line_index|
|
221
|
+
line_number = line_index + 1
|
222
|
+
|
223
|
+
if line_number == error.lineno
|
224
|
+
part1 = Color.red(">")
|
225
|
+
part2 = Color.gray("%#{digits}d |" % line_number)
|
226
|
+
warn("#{part1} #{part2} #{colorize_line(lines[line_index])}")
|
227
|
+
|
228
|
+
part3 = Color.gray(" %#{digits}s |" % " ")
|
229
|
+
warn("#{part3} #{" " * error.column}#{Color.red("^")}")
|
230
|
+
else
|
231
|
+
prefix = Color.gray(" %#{digits}d |" % line_number)
|
232
|
+
warn("#{prefix} #{colorize_line(lines[line_index])}")
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Take a line of Ruby source and colorize the output.
|
238
|
+
def colorize_line(line)
|
239
|
+
require "irb"
|
240
|
+
IRB::Color.colorize_code(line, complete: false, ignore_error: true)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
@@ -113,8 +113,10 @@ class PrettyPrint
|
|
113
113
|
def pretty_print(q)
|
114
114
|
q.text("breakable")
|
115
115
|
|
116
|
-
attributes =
|
117
|
-
|
116
|
+
attributes = [
|
117
|
+
("force=true" if force?),
|
118
|
+
("indent=false" unless indent?)
|
119
|
+
].compact
|
118
120
|
|
119
121
|
if attributes.any?
|
120
122
|
q.text("(")
|
@@ -213,9 +215,12 @@ class PrettyPrint
|
|
213
215
|
# constantly check where the line ends to avoid accidentally printing some
|
214
216
|
# content after a line suffix node.
|
215
217
|
class LineSuffix
|
216
|
-
|
218
|
+
DEFAULT_PRIORITY = 1
|
217
219
|
|
218
|
-
|
220
|
+
attr_reader :priority, :contents
|
221
|
+
|
222
|
+
def initialize(priority: DEFAULT_PRIORITY, contents: [])
|
223
|
+
@priority = priority
|
219
224
|
@contents = contents
|
220
225
|
end
|
221
226
|
|
@@ -741,10 +746,17 @@ class PrettyPrint
|
|
741
746
|
|
742
747
|
# This is a separate command stack that includes the same kind of triplets
|
743
748
|
# 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
|
-
#
|
749
|
+
# go at the end of printed lines once the other doc nodes are accounted for.
|
750
|
+
# Typically this is used to implement comments.
|
746
751
|
line_suffixes = []
|
747
752
|
|
753
|
+
# This is a special sort used to order the line suffixes by both the
|
754
|
+
# priority set on the line suffix and the index it was in the original
|
755
|
+
# array.
|
756
|
+
line_suffix_sort = ->(line_suffix) do
|
757
|
+
[-line_suffix.last, -line_suffixes.index(line_suffix)]
|
758
|
+
end
|
759
|
+
|
748
760
|
# This is a linear stack instead of a mutually recursive call defined on
|
749
761
|
# the individual doc nodes for efficiency.
|
750
762
|
while commands.any?
|
@@ -783,7 +795,7 @@ class PrettyPrint
|
|
783
795
|
commands << [indent, mode, doc.flat_contents] if doc.flat_contents
|
784
796
|
end
|
785
797
|
when LineSuffix
|
786
|
-
line_suffixes << [indent, mode, doc.contents]
|
798
|
+
line_suffixes << [indent, mode, doc.contents, doc.priority]
|
787
799
|
when Breakable
|
788
800
|
if mode == MODE_FLAT
|
789
801
|
if doc.force?
|
@@ -804,7 +816,7 @@ class PrettyPrint
|
|
804
816
|
# to flush them now, as we are about to add a newline.
|
805
817
|
if line_suffixes.any?
|
806
818
|
commands << [indent, mode, doc]
|
807
|
-
commands += line_suffixes.
|
819
|
+
commands += line_suffixes.sort_by(&line_suffix_sort)
|
808
820
|
line_suffixes = []
|
809
821
|
next
|
810
822
|
end
|
@@ -838,7 +850,7 @@ class PrettyPrint
|
|
838
850
|
end
|
839
851
|
|
840
852
|
if commands.empty? && line_suffixes.any?
|
841
|
-
commands += line_suffixes.
|
853
|
+
commands += line_suffixes.sort_by(&line_suffix_sort)
|
842
854
|
line_suffixes = []
|
843
855
|
end
|
844
856
|
end
|
@@ -1012,8 +1024,8 @@ class PrettyPrint
|
|
1012
1024
|
|
1013
1025
|
# Inserts a LineSuffix node into the print tree. The contents of the node are
|
1014
1026
|
# determined by the block.
|
1015
|
-
def line_suffix
|
1016
|
-
doc = LineSuffix.new
|
1027
|
+
def line_suffix(priority: LineSuffix::DEFAULT_PRIORITY)
|
1028
|
+
doc = LineSuffix.new(priority: priority)
|
1017
1029
|
target << doc
|
1018
1030
|
|
1019
1031
|
with_target(doc.contents) { yield }
|
data/lib/syntax_tree/version.rb
CHANGED