prepareRename / rename returns nil at constant definition sites (ConstantWriteNode, ConstantTargetNode)
Nobody has claimed this yet.
- Dominant language
- Ruby
- Stars
- 2k
- Forks
- 281
- Avg merge
- 2h 14m
- Merged PRs (30d)
- 6
Description
Summary
textDocument/prepareRename returns nil (and textDocument/rename silently no-ops) when the cursor is placed on the definition site of a top-level constant. Renaming works only from reference sites. The result is the editor surfaces "current selection cannot be renamed" on what looks like a perfectly valid rename target.
Reproduction
example.rb:
class Example
FOO = 1
#^ cursor here
end
LSP request:
textDocument/prepareRename
position: { line: 1, character: 2 } # on the F of FOO
Expected: a Range covering FOO, allowing the editor to proceed with rename.
Actual: server returns null. Editor reports "cannot be renamed."
Placing the cursor on a reference of FOO elsewhere works correctly.
The same applies to multi-assignment targets (A, B = 1, 2).
Root cause
PrepareRename and Rename filter RubyDocument.locate to:
Prism::ConstantReadNodePrism::ConstantPathNodePrism::ConstantPathTargetNode
A top-level constant assignment LHS (FOO = ...) parses as Prism::ConstantWriteNode, and a multi-assignment LHS parses as Prism::ConstantTargetNode. Neither is in the filter, so locate does not return them and perform exits early.
Even with the filter expanded, RubyIndexer::Index.constant_name also only handles the three read-style nodes and returns nil for write nodes, so the rename path would silently no-op without a matching update there.
Affected files on main:
lib/ruby_lsp/requests/prepare_rename.rb(node_types:filter)lib/ruby_lsp/requests/rename.rb(node_types:filter)lib/ruby_indexer/lib/ruby_indexer/index.rb(Index.constant_name)
Why this looks like an oversight
Reviewed the introducing PRs and the recent Rubydex migration; none discuss the definition-site case:
- #2626 "Add rename support for constants" introduced the filter. PR description and review only discuss
ConstantReadNode/ConstantPathNode/ConstantPathTargetNode. Tests cursor on class names (class RenameMe), never onFOO = .... - #2894 "Add Prepare Rename Request" copies the same three-node list verbatim from
Rename. - #4033 "Migrate rename to use Rubydex" carries the filter and
Index.constant_name's case statement forward unchanged. New tests still cursor on class names only. - Doc PR #3113 states "Rename is currently only supported for constants, module names and class names," consistent with the maintainers believing constants are fully covered.
No issue or PR I could find specifically mentions cursoring on ConstantWriteNode / ConstantTargetNode.
Suggested fix scope
Minimal, low-risk:
- Add
Prism::ConstantWriteNodeandPrism::ConstantTargetNodeto bothnode_types:filters. - Extend
RubyIndexer::Index.constant_nameto returnnode.name.to_sfor those two types. - In
PrepareRename#perform, usetarget.name_loc(nottarget.location) whentargetis aConstantWriteNode, since itslocationcovers the wholeFOO = valueexpression.
Out of scope for a first pass (separate node types, more edge cases): ConstantPathWriteNode, ConstantPathOperatorWriteNode, ConstantOperatorWriteNode, ConstantOrWriteNode, ConstantAndWriteNode. Worth tackling in a follow-up if there is interest.
Local verification
I applied the three-file patch above to a vendored copy of ruby-lsp 0.26.9 and confirmed that:
prepareRenameon a top-level constant definition now returns the correct range.renamefrom the definition site updates both the definition and all references in the workspace.- Reference-site rename (the previously working case) is unaffected.
Happy to send a PR with tests if useful; opening this as a bug report first to surface whether the constraint was deliberate.
Environment
- ruby-lsp 0.26.9 (also confirmed via source review that
mainpost-#4033 / v0.27.0.beta1+ has the same filters andconstant_namecase statement) - Ruby 3.3.3
- Editor: Sublime Text with LSP-ruby
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in lib/ruby_lsp/requests/prepare_rename.rb and lib/ruby_lsp/requests/rename.rb, then inspect RubyIndexer::Index.constant_name in lib/ruby_indexer/lib/ruby_indexer/index.rb. Review the existing rename and prepare-rename tests before making the focused change; done means definition-site prepareRename returns the FOO range and rename updates the definition and references without regressing reference-site behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- ruby
- Domain
- developer-experience, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100