type-guessr 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -0
  3. data/exe/type-guessr +30 -0
  4. data/lib/ruby_lsp/type_guessr/addon.rb +20 -45
  5. data/lib/ruby_lsp/type_guessr/code_index_adapter.rb +352 -0
  6. data/lib/ruby_lsp/type_guessr/constants.rb +39 -0
  7. data/lib/ruby_lsp/type_guessr/{graph_builder.rb → debug_graph_builder.rb} +27 -22
  8. data/lib/ruby_lsp/type_guessr/debug_server.rb +20 -17
  9. data/lib/ruby_lsp/type_guessr/dsl/activerecord_adapter.rb +404 -0
  10. data/lib/ruby_lsp/type_guessr/dsl/ar_schema_watcher.rb +96 -0
  11. data/lib/ruby_lsp/type_guessr/dsl/ar_type_mapper.rb +51 -0
  12. data/lib/ruby_lsp/type_guessr/dsl.rb +3 -0
  13. data/lib/ruby_lsp/type_guessr/dsl_type_registrar.rb +60 -0
  14. data/lib/ruby_lsp/type_guessr/hover.rb +129 -261
  15. data/lib/ruby_lsp/type_guessr/rails_server_addon.rb +83 -0
  16. data/lib/ruby_lsp/type_guessr/runtime_adapter.rb +613 -277
  17. data/lib/ruby_lsp/type_guessr/type_inferrer.rb +8 -105
  18. data/lib/type-guessr.rb +3 -11
  19. data/lib/type_guessr/core/cache/gem_dependency_resolver.rb +113 -0
  20. data/lib/type_guessr/core/cache/gem_signature_cache.rb +98 -0
  21. data/lib/type_guessr/core/cache/gem_signature_extractor.rb +87 -0
  22. data/lib/type_guessr/core/cache.rb +5 -0
  23. data/lib/{ruby_lsp/type_guessr → type_guessr/core}/config.rb +19 -34
  24. data/lib/type_guessr/core/converter/call_converter.rb +161 -0
  25. data/lib/type_guessr/core/converter/container_mutation_converter.rb +241 -0
  26. data/lib/type_guessr/core/converter/context.rb +144 -0
  27. data/lib/type_guessr/core/converter/control_flow_converter.rb +425 -0
  28. data/lib/type_guessr/core/converter/definition_converter.rb +246 -0
  29. data/lib/type_guessr/core/converter/literal_converter.rb +217 -0
  30. data/lib/type_guessr/core/converter/prism_converter.rb +154 -1613
  31. data/lib/type_guessr/core/converter/rbs_converter.rb +35 -14
  32. data/lib/type_guessr/core/converter/registration.rb +100 -0
  33. data/lib/type_guessr/core/converter/variable_converter.rb +225 -0
  34. data/lib/type_guessr/core/converter.rb +4 -0
  35. data/lib/type_guessr/core/index/location_index.rb +32 -0
  36. data/lib/type_guessr/core/index.rb +3 -0
  37. data/lib/type_guessr/core/inference/resolver.rb +516 -349
  38. data/lib/type_guessr/core/inference.rb +4 -0
  39. data/lib/type_guessr/core/ir/nodes.rb +362 -103
  40. data/lib/type_guessr/core/ir.rb +3 -0
  41. data/lib/type_guessr/core/logger.rb +6 -13
  42. data/lib/type_guessr/core/node_context_helper.rb +126 -0
  43. data/lib/type_guessr/core/node_key_generator.rb +31 -0
  44. data/lib/type_guessr/core/registry/class_variable_registry.rb +63 -0
  45. data/lib/type_guessr/core/registry/instance_variable_registry.rb +84 -0
  46. data/lib/type_guessr/core/registry/method_registry.rb +65 -38
  47. data/lib/type_guessr/core/registry/signature_registry.rb +543 -0
  48. data/lib/type_guessr/core/registry.rb +6 -0
  49. data/lib/type_guessr/core/signature_builder.rb +39 -0
  50. data/lib/type_guessr/core/type_serializer.rb +96 -0
  51. data/lib/type_guessr/core/type_simplifier.rb +15 -12
  52. data/lib/type_guessr/core/types.rb +250 -32
  53. data/lib/type_guessr/core.rb +29 -0
  54. data/lib/type_guessr/mcp/file_watcher.rb +87 -0
  55. data/lib/type_guessr/mcp/server.rb +463 -0
  56. data/lib/type_guessr/mcp/standalone_runtime.rb +213 -0
  57. data/lib/type_guessr/version.rb +1 -1
  58. metadata +57 -8
  59. data/lib/type_guessr/core/rbs_provider.rb +0 -304
  60. data/lib/type_guessr/core/registry/variable_registry.rb +0 -87
  61. data/lib/type_guessr/core/signature_provider.rb +0 -101
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ir/nodes"
@@ -1,18 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../ruby_lsp/type_guessr/config"
3
+ require_relative "config"
4
4
 
