diff --git a/lib/ruby_indexer/lib/ruby_indexer/index.rb b/lib/ruby_indexer/lib/ruby_indexer/index.rb index 4cf8895d6..dbafcece4 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/index.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/index.rb @@ -41,6 +41,8 @@ def constant_name(node) case node when Prism::ConstantPathNode, Prism::ConstantReadNode, Prism::ConstantPathTargetNode node.full_name + when Prism::ConstantWriteNode, Prism::ConstantTargetNode + node.name.to_s end rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError, Prism::ConstantPathNode::MissingNodesInConstantPathError diff --git a/lib/ruby_indexer/test/index_test.rb b/lib/ruby_indexer/test/index_test.rb index f236bd00b..c82d883da 100644 --- a/lib/ruby_indexer/test/index_test.rb +++ b/lib/ruby_indexer/test/index_test.rb @@ -2262,6 +2262,12 @@ def test_constant_name node = Prism.parse("class Foo; end").value.statements.body.first.constant_path assert_equal("Foo", Index.constant_name(node)) + node = Prism.parse("Foo = 1").value.statements.body.first + assert_equal("Foo", Index.constant_name(node)) + + node = Prism.parse("Foo, Bar = 1, 2").value.statements.body.first.lefts.first + assert_equal("Foo", Index.constant_name(node)) + node = Prism.parse(<<~RUBY).value.statements.body.first.constant_path class class Foo end diff --git a/lib/ruby_lsp/requests/prepare_rename.rb b/lib/ruby_lsp/requests/prepare_rename.rb index 8b8cc278c..11a1a935a 100644 --- a/lib/ruby_lsp/requests/prepare_rename.rb +++ b/lib/ruby_lsp/requests/prepare_rename.rb @@ -24,7 +24,13 @@ def perform node_context = RubyDocument.locate( @document.ast, char_position, - node_types: [Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode], + node_types: [ + Prism::ConstantReadNode, + Prism::ConstantPathNode, + Prism::ConstantPathTargetNode, + Prism::ConstantWriteNode, + Prism::ConstantTargetNode, + ], code_units_cache: @document.code_units_cache, ) target = node_context.node @@ -39,7 +45,8 @@ def perform ) end - range_from_location(target.location) + location = target.is_a?(Prism::ConstantWriteNode) ? target.name_loc : target.location + range_from_location(location) end end end diff --git a/lib/ruby_lsp/requests/rename.rb b/lib/ruby_lsp/requests/rename.rb index a8e827641..63ccd2d34 100644 --- a/lib/ruby_lsp/requests/rename.rb +++ b/lib/ruby_lsp/requests/rename.rb @@ -36,7 +36,13 @@ def perform node_context = RubyDocument.locate( @document.ast, char_position, - node_types: [Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode], + node_types: [ + Prism::ConstantReadNode, + Prism::ConstantPathNode, + Prism::ConstantPathTargetNode, + Prism::ConstantWriteNode, + Prism::ConstantTargetNode, + ], code_units_cache: @document.code_units_cache, ) target = node_context.node @@ -51,7 +57,7 @@ def perform ) end - target = target #: as Prism::ConstantReadNode | Prism::ConstantPathNode | Prism::ConstantPathTargetNode + target = target #: as Prism::ConstantReadNode | Prism::ConstantPathNode | Prism::ConstantPathTargetNode | Prism::ConstantWriteNode | Prism::ConstantTargetNode name = RubyIndexer::Index.constant_name(target) return unless name diff --git a/test/requests/prepare_rename_expectations_test.rb b/test/requests/prepare_rename_expectations_test.rb index ae690a209..fcaca1825 100644 --- a/test/requests/prepare_rename_expectations_test.rb +++ b/test/requests/prepare_rename_expectations_test.rb @@ -14,6 +14,35 @@ def run_expectations(source) RubyLsp::Requests::PrepareRename.new(document, position).perform end + def test_constant_definition + source = <<~RUBY + Foo = 1 + Bar, Baz = 2, 3 + RUBY + uri = URI("file:///fake.rb") + document = RubyLsp::RubyDocument.new(source: source, version: 1, uri: uri, global_state: @global_state) + + range = RubyLsp::Requests::PrepareRename.new( + document, + { line: 0, character: 1 }, + ).perform #: as !nil + + assert_equal(0, range.start.line) + assert_equal(0, range.start.character) + assert_equal(0, range.end.line) + assert_equal(3, range.end.character) + + range = RubyLsp::Requests::PrepareRename.new( + document, + { line: 1, character: 1 }, + ).perform #: as !nil + + assert_equal(1, range.start.line) + assert_equal(0, range.start.character) + assert_equal(1, range.end.line) + assert_equal(3, range.end.character) + end + private def default_position diff --git a/test/requests/rename_test.rb b/test/requests/rename_test.rb index d9bf7c01e..0d8c5f4d0 100644 --- a/test/requests/rename_test.rb +++ b/test/requests/rename_test.rb @@ -105,8 +105,65 @@ class RenameMe assert_equal("NewMe", untitled_change.edits[0].new_text) end + def test_renaming_from_constant_definition + source = <<~RUBY + Foo = 1 + Foo + RUBY + expected = <<~RUBY + Renamed = 1 + Renamed + RUBY + + assert_equal(expected, rename_source(source, { line: 0, character: 1 })) + end + + def test_renaming_from_constant_target + source = <<~RUBY + Foo, Bar = 1, 2 + Foo + Bar + RUBY + expected = <<~RUBY + Renamed, Bar = 1, 2 + Renamed + Bar + RUBY + + assert_equal(expected, rename_source(source, { line: 0, character: 1 })) + end + private + def rename_source(source, position) + source = source.dup + global_state = RubyLsp::GlobalState.new + uri = URI("untitled:constant-definition") + global_state.index.index_single(uri, source) + store = RubyLsp::Store.new(global_state) + document = store.set( + uri: uri, + source: source, + version: 1, + language_id: :ruby, + ) + + workspace_edit = RubyLsp::Requests::Rename.new( + global_state, + store, + document, + { position: position, newName: "Renamed" }, + ).perform #: as !nil + + document.push_edits( + workspace_edit.changes.fetch(uri.to_s).map do |edit| + { range: edit.range.to_hash.transform_values(&:to_hash), text: edit.new_text } + end, + version: 2, + ) + document.source + end + def expect_renames(fixture_path, new_fixture_path, expected, position, new_name) source = File.read(fixture_path) global_state = RubyLsp::GlobalState.new