syntax_tree 2.5.0 → 2.7.1
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/.github/workflows/main.yml +1 -1
 - data/.rubocop.yml +3 -0
 - data/CHANGELOG.md +39 -1
 - data/Gemfile.lock +1 -1
 - data/README.md +63 -2
 - data/Rakefile +5 -20
 - data/lib/syntax_tree/basic_visitor.rb +74 -0
 - data/lib/syntax_tree/formatter.rb +29 -4
 - data/lib/syntax_tree/language_server/inlay_hints.rb +12 -1
 - data/lib/syntax_tree/language_server.rb +2 -8
 - data/lib/syntax_tree/node.rb +181 -59
 - data/lib/syntax_tree/parser.rb +117 -13
 - data/lib/syntax_tree/plugin/single_quotes.rb +1 -2
 - data/lib/syntax_tree/plugin/trailing_comma.rb +3 -0
 - data/lib/syntax_tree/rake/check_task.rb +66 -0
 - data/lib/syntax_tree/rake/write_task.rb +66 -0
 - data/lib/syntax_tree/rake_tasks.rb +4 -0
 - data/lib/syntax_tree/version.rb +1 -1
 - data/lib/syntax_tree/visitor/field_visitor.rb +9 -3
 - data/lib/syntax_tree/visitor.rb +4 -66
 - data/lib/syntax_tree.rb +2 -0
 - metadata +8 -4
 - data/lib/syntax_tree/formatter/single_quotes.rb +0 -13
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 4e90bde4f4aa60ce6784fd2c03234853bc281b7012cdb496d3f2bd97a1d825a9
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 24c4a344934acc5fb0b8b7d8931f689f98185f00fd9b71726f5aeab2499dea98
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 70d191270dbe2efd07300169410e4e0674b7abd8ec1ed4d95e87f7bb986b790195f8366f27b77f38eeeb94de1a08ce3467d64fbb126910922857366389ce66c0
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: c3994813de2fa53eb132dce7e4d176e58a83071232bcdefe3ded77a3fdd35ed52639d99989878b4cf5ca3d13c0a5bc14cbdfb95555b9705e2dd08c88d2abf360
         
     | 
    
        data/.github/workflows/main.yml
    CHANGED
    
    
    
        data/.rubocop.yml
    CHANGED
    
    
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -6,6 +6,41 @@ 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.7.1] - 2022-05-25
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            ### Added
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            - [#92](https://github.com/ruby-syntax-tree/syntax_tree/pull/92) - (Internal) Drastically increase test coverage, including many more tests for the language server and the CLI.
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            ### Changed
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            - [#87](https://github.com/ruby-syntax-tree/syntax_tree/pull/87) - Don't convert quotes on strings if it would result in more escapes.
         
     | 
| 
      
 18 
     | 
    
         
            +
            - [#91](https://github.com/ruby-syntax-tree/syntax_tree/pull/91) - Always use `[]` with array patterns. There are just too many edge cases where you have to use them anyway. This simplifies the look and makes it more consistent.
         
     | 
| 
      
 19 
     | 
    
         
            +
            - [#92](https://github.com/ruby-syntax-tree/syntax_tree/pull/92) - Remodel the currently shipped plugins such that they're modifying an options hash instead of overriding methods. This should make it easier for other plugins to reference the already loaded plugins, e.g., the RBS plugin referencing the quotes.
         
     | 
| 
      
 20 
     | 
    
         
            +
            - [#92](https://github.com/ruby-syntax-tree/syntax_tree/pull/92) - Fix up the language server inlay hints to continue walking the tree once a pattern is found. This should increase useability.
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            ## [2.7.0] - 2022-05-19
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            ### Added
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            - [#88](https://github.com/ruby-syntax-tree/syntax_tree/pull/88) - Provide a `SyntaxTree::BasicVisitor` that has no visit methods implemented.
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            ### Changed
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            - [#90](https://github.com/ruby-syntax-tree/syntax_tree/pull/90) - Provide better formatting for `SyntaxTree::AryPtn` when its nested inside a `SyntaxTree::RAssign`.
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            ## [2.6.0] - 2022-05-16
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            ### Added
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            - [#74](https://github.com/ruby-syntax-tree/syntax_tree/pull/74) - Add Rake test to run check and format commands.
         
     | 
| 
      
 37 
     | 
    
         
            +
            - [#83](https://github.com/ruby-syntax-tree/syntax_tree/pull/83) - Add a trailing commas plugin.
         
     | 
| 
      
 38 
     | 
    
         
            +
            - [#84](https://github.com/ruby-syntax-tree/syntax_tree/pull/84) - Handle lambda block-local variables.
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            ### Changed
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            - [#85](https://github.com/ruby-syntax-tree/syntax_tree/pull/85) - Better handle trailing operators on command calls.
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
       9 
44 
     | 
    
         
             
            ## [2.5.0] - 2022-05-13
         
     | 
| 
       10 
45 
     | 
    
         | 
| 
       11 
46 
     | 
    
         
             
            ### Added
         
     | 
| 
         @@ -224,7 +259,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a 
     | 
|
| 
       224 
259 
     | 
    
         | 
| 
       225 
260 
     | 
    
         
             
            - 🎉 Initial release! 🎉
         
     | 
| 
       226 
261 
     | 
    
         | 
| 
       227 
     | 
    
         
            -
            [unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2. 
     | 
| 
      
 262 
     | 
    
         
            +
            [unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.7.1...HEAD
         
     | 
| 
      
 263 
     | 
    
         
            +
            [2.7.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.7.0...v2.7.1
         
     | 
| 
      
 264 
     | 
    
         
            +
            [2.7.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.6.0...v2.7.0
         
     | 
| 
      
 265 
     | 
    
         
            +
            [2.6.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.5.0...v2.6.0
         
     | 
| 
       228 
266 
     | 
    
         
             
            [2.5.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.4.1...v2.5.0
         
     | 
| 
       229 
267 
     | 
    
         
             
            [2.4.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.4.0...v2.4.1
         
     | 
| 
       230 
268 
     | 
    
         
             
            [2.4.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.3.1...v2.4.0
         
     | 
    
        data/Gemfile.lock
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | 
         @@ -32,12 +32,16 @@ It is built with only standard library dependencies. It additionally ships with 
     | 
|
| 
       32 
32 
     | 
    
         
             
              - [construct_keys](#construct_keys)
         
     | 
| 
       33 
33 
     | 
    
         
             
            - [Visitor](#visitor)
         
     | 
| 
       34 
34 
     | 
    
         
             
              - [visit_method](#visit_method)
         
     | 
| 
      
 35 
     | 
    
         
            +
              - [BasicVisitor](#basicvisitor)
         
     | 
| 
       35 
36 
     | 
    
         
             
            - [Language server](#language-server)
         
     | 
| 
       36 
37 
     | 
    
         
             
              - [textDocument/formatting](#textdocumentformatting)
         
     | 
| 
       37 
38 
     | 
    
         
             
              - [textDocument/inlayHints](#textdocumentinlayhints)
         
     | 
| 
       38 
39 
     | 
    
         
             
              - [syntaxTree/visualizing](#syntaxtreevisualizing)
         
     | 
| 
       39 
40 
     | 
    
         
             
            - [Plugins](#plugins)
         
     | 
| 
      
 41 
     | 
    
         
            +
              - [Configuration](#configuration)
         
     | 
| 
      
 42 
     | 
    
         
            +
              - [Languages](#languages)
         
     | 
| 
       40 
43 
     | 
    
         
             
            - [Integration](#integration)
         
     | 
| 
      
 44 
     | 
    
         
            +
              - [Rake](#rake)
         
     | 
| 
       41 
45 
     | 
    
         
             
              - [RuboCop](#rubocop)
         
     | 
| 
       42 
46 
     | 
    
         
             
              - [VSCode](#vscode)
         
     | 
| 
       43 
47 
     | 
    
         
             
            - [Contributing](#contributing)
         
     | 
| 
         @@ -223,7 +227,7 @@ This function takes an input string containing Ruby code and returns the syntax 
     | 
|
| 
       223 
227 
     | 
    
         | 
| 
       224 
228 
     | 
    
         
             
            ### SyntaxTree.format(source)
         
     | 
| 
       225 
229 
     | 
    
         | 
| 
       226 
     | 
    
         
            -
            This function takes an input string containing Ruby code, parses it into its underlying syntax tree, and formats it back out to a string.
         
     | 
| 
      
 230 
     | 
    
         
            +
            This function takes an input string containing Ruby code, parses it into its underlying syntax tree, and formats it back out to a string. You can optionally pass a second argument to this method as well that is the maximum width to print. It defaults to `80`.
         
     | 
| 
       227 
231 
     | 
    
         | 
| 
       228 
232 
     | 
    
         
             
            ## Nodes
         
     | 
| 
       229 
233 
     | 
    
         | 
| 
         @@ -370,6 +374,20 @@ Did you mean?  visit_binary 
     | 
|
| 
       370 
374 
     | 
    
         
             
            	from bin/console:8:in `<main>'
         
     | 
| 
       371 
375 
     | 
    
         
             
            ```
         
     | 
| 
       372 
376 
     | 
    
         | 
| 
      
 377 
     | 
    
         
            +
            ### BasicVisitor
         
     | 
| 
      
 378 
     | 
    
         
            +
             
     | 
| 
      
 379 
     | 
    
         
            +
            When you're defining your own visitor, by default it will walk down the tree even if you don't define `visit_*` methods. This is to ensure you can define a subset of the necessary methods in order to only interact with the nodes you're interested in. If you'd like to change this default to instead raise an error if you visit a node you haven't explicitly handled, you can instead inherit from `BasicVisitor`.
         
     | 
| 
      
 380 
     | 
    
         
            +
             
     | 
| 
      
 381 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 382 
     | 
    
         
            +
            class MyVisitor < SyntaxTree::BasicVisitor
         
     | 
| 
      
 383 
     | 
    
         
            +
              def visit_int(node)
         
     | 
| 
      
 384 
     | 
    
         
            +
                # ...
         
     | 
| 
      
 385 
     | 
    
         
            +
              end
         
     | 
| 
      
 386 
     | 
    
         
            +
            end
         
     | 
| 
      
 387 
     | 
    
         
            +
            ```
         
     | 
| 
      
 388 
     | 
    
         
            +
             
     | 
| 
      
 389 
     | 
    
         
            +
            The visitor defined above will error out unless it's only visiting a `SyntaxTree::Int` node. This is useful in a couple of ways, e.g., if you're trying to define a visitor to handle the whole tree but it's currently a work-in-progress.
         
     | 
| 
      
 390 
     | 
    
         
            +
             
     | 
| 
       373 
391 
     | 
    
         
             
            ## Language server
         
     | 
| 
       374 
392 
     | 
    
         | 
| 
       375 
393 
     | 
    
         
             
            Syntax Tree additionally ships with a language server conforming to the [language server protocol](https://microsoft.github.io/language-server-protocol/). It can be invoked through the CLI by running:
         
     | 
| 
         @@ -408,9 +426,12 @@ You can register additional configuration and additional languages that can flow 
     | 
|
| 
       408 
426 
     | 
    
         | 
| 
       409 
427 
     | 
    
         
             
            ### Configuration
         
     | 
| 
       410 
428 
     | 
    
         | 
| 
       411 
     | 
    
         
            -
            To register additional configuration, define a file somewhere in your load path named `syntax_tree/my_plugin 
     | 
| 
      
 429 
     | 
    
         
            +
            To register additional configuration, define a file somewhere in your load path named `syntax_tree/my_plugin`. Then when invoking the CLI, you will pass `--plugins=my_plugin`. To require multiple, separate them by a comma. In this way, you can modify Syntax Tree however you would like. Some plugins ship with Syntax Tree itself. They are:
         
     | 
| 
       412 
430 
     | 
    
         | 
| 
       413 
431 
     | 
    
         
             
            * `plugin/single_quotes` - This will change all of your string literals to use single quotes instead of the default double quotes.
         
     | 
| 
      
 432 
     | 
    
         
            +
            * `plugin/trailing_comma` - This will put trailing commas into multiline array literals, hash literals, and method calls that can support trailing commas.
         
     | 
| 
      
 433 
     | 
    
         
            +
             
     | 
| 
      
 434 
     | 
    
         
            +
            If you're using Syntax Tree as a library, you should require those files directly.
         
     | 
| 
       414 
435 
     | 
    
         | 
| 
       415 
436 
     | 
    
         
             
            ### Languages
         
     | 
| 
       416 
437 
     | 
    
         | 
| 
         @@ -436,6 +457,46 @@ Below are listed all of the "official" language plugins hosted under the same Gi 
     | 
|
| 
       436 
457 
     | 
    
         | 
| 
       437 
458 
     | 
    
         
             
            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 
459 
     | 
    
         | 
| 
      
 460 
     | 
    
         
            +
            ### Rake
         
     | 
| 
      
 461 
     | 
    
         
            +
             
     | 
| 
      
 462 
     | 
    
         
            +
            Syntax Tree ships with the ability to define [rake](https://github.com/ruby/rake) tasks that will trigger runs of the CLI. To define them in your application, add the following configuration to your `Rakefile`:
         
     | 
| 
      
 463 
     | 
    
         
            +
             
     | 
| 
      
 464 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 465 
     | 
    
         
            +
            require "syntax_tree/rake_tasks"
         
     | 
| 
      
 466 
     | 
    
         
            +
            SyntaxTree::Rake::CheckTask.new
         
     | 
| 
      
 467 
     | 
    
         
            +
            SyntaxTree::Rake::WriteTask.new
         
     | 
| 
      
 468 
     | 
    
         
            +
            ```
         
     | 
| 
      
 469 
     | 
    
         
            +
             
     | 
| 
      
 470 
     | 
    
         
            +
            These calls will define `rake stree:check` and `rake stree:write` (equivalent to calling `stree check` and `stree write` with the CLI respectively). You can configure them by either passing arguments to the `new` method or by using a block.
         
     | 
| 
      
 471 
     | 
    
         
            +
             
     | 
| 
      
 472 
     | 
    
         
            +
            #### `name`
         
     | 
| 
      
 473 
     | 
    
         
            +
             
     | 
| 
      
 474 
     | 
    
         
            +
            If you'd like to change the default name of the rake task, you can pass that as the first argument, as in:
         
     | 
| 
      
 475 
     | 
    
         
            +
             
     | 
| 
      
 476 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 477 
     | 
    
         
            +
            SyntaxTree::Rake::WriteTask.new(:format)
         
     | 
| 
      
 478 
     | 
    
         
            +
            ```
         
     | 
| 
      
 479 
     | 
    
         
            +
             
     | 
| 
      
 480 
     | 
    
         
            +
            #### `source_files`
         
     | 
| 
      
 481 
     | 
    
         
            +
             
     | 
| 
      
 482 
     | 
    
         
            +
            If you wanted to configure Syntax Tree to check or write different files than the default (`lib/**/*.rb`), you can set the `source_files` field, as in:
         
     | 
| 
      
 483 
     | 
    
         
            +
             
     | 
| 
      
 484 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 485 
     | 
    
         
            +
            SyntaxTree::Rake::WriteTask.new do |t|
         
     | 
| 
      
 486 
     | 
    
         
            +
              t.source_files = FileList[%w[Gemfile Rakefile lib/**/*.rb test/**/*.rb]]
         
     | 
| 
      
 487 
     | 
    
         
            +
            end
         
     | 
| 
      
 488 
     | 
    
         
            +
            ```
         
     | 
| 
      
 489 
     | 
    
         
            +
             
     | 
| 
      
 490 
     | 
    
         
            +
            #### `plugins`
         
     | 
| 
      
 491 
     | 
    
         
            +
             
     | 
| 
      
 492 
     | 
    
         
            +
            If you're running Syntax Tree with plugins (either your own or the pre-built ones), you can pass that to the `plugins` field, as in:
         
     | 
| 
      
 493 
     | 
    
         
            +
             
     | 
| 
      
 494 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 495 
     | 
    
         
            +
            SyntaxTree::Rake::WriteTask.new do |t|
         
     | 
| 
      
 496 
     | 
    
         
            +
              t.plugins = ["plugin/single_quotes"]
         
     | 
| 
      
 497 
     | 
    
         
            +
            end
         
     | 
| 
      
 498 
     | 
    
         
            +
            ```
         
     | 
| 
      
 499 
     | 
    
         
            +
             
     | 
| 
       439 
500 
     | 
    
         
             
            ### RuboCop
         
     | 
| 
       440 
501 
     | 
    
         | 
| 
       441 
502 
     | 
    
         
             
            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`:
         
     | 
    
        data/Rakefile
    CHANGED
    
    | 
         @@ -2,6 +2,7 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require "bundler/gem_tasks"
         
     | 
| 
       4 
4 
     | 
    
         
             
            require "rake/testtask"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "syntax_tree/rake_tasks"
         
     | 
| 
       5 
6 
     | 
    
         | 
| 
       6 
7 
     | 
    
         
             
            Rake::TestTask.new(:test) do |t|
         
     | 
| 
       7 
8 
     | 
    
         
             
              t.libs << "test"
         
     | 
| 
         @@ -11,24 +12,8 @@ end 
     | 
|
| 
       11 
12 
     | 
    
         | 
| 
       12 
13 
     | 
    
         
             
            task default: :test
         
     | 
| 
       13 
14 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
              Gemfile
         
     | 
| 
       16 
     | 
    
         
            -
              Rakefile
         
     | 
| 
       17 
     | 
    
         
            -
              syntax_tree.gemspec
         
     | 
| 
       18 
     | 
    
         
            -
              lib/**/*.rb
         
     | 
| 
       19 
     | 
    
         
            -
              test/*.rb
         
     | 
| 
       20 
     | 
    
         
            -
            ].freeze
         
     | 
| 
      
 15 
     | 
    
         
            +
            SOURCE_FILES =
         
     | 
| 
      
 16 
     | 
    
         
            +
              FileList[%w[Gemfile Rakefile syntax_tree.gemspec lib/**/*.rb test/*.rb]]
         
     | 
| 
       21 
17 
     | 
    
         | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       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
         
     | 
| 
      
 18 
     | 
    
         
            +
            SyntaxTree::Rake::CheckTask.new { |t| t.source_files = SOURCE_FILES }
         
     | 
| 
      
 19 
     | 
    
         
            +
            SyntaxTree::Rake::WriteTask.new { |t| t.source_files = SOURCE_FILES }
         
     | 
| 
         @@ -0,0 +1,74 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module SyntaxTree
         
     | 
| 
      
 4 
     | 
    
         
            +
              # BasicVisitor is the parent class of the Visitor class that provides the
         
     | 
| 
      
 5 
     | 
    
         
            +
              # ability to walk down the tree. It does not define any handlers, so you
         
     | 
| 
      
 6 
     | 
    
         
            +
              # should extend this class if you want your visitor to raise an error if you
         
     | 
| 
      
 7 
     | 
    
         
            +
              # attempt to visit a node that you don't handle.
         
     | 
| 
      
 8 
     | 
    
         
            +
              class BasicVisitor
         
     | 
| 
      
 9 
     | 
    
         
            +
                # This is raised when you use the Visitor.visit_method method and it fails.
         
     | 
| 
      
 10 
     | 
    
         
            +
                # It is correctable to through DidYouMean.
         
     | 
| 
      
 11 
     | 
    
         
            +
                class VisitMethodError < StandardError
         
     | 
| 
      
 12 
     | 
    
         
            +
                  attr_reader :visit_method
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def initialize(visit_method)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @visit_method = visit_method
         
     | 
| 
      
 16 
     | 
    
         
            +
                    super("Invalid visit method: #{visit_method}")
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                # This class is used by DidYouMean to offer corrections to invalid visit
         
     | 
| 
      
 21 
     | 
    
         
            +
                # method names.
         
     | 
| 
      
 22 
     | 
    
         
            +
                class VisitMethodChecker
         
     | 
| 
      
 23 
     | 
    
         
            +
                  attr_reader :visit_method
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  def initialize(error)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @visit_method = error.visit_method
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def corrections
         
     | 
| 
      
 30 
     | 
    
         
            +
                    @corrections ||=
         
     | 
| 
      
 31 
     | 
    
         
            +
                      DidYouMean::SpellChecker.new(
         
     | 
| 
      
 32 
     | 
    
         
            +
                        dictionary: Visitor.visit_methods
         
     | 
| 
      
 33 
     | 
    
         
            +
                      ).correct(visit_method)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  DidYouMean.correct_error(VisitMethodError, self)
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 40 
     | 
    
         
            +
                  # This method is here to help folks write visitors.
         
     | 
| 
      
 41 
     | 
    
         
            +
                  #
         
     | 
| 
      
 42 
     | 
    
         
            +
                  # It's not always easy to ensure you're writing the correct method name in
         
     | 
| 
      
 43 
     | 
    
         
            +
                  # the visitor since it's perfectly valid to define methods that don't
         
     | 
| 
      
 44 
     | 
    
         
            +
                  # override these parent methods.
         
     | 
| 
      
 45 
     | 
    
         
            +
                  #
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # If you use this method, you can ensure you're writing the correct method
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # name. It will raise an error if the visit method you're defining isn't
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # actually a method on the parent visitor.
         
     | 
| 
      
 49 
     | 
    
         
            +
                  def visit_method(method_name)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    return if visit_methods.include?(method_name)
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    raise VisitMethodError, method_name
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  # This is the list of all of the valid visit methods.
         
     | 
| 
      
 56 
     | 
    
         
            +
                  def visit_methods
         
     | 
| 
      
 57 
     | 
    
         
            +
                    @visit_methods ||=
         
     | 
| 
      
 58 
     | 
    
         
            +
                      Visitor.instance_methods.grep(/^visit_(?!child_nodes)/)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                def visit(node)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  node&.accept(self)
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                def visit_all(nodes)
         
     | 
| 
      
 67 
     | 
    
         
            +
                  nodes.map { |node| visit(node) }
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                def visit_child_nodes(node)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  visit_all(node.child_nodes)
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -4,17 +4,42 @@ module SyntaxTree 
     | 
|
| 
       4 
4 
     | 
    
         
             
              # A slightly enhanced PP that knows how to format recursively including
         
     | 
| 
       5 
5 
     | 
    
         
             
              # comments.
         
     | 
| 
       6 
6 
     | 
    
         
             
              class Formatter < PrettierPrint
         
     | 
| 
      
 7 
     | 
    
         
            +
                # We want to minimize as much as possible the number of options that are
         
     | 
| 
      
 8 
     | 
    
         
            +
                # available in syntax tree. For the most part, if users want non-default
         
     | 
| 
      
 9 
     | 
    
         
            +
                # formatting, they should override the format methods on the specific nodes
         
     | 
| 
      
 10 
     | 
    
         
            +
                # themselves. However, because of some history with prettier and the fact
         
     | 
| 
      
 11 
     | 
    
         
            +
                # that folks have become entrenched in their ways, we decided to provide a
         
     | 
| 
      
 12 
     | 
    
         
            +
                # small amount of configurability.
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                # Note that we're keeping this in a global-ish hash instead of just
         
     | 
| 
      
 15 
     | 
    
         
            +
                # overriding methods on classes so that other plugins can reference this if
         
     | 
| 
      
 16 
     | 
    
         
            +
                # necessary. For example, the RBS plugin references the quote style.
         
     | 
| 
      
 17 
     | 
    
         
            +
                OPTIONS = { quote: "\"", trailing_comma: false }
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
       7 
19 
     | 
    
         
             
                COMMENT_PRIORITY = 1
         
     | 
| 
       8 
20 
     | 
    
         
             
                HEREDOC_PRIORITY = 2
         
     | 
| 
       9 
21 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
                attr_reader :source, :stack 
     | 
| 
      
 22 
     | 
    
         
            +
                attr_reader :source, :stack
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                # These options are overridden in plugins to we need to make sure they are
         
     | 
| 
      
 25 
     | 
    
         
            +
                # available here.
         
     | 
| 
      
 26 
     | 
    
         
            +
                attr_reader :quote, :trailing_comma
         
     | 
| 
      
 27 
     | 
    
         
            +
                alias trailing_comma? trailing_comma
         
     | 
| 
       11 
28 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
                def initialize( 
     | 
| 
       13 
     | 
    
         
            -
                   
     | 
| 
      
 29 
     | 
    
         
            +
                def initialize(
         
     | 
| 
      
 30 
     | 
    
         
            +
                  source,
         
     | 
| 
      
 31 
     | 
    
         
            +
                  *args,
         
     | 
| 
      
 32 
     | 
    
         
            +
                  quote: OPTIONS[:quote],
         
     | 
| 
      
 33 
     | 
    
         
            +
                  trailing_comma: OPTIONS[:trailing_comma]
         
     | 
| 
      
 34 
     | 
    
         
            +
                )
         
     | 
| 
      
 35 
     | 
    
         
            +
                  super(*args)
         
     | 
| 
       14 
36 
     | 
    
         | 
| 
       15 
37 
     | 
    
         
             
                  @source = source
         
     | 
| 
       16 
38 
     | 
    
         
             
                  @stack = []
         
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  # Memoizing these values per formatter to make access faster.
         
     | 
| 
      
 41 
     | 
    
         
            +
                  @quote = quote
         
     | 
| 
      
 42 
     | 
    
         
            +
                  @trailing_comma = trailing_comma
         
     | 
| 
       18 
43 
     | 
    
         
             
                end
         
     | 
| 
       19 
44 
     | 
    
         | 
| 
       20 
45 
     | 
    
         
             
                def self.format(source, node)
         
     | 
| 
         @@ -38,6 +38,7 @@ module SyntaxTree 
     | 
|
| 
       38 
38 
     | 
    
         
             
                  #
         
     | 
| 
       39 
39 
     | 
    
         
             
                  def visit_assign(node)
         
     | 
| 
       40 
40 
     | 
    
         
             
                    parentheses(node.location) if stack[-2].is_a?(Params)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    super
         
     | 
| 
       41 
42 
     | 
    
         
             
                  end
         
     | 
| 
       42 
43 
     | 
    
         | 
| 
       43 
44 
     | 
    
         
             
                  # Adds parentheses around binary expressions to make it clear which
         
     | 
| 
         @@ -57,6 +58,8 @@ module SyntaxTree 
     | 
|
| 
       57 
58 
     | 
    
         
             
                      parentheses(node.location)
         
     | 
| 
       58 
59 
     | 
    
         
             
                    else
         
     | 
| 
       59 
60 
     | 
    
         
             
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                    super
         
     | 
| 
       60 
63 
     | 
    
         
             
                  end
         
     | 
| 
       61 
64 
     | 
    
         | 
| 
       62 
65 
     | 
    
         
             
                  # Adds parentheses around ternary operators contained within certain
         
     | 
| 
         @@ -70,9 +73,13 @@ module SyntaxTree 
     | 
|
| 
       70 
73 
     | 
    
         
             
                  #     a ? b : ₍c ? d : e₎
         
     | 
| 
       71 
74 
     | 
    
         
             
                  #
         
     | 
| 
       72 
75 
     | 
    
         
             
                  def visit_if_op(node)
         
     | 
| 
       73 
     | 
    
         
            -
                     
     | 
| 
      
 76 
     | 
    
         
            +
                    case stack[-2]
         
     | 
| 
      
 77 
     | 
    
         
            +
                    in Assign | Binary | IfOp | OpAssign
         
     | 
| 
       74 
78 
     | 
    
         
             
                      parentheses(node.location)
         
     | 
| 
      
 79 
     | 
    
         
            +
                    else
         
     | 
| 
       75 
80 
     | 
    
         
             
                    end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                    super
         
     | 
| 
       76 
83 
     | 
    
         
             
                  end
         
     | 
| 
       77 
84 
     | 
    
         | 
| 
       78 
85 
     | 
    
         
             
                  # Adds the implicitly rescued StandardError into a bare rescue clause. For
         
     | 
| 
         @@ -92,6 +99,8 @@ module SyntaxTree 
     | 
|
| 
       92 
99 
     | 
    
         
             
                    if node.exception.nil?
         
     | 
| 
       93 
100 
     | 
    
         
             
                      after[node.location.start_char + "rescue".length] << " StandardError"
         
     | 
| 
       94 
101 
     | 
    
         
             
                    end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                    super
         
     | 
| 
       95 
104 
     | 
    
         
             
                  end
         
     | 
| 
       96 
105 
     | 
    
         | 
| 
       97 
106 
     | 
    
         
             
                  # Adds parentheses around unary statements using the - operator that are
         
     | 
| 
         @@ -107,6 +116,8 @@ module SyntaxTree 
     | 
|
| 
       107 
116 
     | 
    
         
             
                    if stack[-2].is_a?(Binary) && (node.operator == "-")
         
     | 
| 
       108 
117 
     | 
    
         
             
                      parentheses(node.location)
         
     | 
| 
       109 
118 
     | 
    
         
             
                    end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                    super
         
     | 
| 
       110 
121 
     | 
    
         
             
                  end
         
     | 
| 
       111 
122 
     | 
    
         | 
| 
       112 
123 
     | 
    
         
             
                  def self.find(program)
         
     | 
| 
         @@ -70,13 +70,11 @@ module SyntaxTree 
     | 
|
| 
       70 
70 
     | 
    
         
             
                         id:,
         
     | 
| 
       71 
71 
     | 
    
         
             
                         params: { textDocument: { uri: } }
         
     | 
| 
       72 
72 
     | 
    
         
             
                       }
         
     | 
| 
       73 
     | 
    
         
            -
                       
     | 
| 
       74 
     | 
    
         
            -
                      PP.pp(SyntaxTree.parse(store[uri]), output)
         
     | 
| 
       75 
     | 
    
         
            -
                      write(id: id, result: output.join)
         
     | 
| 
      
 73 
     | 
    
         
            +
                      write(id: id, result: PP.pp(SyntaxTree.parse(store[uri]), +""))
         
     | 
| 
       76 
74 
     | 
    
         
             
                    in method: %r{\$/.+}
         
     | 
| 
       77 
75 
     | 
    
         
             
                      # ignored
         
     | 
| 
       78 
76 
     | 
    
         
             
                    else
         
     | 
| 
       79 
     | 
    
         
            -
                      raise "Unhandled: #{request}"
         
     | 
| 
      
 77 
     | 
    
         
            +
                      raise ArgumentError, "Unhandled: #{request}"
         
     | 
| 
       80 
78 
     | 
    
         
             
                    end
         
     | 
| 
       81 
79 
     | 
    
         
             
                  end
         
     | 
| 
       82 
80 
     | 
    
         
             
                end
         
     | 
| 
         @@ -109,10 +107,6 @@ module SyntaxTree 
     | 
|
| 
       109 
107 
     | 
    
         
             
                  }
         
     | 
| 
       110 
108 
     | 
    
         
             
                end
         
     | 
| 
       111 
109 
     | 
    
         | 
| 
       112 
     | 
    
         
            -
                def log(message)
         
     | 
| 
       113 
     | 
    
         
            -
                  write(method: "window/logMessage", params: { type: 4, message: message })
         
     | 
| 
       114 
     | 
    
         
            -
                end
         
     | 
| 
       115 
     | 
    
         
            -
             
     | 
| 
       116 
110 
     | 
    
         
             
                def inlay_hints(source)
         
     | 
| 
       117 
111 
     | 
    
         
             
                  inlay_hints = InlayHints.find(SyntaxTree.parse(source))
         
     | 
| 
       118 
112 
     | 
    
         
             
                  serialize = ->(position, text) { { position: position, text: text } }
         
     | 
    
        data/lib/syntax_tree/node.rb
    CHANGED
    
    | 
         @@ -597,10 +597,30 @@ module SyntaxTree 
     | 
|
| 
       597 
597 
     | 
    
         
             
                    q.indent do
         
     | 
| 
       598 
598 
     | 
    
         
             
                      q.breakable("")
         
     | 
| 
       599 
599 
     | 
    
         
             
                      q.format(arguments)
         
     | 
| 
      
 600 
     | 
    
         
            +
                      q.if_break { q.text(",") } if q.trailing_comma? && trailing_comma?
         
     | 
| 
       600 
601 
     | 
    
         
             
                    end
         
     | 
| 
       601 
602 
     | 
    
         
             
                    q.breakable("")
         
     | 
| 
       602 
603 
     | 
    
         
             
                  end
         
     | 
| 
       603 
604 
     | 
    
         
             
                end
         
     | 
| 
      
 605 
     | 
    
         
            +
             
     | 
| 
      
 606 
     | 
    
         
            +
                private
         
     | 
| 
      
 607 
     | 
    
         
            +
             
     | 
| 
      
 608 
     | 
    
         
            +
                def trailing_comma?
         
     | 
| 
      
 609 
     | 
    
         
            +
                  case arguments
         
     | 
| 
      
 610 
     | 
    
         
            +
                  in Args[parts: [*, ArgBlock]]
         
     | 
| 
      
 611 
     | 
    
         
            +
                    # If the last argument is a block, then we can't put a trailing comma
         
     | 
| 
      
 612 
     | 
    
         
            +
                    # after it without resulting in a syntax error.
         
     | 
| 
      
 613 
     | 
    
         
            +
                    false
         
     | 
| 
      
 614 
     | 
    
         
            +
                  in Args[parts: [Command | CommandCall]]
         
     | 
| 
      
 615 
     | 
    
         
            +
                    # If the only argument is a command or command call, then a trailing
         
     | 
| 
      
 616 
     | 
    
         
            +
                    # comma would be parsed as part of that expression instead of on this
         
     | 
| 
      
 617 
     | 
    
         
            +
                    # one, so we don't want to add a trailing comma.
         
     | 
| 
      
 618 
     | 
    
         
            +
                    false
         
     | 
| 
      
 619 
     | 
    
         
            +
                  else
         
     | 
| 
      
 620 
     | 
    
         
            +
                    # Otherwise, we should be okay to add a trailing comma.
         
     | 
| 
      
 621 
     | 
    
         
            +
                    true
         
     | 
| 
      
 622 
     | 
    
         
            +
                  end
         
     | 
| 
      
 623 
     | 
    
         
            +
                end
         
     | 
| 
       604 
624 
     | 
    
         
             
              end
         
     | 
| 
       605 
625 
     | 
    
         | 
| 
       606 
626 
     | 
    
         
             
              # Args represents a list of arguments being passed to a method call or array
         
     | 
| 
         @@ -859,6 +879,7 @@ module SyntaxTree 
     | 
|
| 
       859 
879 
     | 
    
         
             
                        end
         
     | 
| 
       860 
880 
     | 
    
         | 
| 
       861 
881 
     | 
    
         
             
                        q.seplist(contents.parts, separator) { |part| q.format(part) }
         
     | 
| 
      
 882 
     | 
    
         
            +
                        q.if_break { q.text(",") } if q.trailing_comma?
         
     | 
| 
       862 
883 
     | 
    
         
             
                      end
         
     | 
| 
       863 
884 
     | 
    
         
             
                      q.breakable("")
         
     | 
| 
       864 
885 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -954,6 +975,7 @@ module SyntaxTree 
     | 
|
| 
       954 
975 
     | 
    
         
             
                      q.indent do
         
     | 
| 
       955 
976 
     | 
    
         
             
                        q.breakable("")
         
     | 
| 
       956 
977 
     | 
    
         
             
                        q.format(contents)
         
     | 
| 
      
 978 
     | 
    
         
            +
                        q.if_break { q.text(",") } if q.trailing_comma?
         
     | 
| 
       957 
979 
     | 
    
         
             
                      end
         
     | 
| 
       958 
980 
     | 
    
         
             
                    end
         
     | 
| 
       959 
981 
     | 
    
         | 
| 
         @@ -1101,30 +1123,20 @@ module SyntaxTree 
     | 
|
| 
       1101 
1123 
     | 
    
         
             
                end
         
     | 
| 
       1102 
1124 
     | 
    
         | 
| 
       1103 
1125 
     | 
    
         
             
                def format(q)
         
     | 
| 
       1104 
     | 
    
         
            -
                   
     | 
| 
       1105 
     | 
    
         
            -
             
     | 
| 
       1106 
     | 
    
         
            -
             
     | 
| 
      
 1126 
     | 
    
         
            +
                  q.group do
         
     | 
| 
      
 1127 
     | 
    
         
            +
                    q.format(constant) if constant
         
     | 
| 
      
 1128 
     | 
    
         
            +
                    q.text("[")
         
     | 
| 
      
 1129 
     | 
    
         
            +
                    q.indent do
         
     | 
| 
      
 1130 
     | 
    
         
            +
                      q.breakable("")
         
     | 
| 
      
 1131 
     | 
    
         
            +
             
     | 
| 
      
 1132 
     | 
    
         
            +
                      parts = [*requireds]
         
     | 
| 
      
 1133 
     | 
    
         
            +
                      parts << RestFormatter.new(rest) if rest
         
     | 
| 
      
 1134 
     | 
    
         
            +
                      parts += posts
         
     | 
| 
       1107 
1135 
     | 
    
         | 
| 
       1108 
     | 
    
         
            -
                  if constant
         
     | 
| 
       1109 
     | 
    
         
            -
                    q.group do
         
     | 
| 
       1110 
     | 
    
         
            -
                      q.format(constant)
         
     | 
| 
       1111 
     | 
    
         
            -
                      q.text("[")
         
     | 
| 
       1112 
1136 
     | 
    
         
             
                      q.seplist(parts) { |part| q.format(part) }
         
     | 
| 
       1113 
     | 
    
         
            -
                      q.text("]")
         
     | 
| 
       1114 
1137 
     | 
    
         
             
                    end
         
     | 
| 
       1115 
     | 
    
         
            -
             
     | 
| 
       1116 
     | 
    
         
            -
                    return
         
     | 
| 
       1117 
     | 
    
         
            -
                  end
         
     | 
| 
       1118 
     | 
    
         
            -
             
     | 
| 
       1119 
     | 
    
         
            -
                  parent = q.parent
         
     | 
| 
       1120 
     | 
    
         
            -
                  if parts.length == 1 || PATTERNS.include?(parent.class)
         
     | 
| 
       1121 
     | 
    
         
            -
                    q.text("[")
         
     | 
| 
       1122 
     | 
    
         
            -
                    q.seplist(parts) { |part| q.format(part) }
         
     | 
| 
      
 1138 
     | 
    
         
            +
                    q.breakable("")
         
     | 
| 
       1123 
1139 
     | 
    
         
             
                    q.text("]")
         
     | 
| 
       1124 
     | 
    
         
            -
                  elsif parts.empty?
         
     | 
| 
       1125 
     | 
    
         
            -
                    q.text("[]")
         
     | 
| 
       1126 
     | 
    
         
            -
                  else
         
     | 
| 
       1127 
     | 
    
         
            -
                    q.group { q.seplist(parts) { |part| q.format(part) } }
         
     | 
| 
       1128 
1140 
     | 
    
         
             
                  end
         
     | 
| 
       1129 
1141 
     | 
    
         
             
                end
         
     | 
| 
       1130 
1142 
     | 
    
         
             
              end
         
     | 
| 
         @@ -2117,11 +2129,13 @@ module SyntaxTree 
     | 
|
| 
       2117 
2129 
     | 
    
         
             
                      #
         
     | 
| 
       2118 
2130 
     | 
    
         
             
                      #     break
         
     | 
| 
       2119 
2131 
     | 
    
         
             
                      #
         
     | 
| 
       2120 
     | 
    
         
            -
                    in [ 
     | 
| 
       2121 
     | 
    
         
            -
                          
     | 
| 
       2122 
     | 
    
         
            -
                            
     | 
| 
       2123 
     | 
    
         
            -
             
     | 
| 
       2124 
     | 
    
         
            -
             
     | 
| 
      
 2132 
     | 
    
         
            +
                    in [
         
     | 
| 
      
 2133 
     | 
    
         
            +
                         Paren[
         
     | 
| 
      
 2134 
     | 
    
         
            +
                           contents: {
         
     | 
| 
      
 2135 
     | 
    
         
            +
                             body: [ArrayLiteral[contents: { parts: [_, _, *] }] => array]
         
     | 
| 
      
 2136 
     | 
    
         
            +
                           }
         
     | 
| 
      
 2137 
     | 
    
         
            +
                         ]
         
     | 
| 
      
 2138 
     | 
    
         
            +
                       ]
         
     | 
| 
       2125 
2139 
     | 
    
         
             
                      # Here we have a single argument that is a set of parentheses wrapping
         
     | 
| 
       2126 
2140 
     | 
    
         
             
                      # an array literal that has at least 2 elements. We're going to print
         
     | 
| 
       2127 
2141 
     | 
    
         
             
                      # the contents of the array directly. This would be like if we had:
         
     | 
| 
         @@ -2755,10 +2769,17 @@ module SyntaxTree 
     | 
|
| 
       2755 
2769 
     | 
    
         
             
                    q.format(value)
         
     | 
| 
       2756 
2770 
     | 
    
         
             
                    q.text(" ")
         
     | 
| 
       2757 
2771 
     | 
    
         
             
                    q.format(operator)
         
     | 
| 
       2758 
     | 
    
         
            -
             
     | 
| 
       2759 
     | 
    
         
            -
             
     | 
| 
       2760 
     | 
    
         
            -
             
     | 
| 
       2761 
     | 
    
         
            -
             
     | 
| 
      
 2772 
     | 
    
         
            +
             
     | 
| 
      
 2773 
     | 
    
         
            +
                    case pattern
         
     | 
| 
      
 2774 
     | 
    
         
            +
                    in AryPtn | FndPtn | HshPtn
         
     | 
| 
      
 2775 
     | 
    
         
            +
                      q.text(" ")
         
     | 
| 
      
 2776 
     | 
    
         
            +
                      q.format(pattern)
         
     | 
| 
      
 2777 
     | 
    
         
            +
                    else
         
     | 
| 
      
 2778 
     | 
    
         
            +
                      q.group do
         
     | 
| 
      
 2779 
     | 
    
         
            +
                        q.indent do
         
     | 
| 
      
 2780 
     | 
    
         
            +
                          q.breakable
         
     | 
| 
      
 2781 
     | 
    
         
            +
                          q.format(pattern)
         
     | 
| 
      
 2782 
     | 
    
         
            +
                        end
         
     | 
| 
       2762 
2783 
     | 
    
         
             
                      end
         
     | 
| 
       2763 
2784 
     | 
    
         
             
                    end
         
     | 
| 
       2764 
2785 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -3030,8 +3051,20 @@ module SyntaxTree 
     | 
|
| 
       3030 
3051 
     | 
    
         
             
                    doc =
         
     | 
| 
       3031 
3052 
     | 
    
         
             
                      q.nest(0) do
         
     | 
| 
       3032 
3053 
     | 
    
         
             
                        q.format(receiver)
         
     | 
| 
       3033 
     | 
    
         
            -
             
     | 
| 
       3034 
     | 
    
         
            -
                         
     | 
| 
      
 3054 
     | 
    
         
            +
             
     | 
| 
      
 3055 
     | 
    
         
            +
                        # If there are leading comments on the message then we know we have
         
     | 
| 
      
 3056 
     | 
    
         
            +
                        # a newline in the source that is forcing these things apart. In
         
     | 
| 
      
 3057 
     | 
    
         
            +
                        # this case we will have to use a trailing operator.
         
     | 
| 
      
 3058 
     | 
    
         
            +
                        if message.comments.any?(&:leading?)
         
     | 
| 
      
 3059 
     | 
    
         
            +
                          q.format(CallOperatorFormatter.new(operator), stackable: false)
         
     | 
| 
      
 3060 
     | 
    
         
            +
                          q.indent do
         
     | 
| 
      
 3061 
     | 
    
         
            +
                            q.breakable("")
         
     | 
| 
      
 3062 
     | 
    
         
            +
                            q.format(message)
         
     | 
| 
      
 3063 
     | 
    
         
            +
                          end
         
     | 
| 
      
 3064 
     | 
    
         
            +
                        else
         
     | 
| 
      
 3065 
     | 
    
         
            +
                          q.format(CallOperatorFormatter.new(operator), stackable: false)
         
     | 
| 
      
 3066 
     | 
    
         
            +
                          q.format(message)
         
     | 
| 
      
 3067 
     | 
    
         
            +
                        end
         
     | 
| 
       3035 
3068 
     | 
    
         
             
                      end
         
     | 
| 
       3036 
3069 
     | 
    
         | 
| 
       3037 
3070 
     | 
    
         
             
                    case arguments
         
     | 
| 
         @@ -3830,9 +3863,9 @@ module SyntaxTree 
     | 
|
| 
       3830 
3863 
     | 
    
         
             
                # whichever quote the user chose. (If they chose single quotes, then double
         
     | 
| 
       3831 
3864 
     | 
    
         
             
                # quoting would activate the escape sequence, and if they chose double
         
     | 
| 
       3832 
3865 
     | 
    
         
             
                # quotes, then single quotes would deactivate it.)
         
     | 
| 
       3833 
     | 
    
         
            -
                def self.locked?(node)
         
     | 
| 
      
 3866 
     | 
    
         
            +
                def self.locked?(node, quote)
         
     | 
| 
       3834 
3867 
     | 
    
         
             
                  node.parts.any? do |part|
         
     | 
| 
       3835 
     | 
    
         
            -
                    !part.is_a?(TStringContent) || part.value.match?(/\\|#[@${]/)
         
     | 
| 
      
 3868 
     | 
    
         
            +
                    !part.is_a?(TStringContent) || part.value.match?(/\\|#[@${]|#{quote}/)
         
     | 
| 
       3836 
3869 
     | 
    
         
             
                  end
         
     | 
| 
       3837 
3870 
     | 
    
         
             
                end
         
     | 
| 
       3838 
3871 
     | 
    
         | 
| 
         @@ -3947,12 +3980,12 @@ module SyntaxTree 
     | 
|
| 
       3947 
3980 
     | 
    
         | 
| 
       3948 
3981 
     | 
    
         
             
                    if matched
         
     | 
| 
       3949 
3982 
     | 
    
         
             
                      [quote, matching]
         
     | 
| 
       3950 
     | 
    
         
            -
                    elsif Quotes.locked?(self)
         
     | 
| 
      
 3983 
     | 
    
         
            +
                    elsif Quotes.locked?(self, q.quote)
         
     | 
| 
       3951 
3984 
     | 
    
         
             
                      ["#{":" unless hash_key}'", "'"]
         
     | 
| 
       3952 
3985 
     | 
    
         
             
                    else
         
     | 
| 
       3953 
3986 
     | 
    
         
             
                      ["#{":" unless hash_key}#{q.quote}", q.quote]
         
     | 
| 
       3954 
3987 
     | 
    
         
             
                    end
         
     | 
| 
       3955 
     | 
    
         
            -
                  elsif Quotes.locked?(self)
         
     | 
| 
      
 3988 
     | 
    
         
            +
                  elsif Quotes.locked?(self, q.quote)
         
     | 
| 
       3956 
3989 
     | 
    
         
             
                    if quote.start_with?(":")
         
     | 
| 
       3957 
3990 
     | 
    
         
             
                      [hash_key ? quote[1..] : quote, quote[1..]]
         
     | 
| 
       3958 
3991 
     | 
    
         
             
                    else
         
     | 
| 
         @@ -4539,16 +4572,26 @@ module SyntaxTree 
     | 
|
| 
       4539 
4572 
     | 
    
         | 
| 
       4540 
4573 
     | 
    
         
             
                def format(q)
         
     | 
| 
       4541 
4574 
     | 
    
         
             
                  q.format(constant) if constant
         
     | 
| 
       4542 
     | 
    
         
            -
                  q.group(0, "[", "]") do
         
     | 
| 
       4543 
     | 
    
         
            -
                    q.text("*")
         
     | 
| 
       4544 
     | 
    
         
            -
                    q.format(left)
         
     | 
| 
       4545 
     | 
    
         
            -
                    q.comma_breakable
         
     | 
| 
       4546 
4575 
     | 
    
         | 
| 
       4547 
     | 
    
         
            -
             
     | 
| 
       4548 
     | 
    
         
            -
                    q. 
     | 
| 
      
 4576 
     | 
    
         
            +
                  q.group do
         
     | 
| 
      
 4577 
     | 
    
         
            +
                    q.text("[")
         
     | 
| 
       4549 
4578 
     | 
    
         | 
| 
       4550 
     | 
    
         
            -
                    q. 
     | 
| 
       4551 
     | 
    
         
            -
             
     | 
| 
      
 4579 
     | 
    
         
            +
                    q.indent do
         
     | 
| 
      
 4580 
     | 
    
         
            +
                      q.breakable("")
         
     | 
| 
      
 4581 
     | 
    
         
            +
             
     | 
| 
      
 4582 
     | 
    
         
            +
                      q.text("*")
         
     | 
| 
      
 4583 
     | 
    
         
            +
                      q.format(left)
         
     | 
| 
      
 4584 
     | 
    
         
            +
                      q.comma_breakable
         
     | 
| 
      
 4585 
     | 
    
         
            +
             
     | 
| 
      
 4586 
     | 
    
         
            +
                      q.seplist(values) { |value| q.format(value) }
         
     | 
| 
      
 4587 
     | 
    
         
            +
                      q.comma_breakable
         
     | 
| 
      
 4588 
     | 
    
         
            +
             
     | 
| 
      
 4589 
     | 
    
         
            +
                      q.text("*")
         
     | 
| 
      
 4590 
     | 
    
         
            +
                      q.format(right)
         
     | 
| 
      
 4591 
     | 
    
         
            +
                    end
         
     | 
| 
      
 4592 
     | 
    
         
            +
             
     | 
| 
      
 4593 
     | 
    
         
            +
                    q.breakable("")
         
     | 
| 
      
 4594 
     | 
    
         
            +
                    q.text("]")
         
     | 
| 
       4552 
4595 
     | 
    
         
             
                  end
         
     | 
| 
       4553 
4596 
     | 
    
         
             
                end
         
     | 
| 
       4554 
4597 
     | 
    
         
             
              end
         
     | 
| 
         @@ -4751,6 +4794,7 @@ module SyntaxTree 
     | 
|
| 
       4751 
4794 
     | 
    
         
             
                    q.indent do
         
     | 
| 
       4752 
4795 
     | 
    
         
             
                      q.breakable
         
     | 
| 
       4753 
4796 
     | 
    
         
             
                      q.seplist(assocs) { |assoc| q.format(assoc) }
         
     | 
| 
      
 4797 
     | 
    
         
            +
                      q.if_break { q.text(",") } if q.trailing_comma?
         
     | 
| 
       4754 
4798 
     | 
    
         
             
                    end
         
     | 
| 
       4755 
4799 
     | 
    
         
             
                    q.breakable
         
     | 
| 
       4756 
4800 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -5430,12 +5474,14 @@ module SyntaxTree 
     | 
|
| 
       5430 
5474 
     | 
    
         
             
                  q.format(predicate)
         
     | 
| 
       5431 
5475 
     | 
    
         
             
                  q.text(" ?")
         
     | 
| 
       5432 
5476 
     | 
    
         | 
| 
       5433 
     | 
    
         
            -
                  q. 
     | 
| 
       5434 
     | 
    
         
            -
             
     | 
| 
       5435 
     | 
    
         
            -
             
     | 
| 
      
 5477 
     | 
    
         
            +
                  q.indent do
         
     | 
| 
      
 5478 
     | 
    
         
            +
                    q.breakable
         
     | 
| 
      
 5479 
     | 
    
         
            +
                    q.format(truthy)
         
     | 
| 
      
 5480 
     | 
    
         
            +
                    q.text(" :")
         
     | 
| 
       5436 
5481 
     | 
    
         | 
| 
       5437 
     | 
    
         
            -
             
     | 
| 
       5438 
     | 
    
         
            -
             
     | 
| 
      
 5482 
     | 
    
         
            +
                    q.breakable
         
     | 
| 
      
 5483 
     | 
    
         
            +
                    q.format(falsy)
         
     | 
| 
      
 5484 
     | 
    
         
            +
                  end
         
     | 
| 
       5439 
5485 
     | 
    
         
             
                end
         
     | 
| 
       5440 
5486 
     | 
    
         
             
              end
         
     | 
| 
       5441 
5487 
     | 
    
         | 
| 
         @@ -5877,7 +5923,7 @@ module SyntaxTree 
     | 
|
| 
       5877 
5923 
     | 
    
         
             
              #     ->(value) { value * 2 }
         
     | 
| 
       5878 
5924 
     | 
    
         
             
              #
         
     | 
| 
       5879 
5925 
     | 
    
         
             
              class Lambda < Node
         
     | 
| 
       5880 
     | 
    
         
            -
                # [ 
     | 
| 
      
 5926 
     | 
    
         
            +
                # [LambdaVar | Paren] the parameter declaration for this lambda
         
     | 
| 
       5881 
5927 
     | 
    
         
             
                attr_reader :params
         
     | 
| 
       5882 
5928 
     | 
    
         | 
| 
       5883 
5929 
     | 
    
         
             
                # [BodyStmt | Statements] the expressions to be executed in this lambda
         
     | 
| 
         @@ -5932,24 +5978,100 @@ module SyntaxTree 
     | 
|
| 
       5932 
5978 
     | 
    
         
             
                            node.is_a?(Command) || node.is_a?(CommandCall)
         
     | 
| 
       5933 
5979 
     | 
    
         
             
                          end
         
     | 
| 
       5934 
5980 
     | 
    
         | 
| 
       5935 
     | 
    
         
            -
                         
     | 
| 
       5936 
     | 
    
         
            -
             
     | 
| 
      
 5981 
     | 
    
         
            +
                        if force_parens
         
     | 
| 
      
 5982 
     | 
    
         
            +
                          q.text("{")
         
     | 
| 
      
 5983 
     | 
    
         
            +
             
     | 
| 
      
 5984 
     | 
    
         
            +
                          unless statements.empty?
         
     | 
| 
      
 5985 
     | 
    
         
            +
                            q.indent do
         
     | 
| 
      
 5986 
     | 
    
         
            +
                              q.breakable
         
     | 
| 
      
 5987 
     | 
    
         
            +
                              q.format(statements)
         
     | 
| 
      
 5988 
     | 
    
         
            +
                            end
         
     | 
| 
      
 5989 
     | 
    
         
            +
                            q.breakable
         
     | 
| 
      
 5990 
     | 
    
         
            +
                          end
         
     | 
| 
      
 5991 
     | 
    
         
            +
             
     | 
| 
      
 5992 
     | 
    
         
            +
                          q.text("}")
         
     | 
| 
      
 5993 
     | 
    
         
            +
                        else
         
     | 
| 
      
 5994 
     | 
    
         
            +
                          q.text("do")
         
     | 
| 
      
 5995 
     | 
    
         
            +
             
     | 
| 
      
 5996 
     | 
    
         
            +
                          unless statements.empty?
         
     | 
| 
      
 5997 
     | 
    
         
            +
                            q.indent do
         
     | 
| 
      
 5998 
     | 
    
         
            +
                              q.breakable
         
     | 
| 
      
 5999 
     | 
    
         
            +
                              q.format(statements)
         
     | 
| 
      
 6000 
     | 
    
         
            +
                            end
         
     | 
| 
      
 6001 
     | 
    
         
            +
                          end
         
     | 
| 
      
 6002 
     | 
    
         
            +
             
     | 
| 
       5937 
6003 
     | 
    
         
             
                          q.breakable
         
     | 
| 
       5938 
     | 
    
         
            -
                          q. 
     | 
| 
      
 6004 
     | 
    
         
            +
                          q.text("end")
         
     | 
| 
       5939 
6005 
     | 
    
         
             
                        end
         
     | 
| 
       5940 
     | 
    
         
            -
             
     | 
| 
       5941 
     | 
    
         
            -
                        q.breakable
         
     | 
| 
       5942 
     | 
    
         
            -
                        q.text(force_parens ? "}" : "end")
         
     | 
| 
       5943 
6006 
     | 
    
         
             
                      end
         
     | 
| 
       5944 
6007 
     | 
    
         
             
                      .if_flat do
         
     | 
| 
       5945 
     | 
    
         
            -
                        q.text("{ 
     | 
| 
       5946 
     | 
    
         
            -
             
     | 
| 
       5947 
     | 
    
         
            -
                         
     | 
| 
      
 6008 
     | 
    
         
            +
                        q.text("{")
         
     | 
| 
      
 6009 
     | 
    
         
            +
             
     | 
| 
      
 6010 
     | 
    
         
            +
                        unless statements.empty?
         
     | 
| 
      
 6011 
     | 
    
         
            +
                          q.text(" ")
         
     | 
| 
      
 6012 
     | 
    
         
            +
                          q.format(statements)
         
     | 
| 
      
 6013 
     | 
    
         
            +
                          q.text(" ")
         
     | 
| 
      
 6014 
     | 
    
         
            +
                        end
         
     | 
| 
      
 6015 
     | 
    
         
            +
             
     | 
| 
      
 6016 
     | 
    
         
            +
                        q.text("}")
         
     | 
| 
       5948 
6017 
     | 
    
         
             
                      end
         
     | 
| 
       5949 
6018 
     | 
    
         
             
                  end
         
     | 
| 
       5950 
6019 
     | 
    
         
             
                end
         
     | 
| 
       5951 
6020 
     | 
    
         
             
              end
         
     | 
| 
       5952 
6021 
     | 
    
         | 
| 
      
 6022 
     | 
    
         
            +
              # LambdaVar represents the parameters being declared for a lambda. Effectively
         
     | 
| 
      
 6023 
     | 
    
         
            +
              # this node is everything contained within the parentheses. This includes all
         
     | 
| 
      
 6024 
     | 
    
         
            +
              # of the various parameter types, as well as block-local variable
         
     | 
| 
      
 6025 
     | 
    
         
            +
              # declarations.
         
     | 
| 
      
 6026 
     | 
    
         
            +
              #
         
     | 
| 
      
 6027 
     | 
    
         
            +
              #     -> (positional, optional = value, keyword:, █ local) do
         
     | 
| 
      
 6028 
     | 
    
         
            +
              #     end
         
     | 
| 
      
 6029 
     | 
    
         
            +
              #
         
     | 
| 
      
 6030 
     | 
    
         
            +
              class LambdaVar < Node
         
     | 
| 
      
 6031 
     | 
    
         
            +
                # [Params] the parameters being declared with the block
         
     | 
| 
      
 6032 
     | 
    
         
            +
                attr_reader :params
         
     | 
| 
      
 6033 
     | 
    
         
            +
             
     | 
| 
      
 6034 
     | 
    
         
            +
                # [Array[ Ident ]] the list of block-local variable declarations
         
     | 
| 
      
 6035 
     | 
    
         
            +
                attr_reader :locals
         
     | 
| 
      
 6036 
     | 
    
         
            +
             
     | 
| 
      
 6037 
     | 
    
         
            +
                # [Array[ Comment | EmbDoc ]] the comments attached to this node
         
     | 
| 
      
 6038 
     | 
    
         
            +
                attr_reader :comments
         
     | 
| 
      
 6039 
     | 
    
         
            +
             
     | 
| 
      
 6040 
     | 
    
         
            +
                def initialize(params:, locals:, location:, comments: [])
         
     | 
| 
      
 6041 
     | 
    
         
            +
                  @params = params
         
     | 
| 
      
 6042 
     | 
    
         
            +
                  @locals = locals
         
     | 
| 
      
 6043 
     | 
    
         
            +
                  @location = location
         
     | 
| 
      
 6044 
     | 
    
         
            +
                  @comments = comments
         
     | 
| 
      
 6045 
     | 
    
         
            +
                end
         
     | 
| 
      
 6046 
     | 
    
         
            +
             
     | 
| 
      
 6047 
     | 
    
         
            +
                def accept(visitor)
         
     | 
| 
      
 6048 
     | 
    
         
            +
                  visitor.visit_lambda_var(self)
         
     | 
| 
      
 6049 
     | 
    
         
            +
                end
         
     | 
| 
      
 6050 
     | 
    
         
            +
             
     | 
| 
      
 6051 
     | 
    
         
            +
                def child_nodes
         
     | 
| 
      
 6052 
     | 
    
         
            +
                  [params, *locals]
         
     | 
| 
      
 6053 
     | 
    
         
            +
                end
         
     | 
| 
      
 6054 
     | 
    
         
            +
             
     | 
| 
      
 6055 
     | 
    
         
            +
                alias deconstruct child_nodes
         
     | 
| 
      
 6056 
     | 
    
         
            +
             
     | 
| 
      
 6057 
     | 
    
         
            +
                def deconstruct_keys(_keys)
         
     | 
| 
      
 6058 
     | 
    
         
            +
                  { params: params, locals: locals, location: location, comments: comments }
         
     | 
| 
      
 6059 
     | 
    
         
            +
                end
         
     | 
| 
      
 6060 
     | 
    
         
            +
             
     | 
| 
      
 6061 
     | 
    
         
            +
                def empty?
         
     | 
| 
      
 6062 
     | 
    
         
            +
                  params.empty? && locals.empty?
         
     | 
| 
      
 6063 
     | 
    
         
            +
                end
         
     | 
| 
      
 6064 
     | 
    
         
            +
             
     | 
| 
      
 6065 
     | 
    
         
            +
                def format(q)
         
     | 
| 
      
 6066 
     | 
    
         
            +
                  q.format(params)
         
     | 
| 
      
 6067 
     | 
    
         
            +
             
     | 
| 
      
 6068 
     | 
    
         
            +
                  if locals.any?
         
     | 
| 
      
 6069 
     | 
    
         
            +
                    q.text("; ")
         
     | 
| 
      
 6070 
     | 
    
         
            +
                    q.seplist(locals, -> { q.text(", ") }) { |local| q.format(local) }
         
     | 
| 
      
 6071 
     | 
    
         
            +
                  end
         
     | 
| 
      
 6072 
     | 
    
         
            +
                end
         
     | 
| 
      
 6073 
     | 
    
         
            +
              end
         
     | 
| 
      
 6074 
     | 
    
         
            +
             
     | 
| 
       5953 
6075 
     | 
    
         
             
              # LBrace represents the use of a left brace, i.e., {.
         
     | 
| 
       5954 
6076 
     | 
    
         
             
              class LBrace < Node
         
     | 
| 
       5955 
6077 
     | 
    
         
             
                # [String] the left brace
         
     | 
| 
         @@ -8293,7 +8415,7 @@ module SyntaxTree 
     | 
|
| 
       8293 
8415 
     | 
    
         
             
                  end
         
     | 
| 
       8294 
8416 
     | 
    
         | 
| 
       8295 
8417 
     | 
    
         
             
                  opening_quote, closing_quote =
         
     | 
| 
       8296 
     | 
    
         
            -
                    if !Quotes.locked?(self)
         
     | 
| 
      
 8418 
     | 
    
         
            +
                    if !Quotes.locked?(self, q.quote)
         
     | 
| 
       8297 
8419 
     | 
    
         
             
                      [q.quote, q.quote]
         
     | 
| 
       8298 
8420 
     | 
    
         
             
                    elsif quote.start_with?("%")
         
     | 
| 
       8299 
8421 
     | 
    
         
             
                      [quote, Quotes.matching(quote[/%[qQ]?(.)/, 1])]
         
     | 
    
        data/lib/syntax_tree/parser.rb
    CHANGED
    
    | 
         @@ -548,13 +548,6 @@ module SyntaxTree 
     | 
|
| 
       548 
548 
     | 
    
         
             
                      parts[0].location.to(parts[-1].location)
         
     | 
| 
       549 
549 
     | 
    
         
             
                    end
         
     | 
| 
       550 
550 
     | 
    
         | 
| 
       551 
     | 
    
         
            -
                  # If there's the optional then keyword, then we'll delete that and use it
         
     | 
| 
       552 
     | 
    
         
            -
                  # as the end bounds of the location.
         
     | 
| 
       553 
     | 
    
         
            -
                  if (token = find_token(Kw, "then", consume: false))
         
     | 
| 
       554 
     | 
    
         
            -
                    tokens.delete(token)
         
     | 
| 
       555 
     | 
    
         
            -
                    location = location.to(token.location)
         
     | 
| 
       556 
     | 
    
         
            -
                  end
         
     | 
| 
       557 
     | 
    
         
            -
             
     | 
| 
       558 
551 
     | 
    
         
             
                  # If there is a plain *, then we're going to fix up the location of it
         
     | 
| 
       559 
552 
     | 
    
         
             
                  # here because it currently doesn't have anything to use for its precise
         
     | 
| 
       560 
553 
     | 
    
         
             
                  # location. If we hit a comma, then we've gone too far.
         
     | 
| 
         @@ -1698,12 +1691,6 @@ module SyntaxTree 
     | 
|
| 
       1698 
1691 
     | 
    
         
             
                    end
         
     | 
| 
       1699 
1692 
     | 
    
         
             
                  end
         
     | 
| 
       1700 
1693 
     | 
    
         | 
| 
       1701 
     | 
    
         
            -
                  # Delete the optional then keyword
         
     | 
| 
       1702 
     | 
    
         
            -
                  if (token = find_token(Kw, "then", consume: false))
         
     | 
| 
       1703 
     | 
    
         
            -
                    parts << token
         
     | 
| 
       1704 
     | 
    
         
            -
                    tokens.delete(token)
         
     | 
| 
       1705 
     | 
    
         
            -
                  end
         
     | 
| 
       1706 
     | 
    
         
            -
             
     | 
| 
       1707 
1694 
     | 
    
         
             
                  HshPtn.new(
         
     | 
| 
       1708 
1695 
     | 
    
         
             
                    constant: constant,
         
     | 
| 
       1709 
1696 
     | 
    
         
             
                    keywords: keywords || [],
         
     | 
| 
         @@ -1940,6 +1927,41 @@ module SyntaxTree 
     | 
|
| 
       1940 
1927 
     | 
    
         
             
                        token.location.start_char > beginning.location.start_char
         
     | 
| 
       1941 
1928 
     | 
    
         
             
                    end
         
     | 
| 
       1942 
1929 
     | 
    
         | 
| 
      
 1930 
     | 
    
         
            +
                  # We need to do some special mapping here. Since ripper doesn't support
         
     | 
| 
      
 1931 
     | 
    
         
            +
                  # capturing lambda var until 3.2, we need to normalize all of that here.
         
     | 
| 
      
 1932 
     | 
    
         
            +
                  params =
         
     | 
| 
      
 1933 
     | 
    
         
            +
                    case params
         
     | 
| 
      
 1934 
     | 
    
         
            +
                    in Paren[contents: Params]
         
     | 
| 
      
 1935 
     | 
    
         
            +
                      # In this case we've gotten to the <3.2 parentheses wrapping a set of
         
     | 
| 
      
 1936 
     | 
    
         
            +
                      # parameters case. Here we need to manually scan for lambda locals.
         
     | 
| 
      
 1937 
     | 
    
         
            +
                      range = (params.location.start_char + 1)...params.location.end_char
         
     | 
| 
      
 1938 
     | 
    
         
            +
                      locals = lambda_locals(source[range])
         
     | 
| 
      
 1939 
     | 
    
         
            +
             
     | 
| 
      
 1940 
     | 
    
         
            +
                      location = params.contents.location
         
     | 
| 
      
 1941 
     | 
    
         
            +
                      location = location.to(locals.last.location) if locals.any?
         
     | 
| 
      
 1942 
     | 
    
         
            +
             
     | 
| 
      
 1943 
     | 
    
         
            +
                      Paren.new(
         
     | 
| 
      
 1944 
     | 
    
         
            +
                        lparen: params.lparen,
         
     | 
| 
      
 1945 
     | 
    
         
            +
                        contents:
         
     | 
| 
      
 1946 
     | 
    
         
            +
                          LambdaVar.new(
         
     | 
| 
      
 1947 
     | 
    
         
            +
                            params: params.contents,
         
     | 
| 
      
 1948 
     | 
    
         
            +
                            locals: locals,
         
     | 
| 
      
 1949 
     | 
    
         
            +
                            location: location
         
     | 
| 
      
 1950 
     | 
    
         
            +
                          ),
         
     | 
| 
      
 1951 
     | 
    
         
            +
                        location: params.location,
         
     | 
| 
      
 1952 
     | 
    
         
            +
                        comments: params.comments
         
     | 
| 
      
 1953 
     | 
    
         
            +
                      )
         
     | 
| 
      
 1954 
     | 
    
         
            +
                    in Params
         
     | 
| 
      
 1955 
     | 
    
         
            +
                      # In this case we've gotten to the <3.2 plain set of parameters. In
         
     | 
| 
      
 1956 
     | 
    
         
            +
                      # this case there cannot be lambda locals, so we will wrap the
         
     | 
| 
      
 1957 
     | 
    
         
            +
                      # parameters into a lambda var that has no locals.
         
     | 
| 
      
 1958 
     | 
    
         
            +
                      LambdaVar.new(params: params, locals: [], location: params.location)
         
     | 
| 
      
 1959 
     | 
    
         
            +
                    in LambdaVar
         
     | 
| 
      
 1960 
     | 
    
         
            +
                      # In this case we've gotten to 3.2+ lambda var. In this case we don't
         
     | 
| 
      
 1961 
     | 
    
         
            +
                      # need to do anything and can just the value as given.
         
     | 
| 
      
 1962 
     | 
    
         
            +
                      params
         
     | 
| 
      
 1963 
     | 
    
         
            +
                    end
         
     | 
| 
      
 1964 
     | 
    
         
            +
             
     | 
| 
       1943 
1965 
     | 
    
         
             
                  if braces
         
     | 
| 
       1944 
1966 
     | 
    
         
             
                    opening = find_token(TLamBeg)
         
     | 
| 
       1945 
1967 
     | 
    
         
             
                    closing = find_token(RBrace)
         
     | 
| 
         @@ -1962,6 +1984,83 @@ module SyntaxTree 
     | 
|
| 
       1962 
1984 
     | 
    
         
             
                  )
         
     | 
| 
       1963 
1985 
     | 
    
         
             
                end
         
     | 
| 
       1964 
1986 
     | 
    
         | 
| 
      
 1987 
     | 
    
         
            +
                # :call-seq:
         
     | 
| 
      
 1988 
     | 
    
         
            +
                #   on_lambda_var: (Params params, Array[ Ident ] locals) -> LambdaVar
         
     | 
| 
      
 1989 
     | 
    
         
            +
                def on_lambda_var(params, locals)
         
     | 
| 
      
 1990 
     | 
    
         
            +
                  location = params.location
         
     | 
| 
      
 1991 
     | 
    
         
            +
                  location = location.to(locals.last.location) if locals.any?
         
     | 
| 
      
 1992 
     | 
    
         
            +
             
     | 
| 
      
 1993 
     | 
    
         
            +
                  LambdaVar.new(params: params, locals: locals || [], location: location)
         
     | 
| 
      
 1994 
     | 
    
         
            +
                end
         
     | 
| 
      
 1995 
     | 
    
         
            +
             
     | 
| 
      
 1996 
     | 
    
         
            +
                # Ripper doesn't support capturing lambda local variables until 3.2. To
         
     | 
| 
      
 1997 
     | 
    
         
            +
                # mitigate this, we have to parse that code for ourselves. We use the range
         
     | 
| 
      
 1998 
     | 
    
         
            +
                # from the parentheses to find where we _should_ be looking. Then we check
         
     | 
| 
      
 1999 
     | 
    
         
            +
                # if the resulting tokens match a pattern that we determine means that the
         
     | 
| 
      
 2000 
     | 
    
         
            +
                # declaration has block-local variables. Once it does, we parse those out
         
     | 
| 
      
 2001 
     | 
    
         
            +
                # and convert them into Ident nodes.
         
     | 
| 
      
 2002 
     | 
    
         
            +
                def lambda_locals(source)
         
     | 
| 
      
 2003 
     | 
    
         
            +
                  tokens = Ripper.lex(source)
         
     | 
| 
      
 2004 
     | 
    
         
            +
             
     | 
| 
      
 2005 
     | 
    
         
            +
                  # First, check that we have a semi-colon. If we do, then we can start to
         
     | 
| 
      
 2006 
     | 
    
         
            +
                  # parse the tokens _after_ the semicolon.
         
     | 
| 
      
 2007 
     | 
    
         
            +
                  index = tokens.rindex { |token| token[1] == :on_semicolon }
         
     | 
| 
      
 2008 
     | 
    
         
            +
                  return [] unless index
         
     | 
| 
      
 2009 
     | 
    
         
            +
             
     | 
| 
      
 2010 
     | 
    
         
            +
                  # Next, map over the tokens and convert them into Ident nodes. Bail out
         
     | 
| 
      
 2011 
     | 
    
         
            +
                  # midway through if we encounter a token we didn't expect. Basically we're
         
     | 
| 
      
 2012 
     | 
    
         
            +
                  # making our own mini-parser here. To do that we'll walk through a small
         
     | 
| 
      
 2013 
     | 
    
         
            +
                  # state machine:
         
     | 
| 
      
 2014 
     | 
    
         
            +
                  #
         
     | 
| 
      
 2015 
     | 
    
         
            +
                  #     ┌────────┐               ┌────────┐                ┌─────────┐
         
     | 
| 
      
 2016 
     | 
    
         
            +
                  #     │        │               │        │                │┌───────┐│
         
     | 
| 
      
 2017 
     | 
    
         
            +
                  # ──> │  item  │ ─── ident ──> │  next  │ ─── rparen ──> ││ final ││
         
     | 
| 
      
 2018 
     | 
    
         
            +
                  #     │        │ <── comma ─── │        │                │└───────┘│
         
     | 
| 
      
 2019 
     | 
    
         
            +
                  #     └────────┘               └────────┘                └─────────┘
         
     | 
| 
      
 2020 
     | 
    
         
            +
                  #        │  ^                     │  ^
         
     | 
| 
      
 2021 
     | 
    
         
            +
                  #        └──┘                     └──┘
         
     | 
| 
      
 2022 
     | 
    
         
            +
                  #    ignored_nl, sp              nl, sp
         
     | 
| 
      
 2023 
     | 
    
         
            +
                  #
         
     | 
| 
      
 2024 
     | 
    
         
            +
                  state = :item
         
     | 
| 
      
 2025 
     | 
    
         
            +
                  transitions = {
         
     | 
| 
      
 2026 
     | 
    
         
            +
                    item: {
         
     | 
| 
      
 2027 
     | 
    
         
            +
                      on_ignored_nl: :item,
         
     | 
| 
      
 2028 
     | 
    
         
            +
                      on_sp: :item,
         
     | 
| 
      
 2029 
     | 
    
         
            +
                      on_ident: :next
         
     | 
| 
      
 2030 
     | 
    
         
            +
                    },
         
     | 
| 
      
 2031 
     | 
    
         
            +
                    next: {
         
     | 
| 
      
 2032 
     | 
    
         
            +
                      on_nl: :next,
         
     | 
| 
      
 2033 
     | 
    
         
            +
                      on_sp: :next,
         
     | 
| 
      
 2034 
     | 
    
         
            +
                      on_comma: :item,
         
     | 
| 
      
 2035 
     | 
    
         
            +
                      on_rparen: :final
         
     | 
| 
      
 2036 
     | 
    
         
            +
                    },
         
     | 
| 
      
 2037 
     | 
    
         
            +
                    final: {
         
     | 
| 
      
 2038 
     | 
    
         
            +
                    }
         
     | 
| 
      
 2039 
     | 
    
         
            +
                  }
         
     | 
| 
      
 2040 
     | 
    
         
            +
             
     | 
| 
      
 2041 
     | 
    
         
            +
                  tokens[(index + 1)..].each_with_object([]) do |token, locals|
         
     | 
| 
      
 2042 
     | 
    
         
            +
                    (lineno, column), type, value, = token
         
     | 
| 
      
 2043 
     | 
    
         
            +
             
     | 
| 
      
 2044 
     | 
    
         
            +
                    # Make the state transition for the parser. If there isn't a transition
         
     | 
| 
      
 2045 
     | 
    
         
            +
                    # from the current state to a new state for this type, then we're in a
         
     | 
| 
      
 2046 
     | 
    
         
            +
                    # pattern that isn't actually locals. In that case we can return [].
         
     | 
| 
      
 2047 
     | 
    
         
            +
                    state = transitions[state].fetch(type) { return [] }
         
     | 
| 
      
 2048 
     | 
    
         
            +
             
     | 
| 
      
 2049 
     | 
    
         
            +
                    # If we hit an identifier, then add it to our list.
         
     | 
| 
      
 2050 
     | 
    
         
            +
                    next if type != :on_ident
         
     | 
| 
      
 2051 
     | 
    
         
            +
             
     | 
| 
      
 2052 
     | 
    
         
            +
                    location =
         
     | 
| 
      
 2053 
     | 
    
         
            +
                      Location.token(
         
     | 
| 
      
 2054 
     | 
    
         
            +
                        line: lineno,
         
     | 
| 
      
 2055 
     | 
    
         
            +
                        char: line_counts[lineno - 1][column],
         
     | 
| 
      
 2056 
     | 
    
         
            +
                        column: column,
         
     | 
| 
      
 2057 
     | 
    
         
            +
                        size: value.size
         
     | 
| 
      
 2058 
     | 
    
         
            +
                      )
         
     | 
| 
      
 2059 
     | 
    
         
            +
             
     | 
| 
      
 2060 
     | 
    
         
            +
                    locals << Ident.new(value: value, location: location)
         
     | 
| 
      
 2061 
     | 
    
         
            +
                  end
         
     | 
| 
      
 2062 
     | 
    
         
            +
                end
         
     | 
| 
      
 2063 
     | 
    
         
            +
             
     | 
| 
       1965 
2064 
     | 
    
         
             
                # :call-seq:
         
     | 
| 
       1966 
2065 
     | 
    
         
             
                #   on_lbrace: (String value) -> LBrace
         
     | 
| 
       1967 
2066 
     | 
    
         
             
                def on_lbrace(value)
         
     | 
| 
         @@ -2901,6 +3000,11 @@ module SyntaxTree 
     | 
|
| 
       2901 
3000 
     | 
    
         
             
                #     (StringEmbExpr | StringDVar | TStringContent) part
         
     | 
| 
       2902 
3001 
     | 
    
         
             
                #   ) -> StringContent
         
     | 
| 
       2903 
3002 
     | 
    
         
             
                def on_string_add(string, part)
         
     | 
| 
      
 3003 
     | 
    
         
            +
                  # Due to some eccentricities in how ripper works, you need this here in
         
     | 
| 
      
 3004 
     | 
    
         
            +
                  # case you have a syntax error with an embedded expression that doesn't
         
     | 
| 
      
 3005 
     | 
    
         
            +
                  # finish, as in: "#{"
         
     | 
| 
      
 3006 
     | 
    
         
            +
                  return string if part.is_a?(String)
         
     | 
| 
      
 3007 
     | 
    
         
            +
             
     | 
| 
       2904 
3008 
     | 
    
         
             
                  location =
         
     | 
| 
       2905 
3009 
     | 
    
         
             
                    string.parts.any? ? string.location.to(part.location) : part.location
         
     | 
| 
       2906 
3010 
     | 
    
         | 
| 
         @@ -0,0 +1,66 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "rake"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "rake/tasklib"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            require "syntax_tree"
         
     | 
| 
      
 7 
     | 
    
         
            +
            require "syntax_tree/cli"
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module SyntaxTree
         
     | 
| 
      
 10 
     | 
    
         
            +
              module Rake
         
     | 
| 
      
 11 
     | 
    
         
            +
                # A Rake task that runs check on a set of source files.
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # Example:
         
     | 
| 
      
 14 
     | 
    
         
            +
                #
         
     | 
| 
      
 15 
     | 
    
         
            +
                #   require 'syntax_tree/rake/check_task'
         
     | 
| 
      
 16 
     | 
    
         
            +
                #
         
     | 
| 
      
 17 
     | 
    
         
            +
                #   SyntaxTree::Rake::CheckTask.new do |t|
         
     | 
| 
      
 18 
     | 
    
         
            +
                #     t.source_files = '{app,config,lib}/**/*.rb'
         
     | 
| 
      
 19 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 20 
     | 
    
         
            +
                #
         
     | 
| 
      
 21 
     | 
    
         
            +
                # This will create task that can be run with:
         
     | 
| 
      
 22 
     | 
    
         
            +
                #
         
     | 
| 
      
 23 
     | 
    
         
            +
                #   rake stree_check
         
     | 
| 
      
 24 
     | 
    
         
            +
                #
         
     | 
| 
      
 25 
     | 
    
         
            +
                class CheckTask < ::Rake::TaskLib
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # Name of the task.
         
     | 
| 
      
 27 
     | 
    
         
            +
                  # Defaults to :"stree:check".
         
     | 
| 
      
 28 
     | 
    
         
            +
                  attr_accessor :name
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  # Glob pattern to match source files.
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # Defaults to 'lib/**/*.rb'.
         
     | 
| 
      
 32 
     | 
    
         
            +
                  attr_accessor :source_files
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  # The set of plugins to require.
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # Defaults to [].
         
     | 
| 
      
 36 
     | 
    
         
            +
                  attr_accessor :plugins
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  def initialize(
         
     | 
| 
      
 39 
     | 
    
         
            +
                    name = :"stree:check",
         
     | 
| 
      
 40 
     | 
    
         
            +
                    source_files = ::Rake::FileList["lib/**/*.rb"],
         
     | 
| 
      
 41 
     | 
    
         
            +
                    plugins = []
         
     | 
| 
      
 42 
     | 
    
         
            +
                  )
         
     | 
| 
      
 43 
     | 
    
         
            +
                    @name = name
         
     | 
| 
      
 44 
     | 
    
         
            +
                    @source_files = source_files
         
     | 
| 
      
 45 
     | 
    
         
            +
                    @plugins = plugins
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    yield self if block_given?
         
     | 
| 
      
 48 
     | 
    
         
            +
                    define_task
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  private
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  def define_task
         
     | 
| 
      
 54 
     | 
    
         
            +
                    desc "Runs `stree check` over source files"
         
     | 
| 
      
 55 
     | 
    
         
            +
                    task(name) { run_task }
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  def run_task
         
     | 
| 
      
 59 
     | 
    
         
            +
                    arguments = ["check"]
         
     | 
| 
      
 60 
     | 
    
         
            +
                    arguments << "--plugins=#{plugins.join(",")}" if plugins.any?
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                    SyntaxTree::CLI.run(arguments + Array(source_files))
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,66 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "rake"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "rake/tasklib"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            require "syntax_tree"
         
     | 
| 
      
 7 
     | 
    
         
            +
            require "syntax_tree/cli"
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module SyntaxTree
         
     | 
| 
      
 10 
     | 
    
         
            +
              module Rake
         
     | 
| 
      
 11 
     | 
    
         
            +
                # A Rake task that runs format on a set of source files.
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # Example:
         
     | 
| 
      
 14 
     | 
    
         
            +
                #
         
     | 
| 
      
 15 
     | 
    
         
            +
                #   require 'syntax_tree/rake/write_task'
         
     | 
| 
      
 16 
     | 
    
         
            +
                #
         
     | 
| 
      
 17 
     | 
    
         
            +
                #   SyntaxTree::Rake::WriteTask.new do |t|
         
     | 
| 
      
 18 
     | 
    
         
            +
                #     t.source_files = '{app,config,lib}/**/*.rb'
         
     | 
| 
      
 19 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 20 
     | 
    
         
            +
                #
         
     | 
| 
      
 21 
     | 
    
         
            +
                # This will create task that can be run with:
         
     | 
| 
      
 22 
     | 
    
         
            +
                #
         
     | 
| 
      
 23 
     | 
    
         
            +
                #   rake stree_write
         
     | 
| 
      
 24 
     | 
    
         
            +
                #
         
     | 
| 
      
 25 
     | 
    
         
            +
                class WriteTask < ::Rake::TaskLib
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # Name of the task.
         
     | 
| 
      
 27 
     | 
    
         
            +
                  # Defaults to :"stree:write".
         
     | 
| 
      
 28 
     | 
    
         
            +
                  attr_accessor :name
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  # Glob pattern to match source files.
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # Defaults to 'lib/**/*.rb'.
         
     | 
| 
      
 32 
     | 
    
         
            +
                  attr_accessor :source_files
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  # The set of plugins to require.
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # Defaults to [].
         
     | 
| 
      
 36 
     | 
    
         
            +
                  attr_accessor :plugins
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  def initialize(
         
     | 
| 
      
 39 
     | 
    
         
            +
                    name = :"stree:write",
         
     | 
| 
      
 40 
     | 
    
         
            +
                    source_files = ::Rake::FileList["lib/**/*.rb"],
         
     | 
| 
      
 41 
     | 
    
         
            +
                    plugins = []
         
     | 
| 
      
 42 
     | 
    
         
            +
                  )
         
     | 
| 
      
 43 
     | 
    
         
            +
                    @name = name
         
     | 
| 
      
 44 
     | 
    
         
            +
                    @source_files = source_files
         
     | 
| 
      
 45 
     | 
    
         
            +
                    @plugins = plugins
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    yield self if block_given?
         
     | 
| 
      
 48 
     | 
    
         
            +
                    define_task
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  private
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  def define_task
         
     | 
| 
      
 54 
     | 
    
         
            +
                    desc "Runs `stree write` over source files"
         
     | 
| 
      
 55 
     | 
    
         
            +
                    task(name) { run_task }
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  def run_task
         
     | 
| 
      
 59 
     | 
    
         
            +
                    arguments = ["write"]
         
     | 
| 
      
 60 
     | 
    
         
            +
                    arguments << "--plugins=#{plugins.join(",")}" if plugins.any?
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                    SyntaxTree::CLI.run(arguments + Array(source_files))
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/syntax_tree/version.rb
    CHANGED
    
    
| 
         @@ -49,9 +49,7 @@ module SyntaxTree 
     | 
|
| 
       49 
49 
     | 
    
         
             
                # of circumstances, like when visiting the list of optional parameters
         
     | 
| 
       50 
50 
     | 
    
         
             
                # defined on a method.
         
     | 
| 
       51 
51 
     | 
    
         
             
                #
         
     | 
| 
       52 
     | 
    
         
            -
                class FieldVisitor <  
     | 
| 
       53 
     | 
    
         
            -
                  attr_reader :q
         
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
      
 52 
     | 
    
         
            +
                class FieldVisitor < BasicVisitor
         
     | 
| 
       55 
53 
     | 
    
         
             
                  def visit_aref(node)
         
     | 
| 
       56 
54 
     | 
    
         
             
                    node(node, "aref") do
         
     | 
| 
       57 
55 
     | 
    
         
             
                      field("collection", node.collection)
         
     | 
| 
         @@ -586,6 +584,14 @@ module SyntaxTree 
     | 
|
| 
       586 
584 
     | 
    
         
             
                    end
         
     | 
| 
       587 
585 
     | 
    
         
             
                  end
         
     | 
| 
       588 
586 
     | 
    
         | 
| 
      
 587 
     | 
    
         
            +
                  def visit_lambda_var(node)
         
     | 
| 
      
 588 
     | 
    
         
            +
                    node(node, "lambda_var") do
         
     | 
| 
      
 589 
     | 
    
         
            +
                      field("params", node.params)
         
     | 
| 
      
 590 
     | 
    
         
            +
                      list("locals", node.locals) if node.locals.any?
         
     | 
| 
      
 591 
     | 
    
         
            +
                      comments(node)
         
     | 
| 
      
 592 
     | 
    
         
            +
                    end
         
     | 
| 
      
 593 
     | 
    
         
            +
                  end
         
     | 
| 
      
 594 
     | 
    
         
            +
             
     | 
| 
       589 
595 
     | 
    
         
             
                  def visit_lbrace(node)
         
     | 
| 
       590 
596 
     | 
    
         
             
                    visit_token(node, "lbrace")
         
     | 
| 
       591 
597 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/syntax_tree/visitor.rb
    CHANGED
    
    | 
         @@ -4,72 +4,7 @@ module SyntaxTree 
     | 
|
| 
       4 
4 
     | 
    
         
             
              # Visitor is a parent class that provides the ability to walk down the tree
         
     | 
| 
       5 
5 
     | 
    
         
             
              # and handle a subset of nodes. By defining your own subclass, you can
         
     | 
| 
       6 
6 
     | 
    
         
             
              # explicitly handle a node type by defining a visit_* method.
         
     | 
| 
       7 
     | 
    
         
            -
              class Visitor
         
     | 
| 
       8 
     | 
    
         
            -
                # This is raised when you use the Visitor.visit_method method and it fails.
         
     | 
| 
       9 
     | 
    
         
            -
                # It is correctable to through DidYouMean.
         
     | 
| 
       10 
     | 
    
         
            -
                class VisitMethodError < StandardError
         
     | 
| 
       11 
     | 
    
         
            -
                  attr_reader :visit_method
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
                  def initialize(visit_method)
         
     | 
| 
       14 
     | 
    
         
            -
                    @visit_method = visit_method
         
     | 
| 
       15 
     | 
    
         
            -
                    super("Invalid visit method: #{visit_method}")
         
     | 
| 
       16 
     | 
    
         
            -
                  end
         
     | 
| 
       17 
     | 
    
         
            -
                end
         
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
                # This class is used by DidYouMean to offer corrections to invalid visit
         
     | 
| 
       20 
     | 
    
         
            -
                # method names.
         
     | 
| 
       21 
     | 
    
         
            -
                class VisitMethodChecker
         
     | 
| 
       22 
     | 
    
         
            -
                  attr_reader :visit_method
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
                  def initialize(error)
         
     | 
| 
       25 
     | 
    
         
            -
                    @visit_method = error.visit_method
         
     | 
| 
       26 
     | 
    
         
            -
                  end
         
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
                  def corrections
         
     | 
| 
       29 
     | 
    
         
            -
                    @corrections ||=
         
     | 
| 
       30 
     | 
    
         
            -
                      DidYouMean::SpellChecker.new(
         
     | 
| 
       31 
     | 
    
         
            -
                        dictionary: Visitor.visit_methods
         
     | 
| 
       32 
     | 
    
         
            -
                      ).correct(visit_method)
         
     | 
| 
       33 
     | 
    
         
            -
                  end
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
                  DidYouMean.correct_error(VisitMethodError, self)
         
     | 
| 
       36 
     | 
    
         
            -
                end
         
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
                class << self
         
     | 
| 
       39 
     | 
    
         
            -
                  # This method is here to help folks write visitors.
         
     | 
| 
       40 
     | 
    
         
            -
                  #
         
     | 
| 
       41 
     | 
    
         
            -
                  # It's not always easy to ensure you're writing the correct method name in
         
     | 
| 
       42 
     | 
    
         
            -
                  # the visitor since it's perfectly valid to define methods that don't
         
     | 
| 
       43 
     | 
    
         
            -
                  # override these parent methods.
         
     | 
| 
       44 
     | 
    
         
            -
                  #
         
     | 
| 
       45 
     | 
    
         
            -
                  # If you use this method, you can ensure you're writing the correct method
         
     | 
| 
       46 
     | 
    
         
            -
                  # name. It will raise an error if the visit method you're defining isn't
         
     | 
| 
       47 
     | 
    
         
            -
                  # actually a method on the parent visitor.
         
     | 
| 
       48 
     | 
    
         
            -
                  def visit_method(method_name)
         
     | 
| 
       49 
     | 
    
         
            -
                    return if visit_methods.include?(method_name)
         
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                    raise VisitMethodError, method_name
         
     | 
| 
       52 
     | 
    
         
            -
                  end
         
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
                  # This is the list of all of the valid visit methods.
         
     | 
| 
       55 
     | 
    
         
            -
                  def visit_methods
         
     | 
| 
       56 
     | 
    
         
            -
                    @visit_methods ||=
         
     | 
| 
       57 
     | 
    
         
            -
                      Visitor.instance_methods.grep(/^visit_(?!child_nodes)/)
         
     | 
| 
       58 
     | 
    
         
            -
                  end
         
     | 
| 
       59 
     | 
    
         
            -
                end
         
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
                def visit(node)
         
     | 
| 
       62 
     | 
    
         
            -
                  node&.accept(self)
         
     | 
| 
       63 
     | 
    
         
            -
                end
         
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                def visit_all(nodes)
         
     | 
| 
       66 
     | 
    
         
            -
                  nodes.map { |node| visit(node) }
         
     | 
| 
       67 
     | 
    
         
            -
                end
         
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
                def visit_child_nodes(node)
         
     | 
| 
       70 
     | 
    
         
            -
                  visit_all(node.child_nodes)
         
     | 
| 
       71 
     | 
    
         
            -
                end
         
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
      
 7 
     | 
    
         
            +
              class Visitor < BasicVisitor
         
     | 
| 
       73 
8 
     | 
    
         
             
                # Visit an ARef node.
         
     | 
| 
       74 
9 
     | 
    
         
             
                alias visit_aref visit_child_nodes
         
     | 
| 
       75 
10 
     | 
    
         | 
| 
         @@ -301,6 +236,9 @@ module SyntaxTree 
     | 
|
| 
       301 
236 
     | 
    
         
             
                # Visit a Lambda node.
         
     | 
| 
       302 
237 
     | 
    
         
             
                alias visit_lambda visit_child_nodes
         
     | 
| 
       303 
238 
     | 
    
         | 
| 
      
 239 
     | 
    
         
            +
                # Visit a LambdaVar node.
         
     | 
| 
      
 240 
     | 
    
         
            +
                alias visit_lambda_var visit_child_nodes
         
     | 
| 
      
 241 
     | 
    
         
            +
             
     | 
| 
       304 
242 
     | 
    
         
             
                # Visit a LBrace node.
         
     | 
| 
       305 
243 
     | 
    
         
             
                alias visit_lbrace visit_child_nodes
         
     | 
| 
       306 
244 
     | 
    
         | 
    
        data/lib/syntax_tree.rb
    CHANGED
    
    | 
         @@ -10,6 +10,8 @@ require_relative "syntax_tree/formatter" 
     | 
|
| 
       10 
10 
     | 
    
         
             
            require_relative "syntax_tree/node"
         
     | 
| 
       11 
11 
     | 
    
         
             
            require_relative "syntax_tree/parser"
         
     | 
| 
       12 
12 
     | 
    
         
             
            require_relative "syntax_tree/version"
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            require_relative "syntax_tree/basic_visitor"
         
     | 
| 
       13 
15 
     | 
    
         
             
            require_relative "syntax_tree/visitor"
         
     | 
| 
       14 
16 
     | 
    
         
             
            require_relative "syntax_tree/visitor/field_visitor"
         
     | 
| 
       15 
17 
     | 
    
         
             
            require_relative "syntax_tree/visitor/json_visitor"
         
     | 
    
        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.7.1
         
     | 
| 
       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-05- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2022-05-25 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: prettier_print
         
     | 
| 
         @@ -121,14 +121,18 @@ files: 
     | 
|
| 
       121 
121 
     | 
    
         
             
            - doc/logo.svg
         
     | 
| 
       122 
122 
     | 
    
         
             
            - exe/stree
         
     | 
| 
       123 
123 
     | 
    
         
             
            - lib/syntax_tree.rb
         
     | 
| 
      
 124 
     | 
    
         
            +
            - lib/syntax_tree/basic_visitor.rb
         
     | 
| 
       124 
125 
     | 
    
         
             
            - lib/syntax_tree/cli.rb
         
     | 
| 
       125 
126 
     | 
    
         
             
            - lib/syntax_tree/formatter.rb
         
     | 
| 
       126 
     | 
    
         
            -
            - lib/syntax_tree/formatter/single_quotes.rb
         
     | 
| 
       127 
127 
     | 
    
         
             
            - lib/syntax_tree/language_server.rb
         
     | 
| 
       128 
128 
     | 
    
         
             
            - lib/syntax_tree/language_server/inlay_hints.rb
         
     | 
| 
       129 
129 
     | 
    
         
             
            - lib/syntax_tree/node.rb
         
     | 
| 
       130 
130 
     | 
    
         
             
            - lib/syntax_tree/parser.rb
         
     | 
| 
       131 
131 
     | 
    
         
             
            - lib/syntax_tree/plugin/single_quotes.rb
         
     | 
| 
      
 132 
     | 
    
         
            +
            - lib/syntax_tree/plugin/trailing_comma.rb
         
     | 
| 
      
 133 
     | 
    
         
            +
            - lib/syntax_tree/rake/check_task.rb
         
     | 
| 
      
 134 
     | 
    
         
            +
            - lib/syntax_tree/rake/write_task.rb
         
     | 
| 
      
 135 
     | 
    
         
            +
            - lib/syntax_tree/rake_tasks.rb
         
     | 
| 
       132 
136 
     | 
    
         
             
            - lib/syntax_tree/version.rb
         
     | 
| 
       133 
137 
     | 
    
         
             
            - lib/syntax_tree/visitor.rb
         
     | 
| 
       134 
138 
     | 
    
         
             
            - lib/syntax_tree/visitor/field_visitor.rb
         
     | 
| 
         @@ -156,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       156 
160 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       157 
161 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       158 
162 
     | 
    
         
             
            requirements: []
         
     | 
| 
       159 
     | 
    
         
            -
            rubygems_version: 3. 
     | 
| 
      
 163 
     | 
    
         
            +
            rubygems_version: 3.4.0.dev
         
     | 
| 
       160 
164 
     | 
    
         
             
            signing_key:
         
     | 
| 
       161 
165 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       162 
166 
     | 
    
         
             
            summary: A parser based on ripper
         
     | 
| 
         @@ -1,13 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # frozen_string_literal: true
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            module SyntaxTree
         
     | 
| 
       4 
     | 
    
         
            -
              class Formatter
         
     | 
| 
       5 
     | 
    
         
            -
                # This module overrides the quote method on the formatter to use single
         
     | 
| 
       6 
     | 
    
         
            -
                # quotes for everything instead of double quotes.
         
     | 
| 
       7 
     | 
    
         
            -
                module SingleQuotes
         
     | 
| 
       8 
     | 
    
         
            -
                  def quote
         
     | 
| 
       9 
     | 
    
         
            -
                    "'"
         
     | 
| 
       10 
     | 
    
         
            -
                  end
         
     | 
| 
       11 
     | 
    
         
            -
                end
         
     | 
| 
       12 
     | 
    
         
            -
              end
         
     | 
| 
       13 
     | 
    
         
            -
            end
         
     |