5
5
  module TypeGuessr
6
6
  module Core
7
7
  # Unified logging interface for TypeGuessr
8
8
  # Uses Config.debug? to control output
9
9
  module Logger
10
- module_function
11
-
12
- # Log debug message with optional context
13
- # @param msg [String] the debug message
14
- # @param context [Hash] optional context information
15
- def debug(msg, context = {})
10
+ module_function def debug(msg, context = {})
16
11
  return unless debug_enabled?
17
12
 
18
13
  output = "[TypeGuessr:DEBUG] #{msg}"
@@ -23,20 +18,18 @@ module TypeGuessr
23
18
  # Log error message with optional exception
24
19
  # @param msg [String] the error message
25
20
  # @param exception [Exception, nil] optional exception for backtrace
26
- def error(msg, exception = nil)
27
- return unless debug_enabled?
28
-
21
+ module_function def error(msg, exception = nil)
29
22
  warn "[TypeGuessr:ERROR] #{msg}"
30
23
  return unless exception
31
24
 
32
25
  warn " #{exception.class}: #{exception.message}"
33
- warn exception.backtrace.first(5).map { |l| " #{l}" }.join("\n")
26
+ warn exception.backtrace&.first(5)&.map { |l| " #{l}" }&.join("\n")
34
27
  end
35
28
 
36
29
  # Check if debug mode is enabled
37
30
  # @return [Boolean] true if Config.debug? returns true
38
- def debug_enabled?
39
- RubyLsp::TypeGuessr::Config.debug?
31
+ module_function def debug_enabled?
32
+ TypeGuessr::Core::Config.debug?
40
33
  end
41
34
  end
42
35
  end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "node_key_generator"
