syntax_tree 2.7.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -1
- data/Gemfile.lock +9 -7
- data/README.md +3 -3
- data/lib/syntax_tree/basic_visitor.rb +5 -1
- data/lib/syntax_tree/cli.rb +133 -67
- data/lib/syntax_tree/formatter.rb +3 -1
- data/lib/syntax_tree/language_server/inlay_hints.rb +42 -18
- data/lib/syntax_tree/language_server.rb +26 -39
- data/lib/syntax_tree/node.rb +42 -3
- data/lib/syntax_tree/parser.rb +11 -1
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/visitor/field_visitor.rb +4 -0
- data/lib/syntax_tree/visitor.rb +3 -0
- data/lib/syntax_tree.rb +1 -0
- metadata +3 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 28783cde4387bbc7d38d9de500564804cd885733abe59e017be73ce04981a854
         | 
| 4 | 
            +
              data.tar.gz: 6a54750102b8978df75e7f8a7d611e596067bb69863f91d5a8daa9aec8509c31
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: c2f61f01bfa53604686798c96a53d8c98abd30145038db3b7f73f92c5c6b282447f3fe355382f6b3325b3ee98ce60ddcc5c102234b78e9c8d1fe9cbf006cb38d
         | 
| 7 | 
            +
              data.tar.gz: e5a63e7fdc4f7e70e354a5861e42b8c0dffbbd2ffde38754c0a1fca8a6033be2deecd52ea3ec5fb7a2aa05e0d07ab93d3b4d69762ca3c911f36a6e898ef2ffaf
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -6,6 +6,35 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a | |
| 6 6 |  | 
| 7 7 | 
             
            ## [Unreleased]
         | 
