Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use translated Prism AST to run RuboCop #1849

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

vinistock
Copy link
Member

Motivation

Use the Prism AST translator to reuse our existing AST to run RuboCop.

Implementation

RuboCop doesn't have an API where we can simply ask "format this AST", so implementing this requires patching a handful of places so that we can pass the AST around.

The idea is that we instantiate the ProcessedSource ourselves and prevent the Prism translator from re-parsing the file if we already have an AST in hand.

Questions

  1. Is this the best way to pass the existing AST around? Could we be doing this in a more elegant way?
  2. What do we need to do to prevent this from failing in older RuboCop versions that do not support Prism as a backend?

Automated Tests

Will add tests.

Manual Tests

  1. Start the LSP on this branch
  2. Verify that diagnostics and formatting is working properly

@vinistock vinistock added enhancement New feature or request server This pull request should be included in the server gem's release notes labels Mar 27, 2024
@vinistock vinistock self-assigned this Mar 27, 2024
@vinistock
Copy link
Member Author

Tagging @koic and @kddnewton. I'd love to hear your thoughts on the approach and if there's something that can be upstreamed to Prism or RuboCop to make this integration more seamless.

Copy link
Contributor

@kddnewton kddnewton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. In general I think we should provide the ability to pass through a parsed AST to Prism::Translation::Parser in prism itself instead of patching it here. One way to do this would be change it in prism to do:

def parse(source_buffer)
  ...
  Prism.parse(...)
  ...
end

instead to:

def initialize(parser: Prism)
  @parser = parser
end

def parse(source_buffer)
  ....
  @parser.parse(...)
  ...
end

and then pass in a custom object in ruby-lsp for the parser that would return the parse result.

Comment on lines 122 to 128
processed_source = T.unsafe(::RuboCop::AST::ProcessedSource).new(
@options[:stdin],
Prism::Translation::Parser::VERSION_3_3,
file,
parser_engine: parser_engine,
prism_result: @parse_result,
)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could avoid some patching if there was a way to instantiate the ProcessedSource with an existing AST and token list. @koic, would you be open to accepting that?

It would allow us to avoid some patching.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I'm not familiar with Ruby LSP, I cannot say much at this point. However, I'd suggest opening a PR to RuboCop AST. There might be adjustments needed regarding the API, but I think they could be acceptable depending on the use case. cc @bbatsov @marcandre
https://github.com/rubocop/rubocop-ast

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious about the context. In particular, why is @parse_result being passed around as an AST and not a ProcessedSource? I.e., would it be possible upstream in the code to generate a ProcessedSource instead of just an AST, and pass that information instead of just the AST?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All features in the Ruby LSP depend only the Prim's parse result, so we keep that in our representation of documents.

For RuboCop, we just invoke run with the file contents, which will let it re-parse the file and do everything it needs to do.

The Ruby LSP supports multiple formatters. We hook into RuboCop if it is available, but it's not a dependency. So we can't depend on RuboCop internals for our basic document representations, like using a ProcessedSource. All RuboCop things are limited to the runner here.

Now that RuboCop supports the Prism backend, we're trying to find what the easiest way of reusing the existing AST is, so that we can stop parsing documents twice.

The ideal API would be something like what Syntax Tree supports, which allows you to format a given AST. That would not only simplify the code here a lot, it would enable RuboCop to support range formatting for LSPs, which is currently impossible.

That said, the ideal API may require significant refactors, which is why I suggested something a bit simpler that will still minimize the number of patches. If we can instantiate the ProcessedSource in a way that allows us to reuse the existing AST and prevents the second parse, that'll already simplify the code quite a bit.

Copy link

@marcandre marcandre Apr 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But a ProcessedDocument also holds the tokens and the comments

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't understand what you mean. Are there two different classes that would require changing? Or are you saying we could be using something else?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use case this PR aims to address appears reasonable to me. When using a Ruby code with multiple tools (e.g., RuboCop, TypeProf, Steep...), parsing the same source code to an AST in each tool becomes a redundant process. In such cases, it makes sense to reuse the one AST across different tools.

In this PR, the only change to the interface for reusing the AST is the addition of the prism_result keyword argument. This means that backward compatibility is maintained, and there is no impact on the existing RuboCop (AST).

In summary, I think this proposal can be accepted by RuboCop and RuboCop AST. Considering this is not a specialized requirement but a backend tool for LSP, it would be more rational to provide a published interface rather than having users write monkey patches. Below, I share one concern and an idea related to it.

