syntax_tree 2.3.1 → 2.5.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 +20 -1
- data/.rubocop.yml +80 -0
- data/CHANGELOG.md +36 -1
- data/Gemfile.lock +24 -1
- data/README.md +126 -5
- data/Rakefile +27 -5
- data/config/rubocop.yml +64 -0
- data/lib/syntax_tree/cli.rb +63 -27
- data/lib/syntax_tree/formatter/single_quotes.rb +13 -0
- data/lib/syntax_tree/formatter.rb +7 -6
- data/lib/syntax_tree/language_server/inlay_hints.rb +87 -38
- data/lib/syntax_tree/language_server.rb +50 -14
- data/lib/syntax_tree/node.rb +499 -387
- data/lib/syntax_tree/parser.rb +447 -112
- data/lib/syntax_tree/plugin/single_quotes.rb +4 -0
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/visitor/field_visitor.rb +1115 -0
- data/lib/syntax_tree/visitor/json_visitor.rb +25 -1305
- data/lib/syntax_tree/visitor/match_visitor.rb +122 -0
- data/lib/syntax_tree/visitor/pretty_print_visitor.rb +35 -1163
- data/lib/syntax_tree/visitor.rb +6 -1
- data/lib/syntax_tree.rb +12 -18
- data/syntax_tree.gemspec +26 -21
- metadata +38 -5
- data/lib/syntax_tree/prettyprint.rb +0 -1156
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b1505fca44f379d951132dafb8122a2591fe46607c8402d096bab67bd53d0a3
|
4
|
+
data.tar.gz: 0d58e60b6779ec1acd22ca776f3610f290c4b048cc1bf41b425f9c5a74e80db8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42d4dea2571b21b3cf877a129f0a840f30125eb918c558b25b5bc303c915bb758b731151cdf2cfb6e70e92cbc4957d915095d1284312529675fec71f3a6af3a7
|
7
|
+
data.tar.gz: 5178b395ddcfdcf795c18f7c986e86a12f6ca2bdec871792de2c21220dffc7c2a2238a02926cece3021635f42859ab5084ba4dba21932ce7f1791c2a17a5bc49
|
data/.github/workflows/main.yml
CHANGED
@@ -24,9 +24,28 @@ jobs:
|
|
24
24
|
ruby-version: ${{ matrix.ruby }}
|
25
25
|
- name: Test
|
26
26
|
run: bundle exec rake test
|
27
|
+
|
28
|
+
check:
|
29
|
+
name: Check
|
30
|
+
runs-on: ubuntu-latest
|
31
|
+
env:
|
32
|
+
CI: true
|
33
|
+
steps:
|
34
|
+
- uses: actions/checkout@master
|
35
|
+
- uses: ruby/setup-ruby@v1
|
36
|
+
with:
|
37
|
+
bundler-cache: true
|
38
|
+
ruby-version: '3.1'
|
39
|
+
- name: Check
|
40
|
+
run: |
|
41
|
+
bundle exec rake check
|
42
|
+
bundle exec rubocop
|
43
|
+
|
27
44
|
automerge:
|
28
45
|
name: AutoMerge
|
29
|
-
needs:
|
46
|
+
needs:
|
47
|
+
- ci
|
48
|
+
- check
|
30
49
|
runs-on: ubuntu-latest
|
31
50
|
if: github.event_name == 'pull_request_target' && github.actor == 'dependabot[bot]'
|
32
51
|
steps:
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
inherit_from: config/rubocop.yml
|
2
|
+
|
3
|
+
AllCops:
|
4
|
+
DisplayCopNames: true
|
5
|
+
DisplayStyleGuide: true
|
6
|
+
NewCops: enable
|
7
|
+
SuggestExtensions: false
|
8
|
+
TargetRubyVersion: 2.7
|
9
|
+
Exclude:
|
10
|
+
- '{bin,coverage,pkg,test/fixtures,vendor,tmp}/**/*'
|
11
|
+
- test.rb
|
12
|
+
|
13
|
+
Layout/LineLength:
|
14
|
+
Max: 80
|
15
|
+
|
16
|
+
Lint/DuplicateBranch:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Lint/EmptyBlock:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Lint/InterpolationCheck:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Lint/MissingSuper:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Lint/UnusedMethodArgument:
|
29
|
+
AllowUnusedKeywordArguments: true
|
30
|
+
|
31
|
+
Metrics:
|
32
|
+
Enabled: false
|
33
|
+
|
34
|
+
Naming/MethodName:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
Naming/MethodParameterName:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Naming/RescuedExceptionsVariableName:
|
41
|
+
PreferredName: error
|
42
|
+
|
43
|
+
Style/ExplicitBlockArgument:
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
Style/FormatString:
|
47
|
+
EnforcedStyle: percent
|
48
|
+
|
49
|
+
Style/GuardClause:
|
50
|
+
Enabled: false
|
51
|
+
|
52
|
+
Style/IdenticalConditionalBranches:
|
53
|
+
Enabled: false
|
54
|
+
|
55
|
+
Style/IfInsideElse:
|
56
|
+
Enabled: false
|
57
|
+
|
58
|
+
Style/KeywordParametersOrder:
|
59
|
+
Enabled: false
|
60
|
+
|
61
|
+
Style/MissingRespondToMissing:
|
62
|
+
Enabled: false
|
63
|
+
|
64
|
+
Style/MutableConstant:
|
65
|
+
Enabled: false
|
66
|
+
|
67
|
+
Style/NegatedIfElseCondition:
|
68
|
+
Enabled: false
|
69
|
+
|
70
|
+
Style/NumericPredicate:
|
71
|
+
Enabled: false
|
72
|
+
|
73
|
+
Style/ParallelAssignment:
|
74
|
+
Enabled: false
|
75
|
+
|
76
|
+
Style/PerlBackrefs:
|
77
|
+
Enabled: false
|
78
|
+
|
79
|
+
Style/SpecialGlobalVars:
|
80
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,37 @@ 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.5.0] - 2022-05-13
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- [#79](https://github.com/ruby-syntax-tree/syntax_tree/pull/79) - Support an optional `maxwidth` second argument to `SyntaxTree.format`.
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
|
17
|
+
- [#77](https://github.com/ruby-syntax-tree/syntax_tree/pull/77) - Correct the pattern for checking if a dynamic symbol can be converted into a label as a hash key.
|
18
|
+
- [#72](https://github.com/ruby-syntax-tree/syntax_tree/pull/72) - Disallow conditionals with `not` without parentheses in the predicate from turning into a ternary.
|
19
|
+
|
20
|
+
## [2.4.1] - 2022-05-10
|
21
|
+
|
22
|
+
- [#73](https://github.com/ruby-syntax-tree/syntax_tree/pull/73) - Fix nested hash patterns from accidentally adding a `then` to their output.
|
23
|
+
|
24
|
+
## [2.4.0] - 2022-05-07
|
25
|
+
|
26
|
+
### Added
|
27
|
+
|
28
|
+
- [#65](https://github.com/ruby-syntax-tree/syntax_tree/pull/65) - Add a rubocop config at `config/rubocop.yml` that we can ship with the gem so folks can inherit from it to get their styling correct.
|
29
|
+
- [#65](https://github.com/ruby-syntax-tree/syntax_tree/pull/65) - Improve hash pattern formatting by a lot - multiple lines are now not so ugly.
|
30
|
+
- [#62](https://github.com/ruby-syntax-tree/syntax_tree/issues/62) - Add `options` as a method on `SyntaxTree::RegexpLiteral`, add it to pattern matching, and describe it using the `SyntaxTree::Visitor::FieldVisitor` class.
|
31
|
+
- [#69](https://github.com/ruby-syntax-tree/syntax_tree/pull/69) - The `construct_keys` option has been added to every `SyntaxTree::Node` descendant. This allows building a pattern match expression that can be used later. It is meant as a reflection API, not necessarily something that should be eval'd.
|
32
|
+
- [#69](https://github.com/ruby-syntax-tree/syntax_tree/pull/69) - You can now call `stree json` to get a JSON representation of your syntax tree.
|
33
|
+
- [#69](https://github.com/ruby-syntax-tree/syntax_tree/pull/69) - You can now call `stree match` to get a Ruby pattern matching expression to match against the given input.
|
34
|
+
|
35
|
+
### Changed
|
36
|
+
|
37
|
+
- [#69](https://github.com/ruby-syntax-tree/syntax_tree/pull/69) - Fixed a long-standing bug with pretty-print where if certain things were required in different orders you could end up with a bug in `PP` when calling pretty-print with a confusing error referring to inspect keys.
|
38
|
+
- [#69](https://github.com/ruby-syntax-tree/syntax_tree/pull/69) - `SyntaxTree.read` can now handle an empty file.
|
39
|
+
|
9
40
|
## [2.3.1] - 2022-04-22
|
10
41
|
|
11
42
|
### Changed
|
@@ -193,7 +224,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
193
224
|
|
194
225
|
- 🎉 Initial release! 🎉
|
195
226
|
|
196
|
-
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.
|
227
|
+
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.5.0...HEAD
|
228
|
+
[2.5.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.4.1...v2.5.0
|
229
|
+
[2.4.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.4.0...v2.4.1
|
230
|
+
[2.4.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.3.1...v2.4.0
|
231
|
+
[2.3.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.3.0...v2.3.1
|
197
232
|
[2.3.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.2.0...v2.3.0
|
198
233
|
[2.2.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.1.1...v2.2.0
|
199
234
|
[2.1.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.1.0...v2.1.1
|
data/Gemfile.lock
CHANGED
@@ -1,20 +1,42 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
syntax_tree (2.
|
4
|
+
syntax_tree (2.5.0)
|
5
|
+
prettier_print
|
5
6
|
|
6
7
|
GEM
|
7
8
|
remote: https://rubygems.org/
|
8
9
|
specs:
|
10
|
+
ast (2.4.2)
|
9
11
|
docile (1.4.0)
|
10
12
|
minitest (5.15.0)
|
13
|
+
parallel (1.22.1)
|
14
|
+
parser (3.1.2.0)
|
15
|
+
ast (~> 2.4.1)
|
16
|
+
prettier_print (0.1.0)
|
17
|
+
rainbow (3.1.1)
|
11
18
|
rake (13.0.6)
|
19
|
+
regexp_parser (2.4.0)
|
20
|
+
rexml (3.2.5)
|
21
|
+
rubocop (1.29.1)
|
22
|
+
parallel (~> 1.10)
|
23
|
+
parser (>= 3.1.0.0)
|
24
|
+
rainbow (>= 2.2.2, < 4.0)
|
25
|
+
regexp_parser (>= 1.8, < 3.0)
|
26
|
+
rexml (>= 3.2.5, < 4.0)
|
27
|
+
rubocop-ast (>= 1.17.0, < 2.0)
|
28
|
+
ruby-progressbar (~> 1.7)
|
29
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
30
|
+
rubocop-ast (1.17.0)
|
31
|
+
parser (>= 3.1.1.0)
|
32
|
+
ruby-progressbar (1.11.0)
|
12
33
|
simplecov (0.21.2)
|
13
34
|
docile (~> 1.1)
|
14
35
|
simplecov-html (~> 0.11)
|
15
36
|
simplecov_json_formatter (~> 0.1)
|
16
37
|
simplecov-html (0.12.3)
|
17
38
|
simplecov_json_formatter (0.1.4)
|
39
|
+
unicode-display_width (2.1.0)
|
18
40
|
|
19
41
|
PLATFORMS
|
20
42
|
arm64-darwin-21
|
@@ -27,6 +49,7 @@ DEPENDENCIES
|
|
27
49
|
bundler
|
28
50
|
minitest
|
29
51
|
rake
|
52
|
+
rubocop
|
30
53
|
simplecov
|
31
54
|
syntax_tree!
|
32
55
|
|
data/README.md
CHANGED
@@ -16,6 +16,8 @@ It is built with only standard library dependencies. It additionally ships with
|
|
16
16
|
- [ast](#ast)
|
17
17
|
- [check](#check)
|
18
18
|
- [format](#format)
|
19
|
+
- [json](#json)
|
20
|
+
- [match](#match)
|
19
21
|
- [write](#write)
|
20
22
|
- [Library](#library)
|
21
23
|
- [SyntaxTree.read(filepath)](#syntaxtreereadfilepath)
|
@@ -27,6 +29,7 @@ It is built with only standard library dependencies. It additionally ships with
|
|
27
29
|
- [pretty_print(q)](#pretty_printq)
|
28
30
|
- [to_json(*opts)](#to_jsonopts)
|
29
31
|
- [format(q)](#formatq)
|
32
|
+
- [construct_keys](#construct_keys)
|
30
33
|
- [Visitor](#visitor)
|
31
34
|
- [visit_method](#visit_method)
|
32
35
|
- [Language server](#language-server)
|
@@ -34,6 +37,9 @@ It is built with only standard library dependencies. It additionally ships with
|
|
34
37
|
- [textDocument/inlayHints](#textdocumentinlayhints)
|
35
38
|
- [syntaxTree/visualizing](#syntaxtreevisualizing)
|
36
39
|
- [Plugins](#plugins)
|
40
|
+
- [Integration](#integration)
|
41
|
+
- [RuboCop](#rubocop)
|
42
|
+
- [VSCode](#vscode)
|
37
43
|
- [Contributing](#contributing)
|
38
44
|
- [License](#license)
|
39
45
|
|
@@ -122,6 +128,73 @@ For a file that contains `1 + 1`, you will receive:
|
|
122
128
|
1 + 1
|
123
129
|
```
|
124
130
|
|
131
|
+
### json
|
132
|
+
|
133
|
+
This command will output a JSON representation of the syntax tree that is functionally equivalent to the input. This is mostly used in contexts where you need to access the tree from JavaScript or serialize it over a network.
|
134
|
+
|
135
|
+
```sh
|
136
|
+
stree json path/to/file.rb
|
137
|
+
```
|
138
|
+
|
139
|
+
For a file that contains `1 + 1`, you will receive:
|
140
|
+
|
141
|
+
```json
|
142
|
+
{
|
143
|
+
"type": "program",
|
144
|
+
"location": [1, 0, 1, 6],
|
145
|
+
"statements": {
|
146
|
+
"type": "statements",
|
147
|
+
"location": [1, 0, 1, 6],
|
148
|
+
"body": [
|
149
|
+
{
|
150
|
+
"type": "binary",
|
151
|
+
"location": [1, 0, 1, 5],
|
152
|
+
"left": {
|
153
|
+
"type": "int",
|
154
|
+
"location": [1, 0, 1, 1],
|
155
|
+
"value": "1",
|
156
|
+
"comments": []
|
157
|
+
},
|
158
|
+
"operator": "+",
|
159
|
+
"right": {
|
160
|
+
"type": "int",
|
161
|
+
"location": [1, 4, 1, 5],
|
162
|
+
"value": "1",
|
163
|
+
"comments": []
|
164
|
+
},
|
165
|
+
"comments": []
|
166
|
+
}
|
167
|
+
],
|
168
|
+
"comments": []
|
169
|
+
},
|
170
|
+
"comments": []
|
171
|
+
}
|
172
|
+
```
|
173
|
+
|
174
|
+
### match
|
175
|
+
|
176
|
+
This command will output a Ruby case-match expression that would match correctly against the input.
|
177
|
+
|
178
|
+
```sh
|
179
|
+
stree match path/to/file.rb
|
180
|
+
```
|
181
|
+
|
182
|
+
For a file that contains `1 + 1`, you will receive:
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
SyntaxTree::Program[
|
186
|
+
statements: SyntaxTree::Statements[
|
187
|
+
body: [
|
188
|
+
SyntaxTree::Binary[
|
189
|
+
left: SyntaxTree::Int[value: "1"],
|
190
|
+
operator: :+,
|
191
|
+
right: SyntaxTree::Int[value: "1"]
|
192
|
+
]
|
193
|
+
]
|
194
|
+
]
|
195
|
+
]
|
196
|
+
```
|
197
|
+
|
125
198
|
### write
|
126
199
|
|
127
200
|
This command will format the listed files and write that formatted version back to the source files. Note that this overwrites the original content, to be sure to be using a version control system.
|
@@ -223,6 +296,27 @@ formatter.output.join
|
|
223
296
|
# => "1 + 1"
|
224
297
|
```
|
225
298
|
|
299
|
+
### construct_keys
|
300
|
+
|
301
|
+
Every node responds to `construct_keys`, which will return a string that contains a Ruby pattern-matching expression that could be used to match against the current node. It's meant to be used in tooling and through the CLI mostly.
|
302
|
+
|
303
|
+
```ruby
|
304
|
+
program = SyntaxTree.parse("1 + 1")
|
305
|
+
puts program.construct_keys
|
306
|
+
|
307
|
+
# SyntaxTree::Program[
|
308
|
+
# statements: SyntaxTree::Statements[
|
309
|
+
# body: [
|
310
|
+
# SyntaxTree::Binary[
|
311
|
+
# left: SyntaxTree::Int[value: "1"],
|
312
|
+
# operator: :+,
|
313
|
+
# right: SyntaxTree::Int[value: "1"]
|
314
|
+
# ]
|
315
|
+
# ]
|
316
|
+
# ]
|
317
|
+
# ]
|
318
|
+
```
|
319
|
+
|
226
320
|
## Visitor
|
227
321
|
|
228
322
|
If you want to operate over a set of nodes in the tree but don't want to walk the tree manually, the `Visitor` class makes it easy. `SyntaxTree::Visitor` is an implementation of the double dispatch visitor pattern. It works by the user defining visit methods that process nodes in the tree, which then call back to other visit methods to continue the descent. This is easier shown in code.
|
@@ -310,7 +404,17 @@ The language server additionally includes this custom request to return a textua
|
|
310
404
|
|
311
405
|
## Plugins
|
312
406
|
|
313
|
-
You can register additional languages that can flow through the same CLI with Syntax Tree's plugin system.
|
407
|
+
You can register additional configuration and additional languages that can flow through the same CLI with Syntax Tree's plugin system. When invoking the CLI, you pass through the list of plugins with the `--plugins` options to the commands that accept them. They should be a comma-delimited list. When the CLI first starts, it will require the files corresponding to those names.
|
408
|
+
|
409
|
+
### Configuration
|
410
|
+
|
411
|
+
To register additional configuration, define a file somewhere in your load path named `syntax_tree/my_plugin` directory. Then when invoking the CLI, you will pass `--plugins=my_plugin`. That will get required. In this way, you can modify Syntax Tree however you would like. Some plugins ship with Syntax Tree itself. They are:
|
412
|
+
|
413
|
+
* `plugin/single_quotes` - This will change all of your string literals to use single quotes instead of the default double quotes.
|
414
|
+
|
415
|
+
### Languages
|
416
|
+
|
417
|
+
To register a new language, call:
|
314
418
|
|
315
419
|
```ruby
|
316
420
|
SyntaxTree.register_handler(".mylang", MyLanguage)
|
@@ -322,11 +426,28 @@ In this case, whenever the CLI encounters a filepath that ends with the given ex
|
|
322
426
|
* `MyLanguage.parse(source)` - this should return the syntax tree corresponding to the given source. Those objects should implement the `pretty_print` interface.
|
323
427
|
* `MyLanguage.format(source)` - this should return the formatted version of the given source.
|
324
428
|
|
325
|
-
Below are listed all of the "official" plugins hosted under the same GitHub organization, which can be used as references for how to implement other plugins.
|
429
|
+
Below are listed all of the "official" language plugins hosted under the same GitHub organization, which can be used as references for how to implement other plugins.
|
430
|
+
|
431
|
+
* [haml](https://github.com/ruby-syntax-tree/syntax_tree-haml) for the [Haml template language](https://haml.info/).
|
432
|
+
* [json](https://github.com/ruby-syntax-tree/syntax_tree-json) for JSON.
|
433
|
+
* [rbs](https://github.com/ruby-syntax-tree/syntax_tree-rbs) for the [RBS type language](https://github.com/ruby/rbs).
|
434
|
+
|
435
|
+
## Integration
|
436
|
+
|
437
|
+
Syntax Tree's goal is to seemlessly integrate into your workflow. To this end, it provides a couple of additional tools beyond the CLI and the Ruby library.
|
438
|
+
|
439
|
+
### RuboCop
|
440
|
+
|
441
|
+
RuboCop and Syntax Tree serve different purposes, but there is overlap with some of RuboCop's functionality. Syntax Tree provides a RuboCop configuration file to disable rules that are redundant with Syntax Tree. To use this configuration file, add the following snippet to the top of your project's `.rubocop.yml`:
|
442
|
+
|
443
|
+
```yaml
|
444
|
+
inherit_gem:
|
445
|
+
syntax_tree: config/rubocop.yml
|
446
|
+
```
|
447
|
+
|
448
|
+
### VSCode
|
326
449
|
|
327
|
-
|
328
|
-
* [SyntaxTree::JSON](https://github.com/ruby-syntax-tree/syntax_tree-json) for JSON.
|
329
|
-
* [SyntaxTree::RBS](https://github.com/ruby-syntax-tree/syntax_tree-rbs) for the [RBS type language](https://github.com/ruby/rbs).
|
450
|
+
To integrate Syntax Tree into VSCode, you should use the official VSCode extension [ruby-syntax-tree/vscode-syntax-tree](https://github.com/ruby-syntax-tree/vscode-syntax-tree).
|
330
451
|
|
331
452
|
## Contributing
|
332
453
|
|
data/Rakefile
CHANGED
@@ -1,12 +1,34 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rake/testtask"
|
5
5
|
|
6
6
|
Rake::TestTask.new(:test) do |t|
|
7
|
-
t.libs <<
|
8
|
-
t.libs <<
|
9
|
-
t.test_files = FileList[
|
7
|
+
t.libs << "test"
|
8
|
+
t.libs << "lib"
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
10
10
|
end
|
11
11
|
|
12
12
|
task default: :test
|
13
|
+
|
14
|
+
FILEPATHS = %w[
|
15
|
+
Gemfile
|
16
|
+
Rakefile
|
17
|
+
syntax_tree.gemspec
|
18
|
+
lib/**/*.rb
|
19
|
+
test/*.rb
|
20
|
+
].freeze
|
21
|
+
|
22
|
+
task :syntax_tree do
|
23
|
+
$:.unshift File.expand_path("lib", __dir__)
|
24
|
+
require "syntax_tree"
|
25
|
+
require "syntax_tree/cli"
|
26
|
+
end
|
27
|
+
|
28
|
+
task check: :syntax_tree do
|
29
|
+
exit SyntaxTree::CLI.run(["check"] + FILEPATHS)
|
30
|
+
end
|
31
|
+
|
32
|
+
task format: :syntax_tree do
|
33
|
+
exit SyntaxTree::CLI.run(["write"] + FILEPATHS)
|
34
|
+
end
|
data/config/rubocop.yml
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Disabling all Layout/* rules, as they're unnecessary when the user is using
|
2
|
+
# Syntax Tree to handle all of the formatting.
|
3
|
+
Layout:
|
4
|
+
Enabled: false
|
5
|
+
|
6
|
+
# Re-enable Layout/LineLength because certain cops that most projects use
|
7
|
+
# (e.g. Style/IfUnlessModifier) require Layout/LineLength to be enabled.
|
8
|
+
# By leaving it disabled, those rules will mis-fire.
|
9
|
+
#
|
10
|
+
# Users can always override these defaults in their own rubocop.yml files.
|
11
|
+
# https://github.com/prettier/plugin-ruby/issues/825
|
12
|
+
Layout/LineLength:
|
13
|
+
Enabled: true
|
14
|
+
|
15
|
+
Style/MultilineIfModifier:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
# Syntax Tree will expand empty methods to put the end keyword on the subsequent
|
19
|
+
# line to reduce git diff noise.
|
20
|
+
Style/EmptyMethod:
|
21
|
+
EnforcedStyle: expanded
|
22
|
+
|
23
|
+
# lambdas that are constructed with the lambda method call cannot be safely
|
24
|
+
# turned into lambda literals without removing a method call.
|
25
|
+
Style/Lambda:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
# When method chains with multiple blocks are chained together, rubocop will let
|
29
|
+
# them pass if they're using braces but not if they're using do and end
|
30
|
+
# keywords. Because we will break individual blocks down to using keywords if
|
31
|
+
# they are multiline, this conflicts with rubocop.
|
32
|
+
Style/MultilineBlockChain:
|
33
|
+
Enabled: false
|
34
|
+
|
35
|
+
# Syntax Tree by default uses double quotes, so changing the configuration here
|
36
|
+
# to match that.
|
37
|
+
Style/StringLiterals:
|
38
|
+
EnforcedStyle: double_quotes
|
39
|
+
|
40
|
+
Style/StringLiteralsInInterpolation:
|
41
|
+
EnforcedStyle: double_quotes
|
42
|
+
|
43
|
+
Style/QuotedSymbols:
|
44
|
+
EnforcedStyle: double_quotes
|
45
|
+
|
46
|
+
# We let users have a little more freedom with symbol and words arrays. If the
|
47
|
+
# user only has an individual item like ["value"] then we don't bother
|
48
|
+
# converting it because it ends up being just noise.
|
49
|
+
Style/SymbolArray:
|
50
|
+
Enabled: false
|
51
|
+
|
52
|
+
Style/WordArray:
|
53
|
+
Enabled: false
|
54
|
+
|
55
|
+
# We don't support trailing commas in Syntax Tree by default, so just turning
|
56
|
+
# these off for now.
|
57
|
+
Style/TrailingCommaInArguments:
|
58
|
+
Enabled: false
|
59
|
+
|
60
|
+
Style/TrailingCommaInArrayLiteral:
|
61
|
+
Enabled: false
|
62
|
+
|
63
|
+
Style/TrailingCommaInHashLiteral:
|
64
|
+
Enabled: false
|
data/lib/syntax_tree/cli.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module SyntaxTree
|
4
|
+
# Syntax Tree ships with the `stree` CLI, which can be used to inspect and
|
5
|
+
# manipulate Ruby code. This module is responsible for powering that CLI.
|
4
6
|
module CLI
|
5
7
|
# A utility wrapper around colored strings in the output.
|
6
8
|
class Color
|
@@ -46,7 +48,7 @@ module SyntaxTree
|
|
46
48
|
|
47
49
|
# An action of the CLI that prints out the AST for the given source.
|
48
50
|
class AST < Action
|
49
|
-
def run(handler,
|
51
|
+
def run(handler, _filepath, source)
|
50
52
|
pp handler.parse(source)
|
51
53
|
end
|
52
54
|
end
|
@@ -83,9 +85,7 @@ module SyntaxTree
|
|
83
85
|
warning = "[#{Color.yellow("warn")}] #{filepath}"
|
84
86
|
formatted = handler.format(source)
|
85
87
|
|
86
|
-
if formatted != handler.format(formatted)
|
87
|
-
raise NonIdempotentFormatError
|
88
|
-
end
|
88
|
+
raise NonIdempotentFormatError if formatted != handler.format(formatted)
|
89
89
|
rescue StandardError
|
90
90
|
warn(warning)
|
91
91
|
raise
|
@@ -102,8 +102,8 @@ module SyntaxTree
|
|
102
102
|
|
103
103
|
# An action of the CLI that prints out the doc tree IR for the given source.
|
104
104
|
class Doc < Action
|
105
|
-
def run(handler,
|
106
|
-
formatter = Formatter.new([])
|
105
|
+
def run(handler, _filepath, source)
|
106
|
+
formatter = Formatter.new(source, [])
|
107
107
|
handler.parse(source).format(formatter)
|
108
108
|
pp formatter.groups.first
|
109
109
|
end
|
@@ -111,11 +111,28 @@ module SyntaxTree
|
|
111
111
|
|
112
112
|
# An action of the CLI that formats the input source and prints it out.
|
113
113
|
class Format < Action
|
114
|
-
def run(handler,
|
114
|
+
def run(handler, _filepath, source)
|
115
115
|
puts handler.format(source)
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
|
+
# An action of the CLI that converts the source into its equivalent JSON
|
120
|
+
# representation.
|
121
|
+
class Json < Action
|
122
|
+
def run(handler, _filepath, source)
|
123
|
+
object = Visitor::JSONVisitor.new.visit(handler.parse(source))
|
124
|
+
puts JSON.pretty_generate(object)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# An action of the CLI that outputs a pattern-matching Ruby expression that
|
129
|
+
# would match the input given.
|
130
|
+
class Match < Action
|
131
|
+
def run(handler, _filepath, source)
|
132
|
+
puts handler.parse(source).construct_keys
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
119
136
|
# An action of the CLI that formats the input source and writes the
|
120
137
|
# formatted output back to the file.
|
121
138
|
class Write < Action
|
@@ -154,6 +171,12 @@ module SyntaxTree
|
|
154
171
|
#{Color.bold("stree format [OPTIONS] [FILE]")}
|
155
172
|
Print out the formatted version of the given files
|
156
173
|
|
174
|
+
#{Color.bold("stree json [OPTIONS] [FILE]")}
|
175
|
+
Print out the JSON representation of the given files
|
176
|
+
|
177
|
+
#{Color.bold("stree match [OPTIONS] [FILE]")}
|
178
|
+
Print out a pattern-matching Ruby expression that would match the given files
|
179
|
+
|
157
180
|
#{Color.bold("stree help")}
|
158
181
|
Display this help message
|
159
182
|
|
@@ -201,6 +224,10 @@ module SyntaxTree
|
|
201
224
|
Debug.new
|
202
225
|
when "doc"
|
203
226
|
Doc.new
|
227
|
+
when "j", "json"
|
228
|
+
Json.new
|
229
|
+
when "m", "match"
|
230
|
+
Match.new
|
204
231
|
when "f", "format"
|
205
232
|
Format.new
|
206
233
|
when "w", "write"
|
@@ -212,7 +239,7 @@ module SyntaxTree
|
|
212
239
|
|
213
240
|
# If we're not reading from stdin and the user didn't supply and
|
214
241
|
# filepaths to be read, then we exit with the usage message.
|
215
|
-
if
|
242
|
+
if $stdin.tty? && arguments.empty?
|
216
243
|
warn(HELP)
|
217
244
|
return 1
|
218
245
|
end
|
@@ -239,18 +266,11 @@ module SyntaxTree
|
|
239
266
|
action.run(handler, filepath, source)
|
240
267
|
rescue Parser::ParseError => error
|
241
268
|
warn("Error: #{error.message}")
|
242
|
-
|
243
|
-
if error.lineno
|
244
|
-
highlight_error(error, source)
|
245
|
-
else
|
246
|
-
warn(error.message)
|
247
|
-
warn(error.backtrace)
|
248
|
-
end
|
249
|
-
|
269
|
+
highlight_error(error, source)
|
250
270
|
errored = true
|
251
271
|
rescue Check::UnformattedError, Debug::NonIdempotentFormatError
|
252
272
|
errored = true
|
253
|
-
rescue => error
|
273
|
+
rescue StandardError => error
|
254
274
|
warn(error.message)
|
255
275
|
warn(error.backtrace)
|
256
276
|
errored = true
|
@@ -268,18 +288,20 @@ module SyntaxTree
|
|
268
288
|
private
|
269
289
|
|
270
290
|
def each_file(arguments)
|
271
|
-
if
|
291
|
+
if $stdin.tty? || arguments.any?
|
272
292
|
arguments.each do |pattern|
|
273
|
-
Dir
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
293
|
+
Dir
|
294
|
+
.glob(pattern)
|
295
|
+
.each do |filepath|
|
296
|
+
next unless File.file?(filepath)
|
297
|
+
|
298
|
+
handler = HANDLERS[File.extname(filepath)]
|
299
|
+
source = handler.read(filepath)
|
300
|
+
yield handler, filepath, source
|
301
|
+
end
|
280
302
|
end
|
281
303
|
else
|
282
|
-
yield HANDLERS[".rb"], :stdin,
|
304
|
+
yield HANDLERS[".rb"], :stdin, $stdin.read
|
283
305
|
end
|
284
306
|
end
|
285
307
|
|
@@ -310,7 +332,21 @@ module SyntaxTree
|
|
310
332
|
# Take a line of Ruby source and colorize the output.
|
311
333
|
def colorize_line(line)
|
312
334
|
require "irb"
|
313
|
-
IRB::Color.colorize_code(line,
|
335
|
+
IRB::Color.colorize_code(line, **colorize_options)
|
336
|
+
end
|
337
|
+
|
338
|
+
# These are the options we're going to pass into IRB::Color.colorize_code.
|
339
|
+
# Since we support multiple versions of IRB, we're going to need to do
|
340
|
+
# some reflection to make sure we always pass valid options.
|
341
|
+
def colorize_options
|
342
|
+
options = { complete: false }
|
343
|
+
|
344
|
+
parameters = IRB::Color.method(:colorize_code).parameters
|
345
|
+
if parameters.any? { |(_type, name)| name == :ignore_error }
|
346
|
+
options[:ignore_error] = true
|
347
|
+
end
|
348
|
+
|
349
|
+
options
|
314
350
|
end
|
315
351
|
end
|
316
352
|
end
|