4
+
5
+ module TypeGuessr
6
+ module Core
7
+ # Helper module for generating scope IDs and node hashes from Prism nodes and NodeContext.
8
+ # Extracted from Hover and TypeInferrer to eliminate code duplication.
9
+ #
10
+ # This module provides framework-agnostic utilities that bridge between ruby-lsp's
11
+ # NodeContext and TypeGuessr's IR node key format.
12
+ module NodeContextHelper
13
+ module_function def generate_scope_id(node_context, exclude_method: false)
14
+ class_path = node_context.nesting.map do |n|
15
+ n.is_a?(String) ? n : n.name.to_s
16
+ end.join("::")
17
+
18
+ method_name = exclude_method ? nil : node_context.surrounding_method
19
+
20
+ if method_name
21
+ "#{class_path}##{method_name}"
22
+ else
23
+ class_path
24
+ end
25
+ end
26
+
27
+ # Generate node_hash from Prism node to match IR node_hash format
28
+ # @param node [Prism::Node] The Prism node
29
+ # @param node_context [RubyLsp::NodeContext] The context (for block param detection and nesting)
30
+ # @return [String, nil] The node hash or nil for unsupported node types
31
+ module_function def generate_node_hash(node, node_context)
32
+ offset = node.location.start_offset
33
+ case node
34
+ when Prism::LocalVariableWriteNode, Prism::LocalVariableTargetNode
35
+ NodeKeyGenerator.local_write(node.name, offset)
36
+ when Prism::LocalVariableReadNode
37
+ NodeKeyGenerator.local_read(node.name, offset)
38
+ when Prism::InstanceVariableWriteNode, Prism::InstanceVariableTargetNode
39
+ NodeKeyGenerator.ivar_write(node.name, offset)
40
+ when Prism::InstanceVariableReadNode
41
+ NodeKeyGenerator.ivar_read(node.name, offset)
42
+ when Prism::ClassVariableWriteNode, Prism::ClassVariableTargetNode
43
+ NodeKeyGenerator.cvar_write(node.name, offset)
44
+ when Prism::ClassVariableReadNode
45
+ NodeKeyGenerator.cvar_read(node.name, offset)
46
+ when Prism::GlobalVariableWriteNode, Prism::GlobalVariableTargetNode
47
+ NodeKeyGenerator.global_write(node.name, offset)
48
+ when Prism::GlobalVariableReadNode
49
+ NodeKeyGenerator.global_read(node.name, offset)
50
+ when Prism::RequiredParameterNode, Prism::OptionalParameterNode, Prism::RestParameterNode,
51
+ Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode,
52
+ Prism::KeywordRestParameterNode, Prism::BlockParameterNode
53
+ # Check if this is a block parameter (parent is BlockParametersNode)
54
+ if block_parameter?(node, node_context)
55
+ index = block_parameter_index(node, node_context)
56
+ NodeKeyGenerator.bparam(index, offset)
57
+ else
58
+ NodeKeyGenerator.param(node.name, offset)
59
+ end
60
+ when Prism::ForwardingParameterNode
61
+ NodeKeyGenerator.param(:"...", offset)
62
+ when Prism::CallNode
63
+ # Use message_loc for accurate offset
64
+ call_offset = node.message_loc&.start_offset || offset
65
+ NodeKeyGenerator.call(node.name, call_offset)
66
+ when Prism::DefNode
67
+ # Use name_loc for accurate offset
68
+ def_offset = node.name_loc&.start_offset || offset
69
+ NodeKeyGenerator.def_node(node.name, def_offset)
70
+ when Prism::SelfNode
71
+ class_path = node_context.nesting.map do |n|
72
+ n.is_a?(String) ? n : n.name.to_s
73
+ end.join("::")
74
+ NodeKeyGenerator.self_node(class_path, offset)
75
+ end
76
+ end
77
+
78
+ # Check if a parameter node is inside a block (not a method definition)
79
+ # @param node [Prism::Node] The parameter node
80
+ # @param node_context [RubyLsp::NodeContext] The context
81
+ # @return [Boolean] true if inside a block
82
+ module_function def block_parameter?(node, node_context)
83
+ call_node = node_context.call_node
84
+ return false unless call_node&.block
85
+
86
+ # Check if this parameter is in the block's parameters
87
+ block_params = call_node.block.parameters&.parameters
88
+ return false unless block_params
89
+
90
+ all_params = collect_block_params(block_params)
91
+ all_params.include?(node)
92
+ end
93
+
94
+ # Get the index of a block parameter
95
+ # @param node [Prism::Node] The parameter node
96
+ # @param node_context [RubyLsp::NodeContext] The context
97
+ # @return [Integer] The parameter index
98
+ module_function def block_parameter_index(node, node_context)
99
+ call_node = node_context.call_node
100
+ return 0 unless call_node&.block
101
+
102
+ block_params = call_node.block.parameters&.parameters
103
+ return 0 unless block_params
104
+
105
+ all_params = collect_block_params(block_params)
106
+ all_params.index(node) || 0
107
+ end
108
+
109
+ # Collect all parameters from block parameters node
110
+ # Uses respond_to? guards for safety across different Prism versions
111
+ # @param block_params [Prism::ParametersNode] The block parameters
112
+ # @return [Array<Prism::Node>] All parameter nodes
113
+ module_function def collect_block_params(block_params)
114
+ params = []
115
+ params.concat(block_params.requireds) if block_params.respond_to?(:requireds)
116
+ params.concat(block_params.optionals) if block_params.respond_to?(:optionals)
117
+ params << block_params.rest if block_params.respond_to?(:rest) && block_params.rest
118
+ params.concat(block_params.posts) if block_params.respond_to?(:posts)
119
+ params.concat(block_params.keywords) if block_params.respond_to?(:keywords)
120
+ params << block_params.keyword_rest if block_params.respond_to?(:keyword_rest) && block_params.keyword_rest
121
+ params << block_params.block if block_params.respond_to?(:block) && block_params.block
122
+ params.compact
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypeGuessr
4
+ module Core
5
+ # Generates unique node keys for IR nodes and Prism node lookups.
6
+ # Single source of truth for node key format to ensure consistency
7
+ # between IR node generation and hover/type inference lookups.
8
+ module NodeKeyGenerator
9
+ module_function def local_write(name, offset) = "local_write:#{name}:#{offset}"
10
+ module_function def local_read(name, offset) = "local_read:#{name}:#{offset}"
11
+ module_function def ivar_write(name, offset) = "ivar_write:#{name}:#{offset}"
12
+ module_function def ivar_read(name, offset) = "ivar_read:#{name}:#{offset}"
13
+ module_function def cvar_write(name, offset) = "cvar_write:#{name}:#{offset}"
14
+ module_function def cvar_read(name, offset) = "cvar_read:#{name}:#{offset}"
15
+ module_function def global_write(name, offset) = "global_write:#{name}:#{offset}"
16
+ module_function def global_read(name, offset) = "global_read:#{name}:#{offset}"
17
+ module_function def param(name, offset) = "param:#{name}:#{offset}"
18
+ module_function def bparam(index, offset) = "bparam:#{index}:#{offset}"
19
+ module_function def call(method, offset) = "call:#{method}:#{offset}"
20
+ module_function def def_node(name, offset) = "def:#{name}:#{offset}"
21
+ module_function def self_node(class_name, offset) = "self:#{class_name}:#{offset}"
22
+ module_function def return_node(offset) = "return:#{offset}"
23
+ module_function def merge(offset) = "merge:#{offset}"
24
+ module_function def or_node(offset) = "or:#{offset}"
25
+ module_function def literal(type_name, offset) = "lit:#{type_name}:#{offset}"
26
+ module_function def constant(name, offset) = "const:#{name}:#{offset}"
27
+ module_function def narrow(kind, offset) = "narrow:#{kind}:#{offset}"
28
+ module_function def class_module(name, offset) = "class:#{name}:#{offset}"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypeGuessr
4
+ module Core
5
+ module Registry
6
+ # Stores and retrieves class variable write nodes
7
+ # No inheritance chain traversal (class variables are class-scoped)
8
+ class ClassVariableRegistry
9
+ def initialize
10
+ @variables = {} # { "ClassName" => { :@@name => WriteNode } }
11
+ @file_entries = {} # { file_path => [[class_name, name], ...] }
12
+ end
13
+
14
+ # Register a class variable write
15
+ # @param class_name [String] Class name
16
+ # @param name [Symbol] Variable name (e.g., :@@count)
17
+ # @param write_node [IR::ClassVariableWriteNode]
18
+ # @param file_path [String, nil] Source file path for tracking
19
+ def register(class_name, name, write_node, file_path: nil)
20
+ return unless class_name
21
+
22
+ @variables[class_name] ||= {}
23
+ return if @variables[class_name].key?(name) # first write wins
24
+
25
+ @variables[class_name][name] = write_node
26
+
27
+ return unless file_path
28
+
29
+ @file_entries[file_path] ||= []
30
+ @file_entries[file_path] << [class_name, name]
31
+ end
32
+
33
+ # Remove all entries registered from a specific file
34
+ # @param file_path [String] Source file path
35
+ def remove_file(file_path)
36
+ entries = @file_entries.delete(file_path)
37
+ return unless entries
38
+
39
+ entries.each do |class_name, name|
40
+ @variables[class_name]&.delete(name)
41
+ @variables.delete(class_name) if @variables[class_name] && @variables[class_name].empty?
42
+ end
43
+ end
44
+
45
+ # Look up a class variable write
46
+ # @param class_name [String]
47
+ # @param name [Symbol]
48
+ # @return [IR::ClassVariableWriteNode, nil]
49
+ def lookup(class_name, name)
50
+ return nil unless class_name
51
+
52
+ @variables.dig(class_name, name)
53
+ end
54
+
55
+ # Clear all registered variables
56
+ def clear
57
+ @variables.clear
58
+ @file_entries.clear
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypeGuessr
4
+ module Core
5
+ module Registry
6
+ # Stores and retrieves instance variable write nodes
7
+ # Supports inheritance chain traversal when code_index is set
8
+ class InstanceVariableRegistry
9
+ # Adapter for getting class ancestors (must respond to #ancestors_of)
10
+ # @return [#ancestors_of, nil] Adapter that returns array of ancestor names
11
+ attr_accessor :code_index
12
+
13
+ # @param code_index [#ancestors_of, nil] Adapter for inheritance lookup
14
+ def initialize(code_index: nil)
15
+ @variables = {} # { "ClassName" => { :@name => WriteNode } }
16
+ @file_entries = {} # { file_path => [[class_name, name], ...] }
17
+ @code_index = code_index
18
+ end
19
+
20
+ # Register an instance variable write
21
+ # @param class_name [String] Class name
22
+ # @param name [Symbol] Variable name (e.g., :@recipe)
23
+ # @param write_node [IR::InstanceVariableWriteNode]
24
+ # @param file_path [String, nil] Source file path for tracking
25
+ def register(class_name, name, write_node, file_path: nil)
26
+ return unless class_name
27
+
28
+ @variables[class_name] ||= {}
29
+ return if @variables[class_name].key?(name) # first write wins
30
+
31
+ @variables[class_name][name] = write_node
32
+
33
+ return unless file_path
34
+
35
+ @file_entries[file_path] ||= []
36
+ @file_entries[file_path] << [class_name, name]
37
+ end
38
+
39
+ # Remove all entries registered from a specific file
40
+ # @param file_path [String] Source file path
41
+ def remove_file(file_path)
42
+ entries = @file_entries.delete(file_path)
43
+ return unless entries
44
+
45
+ entries.each do |class_name, name|
46
+ @variables[class_name]&.delete(name)
47
+ @variables.delete(class_name) if @variables[class_name] && @variables[class_name].empty?
48
+ end
49
+ end
50
+
51
+ # Look up an instance variable write (with inheritance chain traversal)
52
+ # @param class_name [String]
53
+ # @param name [Symbol]
54
+ # @return [IR::InstanceVariableWriteNode, nil]
55
+ def lookup(class_name, name)
56
+ return nil unless class_name
57
+
58
+ # Try current class first
59
+ result = @variables.dig(class_name, name)
60
+ return result if result
61
+
62
+ # Traverse ancestor chain if code_index available
63
+ return nil unless @code_index
64
+
65
+ ancestors = @code_index.ancestors_of(class_name)
66
+ ancestors.each do |ancestor_name|
67
+ next if ancestor_name == class_name # Skip self
68
+
69
+ result = @variables.dig(ancestor_name, name)
70
+ return result if result
71
+ end
72
+
73
+ nil
74
+ end
75
+
76
+ # Clear all registered variables
77
+ def clear
78
+ @variables.clear
79
+ @file_entries.clear
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -4,25 +4,56 @@ module TypeGuessr
4
4
  module Core
