type-guessr 0.0.2 → 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.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/lib/ruby_lsp/type_guessr/addon.rb +4 -5
- data/lib/ruby_lsp/type_guessr/code_index_adapter.rb +17 -0
- data/lib/ruby_lsp/type_guessr/{graph_builder.rb → debug_graph_builder.rb} +3 -3
- data/lib/ruby_lsp/type_guessr/debug_server.rb +2 -2
- data/lib/ruby_lsp/type_guessr/dsl/activerecord_adapter.rb +404 -0
- data/lib/ruby_lsp/type_guessr/dsl/ar_schema_watcher.rb +96 -0
- data/lib/ruby_lsp/type_guessr/dsl/ar_type_mapper.rb +51 -0
- data/lib/ruby_lsp/type_guessr/dsl.rb +3 -0
- data/lib/ruby_lsp/type_guessr/dsl_type_registrar.rb +60 -0
- data/lib/ruby_lsp/type_guessr/hover.rb +46 -40
- data/lib/ruby_lsp/type_guessr/rails_server_addon.rb +83 -0
- data/lib/ruby_lsp/type_guessr/runtime_adapter.rb +90 -16
- data/lib/type-guessr.rb +2 -13
- data/lib/type_guessr/core/cache/gem_signature_cache.rb +3 -2
- data/lib/type_guessr/core/cache.rb +5 -0
- data/lib/{ruby_lsp/type_guessr → type_guessr/core}/config.rb +2 -2
- data/lib/type_guessr/core/converter/call_converter.rb +161 -0
- data/lib/type_guessr/core/converter/container_mutation_converter.rb +241 -0
- data/lib/type_guessr/core/converter/context.rb +144 -0
- data/lib/type_guessr/core/converter/control_flow_converter.rb +425 -0
- data/lib/type_guessr/core/converter/definition_converter.rb +246 -0
- data/lib/type_guessr/core/converter/literal_converter.rb +217 -0
- data/lib/type_guessr/core/converter/prism_converter.rb +9 -1682
- data/lib/type_guessr/core/converter/rbs_converter.rb +15 -1
- data/lib/type_guessr/core/converter/registration.rb +100 -0
- data/lib/type_guessr/core/converter/variable_converter.rb +225 -0
- data/lib/type_guessr/core/converter.rb +4 -0
- data/lib/type_guessr/core/index.rb +3 -0
- data/lib/type_guessr/core/inference/resolver.rb +206 -208
- data/lib/type_guessr/core/inference.rb +4 -0
- data/lib/type_guessr/core/ir.rb +3 -0
- data/lib/type_guessr/core/logger.rb +3 -5
- data/lib/type_guessr/core/registry/method_registry.rb +9 -0
- data/lib/type_guessr/core/registry/signature_registry.rb +73 -16
- data/lib/type_guessr/core/registry.rb +6 -0
- data/lib/type_guessr/core/type_serializer.rb +18 -14
- data/lib/type_guessr/core/type_simplifier.rb +5 -5
- data/lib/type_guessr/core/types.rb +64 -22
- data/lib/type_guessr/core.rb +29 -0
- data/lib/type_guessr/mcp/server.rb +55 -46
- data/lib/type_guessr/mcp/standalone_runtime.rb +70 -110
- data/lib/type_guessr/version.rb +1 -1
- metadata +25 -5
- data/.mcp.json +0 -9
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "singleton"
|
|
4
3
|
require "rbs"
|
|
5
4
|
require_relative "../types"
|
|
6
5
|
require_relative "../logger"
|
|
7
|
-
require_relative "../converter
|
|
6
|
+
require_relative "../converter"
|
|
8
7
|
require_relative "../type_serializer"
|
|
9
8
|
|
|
10
9
|
module TypeGuessr
|
|
@@ -13,7 +12,9 @@ module TypeGuessr
|
|
|
13
12
|
# Preloads stdlib RBS signatures and provides O(1) hash lookup
|
|
14
13
|
# Singleton to ensure RBS is loaded only once across all usages
|
|
15
14
|
class SignatureRegistry
|
|
16
|
-
|
|
15
|
+
class << self
|
|
16
|
+
attr_accessor :instance
|
|
17
|
+
end
|
|
17
18
|
|
|
18
19
|
# Represents a method signature entry from RBS
|
|
19
20
|
# Handles overload resolution and type conversion
|
|
@@ -205,11 +206,14 @@ module TypeGuessr
|
|
|
205
206
|
class GemMethodEntry
|
|
206
207
|
attr_reader :params
|
|
207
208
|
|
|
208
|
-
def initialize(return_type, params = [])
|
|
209
|
+
def initialize(return_type, params = [], skip_stdlib_rbs: false)
|
|
209
210
|
@return_type = return_type
|
|
210
211
|
@params = params
|
|
212
|
+
@skip_stdlib_rbs = skip_stdlib_rbs
|
|
211
213
|
end
|
|
212
214
|
|
|
215
|
+
def skip_stdlib_rbs? = @skip_stdlib_rbs
|
|
216
|
+
|
|
213
217
|
def return_type(_arg_types = [])
|
|
214
218
|
@return_type
|
|
215
219
|
end
|
|
@@ -233,14 +237,16 @@ module TypeGuessr
|
|
|
233
237
|
# Get formatted signature strings for display
|
|
234
238
|
# @return [Array<String>] human-readable method signatures
|
|
235
239
|
def signature_strings
|
|
236
|
-
param_str = @params.
|
|
240
|
+
param_str = @params.join(", ")
|
|
237
241
|
["(#{param_str}) -> #{@return_type}"]
|
|
238
242
|
end
|
|
239
243
|
end
|
|
240
244
|
|
|
245
|
+
attr_accessor :code_index
|
|
241
246
|
attr_writer :on_demand_inferrer # ->(class_name, method_name, kind) { ... }
|
|
242
247
|
|
|
243
|
-
def initialize
|
|
248
|
+
def initialize(code_index: nil)
|
|
249
|
+
@code_index = code_index
|
|
244
250
|
@instance_methods = {} # { "String" => { "upcase" => MethodEntry } }
|
|
245
251
|
@class_methods = {} # { "File" => { "read" => MethodEntry } }
|
|
246
252
|
@converter = Converter::RBSConverter.new
|
|
@@ -265,19 +271,42 @@ module TypeGuessr
|
|
|
265
271
|
end
|
|
266
272
|
|
|
267
273
|
# Look up instance method entry
|
|
274
|
+
# Falls back to ancestor chain traversal when code_index is available
|
|
268
275
|
# @param class_name [String] the class name
|
|
269
276
|
# @param method_name [String] the method name
|
|
270
277
|
# @return [MethodEntry, nil] method entry or nil if not found
|
|
271
278
|
def lookup(class_name, method_name)
|
|
272
|
-
@instance_methods.dig(class_name, method_name)
|
|
279
|
+
result = @instance_methods.dig(class_name, method_name)
|
|
280
|
+
return result if result
|
|
281
|
+
|
|
282
|
+
return nil unless @code_index
|
|
283
|
+
|
|
284
|
+
@code_index.ancestors_of(class_name).each do |ancestor|
|
|
285
|
+
next if ancestor == class_name
|
|
286
|
+
|
|
287
|
+
result = @instance_methods.dig(ancestor, method_name)
|
|
288
|
+
return result if result
|
|
289
|
+
end
|
|
290
|
+
nil
|
|
273
291
|
end
|
|
274
292
|
|
|
275
293
|
# Look up class method entry
|
|
294
|
+
# Falls back to ancestor chain traversal when code_index is available
|
|
276
295
|
# @param class_name [String] the class name
|
|
277
296
|
# @param method_name [String] the method name
|
|
278
297
|
# @return [MethodEntry, nil] method entry or nil if not found
|
|
279
298
|
def lookup_class_method(class_name, method_name)
|
|
280
|
-
@class_methods.dig(class_name, method_name)
|
|
299
|
+
result = @class_methods.dig(class_name, method_name)
|
|
300
|
+
return result if result
|
|
301
|
+
return nil unless @code_index
|
|
302
|
+
|
|
303
|
+
@code_index.ancestors_of(class_name).each do |ancestor|
|
|
304
|
+
next if ancestor == class_name
|
|
305
|
+
|
|
306
|
+
result = @class_methods.dig(ancestor, method_name)
|
|
307
|
+
return result if result
|
|
308
|
+
end
|
|
309
|
+
nil
|
|
281
310
|
end
|
|
282
311
|
|
|
283
312
|
# Get method return type (convenience method matching old SignatureProvider API)
|
|
@@ -358,21 +387,33 @@ module TypeGuessr
|
|
|
358
387
|
end
|
|
359
388
|
|
|
360
389
|
# Register a single gem instance method
|
|
361
|
-
# Skips if RBS entry already exists (RBS takes priority)
|
|
362
|
-
|
|
390
|
+
# Skips if RBS entry already exists (RBS takes priority).
|
|
391
|
+
# force: true overwrites existing GemMethodEntry (but never MethodEntry/RBS).
|
|
392
|
+
def register_gem_method(class_name, method_name, return_type, params = [], force: false)
|
|
363
393
|
@instance_methods[class_name] ||= {}
|
|
364
|
-
return if @instance_methods[class_name].key?(method_name)
|
|
365
394
|
|
|
366
|
-
@instance_methods[class_name][method_name]
|
|
395
|
+
existing = @instance_methods[class_name][method_name]
|
|
396
|
+
if existing
|
|
397
|
+
return if existing.is_a?(MethodEntry)
|
|
398
|
+
return unless force
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
@instance_methods[class_name][method_name] = GemMethodEntry.new(return_type, params, skip_stdlib_rbs: force)
|
|
367
402
|
end
|
|
368
403
|
|
|
369
404
|
# Register a single gem class method
|
|
370
|
-
# Skips if RBS entry already exists (RBS takes priority)
|
|
371
|
-
|
|
405
|
+
# Skips if RBS entry already exists (RBS takes priority).
|
|
406
|
+
# force: true overwrites existing GemMethodEntry (but never MethodEntry/RBS).
|
|
407
|
+
def register_gem_class_method(class_name, method_name, return_type, params = [], force: false)
|
|
372
408
|
@class_methods[class_name] ||= {}
|
|
373
|
-
return if @class_methods[class_name].key?(method_name)
|
|
374
409
|
|
|
375
|
-
@class_methods[class_name][method_name]
|
|
410
|
+
existing = @class_methods[class_name][method_name]
|
|
411
|
+
if existing
|
|
412
|
+
return if existing.is_a?(MethodEntry)
|
|
413
|
+
return unless force
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
@class_methods[class_name][method_name] = GemMethodEntry.new(return_type, params, skip_stdlib_rbs: force)
|
|
376
417
|
end
|
|
377
418
|
|
|
378
419
|
# Bulk load gem cache data into the registry
|
|
@@ -447,9 +488,25 @@ module TypeGuessr
|
|
|
447
488
|
env = RBS::Environment.from_loader(loader).resolve_type_names
|
|
448
489
|
builder = RBS::DefinitionBuilder.new(env: env)
|
|
449
490
|
|
|
491
|
+
# Collect type parameter names for generic classes (e.g., "Set" => [:A])
|
|
492
|
+
# This lets RBSConverter build ClassInstance with named type_params
|
|
493
|
+
class_type_params = {}
|
|
494
|
+
env.class_decls.each do |type_name, entry|
|
|
495
|
+
params = entry.type_params.map(&:name)
|
|
496
|
+
class_type_params[type_name.to_s.delete_prefix("::")] = params if params.any?
|
|
497
|
+
end
|
|
498
|
+
@converter = Converter::RBSConverter.new(class_type_params)
|
|
499
|
+
|
|
450
500
|
env.class_decls.each_key do |type_name|
|
|
451
501
|
load_class_definitions(type_name, builder)
|
|
452
502
|
end
|
|
503
|
+
|
|
504
|
+
env.class_alias_decls.each_value do |entry|
|
|
505
|
+
alias_name = entry.decl.new_name.to_s.delete_prefix("::")
|
|
506
|
+
original_name = entry.decl.old_name.to_s.delete_prefix("::")
|
|
507
|
+
@instance_methods[alias_name] ||= @instance_methods[original_name] if @instance_methods[original_name]
|
|
508
|
+
@class_methods[alias_name] ||= @class_methods[original_name] if @class_methods[original_name]
|
|
509
|
+
end
|
|
453
510
|
rescue StandardError => e
|
|
454
511
|
Logger.error("Failed to preload RBS environment", e)
|
|
455
512
|
end
|
|
@@ -14,7 +14,9 @@ module TypeGuessr
|
|
|
14
14
|
when Types::Unknown
|
|
15
15
|
{ "_type" => "Unknown" }
|
|
16
16
|
when Types::ClassInstance
|
|
17
|
-
{ "_type" => "ClassInstance", "name" => type.name }
|
|
17
|
+
h = { "_type" => "ClassInstance", "name" => type.name }
|
|
18
|
+
h["type_params"] = type.type_params.to_h { |k, v| [k.to_s, serialize(v)] } if type.type_params&.any?
|
|
19
|
+
h
|
|
18
20
|
when Types::SingletonType
|
|
19
21
|
{ "_type" => "SingletonType", "name" => type.name }
|
|
20
22
|
when Types::TupleType
|
|
@@ -49,19 +51,21 @@ module TypeGuessr
|
|
|
49
51
|
module_function def deserialize(hash)
|
|
50
52
|
case hash["_type"]
|
|
51
53
|
when "Unguessed" then Types::Unguessed.instance
|
|
52
|
-
when "Unknown"
|
|
53
|
-
when "ClassInstance"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
when "
|
|
57
|
-
when "
|
|
58
|
-
when "
|
|
59
|
-
when "
|
|
60
|
-
when "
|
|
61
|
-
when "
|
|
62
|
-
when "
|
|
63
|
-
when "
|
|
64
|
-
when "
|
|
54
|
+
when "Unknown" then Types::Unknown.instance
|
|
55
|
+
when "ClassInstance"
|
|
56
|
+
type_params = hash["type_params"]&.to_h { |k, v| [k.to_sym, deserialize(v)] }
|
|
57
|
+
Types::ClassInstance.for(hash["name"], type_params)
|
|
58
|
+
when "SingletonType" then Types::SingletonType.new(hash["name"])
|
|
59
|
+
when "ArrayType" then Types::ArrayType.new(deserialize(hash["element_type"]))
|
|
60
|
+
when "TupleType" then Types::TupleType.new(hash["element_types"].map { |t| deserialize(t) })
|
|
61
|
+
when "HashType" then Types::HashType.new(deserialize(hash["key_type"]), deserialize(hash["value_type"]))
|
|
62
|
+
when "RangeType" then Types::RangeType.new(deserialize(hash["element_type"]))
|
|
63
|
+
when "HashShape" then Types::HashShape.new(hash["fields"].to_h { |k, v| [k.to_sym, deserialize(v)] })
|
|
64
|
+
when "Union" then Types::Union.new(hash["types"].map { |t| deserialize(t) })
|
|
65
|
+
when "TypeVariable" then Types::TypeVariable.new(hash["name"].to_sym)
|
|
66
|
+
when "SelfType" then Types::SelfType.instance
|
|
67
|
+
when "ForwardingArgs" then Types::ForwardingArgs.instance
|
|
68
|
+
when "MethodSignature" then deserialize_method_signature(hash)
|
|
65
69
|
else
|
|
66
70
|
raise ArgumentError, "Unknown type: #{hash["_type"]}"
|
|
67
71
|
end
|
|
@@ -33,8 +33,8 @@ module TypeGuessr
|
|
|
33
33
|
# 1. Single element: unwrap
|
|
34
34
|
return types.first if types.size == 1
|
|
35
35
|
|
|
36
|
-
# 2.
|
|
37
|
-
types =
|
|
36
|
+
# 2. Remove subclass types when parent class is also present
|
|
37
|
+
types = remove_subclass_types(types) if @code_index
|
|
38
38
|
|
|
39
39
|
# 3. Check again after filtering
|
|
40
40
|
return types.first if types.size == 1
|
|
@@ -45,10 +45,10 @@ module TypeGuessr
|
|
|
45
45
|
Types::Union.new(types)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
#
|
|
48
|
+
# Remove types whose ancestor is also in the list (e.g., remove Integer when Numeric is present)
|
|
49
49
|
# @param types [Array<Types::Type>] List of types
|
|
50
|
-
# @return [Array<Types::Type>] Filtered list with
|
|
51
|
-
private def
|
|
50
|
+
# @return [Array<Types::Type>] Filtered list with subclass types removed
|
|
51
|
+
private def remove_subclass_types(types)
|
|
52
52
|
# Extract class names from ClassInstance types
|
|
53
53
|
class_names = types.filter_map do |t|
|
|
54
54
|
t.name if t.is_a?(Types::ClassInstance)
|
|
@@ -36,7 +36,7 @@ module TypeGuessr
|
|
|
36
36
|
# Get type variable substitutions for this type
|
|
37
37
|
# Used for substituting type variables in block parameters
|
|
38
38
|
# @return [Hash{Symbol => Type}] type variable substitutions (e.g., { Elem: Integer, K: Symbol, V: String })
|
|
39
|
-
def
|
|
39
|
+
def type_parameter_bindings
|
|
40
40
|
{}
|
|
41
41
|
end
|
|
42
42
|
|
|
@@ -68,42 +68,73 @@ module TypeGuessr
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
# ClassInstance - instance of a class
|
|
71
|
+
# @param type_params [Hash{Symbol => Type}, nil] type parameters (e.g., { A: String } for Set[String])
|
|
71
72
|
class ClassInstance < Type
|
|
72
73
|
CACHE = {} # rubocop:disable Style/MutableConstant
|
|
74
|
+
GENERIC_CACHE = {} # rubocop:disable Style/MutableConstant
|
|
73
75
|
|
|
74
76
|
# Factory method that caches instances for reuse
|
|
75
77
|
# @param name [String] The class name
|
|
78
|
+
# @param type_params [Hash{Symbol => Type}, nil] type parameters
|
|
76
79
|
# @return [ClassInstance] Cached or new instance
|
|
77
|
-
def self.for(name)
|
|
78
|
-
|
|
80
|
+
def self.for(name, type_params = nil)
|
|
81
|
+
if type_params.nil?
|
|
82
|
+
CACHE[name] ||= new(name).freeze
|
|
83
|
+
else
|
|
84
|
+
key = [name, type_params]
|
|
85
|
+
GENERIC_CACHE[key] ||= new(name, type_params).freeze
|
|
86
|
+
end
|
|
79
87
|
end
|
|
80
88
|
|
|
81
|
-
attr_reader :name
|
|
89
|
+
attr_reader :name, :type_params
|
|
82
90
|
|
|
83
|
-
def initialize(name)
|
|
91
|
+
def initialize(name, type_params = nil)
|
|
84
92
|
super()
|
|
85
93
|
@name = name
|
|
94
|
+
@type_params = type_params&.freeze
|
|
86
95
|
end
|
|
87
96
|
|
|
88
97
|
def eql?(other)
|
|
89
|
-
super && @name == other.name
|
|
98
|
+
super && @name == other.name && @type_params == other.type_params
|
|
90
99
|
end
|
|
91
100
|
|
|
92
101
|
def hash
|
|
93
|
-
[self.class, @name].hash
|
|
102
|
+
[self.class, @name, @type_params].hash
|
|
94
103
|
end
|
|
95
104
|
|
|
96
105
|
def to_s
|
|
97
|
-
case @name
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
106
|
+
base = case @name
|
|
107
|
+
when "NilClass" then "nil"
|
|
108
|
+
when "TrueClass" then "true"
|
|
109
|
+
when "FalseClass" then "false"
|
|
110
|
+
else @name
|
|
111
|
+
end
|
|
112
|
+
if @type_params&.any?
|
|
113
|
+
"#{base}[#{@type_params.values.join(", ")}]"
|
|
114
|
+
else
|
|
115
|
+
base
|
|
102
116
|
end
|
|
103
117
|
end
|
|
104
118
|
|
|
105
119
|
def inspect
|
|
106
|
-
|
|
120
|
+
if @type_params&.any?
|
|
121
|
+
"#<ClassInstance:#{@name}[#{@type_params.map { |k, v| "#{k}=#{v.inspect}" }.join(", ")}]>"
|
|
122
|
+
else
|
|
123
|
+
"#<ClassInstance:#{@name}>"
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def type_parameter_bindings
|
|
128
|
+
@type_params || {}
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def substitute(substitutions)
|
|
132
|
+
return self if @type_params.nil? || @type_params.empty?
|
|
133
|
+
|
|
134
|
+
new_params = @type_params.transform_values { |t| t.substitute(substitutions) }
|
|
135
|
+
return self if new_params == @type_params
|
|
136
|
+
|
|
137
|
+
ClassInstance.new(@name, new_params)
|
|
107
138
|
end
|
|
108
139
|
|
|
109
140
|
def rbs_class_name
|
|
@@ -164,15 +195,17 @@ module TypeGuessr
|
|
|
164
195
|
def to_s
|
|
165
196
|
if bool_type?
|
|
166
197
|
"bool"
|
|
198
|
+
elsif nullable_bool_type?
|
|
199
|
+
"bool?"
|
|
167
200
|
elsif optional_type?
|
|
168
|
-
"
|
|
201
|
+
"#{non_nil_type}?"
|
|
169
202
|
else
|
|
170
203
|
@types.map(&:to_s).sort.join(" | ")
|
|
171
204
|
end
|
|
172
205
|
end
|
|
173
206
|
|
|
174
207
|
def inspect
|
|
175
|
-
"#<Union:#{@types.
|
|
208
|
+
"#<Union:#{@types.join("|")}>"
|
|
176
209
|
end
|
|
177
210
|
|
|
178
211
|
def substitute(substitutions)
|
|
@@ -190,6 +223,15 @@ module TypeGuessr
|
|
|
190
223
|
has_true && has_false
|
|
191
224
|
end
|
|
192
225
|
|
|
226
|
+
private def nullable_bool_type?
|
|
227
|
+
return false unless @types.size == 3
|
|
228
|
+
|
|
229
|
+
has_true = @types.any? { |t| t.is_a?(ClassInstance) && t.name == "TrueClass" }
|
|
230
|
+
has_false = @types.any? { |t| t.is_a?(ClassInstance) && t.name == "FalseClass" }
|
|
231
|
+
has_nil = @types.any? { |t| nil_type?(t) }
|
|
232
|
+
has_true && has_false && has_nil
|
|
233
|
+
end
|
|
234
|
+
|
|
193
235
|
private def optional_type?
|
|
194
236
|
@types.size == 2 && @types.any? { |t| nil_type?(t) }
|
|
195
237
|
end
|
|
@@ -225,7 +267,7 @@ module TypeGuessr
|
|
|
225
267
|
private def simplify_if_unknown_present(types)
|
|
226
268
|
return types if types.size <= 1
|
|
227
269
|
|
|
228
|
-
has_unknown = types.any?
|
|
270
|
+
has_unknown = types.any?(Unknown)
|
|
229
271
|
has_unknown ? [Unknown.instance] : types
|
|
230
272
|
end
|
|
231
273
|
|
|
@@ -270,7 +312,7 @@ module TypeGuessr
|
|
|
270
312
|
"Array"
|
|
271
313
|
end
|
|
272
314
|
|
|
273
|
-
def
|
|
315
|
+
def type_parameter_bindings
|
|
274
316
|
{ Elem: @element_type }
|
|
275
317
|
end
|
|
276
318
|
end
|
|
@@ -321,7 +363,7 @@ module TypeGuessr
|
|
|
321
363
|
"Array"
|
|
322
364
|
end
|
|
323
365
|
|
|
324
|
-
def
|
|
366
|
+
def type_parameter_bindings
|
|
325
367
|
return { Elem: Unknown.instance } if @element_types.empty?
|
|
326
368
|
|
|
327
369
|
unique = @element_types.uniq
|
|
@@ -368,7 +410,7 @@ module TypeGuessr
|
|
|
368
410
|
"Hash"
|
|
369
411
|
end
|
|
370
412
|
|
|
371
|
-
def
|
|
413
|
+
def type_parameter_bindings
|
|
372
414
|
{ K: @key_type, V: @value_type }
|
|
373
415
|
end
|
|
374
416
|
end
|
|
@@ -409,7 +451,7 @@ module TypeGuessr
|
|
|
409
451
|
"Range"
|
|
410
452
|
end
|
|
411
453
|
|
|
412
|
-
def
|
|
454
|
+
def type_parameter_bindings
|
|
413
455
|
{ Elem: @element_type }
|
|
414
456
|
end
|
|
415
457
|
end
|
|
@@ -469,7 +511,7 @@ module TypeGuessr
|
|
|
469
511
|
"Hash"
|
|
470
512
|
end
|
|
471
513
|
|
|
472
|
-
def
|
|
514
|
+
def type_parameter_bindings
|
|
473
515
|
key_type = ClassInstance.for("Symbol")
|
|
474
516
|
value_types = @fields.values.uniq
|
|
475
517
|
value_type = if value_types.empty?
|
|
@@ -574,7 +616,7 @@ module TypeGuessr
|
|
|
574
616
|
end
|
|
575
617
|
|
|
576
618
|
def to_s
|
|
577
|
-
params_str = @params.
|
|
619
|
+
params_str = @params.join(", ")
|
|
578
620
|
"(#{params_str}) -> #{@return_type}"
|
|
579
621
|
end
|
|
580
622
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Configuration
|
|
4
|
+
require_relative "core/config"
|
|
5
|
+
|
|
6
|
+
# Core type system
|
|
7
|
+
require_relative "core/types"
|
|
8
|
+
|
|
9
|
+
# Node infrastructure
|
|
10
|
+
require_relative "core/node_key_generator"
|
|
11
|
+
require_relative "core/node_context_helper"
|
|
12
|
+
require_relative "core/ir"
|
|
13
|
+
require_relative "core/index"
|
|
14
|
+
|
|
15
|
+
# Registries
|
|
16
|
+
require_relative "core/registry"
|
|
17
|
+
|
|
18
|
+
# Converters
|
|
19
|
+
require_relative "core/converter"
|
|
20
|
+
|
|
21
|
+
# Inference
|
|
22
|
+
require_relative "core/inference"
|
|
23
|
+
|
|
24
|
+
# Utilities
|
|
25
|
+
require_relative "core/signature_builder"
|
|
26
|
+
require_relative "core/type_simplifier"
|
|
27
|
+
require_relative "core/type_serializer"
|
|
28
|
+
require_relative "core/cache"
|
|
29
|
+
require_relative "core/logger"
|
|
@@ -10,26 +10,7 @@ require "ruby_indexer/ruby_indexer"
|
|
|
10
10
|
require "ruby_lsp/internal"
|
|
11
11
|
|
|
12
12
|
# Load core components
|
|
13
|
-
require_relative "../core
|
|
14
|
-
require_relative "../core/node_key_generator"
|
|
15
|
-
require_relative "../core/node_context_helper"
|
|
16
|
-
require_relative "../core/ir/nodes"
|
|
17
|
-
require_relative "../core/index/location_index"
|
|
18
|
-
require_relative "../core/converter/prism_converter"
|
|
19
|
-
require_relative "../core/converter/rbs_converter"
|
|
20
|
-
require_relative "../core/inference/result"
|
|
21
|
-
require_relative "../core/inference/resolver"
|
|
22
|
-
require_relative "../core/registry/signature_registry"
|
|
23
|
-
require_relative "../core/registry/method_registry"
|
|
24
|
-
require_relative "../core/registry/instance_variable_registry"
|
|
25
|
-
require_relative "../core/registry/class_variable_registry"
|
|
26
|
-
require_relative "../core/signature_builder"
|
|
27
|
-
require_relative "../core/type_simplifier"
|
|
28
|
-
require_relative "../core/type_serializer"
|
|
29
|
-
require_relative "../core/logger"
|
|
30
|
-
require_relative "../core/cache/gem_signature_cache"
|
|
31
|
-
require_relative "../core/cache/gem_dependency_resolver"
|
|
32
|
-
require_relative "../core/cache/gem_signature_extractor"
|
|
13
|
+
require_relative "../core"
|
|
33
14
|
|
|
34
15
|
# Load CodeIndexAdapter, StandaloneRuntime, and FileWatcher
|
|
35
16
|
require_relative "../../ruby_lsp/type_guessr/code_index_adapter"
|
|
@@ -87,7 +68,8 @@ module TypeGuessr
|
|
|
87
68
|
|
|
88
69
|
private def build_runtime(ruby_index)
|
|
89
70
|
code_index = RubyLsp::TypeGuessr::CodeIndexAdapter.new(ruby_index)
|
|
90
|
-
signature_registry = Core::Registry::SignatureRegistry.
|
|
71
|
+
signature_registry = Core::Registry::SignatureRegistry.new(code_index: code_index)
|
|
72
|
+
Core::Registry::SignatureRegistry.instance = signature_registry
|
|
91
73
|
|
|
92
74
|
method_registry = Core::Registry::MethodRegistry.new(code_index: code_index)
|
|
93
75
|
ivar_registry = Core::Registry::InstanceVariableRegistry.new(code_index: code_index)
|
|
@@ -380,49 +362,71 @@ module TypeGuessr
|
|
|
380
362
|
end
|
|
381
363
|
|
|
382
364
|
private def build_tools
|
|
383
|
-
[
|
|
365
|
+
[build_get_method_sources_tool, build_get_method_signatures_tool, build_search_methods_tool]
|
|
384
366
|
end
|
|
385
367
|
|
|
386
|
-
private def
|
|
368
|
+
private def build_get_method_sources_tool
|
|
387
369
|
runtime = @runtime
|
|
388
370
|
to_response = method(:json_response)
|
|
389
371
|
|
|
390
372
|
::MCP::Tool.define(
|
|
391
|
-
name: "
|
|
392
|
-
description: "
|
|
393
|
-
"Returns the
|
|
373
|
+
name: "get_method_sources",
|
|
374
|
+
description: "Get source code of methods by class and method name. " \
|
|
375
|
+
"Returns the full method definition with file path and line number. " \
|
|
376
|
+
"Use when you need to read a method's implementation without knowing " \
|
|
377
|
+
"the exact file location.",
|
|
394
378
|
input_schema: {
|
|
395
379
|
type: "object",
|
|
396
380
|
properties: {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
381
|
+
methods: {
|
|
382
|
+
type: "array",
|
|
383
|
+
items: {
|
|
384
|
+
type: "object",
|
|
385
|
+
properties: {
|
|
386
|
+
class_name: { type: "string", description: "Fully qualified class name (e.g., 'User', 'Admin::User')" },
|
|
387
|
+
method_name: { type: "string", description: "Method name (e.g., 'save', 'initialize')" }
|
|
388
|
+
},
|
|
389
|
+
required: %w[class_name method_name]
|
|
390
|
+
},
|
|
391
|
+
description: "Array of {class_name, method_name} to look up"
|
|
392
|
+
}
|
|
400
393
|
},
|
|
401
|
-
required: %w[
|
|
394
|
+
required: %w[methods]
|
|
402
395
|
}
|
|
403
|
-
) do |
|
|
404
|
-
to_response.call(runtime.
|
|
396
|
+
) do |methods:, **|
|
|
397
|
+
to_response.call(runtime.method_sources(methods))
|
|
405
398
|
end
|
|
406
399
|
end
|
|
407
400
|
|
|
408
|
-
private def
|
|
401
|
+
private def build_get_method_signatures_tool
|
|
409
402
|
runtime = @runtime
|
|
410
403
|
to_response = method(:json_response)
|
|
411
404
|
|
|
412
405
|
::MCP::Tool.define(
|
|
413
|
-
name: "
|
|
414
|
-
description: "Get
|
|
415
|
-
"
|
|
406
|
+
name: "get_method_signatures",
|
|
407
|
+
description: "Get inferred signatures for multiple methods in one call. " \
|
|
408
|
+
"More efficient than calling get_method_signature repeatedly " \
|
|
409
|
+
"when investigating a class or module.",
|
|
416
410
|
input_schema: {
|
|
417
411
|
type: "object",
|
|
418
412
|
properties: {
|
|
419
|
-
|
|
420
|
-
|
|
413
|
+
methods: {
|
|
414
|
+
type: "array",
|
|
415
|
+
items: {
|
|
416
|
+
type: "object",
|
|
417
|
+
properties: {
|
|
418
|
+
class_name: { type: "string", description: "Fully qualified class name" },
|
|
419
|
+
method_name: { type: "string", description: "Method name" }
|
|
420
|
+
},
|
|
421
|
+
required: %w[class_name method_name]
|
|
422
|
+
},
|
|
423
|
+
description: "Array of {class_name, method_name} to look up"
|
|
424
|
+
}
|
|
421
425
|
},
|
|
422
|
-
required: %w[
|
|
426
|
+
required: %w[methods]
|
|
423
427
|
}
|
|
424
|
-
) do |
|
|
425
|
-
to_response.call(runtime.
|
|
428
|
+
) do |methods:, **|
|
|
429
|
+
to_response.call(runtime.method_signatures(methods))
|
|
426
430
|
end
|
|
427
431
|
end
|
|
428
432
|
|
|
@@ -432,17 +436,22 @@ module TypeGuessr
|
|
|
432
436
|
|
|
433
437
|
::MCP::Tool.define(
|
|
434
438
|
name: "search_methods",
|
|
435
|
-
description: "Search for method definitions
|
|
436
|
-
"
|
|
439
|
+
description: "Search for method definitions across the project with type-aware results. " \
|
|
440
|
+
"Returns matching methods with their inferred signatures. Unlike grep, results " \
|
|
441
|
+
"include class hierarchy context and inferred types. Supports patterns: " \
|
|
442
|
+
"'User#save' (specific), 'save' (all classes), 'Admin::*' (namespace). " \
|
|
443
|
+
"Use when exploring an unfamiliar codebase or finding which classes implement " \
|
|
444
|
+
"a given method.",
|
|
437
445
|
input_schema: {
|
|
438
446
|
type: "object",
|
|
439
447
|
properties: {
|
|
440
|
-
query: { type: "string", description: "Search query (e.g., 'User#save', 'save', 'initialize')" }
|
|
448
|
+
query: { type: "string", description: "Search query (e.g., 'User#save', 'save', 'initialize')" },
|
|
449
|
+
include_signatures: { type: "boolean", description: "Include inferred method signatures in results (default: false)" }
|
|
441
450
|
},
|
|
442
451
|
required: %w[query]
|
|
443
452
|
}
|
|
444
|
-
) do |query:, **|
|
|
445
|
-
to_response.call(runtime.search_methods(query))
|
|
453
|
+
) do |query:, include_signatures: false, **|
|
|
454
|
+
to_response.call(runtime.search_methods(query, include_signatures: include_signatures))
|
|
446
455
|
end
|
|
447
456
|
end
|
|
448
457
|
|