Repository navigation
Conversation
Resolve local variables per scope with a Prism visitor that uses the depth Prism assigns to each local variable node. This covers reads, writes, parameters, block locals, pattern matching, regexp named captures and hash/keyword shorthands, and only looks at the current document since locals can't be referenced from other files.
Author
|
I have signed the CLA! |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Closes #3111
Closes #2642
Find references and rename return nothing for local variables, and go to definition says "No definition found". This is one of the most common things people reach for in an editor, and the earlier attempt (#3558) went stale.
Implementation
Requests::Support::LocalVariableFinderis aPrism::Visitorthat groups every occurrence of a local by the scope that declares it, using thedepthPrism stores on local variable nodes. Def, class, module and singleton class bodies are hard boundaries; blocks and lambdas can see their parents. Superclasses,class << expranddef recv.mreceivers are visited in the outer scope, as Ruby evaluates them.forandrescue => etargets, every parameter kind (including destructured block params), block locals (|a; b|), pattern matching (^pin,=> var,in { name: }), regexp named captures (the name inside the group is used), and{ name: }/foo(name:)shorthands. Numbered params anditare ignored.includeDeclaration: falseexcludes the declaration.<name> = nilwith Prism (rejects constants, keywords,_1, etc.)InvalidNameErrorif the new name is a local or a bare method call in any scope that can see the variable, since that would change behavior{ name: }becomes{ name: new_name }typed: trueor higher files where Sorbet already provides this.test_definition_call_node_precisionasserted that thefooreceiver infoo.messagereturns nothing; it now jumps to thefooparameter, so I updated that assertion.The finder doesn't touch the indexer, so porting this onto the Rubydex branch should only mean adjusting the few lines at the top of each request. Happy to do that if you'd rather it land there.
Building the finder for a 1,500 line file (
lib/ruby_lsp/server.rb) takes about 2 ms on my machine (Apple Silicon, median of 15 runs).Automated Tests
Added
test/requests/support/local_variable_finder_test.rb(scoping, shadowing, every parameter type, patterns, regexp captures, shorthands, cursor at the end of a name, multibyte, name collision checks) plus request-level tests for references (including ERB andincludeDeclaration: false), rename (methods, params, shorthands, hash patterns, regexp captures, multibyte, collisions, invalid names, keyword params), prepare rename and definition (including the Sorbet case).Ran
bundle exec rake test, Sorbet type checking (thesorbetbinary directly, sincesrb tc's cache couldn't run in my sandbox) andbundle exec rubocoplocally. Everything related passes; the only failures I saw locally are insetup_bundler_test.rb/integration_test.rb/ bundle composition tests inserver_test.rb, which also fail onmainin my environment (they need to install gems).Manual Tests
sumand run Find All References: you should get the three uses inside this method only.sumand rename tosubtotal: the hash becomes{ sum: subtotal, tax: }.tax: it is rejected because it's a keyword parameter.iteminside the block jumps to the block parameter.