Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/ruby_language_server/project_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,13 @@ def project_definitions_for(name, parent_scopes = [], class_method_filter = nil)
# Move up to parent scope
current_scope = current_scope.parent
end

# Fallback: if nothing found in scope chain, search globally
if results.empty?
all_scopes = RubyLanguageServer::ScopeData::Scope.where(name: name)
all_scopes = all_scopes.where(class_method: class_method_filter) unless class_method_filter.nil?
results.concat(all_scopes.to_a)
end
end

# Return locations for all matching scopes and variables
Expand Down
98 changes: 98 additions & 0 deletions spec/lib/ruby_language_server/project_manager_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -467,5 +467,103 @@ class Baz
assert_equal 0, results.first[:range][:start][:line]
end
end

describe 'global fallback when not found in scope' do
it 'finds constants defined elsewhere when not in local scope' do
# Define a class in one file
external_file = <<~CODE_FILE
class ExternalClass
def external_method
end
end
CODE_FILE

# Reference it from another file in a different scope
reference_file = <<~CODE_FILE
module MyModule
class MyClass
def my_method
ExternalClass # Not in local scope but exists globally
end
end
end
CODE_FILE

project_manager.update_document_content('external_uri', external_file)
project_manager.tags_for_uri('external_uri') # Force load

project_manager.update_document_content('reference_uri', reference_file)
project_manager.tags_for_uri('reference_uri') # Force load

# Position on "ExternalClass" (line 3, character 8)
position = OpenStruct.new(line: 3, character: 8)
results = project_manager.possible_definitions('reference_uri', position)

# Should find the class via global fallback
assert_equal 1, results.length
assert_equal 'external_uri', results.first[:uri]
assert_equal 0, results.first[:range][:start][:line]
end

it 'finds methods defined elsewhere when not in local scope' do
# Define a helper module with utility methods
helper_file = <<~CODE_FILE
module Helpers
def self.format_text(text)
text.upcase
end
end
CODE_FILE

# Use the method from a different module
usage_file = <<~CODE_FILE
module Application
class TextService
def process
format_text
end
end
end
CODE_FILE

project_manager.update_document_content('helper_uri', helper_file)
project_manager.tags_for_uri('helper_uri')

project_manager.update_document_content('usage_uri', usage_file)
project_manager.tags_for_uri('usage_uri')

# Position on "format_text" method call (line 3, character 8)
position = OpenStruct.new(line: 3, character: 8)
results = project_manager.possible_definitions('usage_uri', position)

# Should find the method definition via global fallback
# Note: This finds the class method Helpers.format_text
assert_equal 1, results.length
assert_equal 'helper_uri', results.first[:uri]
assert_equal 1, results.first[:range][:start][:line]
end

it 'returns empty array when nothing exists anywhere' do
reference_file = <<~CODE_FILE
module MyModule
class MyClass
def my_method
CompletelyNonExistentClass
end
end
end
CODE_FILE

project_manager.update_document_content('reference_uri', reference_file)
project_manager.tags_for_uri('reference_uri')

# Position on "CompletelyNonExistentClass"
position = OpenStruct.new(line: 3, character: 8)
results = project_manager.possible_definitions('reference_uri', position)

# Should return empty array as nothing exists
assert_equal [], results
end
end
end
end