NOTE: Naming and the two ASTs (whitequark AST and Prism AST)

A point of caution is that the current AST RuboCop can analyze is only the whitequark/parser AST interface (Prism::Translation::Parser). It cannot process the vanilla Prism AST interface.

Therefore, I'm not sure if prism_result is the best name for the AST argument (since it's not the Prism AST). The keyword argument name could be ast, which is simpler, but naming is difficult and I'm not confident that it's better 😅

Here is a proposed design for the ast parameter, assuming future RuboCop can use Prism AST:

  • Current situation: When the ast: ast parameter is passed along with either parser_engine: :parser_whitequark or parser_engine: parser_prism, the AST is processed as a whitequark AST.
  • Future extension: When the ast: ast parameter and parser_engine: :prism are passed, the AST would be processed as a Prism AST (note that parser_engine: prism is not yet implemented).

This would potentially allow for a future interface where RuboCop can use a direct Prism AST without Prism::Translation::Parser, and handle any incompatibilities in ASTs.

@vinistock vinistock force-pushed the vs/use_prism_translator branch from 3095014 to 35ec8e9 Compare May 3, 2024 20:18
@@ -31,12 +31,12 @@ def initialize(source:, version:, uri:, encoding: Encoding::UTF_8)
@version = T.let(version, Integer)
@uri = T.let(uri, URI::Generic)
@needs_parsing = T.let(true, T::Boolean)
@parse_result = T.let(parse, Prism::ParseResult)
@parse_result = T.let(parse, Prism::ParseLexResult)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should pull the ast out of this result here, so that tree can just be an attr_reader

Copy link
Contributor

This pull request is being marked as stale because there was no activity in the last 2 months

@github-actions github-actions bot added the Stale label Jul 21, 2024
@vinistock vinistock added pinned This issue or pull request is pinned and won't be marked as stale and removed Stale labels Jul 22, 2024
koic added a commit to koic/prism that referenced this pull request Feb 25, 2025
Follow-up to Shopify/ruby-lsp#1849.

This is an extension of `Prism::Translation::Parser` to implement Shopify/ruby-lsp#1849.
It is based on the comments in Shopify/ruby-lsp#1849 (review),
but also adds a default argument for delegation to `Parser::Base` super class.
koic added a commit to koic/prism that referenced this pull request Feb 25, 2025
Follow-up to Shopify/ruby-lsp#1849.

This is an extension of `Prism::Translation::Parser` to implement Shopify/ruby-lsp#1849.
It is based on the comments in Shopify/ruby-lsp#1849 (review),
but also adds a default argument for delegation to `Parser::Base` super class.
koic added a commit to koic/rubocop-ast that referenced this pull request Feb 25, 2025
Follow-up to Shopify/ruby-lsp#1849.

This feature utilizes ruby/prism#3478 to implement Shopify/ruby-lsp#1849.

By using ruby/prism#3478, this feature enables performance improvements by reusing Prism's parsed results
instead of parsing the source code. Below is a sample code snippet, which is expected to improve performance by
1.3x in this case.

```ruby
#!/usr/local/bin/ruby

require 'benchmark/ips'
require 'prism'
require 'rubocop-ast'

@source = File.read(__FILE__)
@parse_lex_result = Prism.parse_lex(@source)

def build_processed_source(parse_lex_result)
  RuboCop::AST::ProcessedSource.new(
    @source,
    3.4,
    __FILE__,
    parser_engine: 'parser_prism',
    prism_result: parse_lex_result
  )
end

Benchmark.ips do |x|
  x.report('source')                { build_processed_source(nil) }
  x.report('Prism::ParseLexResult') { build_processed_source(@parse_lex_result) }

  x.compare!
end
```

```console
$ bundle exec ruby reusable_ast.rb
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [x86_64-darwin23]
Warming up --------------------------------------
              source   116.000 i/100ms
Prism::ParseLexResult
                       151.000 i/100ms
Calculating -------------------------------------
              source      1.144k (± 8.4%) i/s -      5.684k in   5.018270s
Prism::ParseLexResult
                          1.460k (± 5.2%) i/s -      7.399k in   5.082102s

Comparison:
Prism::ParseLexResult:     1460.3 i/s
              source:     1144.4 i/s - 1.28x  slower
```

Achieving 1.3x speedup with such this simple modification is a significant improvement for Ruby LSP and its users.