5
5
  module Registry
6
6
  # Stores and retrieves project method definitions
7
- # Supports inheritance chain traversal when ancestry_provider is set
7
+ # Supports inheritance chain traversal when code_index is set
8
8
  class MethodRegistry
9
- # Callback for getting class ancestors
10
- # @return [Proc, nil] A proc that takes class_name and returns array of ancestor names
11
- attr_accessor :ancestry_provider
9
+ # Adapter for getting class ancestors (must respond to #ancestors_of)
10
+ # @return [#ancestors_of, nil] Adapter that returns array of ancestor names
11
+ attr_accessor :code_index
12
12
 
13
- # @param ancestry_provider [Proc, nil] Returns ancestors for inheritance lookup
14
- def initialize(ancestry_provider: nil)
13
+ # @param code_index [#ancestors_of, nil] Adapter for inheritance lookup
14
+ def initialize(code_index: nil)
15
15
  @methods = {} # { "ClassName" => { "method_name" => DefNode } }
16
- @ancestry_provider = ancestry_provider
16
+ @file_entries = {} # { file_path => [[class_name, method_name], ...] }
17
+ @entry_sources = {} # { [class_name, method_name] => Set[file_path, ...] }
18
+ @code_index = code_index
17
19
  end
18
20
 
19
21
  # Register a method definition