| 8 8 |  | 
| 9 | 
            +
            ## [3.0.0] - 2022-07-04
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            ### Changed
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            - [#102](https://github.com/ruby-syntax-tree/syntax_tree/issues/102) - Handle requests to the language server for files that do not yet exist on disk.
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            ### Removed
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            - [#108](https://github.com/ruby-syntax-tree/syntax_tree/pull/108) - Remove old inlay hints code.
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            ## [2.9.0] - 2022-07-04
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            ### Added
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            - [#106](https://github.com/ruby-syntax-tree/syntax_tree/pull/106) - Add inlay hint support to match the LSP specification.
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            ## [2.8.0] - 2022-06-21
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            ### Added
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            - [#95](https://github.com/ruby-syntax-tree/syntax_tree/pull/95) - The `HeredocEnd` node has been added which effectively results in the ability to determine the location of the ending of a heredoc from source.
         | 
| 30 | 
            +
            - [#99](https://github.com/ruby-syntax-tree/syntax_tree/pull/99) - The LSP now allows you to pass the same configuration options as the other CLI commands which allows formatting to be modified in the VSCode extension.
         | 
| 31 | 
            +
            - [#100](https://github.com/ruby-syntax-tree/syntax_tree/pull/100) - The LSP now explicitly responds to the shutdown request so that VSCode never deadlocks.
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            ### Changed
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            - [#96](https://github.com/ruby-syntax-tree/syntax_tree/pull/96) - The CLI now runs in parallel by default. There is a worker created for each processor on the running machine (as determined by `Etc.nprocessors`).
         | 
| 36 | 
            +
            - [#97](https://github.com/ruby-syntax-tree/syntax_tree/pull/97) - Syntax Tree now handles the case where `DidYouMean` is not available for whatever reason, as well as handles the newer `detailed_message` API for errors.
         | 
| 37 | 
            +
             | 
| 9 38 | 
             
            ## [2.7.1] - 2022-05-25
         | 
| 10 39 |  | 
| 11 40 | 
             
            ### Added
         | 
| @@ -259,7 +288,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a | |
| 259 288 |  | 
| 260 289 | 
             
            - 🎉 Initial release! 🎉
         | 
| 261 290 |  | 
| 262 | 
            -
            [unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2. | 
| 291 | 
            +
            [unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.9.0...HEAD
         | 
| 292 | 
            +
            [2.9.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.8.0...v2.9.0
         | 
| 293 | 
            +
            [2.8.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.7.1...v2.8.0
         | 
| 263 294 | 
             
            [2.7.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.7.0...v2.7.1
         | 
| 264 295 | 
             
            [2.7.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.6.0...v2.7.0
         | 
| 265 296 | 
             
            [2.6.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.5.0...v2.6.0
         | 
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                syntax_tree ( | 
| 4 | 
            +
                syntax_tree (3.0.0)
         | 
| 5 5 | 
             
                  prettier_print
         | 
| 6 6 |  | 
| 7 7 | 
             
            GEM
         | 
| @@ -9,25 +9,27 @@ GEM | |
| 9 9 | 
             
              specs:
         | 
| 10 10 | 
             
                ast (2.4.2)
         | 
| 11 11 | 
             
                docile (1.4.0)
         | 
| 12 | 
            -
                 | 
| 12 | 
            +
                json (2.6.2)
         | 
| 13 | 
            +
                minitest (5.16.2)
         | 
| 13 14 | 
             
                parallel (1.22.1)
         | 
| 14 15 | 
             
                parser (3.1.2.0)
         | 
| 15 16 | 
             
                  ast (~> 2.4.1)
         | 
| 16 17 | 
             
                prettier_print (0.1.0)
         | 
| 17 18 | 
             
                rainbow (3.1.1)
         | 
| 18 19 | 
             
                rake (13.0.6)
         | 
| 19 | 
            -
                regexp_parser (2. | 
| 20 | 
            +
                regexp_parser (2.5.0)
         | 
| 20 21 | 
             
                rexml (3.2.5)
         | 
| 21 | 
            -
                rubocop (1. | 
| 22 | 
            +
                rubocop (1.31.1)
         | 
| 23 | 
            +
                  json (~> 2.3)
         | 
| 22 24 | 
             
                  parallel (~> 1.10)
         | 
| 23 25 | 
             
                  parser (>= 3.1.0.0)
         | 
| 24 26 | 
             
                  rainbow (>= 2.2.2, < 4.0)
         | 
| 25 27 | 
             
                  regexp_parser (>= 1.8, < 3.0)
         | 
| 26 28 | 
             
                  rexml (>= 3.2.5, < 4.0)
         | 
| 27 | 
            -
                  rubocop-ast (>= 1. | 
| 29 | 
            +
                  rubocop-ast (>= 1.18.0, < 2.0)
         | 
| 28 30 | 
             
                  ruby-progressbar (~> 1.7)
         | 
| 29 31 | 
             
                  unicode-display_width (>= 1.4.0, < 3.0)
         | 
| 30 | 
            -
                rubocop-ast (1. | 
| 32 | 
            +
                rubocop-ast (1.18.0)
         | 
| 31 33 | 
             
                  parser (>= 3.1.1.0)
         | 
| 32 34 | 
             
                ruby-progressbar (1.11.0)
         | 
| 33 35 | 
             
                simplecov (0.21.2)
         | 
| @@ -36,7 +38,7 @@ GEM | |
| 36 38 | 
             
                  simplecov_json_formatter (~> 0.1)
         | 
| 37 39 | 
             
                simplecov-html (0.12.3)
         | 
| 38 40 | 
             
                simplecov_json_formatter (0.1.4)
         | 
| 39 | 
            -
                unicode-display_width (2. | 
| 41 | 
            +
                unicode-display_width (2.2.0)
         | 
| 40 42 |  | 
| 41 43 | 
             
            PLATFORMS
         | 
| 42 44 | 
             
              arm64-darwin-21
         | 
    
        data/README.md
    CHANGED
    
    | @@ -35,7 +35,7 @@ It is built with only standard library dependencies. It additionally ships with | |
| 35 35 | 
             
              - [BasicVisitor](#basicvisitor)
         | 
| 36 36 | 
             
            - [Language server](#language-server)
         | 
| 37 37 | 
             
              - [textDocument/formatting](#textdocumentformatting)
         | 
| 38 | 
            -
              - [textDocument/ | 
| 38 | 
            +
              - [textDocument/inlayHint](#textdocumentinlayhint)
         | 
| 39 39 | 
             
              - [syntaxTree/visualizing](#syntaxtreevisualizing)
         | 
| 40 40 | 
             
            - [Plugins](#plugins)
         | 
| 41 41 | 
             
              - [Configuration](#configuration)
         | 
| @@ -402,7 +402,7 @@ By default, the language server is relatively minimal, mostly meant to provide a | |
| 402 402 |  | 
| 403 403 | 
             
            As mentioned above, the language server responds to formatting requests with the formatted document. It typically responds on the order of tens of milliseconds, so it should be fast enough for any IDE.
         | 
| 404 404 |  | 
| 405 | 
            -
            ### textDocument/ | 
| 405 | 
            +
            ### textDocument/inlayHint
         | 
| 406 406 |  | 
| 407 407 | 
             
            The language server also responds to the relatively new inlay hints request. This request allows the language server to define additional information that should exist in the source code as helpful hints to the developer. In our case we use it to display things like implicit parentheses. For example, if you had the following code:
         | 
| 408 408 |  | 
| @@ -410,7 +410,7 @@ The language server also responds to the relatively new inlay hints request. Thi | |
| 410 410 | 
             
            1 + 2 * 3
         | 
| 411 411 | 
             
            ```
         | 
| 412 412 |  | 
| 413 | 
            -
            Implicity, the `2 * 3` is going to be executed first because the `*` operator has higher precedence than the `+` operator.  | 
| 413 | 
            +
            Implicity, the `2 * 3` is going to be executed first because the `*` operator has higher precedence than the `+` operator. To ease mental overhead, our language server includes small parentheses to make this explicit, as in:
         | 
| 414 414 |  | 
| 415 415 | 
             
            ```ruby
         | 
| 416 416 | 
             
            1 + ₍2 * 3₎
         | 
| @@ -33,7 +33,11 @@ module SyntaxTree | |
| 33 33 | 
             
                      ).correct(visit_method)
         | 
| 34 34 | 
             
                  end
         | 
| 35 35 |  | 
| 36 | 
            -
                  DidYouMean | 
| 36 | 
            +
                  # In some setups with Ruby you can turn off DidYouMean, so we're going to
         | 
| 37 | 
            +
                  # respect that setting here.
         | 
| 38 | 
            +
                  if defined?(DidYouMean) && DidYouMean.method_defined?(:correct_error)
         | 
| 39 | 
            +
                    DidYouMean.correct_error(VisitMethodError, self)
         | 
| 40 | 
            +
                  end
         | 
| 37 41 | 
             
                end
         | 
| 38 42 |  | 
| 39 43 | 
             
                class << self
         | 
    
        data/lib/syntax_tree/cli.rb
    CHANGED
    
    | @@ -34,9 +34,41 @@ module SyntaxTree | |
| 34 34 | 
             
                  end
         | 
| 35 35 | 
             
                end
         | 
| 36 36 |  | 
| 37 | 
            +
                # An item of work that corresponds to a file to be processed.
         | 
| 38 | 
            +
                class FileItem
         | 
| 39 | 
            +
                  attr_reader :filepath
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  def initialize(filepath)
         | 
| 42 | 
            +
                    @filepath = filepath
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  def handler
         | 
| 46 | 
            +
                    HANDLERS[File.extname(filepath)]
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  def source
         | 
| 50 | 
            +
                    handler.read(filepath)
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                # An item of work that corresponds to the stdin content.
         | 
| 55 | 
            +
                class STDINItem
         | 
| 56 | 
            +
                  def handler
         | 
| 57 | 
            +
                    HANDLERS[".rb"]
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  def filepath
         | 
| 61 | 
            +
                    :stdin
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  def source
         | 
| 65 | 
            +
                    $stdin.read
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 37 69 | 
             
                # The parent action class for the CLI that implements the basics.
         | 
| 38 70 | 
             
                class Action
         | 
| 39 | 
            -
                  def run( | 
| 71 | 
            +
                  def run(item)
         | 
| 40 72 | 
             
                  end
         | 
| 41 73 |  | 
| 42 74 | 
             
                  def success
         | 
| @@ -48,8 +80,8 @@ module SyntaxTree | |
| 48 80 |  | 
| 49 81 | 
             
                # An action of the CLI that prints out the AST for the given source.
         | 
| 50 82 | 
             
                class AST < Action
         | 
| 51 | 
            -
                  def run( | 
| 52 | 
            -
                    pp handler.parse(source)
         | 
| 83 | 
            +
                  def run(item)
         | 
| 84 | 
            +
                    pp item.handler.parse(item.source)
         | 
| 53 85 | 
             
                  end
         | 
| 54 86 | 
             
                end
         | 
| 55 87 |  | 
| @@ -59,10 +91,11 @@ module SyntaxTree | |
| 59 91 | 
             
                  class UnformattedError < StandardError
         | 
| 60 92 | 
             
                  end
         | 
| 61 93 |  | 
| 62 | 
            -
                  def run( | 
| 63 | 
            -
                     | 
| 94 | 
            +
                  def run(item)
         | 
| 95 | 
            +
                    source = item.source
         | 
| 96 | 
            +
                    raise UnformattedError if source != item.handler.format(source)
         | 
| 64 97 | 
             
                  rescue StandardError
         | 
| 65 | 
            -
                    warn("[#{Color.yellow("warn")}] #{filepath}")
         | 
| 98 | 
            +
                    warn("[#{Color.yellow("warn")}] #{item.filepath}")
         | 
| 66 99 | 
             
                    raise
         | 
| 67 100 | 
             
                  end
         | 
| 68 101 |  | 
| @@ -81,9 +114,11 @@ module SyntaxTree | |
| 81 114 | 
             
                  class NonIdempotentFormatError < StandardError
         | 
| 82 115 | 
             
                  end
         | 
| 83 116 |  | 
| 84 | 
            -
                  def run( | 
| 85 | 
            -
                     | 
| 86 | 
            -
             | 
| 117 | 
            +
                  def run(item)
         | 
| 118 | 
            +
                    handler = item.handler
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                    warning = "[#{Color.yellow("warn")}] #{item.filepath}"
         | 
| 121 | 
            +
                    formatted = handler.format(item.source)
         | 
| 87 122 |  | 
| 88 123 | 
             
                    raise NonIdempotentFormatError if formatted != handler.format(formatted)
         | 
| 89 124 | 
             
                  rescue StandardError
         | 
| @@ -102,25 +137,27 @@ module SyntaxTree | |
| 102 137 |  | 
| 103 138 | 
             
                # An action of the CLI that prints out the doc tree IR for the given source.
         | 
| 104 139 | 
             
                class Doc < Action
         | 
| 105 | 
            -
                  def run( | 
| 140 | 
            +
                  def run(item)
         | 
| 141 | 
            +
                    source = item.source
         | 
| 142 | 
            +
             | 
| 106 143 | 
             
                    formatter = Formatter.new(source, [])
         | 
| 107 | 
            -
                    handler.parse(source).format(formatter)
         | 
| 144 | 
            +
                    item.handler.parse(source).format(formatter)
         | 
| 108 145 | 
             
                    pp formatter.groups.first
         | 
| 109 146 | 
             
                  end
         | 
| 110 147 | 
             
                end
         | 
| 111 148 |  | 
| 112 149 | 
             
                # An action of the CLI that formats the input source and prints it out.
         | 
| 113 150 | 
             
                class Format < Action
         | 
| 114 | 
            -
                  def run( | 
| 115 | 
            -
                    puts handler.format(source)
         | 
| 151 | 
            +
                  def run(item)
         | 
| 152 | 
            +
                    puts item.handler.format(item.source)
         | 
| 116 153 | 
             
                  end
         | 
| 117 154 | 
             
                end
         | 
| 118 155 |  | 
| 119 156 | 
             
                # An action of the CLI that converts the source into its equivalent JSON
         | 
| 120 157 | 
             
                # representation.
         | 
| 121 158 | 
             
                class Json < Action
         | 
| 122 | 
            -
                  def run( | 
| 123 | 
            -
                    object = Visitor::JSONVisitor.new.visit(handler.parse(source))
         | 
| 159 | 
            +
                  def run(item)
         | 
| 160 | 
            +
                    object = Visitor::JSONVisitor.new.visit(item.handler.parse(item.source))
         | 
| 124 161 | 
             
                    puts JSON.pretty_generate(object)
         | 
| 125 162 | 
             
                  end
         | 
| 126 163 | 
             
                end
         | 
| @@ -128,27 +165,28 @@ module SyntaxTree | |
| 128 165 | 
             
                # An action of the CLI that outputs a pattern-matching Ruby expression that
         | 
| 129 166 | 
             
                # would match the input given.
         | 
| 130 167 | 
             
                class Match < Action
         | 
| 131 | 
            -
                  def run( | 
| 132 | 
            -
                    puts handler.parse(source).construct_keys
         | 
| 168 | 
            +
                  def run(item)
         | 
| 169 | 
            +
                    puts item.handler.parse(item.source).construct_keys
         | 
| 133 170 | 
             
                  end
         | 
| 134 171 | 
             
                end
         | 
| 135 172 |  | 
| 136 173 | 
             
                # An action of the CLI that formats the input source and writes the
         | 
| 137 174 | 
             
                # formatted output back to the file.
         | 
| 138 175 | 
             
                class Write < Action
         | 
| 139 | 
            -
                  def run( | 
| 140 | 
            -
                     | 
| 176 | 
            +
                  def run(item)
         | 
| 177 | 
            +
                    filepath = item.filepath
         | 
| 141 178 | 
             
                    start = Time.now
         | 
| 142 179 |  | 
| 143 | 
            -
                     | 
| 180 | 
            +
                    source = item.source
         | 
| 181 | 
            +
                    formatted = item.handler.format(source)
         | 
| 144 182 | 
             
                    File.write(filepath, formatted) if filepath != :stdin
         | 
| 145 183 |  | 
| 146 184 | 
             
                    color = source == formatted ? Color.gray(filepath) : filepath
         | 
| 147 185 | 
             
                    delta = ((Time.now - start) * 1000).round
         | 
| 148 186 |  | 
| 149 | 
            -
                    puts " | 
| 187 | 
            +
                    puts "#{color} #{delta}ms"
         | 
| 150 188 | 
             
                  rescue StandardError
         | 
| 151 | 
            -
                    puts  | 
| 189 | 
            +
                    puts filepath
         | 
| 152 190 | 
             
                    raise
         | 
| 153 191 | 
             
                  end
         | 
| 154 192 | 
             
                end
         | 
| @@ -180,7 +218,7 @@ module SyntaxTree | |
| 180 218 | 
             
                  #{Color.bold("stree help")}
         | 
| 181 219 | 
             
                    Display this help message
         | 
| 182 220 |  | 
| 183 | 
            -
                  #{Color.bold("stree lsp")}
         | 
| 221 | 
            +
                  #{Color.bold("stree lsp [OPTIONS]")}
         | 
| 184 222 | 
             
                    Run syntax tree in language server mode
         | 
| 185 223 |  | 
| 186 224 | 
             
                  #{Color.bold("stree version")}
         | 
| @@ -201,6 +239,20 @@ module SyntaxTree | |
| 201 239 | 
             
                  def run(argv)
         | 
| 202 240 | 
             
                    name, *arguments = argv
         | 
| 203 241 |  | 
| 242 | 
            +
                    # If there are any plugins specified on the command line, then load them
         | 
| 243 | 
            +
                    # by requiring them here. We do this by transforming something like
         | 
| 244 | 
            +
                    #
         | 
| 245 | 
            +
                    #     stree format --plugins=haml template.haml
         | 
| 246 | 
            +
                    #
         | 
| 247 | 
            +
                    # into
         | 
| 248 | 
            +
                    #
         | 
| 249 | 
            +
                    #     require "syntax_tree/haml"
         | 
| 250 | 
            +
                    #
         | 
| 251 | 
            +
                    if arguments.first&.start_with?("--plugins=")
         | 
| 252 | 
            +
                      plugins = arguments.shift[/^--plugins=(.*)$/, 1]
         | 
| 253 | 
            +
                      plugins.split(",").each { |plugin| require "syntax_tree/#{plugin}" }
         | 
| 254 | 
            +
                    end
         | 
| 255 | 
            +
             | 
| 204 256 | 
             
                    case name
         | 
| 205 257 | 
             
                    when "help"
         | 
| 206 258 | 
             
                      puts HELP
         | 
| @@ -244,38 +296,41 @@ module SyntaxTree | |
| 244 296 | 
             
                      return 1
         | 
| 245 297 | 
             
                    end
         | 
| 246 298 |  | 
| 247 | 
            -
                    #  | 
| 248 | 
            -
                     | 
| 249 | 
            -
                    #
         | 
| 250 | 
            -
                    #     stree format --plugins=haml template.haml
         | 
| 251 | 
            -
                    #
         | 
| 252 | 
            -
                    # into
         | 
| 253 | 
            -
                    #
         | 
| 254 | 
            -
                    #     require "syntax_tree/haml"
         | 
| 255 | 
            -
                    #
         | 
| 256 | 
            -
                    if arguments.first&.start_with?("--plugins=")
         | 
| 257 | 
            -
                      plugins = arguments.shift[/^--plugins=(.*)$/, 1]
         | 
| 258 | 
            -
                      plugins.split(",").each { |plugin| require "syntax_tree/#{plugin}" }
         | 
| 259 | 
            -
                    end
         | 
| 299 | 
            +
                    # We're going to build up a queue of items to process.
         | 
| 300 | 
            +
                    queue = Queue.new
         | 
| 260 301 |  | 
| 261 | 
            -
                    #  | 
| 262 | 
            -
                    #  | 
| 263 | 
            -
                     | 
| 264 | 
            -
             | 
| 265 | 
            -
             | 
| 266 | 
            -
             | 
| 267 | 
            -
             | 
| 268 | 
            -
             | 
| 269 | 
            -
             | 
| 270 | 
            -
                       | 
| 271 | 
            -
                     | 
| 272 | 
            -
                       | 
| 273 | 
            -
                    rescue StandardError => error
         | 
| 274 | 
            -
                      warn(error.message)
         | 
| 275 | 
            -
                      warn(error.backtrace)
         | 
| 276 | 
            -
                      errored = true
         | 
| 302 | 
            +
                    # If we're reading from stdin, then we'll just add the stdin object to
         | 
| 303 | 
            +
                    # the queue. Otherwise, we'll add each of the filepaths to the queue.
         | 
| 304 | 
            +
                    if $stdin.tty? || arguments.any?
         | 
| 305 | 
            +
                      arguments.each do |pattern|
         | 
| 306 | 
            +
                        Dir
         | 
| 307 | 
            +
                          .glob(pattern)
         | 
| 308 | 
            +
                          .each do |filepath|
         | 
| 309 | 
            +
                            queue << FileItem.new(filepath) if File.file?(filepath)
         | 
| 310 | 
            +
                          end
         | 
| 311 | 
            +
                      end
         | 
| 312 | 
            +
                    else
         | 
| 313 | 
            +
                      queue << STDINItem.new
         | 
| 277 314 | 
             
                    end
         | 
| 278 315 |  | 
| 316 | 
            +
                    # At the end, we're going to return whether or not this worker ever
         | 
| 317 | 
            +
                    # encountered an error.
         | 
| 318 | 
            +
                    errored =
         | 
| 319 | 
            +
                      with_workers(queue) do |item|
         | 
| 320 | 
            +
                        action.run(item)
         | 
| 321 | 
            +
                        false
         | 
| 322 | 
            +
                      rescue Parser::ParseError => error
         | 
| 323 | 
            +
                        warn("Error: #{error.message}")
         | 
| 324 | 
            +
                        highlight_error(error, item.source)
         | 
| 325 | 
            +
                        true
         | 
| 326 | 
            +
                      rescue Check::UnformattedError, Debug::NonIdempotentFormatError
         | 
| 327 | 
            +
                        true
         | 
| 328 | 
            +
                      rescue StandardError => error
         | 
| 329 | 
            +
                        warn(error.message)
         | 
| 330 | 
            +
                        warn(error.backtrace)
         | 
| 331 | 
            +
                        true
         | 
| 332 | 
            +
                      end
         | 
| 333 | 
            +
             | 
| 279 334 | 
             
                    if errored
         | 
| 280 335 | 
             
                      action.failure
         | 
| 281 336 | 
             
                      1
         | 
| @@ -287,22 +342,33 @@ module SyntaxTree | |
| 287 342 |  | 
| 288 343 | 
             
                  private
         | 
| 289 344 |  | 
| 290 | 
            -
                  def  | 
| 291 | 
            -
                     | 
| 292 | 
            -
             | 
| 293 | 
            -
             | 
| 294 | 
            -
             | 
| 295 | 
            -
             | 
| 296 | 
            -
             | 
| 297 | 
            -
             | 
| 298 | 
            -
             | 
| 299 | 
            -
             | 
| 300 | 
            -
             | 
| 301 | 
            -
                           | 
| 345 | 
            +
                  def with_workers(queue)
         | 
| 346 | 
            +
                    # If the queue is just 1 item, then we're not going to bother going
         | 
| 347 | 
            +
                    # through the whole ceremony of parallelizing the work.
         | 
| 348 | 
            +
                    return yield queue.shift if queue.size == 1
         | 
| 349 | 
            +
             | 
| 350 | 
            +
                    workers =
         | 
| 351 | 
            +
                      Etc.nprocessors.times.map do
         | 
| 352 | 
            +
                        Thread.new do
         | 
| 353 | 
            +
                          # Propagate errors in the worker threads up to the parent thread.
         | 
| 354 | 
            +
                          Thread.current.abort_on_exception = true
         | 
| 355 | 
            +
             | 
| 356 | 
            +
                          # Track whether or not there are any errors from any of the files
         | 
| 357 | 
            +
                          # that we take action on so that we can properly clean up and
         | 
| 358 | 
            +
                          # exit.
         | 
| 359 | 
            +
                          errored = false
         | 
| 360 | 
            +
             | 
| 361 | 
            +
                          # While there is still work left to do, shift off the queue and
         | 
| 362 | 
            +
                          # process the item.
         | 
| 363 | 
            +
                          (errored ||= yield queue.shift) until queue.empty?
         | 
| 364 | 
            +
             | 
| 365 | 
            +
                          # At the end, we're going to return whether or not this worker
         | 
| 366 | 
            +
                          # ever encountered an error.
         | 
| 367 | 
            +
                          errored
         | 
| 368 | 
            +
                        end
         | 
| 302 369 | 
             
                      end
         | 
| 303 | 
            -
             | 
| 304 | 
            -
             | 
| 305 | 
            -
                    end
         | 
| 370 | 
            +
             | 
| 371 | 
            +
                    workers.inject(false) { |accum, thread| accum || thread.value }
         | 
| 306 372 | 
             
                  end
         | 
| 307 373 |  | 
| 308 374 | 
             
                  # Highlights a snippet from a source and parse error.
         | 
| @@ -68,7 +68,9 @@ module SyntaxTree | |
| 68 68 | 
             
                    # going to just print out the node as it was seen in the source.
         | 
| 69 69 | 
             
                    doc =
         | 
| 70 70 | 
             
                      if leading.last&.ignore?
         | 
| 71 | 
            -
                         | 
| 71 | 
            +
                        range = source[node.location.start_char...node.location.end_char]
         | 
| 72 | 
            +
                        separator = -> { breakable(indent: false, force: true) }
         | 
| 73 | 
            +
                        seplist(range.split(/\r?\n/, -1), separator) { |line| text(line) }
         | 
| 72 74 | 
             
                      else
         | 
| 73 75 | 
             
                        node.format(self)
         | 
| 74 76 | 
             
                      end
         | 
| @@ -2,20 +2,37 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module SyntaxTree
         | 
| 4 4 | 
             
              class LanguageServer
         | 
| 5 | 
            -
                # This class provides inlay hints for the language server.  | 
| 6 | 
            -
                #  | 
| 7 | 
            -
                # is a little different for now.
         | 
| 8 | 
            -
                #
         | 
| 9 | 
            -
                # For more information, see the spec here:
         | 
| 5 | 
            +
                # This class provides inlay hints for the language server. For more
         | 
| 6 | 
            +
                # information, see the spec here:
         | 
| 10 7 | 
             
                # https://github.com/microsoft/language-server-protocol/issues/956.
         | 
| 11 | 
            -
                #
         | 
| 12 8 | 
             
                class InlayHints < Visitor
         | 
| 13 | 
            -
                   | 
| 9 | 
            +
                  # This represents a hint that is going to be displayed in the editor.
         | 
| 10 | 
            +
                  class Hint
         | 
| 11 | 
            +
                    attr_reader :line, :character, :label
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    def initialize(line:, character:, label:)
         | 
| 14 | 
            +
                      @line = line
         | 
| 15 | 
            +
                      @character = character
         | 
| 16 | 
            +
                      @label = label
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    # This is the shape that the LSP expects.
         | 
| 20 | 
            +
                    def to_json(*opts)
         | 
| 21 | 
            +
                      {
         | 
| 22 | 
            +
                        position: {
         | 
| 23 | 
            +
                          line: line,
         | 
| 24 | 
            +
                          character: character
         | 
| 25 | 
            +
                        },
         | 
| 26 | 
            +
                        label: label
         | 
| 27 | 
            +
                      }.to_json(*opts)
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  attr_reader :stack, :hints
         | 
| 14 32 |  | 
| 15 33 | 
             
                  def initialize
         | 
| 16 34 | 
             
                    @stack = []
         | 
| 17 | 
            -
                    @ | 
| 18 | 
            -
                    @after = Hash.new { |hash, key| hash[key] = +"" }
         | 
| 35 | 
            +
                    @hints = []
         | 
| 19 36 | 
             
                  end
         | 
| 20 37 |  | 
| 21 38 | 
             
                  def visit(node)
         | 
| @@ -97,7 +114,11 @@ module SyntaxTree | |
| 97 114 | 
             
                  #
         | 
| 98 115 | 
             
                  def visit_rescue(node)
         | 
| 99 116 | 
             
                    if node.exception.nil?
         | 
| 100 | 
            -
                       | 
| 117 | 
            +
                      hints << Hint.new(
         | 
| 118 | 
            +
                        line: node.location.start_line - 1,
         | 
| 119 | 
            +
                        character: node.location.start_column + "rescue".length,
         | 
| 120 | 
            +
                        label: " StandardError"
         | 
| 121 | 
            +
                      )
         | 
| 101 122 | 
             
                    end
         | 
| 102 123 |  | 
| 103 124 | 
             
                    super
         | 
| @@ -120,17 +141,20 @@ module SyntaxTree | |
| 120 141 | 
             
                    super
         | 
| 121 142 | 
             
                  end
         | 
| 122 143 |  | 
| 123 | 
            -
                  def self.find(program)
         | 
| 124 | 
            -
                    visitor = new
         | 
| 125 | 
            -
                    visitor.visit(program)
         | 
| 126 | 
            -
                    visitor
         | 
| 127 | 
            -
                  end
         | 
| 128 | 
            -
             | 
| 129 144 | 
             
                  private
         | 
| 130 145 |  | 
| 131 146 | 
             
                  def parentheses(location)
         | 
| 132 | 
            -
                     | 
| 133 | 
            -
             | 
| 147 | 
            +
                    hints << Hint.new(
         | 
| 148 | 
            +
                      line: location.start_line - 1,
         | 
| 149 | 
            +
                      character: location.start_column,
         | 
| 150 | 
            +
                      label: "₍"
         | 
| 151 | 
            +
                    )
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                    hints << Hint.new(
         | 
| 154 | 
            +
                      line: location.end_line - 1,
         | 
| 155 | 
            +
                      character: location.end_column,
         | 
| 156 | 
            +
                      label: "₎"
         | 
| 157 | 
            +
                    )
         | 
| 134 158 | 
             
                  end
         | 
| 135 159 | 
             
                end
         | 
| 136 160 | 
             
              end
         | 
| @@ -20,70 +20,60 @@ module SyntaxTree | |
| 20 20 | 
             
                  @output = output.binmode
         | 
| 21 21 | 
             
                end
         | 
| 22 22 |  | 
| 23 | 
            +
                # rubocop:disable Layout/LineLength
         | 
| 23 24 | 
             
                def run
         | 
| 24 25 | 
             
                  store =
         | 
| 25 26 | 
             
                    Hash.new do |hash, uri|
         | 
| 26 | 
            -
                       | 
| 27 | 
            +
                      filepath = CGI.unescape(URI.parse(uri).path)
         | 
| 28 | 
            +
                      File.exist?(filepath) ? (hash[uri] = File.read(filepath)) : nil
         | 
| 27 29 | 
             
                    end
         | 
| 28 30 |  | 
| 29 31 | 
             
                  while (headers = input.gets("\r\n\r\n"))
         | 
| 30 32 | 
             
                    source = input.read(headers[/Content-Length: (\d+)/i, 1].to_i)
         | 
| 31 33 | 
             
                    request = JSON.parse(source, symbolize_names: true)
         | 
| 32 34 |  | 
| 35 | 
            +
                    # stree-ignore
         | 
| 33 36 | 
             
                    case request
         | 
| 34 37 | 
             
                    in { method: "initialize", id: }
         | 
| 35 38 | 
             
                      store.clear
         | 
| 36 39 | 
             
                      write(id: id, result: { capabilities: capabilities })
         | 
| 37 | 
            -
                    in method: "initialized"
         | 
| 40 | 
            +
                    in { method: "initialized" }
         | 
| 38 41 | 
             
                      # ignored
         | 
| 39 | 
            -
                    in method: "shutdown"
         | 
| 42 | 
            +
                    in { method: "shutdown" } # tolerate missing ID to be a good citizen
         | 
| 40 43 | 
             
                      store.clear
         | 
| 44 | 
            +
                      write(id: request[:id], result: {})
         | 
| 41 45 | 
             
                      return
         | 
| 42 | 
            -
                    in {
         | 
| 43 | 
            -
                         method: "textDocument/didChange",
         | 
| 44 | 
            -
                         params: { textDocument: { uri: }, contentChanges: [{ text: }, *] }
         | 
| 45 | 
            -
                       }
         | 
| 46 | 
            +
                    in { method: "textDocument/didChange", params: { textDocument: { uri: }, contentChanges: [{ text: }, *] } }
         | 
| 46 47 | 
             
                      store[uri] = text
         | 
| 47 | 
            -
                    in {
         | 
| 48 | 
            -
                         method: "textDocument/didOpen",
         | 
| 49 | 
            -
                         params: { textDocument: { uri:, text: } }
         | 
| 50 | 
            -
                       }
         | 
| 48 | 
            +
                    in { method: "textDocument/didOpen", params: { textDocument: { uri:, text: } } }
         | 
| 51 49 | 
             
                      store[uri] = text
         | 
| 52 | 
            -
                    in {
         | 
| 53 | 
            -
                         method: "textDocument/didClose", params: { textDocument: { uri: } }
         | 
| 54 | 
            -
                       }
         | 
| 50 | 
            +
                    in { method: "textDocument/didClose", params: { textDocument: { uri: } } }
         | 
| 55 51 | 
             
                      store.delete(uri)
         | 
| 56 | 
            -
                    in {
         | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
                      write(id: id, result:  | 
| 62 | 
            -
                    in {
         | 
| 63 | 
            -
                         method: "textDocument/inlayHints",
         | 
| 64 | 
            -
                         id:,
         | 
| 65 | 
            -
                         params: { textDocument: { uri: } }
         | 
| 66 | 
            -
                       }
         | 
| 67 | 
            -
                      write(id: id, result: inlay_hints(store[uri]))
         | 
| 68 | 
            -
                    in {
         | 
| 69 | 
            -
                         method: "syntaxTree/visualizing",
         | 
| 70 | 
            -
                         id:,
         | 
| 71 | 
            -
                         params: { textDocument: { uri: } }
         | 
| 72 | 
            -
                       }
         | 
| 52 | 
            +
                    in { method: "textDocument/formatting", id:, params: { textDocument: { uri: } } }
         | 
| 53 | 
            +
                      contents = store[uri]
         | 
| 54 | 
            +
                      write(id: id, result: contents ? [format(store[uri])] : nil)
         | 
| 55 | 
            +
                    in { method: "textDocument/inlayHint", id:, params: { textDocument: { uri: } } }
         | 
| 56 | 
            +
                      contents = store[uri]
         | 
| 57 | 
            +
                      write(id: id, result: contents ? inlay_hints(store[uri]) : nil)
         | 
| 58 | 
            +
                    in { method: "syntaxTree/visualizing", id:, params: { textDocument: { uri: } } }
         | 
| 73 59 | 
             
                      write(id: id, result: PP.pp(SyntaxTree.parse(store[uri]), +""))
         | 
| 74 | 
            -
                    in method: %r{\$/.+}
         | 
| 60 | 
            +
                    in { method: %r{\$/.+} }
         | 
| 75 61 | 
             
                      # ignored
         | 
| 76 62 | 
             
                    else
         | 
| 77 63 | 
             
                      raise ArgumentError, "Unhandled: #{request}"
         | 
| 78 64 | 
             
                    end
         | 
| 79 65 | 
             
                  end
         | 
| 80 66 | 
             
                end
         | 
| 67 | 
            +
                # rubocop:enable Layout/LineLength
         | 
| 81 68 |  | 
| 82 69 | 
             
                private
         | 
| 83 70 |  | 
| 84 71 | 
             
                def capabilities
         | 
| 85 72 | 
             
                  {
         | 
| 86 73 | 
             
                    documentFormattingProvider: true,
         | 
| 74 | 
            +
                    inlayHintProvider: {
         | 
| 75 | 
            +
                      resolveProvider: false
         | 
| 76 | 
            +
                    },
         | 
| 87 77 | 
             
                    textDocumentSync: {
         | 
| 88 78 | 
             
                      change: 1,
         | 
| 89 79 | 
             
                      openClose: true
         | 
| @@ -108,16 +98,13 @@ module SyntaxTree | |
| 108 98 | 
             
                end
         | 
| 109 99 |  | 
| 110 100 | 
             
                def inlay_hints(source)
         | 
| 111 | 
            -
                   | 
| 112 | 
            -
                   | 
| 113 | 
            -
             | 
| 114 | 
            -
                  {
         | 
| 115 | 
            -
                    before: inlay_hints.before.map(&serialize),
         | 
| 116 | 
            -
                    after: inlay_hints.after.map(&serialize)
         | 
| 117 | 
            -
                  }
         | 
| 101 | 
            +
                  visitor = InlayHints.new
         | 
| 102 | 
            +
                  SyntaxTree.parse(source).accept(visitor)
         | 
| 103 | 
            +
                  visitor.hints
         | 
| 118 104 | 
             
                rescue Parser::ParseError
         | 
| 119 105 | 
             
                  # If there is a parse error, then we're not going to return any inlay
         | 
| 120 106 | 
             
                  # hints for this source.
         | 
| 107 | 
            +
                  []
         | 
| 121 108 | 
             
                end
         | 
| 122 109 |  | 
| 123 110 | 
             
                def write(value)
         | 
    
        data/lib/syntax_tree/node.rb
    CHANGED
    
    | @@ -4813,7 +4813,7 @@ module SyntaxTree | |
| 4813 4813 | 
             
                # [HeredocBeg] the opening of the heredoc
         | 
| 4814 4814 | 
             
                attr_reader :beginning
         | 
| 4815 4815 |  | 
| 4816 | 
            -
                # [ | 
| 4816 | 
            +
                # [HeredocEnd] the ending of the heredoc
         | 
| 4817 4817 | 
             
                attr_reader :ending
         | 
| 4818 4818 |  | 
| 4819 4819 | 
             
                # [Integer] how far to dedent the heredoc
         | 
| @@ -4847,7 +4847,7 @@ module SyntaxTree | |
| 4847 4847 | 
             
                end
         | 
| 4848 4848 |  | 
| 4849 4849 | 
             
                def child_nodes
         | 
| 4850 | 
            -
                  [beginning, *parts]
         | 
| 4850 | 
            +
                  [beginning, *parts, ending]
         | 
| 4851 4851 | 
             
                end
         | 
| 4852 4852 |  | 
| 4853 4853 | 
             
                alias deconstruct child_nodes
         | 
| @@ -4883,7 +4883,7 @@ module SyntaxTree | |
| 4883 4883 | 
             
                          end
         | 
| 4884 4884 | 
             
                        end
         | 
| 4885 4885 |  | 
| 4886 | 
            -
                        q. | 
| 4886 | 
            +
                        q.format(ending)
         | 
| 4887 4887 | 
             
                      end
         | 
| 4888 4888 | 
             
                    end
         | 
| 4889 4889 | 
             
                  end
         | 
| @@ -4929,6 +4929,45 @@ module SyntaxTree | |
| 4929 4929 | 
             
                end
         | 
| 4930 4930 | 
             
              end
         | 
| 4931 4931 |  | 
| 4932 | 
            +
              # HeredocEnd represents the closing declaration of a heredoc.
         | 
| 4933 | 
            +
              #
         | 
| 4934 | 
            +
              #     <<~DOC
         | 
| 4935 | 
            +
              #       contents
         | 
| 4936 | 
            +
              #     DOC
         | 
| 4937 | 
            +
              #
         | 
| 4938 | 
            +
              # In the example above the HeredocEnd node represents the closing DOC.
         | 
| 4939 | 
            +
              class HeredocEnd < Node
         | 
| 4940 | 
            +
                # [String] the closing declaration of the heredoc
         | 
| 4941 | 
            +
                attr_reader :value
         | 
| 4942 | 
            +
             | 
| 4943 | 
            +
                # [Array[ Comment | EmbDoc ]] the comments attached to this node
         | 
| 4944 | 
            +
                attr_reader :comments
         | 
| 4945 | 
            +
             | 
| 4946 | 
            +
                def initialize(value:, location:, comments: [])
         | 
| 4947 | 
            +
                  @value = value
         | 
| 4948 | 
            +
                  @location = location
         | 
| 4949 | 
            +
                  @comments = comments
         | 
| 4950 | 
            +
                end
         | 
| 4951 | 
            +
             | 
| 4952 | 
            +
                def accept(visitor)
         | 
| 4953 | 
            +
                  visitor.visit_heredoc_end(self)
         | 
| 4954 | 
            +
                end
         | 
| 4955 | 
            +
             | 
| 4956 | 
            +
                def child_nodes
         | 
| 4957 | 
            +
                  []
         | 
| 4958 | 
            +
                end
         | 
| 4959 | 
            +
             | 
| 4960 | 
            +
                alias deconstruct child_nodes
         | 
| 4961 | 
            +
             | 
| 4962 | 
            +
                def deconstruct_keys(_keys)
         | 
| 4963 | 
            +
                  { value: value, location: location, comments: comments }
         | 
| 4964 | 
            +
                end
         | 
| 4965 | 
            +
             | 
| 4966 | 
            +
                def format(q)
         | 
| 4967 | 
            +
                  q.text(value)
         | 
| 4968 | 
            +
                end
         | 
| 4969 | 
            +
              end
         | 
| 4970 | 
            +
             | 
| 4932 4971 | 
             
              # HshPtn represents matching against a hash pattern using the Ruby 2.7+
         | 
| 4933 4972 | 
             
              # pattern matching syntax.
         | 
| 4934 4973 | 
             
              #
         | 
    
        data/lib/syntax_tree/parser.rb
    CHANGED
    
    | @@ -1640,9 +1640,19 @@ module SyntaxTree | |
| 1640 1640 | 
             
                def on_heredoc_end(value)
         | 
| 1641 1641 | 
             
                  heredoc = @heredocs[-1]
         | 
| 1642 1642 |  | 
| 1643 | 
            +
                  location =
         | 
| 1644 | 
            +
                    Location.token(
         | 
| 1645 | 
            +
                      line: lineno,
         | 
| 1646 | 
            +
                      char: char_pos,
         | 
| 1647 | 
            +
                      column: current_column,
         | 
| 1648 | 
            +
                      size: value.size + 1
         | 
| 1649 | 
            +
                    )
         | 
| 1650 | 
            +
             | 
| 1651 | 
            +
                  heredoc_end = HeredocEnd.new(value: value.chomp, location: location)
         | 
| 1652 | 
            +
             | 
| 1643 1653 | 
             
                  @heredocs[-1] = Heredoc.new(
         | 
| 1644 1654 | 
             
                    beginning: heredoc.beginning,
         | 
| 1645 | 
            -
                    ending:  | 
| 1655 | 
            +
                    ending: heredoc_end,
         | 
| 1646 1656 | 
             
                    dedent: heredoc.dedent,
         | 
| 1647 1657 | 
             
                    parts: heredoc.parts,
         | 
| 1648 1658 | 
             
                    location:
         | 
    
        data/lib/syntax_tree/version.rb
    CHANGED
    
    
| @@ -497,6 +497,10 @@ module SyntaxTree | |
| 497 497 | 
             
                    visit_token(node, "heredoc_beg")
         | 
| 498 498 | 
             
                  end
         | 
| 499 499 |  | 
| 500 | 
            +
                  def visit_heredoc_end(node)
         | 
| 501 | 
            +
                    visit_token(node, "heredoc_end")
         | 
| 502 | 
            +
                  end
         | 
| 503 | 
            +
             | 
| 500 504 | 
             
                  def visit_hshptn(node)
         | 
| 501 505 | 
             
                    node(node, "hshptn") do
         | 
| 502 506 | 
             
                      field("constant", node.constant) if node.constant
         | 
    
        data/lib/syntax_tree/visitor.rb
    CHANGED
    
    | @@ -194,6 +194,9 @@ module SyntaxTree | |
| 194 194 | 
             
                # Visit a HeredocBeg node.
         | 
| 195 195 | 
             
                alias visit_heredoc_beg visit_child_nodes
         | 
| 196 196 |  | 
| 197 | 
            +
                # Visit a HeredocEnd node.
         | 
| 198 | 
            +
                alias visit_heredoc_end visit_child_nodes
         | 
| 199 | 
            +
             | 
| 197 200 | 
             
                # Visit a HshPtn node.
         | 
| 198 201 | 
             
                alias visit_hshptn visit_child_nodes
         | 
| 199 202 |  | 
    
        data/lib/syntax_tree.rb
    CHANGED
    
    
    
        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:  | 
| 4 | 
            +
              version: 3.0.0
         | 
| 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-07-05 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: prettier_print
         | 
| @@ -160,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 160 160 | 
             
                - !ruby/object:Gem::Version
         | 
| 161 161 | 
             
                  version: '0'
         | 
| 162 162 | 
             
            requirements: []
         | 
| 163 | 
            -
            rubygems_version: 3. | 
| 163 | 
            +
            rubygems_version: 3.3.3
         | 
| 164 164 | 
             
            signing_key:
         | 
| 165 165 | 
             
            specification_version: 4
         | 
| 166 166 | 
             
            summary: A parser based on ripper
         |