## Compatibility

By using `parser_class.instance_method(:initialize).parameters.assoc(:key)`,
the implementation checks whether `Prism#initialize` supports the `:parser` keyword.
If it does not, the system falls back to the conventional parsing method.
This ensures backward compatibility with previous versions of Prism.

## Development Notes

Since this feature is specifically designed for Prism, the keyword name `prism_result` is meant for Prism.
While it may be possible to achieve similar functionality with the Parser gem, there are currently no concrete use cases.
Therefore, for clarity, we have chosen `prism_result` as the keyword.

Additionally, Prism has two types of results: `Prism::ParseResult` and `Prism::ParseLexResult`,
but the `prism_result` keyword argument is meant to receive `Prism::ParseLexResult`.
Since `prism_parse_lex_result` would be too long, the name `prism_result` was chosen to align with the superclass `Prism::Result`.
koic added a commit to koic/rubocop-ast that referenced this pull request Feb 25, 2025
Follow-up to Shopify/ruby-lsp#1849.

This feature utilizes ruby/prism#3478 to implement Shopify/ruby-lsp#1849.

By using ruby/prism#3478, this feature enables performance improvements by reusing Prism's parsed results
instead of parsing the source code. Below is a sample code snippet, which is expected to improve performance by
1.3x in this case.

```ruby
#!/usr/local/bin/ruby

require 'benchmark/ips'
require 'prism'
require 'rubocop-ast'

@source = File.read(__FILE__)
@parse_lex_result = Prism.parse_lex(@source)

def build_processed_source(parse_lex_result)
  RuboCop::AST::ProcessedSource.new(
    @source,
    3.4,
    __FILE__,
    parser_engine: 'parser_prism',
    prism_result: parse_lex_result
  )
end

Benchmark.ips do |x|
  x.report('source')                { build_processed_source(nil) }
  x.report('Prism::ParseLexResult') { build_processed_source(@parse_lex_result) }

  x.compare!
end
```

```console
$ bundle exec ruby reusable_ast.rb
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [x86_64-darwin23]
Warming up --------------------------------------
              source   116.000 i/100ms
Prism::ParseLexResult
                       151.000 i/100ms
Calculating -------------------------------------
              source      1.144k (± 8.4%) i/s -      5.684k in   5.018270s
Prism::ParseLexResult
                          1.460k (± 5.2%) i/s -      7.399k in   5.082102s

Comparison:
Prism::ParseLexResult:     1460.3 i/s
              source:     1144.4 i/s - 1.28x  slower
```

Achieving 1.3x speedup with such this simple modification is a significant improvement for Ruby LSP and its users.

## Compatibility

By using `parser_class.instance_method(:initialize).parameters.assoc(:key)`,
the implementation checks whether `Prism#initialize` supports the `:parser` keyword.
If it does not, the system falls back to the conventional parsing method.
This ensures backward compatibility with previous versions of Prism.

## Development Notes

Since this feature is specifically designed for Prism, the keyword name `prism_result` is meant for Prism.
While it may be possible to achieve similar functionality with the Parser gem, there are currently no concrete use cases.
Therefore, for clarity, we have chosen `prism_result` as the keyword.

Additionally, Prism has two types of results: `Prism::ParseResult` and `Prism::ParseLexResult`,
but the `prism_result` keyword argument is meant to receive `Prism::ParseLexResult`.
Since `prism_parse_lex_result` would be too long, the name `prism_result` was chosen to align with the superclass `Prism::Result`.
koic added a commit to koic/prism that referenced this pull request Feb 25, 2025
Follow-up to Shopify/ruby-lsp#1849.

This is an extension of `Prism::Translation::Parser` to implement Shopify/ruby-lsp#1849.
It is based on the comments in Shopify/ruby-lsp#1849 (review),
but also adds a default argument for delegation to `Parser::Base` super class.

Using this API, rubocop/rubocop-ast#359 has been implemented in RuboCop AST.
As detailed in rubocop/rubocop-ast#359, this change is expected to improve performance by 1.3x
for some source code.
Achieving a 1.3x speedup with such this simple modification is a significant improvement for Ruby LSP and its users.
koic added a commit to koic/rubocop that referenced this pull request Feb 25, 2025
Follow-up to Shopify/ruby-lsp#1849

This feature utilizes ruby/prism#3478 and rubocop/rubocop-ast#359
to implement Shopify/ruby-lsp#1849.

