Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

prepareRename / rename returns nil at constant definition sites (ConstantWriteNode, ConstantTargetNode)

Open
#4,090 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
76/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
ruby

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.

Description

good-first-issue pinned

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::ConstantReadNode
  • Prism::ConstantPathNode
  • Prism::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 on FOO = ....
  • #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:

  1. Add Prism::ConstantWriteNode and Prism::ConstantTargetNode to both node_types: filters.
  2. Extend RubyIndexer::Index.constant_name to return node.name.to_s for those two types.
  3. In PrepareRename#perform, use target.name_loc (not target.location) when target is a ConstantWriteNode, since its location covers the whole FOO = value expression.

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:

  • prepareRename on a top-level constant definition now returns the correct range.
  • rename from 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 main post-#4033 / v0.27.0.beta1+ has the same filters and constant_name case statement)
  • Ruby 3.3.3
  • Editor: Sublime Text with LSP-ruby
Dominant language
Ruby
Stars
2k
Forks
281
Avg merge
1h 47m
Merged PRs (30d)
8

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Shopify/ruby-lsp

All issues in Shopify/ruby-lsp

Similar issues

More Ruby issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.