@@ -22,6 +22,7 @@ def provider
2222 def initialize ( global_state , store , document , params )
2323 super ( )
2424 @global_state = global_state
25+ @graph = global_state . graph #: Rubydex::Graph
2526 @store = store
2627 @document = document
2728 @position = params [ :position ] #: Hash[Symbol, Integer]
@@ -56,17 +57,14 @@ def perform
5657 name = RubyIndexer ::Index . constant_name ( target )
5758 return unless name
5859
59- entries = @global_state . index . resolve ( name , node_context . nesting )
60- return unless entries
60+ declaration = @graph . resolve_constant ( name , node_context . nesting )
61+ return unless declaration
6162
62- if ( conflict_entries = @global_state . index . resolve ( @new_name , node_context . nesting ) )
63- raise InvalidNameError , "The new name is already in use by #{ conflict_entries . first & .name } "
63+ if ( conflict = @graph . resolve_constant ( @new_name , node_context . nesting ) )
64+ raise InvalidNameError , "The new name is already in use by #{ conflict . name } "
6465 end
6566
66- fully_qualified_name = entries . first #: as !nil
67- . name
68- reference_target = RubyIndexer ::ReferenceFinder ::ConstTarget . new ( fully_qualified_name )
69- changes = collect_text_edits ( reference_target , name )
67+ changes = collect_text_edits ( declaration , name )
7068
7169 # If the client doesn't support resource operations, such as renaming files, then we can only return the basic
7270 # text changes
@@ -78,12 +76,12 @@ def perform
7876 # renamed and then the URI associated to the text edit no longer exists, causing it to be dropped
7977 document_changes = changes . map do |uri , edits |
8078 Interface ::TextDocumentEdit . new (
81- text_document : Interface ::VersionedTextDocumentIdentifier . new ( uri : uri , version : nil ) ,
79+ text_document : Interface ::OptionalVersionedTextDocumentIdentifier . new ( uri : uri , version : nil ) ,
8280 edits : edits ,
8381 )
8482 end
8583
86- collect_file_renames ( fully_qualified_name , document_changes )
84+ collect_file_renames ( declaration . name , document_changes )
8785 Interface ::WorkspaceEdit . new ( document_changes : document_changes )
8886 end
8987
@@ -97,80 +95,76 @@ def collect_file_renames(fully_qualified_name, document_changes)
9795 # We also look for an associated test file and rename it too
9896 short_name = fully_qualified_name . split ( "::" ) . last #: as !nil
9997
100- @global_state . index [ fully_qualified_name ] &.each do |entry |
98+ declaration = @graph [ fully_qualified_name ]
99+ return unless declaration
100+
101+ unless [
102+ Rubydex ::Class ,
103+ Rubydex ::Module ,
104+ Rubydex ::Constant ,
105+ Rubydex ::ConstantAlias ,
106+ ] . any? { |type | declaration . is_a? ( type ) }
107+ return
108+ end
109+
110+ declaration . definitions . each do |definition |
101111 # Do not rename files that are not part of the workspace
102- uri = entry . uri
112+ uri = URI ( definition . location . uri )
103113 file_path = uri . full_path
104114 next unless file_path &.start_with? ( @global_state . workspace_path )
105115
106- case entry
107- when RubyIndexer ::Entry ::Class , RubyIndexer ::Entry ::Module , RubyIndexer ::Entry ::Constant ,
108- RubyIndexer ::Entry ::ConstantAlias , RubyIndexer ::Entry ::UnresolvedConstantAlias
109-
110- file_name = file_from_constant_name ( short_name )
116+ file_name = file_from_constant_name ( short_name )
117+ next unless "#{ file_name } .rb" == File . basename ( file_path )
111118
112- if "#{ file_name } .rb" == entry . file_name
113- new_file_name = file_from_constant_name (
114- @new_name . split ( "::" ) . last , #: as !nil
115- )
119+ new_file_name = file_from_constant_name (
120+ @new_name . split ( "::" ) . last , #: as !nil
121+ )
116122
117- new_uri = URI ::Generic . from_path ( path : File . join (
118- File . dirname ( file_path ) ,
119- "#{ new_file_name } .rb" ,
120- ) ) . to_s
123+ new_uri = URI ::Generic . from_path ( path : File . join (
124+ File . dirname ( file_path ) ,
125+ "#{ new_file_name } .rb" ,
126+ ) ) . to_s
121127
122- document_changes << Interface ::RenameFile . new ( kind : "rename" , old_uri : uri . to_s , new_uri : new_uri )
123- end
124- end
128+ document_changes << Interface ::RenameFile . new ( kind : "rename" , old_uri : uri . to_s , new_uri : new_uri )
125129 end
126130 end
127131
128- #: (RubyIndexer::ReferenceFinder::Target target, String name) -> Hash[String, Array[Interface::TextEdit]]
129- def collect_text_edits ( target , name )
130- changes = { }
131-
132- Dir . glob ( File . join ( @global_state . workspace_path , "**/*.rb" ) ) . each do |path |
133- uri = URI ::Generic . from_path ( path : path )
134- # If the document is being managed by the client, then we should use whatever is present in the store instead
135- # of reading from disk
136- next if @store . key? ( uri )
137-
138- parse_result = Prism . parse_file ( path )
139- edits = collect_changes ( target , parse_result . value , name , uri )
140- changes [ uri . to_s ] = edits unless edits . empty?
141- rescue Errno ::EISDIR , Errno ::ENOENT
142- # If `path` is a directory, just ignore it and continue. If the file doesn't exist, then we also ignore it.
143- end
144-
145- @store . each do |uri , document |
146- next unless document . is_a? ( RubyDocument ) || document . is_a? ( ERBDocument )
132+ #: (Rubydex::Declaration declaration, String name) -> Hash[String, Array[Interface::TextEdit]]
133+ def collect_text_edits ( declaration , name )
134+ changes = { } #: Hash[String, Array[Interface::TextEdit]]
135+ short_name = name . split ( "::" ) . last #: as !nil
136+ new_short_name = @new_name . split ( "::" ) . last #: as !nil
137+
138+ # Collect edits for definition sites (where the constant is declared)
139+ declaration . definitions . each do |definition |
140+ name_loc = definition . name_location
141+ next unless name_loc
142+
143+ uri_string = name_loc . uri
144+ edits = ( changes [ uri_string ] ||= [ ] )
145+
146+ # The name_location spans the constant name as written in the definition.
147+ # We only replace the unqualified name portion (the last segment).
148+ range = Interface ::Range . new (
149+ start : Interface ::Position . new (
150+ line : name_loc . end_line ,
151+ character : name_loc . end_column - short_name . length ,
152+ ) ,
153+ end : Interface ::Position . new ( line : name_loc . end_line , character : name_loc . end_column ) ,
154+ )
147155
148- edits = collect_changes ( target , document . ast , name , document . uri )
149- changes [ uri ] = edits unless edits . empty?
156+ edits << Interface ::TextEdit . new ( range : range , new_text : new_short_name )
150157 end
151158
152- changes
153- end
154-
155- #: (RubyIndexer::ReferenceFinder::Target target, Prism::Node ast, String name, URI::Generic uri) -> Array[Interface::TextEdit]
156- def collect_changes ( target , ast , name , uri )
157- dispatcher = Prism ::Dispatcher . new
158- finder = RubyIndexer ::ReferenceFinder . new ( target , @global_state . index , dispatcher , uri )
159- dispatcher . visit ( ast )
160-
161- finder . references . map do |reference |
162- adjust_reference_for_edit ( name , reference )
159+ # Collect edits for reference sites (where the constant is used)
160+ declaration . references . each do |reference |
161+ ref = reference #: as Rubydex::ConstantReference
162+ uri_string = ref . location . uri
163+ edits = ( changes [ uri_string ] ||= [ ] )
164+ edits << Interface ::TextEdit . new ( range : ref . to_lsp_range , new_text : new_short_name )
163165 end
164- end
165-
166- #: (String name, RubyIndexer::ReferenceFinder::Reference reference) -> Interface::TextEdit
167- def adjust_reference_for_edit ( name , reference )
168- # The reference may include a namespace in front. We need to check if the rename new name includes namespaces
169- # and then adjust both the text and the location to produce the correct edit
170- location = reference . location
171- new_text = reference . name . sub ( name , @new_name )
172166
173- Interface :: TextEdit . new ( range : range_from_location ( location ) , new_text : new_text )
167+ changes
174168 end
175169
176170 #: (String constant_name) -> String
0 commit comments