As described in rubocop/rubocop-ast#359,
it speeds up the processing around `Prism::Translation::Parser` via `ProcessedSource` by 1.3x when using the Ruby LSP add-on.
koic added a commit to koic/rubocop that referenced this pull request Feb 25, 2025
Follow-up to Shopify/ruby-lsp#1849

This feature utilizes ruby/prism#3478 and rubocop/rubocop-ast#359
to implement Shopify/ruby-lsp#1849.

As described in rubocop/rubocop-ast#359,
it speeds up the processing around `Prism::Translation::Parser` via `ProcessedSource` by 1.3x when using the Ruby LSP add-on.
@koic
Copy link
Contributor

koic commented Feb 25, 2025

@vinistock

Ruby LSP currently has over 1.3M+ installations, making this optimization meaningful for users:
https://marketplace.visualstudio.com/items?itemName=Shopify.ruby-lsp

A key point is that users can benefit from it without any breaking changes.

To implement this proposal, the following PRs have been opened for Prism, RuboCop AST, and RuboCop:

As described in this PR, the return value of document.parse_result must be Prism::ParseLexResult. For more details, refer to an example in rubocop/rubocop#13899.

Additionally, if ProcessedSource is used directly from Ruby LSP, refer to rubocop/rubocop-ast#359. Pay particular attention to the keyword argument prism_result and the requirement that its value must be Prism::ParseLexResult.

cc @bbatsov @marcandre @kddnewton

koic added a commit to koic/rubocop-ast that referenced this pull request Feb 25, 2025
Follow-up to Shopify/ruby-lsp#1849.

This feature utilizes ruby/prism#3478 to implement Shopify/ruby-lsp#1849.

By using ruby/prism#3478, this feature enables performance improvements by reusing Prism's parsed results
instead of parsing the source code. Below is a sample code snippet, which is expected to improve performance by
1.3x in this case.

```ruby
#!/usr/local/bin/ruby

require 'benchmark/ips'
require 'prism'
require 'rubocop-ast'

@source = File.read(__FILE__)
@parse_lex_result = Prism.parse_lex(@source)

def build_processed_source(parse_lex_result)
  RuboCop::AST::ProcessedSource.new(
    @source,
    3.4,
    __FILE__,
    parser_engine: 'parser_prism',
    prism_result: parse_lex_result
  )
end

Benchmark.ips do |x|
  x.report('source')                { build_processed_source(nil) }
  x.report('Prism::ParseLexResult') { build_processed_source(@parse_lex_result) }

  x.compare!
end
```

```console
$ bundle exec ruby reusable_ast.rb
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [x86_64-darwin23]
Warming up --------------------------------------
              source   116.000 i/100ms
Prism::ParseLexResult
                       151.000 i/100ms
Calculating -------------------------------------
              source      1.144k (± 8.4%) i/s -      5.684k in   5.018270s
Prism::ParseLexResult
                          1.460k (± 5.2%) i/s -      7.399k in   5.082102s

Comparison:
Prism::ParseLexResult:     1460.3 i/s
              source:     1144.4 i/s - 1.28x  slower
```

Achieving 1.3x speedup with such this simple modification is a significant improvement for Ruby LSP and its users.

## Compatibility

By using `parser_class.instance_method(:initialize).parameters.assoc(:key)`,
the implementation checks whether `Prism#initialize` supports the `:parser` keyword.
If it does not, the system falls back to the conventional parsing method.
This ensures backward compatibility with previous versions of Prism.

## Development Notes

Since this feature is specifically designed for Prism, the keyword name `prism_result` is meant for Prism.
While it may be possible to achieve similar functionality with the Parser gem, there are currently no concrete use cases.
Therefore, for clarity, we have chosen `prism_result` as the keyword.

Additionally, Prism has two types of results: `Prism::ParseResult` and `Prism::ParseLexResult`,
but the `prism_result` keyword argument is meant to receive `Prism::ParseLexResult`.
Since `prism_parse_lex_result` would be too long, the name `prism_result` was chosen to align with the superclass `Prism::Result`.
koic added a commit to koic/prism that referenced this pull request Feb 25, 2025
Follow-up to Shopify/ruby-lsp#1849.

This is an extension of `Prism::Translation::Parser` to implement Shopify/ruby-lsp#1849.
It is based on the comments in Shopify/ruby-lsp#1849 (review),
but also adds a default argument for delegation to `Parser::Base` super class.

