rubydex 0.1.0.beta11 → 0.1.0.beta13

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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +23 -23
  3. data/README.md +125 -125
  4. data/THIRD_PARTY_LICENSES.html +2018 -945
  5. data/exe/rdx +47 -47
  6. data/ext/rubydex/declaration.c +453 -388
  7. data/ext/rubydex/declaration.h +23 -23
  8. data/ext/rubydex/definition.c +284 -197
  9. data/ext/rubydex/definition.h +28 -28
  10. data/ext/rubydex/diagnostic.c +6 -6
  11. data/ext/rubydex/diagnostic.h +11 -11
  12. data/ext/rubydex/document.c +97 -98
  13. data/ext/rubydex/document.h +10 -10
  14. data/ext/rubydex/extconf.rb +146 -127
  15. data/ext/rubydex/graph.c +701 -512
  16. data/ext/rubydex/graph.h +10 -10
  17. data/ext/rubydex/handle.h +44 -44
  18. data/ext/rubydex/location.c +22 -22
  19. data/ext/rubydex/location.h +15 -15
  20. data/ext/rubydex/reference.c +123 -104
  21. data/ext/rubydex/reference.h +15 -16
  22. data/ext/rubydex/rubydex.c +22 -22
  23. data/ext/rubydex/utils.c +108 -86
  24. data/ext/rubydex/utils.h +34 -28
  25. data/lib/rubydex/comment.rb +17 -17
  26. data/lib/rubydex/declaration.rb +11 -0
  27. data/lib/rubydex/diagnostic.rb +21 -21
  28. data/lib/rubydex/failures.rb +15 -15
  29. data/lib/rubydex/graph.rb +98 -92
  30. data/lib/rubydex/keyword.rb +17 -0
  31. data/lib/rubydex/keyword_parameter.rb +13 -0
  32. data/lib/rubydex/location.rb +90 -90
  33. data/lib/rubydex/mixin.rb +22 -0
  34. data/lib/rubydex/version.rb +5 -5
  35. data/lib/rubydex.rb +24 -20
  36. data/rbi/rubydex.rbi +425 -310
  37. data/rust/Cargo.lock +1851 -1851
  38. data/rust/Cargo.toml +29 -29
  39. data/rust/about.toml +10 -10
  40. data/rust/{about.hbs → about_templates/about.hbs} +81 -78
  41. data/rust/about_templates/mingw_licenses.hbs +1071 -0
  42. data/rust/rubydex/Cargo.toml +42 -42
  43. data/rust/rubydex/src/compile_assertions.rs +13 -13
  44. data/rust/rubydex/src/diagnostic.rs +110 -109
  45. data/rust/rubydex/src/errors.rs +28 -28
  46. data/rust/rubydex/src/indexing/local_graph.rs +224 -224
  47. data/rust/rubydex/src/indexing/rbs_indexer.rs +1551 -1554
  48. data/rust/rubydex/src/indexing/ruby_indexer.rs +2329 -6753
  49. data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +4962 -0
  50. data/rust/rubydex/src/indexing.rs +210 -210
  51. data/rust/rubydex/src/integrity.rs +279 -278
  52. data/rust/rubydex/src/job_queue.rs +199 -205
  53. data/rust/rubydex/src/lib.rs +17 -17
  54. data/rust/rubydex/src/listing.rs +371 -272
  55. data/rust/rubydex/src/main.rs +160 -160
  56. data/rust/rubydex/src/model/built_in.rs +83 -0
  57. data/rust/rubydex/src/model/comment.rs +24 -24
  58. data/rust/rubydex/src/model/declaration.rs +679 -588
  59. data/rust/rubydex/src/model/definitions.rs +1682 -1602
  60. data/rust/rubydex/src/model/document.rs +222 -252
  61. data/rust/rubydex/src/model/encoding.rs +22 -22
  62. data/rust/rubydex/src/model/graph.rs +3782 -3556
  63. data/rust/rubydex/src/model/id.rs +110 -110
  64. data/rust/rubydex/src/model/identity_maps.rs +58 -58
  65. data/rust/rubydex/src/model/ids.rs +60 -38
  66. data/rust/rubydex/src/model/keywords.rs +256 -256
  67. data/rust/rubydex/src/model/name.rs +298 -298
  68. data/rust/rubydex/src/model/references.rs +111 -111
  69. data/rust/rubydex/src/model/string_ref.rs +50 -50
  70. data/rust/rubydex/src/model/visibility.rs +41 -41
  71. data/rust/rubydex/src/model.rs +15 -14
  72. data/rust/rubydex/src/offset.rs +147 -147
  73. data/rust/rubydex/src/position.rs +6 -6
  74. data/rust/rubydex/src/query.rs +1841 -1700
  75. data/rust/rubydex/src/resolution.rs +1852 -5895
  76. data/rust/rubydex/src/resolution_tests.rs +4701 -0
  77. data/rust/rubydex/src/stats/memory.rs +71 -71
  78. data/rust/rubydex/src/stats/orphan_report.rs +264 -263
  79. data/rust/rubydex/src/stats/timer.rs +127 -127
  80. data/rust/rubydex/src/stats.rs +11 -11
  81. data/rust/rubydex/src/test_utils/context.rs +226 -226
  82. data/rust/rubydex/src/test_utils/graph_test.rs +730 -679
  83. data/rust/rubydex/src/test_utils/local_graph_test.rs +602 -602
  84. data/rust/rubydex/src/test_utils.rs +52 -52
  85. data/rust/rubydex/src/visualization/dot.rs +192 -176
  86. data/rust/rubydex/src/visualization.rs +6 -6
  87. data/rust/rubydex/tests/cli.rs +185 -167
  88. data/rust/rubydex-mcp/Cargo.toml +28 -28
  89. data/rust/rubydex-mcp/src/main.rs +48 -48
  90. data/rust/rubydex-mcp/src/server.rs +1145 -1145
  91. data/rust/rubydex-mcp/src/tools.rs +49 -49
  92. data/rust/rubydex-mcp/tests/mcp.rs +302 -302
  93. data/rust/rubydex-sys/Cargo.toml +20 -20
  94. data/rust/rubydex-sys/build.rs +14 -14
  95. data/rust/rubydex-sys/cbindgen.toml +12 -12
  96. data/rust/rubydex-sys/src/declaration_api.rs +485 -469
  97. data/rust/rubydex-sys/src/definition_api.rs +443 -352
  98. data/rust/rubydex-sys/src/diagnostic_api.rs +99 -99
  99. data/rust/rubydex-sys/src/document_api.rs +85 -54
  100. data/rust/rubydex-sys/src/graph_api.rs +1017 -700
  101. data/rust/rubydex-sys/src/lib.rs +79 -9
  102. data/rust/rubydex-sys/src/location_api.rs +79 -79
  103. data/rust/rubydex-sys/src/name_api.rs +187 -135
  104. data/rust/rubydex-sys/src/reference_api.rs +267 -195
  105. data/rust/rubydex-sys/src/utils.rs +70 -70
  106. data/rust/rustfmt.toml +2 -2
  107. metadata +16 -9
  108. data/lib/rubydex/librubydex_sys.so +0 -0