20
22
  # @param class_name [String] Class name (empty string for top-level)
21
23
  # @param method_name [String] Method name
22
24
  # @param def_node [IR::DefNode] Method definition node
23
- def register(class_name, method_name, def_node)
25
+ # @param file_path [String, nil] Source file path for tracking
26
+ def register(class_name, method_name, def_node, file_path: nil)
24
27
  @methods[class_name] ||= {}
25
28
  @methods[class_name][method_name] = def_node
29
+
30
+ return unless file_path
31
+
32
+ @file_entries[file_path] ||= []
33
+ @file_entries[file_path] << [class_name, method_name]
34
+ key = [class_name, method_name]
35
+ @entry_sources[key] ||= Set.new
36
+ @entry_sources[key] << file_path
37
+ end
38
+
39
+ # Remove all method entries registered from a specific file
40
+ # @param file_path [String] Source file path
41
+ def remove_file(file_path)
42
+ entries = @file_entries.delete(file_path)
43
+ return unless entries
44
+
45
+ entries.each do |class_name, method_name|
46
+ key = [class_name, method_name]
47
+ sources = @entry_sources[key]
48
+ next unless sources
49
+
50
+ sources.delete(file_path)
51
+ next unless sources.empty?
52
+
53
+ @entry_sources.delete(key)
54
+ @methods[class_name]&.delete(method_name)
55
+ @methods.delete(class_name) if @methods[class_name] && @methods[class_name].empty?
56
+ end
26
57
  end
