diff --git a/lib/ruby_lsp/listeners/completion.rb b/lib/ruby_lsp/listeners/completion.rb index 84898bb15..ea4c71fe2 100644 --- a/lib/ruby_lsp/listeners/completion.rb +++ b/lib/ruby_lsp/listeners/completion.rb @@ -440,8 +440,11 @@ def complete_methods(node, name) return unless range guessed_type = type.is_a?(TypeInferrer::GuessedType) && type.name + external_references = @node_context.fully_qualified_name != type.name @index.method_completion_candidates(method_name, type.name).each do |entry| + next if entry.visibility != RubyIndexer::Entry::Visibility::PUBLIC && external_references + entry_name = entry.name owner_name = entry.owner&.name diff --git a/test/requests/completion_test.rb b/test/requests/completion_test.rb index 66b5e34bb..245bc850b 100644 --- a/test/requests/completion_test.rb +++ b/test/requests/completion_test.rb @@ -1497,6 +1497,78 @@ def test_guessed_type_name_is_only_included_for_guessed_types end end + def test_completion_for_private_methods + source = +<<~RUBY + class Foo + def bar + b + end + + private + + def baz + end + end + + foo = Foo.new + foo.b + RUBY + + with_server(source) do |server, uri| + server.process_message(id: 1, method: "textDocument/completion", params: { + textDocument: { uri: uri }, + position: { line: 2, character: 5 }, + }) + + result = server.pop_response.response + assert_includes(result.map(&:label), "baz") + + server.process_message(id: 1, method: "textDocument/completion", params: { + textDocument: { uri: uri }, + position: { line: 12, character: 5 }, + }) + + result = server.pop_response.response + refute_includes(result.map(&:label), "baz") + end + end + + def test_completion_for_protected_methods + source = +<<~RUBY + class Foo + def bar + b + end + + protected + + def baz + end + end + + foo = Foo.new + foo.b + RUBY + + with_server(source) do |server, uri| + server.process_message(id: 1, method: "textDocument/completion", params: { + textDocument: { uri: uri }, + position: { line: 2, character: 5 }, + }) + + result = server.pop_response.response + assert_includes(result.map(&:label), "baz") + + server.process_message(id: 1, method: "textDocument/completion", params: { + textDocument: { uri: uri }, + position: { line: 12, character: 5 }, + }) + + result = server.pop_response.response + refute_includes(result.map(&:label), "baz") + end + end + private def with_file_structure(server, &block)