Using this API, rubocop/rubocop-ast#359 has been implemented in RuboCop AST.
As detailed in rubocop/rubocop-ast#359, this change is expected to improve performance by 1.3x
for some source code.
Achieving a 1.3x speedup with such this simple modification is a significant improvement for Ruby LSP and its users.
@bbatsov
Copy link

bbatsov commented Feb 25, 2025

@koic I'm totally in favor of doing the necessary changes to RuboCop and rubocop-ast.

koic added a commit to koic/rubocop-ast that referenced this pull request Feb 25, 2025
Follow-up to Shopify/ruby-lsp#1849.

This feature utilizes ruby/prism#3478 to implement Shopify/ruby-lsp#1849.

By using ruby/prism#3478, this feature enables performance improvements by reusing Prism's parsed results
instead of parsing the source code. Below is a sample code snippet, which is expected to improve performance by
1.3x in this case.

```ruby
#!/usr/local/bin/ruby

require 'benchmark/ips'
require 'prism'
require 'rubocop-ast'

@source = File.read(__FILE__)
@parse_lex_result = Prism.parse_lex(@source)

def build_processed_source(parse_lex_result)
  RuboCop::AST::ProcessedSource.new(
    @source,
    3.4,
    __FILE__,
    parser_engine: 'parser_prism',
    prism_result: parse_lex_result
  )
end

Benchmark.ips do |x|
  x.report('source')                { build_processed_source(nil) }
  x.report('Prism::ParseLexResult') { build_processed_source(@parse_lex_result) }

  x.compare!
end
```

```console
$ bundle exec ruby reusable_ast.rb
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [x86_64-darwin23]
Warming up --------------------------------------
              source   116.000 i/100ms
Prism::ParseLexResult
                       151.000 i/100ms
Calculating -------------------------------------
              source      1.144k (± 8.4%) i/s -      5.684k in   5.018270s
Prism::ParseLexResult
                          1.460k (± 5.2%) i/s -      7.399k in   5.082102s

Comparison:
Prism::ParseLexResult:     1460.3 i/s
              source:     1144.4 i/s - 1.28x  slower
```

Achieving 1.3x speedup with such this simple modification is a significant improvement for Ruby LSP and its users.

## Compatibility

By using `parser_class.instance_method(:initialize).parameters.assoc(:key)`,
the implementation checks whether `Prism#initialize` supports the `:parser` keyword.
If it does not, the system falls back to the conventional parsing method.
This ensures backward compatibility with previous versions of Prism.

## Development Notes

Since this feature is specifically designed for Prism, the keyword name `prism_result` is meant for Prism.
While it may be possible to achieve similar functionality with the Parser gem, there are currently no concrete use cases.
Therefore, for clarity, we have chosen `prism_result` as the keyword.

Additionally, Prism has two types of results: `Prism::ParseResult` and `Prism::ParseLexResult`,
but the `prism_result` keyword argument is meant to receive `Prism::ParseLexResult`.
Since `prism_parse_lex_result` would be too long, the name `prism_result` was chosen to align with the superclass `Prism::Result`.
koic added a commit to koic/rubocop that referenced this pull request Feb 25, 2025
Follow-up to Shopify/ruby-lsp#1849

This feature utilizes ruby/prism#3478 and rubocop/rubocop-ast#359
to implement Shopify/ruby-lsp#1849.

As described in rubocop/rubocop-ast#359,
it speeds up the processing around `Prism::Translation::Parser` via `ProcessedSource` by 1.3x when using the Ruby LSP add-on.
matzbot pushed a commit to ruby/ruby that referenced this pull request Feb 25, 2025
Follow-up to Shopify/ruby-lsp#1849.

This is an extension of `Prism::Translation::Parser` to implement Shopify/ruby-lsp#1849.
It is based on the comments in Shopify/ruby-lsp#1849 (review),
but also adds a default argument for delegation to `Parser::Base` super class.

Using this API, rubocop/rubocop-ast#359 has been implemented in RuboCop AST.
As detailed in rubocop/rubocop-ast#359, this change is expected to improve performance by 1.3x
for some source code.
Achieving a 1.3x speedup with such this simple modification is a significant improvement for Ruby LSP and its users.

ruby/prism@925725291c
@vinistock
Copy link
Member Author