27
58
 
28
59
  # Look up a method definition (with inheritance chain traversal)
@@ -34,10 +65,10 @@ module TypeGuessr
34
65
  result = @methods.dig(class_name, method_name)
35
66
  return result if result
36
67
 
37
- # Traverse ancestor chain if provider available
38
- return nil unless @ancestry_provider
68
+ # Traverse ancestor chain if code_index available
69
+ return nil unless @code_index
39
70
 
40
- ancestors = @ancestry_provider.call(class_name)
71
+ ancestors = @code_index.ancestors_of(class_name)
41
72
  ancestors.each do |ancestor_name|
42
73
  next if ancestor_name == class_name # Skip self
43
74
 
@@ -48,12 +79,6 @@ module TypeGuessr
48
79
  nil
49
80
  end
50
81
 
51
- # Get all registered class names
52
- # @return [Array<String>] List of class names (frozen)
53
- def registered_classes
54
- @methods.keys.freeze
55
- end
56
-
57
82
  # Get all methods for a specific class (direct methods only)
58
83
  # @param class_name [String] Class name
59
84
  # @return [Hash<String, IR::DefNode>] Methods hash (frozen)
@@ -61,6 +86,27 @@ module TypeGuessr
61
86
  (@methods[class_name] || {}).freeze
62
87
  end
63
88
 
89
+ # Iterate over all registered methods
90
+ # @yield [class_name, method_name, def_node]
91
+ def each_entry(&block)
92
+ return enum_for(:each_entry) unless block
93
+
94
+ @methods.each do |class_name, methods_hash|
95
+ methods_hash.each do |method_name, def_node|
96
+ block.call(class_name, method_name, def_node)
97
+ end
98
+ end
99
+ end
100
+
101
+ # Get the source file path for a registered method
102
+ # @param class_name [String] Class name
103
+ # @param method_name [String] Method name
104
+ # @return [String, nil] File path or nil
105
+ def source_file_for(class_name, method_name)
106
+ sources = @entry_sources[[class_name, method_name]]
107
+ sources&.first
108
+ end
109
+
64
110
  # Search for methods matching a pattern
65
111
  # @param pattern [String] Search pattern (partial match on "ClassName#method_name")
66
112
  # @return [Array<Array>] Array of [class_name, method_name, def_node]
@@ -75,30 +121,11 @@ module TypeGuessr
75
121
  results
76
122
  end
77
123
 
78
- # Get all methods available on a class (including inherited)
79
- # @param class_name [String]
80
- # @return [Set<String>] Method names
81
- def all_methods_for_class(class_name)
82
- # Start with directly defined methods
83
- class_methods = (@methods[class_name]&.keys || []).to_set
84
-
85
- # Add inherited methods if ancestry_provider is available
86
- return class_methods unless @ancestry_provider
87
-
88
- ancestors = @ancestry_provider.call(class_name)
89
- ancestors.each do |ancestor_name|
90
- next if ancestor_name == class_name # Skip self
91
-
92
- ancestor_methods = @methods[ancestor_name]&.keys || []
93
- class_methods.merge(ancestor_methods)
94
- end
95
-
96
- class_methods
97
- end
98
-
99
124
  # Clear all registered methods
100
125
  def clear
101
126
  @methods.clear
127
+ @file_entries.clear
128
+ @entry_sources.clear
102
129
  end
103
130
  end
104
131
  end