@@ -1,302 +1,302 @@
1
- use assert_cmd::prelude::*;
2
- use rubydex::test_utils::with_context;
3
- use serde_json::{Value, json};
4
- use std::io::{BufRead, BufReader, Read, Write};
5
- use std::process::{Command, Stdio};
6
-
7
- const MAX_INDEXING_RETRIES: u64 = 60;
8
-
9
- /// Send a JSON-RPC message followed by a newline to the process stdin.
10
- fn send_message(stdin: &mut impl Write, msg: &Value) {
11
- let payload = serde_json::to_string(msg).unwrap();
12
- writeln!(stdin, "{payload}").unwrap();
13
- stdin.flush().unwrap();
14
- }
15
-
16
- /// Read a single JSON-RPC response line from the process stdout.
17
- fn read_response(reader: &mut BufReader<impl std::io::Read>) -> Value {
18
- let mut line = String::new();
19
- reader.read_line(&mut line).unwrap();
20
- serde_json::from_str(line.trim()).unwrap()
21
- }
22
-
23
- fn send_request(stdin: &mut impl Write, id: u64, method: &str, params: &Value) {
24
- send_message(
25
- stdin,
26
- &json!({
27
- "jsonrpc": "2.0",
28
- "id": id,
29
- "method": method,
30
- "params": params,
31
- }),
32
- );
33
- }
34
-
35
- fn read_response_for_id(reader: &mut BufReader<impl Read>, expected_id: u64) -> Value {
36
- let response = read_response(reader);
37
- assert_eq!(response["id"], expected_id);
38
- response
39
- }
40
-
41
- fn call_tool(
42
- stdin: &mut impl Write,
43
- reader: &mut BufReader<impl Read>,
44
- request_id: u64,
45
- tool_name: &str,
46
- arguments: &Value,
47
- ) -> Value {
48
- send_request(
49
- stdin,
50
- request_id,
51
- "tools/call",
52
- &json!({
53
- "name": tool_name,
54
- "arguments": arguments,
55
- }),
56
- );
57
-
58
- let response = read_response_for_id(reader, request_id);
59
- let content_text = response["result"]["content"][0]["text"].as_str().unwrap();
60
- serde_json::from_str(content_text).unwrap()
61
- }
62
-
63
- fn call_next_tool(
64
- stdin: &mut impl Write,
65
- reader: &mut BufReader<impl Read>,
66
- request_id: &mut u64,
67
- tool_name: &str,
68
- arguments: &Value,
69
- ) -> Value {
70
- *request_id += 1;
71
- call_tool(stdin, reader, *request_id, tool_name, arguments)
72
- }
73
-
74
- fn names_from_entries(entries: &[Value]) -> Vec<String> {
75
- entries
76
- .iter()
77
- .filter_map(|entry| entry["name"].as_str().map(ToOwned::to_owned))
78
- .collect()
79
- }
80
-
81
- fn assert_has_name(names: &[String], expected_name: &str, context: &str) {
82
- assert!(
83
- names.iter().any(|name| name == expected_name),
84
- "Expected {context} to include {expected_name}, got: {names:?}"
85
- );
86
- }
87
-
88
- fn initialize_session(stdin: &mut impl Write, reader: &mut BufReader<impl Read>) {
89
- send_request(
90
- stdin,
91
- 1,
92
- "initialize",
93
- &json!({
94
- "protocolVersion": "2025-03-26",
95
- "capabilities": {},
96
- "clientInfo": { "name": "test-client", "version": "0.1.0" }
97
- }),
98
- );
99
-
100
- let response = read_response_for_id(reader, 1);
101
- assert!(response["result"]["capabilities"]["tools"].is_object());
102
-
103
- send_message(
104
- stdin,
105
- &json!({
106
- "jsonrpc": "2.0",
107
- "method": "notifications/initialized"
108
- }),
109
- );
110
- }
111
-
112
- fn assert_tools_are_registered(stdin: &mut impl Write, reader: &mut BufReader<impl Read>) {
113
- send_request(stdin, 2, "tools/list", &json!({}));
114
-
115
- let response = read_response_for_id(reader, 2);
116
- let tools = response["result"]["tools"].as_array().unwrap();
117
- let tool_names: Vec<&str> = tools.iter().map(|tool| tool["name"].as_str().unwrap()).collect();
118
-
119
- assert!(
120
- tool_names.contains(&"search_declarations"),
121
- "Missing search_declarations tool"
122
- );
123
- assert!(tool_names.contains(&"get_declaration"), "Missing get_declaration tool");
124
- assert!(tool_names.contains(&"get_descendants"), "Missing get_descendants tool");
125
- assert!(
126
- tool_names.contains(&"find_constant_references"),
127
- "Missing find_constant_references tool"
128
- );
129
- assert!(
130
- tool_names.contains(&"get_file_declarations"),
131
- "Missing get_file_declarations tool"
132
- );
133
- assert!(tool_names.contains(&"codebase_stats"), "Missing codebase_stats tool");
134
- assert_eq!(tool_names.len(), 6, "Expected exactly 6 tools");
135
- }
136
-
137
- fn wait_for_indexing_to_complete(
138
- stdin: &mut impl Write,
139
- reader: &mut BufReader<impl Read>,
140
- request_id: &mut u64,
141
- ) -> Value {
142
- for _ in 0..MAX_INDEXING_RETRIES {
143
- let parsed = call_tool(stdin, reader, *request_id, "codebase_stats", &json!({}));
144
- if parsed.get("error").is_none() {
145
- return parsed;
146
- }
147
-
148
- assert_eq!(
149
- parsed["error"].as_str(),
150
- Some("indexing"),
151
- "Expected transient indexing error while booting, got: {parsed}"
152
- );
153
-
154
- *request_id += 1;
155
- std::thread::sleep(std::time::Duration::from_millis(50));
156
- }
157
-
158
- panic!("Timed out waiting for indexing to complete");
159
- }
160
-
161
- #[test]
162
- #[allow(clippy::too_many_lines)]
163
- fn mcp_server_e2e() {
164
- with_context(|context| {
165
- context.write(
166
- "app.rb",
167
- r#"
168
- class Animal
169
- def speak
170
- "..."
171
- end
172
- end
173
-
174
- class Dog < Animal
175
- def speak
176
- "Woof!"
177
- end
178
- end
179
-
180
- module Greetable
181
- def greet
182
- "Hello"
183
- end
184
- end
185
-
186
- class Kennel
187
- def build
188
- Animal.new
189
- end
190
- end
191
- "#,
192
- );
193
-
194
- let mut child = Command::cargo_bin("rubydex_mcp")
195
- .unwrap()
196
- .args([context.absolute_path().to_str().unwrap()])
197
- .stdin(Stdio::piped())
198
- .stdout(Stdio::piped())
199
- .stderr(Stdio::piped())
200
- .spawn()
201
- .unwrap();
202
-
203
- let mut stdin = child.stdin.take().unwrap();
204
- let stdout = child.stdout.take().unwrap();
205
- let mut reader = BufReader::new(stdout);
206
-
207
- initialize_session(&mut stdin, &mut reader);
208
- assert_tools_are_registered(&mut stdin, &mut reader);
209
-
210
- // Wait for indexing readiness before asserting semantic tool results.
211
- let mut request_id = 3;
212
- let stats = wait_for_indexing_to_complete(&mut stdin, &mut reader, &mut request_id);
213
- assert_eq!(stats["files"], 1);
214
- assert!(stats["declarations"].as_u64().unwrap() > 0);
215
-
216
- // Semantic query: search declarations.
217
- let search_response = call_next_tool(
218
- &mut stdin,
219
- &mut reader,
220
- &mut request_id,
221
- "search_declarations",
222
- &json!({ "query": "Dog" }),
223
- );
224
- let results = search_response["results"].as_array().unwrap();
225
- let result_names = names_from_entries(results);
226
- assert_has_name(&result_names, "Dog", "search results");
227
- assert!(search_response["total"].as_u64().unwrap() > 0);
228
-
229
- // Semantic query: inspect declaration details.
230
- let decl = call_next_tool(
231
- &mut stdin,
232
- &mut reader,
233
- &mut request_id,
234
- "get_declaration",
235
- &json!({ "name": "Dog" }),
236
- );
237
- assert_eq!(decl["name"], "Dog");
238
- assert_eq!(decl["kind"], "Class");
239
- assert!(!decl["definitions"].as_array().unwrap().is_empty());
240
-
241
- // Verify ancestors are included in get_declaration response
242
- let ancestor_entries = decl["ancestors"].as_array().unwrap();
243
- let ancestor_names = names_from_entries(ancestor_entries);
244
- assert_has_name(&ancestor_names, "Animal", "Dog ancestors");
245
-
246
- // Semantic query: descendants.
247
- let descendants = call_next_tool(
248
- &mut stdin,
249
- &mut reader,
250
- &mut request_id,
251
- "get_descendants",
252
- &json!({ "name": "Animal" }),
253
- );
254
- let descendant_entries = descendants["descendants"].as_array().unwrap();
255
- let descendant_names = names_from_entries(descendant_entries);
256
- assert_has_name(&descendant_names, "Dog", "Animal descendants");
257
- assert!(descendants["total"].as_u64().unwrap() > 0);
258
-
259
- // Semantic query: resolved constant references.
260
- let references = call_next_tool(
261
- &mut stdin,
262
- &mut reader,
263
- &mut request_id,
264
- "find_constant_references",
265
- &json!({ "name": "Animal" }),
266
- );
267
- let refs = references["references"].as_array().unwrap();
268
- assert!(
269
- !refs.is_empty(),
270
- "Expected at least one reference to Animal, got: {references}"
271
- );
272
- assert!(
273
- refs.iter().all(|entry| entry["path"].as_str().is_some()),
274
- "Expected references to include file paths, got: {references}"
275
- );
276
- assert!(references["total"].as_u64().unwrap() > 0);
277
-
278
- // Semantic query: file declarations.
279
- let file_declarations = call_next_tool(
280
- &mut stdin,
281
- &mut reader,
282
- &mut request_id,
283
- "get_file_declarations",
284
- &json!({ "file_path": "app.rb" }),
285
- );
286
- assert!(
287
- file_declarations["file"]
288
- .as_str()
289
- .is_some_and(|path| path.ends_with("app.rb")),
290
- "Expected file path to end with app.rb, got: {file_declarations}"
291
- );
292
- let declaration_entries = file_declarations["declarations"].as_array().unwrap();
293
- let declaration_names = names_from_entries(declaration_entries);
294
- assert_has_name(&declaration_names, "Animal", "file declarations");
295
- assert_has_name(&declaration_names, "Dog", "file declarations");
296
- assert_has_name(&declaration_names, "Greetable", "file declarations");
297
-
298
- // Clean up: drop stdin to signal EOF, then wait for the process to exit
299
- drop(stdin);
300
- let _ = child.wait().unwrap();
301
- });
302
- }
1
+ use assert_cmd::prelude::*;
2
+ use rubydex::test_utils::with_context;
3
+ use serde_json::{Value, json};
4
+ use std::io::{BufRead, BufReader, Read, Write};
5
+ use std::process::{Command, Stdio};
6
+
7
+ const MAX_INDEXING_RETRIES: u64 = 60;
8
+
9
+ /// Send a JSON-RPC message followed by a newline to the process stdin.
10
+ fn send_message(stdin: &mut impl Write, msg: &Value) {
11
+ let payload = serde_json::to_string(msg).unwrap();
12
+ writeln!(stdin, "{payload}").unwrap();
13
+ stdin.flush().unwrap();
14
+ }
15
+
16
+ /// Read a single JSON-RPC response line from the process stdout.
17
+ fn read_response(reader: &mut BufReader<impl std::io::Read>) -> Value {
18
+ let mut line = String::new();
19
+ reader.read_line(&mut line).unwrap();
20
+ serde_json::from_str(line.trim()).unwrap()
21
+ }
22
+
23
+ fn send_request(stdin: &mut impl Write, id: u64, method: &str, params: &Value) {
24
+ send_message(
25
+ stdin,
26
+ &json!({
27
+ "jsonrpc": "2.0",
28
+ "id": id,
29
+ "method": method,
30
+ "params": params,
31
+ }),
32
+ );
33
+ }
34
+
35
+ fn read_response_for_id(reader: &mut BufReader<impl Read>, expected_id: u64) -> Value {
36
+ let response = read_response(reader);
37
+ assert_eq!(response["id"], expected_id);
38
+ response
39
+ }
40
+
41
+ fn call_tool(
42
+ stdin: &mut impl Write,
43
+ reader: &mut BufReader<impl Read>,
44
+ request_id: u64,
45
+ tool_name: &str,
46
+ arguments: &Value,
47
+ ) -> Value {
48
+ send_request(
49
+ stdin,
50
+ request_id,
51
+ "tools/call",
52
+ &json!({
53
+ "name": tool_name,
54
+ "arguments": arguments,
55
+ }),
56
+ );
57
+
58
+ let response = read_response_for_id(reader, request_id);
59
+ let content_text = response["result"]["content"][0]["text"].as_str().unwrap();
60
+ serde_json::from_str(content_text).unwrap()
61
+ }
62
+
63
+ fn call_next_tool(
64
+ stdin: &mut impl Write,
65
+ reader: &mut BufReader<impl Read>,
66
+ request_id: &mut u64,
67
+ tool_name: &str,
68
+ arguments: &Value,
69
+ ) -> Value {
70
+ *request_id += 1;
71
+ call_tool(stdin, reader, *request_id, tool_name, arguments)
72
+ }
73
+
74
+ fn names_from_entries(entries: &[Value]) -> Vec<String> {
75
+ entries
76
+ .iter()
77
+ .filter_map(|entry| entry["name"].as_str().map(ToOwned::to_owned))
78
+ .collect()
79
+ }
80
+
81
+ fn assert_has_name(names: &[String], expected_name: &str, context: &str) {
82
+ assert!(
83
+ names.iter().any(|name| name == expected_name),
84
+ "Expected {context} to include {expected_name}, got: {names:?}"
85
+ );
86
+ }
87
+
88
+ fn initialize_session(stdin: &mut impl Write, reader: &mut BufReader<impl Read>) {
89
+ send_request(
90
+ stdin,
91
+ 1,
92
+ "initialize",
93
+ &json!({
94
+ "protocolVersion": "2025-03-26",
95
+ "capabilities": {},
96
+ "clientInfo": { "name": "test-client", "version": "0.1.0" }
97
+ }),
98
+ );
99
+
100
+ let response = read_response_for_id(reader, 1);
101
+ assert!(response["result"]["capabilities"]["tools"].is_object());
102
+
103
+ send_message(
104
+ stdin,
105
+ &json!({
106
+ "jsonrpc": "2.0",
107
+ "method": "notifications/initialized"
108
+ }),
109
+ );
110
+ }
111
+
112
+ fn assert_tools_are_registered(stdin: &mut impl Write, reader: &mut BufReader<impl Read>) {
113
+ send_request(stdin, 2, "tools/list", &json!({}));
114
+
115
+ let response = read_response_for_id(reader, 2);
116
+ let tools = response["result"]["tools"].as_array().unwrap();
117
+ let tool_names: Vec<&str> = tools.iter().map(|tool| tool["name"].as_str().unwrap()).collect();
118
+
119
+ assert!(
120
+ tool_names.contains(&"search_declarations"),
121
+ "Missing search_declarations tool"
122
+ );
123
+ assert!(tool_names.contains(&"get_declaration"), "Missing get_declaration tool");
124
+ assert!(tool_names.contains(&"get_descendants"), "Missing get_descendants tool");
125
+ assert!(
126
+ tool_names.contains(&"find_constant_references"),
127
+ "Missing find_constant_references tool"
128
+ );
129
+ assert!(
130
+ tool_names.contains(&"get_file_declarations"),
131
+ "Missing get_file_declarations tool"
132
+ );
133
+ assert!(tool_names.contains(&"codebase_stats"), "Missing codebase_stats tool");
134
+ assert_eq!(tool_names.len(), 6, "Expected exactly 6 tools");
135
+ }
136
+
137
+ fn wait_for_indexing_to_complete(
138
+ stdin: &mut impl Write,
139
+ reader: &mut BufReader<impl Read>,
140
+ request_id: &mut u64,
141
+ ) -> Value {
142
+ for _ in 0..MAX_INDEXING_RETRIES {
143
+ let parsed = call_tool(stdin, reader, *request_id, "codebase_stats", &json!({}));
144
+ if parsed.get("error").is_none() {
145
+ return parsed;
146
+ }
147
+
148
+ assert_eq!(
149
+ parsed["error"].as_str(),
150
+ Some("indexing"),
151
+ "Expected transient indexing error while booting, got: {parsed}"
152
+ );
153
+
154
+ *request_id += 1;
155
+ std::thread::sleep(std::time::Duration::from_millis(50));
156
+ }
157
+
158
+ panic!("Timed out waiting for indexing to complete");
159
+ }
160
+
161
+ #[test]
162
+ #[allow(clippy::too_many_lines)]
163
+ fn mcp_server_e2e() {
164
+ with_context(|context| {
165
+ context.write(
166
+ "app.rb",
167
+ r#"
168
+ class Animal
169
+ def speak
170
+ "..."
171
+ end
172
+ end
173
+
174
+ class Dog < Animal
175
+ def speak
176
+ "Woof!"
177
+ end
178
+ end
179
+
180
+ module Greetable
181
+ def greet
182
+ "Hello"
183
+ end
184
+ end
185
+
186
+ class Kennel
187
+ def build
188
+ Animal.new
189
+ end
190
+ end
191
+ "#,
192
+ );
193
+
194
+ let mut child = Command::cargo_bin("rubydex_mcp")
195
+ .unwrap()
196
+ .args([context.absolute_path().to_str().unwrap()])
197
+ .stdin(Stdio::piped())
198
+ .stdout(Stdio::piped())
199
+ .stderr(Stdio::piped())
200
+ .spawn()
201
+ .unwrap();
202
+
203
+ let mut stdin = child.stdin.take().unwrap();
204
+ let stdout = child.stdout.take().unwrap();
205
+ let mut reader = BufReader::new(stdout);
206
+
207
+ initialize_session(&mut stdin, &mut reader);
208
+ assert_tools_are_registered(&mut stdin, &mut reader);
209
+
210
+ // Wait for indexing readiness before asserting semantic tool results.
211
+ let mut request_id = 3;
212
+ let stats = wait_for_indexing_to_complete(&mut stdin, &mut reader, &mut request_id);
213
+ assert_eq!(stats["files"], 2);
214
+ assert!(stats["declarations"].as_u64().unwrap() > 0);
215
+
216
+ // Semantic query: search declarations.
217
+ let search_response = call_next_tool(
218
+ &mut stdin,
219
+ &mut reader,
220
+ &mut request_id,
221
+ "search_declarations",
222
+ &json!({ "query": "Dog" }),
223
+ );
224
+ let results = search_response["results"].as_array().unwrap();
225
+ let result_names = names_from_entries(results);
226
+ assert_has_name(&result_names, "Dog", "search results");
227
+ assert!(search_response["total"].as_u64().unwrap() > 0);
228
+
229
+ // Semantic query: inspect declaration details.
230
+ let decl = call_next_tool(
231
+ &mut stdin,
232
+ &mut reader,
233
+ &mut request_id,
234
+ "get_declaration",
235
+ &json!({ "name": "Dog" }),
236
+ );
237
+ assert_eq!(decl["name"], "Dog");
238
+ assert_eq!(decl["kind"], "Class");
239
+ assert!(!decl["definitions"].as_array().unwrap().is_empty());
240
+
241
+ // Verify ancestors are included in get_declaration response
242
+ let ancestor_entries = decl["ancestors"].as_array().unwrap();
243
+ let ancestor_names = names_from_entries(ancestor_entries);
244
+ assert_has_name(&ancestor_names, "Animal", "Dog ancestors");
245
+
246
+ // Semantic query: descendants.
247
+ let descendants = call_next_tool(
248
+ &mut stdin,
249
+ &mut reader,
250
+ &mut request_id,
251
+ "get_descendants",
252
+ &json!({ "name": "Animal" }),
253
+ );
254
+ let descendant_entries = descendants["descendants"].as_array().unwrap();
255
+ let descendant_names = names_from_entries(descendant_entries);
256
+ assert_has_name(&descendant_names, "Dog", "Animal descendants");
257
+ assert!(descendants["total"].as_u64().unwrap() > 0);
258
+
259
+ // Semantic query: resolved constant references.
260
+ let references = call_next_tool(
261
+ &mut stdin,
262
+ &mut reader,
263
+ &mut request_id,
264
+ "find_constant_references",
265
+ &json!({ "name": "Animal" }),
266
+ );
267
+ let refs = references["references"].as_array().unwrap();
268
+ assert!(
269
+ !refs.is_empty(),
270
+ "Expected at least one reference to Animal, got: {references}"
271
+ );
272
+ assert!(
273
+ refs.iter().all(|entry| entry["path"].as_str().is_some()),
274
+ "Expected references to include file paths, got: {references}"
275
+ );
276
+ assert!(references["total"].as_u64().unwrap() > 0);
277
+
278
+ // Semantic query: file declarations.
279
+ let file_declarations = call_next_tool(
280
+ &mut stdin,
281
+ &mut reader,
282
+ &mut request_id,
283
+ "get_file_declarations",
284
+ &json!({ "file_path": "app.rb" }),
285
+ );
286
+ assert!(
287
+ file_declarations["file"]
288
+ .as_str()
289
+ .is_some_and(|path| path.ends_with("app.rb")),
290
+ "Expected file path to end with app.rb, got: {file_declarations}"
291
+ );
292
+ let declaration_entries = file_declarations["declarations"].as_array().unwrap();
293
+ let declaration_names = names_from_entries(declaration_entries);
294
+ assert_has_name(&declaration_names, "Animal", "file declarations");
295
+ assert_has_name(&declaration_names, "Dog", "file declarations");
296
+ assert_has_name(&declaration_names, "Greetable", "file declarations");
297
+
298
+ // Clean up: drop stdin to signal EOF, then wait for the process to exit
299
+ drop(stdin);
300
+ let _ = child.wait().unwrap();
301
+ });
302
+ }
@@ -1,20 +1,20 @@
1
- [package]
2
- name = "rubydex-sys"
3
- version = "0.1.0"
4
- edition = "2024"
5
- license = "MIT"
6
-
7
- [lib]
8
- crate-type = ["cdylib", "staticlib"]
9
-
10
- [dependencies]
11
- rubydex = { path = "../rubydex" }
12
- libc = "0.2.174"
13
- url = "2.5.4"
14
- line-index = "0.1.2"
15
-
16
- [build-dependencies]
17
- cbindgen = "0.29"
18
-
19
- [lints]
20
- workspace = true
1
+ [package]
2
+ name = "rubydex-sys"
3
+ version = "0.1.0"
4
+ edition = "2024"
5
+ license = "MIT"
6
+
7
+ [lib]
8
+ crate-type = ["cdylib", "staticlib"]
9
+
10
+ [dependencies]
11
+ rubydex = { path = "../rubydex" }
12
+ libc = "0.2.174"
13
+ url = "2.5.4"
14
+ line-index = "0.1.2"
15
+
16
+ [build-dependencies]
17
+ cbindgen = "0.29"
18
+
19
+ [lints]
20
+ workspace = true