rubydex 0.1.0.beta12-aarch64-linux

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 (109) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +23 -0
  3. data/README.md +125 -0
  4. data/THIRD_PARTY_LICENSES.html +4562 -0
  5. data/exe/rdx +47 -0
  6. data/ext/rubydex/declaration.c +453 -0
  7. data/ext/rubydex/declaration.h +23 -0
  8. data/ext/rubydex/definition.c +284 -0
  9. data/ext/rubydex/definition.h +28 -0
  10. data/ext/rubydex/diagnostic.c +6 -0
  11. data/ext/rubydex/diagnostic.h +11 -0
  12. data/ext/rubydex/document.c +97 -0
  13. data/ext/rubydex/document.h +10 -0
  14. data/ext/rubydex/extconf.rb +138 -0
  15. data/ext/rubydex/graph.c +681 -0
  16. data/ext/rubydex/graph.h +10 -0
  17. data/ext/rubydex/handle.h +44 -0
  18. data/ext/rubydex/location.c +22 -0
  19. data/ext/rubydex/location.h +15 -0
  20. data/ext/rubydex/reference.c +123 -0
  21. data/ext/rubydex/reference.h +15 -0
  22. data/ext/rubydex/rubydex.c +22 -0
  23. data/ext/rubydex/utils.c +108 -0
  24. data/ext/rubydex/utils.h +34 -0
  25. data/lib/rubydex/3.2/rubydex.so +0 -0
  26. data/lib/rubydex/3.3/rubydex.so +0 -0
  27. data/lib/rubydex/3.4/rubydex.so +0 -0
  28. data/lib/rubydex/4.0/rubydex.so +0 -0
  29. data/lib/rubydex/comment.rb +17 -0
  30. data/lib/rubydex/diagnostic.rb +21 -0
  31. data/lib/rubydex/failures.rb +15 -0
  32. data/lib/rubydex/graph.rb +98 -0
  33. data/lib/rubydex/keyword.rb +17 -0
  34. data/lib/rubydex/keyword_parameter.rb +13 -0
  35. data/lib/rubydex/librubydex_sys.so +0 -0
  36. data/lib/rubydex/location.rb +90 -0
  37. data/lib/rubydex/mixin.rb +22 -0
  38. data/lib/rubydex/version.rb +5 -0
  39. data/lib/rubydex.rb +23 -0
  40. data/rbi/rubydex.rbi +422 -0
  41. data/rust/Cargo.lock +1851 -0
  42. data/rust/Cargo.toml +29 -0
  43. data/rust/about.hbs +78 -0
  44. data/rust/about.toml +10 -0
  45. data/rust/rubydex/Cargo.toml +42 -0
  46. data/rust/rubydex/src/compile_assertions.rs +13 -0
  47. data/rust/rubydex/src/diagnostic.rs +110 -0
  48. data/rust/rubydex/src/errors.rs +28 -0
  49. data/rust/rubydex/src/indexing/local_graph.rs +224 -0
  50. data/rust/rubydex/src/indexing/rbs_indexer.rs +1551 -0
  51. data/rust/rubydex/src/indexing/ruby_indexer.rs +2329 -0
  52. data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +4962 -0
  53. data/rust/rubydex/src/indexing.rs +210 -0
  54. data/rust/rubydex/src/integrity.rs +279 -0
  55. data/rust/rubydex/src/job_queue.rs +205 -0
  56. data/rust/rubydex/src/lib.rs +17 -0
  57. data/rust/rubydex/src/listing.rs +371 -0
  58. data/rust/rubydex/src/main.rs +160 -0
  59. data/rust/rubydex/src/model/built_in.rs +83 -0
  60. data/rust/rubydex/src/model/comment.rs +24 -0
  61. data/rust/rubydex/src/model/declaration.rs +671 -0
  62. data/rust/rubydex/src/model/definitions.rs +1682 -0
  63. data/rust/rubydex/src/model/document.rs +222 -0
  64. data/rust/rubydex/src/model/encoding.rs +22 -0
  65. data/rust/rubydex/src/model/graph.rs +3754 -0
  66. data/rust/rubydex/src/model/id.rs +110 -0
  67. data/rust/rubydex/src/model/identity_maps.rs +58 -0
  68. data/rust/rubydex/src/model/ids.rs +60 -0
  69. data/rust/rubydex/src/model/keywords.rs +256 -0
  70. data/rust/rubydex/src/model/name.rs +298 -0
  71. data/rust/rubydex/src/model/references.rs +111 -0
  72. data/rust/rubydex/src/model/string_ref.rs +50 -0
  73. data/rust/rubydex/src/model/visibility.rs +41 -0
  74. data/rust/rubydex/src/model.rs +15 -0
  75. data/rust/rubydex/src/offset.rs +147 -0
  76. data/rust/rubydex/src/position.rs +6 -0
  77. data/rust/rubydex/src/query.rs +1841 -0
  78. data/rust/rubydex/src/resolution.rs +6517 -0
  79. data/rust/rubydex/src/stats/memory.rs +71 -0
  80. data/rust/rubydex/src/stats/orphan_report.rs +264 -0
  81. data/rust/rubydex/src/stats/timer.rs +127 -0
  82. data/rust/rubydex/src/stats.rs +11 -0
  83. data/rust/rubydex/src/test_utils/context.rs +226 -0
  84. data/rust/rubydex/src/test_utils/graph_test.rs +730 -0
  85. data/rust/rubydex/src/test_utils/local_graph_test.rs +602 -0
  86. data/rust/rubydex/src/test_utils.rs +52 -0
  87. data/rust/rubydex/src/visualization/dot.rs +192 -0
  88. data/rust/rubydex/src/visualization.rs +6 -0
  89. data/rust/rubydex/tests/cli.rs +185 -0
  90. data/rust/rubydex-mcp/Cargo.toml +28 -0
  91. data/rust/rubydex-mcp/src/main.rs +48 -0
  92. data/rust/rubydex-mcp/src/server.rs +1145 -0
  93. data/rust/rubydex-mcp/src/tools.rs +49 -0
  94. data/rust/rubydex-mcp/tests/mcp.rs +302 -0
  95. data/rust/rubydex-sys/Cargo.toml +20 -0
  96. data/rust/rubydex-sys/build.rs +14 -0
  97. data/rust/rubydex-sys/cbindgen.toml +12 -0
  98. data/rust/rubydex-sys/src/declaration_api.rs +485 -0
  99. data/rust/rubydex-sys/src/definition_api.rs +443 -0
  100. data/rust/rubydex-sys/src/diagnostic_api.rs +99 -0
  101. data/rust/rubydex-sys/src/document_api.rs +85 -0
  102. data/rust/rubydex-sys/src/graph_api.rs +948 -0
  103. data/rust/rubydex-sys/src/lib.rs +79 -0
  104. data/rust/rubydex-sys/src/location_api.rs +79 -0
  105. data/rust/rubydex-sys/src/name_api.rs +135 -0
  106. data/rust/rubydex-sys/src/reference_api.rs +267 -0
  107. data/rust/rubydex-sys/src/utils.rs +70 -0
  108. data/rust/rustfmt.toml +2 -0
  109. metadata +159 -0
