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.
- checksums.yaml +4 -4
- data/README.md +41 -0
- data/exe/type-guessr +30 -0
- data/lib/ruby_lsp/type_guessr/addon.rb +20 -45
- data/lib/ruby_lsp/type_guessr/code_index_adapter.rb +352 -0
- data/lib/ruby_lsp/type_guessr/constants.rb +39 -0
- data/lib/ruby_lsp/type_guessr/{graph_builder.rb → debug_graph_builder.rb} +27 -22
- data/lib/ruby_lsp/type_guessr/debug_server.rb +20 -17
- 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 +129 -261
- data/lib/ruby_lsp/type_guessr/rails_server_addon.rb +83 -0
- data/lib/ruby_lsp/type_guessr/runtime_adapter.rb +613 -277
- data/lib/ruby_lsp/type_guessr/type_inferrer.rb +8 -105
- data/lib/type-guessr.rb +3 -11
- data/lib/type_guessr/core/cache/gem_dependency_resolver.rb +113 -0
- data/lib/type_guessr/core/cache/gem_signature_cache.rb +98 -0
- data/lib/type_guessr/core/cache/gem_signature_extractor.rb +87 -0
- data/lib/type_guessr/core/cache.rb +5 -0
- data/lib/{ruby_lsp/type_guessr → type_guessr/core}/config.rb +19 -34
- 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 +154 -1613
- data/lib/type_guessr/core/converter/rbs_converter.rb +35 -14
- 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/location_index.rb +32 -0
- data/lib/type_guessr/core/index.rb +3 -0
- data/lib/type_guessr/core/inference/resolver.rb +516 -349
- data/lib/type_guessr/core/inference.rb +4 -0
- data/lib/type_guessr/core/ir/nodes.rb +362 -103
- data/lib/type_guessr/core/ir.rb +3 -0
- data/lib/type_guessr/core/logger.rb +6 -13
- data/lib/type_guessr/core/node_context_helper.rb +126 -0
- data/lib/type_guessr/core/node_key_generator.rb +31 -0
- data/lib/type_guessr/core/registry/class_variable_registry.rb +63 -0
- data/lib/type_guessr/core/registry/instance_variable_registry.rb +84 -0
- data/lib/type_guessr/core/registry/method_registry.rb +65 -38
- data/lib/type_guessr/core/registry/signature_registry.rb +543 -0
- data/lib/type_guessr/core/registry.rb +6 -0
- data/lib/type_guessr/core/signature_builder.rb +39 -0
- data/lib/type_guessr/core/type_serializer.rb +96 -0
- data/lib/type_guessr/core/type_simplifier.rb +15 -12
- data/lib/type_guessr/core/types.rb +250 -32
- data/lib/type_guessr/core.rb +29 -0
- data/lib/type_guessr/mcp/file_watcher.rb +87 -0
- data/lib/type_guessr/mcp/server.rb +463 -0
- data/lib/type_guessr/mcp/standalone_runtime.rb +213 -0
- data/lib/type_guessr/version.rb +1 -1
- metadata +57 -8
- data/lib/type_guessr/core/rbs_provider.rb +0 -304
- data/lib/type_guessr/core/registry/variable_registry.rb +0 -87
- data/lib/type_guessr/core/signature_provider.rb +0 -101
|
@@ -4,7 +4,7 @@ module RubyLsp
|
|
|
4
4
|
module TypeGuessr
|
|
5
5
|
# Builds graph data from IR nodes for visualization
|
|
6
6
|
# Uses body_nodes structure with value/receiver/args edges
|
|
7
|
-
class
|
|
7
|
+
class DebugGraphBuilder
|
|
8
8
|
def initialize(runtime_adapter)
|
|
9
9
|
@runtime_adapter = runtime_adapter
|
|
10
10
|
end
|
|
@@ -41,9 +41,7 @@ module RubyLsp
|
|
|
41
41
|
# Limit to MAX_NODES to prevent infinite/huge graphs
|
|
42
42
|
MAX_NODES = 200
|
|
43
43
|
|
|
44
|
-
private
|
|
45
|
-
|
|
46
|
-
def build_def_node_graph(def_node, def_key)
|
|
44
|
+
private def build_def_node_graph(def_node, def_key)
|
|
47
45
|
method_scope = @scope_id.empty? ? "##{def_node.name}" : "#{@scope_id}##{def_node.name}"
|
|
48
46
|
|
|
49
47
|
# 1. Add DefNode
|
|
@@ -72,7 +70,7 @@ module RubyLsp
|
|
|
72
70
|
@nodes[return_key] = {
|
|
73
71
|
key: return_key,
|
|
74
72
|
type: "Return",
|
|
75
|
-
|
|
73
|
+
offset: def_node.loc,
|
|
76
74
|
inferred_type: infer_type_str(def_node),
|
|
77
75
|
details: { virtual: true }
|
|
78
76
|
}
|
|
@@ -84,7 +82,7 @@ module RubyLsp
|
|
|
84
82
|
add_edge(return_key, def_key)
|
|
85
83
|
end
|
|
86
84
|
|
|
87
|
-
def process_body_node(node, scope_id, return_sources)
|
|
85
|
+
private def process_body_node(node, scope_id, return_sources)
|
|
88
86
|
return unless node
|
|
89
87
|
return if @nodes.size >= MAX_NODES
|
|
90
88
|
|
|
@@ -137,6 +135,11 @@ module RubyLsp
|
|
|
137
135
|
branch_key = process_body_node(branch, scope_id, return_sources)
|
|
138
136
|
add_edge(branch_key, node_key) if branch_key
|
|
139
137
|
end
|
|
138
|
+
when ::TypeGuessr::Core::IR::OrNode
|
|
139
|
+
[node.lhs, node.rhs].each do |child|
|
|
140
|
+
child_key = process_body_node(child, scope_id, return_sources)
|
|
141
|
+
add_edge(child_key, node_key) if child_key
|
|
142
|
+
end
|
|
140
143
|
when ::TypeGuessr::Core::IR::LiteralNode
|
|
141
144
|
# Process internal values (for Hash/Array literals with expressions) # -- values is an Array attribute, not Hash#values
|
|
142
145
|
node.values&.each do |value_node|
|
|
@@ -149,7 +152,7 @@ module RubyLsp
|
|
|
149
152
|
node_key
|
|
150
153
|
end
|
|
151
154
|
|
|
152
|
-
def add_node(node, scope_id = @scope_id)
|
|
155
|
+
private def add_node(node, scope_id = @scope_id)
|
|
153
156
|
node_key = node.node_key(scope_id)
|
|
154
157
|
return node_key if @nodes.key?(node_key)
|
|
155
158
|
|
|
@@ -157,20 +160,20 @@ module RubyLsp
|
|
|
157
160
|
node_key
|
|
158
161
|
end
|
|
159
162
|
|
|
160
|
-
def add_edge(from_key, to_key)
|
|
163
|
+
private def add_edge(from_key, to_key)
|
|
161
164
|
return unless from_key && to_key
|
|
162
165
|
|
|
163
166
|
edge = { from: from_key, to: to_key }
|
|
164
167
|
@edges << edge unless @edges.include?(edge)
|
|
165
168
|
end
|
|
166
169
|
|
|
167
|
-
def infer_type_str(node)
|
|
170
|
+
private def infer_type_str(node)
|
|
168
171
|
result = @runtime_adapter.infer_type(node)
|
|
169
172
|
result.type.to_s
|
|
170
173
|
end
|
|
171
174
|
|
|
172
175
|
# BFS traversal for non-DefNode (fallback)
|
|
173
|
-
def traverse_dependencies(node, node_key)
|
|
176
|
+
private def traverse_dependencies(node, node_key)
|
|
174
177
|
visited = Set.new
|
|
175
178
|
queue = [[node, node_key]]
|
|
176
179
|
|
|
@@ -193,12 +196,12 @@ module RubyLsp
|
|
|
193
196
|
|
|
194
197
|
# Extract scope_id from a node key
|
|
195
198
|
# Key format: {scope_id}:{type}:{name}:{line}
|
|
196
|
-
def extract_scope_id(node_key)
|
|
199
|
+
private def extract_scope_id(node_key)
|
|
197
200
|
# For root key like "Class:def:name:line", scope is "Class"
|
|
198
201
|
# For "Class#method:type:name:line", scope is "Class#method"
|
|
199
202
|
# Find the last occurrence of known type prefixes
|
|
200
|
-
type_prefixes = %w[def: param: call: lit: local_write: local_read: ivar_write: ivar_read:
|
|
201
|
-
self:]
|
|
203
|
+
type_prefixes = %w[def: param: call: lit: local_write: local_read: ivar_write: ivar_read:
|
|
204
|
+
cvar_write: cvar_read: merge: or: const: bparam: return: class: self:]
|
|
202
205
|
last_type_pos = nil
|
|
203
206
|
|
|
204
207
|
type_prefixes.each do |prefix|
|
|
@@ -226,27 +229,27 @@ module RubyLsp
|
|
|
226
229
|
end
|
|
227
230
|
|
|
228
231
|
# Serialize a node to hash format
|
|
229
|
-
def serialize_node(node, node_key)
|
|
230
|
-
warn("[
|
|
232
|
+
private def serialize_node(node, node_key)
|
|
233
|
+
warn("[DebugGraphBuilder] serialize_node: #{node_key}") if ::TypeGuessr::Core::Config.debug?
|
|
231
234
|
result = @runtime_adapter.infer_type(node)
|
|
232
|
-
warn("[
|
|
235
|
+
warn("[DebugGraphBuilder] infer_type done for: #{node_key}") if ::TypeGuessr::Core::Config.debug?
|
|
233
236
|
|
|
234
237
|
{
|
|
235
238
|
key: node_key,
|
|
236
239
|
type: node_type_name(node),
|
|
237
|
-
|
|
240
|
+
offset: node.loc,
|
|
238
241
|
inferred_type: result.type.to_s,
|
|
239
242
|
details: extract_details(node, node_key)
|
|
240
243
|
}
|
|
241
244
|
end
|
|
242
245
|
|
|
243
246
|
# Get the short type name for a node
|
|
244
|
-
def node_type_name(node)
|
|
245
|
-
node.class.name
|
|
247
|
+
private def node_type_name(node)
|
|
248
|
+
::TypeGuessr::Core::IR.extract_last_name(node.class.name)
|
|
246
249
|
end
|
|
247
250
|
|
|
248
251
|
# Extract details based on node type
|
|
249
|
-
def extract_details(node, node_key)
|
|
252
|
+
private def extract_details(node, node_key)
|
|
250
253
|
case node
|
|
251
254
|
when ::TypeGuessr::Core::IR::DefNode
|
|
252
255
|
# Build full method signature with param types
|
|
@@ -288,6 +291,8 @@ module RubyLsp
|
|
|
288
291
|
{ literal_type: node.type.to_s }
|
|
289
292
|
when ::TypeGuessr::Core::IR::MergeNode
|
|
290
293
|
{ branches_count: node.branches.size }
|
|
294
|
+
when ::TypeGuessr::Core::IR::OrNode
|
|
295
|
+
{ kind: "or" }
|
|
291
296
|
when ::TypeGuessr::Core::IR::ConstantNode
|
|
292
297
|
{ name: node.name.to_s }
|
|
293
298
|
when ::TypeGuessr::Core::IR::BlockParamSlot
|
|
@@ -302,7 +307,7 @@ module RubyLsp
|
|
|
302
307
|
end
|
|
303
308
|
|
|
304
309
|
# Format receiver node for display
|
|
305
|
-
def format_receiver(receiver)
|
|
310
|
+
private def format_receiver(receiver)
|
|
306
311
|
return nil unless receiver
|
|
307
312
|
|
|
308
313
|
case receiver
|
|
@@ -321,7 +326,7 @@ module RubyLsp
|
|
|
321
326
|
|
|
322
327
|
# Infer node key for a dependency
|
|
323
328
|
# Uses parent node info to determine correct scope for children
|
|
324
|
-
def infer_dependency_key(dep_node, parent_key, parent_node = nil)
|
|
329
|
+
private def infer_dependency_key(dep_node, parent_key, parent_node = nil)
|
|
325
330
|
# Extract scope_id from parent key
|
|
326
331
|
# Key format: {scope_id}:{type}:{name}:{line}
|
|
327
332
|
# scope_id can contain :: for namespaces and # for methods
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require "socket"
|
|
4
4
|
require "json"
|
|
5
5
|
require "cgi"
|
|
6
|
-
require_relative "
|
|
6
|
+
require_relative "debug_graph_builder"
|
|
7
7
|
|
|
8
8
|
module RubyLsp
|
|
9
9
|
module TypeGuessr
|
|
@@ -16,7 +16,7 @@ module RubyLsp
|
|
|
16
16
|
def initialize(global_state, runtime_adapter, port: DEFAULT_PORT)
|
|
17
17
|
@global_state = global_state
|
|
18
18
|
@runtime_adapter = runtime_adapter
|
|
19
|
-
@graph_builder =
|
|
19
|
+
@graph_builder = DebugGraphBuilder.new(runtime_adapter)
|
|
20
20
|
@port = port
|
|
21
21
|
@server = nil
|
|
22
22
|
@thread = nil
|
|
@@ -38,9 +38,7 @@ module RubyLsp
|
|
|
38
38
|
@server = nil
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
private
|
|
42
|
-
|
|
43
|
-
def run_server
|
|
41
|
+
private def run_server
|
|
44
42
|
@server = TCPServer.new("127.0.0.1", @port)
|
|
45
43
|
warn("[TypeGuessr DebugServer] Listening on 127.0.0.1:#{@port}")
|
|
46
44
|
|
|
@@ -63,7 +61,7 @@ module RubyLsp
|
|
|
63
61
|
warn("[TypeGuessr DebugServer] #{e.backtrace&.first(5)&.join("\n")}")
|
|
64
62
|
end
|
|
65
63
|
|
|
66
|
-
def handle_request(client)
|
|
64
|
+
private def handle_request(client)
|
|
67
65
|
request_line = client.gets
|
|
68
66
|
return client.close if request_line.nil?
|
|
69
67
|
|
|
@@ -85,7 +83,7 @@ module RubyLsp
|
|
|
85
83
|
client.close
|
|
86
84
|
end
|
|
87
85
|
|
|
88
|
-
def parse_query_string(query_string)
|
|
86
|
+
private def parse_query_string(query_string)
|
|
89
87
|
return {} unless query_string
|
|
90
88
|
|
|
91
89
|
query_string.split("&").to_h do |pair|
|
|
@@ -94,7 +92,7 @@ module RubyLsp
|
|
|
94
92
|
end
|
|
95
93
|
end
|
|
96
94
|
|
|
97
|
-
def route_request(path, params)
|
|
95
|
+
private def route_request(path, params)
|
|
98
96
|
case path
|
|
99
97
|
when "/"
|
|
100
98
|
index_page
|
|
@@ -111,7 +109,7 @@ module RubyLsp
|
|
|
111
109
|
end
|
|
112
110
|
end
|
|
113
111
|
|
|
114
|
-
def send_response(client, response)
|
|
112
|
+
private def send_response(client, response)
|
|
115
113
|
client.print "HTTP/1.1 #{response[:status]}\r\n"
|
|
116
114
|
client.print "Content-Type: #{response[:content_type]}\r\n"
|
|
117
115
|
client.print "Content-Length: #{response[:body].bytesize}\r\n"
|
|
@@ -122,14 +120,14 @@ module RubyLsp
|
|
|
122
120
|
|
|
123
121
|
# API Endpoints
|
|
124
122
|
|
|
125
|
-
def search_api(query)
|
|
123
|
+
private def search_api(query)
|
|
126
124
|
return json_response({ query: query, results: [] }) if query.empty?
|
|
127
125
|
|
|
128
126
|
results = @runtime_adapter.search_project_methods(query)
|
|
129
127
|
json_response({ query: query, results: results })
|
|
130
128
|
end
|
|
131
129
|
|
|
132
|
-
def keys_api(query)
|
|
130
|
+
private def keys_api(query)
|
|
133
131
|
all_keys = @runtime_adapter.instance_variable_get(:@location_index)
|
|
134
132
|
.instance_variable_get(:@key_index).keys
|
|
135
133
|
keys = if query.empty?
|
|
@@ -140,7 +138,7 @@ module RubyLsp
|
|
|
140
138
|
json_response({ query: query, total: all_keys.size, keys: keys })
|
|
141
139
|
end
|
|
142
140
|
|
|
143
|
-
def graph_api(node_key)
|
|
141
|
+
private def graph_api(node_key)
|
|
144
142
|
return json_error("node_key parameter required", 400) if node_key.empty?
|
|
145
143
|
|
|
146
144
|
begin
|
|
@@ -164,7 +162,7 @@ module RubyLsp
|
|
|
164
162
|
end
|
|
165
163
|
end
|
|
166
164
|
|
|
167
|
-
def json_response(data)
|
|
165
|
+
private def json_response(data)
|
|
168
166
|
{
|
|
169
167
|
status: "200 OK",
|
|
170
168
|
content_type: "application/json; charset=utf-8",
|
|
@@ -172,7 +170,7 @@ module RubyLsp
|
|
|
172
170
|
}
|
|
173
171
|
end
|
|
174
172
|
|
|
175
|
-
def json_error(message, status_code)
|
|
173
|
+
private def json_error(message, status_code)
|
|
176
174
|
status_text = status_code == 404 ? "Not Found" : "Bad Request"
|
|
177
175
|
{
|
|
178
176
|
status: "#{status_code} #{status_text}",
|
|
@@ -181,7 +179,7 @@ module RubyLsp
|
|
|
181
179
|
}
|
|
182
180
|
end
|
|
183
181
|
|
|
184
|
-
def not_found
|
|
182
|
+
private def not_found
|
|
185
183
|
{
|
|
186
184
|
status: "404 Not Found",
|
|
187
185
|
content_type: "text/html; charset=utf-8",
|
|
@@ -191,7 +189,7 @@ module RubyLsp
|
|
|
191
189
|
|
|
192
190
|
# HTML Pages
|
|
193
191
|
|
|
194
|
-
def index_page
|
|
192
|
+
private def index_page
|
|
195
193
|
{
|
|
196
194
|
status: "200 OK",
|
|
197
195
|
content_type: "text/html; charset=utf-8",
|
|
@@ -337,7 +335,7 @@ module RubyLsp
|
|
|
337
335
|
}
|
|
338
336
|
end
|
|
339
337
|
|
|
340
|
-
def graph_page(node_key)
|
|
338
|
+
private def graph_page(node_key)
|
|
341
339
|
return not_found if node_key.empty?
|
|
342
340
|
|
|
343
341
|
{
|
|
@@ -808,6 +806,10 @@ module RubyLsp
|
|
|
808
806
|
return escapeForMermaid(`Merge (${d.branches_count} branches)\\n(L${node.line})`);
|
|
809
807
|
}
|
|
810
808
|
|
|
809
|
+
if (node.type === 'OrNode') {
|
|
810
|
+
return escapeForMermaid(`Or (||)\\n(L${node.line})`);
|
|
811
|
+
}
|
|
812
|
+
|
|
811
813
|
// Default format for other nodes
|
|
812
814
|
let label = node.type;
|
|
813
815
|
if (d.name) label += `: ${d.name}`;
|
|
@@ -838,6 +840,7 @@ module RubyLsp
|
|
|
838
840
|
ParamNode: 'paramNode',
|
|
839
841
|
LiteralNode: 'literalNode',
|
|
840
842
|
MergeNode: 'mergeNode',
|
|
843
|
+
OrNode: 'mergeNode',
|
|
841
844
|
BlockParamSlot: 'blockParamNode'
|
|
842
845
|
};
|
|
843
846
|
return styles[nodeType] || 'otherNode';
|