Amazing work! Thanks for putting those together. With the necessary changes pushed to RuboCop, the implementation will become much more straight forward.

I'm keeping an eye on the PRs so that we can make the changes on the Ruby LSP.

koic added a commit to rubocop/rubocop-ast that referenced this pull request Mar 11, 2025
Follow-up to Shopify/ruby-lsp#1849.

This feature utilizes ruby/prism#3478 to implement Shopify/ruby-lsp#1849.

By using ruby/prism#3478, this feature enables performance improvements by reusing Prism's parsed results
instead of parsing the source code. Below is a sample code snippet, which is expected to improve performance by
1.3x in this case.

```ruby
#!/usr/local/bin/ruby

require 'benchmark/ips'
require 'prism'
require 'rubocop-ast'

@source = File.read(__FILE__)
@parse_lex_result = Prism.parse_lex(@source)

def build_processed_source(parse_lex_result)
  RuboCop::AST::ProcessedSource.new(
    @source,
    3.4,
    __FILE__,
    parser_engine: 'parser_prism',
    prism_result: parse_lex_result
  )
end

Benchmark.ips do |x|
  x.report('source')                { build_processed_source(nil) }
  x.report('Prism::ParseLexResult') { build_processed_source(@parse_lex_result) }

  x.compare!
end
```

```console
$ bundle exec ruby reusable_ast.rb
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [x86_64-darwin23]
Warming up --------------------------------------
              source   116.000 i/100ms
Prism::ParseLexResult
                       151.000 i/100ms
Calculating -------------------------------------
              source      1.144k (± 8.4%) i/s -      5.684k in   5.018270s
Prism::ParseLexResult
                          1.460k (± 5.2%) i/s -      7.399k in   5.082102s

Comparison:
Prism::ParseLexResult:     1460.3 i/s
              source:     1144.4 i/s - 1.28x  slower
```

Achieving 1.3x speedup with such this simple modification is a significant improvement for Ruby LSP and its users.

## Compatibility

By using `parser_class.instance_method(:initialize).parameters.assoc(:key)`,
the implementation checks whether `Prism#initialize` supports the `:parser` keyword.
If it does not, the system falls back to the conventional parsing method.
This ensures backward compatibility with previous versions of Prism.

## Development Notes

Since this feature is specifically designed for Prism, the keyword name `prism_result` is meant for Prism.
While it may be possible to achieve similar functionality with the Parser gem, there are currently no concrete use cases.
Therefore, for clarity, we have chosen `prism_result` as the keyword.

Additionally, Prism has two types of results: `Prism::ParseResult` and `Prism::ParseLexResult`,
but the `prism_result` keyword argument is meant to receive `Prism::ParseLexResult`.
Since `prism_parse_lex_result` would be too long, the name `prism_result` was chosen to align with the superclass `Prism::Result`.
koic added a commit to koic/rubocop that referenced this pull request Mar 16, 2025
Follow-up to Shopify/ruby-lsp#1849

This feature utilizes ruby/prism#3478 and rubocop/rubocop-ast#359
to implement Shopify/ruby-lsp#1849.

As described in rubocop/rubocop-ast#359,
it speeds up the processing around `Prism::Translation::Parser` via `ProcessedSource` by 1.3x when using the Ruby LSP add-on.
@koic
Copy link
Contributor

koic commented Mar 16, 2025

@vinistock RuboCop AST rubocop/rubocop-ast#359 introduced an API for ProcessedSource to reuse PrismLexResult. This is available in rubocop-ast 1.39 or later. However, actual reuse requires Prism ruby/prism#3478, which has not been released yet. In any case, I hope these changes eliminate the need for individual hacks.

koic added a commit to koic/rubocop that referenced this pull request Mar 18, 2025
Follow-up to Shopify/ruby-lsp#1849

This feature utilizes ruby/prism#3478 and rubocop/rubocop-ast#359
to implement Shopify/ruby-lsp#1849.

As described in rubocop/rubocop-ast#359,
it speeds up the processing around `Prism::Translation::Parser` via `ProcessedSource` by 1.3x when using the Ruby LSP add-on.
@koic
Copy link
Contributor

koic commented Mar 19, 2025

However, actual reuse requires Prism ruby/prism#3478, which has not been released yet.

It is included in the released Prism 1.4.
https://rubygems.org/gems/prism/versions/1.4.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request pinned This issue or pull request is pinned and won't be marked as stale server This pull request should be included in the server gem's release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants