@@ -139,15 +139,6 @@ def update_document_content(uri, text)
139139 # diagnostics_ready? ? updated_diagnostics_for_codefile(code_file) : []
140140 end
141141
142- # def updated_diagnostics_for_codefile(code_file)
143- # # Maybe we should be sharing this GoodCop across instances
144- # RubyLanguageServer.logger.debug("updated_diagnostics_for_codefile: #{code_file.uri}")
145- # project_relative_filename = filename_relative_to_project(code_file.uri)
146- # # code_file.diagnostics = GoodCop.instance&.diagnostics(code_file.text, project_relative_filename)
147- # RubyLanguageServer.logger.debug("code_file.diagnostics: #{code_file.diagnostics}")
148- # code_file.diagnostics
149- # end
150-
151142 # Returns the context of what is being typed in the given line
152143 def context_at_location ( uri , position )
153144 code_file = code_file_for_uri ( uri )
@@ -251,22 +242,36 @@ def scope_definitions_for(name, scope, uri)
251242
252243 # class_method_filter is for new -> initialize
253244 def project_definitions_for ( name , parent_scopes = [ ] , class_method_filter = nil )
254- # If no parent scope, just return all matches as before
255- if parent_scopes . empty?
256- all_scopes = RubyLanguageServer ::ScopeData ::Scope . where ( name : name )
257- all_scopes = all_scopes . where ( class_method : class_method_filter ) unless class_method_filter . nil?
258- all_variables = RubyLanguageServer ::ScopeData ::Variable . where ( name : name )
259- all_matches = all_scopes . to_a + all_variables . to_a
260- return all_matches . reject { |item | item . code_file . nil? } . map do |item |
261- line = item . respond_to? ( :top_line ) ? item . top_line : item . line
262- Location . hash ( item . code_file . uri , line , 1 )
263- end || [ ]
264- end
245+ # If no parent scope, search project-wide
246+ return search_project_wide ( name , class_method_filter ) if parent_scopes . empty?
265247
266- # First pass: Walk up the parent scope chain, looking ONLY for variables (closest wins)
267- # Variables in scope chain take priority over child scopes and project-wide definitions
268248 supplied_scope = parent_scopes . first
269249
250+ # First pass: Look for variables (constants) in the scope chain
251+ result = search_variables_in_scope_chain ( name , supplied_scope )
252+ return result if result . any?
253+
254+ # Second pass: Look for child scopes in the scope chain
255+ result = search_child_scopes_in_chain ( name , supplied_scope , class_method_filter )
256+ return result if result . any?
257+
258+ # Fall back to project-wide search
259+ search_project_wide ( name , class_method_filter )
260+ end
261+
262+ private
263+
264+ # Convert a collection of scopes or variables to location hashes
265+ def scope_or_variable_to_locations ( items )
266+ items . reject { |item | item . code_file . nil? } . map do |item |
267+ line = item . respond_to? ( :top_line ) ? item . top_line : item . line
268+ Location . hash ( item . code_file . uri , line , 1 )
269+ end
270+ end
271+
272+ # Search for a variable/constant up the scope chain (first pass)
273+ # Variables in scope chain take priority over child scopes
274+ def search_variables_in_scope_chain ( name , supplied_scope )
270275 current = supplied_scope
271276 while current
272277 # For constants, skip method scopes and keep searching up to class/module scopes
@@ -282,11 +287,13 @@ def project_definitions_for(name, parent_scopes = [], class_method_filter = nil)
282287 return [ Location . hash ( matching_variable . code_file . uri , line , 1 ) ]
283288 end
284289
285- # Move up to parent scope and continue searching for variables
286290 current = current . parent
287291 end
292+ [ ]
293+ end
288294
289- # Second pass: If no variables found, check child scopes in the scope chain
295+ # Search for child scopes up the scope chain (second pass)
296+ def search_child_scopes_in_chain ( name , supplied_scope , class_method_filter )
290297 current = supplied_scope
291298 while current
292299 # For constants, skip method scopes and keep searching up to class/module scopes
@@ -295,33 +302,25 @@ def project_definitions_for(name, parent_scopes = [], class_method_filter = nil)
295302 next
296303 end
297304
298- # Check child scopes (classes/modules/methods) with this name (for class/module/method lookup)
305+ # Check child scopes (classes/modules/methods) with this name
299306 child_scopes = current . children . where ( name : name )
300307 child_scopes = child_scopes . where ( class_method : class_method_filter ) unless class_method_filter . nil?
301- unless child_scopes . empty?
302- return child_scopes . reject { |item | item . code_file . nil? } . map do |item |
303- line = item . respond_to? ( :top_line ) ? item . top_line : item . line
304- Location . hash ( item . code_file . uri , line , 1 )
305- end
306- end
308+ return scope_or_variable_to_locations ( child_scopes ) if child_scopes . any?
307309
308- # Move up to parent scope and continue searching
309310 current = current . parent
310311 end
312+ [ ]
313+ end
311314
312- # If nothing found in the scope chain, fall back to project-wide search
315+ # Search for a name project-wide (fallback)
316+ def search_project_wide ( name , class_method_filter )
313317 all_scopes = RubyLanguageServer ::ScopeData ::Scope . where ( name : name )
314318 all_scopes = all_scopes . where ( class_method : class_method_filter ) unless class_method_filter . nil?
315319 all_variables = RubyLanguageServer ::ScopeData ::Variable . where ( name : name )
316320 all_matches = all_scopes . to_a + all_variables . to_a
317- all_matches . reject { |item | item . code_file . nil? } . map do |item |
318- line = item . respond_to? ( :top_line ) ? item . top_line : item . line
319- Location . hash ( item . code_file . uri , line , 1 )
320- end
321+ scope_or_variable_to_locations ( all_matches )
321322 end
322323
323- private
324-
325324 # Find a scope by its path (e.g., "Foo::Bar")
326325 # Returns nil if path is nil or empty (for root scope searches)
327326 def find_scope_by_path ( path )
0 commit comments