@@ -162,16 +162,26 @@ def possible_definitions(uri, position)
162162 name = word_at_location ( uri , position )
163163 return { } if name . blank?
164164
165+ # Special case: "new" should find "initialize" (instance method, not class method)
165166 name = 'initialize' if name == 'new'
167+
166168 context = context_at_location ( uri , position )
167169
168- # If context has more than one element and the receiver is not a constant (class),
169- # it's an instance method call (e.g., foo.bar, not Foo.bar)
170- # In that case, skip scope chain search and go straight to project definitions
171- if context && context . length > 1 && context . first && context . first [ 0 ] =~ /[a-z_@]/ # rubocop:disable Style/IfUnlessModifier
172- return project_definitions_for ( name )
170+ # If context has more than one element it's a method call on a receiver
171+ if context . length > 1
172+ receiver = context . first
173+ # Determine if it's a class method call (Foo.method) or instance method call (foo.method)
174+ if likely_class_name? ( receiver )
175+ # Class method call or MyClass.new (which finds initialize as instance method)
176+ # initialize is weird because it's defined as an instance method but called on the class.
177+ return project_definitions_for ( name , name == 'initialize' ? false : true )
178+ else
179+ # Instance method call (e.g., foo.bar, @foo.bar, FOO.bar)
180+ return project_definitions_for ( name , false )
181+ end
173182 end
174183
184+ # No receiver - search in scope chain first, then project-wide
175185 scope = scopes_at ( uri , position ) . first
176186 results = scope_definitions_for ( name , scope , uri )
177187 return results unless results . empty?
@@ -194,8 +204,14 @@ def scope_definitions_for(name, scope, uri)
194204 return_array . uniq
195205 end
196206
197- def project_definitions_for ( name )
207+ def project_definitions_for ( name , class_method_filter = nil )
198208 scopes = RubyLanguageServer ::ScopeData ::Scope . where ( name :)
209+
210+ # Filter by class_method attribute if specified
211+ unless class_method_filter . nil?
212+ scopes = scopes . where ( class_method : class_method_filter )
213+ end
214+
199215 variables = RubyLanguageServer ::ScopeData ::Variable . constant_variables . where ( name :)
200216 ( scopes + variables ) . reject { |scope | scope . code_file . nil? } . map do |scope |
201217 Location . hash ( scope . code_file . uri , scope . top_line , 1 )
@@ -204,6 +220,18 @@ def project_definitions_for(name)
204220
205221 private
206222
223+ # Guess if a receiver name is likely a class name based on idiomatic Ruby conventions.
224+ # This is a heuristic and not 100% accurate (e.g., FOO could be a constant holding an instance).
225+ # Returns true for names like "Foo" or "FooBar" (starts with uppercase, has lowercase letters).
226+ # Returns false for "FOO" (all uppercase constant) or "foo" (variable/method).
227+ def likely_class_name? ( name )
228+ # Must start with uppercase letter
229+ return false unless name [ 0 ] =~ /[A-Z]/
230+
231+ # Must contain at least one lowercase letter to distinguish from constants like FOO
232+ name =~ /[a-z]/
233+ end
234+
207235 def code_file_for_uri ( uri )
208236 CodeFile . find_by_uri ( uri ) || CodeFile . build ( uri , nil )
209237 end
0 commit comments