@@ -0,0 +1,681 @@
1
+ #include "graph.h"
2
+ #include "declaration.h"
3
+ #include "diagnostic.h"
4
+ #include "document.h"
5
+ #include "location.h"
6
+ #include "reference.h"
7
+ #include "ruby/internal/globals.h"
8
+ #include "rustbindings.h"
9
+ #include "utils.h"
10
+
11
+ static VALUE cGraph;
12
+ static VALUE mRubydex;
13
+ static VALUE cKeyword;
14
+ static VALUE cKeywordParameter;
15
+
16
+ // Free function for the custom Graph allocator. We always have to call into Rust to free data allocated by it
17
+ static void graph_free(void *ptr) {
18
+ if (ptr) {
19
+ rdx_graph_free(ptr);
20
+ }
21
+ }
22
+
23
+ const rb_data_type_t graph_type = {"Graph", {0, graph_free, 0}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY};
24
+
25
+ // Custom allocator for the Graph class. Calls into Rust to create a new `Arc<Mutex<Graph>>` that gets stored internally
26
+ // as a void pointer
27
+ static VALUE rdxr_graph_alloc(VALUE klass) {
28
+ void *graph = rdx_graph_new();
29
+ return TypedData_Wrap_Struct(klass, &graph_type, graph);
30
+ }
31
+
32
+ // Graph#index_all: (Array[String] file_paths) -> Array[String]
33
+ // Returns an array of IO error messages encountered during indexing
34
+ static VALUE rdxr_graph_index_all(VALUE self, VALUE file_paths) {
35
+ rdxi_check_array_of_strings(file_paths);
36
+
37
+ // Convert the given file paths into a char** array, so that we can pass to Rust
38
+ size_t length = RARRAY_LEN(file_paths);
39
+ char **converted_file_paths = rdxi_str_array_to_char(file_paths, length);
40
+
41
+ // Get the underlying graph pointer and then invoke the Rust index all implementation
42
+ void *graph;
43
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
44
+
45
+ size_t error_count = 0;
46
+ const char *const *errors = rdx_index_all(graph, (const char **)converted_file_paths, length, &error_count);
47
+
48
+ rdxi_free_str_array(converted_file_paths, length);
49
+
50
+ if (errors == NULL) {
51
+ return rb_ary_new();
52
+ }
53
+
54
+ VALUE array = rb_ary_new_capa((long)error_count);
55
+ for (size_t i = 0; i < error_count; i++) {
56
+ rb_ary_push(array, rb_utf8_str_new_cstr(errors[i]));
57
+ }
58
+
59
+ free_c_string_array(errors, error_count);
60
+ return array;
61
+ }
62
+
63
+ // Indexes a single source string in memory, dispatching to the appropriate indexer based on language_id
64
+ //
65
+ // Graph#index_source: (String uri, String source, String language_id) -> void
66
+ static VALUE rdxr_graph_index_source(VALUE self, VALUE uri, VALUE source, VALUE language_id) {
67
+ Check_Type(uri, T_STRING);
68
+ Check_Type(source, T_STRING);
69
+ Check_Type(language_id, T_STRING);
70
+
71
+ void *graph;
72
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
73
+
74
+ const char *uri_str = StringValueCStr(uri);
75
+ const char *language_id_str = StringValueCStr(language_id);
76
+ const char *source_str = RSTRING_PTR(source);
77
+ size_t source_len = RSTRING_LEN(source);
78
+
79
+ enum IndexSourceResult result = rdx_index_source(graph, uri_str, source_str, source_len, language_id_str);
80
+ switch (result) {
81
+ case IndexSourceResult_Success:
82
+ break;
83
+ case IndexSourceResult_InvalidUri:
84
+ rb_raise(rb_eArgError, "invalid URI (not valid UTF-8)");
85
+ break;
86
+ case IndexSourceResult_InvalidSource:
87
+ rb_raise(rb_eArgError, "source is not valid UTF-8");
88
+ break;
89
+ case IndexSourceResult_InvalidLanguageId:
90
+ rb_raise(rb_eArgError, "invalid language_id (not valid UTF-8)");
91
+ break;
92
+ case IndexSourceResult_UnsupportedLanguageId:
93
+ rb_raise(rb_eArgError, "unsupported language_id `%s`", language_id_str);
94
+ break;
95
+ }
96
+
97
+ return Qnil;
98
+ }
99
+
100
+ // Size function for the declarations enumerator
101
+ static VALUE graph_declarations_size(VALUE self, VALUE _args, VALUE _eobj) {
102
+ void *graph;
103
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
104
+
105
+ DeclarationsIter *iter = rdx_graph_declarations_iter_new(graph);
106
+ size_t len = rdx_graph_declarations_iter_len(iter);
107
+ rdx_graph_declarations_iter_free(iter);
108
+
109
+ return SIZET2NUM(len);
110
+ }
111
+
112
+ // Graph#declarations: () -> Enumerator[Declaration]
113
+ // Returns an enumerator that yields all declarations lazily
114
+ static VALUE rdxr_graph_declarations(VALUE self) {
115
+ if (!rb_block_given_p()) {
116
+ return rb_enumeratorize_with_size(self, rb_str_new2("declarations"), 0, NULL, graph_declarations_size);
117
+ }
118
+
119
+ void *graph;
120
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
121
+
122
+ void *iter = rdx_graph_declarations_iter_new(graph);
123
+ VALUE args = rb_ary_new_from_args(2, self, ULL2NUM((uintptr_t)iter));
124
+ rb_ensure(rdxi_declarations_yield, args, rdxi_declarations_ensure, args);
125
+
126
+ return self;
127
+ }
128
+
129
+ static VALUE rdxr_graph_yield_search_results(VALUE self, void *iter) {
130
+ if (iter == NULL) {
131
+ // The only case where the iterator will be NULL instead of a list is if the query cannot be converted to a Rust
132
+ // string
133
+ rb_raise(rb_eRuntimeError, "Converting query to Rust string failed");
134
+ }
135
+
136
+ VALUE args = rb_ary_new_from_args(2, self, ULL2NUM((uintptr_t)iter));
137
+ rb_ensure(rdxi_declarations_yield, args, rdxi_declarations_ensure, args);
138
+
139
+ return self;
140
+ }
141
+
142
+ // Graph#search: (String query) -> Enumerator[Declaration]
143
+ // Returns an enumerator that yields declarations matching the query exactly (substring match)
144
+ static VALUE rdxr_graph_search(VALUE self, VALUE query) {
145
+ Check_Type(query, T_STRING);
146
+
147
+ if (!rb_block_given_p()) {
148
+ return rb_enumeratorize(self, rb_str_new2("search"), 1, &query);
149
+ }
150
+
151
+ void *graph;
152
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
153
+
154
+ return rdxr_graph_yield_search_results(self, rdx_graph_declarations_search(graph, StringValueCStr(query)));
155
+ }
156
+
157
+ // Graph#fuzzy_search: (String query) -> Enumerator[Declaration]
158
+ // Returns an enumerator that yields declarations matching the query fuzzily
159
+ static VALUE rdxr_graph_fuzzy_search(VALUE self, VALUE query) {
160
+ Check_Type(query, T_STRING);
161
+
162
+ if (!rb_block_given_p()) {
163
+ return rb_enumeratorize(self, rb_str_new2("fuzzy_search"), 1, &query);
164
+ }
165
+
166
+ void *graph;
167
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
168
+
169
+ return rdxr_graph_yield_search_results(self, rdx_graph_declarations_fuzzy_search(graph, StringValueCStr(query)));
170
+ }
171
+
172
+ // Body function for rb_ensure in Graph#documents
173
+ static VALUE graph_documents_yield(VALUE args) {
174
+ VALUE self = rb_ary_entry(args, 0);
175
+ void *iter = (void *)(uintptr_t)NUM2ULL(rb_ary_entry(args, 1));
176
+
177
+ uint64_t id = 0;
178
+ while (rdx_graph_documents_iter_next(iter, &id)) {
179
+ VALUE argv[] = {self, ULL2NUM(id)};
180
+ VALUE handle = rb_class_new_instance(2, argv, cDocument);
181
+ rb_yield(handle);
182
+ }
183
+
184
+ return Qnil;
185
+ }
186
+
187
+ // Ensure function for rb_ensure in Graph#documents to always free the iterator
188
+ static VALUE graph_documents_ensure(VALUE args) {
189
+ void *iter = (void *)(uintptr_t)NUM2ULL(rb_ary_entry(args, 1));
190
+ rdx_graph_documents_iter_free(iter);
191
+
192
+ return Qnil;
193
+ }
194
+
195
+ // Size function for the documents enumerator
196
+ static VALUE graph_documents_size(VALUE self, VALUE _args, VALUE _eobj) {
197
+ void *graph;
198
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
199
+
200
+ DocumentsIter *iter = rdx_graph_documents_iter_new(graph);
201
+ size_t len = rdx_graph_documents_iter_len(iter);
202
+ rdx_graph_documents_iter_free(iter);
203
+
204
+ return SIZET2NUM(len);
205
+ }
206
+
207
+ // Graph#documents: () -> Enumerator[Document]
208
+ // Returns an enumerator that yields all documents lazily
209
+ static VALUE rdxr_graph_documents(VALUE self) {
210
+ if (!rb_block_given_p()) {
211
+ return rb_enumeratorize_with_size(self, rb_str_new2("documents"), 0, NULL, graph_documents_size);
212
+ }
213
+
214
+ void *graph;
215
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
216
+
217
+ void *iter = rdx_graph_documents_iter_new(graph);
218
+ VALUE args = rb_ary_new_from_args(2, self, ULL2NUM((uintptr_t)iter));
219
+ rb_ensure(graph_documents_yield, args, graph_documents_ensure, args);
220
+
221
+ return self;
222
+ }
223
+
224
+ // Graph#[]: (String fully_qualified_name) -> Declaration
225
+ // Returns a declaration handle for the given ID
226
+ static VALUE rdxr_graph_aref(VALUE self, VALUE key) {
227
+ void *graph;
228
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
229
+
230
+ if (TYPE(key) != T_STRING) {
231
+ rb_raise(rb_eTypeError, "expected String");
232
+ }
233
+
234
+ const CDeclaration *decl = rdx_graph_get_declaration(graph, StringValueCStr(key));
235
+ if (decl == NULL) {
236
+ return Qnil;
237
+ }
238
+
239
+ VALUE decl_class = rdxi_declaration_class_for_kind(decl->kind);
240
+ VALUE argv[] = {self, ULL2NUM(decl->id)};
241
+ free_c_declaration(decl);
242
+
243
+ return rb_class_new_instance(2, argv, decl_class);
244
+ }
245
+
246
+ // Size function for the constant_references enumerator
247
+ static VALUE graph_constant_references_size(VALUE self, VALUE _args, VALUE _eobj) {
248
+ void *graph;
249
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
250
+
251
+ struct ConstantReferencesIter *iter = rdx_graph_constant_references_iter_new(graph);
252
+ size_t len = rdx_constant_references_iter_len(iter);
253
+ rdx_constant_references_iter_free(iter);
254
+
255
+ return SIZET2NUM(len);
256
+ }
257
+
258
+ // Graph#constant_references: () -> Enumerator[ConstantReference]
259
+ // Returns an enumerator that yields constant references lazily
260
+ static VALUE rdxr_graph_constant_references(VALUE self) {
261
+ if (!rb_block_given_p()) {
262
+ return rb_enumeratorize_with_size(self, rb_str_new2("constant_references"), 0, NULL,
263
+ graph_constant_references_size);
264
+ }
265
+
266
+ void *graph;
267
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
268
+
269
+ void *iter = rdx_graph_constant_references_iter_new(graph);
270
+ VALUE args = rb_ary_new_from_args(2, self, ULL2NUM((uintptr_t)iter));
271
+ rb_ensure(rdxi_constant_references_yield, args, rdxi_constant_references_ensure, args);
272
+
273
+ return self;
274
+ }
275
+
276
+ // Size function for the method_references enumerator
277
+ static VALUE graph_method_references_size(VALUE self, VALUE _args, VALUE _eobj) {
278
+ void *graph;
279
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
280
+
281
+ struct MethodReferencesIter *iter = rdx_graph_method_references_iter_new(graph);
282
+ size_t len = rdx_method_references_iter_len(iter);
283
+ rdx_method_references_iter_free(iter);
284
+
285
+ return SIZET2NUM(len);
286
+ }
287
+
288
+ // Graph#method_references: () -> Enumerator[MethodReference]
289
+ // Returns an enumerator that yields method references lazily
290
+ static VALUE rdxr_graph_method_references(VALUE self) {
291
+ if (!rb_block_given_p()) {
292
+ return rb_enumeratorize_with_size(self, rb_str_new2("method_references"), 0, NULL,
293
+ graph_method_references_size);
294
+ }
295
+
296
+ void *graph;
297
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
298
+
299
+ void *iter = rdx_graph_method_references_iter_new(graph);
300
+ VALUE args = rb_ary_new_from_args(2, self, ULL2NUM((uintptr_t)iter));
301
+ rb_ensure(rdxi_method_references_yield, args, rdxi_method_references_ensure, args);
302
+
303
+ return self;
304
+ }
305
+
306
+ // Graph#delete_document: (String uri) -> Document?
307
+ // Deletes a document and all of its definitions from the graph.
308
+ // Returns the removed Document or nil if it doesn't exist.
309
+ static VALUE rdxr_graph_delete_document(VALUE self, VALUE uri) {
310
+ Check_Type(uri, T_STRING);
311
+
312
+ void *graph;
313
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
314
+ const uint64_t *uri_id = rdx_graph_delete_document(graph, StringValueCStr(uri));
315
+
316
+ if (uri_id == NULL) {
317
+ return Qnil;
318
+ }
319
+
320
+ VALUE argv[] = {self, ULL2NUM(*uri_id)};
321
+ free_u64(uri_id);
322
+ return rb_class_new_instance(2, argv, cDocument);
323
+ }
324
+
325
+ // Graph#resolve: () -> self
326
+ // Runs the resolver to compute declarations and ownership
327
+ static VALUE rdxr_graph_resolve(VALUE self) {
328
+ void *graph;
329
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
330
+ rdx_graph_resolve(graph);
331
+ return self;
332
+ }
333
+
334
+ // Graph#encoding=: (String) -> void
335
+ // Sets the encoding used for transforming byte offsets into LSP code unit line/column positions
336
+ static VALUE rdxr_graph_set_encoding(VALUE self, VALUE encoding) {
337
+ Check_Type(encoding, T_STRING);
338
+
339
+ void *graph;
340
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
341
+
342
+ char *encoding_string = StringValueCStr(encoding);
343
+ if (!rdx_graph_set_encoding(graph, encoding_string)) {
344
+ rb_raise(rb_eArgError, "invalid encoding `%s` (should be utf8, utf16 or utf32)", encoding_string);
345
+ }
346
+
347
+ return Qnil;
348
+ }
349
+
350
+ // Graph#resolve_constant: (String, Array[String]) -> Declaration?
351
+ // Runs the resolver on a single constant reference to determine what it points to
352
+ static VALUE rdxr_graph_resolve_constant(VALUE self, VALUE const_name, VALUE nesting) {
353
+ Check_Type(const_name, T_STRING);
354
+ rdxi_check_array_of_strings(nesting);
355
+
356
+ // Convert the given file paths into a char** array, so that we can pass to Rust
357
+ size_t length = RARRAY_LEN(nesting);
358
+ char **converted_file_paths = rdxi_str_array_to_char(nesting, length);
359
+
360
+ void *graph;
361
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
362
+
363
+ const CDeclaration *decl =
364
+ rdx_graph_resolve_constant(graph, StringValueCStr(const_name), (const char **)converted_file_paths, length);
365
+
366
+ rdxi_free_str_array(converted_file_paths, length);
367
+
368
+ if (decl == NULL) {
369
+ return Qnil;
370
+ }
371
+
372
+ VALUE decl_class = rdxi_declaration_class_for_kind(decl->kind);
373
+ VALUE argv[] = {self, ULL2NUM(decl->id)};
374
+ free_c_declaration(decl);
375
+
376
+ return rb_class_new_instance(2, argv, decl_class);
377
+ }
378
+
379
+ // Graph#resolve_require_path: (String require_path, Array[String] load_paths) -> Document?
380
+ // Resolves a require path to its Document.
381
+ static VALUE rdxr_graph_resolve_require_path(VALUE self, VALUE require_path, VALUE load_paths) {
382
+ Check_Type(require_path, T_STRING);
383
+ rdxi_check_array_of_strings(load_paths);
384
+
385
+ void *graph;
386
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
387
+ const char *path_str = StringValueCStr(require_path);
388
+
389
+ size_t paths_len = RARRAY_LEN(load_paths);
390
+ char **converted_paths = rdxi_str_array_to_char(load_paths, paths_len);
391
+
392
+ const uint64_t *uri_id = rdx_resolve_require_path(graph, path_str, (const char **)converted_paths, paths_len);
393
+
394
+ rdxi_free_str_array(converted_paths, paths_len);
395
+
396
+ if (uri_id == NULL) {
397
+ return Qnil;
398
+ }
399
+
400
+ VALUE argv[] = {self, ULL2NUM(*uri_id)};
401
+ free_u64(uri_id);
402
+ return rb_class_new_instance(2, argv, cDocument);
403
+ }
404
+
405
+ // Graph#require_paths: (Array[String] load_path) -> Array[String]
406
+ // Returns all require paths for completion.
407
+ static VALUE rdxr_graph_require_paths(VALUE self, VALUE load_path) {
408
+ rdxi_check_array_of_strings(load_path);
409
+
410
+ void *graph;
411
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
412
+
413
+ size_t paths_len = RARRAY_LEN(load_path);
414
+ char **converted_paths = rdxi_str_array_to_char(load_path, paths_len);
415
+
416
+ size_t out_count = 0;
417
+ const char *const *results = rdx_require_paths(graph, (const char **)converted_paths, paths_len, &out_count);
418
+
419
+ rdxi_free_str_array(converted_paths, paths_len);
420
+
421
+ if (results == NULL) {
422
+ return rb_ary_new();
423
+ }
424
+
425
+ VALUE array = rb_ary_new_capa((long)out_count);
426
+ for (size_t i = 0; i < out_count; i++) {
427
+ rb_ary_push(array, rb_utf8_str_new_cstr(results[i]));
428
+ }
429
+
430
+ free_c_string_array(results, out_count);
431
+ return array;
432
+ }
433
+
434
+ // Graph#check_integrity: () -> Array[Rubydex::IntegrityFailure]
435
+ // Returns an array of IntegrityFailure objects, empty if no issues found
436
+ static VALUE rdxr_graph_check_integrity(VALUE self) {
437
+ void *graph;
438
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
439
+
440
+ size_t error_count = 0;
441
+ const char *const *errors = rdx_check_integrity(graph, &error_count);
442
+
443
+ if (errors == NULL) {
444
+ return rb_ary_new();
445
+ }
446
+
447
+ VALUE cIntegrityError = rb_const_get(mRubydex, rb_intern("IntegrityFailure"));
448
+ VALUE array = rb_ary_new_capa((long)error_count);
449
+
450
+ for (size_t i = 0; i < error_count; i++) {
451
+ VALUE argv[] = {rb_utf8_str_new_cstr(errors[i])};
452
+ VALUE error = rb_class_new_instance(1, argv, cIntegrityError);
453
+ rb_ary_push(array, error);
454
+ }
455
+
456
+ free_c_string_array(errors, error_count);
457
+ return array;
458
+ }
459
+
460
+ // Graph#diagnostics -> Array[Rubydex::Diagnostic]
461
+ static VALUE rdxr_graph_diagnostics(VALUE self) {
462
+ void *graph;
463
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
464
+
465
+ DiagnosticArray *array = rdx_graph_diagnostics(graph);
466
+ if (array == NULL || array->len == 0) {
467
+ if (array != NULL) {
468
+ rdx_diagnostics_free(array);
469
+ }
470
+ return rb_ary_new();
471
+ }
472
+
473
+ VALUE diagnostics = rb_ary_new_capa((long)array->len);
474
+ for (size_t i = 0; i < array->len; i++) {
475
+ DiagnosticEntry entry = array->items[i];
476
+ VALUE message = entry.message == NULL ? Qnil : rb_utf8_str_new_cstr(entry.message);
477
+ VALUE rule = rb_str_new2(entry.rule);
478
+ VALUE location = rdxi_build_location_value(entry.location);
479
+
480
+ VALUE kwargs = rb_hash_new();
481
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("rule")), rule);
482
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("message")), message);
483
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("location")), location);
484
+
485
+ VALUE diagnostic = rb_class_new_instance_kw(1, &kwargs, cDiagnostic, RB_PASS_KEYWORDS);
486
+ rb_ary_push(diagnostics, diagnostic);
487
+ }
488
+
489
+ rdx_diagnostics_free(array);
490
+ return diagnostics;
491
+ }
492
+
493
+ // Helper: convert a CompletionResult into a Ruby array, raising ArgumentError on error.
494
+ static VALUE completion_result_to_ruby_array(struct CompletionResult result, VALUE graph_obj) {
495
+ if (result.error != NULL) {
496
+ VALUE msg = rb_utf8_str_new_cstr(result.error);
497
+ free_c_string(result.error);
498
+ rb_raise(rb_eArgError, "%s", StringValueCStr(msg));
499
+ }
500
+
501
+ CompletionCandidateArray *array = result.candidates;
502
+ if (array == NULL) {
503
+ return rb_ary_new();
504
+ }
505
+
506
+ if (array->len == 0) {
507
+ rdx_completion_candidates_free(array);
508
+ return rb_ary_new();
509
+ }
510
+
511
+ VALUE ruby_array = rb_ary_new_capa((long)array->len);
512
+
513
+ for (size_t i = 0; i < array->len; i++) {
514
+ CCompletionCandidate item = array->items[i];
515
+ VALUE obj;
516
+
517
+ switch (item.kind) {
518
+ case CCompletionCandidateKind_Declaration: {
519
+ VALUE decl_class = rdxi_declaration_class_for_kind(item.declaration->kind);
520
+ VALUE argv[] = {graph_obj, ULL2NUM(item.declaration->id)};
521
+ obj = rb_class_new_instance(2, argv, decl_class);
522
+ break;
523
+ }
524
+ case CCompletionCandidateKind_Keyword: {
525
+ VALUE argv[2] = {
526
+ rb_utf8_str_new_cstr(item.name),
527
+ rb_utf8_str_new_cstr(item.documentation),
528
+ };
529
+ obj = rb_class_new_instance(2, argv, cKeyword);
530
+ break;
531
+ }
532
+ case CCompletionCandidateKind_KeywordParameter: {
533
+ VALUE argv[1] = { rb_utf8_str_new_cstr(item.name) };
534
+ obj = rb_class_new_instance(1, argv, cKeywordParameter);
535
+ break;
536
+ }
537
+ default:
538
+ rdx_completion_candidates_free(array);
539
+ rb_raise(rb_eRuntimeError, "Unknown CCompletionCandidateKind: %d", item.kind);
540
+ }
541
+
542
+ rb_ary_push(ruby_array, obj);
543
+ }
544
+
545
+ rdx_completion_candidates_free(array);
546
+ return ruby_array;
547
+ }
548
+
549
+ // Graph#complete_expression: (Array[String] nesting) -> Array[Declaration | Keyword]
550
+ // Returns completion candidates for an expression context.
551
+ // The nesting array represents the lexical scope stack
552
+ static VALUE rdxr_graph_complete_expression(VALUE self, VALUE nesting) {
553
+ rdxi_check_array_of_strings(nesting);
554
+
555
+ void *graph;
556
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
557
+
558
+ size_t nesting_count = RARRAY_LEN(nesting);
559
+ char **converted_nesting = rdxi_str_array_to_char(nesting, nesting_count);
560
+
561
+ struct CompletionResult result =
562
+ rdx_graph_complete_expression(graph, (const char *const *)converted_nesting, nesting_count);
563
+
564
+ rdxi_free_str_array(converted_nesting, nesting_count);
565
+ return completion_result_to_ruby_array(result, self);
566
+ }
567
+
568
+ // Graph#complete_namespace_access: (String name) -> Array[Declaration]
569
+ // Returns completion candidates after a namespace access operator (e.g., `Foo::`).
570
+ static VALUE rdxr_graph_complete_namespace_access(VALUE self, VALUE name) {
571
+ Check_Type(name, T_STRING);
572
+
573
+ void *graph;
574
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
575
+
576
+ struct CompletionResult result = rdx_graph_complete_namespace_access(graph, StringValueCStr(name));
577
+ return completion_result_to_ruby_array(result, self);
578
+ }
579
+
580
+ // Graph#complete_method_call: (String name) -> Array[Declaration]
581
+ // Returns completion candidates after a method call operator (e.g., `foo.`).
582
+ static VALUE rdxr_graph_complete_method_call(VALUE self, VALUE name) {
583
+ Check_Type(name, T_STRING);
584
+
585
+ void *graph;
586
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
587
+
588
+ struct CompletionResult result = rdx_graph_complete_method_call(graph, StringValueCStr(name));
589
+ return completion_result_to_ruby_array(result, self);
590
+ }
591
+
592
+ // Graph#complete_method_argument: (String name, Array[String] nesting) -> Array[Declaration | Keyword | KeywordParameter]
593
+ // Returns completion candidates inside a method call's argument list (e.g., `foo.bar(|)`).
594
+ static VALUE rdxr_graph_complete_method_argument(VALUE self, VALUE name, VALUE nesting) {
595
+ Check_Type(name, T_STRING);
596
+ rdxi_check_array_of_strings(nesting);
597
+
598
+ void *graph;
599
+ TypedData_Get_Struct(self, void *, &graph_type, graph);
600
+
601
+ size_t nesting_count = RARRAY_LEN(nesting);
602
+ char **converted_nesting = rdxi_str_array_to_char(nesting, nesting_count);
603
+
604
+ struct CompletionResult result = rdx_graph_complete_method_argument(
605
+ graph, StringValueCStr(name), (const char *const *)converted_nesting, nesting_count);
606
+
607
+ rdxi_free_str_array(converted_nesting, nesting_count);
608
+ return completion_result_to_ruby_array(result, self);
609
+ }
610
+
611
+ // Graph#exclude_paths: (Array[String] paths) -> void
612
+ // Excludes the given paths from file discovery during indexing.
613
+ static VALUE rdxr_graph_exclude_paths(VALUE self, VALUE paths) {
614
+ Check_Type(paths, T_ARRAY);
615
+ rdxi_check_array_of_strings(paths);
616
+
617
+ size_t length = RARRAY_LEN(paths);
618
+ char **converted_paths = rdxi_str_array_to_char(paths, length);
619
+
620
+ void *graph;
621
+ TypedData_Get_Struct(self, void*, &graph_type, graph);
622
+
623
+ rdx_graph_exclude_paths(graph, (const char **)converted_paths, length);
624
+ rdxi_free_str_array(converted_paths, length);
625
+
626
+ return Qnil;
627
+ }
628
+
629
+ // Graph#excluded_paths: () -> Array[String]
630
+ // Returns the list of paths currently excluded from file discovery.
631
+ static VALUE rdxr_graph_excluded_paths(VALUE self) {
632
+ void *graph;
633
+ TypedData_Get_Struct(self, void*, &graph_type, graph);
634
+
635
+ size_t out_count = 0;
636
+ const char *const *results = rdx_graph_excluded_paths(graph, &out_count);
637
+
638
+ if (results == NULL) {
639
+ return rb_ary_new();
640
+ }
641
+
642
+ VALUE array = rb_ary_new_capa((long)out_count);
643
+ for (size_t i = 0; i < out_count; i++) {
644
+ rb_ary_push(array, rb_utf8_str_new_cstr(results[i]));
645
+ }
646
+
647
+ free_c_string_array(results, out_count);
648
+ return array;
649
+ }
650
+
651
+ void rdxi_initialize_graph(VALUE moduleRubydex) {
652
+ mRubydex = moduleRubydex;
653
+ cGraph = rb_define_class_under(mRubydex, "Graph", rb_cObject);
654
+ cKeyword = rb_define_class_under(mRubydex, "Keyword", rb_cObject);
655
+ cKeywordParameter = rb_define_class_under(mRubydex, "KeywordParameter", rb_cObject);
656
+
657
+ rb_define_alloc_func(cGraph, rdxr_graph_alloc);
658
+ rb_define_method(cGraph, "index_all", rdxr_graph_index_all, 1);
659
+ rb_define_method(cGraph, "index_source", rdxr_graph_index_source, 3);
660
+ rb_define_method(cGraph, "delete_document", rdxr_graph_delete_document, 1);
661
+ rb_define_method(cGraph, "resolve", rdxr_graph_resolve, 0);
662
+ rb_define_method(cGraph, "resolve_constant", rdxr_graph_resolve_constant, 2);
663
+ rb_define_method(cGraph, "declarations", rdxr_graph_declarations, 0);
664
+ rb_define_method(cGraph, "documents", rdxr_graph_documents, 0);
665
+ rb_define_method(cGraph, "constant_references", rdxr_graph_constant_references, 0);
666
+ rb_define_method(cGraph, "method_references", rdxr_graph_method_references, 0);
667
+ rb_define_method(cGraph, "diagnostics", rdxr_graph_diagnostics, 0);
668
+ rb_define_method(cGraph, "check_integrity", rdxr_graph_check_integrity, 0);
669
+ rb_define_method(cGraph, "[]", rdxr_graph_aref, 1);
670
+ rb_define_method(cGraph, "search", rdxr_graph_search, 1);
671
+ rb_define_method(cGraph, "fuzzy_search", rdxr_graph_fuzzy_search, 1);
672
+ rb_define_method(cGraph, "encoding=", rdxr_graph_set_encoding, 1);
673
+ rb_define_method(cGraph, "resolve_require_path", rdxr_graph_resolve_require_path, 2);
674
+ rb_define_method(cGraph, "require_paths", rdxr_graph_require_paths, 1);
675
+ rb_define_method(cGraph, "complete_expression", rdxr_graph_complete_expression, 1);
676
+ rb_define_method(cGraph, "complete_namespace_access", rdxr_graph_complete_namespace_access, 1);
677
+ rb_define_method(cGraph, "complete_method_call", rdxr_graph_complete_method_call, 1);
678
+ rb_define_method(cGraph, "complete_method_argument", rdxr_graph_complete_method_argument, 2);
679
+ rb_define_method(cGraph, "exclude_paths", rdxr_graph_exclude_paths, 1);
680
+ rb_define_method(cGraph, "excluded_paths", rdxr_graph_excluded_paths, 0);
681
+ }
@@ -0,0 +1,10 @@
1
+ #ifndef RUBYDEX_GRAPH_H
2
+ #define RUBYDEX_GRAPH_H
3
+
4
+ #include "ruby.h"
5
+
6
+ extern const rb_data_type_t graph_type;
7
+
8
+ void rdxi_initialize_graph(VALUE mRubydex);
9
+
10
+ #endif // RUBYDEX_GRAPH_H