From 4930cfe4921099e3dd156f592b83eaf1d242f7e9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 04:08:33 +0000 Subject: [PATCH 1/5] Initial plan From a1cfb1072ec2f2bd7ece06ddc4253e73cdcdd428 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 04:11:50 +0000 Subject: [PATCH 2/5] Initial exploration of fallback lookup issue Co-authored-by: kwerle <23320+kwerle@users.noreply.github.com> --- test_fallback.rb | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 test_fallback.rb diff --git a/test_fallback.rb b/test_fallback.rb new file mode 100644 index 0000000..f9d37aa --- /dev/null +++ b/test_fallback.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require_relative 'spec/test_helper' +require 'minitest/autorun' + +describe 'Fallback behavior' do + let(:project_manager) { RubyLanguageServer::ProjectManager.new('/proj', 'file:///foo/') } + + it 'should test current behavior when nothing found in scope' do + # Define a class in one file + other_file = <<~CODE + class MyExternalClass + def external_method + end + end + CODE + + # Reference it from another file where it's not in scope + file_text = <<~CODE + module SomeModule + class SomeClass + def some_method + MyExternalClass # Exists elsewhere but not in local scope + end + end + end + CODE + + project_manager.update_document_content('other_uri', other_file) + project_manager.tags_for_uri('other_uri') # Force load + + project_manager.update_document_content('test_uri', file_text) + project_manager.tags_for_uri('test_uri') # Force load + + # Try to find MyExternalClass from test_uri + position = OpenStruct.new(line: 3, character: 6) + results = project_manager.possible_definitions('test_uri', position) + puts "Results for MyExternalClass (should find it globally): #{results.inspect}" + puts "Expected to find definition in other_uri" + end + + it 'should test when constant truly does not exist' do + file_text = <<~CODE + module SomeModule + class SomeClass + def some_method + TotallyNonExistentClass # This truly doesn't exist anywhere + end + end + end + CODE + + project_manager.update_document_content('test_uri', file_text) + project_manager.tags_for_uri('test_uri') # Force load + + # Try to find non-existent constant + position = OpenStruct.new(line: 3, character: 6) + results = project_manager.possible_definitions('test_uri', position) + puts "Results for TotallyNonExistentClass (shouldn't find anything): #{results.inspect}" + end +end From c923aeb0a615360f4b8b69db78cf22dbea3306e2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 04:13:32 +0000 Subject: [PATCH 3/5] Add global fallback when lookup fails to find anything in scope chain Co-authored-by: kwerle <23320+kwerle@users.noreply.github.com> --- lib/ruby_language_server/project_manager.rb | 11 +++ .../project_manager_spec.rb | 93 +++++++++++++++++++ test_fallback.rb | 61 ------------ 3 files changed, 104 insertions(+), 61 deletions(-) delete mode 100644 test_fallback.rb diff --git a/lib/ruby_language_server/project_manager.rb b/lib/ruby_language_server/project_manager.rb index 64924ab..0357a99 100644 --- a/lib/ruby_language_server/project_manager.rb +++ b/lib/ruby_language_server/project_manager.rb @@ -280,6 +280,17 @@ 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) + + # Also search for constants/variables globally + all_variables = RubyLanguageServer::ScopeData::Variable.where(name: name) + results.concat(all_variables.to_a) + end end # Return locations for all matching scopes and variables diff --git a/spec/lib/ruby_language_server/project_manager_spec.rb b/spec/lib/ruby_language_server/project_manager_spec.rb index cbec7c2..244aafb 100644 --- a/spec/lib/ruby_language_server/project_manager_spec.rb +++ b/spec/lib/ruby_language_server/project_manager_spec.rb @@ -467,5 +467,98 @@ 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 class with a method in one file + external_file = <<~CODE_FILE + class MyExternalClass + def my_external_method + end + end + CODE_FILE + + # Reference the method from another scope + reference_file = <<~CODE_FILE + module DifferentModule + class DifferentClass + def some_method + # Call method that's not in local scope + end + end + end + CODE_FILE + + project_manager.update_document_content('external_uri', external_file) + project_manager.tags_for_uri('external_uri') + + project_manager.update_document_content('reference_uri', reference_file) + project_manager.tags_for_uri('reference_uri') + + # Note: Testing method fallback would require context parsing + # For now, just verify constant fallback doesn't break method lookup + position = OpenStruct.new(line: 3, character: 8) + results = project_manager.possible_definitions('reference_uri', position) + assert_instance_of Array, results + 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 diff --git a/test_fallback.rb b/test_fallback.rb deleted file mode 100644 index f9d37aa..0000000 --- a/test_fallback.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -require_relative 'spec/test_helper' -require 'minitest/autorun' - -describe 'Fallback behavior' do - let(:project_manager) { RubyLanguageServer::ProjectManager.new('/proj', 'file:///foo/') } - - it 'should test current behavior when nothing found in scope' do - # Define a class in one file - other_file = <<~CODE - class MyExternalClass - def external_method - end - end - CODE - - # Reference it from another file where it's not in scope - file_text = <<~CODE - module SomeModule - class SomeClass - def some_method - MyExternalClass # Exists elsewhere but not in local scope - end - end - end - CODE - - project_manager.update_document_content('other_uri', other_file) - project_manager.tags_for_uri('other_uri') # Force load - - project_manager.update_document_content('test_uri', file_text) - project_manager.tags_for_uri('test_uri') # Force load - - # Try to find MyExternalClass from test_uri - position = OpenStruct.new(line: 3, character: 6) - results = project_manager.possible_definitions('test_uri', position) - puts "Results for MyExternalClass (should find it globally): #{results.inspect}" - puts "Expected to find definition in other_uri" - end - - it 'should test when constant truly does not exist' do - file_text = <<~CODE - module SomeModule - class SomeClass - def some_method - TotallyNonExistentClass # This truly doesn't exist anywhere - end - end - end - CODE - - project_manager.update_document_content('test_uri', file_text) - project_manager.tags_for_uri('test_uri') # Force load - - # Try to find non-existent constant - position = OpenStruct.new(line: 3, character: 6) - results = project_manager.possible_definitions('test_uri', position) - puts "Results for TotallyNonExistentClass (shouldn't find anything): #{results.inspect}" - end -end From 688fd107ac3d7eb7b788cc0540f90a9337908d2c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 04:16:35 +0000 Subject: [PATCH 4/5] Add comprehensive tests for constant, method, and variable fallback Co-authored-by: kwerle <23320+kwerle@users.noreply.github.com> --- .../project_manager_spec.rb | 77 ++++++++++++++----- 1 file changed, 59 insertions(+), 18 deletions(-) diff --git a/spec/lib/ruby_language_server/project_manager_spec.rb b/spec/lib/ruby_language_server/project_manager_spec.rb index 244aafb..78c6cae 100644 --- a/spec/lib/ruby_language_server/project_manager_spec.rb +++ b/spec/lib/ruby_language_server/project_manager_spec.rb @@ -506,36 +506,77 @@ def my_method end it 'finds methods defined elsewhere when not in local scope' do - # Define a class with a method in one file - external_file = <<~CODE_FILE - class MyExternalClass - def my_external_method + # Define a helper module with utility methods + helper_file = <<~CODE_FILE + module Helpers + def self.format_text(text) + text.upcase end end CODE_FILE - # Reference the method from another scope - reference_file = <<~CODE_FILE - module DifferentModule - class DifferentClass - def some_method - # Call method that's not in local scope + # 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('external_uri', external_file) - project_manager.tags_for_uri('external_uri') + project_manager.update_document_content('helper_uri', helper_file) + project_manager.tags_for_uri('helper_uri') - project_manager.update_document_content('reference_uri', reference_file) - project_manager.tags_for_uri('reference_uri') + project_manager.update_document_content('usage_uri', usage_file) + project_manager.tags_for_uri('usage_uri') - # Note: Testing method fallback would require context parsing - # For now, just verify constant fallback doesn't break method lookup + # Position on "format_text" method call (line 3, character 8) position = OpenStruct.new(line: 3, character: 8) - results = project_manager.possible_definitions('reference_uri', position) - assert_instance_of Array, results + 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 'finds constants/variables defined elsewhere when not in local scope' do + # Define constants in a class + constants_file = <<~CODE_FILE + class AppConfig + DATABASE_URL = 'postgres://localhost/db' + MAX_RETRIES = 3 + end + CODE_FILE + + # Reference from another module + usage_file = <<~CODE_FILE + module Application + class Config + def load_config + DATABASE_URL + end + end + end + CODE_FILE + + project_manager.update_document_content('constants_uri', constants_file) + project_manager.tags_for_uri('constants_uri') + + project_manager.update_document_content('usage_uri', usage_file) + project_manager.tags_for_uri('usage_uri') + + # Position on "DATABASE_URL" (line 3, character 8) + position = OpenStruct.new(line: 3, character: 8) + results = project_manager.possible_definitions('usage_uri', position) + + # Should find the constant via global fallback + assert_equal 1, results.length + assert_equal 'constants_uri', results.first[:uri] + assert_equal 1, results.first[:range][:start][:line] end it 'returns empty array when nothing exists anywhere' do From 7bc7dac1254cbef0f637ed5472fb1cb345151d44 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 04:22:03 +0000 Subject: [PATCH 5/5] Remove variable search from global fallback as requested Co-authored-by: kwerle <23320+kwerle@users.noreply.github.com> --- lib/ruby_language_server/project_manager.rb | 4 --- .../project_manager_spec.rb | 36 ------------------- 2 files changed, 40 deletions(-) diff --git a/lib/ruby_language_server/project_manager.rb b/lib/ruby_language_server/project_manager.rb index 0357a99..e4bcc33 100644 --- a/lib/ruby_language_server/project_manager.rb +++ b/lib/ruby_language_server/project_manager.rb @@ -286,10 +286,6 @@ def project_definitions_for(name, parent_scopes = [], class_method_filter = nil) 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) - - # Also search for constants/variables globally - all_variables = RubyLanguageServer::ScopeData::Variable.where(name: name) - results.concat(all_variables.to_a) end end diff --git a/spec/lib/ruby_language_server/project_manager_spec.rb b/spec/lib/ruby_language_server/project_manager_spec.rb index 78c6cae..2ea9514 100644 --- a/spec/lib/ruby_language_server/project_manager_spec.rb +++ b/spec/lib/ruby_language_server/project_manager_spec.rb @@ -543,42 +543,6 @@ def process assert_equal 1, results.first[:range][:start][:line] end - it 'finds constants/variables defined elsewhere when not in local scope' do - # Define constants in a class - constants_file = <<~CODE_FILE - class AppConfig - DATABASE_URL = 'postgres://localhost/db' - MAX_RETRIES = 3 - end - CODE_FILE - - # Reference from another module - usage_file = <<~CODE_FILE - module Application - class Config - def load_config - DATABASE_URL - end - end - end - CODE_FILE - - project_manager.update_document_content('constants_uri', constants_file) - project_manager.tags_for_uri('constants_uri') - - project_manager.update_document_content('usage_uri', usage_file) - project_manager.tags_for_uri('usage_uri') - - # Position on "DATABASE_URL" (line 3, character 8) - position = OpenStruct.new(line: 3, character: 8) - results = project_manager.possible_definitions('usage_uri', position) - - # Should find the constant via global fallback - assert_equal 1, results.length - assert_equal 'constants_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