type-guessr 0.0.2 → 0.0.4
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 +18 -1
- 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 +312 -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 +24 -4
- data/.mcp.json +0 -9
|
@@ -8,8 +8,6 @@ module TypeGuessr
|
|
|
8
8
|
# Provides type inference, method signature lookup, and method search
|
|
9
9
|
# for use by the MCP server. All public query methods are thread-safe.
|
|
10
10
|
class StandaloneRuntime
|
|
11
|
-
NodeContextHelper = Core::NodeContextHelper
|
|
12
|
-
|
|
13
11
|
# @param converter [Core::Converter::PrismConverter]
|
|
14
12
|
# @param location_index [Core::Index::LocationIndex]
|
|
15
13
|
# @param signature_registry [Core::Registry::SignatureRegistry]
|
|
@@ -41,6 +39,7 @@ module TypeGuessr
|
|
|
41
39
|
|
|
42
40
|
@mutex.synchronize do
|
|
43
41
|
@location_index.remove_file(file_path)
|
|
42
|
+
@method_registry.remove_file(file_path)
|
|
44
43
|
@resolver.clear_cache
|
|
45
44
|
|
|
46
45
|
context = Core::Converter::PrismConverter::Context.new(
|
|
@@ -62,6 +61,7 @@ module TypeGuessr
|
|
|
62
61
|
def remove_indexed_file(file_path)
|
|
63
62
|
@mutex.synchronize do
|
|
64
63
|
@location_index.remove_file(file_path)
|
|
64
|
+
@method_registry.remove_file(file_path)
|
|
65
65
|
@resolver.clear_cache
|
|
66
66
|
end
|
|
67
67
|
end
|
|
@@ -87,38 +87,6 @@ module TypeGuessr
|
|
|
87
87
|
@signature_registry.preload
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
# Infer type at a specific file location
|
|
91
|
-
# @param file_path [String] Absolute path to the Ruby file
|
|
92
|
-
# @param line [Integer] Line number (1-based)
|
|
93
|
-
# @param column [Integer] Column number (0-based)
|
|
94
|
-
# @return [Hash] Inference result with :type, :reason, :node_type keys, or :error on failure
|
|
95
|
-
def infer_at(file_path, line, column)
|
|
96
|
-
file_path = File.expand_path(file_path)
|
|
97
|
-
|
|
98
|
-
source = File.read(file_path)
|
|
99
|
-
parse_result = Prism.parse_lex(source)
|
|
100
|
-
return { error: "Failed to parse file" } unless parse_result.value
|
|
101
|
-
|
|
102
|
-
ast = parse_result.value.first
|
|
103
|
-
code_units_cache = parse_result.code_units_cache(Encoding::UTF_8)
|
|
104
|
-
|
|
105
|
-
char_position = line_column_to_offset(source, line, column)
|
|
106
|
-
return { error: "Invalid line/column" } unless char_position
|
|
107
|
-
|
|
108
|
-
node_context = RubyLsp::RubyDocument.locate(
|
|
109
|
-
ast,
|
|
110
|
-
char_position,
|
|
111
|
-
code_units_cache: code_units_cache
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
prism_node = node_context.node
|
|
115
|
-
return { error: "No node found at position" } unless prism_node
|
|
116
|
-
|
|
117
|
-
infer_from_prism_node(prism_node, node_context)
|
|
118
|
-
rescue StandardError => e
|
|
119
|
-
{ error: e.message, backtrace: e.backtrace&.first(3) }
|
|
120
|
-
end
|
|
121
|
-
|
|
122
90
|
# Get method signature for a class#method
|
|
123
91
|
# @param class_name [String] Fully qualified class name (e.g., "User", "Admin::User")
|
|
124
92
|
# @param method_name [String] Method name (e.g., "save", "initialize")
|
|
@@ -132,7 +100,9 @@ module TypeGuessr
|
|
|
132
100
|
if entry.is_a?(Core::Registry::SignatureRegistry::MethodEntry)
|
|
133
101
|
return {
|
|
134
102
|
source: "rbs",
|
|
135
|
-
signatures: entry.signature_strings
|
|
103
|
+
signatures: entry.signature_strings,
|
|
104
|
+
class_name: class_name,
|
|
105
|
+
method_name: method_name
|
|
136
106
|
}
|
|
137
107
|
end
|
|
138
108
|
|
|
@@ -145,7 +115,8 @@ module TypeGuessr
|
|
|
145
115
|
}
|
|
146
116
|
end
|
|
147
117
|
|
|
148
|
-
return { error: "Method not found: #{class_name}##{method_name}"
|
|
118
|
+
return { error: "Method not found: #{class_name}##{method_name}", class_name: class_name,
|
|
119
|
+
method_name: method_name }
|
|
149
120
|
end
|
|
150
121
|
|
|
151
122
|
sig = @mutex.synchronize { @signature_builder.build_from_def_node(def_node) }
|
|
@@ -156,98 +127,87 @@ module TypeGuessr
|
|
|
156
127
|
method_name: method_name
|
|
157
128
|
}
|
|
158
129
|
rescue StandardError => e
|
|
159
|
-
{ error: e.message }
|
|
130
|
+
{ error: e.message, class_name: class_name, method_name: method_name }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Get signatures for multiple methods in one call
|
|
134
|
+
# @param methods [Array<Hash>] Array of { class_name:, method_name: } hashes
|
|
135
|
+
# @return [Array<Hash>] Array of signature results (same format as method_signature)
|
|
136
|
+
def method_signatures(methods)
|
|
137
|
+
methods.map do |entry|
|
|
138
|
+
method_signature(entry[:class_name], entry[:method_name])
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Get source code for a single method
|
|
143
|
+
# @param class_name [String] Fully qualified class name
|
|
144
|
+
# @param method_name [String] Method name
|
|
145
|
+
# @return [Hash] Source result with :source, :file_path, :line keys, or :error on failure
|
|
146
|
+
def method_source(class_name, method_name)
|
|
147
|
+
def_node = @mutex.synchronize { @method_registry.lookup(class_name, method_name) }
|
|
148
|
+
unless def_node
|
|
149
|
+
return { error: "Method not found: #{class_name}##{method_name}",
|
|
150
|
+
class_name: class_name, method_name: method_name }
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
file_path = @mutex.synchronize { @method_registry.source_file_for(class_name, method_name) }
|
|
154
|
+
unless file_path
|
|
155
|
+
return { error: "Source file not found: #{class_name}##{method_name}",
|
|
156
|
+
class_name: class_name, method_name: method_name }
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
source = File.read(file_path)
|
|
160
|
+
prism_result = Prism.parse(source)
|
|
161
|
+
node_context = RubyLsp::RubyDocument.locate(
|
|
162
|
+
prism_result.value, def_node.loc,
|
|
163
|
+
code_units_cache: prism_result.code_units_cache(Encoding::UTF_8)
|
|
164
|
+
)
|
|
165
|
+
prism_def = node_context.node.is_a?(Prism::DefNode) ? node_context.node : node_context.parent
|
|
166
|
+
|
|
167
|
+
{
|
|
168
|
+
class_name: class_name,
|
|
169
|
+
method_name: method_name,
|
|
170
|
+
source: prism_def.slice,
|
|
171
|
+
file_path: file_path,
|
|
172
|
+
line: prism_def.location.start_line
|
|
173
|
+
}
|
|
174
|
+
rescue StandardError => e
|
|
175
|
+
{ error: e.message, class_name: class_name, method_name: method_name }
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Get source code for multiple methods in one call
|
|
179
|
+
# @param methods [Array<Hash>] Array of { class_name:, method_name: } hashes
|
|
180
|
+
# @return [Array<Hash>] Array of source results (same format as method_source)
|
|
181
|
+
def method_sources(methods)
|
|
182
|
+
methods.map do |entry|
|
|
183
|
+
method_source(entry[:class_name], entry[:method_name])
|
|
184
|
+
end
|
|
160
185
|
end
|
|
161
186
|
|
|
162
187
|
# Search for methods matching a query pattern
|
|
163
188
|
# @param query [String] Search query (e.g., "User#save", "save", "initialize")
|
|
189
|
+
# @param include_signatures [Boolean] When true, include inferred signature for each result
|
|
164
190
|
# @return [Array<Hash>] Array of matching methods with :class_name, :method_name, :full_name, :location
|
|
165
|
-
def search_methods(query)
|
|
191
|
+
def search_methods(query, include_signatures: false)
|
|
166
192
|
@mutex.synchronize do
|
|
167
193
|
results = @method_registry.search(query)
|
|
168
194
|
results.map do |class_name, method_name, def_node|
|
|
169
|
-
{
|
|
195
|
+
entry = {
|
|
170
196
|
class_name: class_name,
|
|
171
197
|
method_name: method_name,
|
|
172
198
|
full_name: "#{class_name}##{method_name}",
|
|
173
199
|
location: def_node.loc ? { offset: def_node.loc } : nil
|
|
174
200
|
}
|
|
201
|
+
if include_signatures
|
|
202
|
+
sig = @signature_builder.build_from_def_node(def_node)
|
|
203
|
+
entry[:signature] = sig.to_s
|
|
204
|
+
end
|
|
205
|
+
entry
|
|
175
206
|
end
|
|
176
207
|
end
|
|
177
208
|
rescue StandardError => e
|
|
178
209
|
{ error: e.message }
|
|
179
210
|
end
|
|
180
|
-
|
|
181
|
-
private
|
|
182
|
-
|
|
183
|
-
# Resolve a Prism AST node to a type inference result
|
|
184
|
-
# @param prism_node [Prism::Node] The AST node at the cursor position
|
|
185
|
-
# @param node_context [RubyLsp::NodeContext] Context from RubyDocument.locate
|
|
186
|
-
# @return [Hash] Inference result
|
|
187
|
-
private def infer_from_prism_node(prism_node, node_context)
|
|
188
|
-
exclude_method = prism_node.is_a?(Prism::DefNode)
|
|
189
|
-
scope_id = NodeContextHelper.generate_scope_id(node_context, exclude_method: exclude_method)
|
|
190
|
-
node_hash = NodeContextHelper.generate_node_hash(prism_node, node_context)
|
|
191
|
-
return { error: "Unsupported node type: #{prism_node.class}" } unless node_hash
|
|
192
|
-
|
|
193
|
-
node_key = "#{scope_id}:#{node_hash}"
|
|
194
|
-
|
|
195
|
-
ir_node = @mutex.synchronize { @location_index.find_by_key(node_key) }
|
|
196
|
-
return { error: "Node not indexed", node_key: node_key, node_type: prism_node.class.name } unless ir_node
|
|
197
|
-
|
|
198
|
-
infer_from_ir_node(ir_node)
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
# Infer type from an IR node, dispatching by node type
|
|
202
|
-
# @param ir_node [Core::IR::Node] The indexed IR node
|
|
203
|
-
# @return [Hash] Inference result
|
|
204
|
-
private def infer_from_ir_node(ir_node)
|
|
205
|
-
if ir_node.is_a?(Core::IR::DefNode)
|
|
206
|
-
sig = @mutex.synchronize { @signature_builder.build_from_def_node(ir_node) }
|
|
207
|
-
return {
|
|
208
|
-
type: "method_signature",
|
|
209
|
-
signature: sig.to_s,
|
|
210
|
-
node_type: "DefNode"
|
|
211
|
-
}
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
result = @mutex.synchronize { @resolver.infer(ir_node) }
|
|
215
|
-
|
|
216
|
-
if ir_node.is_a?(Core::IR::CallNode)
|
|
217
|
-
{
|
|
218
|
-
type: result.type.to_s,
|
|
219
|
-
method: ir_node.method.to_s,
|
|
220
|
-
reason: result.reason,
|
|
221
|
-
node_type: "CallNode"
|
|
222
|
-
}
|
|
223
|
-
else
|
|
224
|
-
{
|
|
225
|
-
type: result.type.to_s,
|
|
226
|
-
reason: result.reason,
|
|
227
|
-
node_type: ir_node.class.name.split("::").last
|
|
228
|
-
}
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
# Convert 1-based line / 0-based column to byte offset
|
|
233
|
-
# @param source [String] Source code
|
|
234
|
-
# @param line [Integer] Line number (1-based)
|
|
235
|
-
# @param column [Integer] Column number (0-based)
|
|
236
|
-
# @return [Integer, nil] Byte offset, or nil if line/column is out of bounds
|
|
237
|
-
private def line_column_to_offset(source, line, column)
|
|
238
|
-
current_line = 1
|
|
239
|
-
current_offset = 0
|
|
240
|
-
|
|
241
|
-
source.each_char do |char|
|
|
242
|
-
return current_offset + column if current_line == line
|
|
243
|
-
|
|
244
|
-
current_line += 1 if char == "\n"
|
|
245
|
-
current_offset += char.bytesize
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
# Last line (no trailing newline)
|
|
249
|
-
current_offset + column if current_line == line
|
|
250
|
-
end
|
|
251
211
|
end
|
|
252
212
|
end
|
|
253
213
|
end
|
data/lib/type_guessr/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: type-guessr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- riseshia
|
|
@@ -45,32 +45,52 @@ executables:
|
|
|
45
45
|
extensions: []
|
|
46
46
|
extra_rdoc_files: []
|
|
47
47
|
files:
|
|
48
|
-
- ".mcp.json"
|
|
49
48
|
- LICENSE
|
|
50
49
|
- README.md
|
|
51
50
|
- exe/type-guessr
|
|
52
51
|
- lib/ruby_lsp/type_guessr/addon.rb
|
|
53
52
|
- lib/ruby_lsp/type_guessr/code_index_adapter.rb
|
|
54
|
-
- lib/ruby_lsp/type_guessr/config.rb
|
|
55
53
|
- lib/ruby_lsp/type_guessr/constants.rb
|
|
54
|
+
- lib/ruby_lsp/type_guessr/debug_graph_builder.rb
|
|
56
55
|
- lib/ruby_lsp/type_guessr/debug_server.rb
|
|
57
|
-
- lib/ruby_lsp/type_guessr/
|
|
56
|
+
- lib/ruby_lsp/type_guessr/dsl.rb
|
|
57
|
+
- lib/ruby_lsp/type_guessr/dsl/activerecord_adapter.rb
|
|
58
|
+
- lib/ruby_lsp/type_guessr/dsl/ar_schema_watcher.rb
|
|
59
|
+
- lib/ruby_lsp/type_guessr/dsl/ar_type_mapper.rb
|
|
60
|
+
- lib/ruby_lsp/type_guessr/dsl_type_registrar.rb
|
|
58
61
|
- lib/ruby_lsp/type_guessr/hover.rb
|
|
62
|
+
- lib/ruby_lsp/type_guessr/rails_server_addon.rb
|
|
59
63
|
- lib/ruby_lsp/type_guessr/runtime_adapter.rb
|
|
60
64
|
- lib/ruby_lsp/type_guessr/type_inferrer.rb
|
|
61
65
|
- lib/type-guessr.rb
|
|
66
|
+
- lib/type_guessr/core.rb
|
|
67
|
+
- lib/type_guessr/core/cache.rb
|
|
62
68
|
- lib/type_guessr/core/cache/gem_dependency_resolver.rb
|
|
63
69
|
- lib/type_guessr/core/cache/gem_signature_cache.rb
|
|
64
70
|
- lib/type_guessr/core/cache/gem_signature_extractor.rb
|
|
71
|
+
- lib/type_guessr/core/config.rb
|
|
72
|
+
- lib/type_guessr/core/converter.rb
|
|
73
|
+
- lib/type_guessr/core/converter/call_converter.rb
|
|
74
|
+
- lib/type_guessr/core/converter/container_mutation_converter.rb
|
|
75
|
+
- lib/type_guessr/core/converter/context.rb
|
|
76
|
+
- lib/type_guessr/core/converter/control_flow_converter.rb
|
|
77
|
+
- lib/type_guessr/core/converter/definition_converter.rb
|
|
78
|
+
- lib/type_guessr/core/converter/literal_converter.rb
|
|
65
79
|
- lib/type_guessr/core/converter/prism_converter.rb
|
|
66
80
|
- lib/type_guessr/core/converter/rbs_converter.rb
|
|
81
|
+
- lib/type_guessr/core/converter/registration.rb
|
|
82
|
+
- lib/type_guessr/core/converter/variable_converter.rb
|
|
83
|
+
- lib/type_guessr/core/index.rb
|
|
67
84
|
- lib/type_guessr/core/index/location_index.rb
|
|
85
|
+
- lib/type_guessr/core/inference.rb
|
|
68
86
|
- lib/type_guessr/core/inference/resolver.rb
|
|
69
87
|
- lib/type_guessr/core/inference/result.rb
|
|
88
|
+
- lib/type_guessr/core/ir.rb
|
|
70
89
|
- lib/type_guessr/core/ir/nodes.rb
|
|
71
90
|
- lib/type_guessr/core/logger.rb
|
|
72
91
|
- lib/type_guessr/core/node_context_helper.rb
|
|
73
92
|
- lib/type_guessr/core/node_key_generator.rb
|
|
93
|
+
- lib/type_guessr/core/registry.rb
|
|
74
94
|
- lib/type_guessr/core/registry/class_variable_registry.rb
|
|
75
95
|
- lib/type_guessr/core/registry/instance_variable_registry.rb
|
|
76
96
|
- lib/type_guessr/core/registry/method_registry.rb
|