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.
- checksums.yaml +7 -0
- data/LICENSE.txt +23 -0
- data/README.md +125 -0
- data/THIRD_PARTY_LICENSES.html +4562 -0
- data/exe/rdx +47 -0
- data/ext/rubydex/declaration.c +453 -0
- data/ext/rubydex/declaration.h +23 -0
- data/ext/rubydex/definition.c +284 -0
- data/ext/rubydex/definition.h +28 -0
- data/ext/rubydex/diagnostic.c +6 -0
- data/ext/rubydex/diagnostic.h +11 -0
- data/ext/rubydex/document.c +97 -0
- data/ext/rubydex/document.h +10 -0
- data/ext/rubydex/extconf.rb +138 -0
- data/ext/rubydex/graph.c +681 -0
- data/ext/rubydex/graph.h +10 -0
- data/ext/rubydex/handle.h +44 -0
- data/ext/rubydex/location.c +22 -0
- data/ext/rubydex/location.h +15 -0
- data/ext/rubydex/reference.c +123 -0
- data/ext/rubydex/reference.h +15 -0
- data/ext/rubydex/rubydex.c +22 -0
- data/ext/rubydex/utils.c +108 -0
- data/ext/rubydex/utils.h +34 -0
- data/lib/rubydex/3.2/rubydex.so +0 -0
- data/lib/rubydex/3.3/rubydex.so +0 -0
- data/lib/rubydex/3.4/rubydex.so +0 -0
- data/lib/rubydex/4.0/rubydex.so +0 -0
- data/lib/rubydex/comment.rb +17 -0
- data/lib/rubydex/diagnostic.rb +21 -0
- data/lib/rubydex/failures.rb +15 -0
- data/lib/rubydex/graph.rb +98 -0
- data/lib/rubydex/keyword.rb +17 -0
- data/lib/rubydex/keyword_parameter.rb +13 -0
- data/lib/rubydex/librubydex_sys.so +0 -0
- data/lib/rubydex/location.rb +90 -0
- data/lib/rubydex/mixin.rb +22 -0
- data/lib/rubydex/version.rb +5 -0
- data/lib/rubydex.rb +23 -0
- data/rbi/rubydex.rbi +422 -0
- data/rust/Cargo.lock +1851 -0
- data/rust/Cargo.toml +29 -0
- data/rust/about.hbs +78 -0
- data/rust/about.toml +10 -0
- data/rust/rubydex/Cargo.toml +42 -0
- data/rust/rubydex/src/compile_assertions.rs +13 -0
- data/rust/rubydex/src/diagnostic.rs +110 -0
- data/rust/rubydex/src/errors.rs +28 -0
- data/rust/rubydex/src/indexing/local_graph.rs +224 -0
- data/rust/rubydex/src/indexing/rbs_indexer.rs +1551 -0
- data/rust/rubydex/src/indexing/ruby_indexer.rs +2329 -0
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +4962 -0
- data/rust/rubydex/src/indexing.rs +210 -0
- data/rust/rubydex/src/integrity.rs +279 -0
- data/rust/rubydex/src/job_queue.rs +205 -0
- data/rust/rubydex/src/lib.rs +17 -0
- data/rust/rubydex/src/listing.rs +371 -0
- data/rust/rubydex/src/main.rs +160 -0
- data/rust/rubydex/src/model/built_in.rs +83 -0
- data/rust/rubydex/src/model/comment.rs +24 -0
- data/rust/rubydex/src/model/declaration.rs +671 -0
- data/rust/rubydex/src/model/definitions.rs +1682 -0
- data/rust/rubydex/src/model/document.rs +222 -0
- data/rust/rubydex/src/model/encoding.rs +22 -0
- data/rust/rubydex/src/model/graph.rs +3754 -0
- data/rust/rubydex/src/model/id.rs +110 -0
- data/rust/rubydex/src/model/identity_maps.rs +58 -0
- data/rust/rubydex/src/model/ids.rs +60 -0
- data/rust/rubydex/src/model/keywords.rs +256 -0
- data/rust/rubydex/src/model/name.rs +298 -0
- data/rust/rubydex/src/model/references.rs +111 -0
- data/rust/rubydex/src/model/string_ref.rs +50 -0
- data/rust/rubydex/src/model/visibility.rs +41 -0
- data/rust/rubydex/src/model.rs +15 -0
- data/rust/rubydex/src/offset.rs +147 -0
- data/rust/rubydex/src/position.rs +6 -0
- data/rust/rubydex/src/query.rs +1841 -0
- data/rust/rubydex/src/resolution.rs +6517 -0
- data/rust/rubydex/src/stats/memory.rs +71 -0
- data/rust/rubydex/src/stats/orphan_report.rs +264 -0
- data/rust/rubydex/src/stats/timer.rs +127 -0
- data/rust/rubydex/src/stats.rs +11 -0
- data/rust/rubydex/src/test_utils/context.rs +226 -0
- data/rust/rubydex/src/test_utils/graph_test.rs +730 -0
- data/rust/rubydex/src/test_utils/local_graph_test.rs +602 -0
- data/rust/rubydex/src/test_utils.rs +52 -0
- data/rust/rubydex/src/visualization/dot.rs +192 -0
- data/rust/rubydex/src/visualization.rs +6 -0
- data/rust/rubydex/tests/cli.rs +185 -0
- data/rust/rubydex-mcp/Cargo.toml +28 -0
- data/rust/rubydex-mcp/src/main.rs +48 -0
- data/rust/rubydex-mcp/src/server.rs +1145 -0
- data/rust/rubydex-mcp/src/tools.rs +49 -0
- data/rust/rubydex-mcp/tests/mcp.rs +302 -0
- data/rust/rubydex-sys/Cargo.toml +20 -0
- data/rust/rubydex-sys/build.rs +14 -0
- data/rust/rubydex-sys/cbindgen.toml +12 -0
- data/rust/rubydex-sys/src/declaration_api.rs +485 -0
- data/rust/rubydex-sys/src/definition_api.rs +443 -0
- data/rust/rubydex-sys/src/diagnostic_api.rs +99 -0
- data/rust/rubydex-sys/src/document_api.rs +85 -0
- data/rust/rubydex-sys/src/graph_api.rs +948 -0
- data/rust/rubydex-sys/src/lib.rs +79 -0
- data/rust/rubydex-sys/src/location_api.rs +79 -0
- data/rust/rubydex-sys/src/name_api.rs +135 -0
- data/rust/rubydex-sys/src/reference_api.rs +267 -0
- data/rust/rubydex-sys/src/utils.rs +70 -0
- data/rust/rustfmt.toml +2 -0
- metadata +159 -0
|
@@ -0,0 +1,948 @@
|
|
|
1
|
+
//! This file provides the C API for the Graph object
|
|
2
|
+
|
|
3
|
+
use crate::declaration_api::CDeclaration;
|
|
4
|
+
use crate::declaration_api::DeclarationsIter;
|
|
5
|
+
use crate::document_api::DocumentsIter;
|
|
6
|
+
use crate::reference_api::{CConstantReference, CMethodReference, ConstantReferencesIter, MethodReferencesIter};
|
|
7
|
+
use crate::{name_api, utils};
|
|
8
|
+
use libc::{c_char, c_void};
|
|
9
|
+
use rubydex::indexing::LanguageId;
|
|
10
|
+
use rubydex::model::encoding::Encoding;
|
|
11
|
+
use rubydex::model::graph::Graph;
|
|
12
|
+
use rubydex::model::ids::{DeclarationId, NameId};
|
|
13
|
+
use rubydex::model::name::NameRef;
|
|
14
|
+
use rubydex::query::{CompletionCandidate, CompletionContext, CompletionReceiver};
|
|
15
|
+
use rubydex::resolution::Resolver;
|
|
16
|
+
use rubydex::{indexing, integrity, listing, query};
|
|
17
|
+
use std::ffi::CString;
|
|
18
|
+
use std::path::PathBuf;
|
|
19
|
+
use std::{mem, ptr};
|
|
20
|
+
|
|
21
|
+
pub type GraphPointer = *mut c_void;
|
|
22
|
+
|
|
23
|
+
/// Creates a new graph within a mutex. This is meant to be used when creating new Graph objects in Ruby
|
|
24
|
+
#[unsafe(no_mangle)]
|
|
25
|
+
pub extern "C" fn rdx_graph_new() -> GraphPointer {
|
|
26
|
+
Box::into_raw(Box::new(Graph::new())) as GraphPointer
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// Frees a Graph through its pointer
|
|
30
|
+
#[unsafe(no_mangle)]
|
|
31
|
+
pub extern "C" fn rdx_graph_free(pointer: GraphPointer) {
|
|
32
|
+
unsafe {
|
|
33
|
+
let _ = Box::from_raw(pointer.cast::<Graph>());
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
pub fn with_graph<F, T>(pointer: GraphPointer, action: F) -> T
|
|
38
|
+
where
|
|
39
|
+
F: FnOnce(&Graph) -> T,
|
|
40
|
+
{
|
|
41
|
+
let mut graph = unsafe { Box::from_raw(pointer.cast::<Graph>()) };
|
|
42
|
+
let result = action(&mut graph);
|
|
43
|
+
mem::forget(graph);
|
|
44
|
+
result
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fn with_mut_graph<F, T>(pointer: GraphPointer, action: F) -> T
|
|
48
|
+
where
|
|
49
|
+
F: FnOnce(&mut Graph) -> T,
|
|
50
|
+
{
|
|
51
|
+
let mut graph = unsafe { Box::from_raw(pointer.cast::<Graph>()) };
|
|
52
|
+
let result = action(&mut graph);
|
|
53
|
+
mem::forget(graph);
|
|
54
|
+
result
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/// Searches the graph using exact substring matching
|
|
58
|
+
///
|
|
59
|
+
/// # Safety
|
|
60
|
+
///
|
|
61
|
+
/// Expects both the graph and the query pointers to be valid
|
|
62
|
+
#[unsafe(no_mangle)]
|
|
63
|
+
pub unsafe extern "C" fn rdx_graph_declarations_search(
|
|
64
|
+
pointer: GraphPointer,
|
|
65
|
+
c_query: *const c_char,
|
|
66
|
+
) -> *mut DeclarationsIter {
|
|
67
|
+
{
|
|
68
|
+
let Ok(query) = (unsafe { utils::convert_char_ptr_to_string(c_query) }) else {
|
|
69
|
+
return ptr::null_mut();
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
let entries = with_graph(pointer, |graph| {
|
|
73
|
+
query::declaration_search(graph, &query, &query::MatchMode::Exact)
|
|
74
|
+
.into_iter()
|
|
75
|
+
.filter_map(|id| {
|
|
76
|
+
let decl = graph.declarations().get(&id)?;
|
|
77
|
+
Some(CDeclaration::from_declaration(id, decl))
|
|
78
|
+
})
|
|
79
|
+
.collect::<Vec<CDeclaration>>()
|
|
80
|
+
.into_boxed_slice()
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
DeclarationsIter::new(entries)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// Searches the graph using fuzzy matching
|
|
88
|
+
///
|
|
89
|
+
/// # Safety
|
|
90
|
+
///
|
|
91
|
+
/// Expects both the graph and the query pointers to be valid
|
|
92
|
+
#[unsafe(no_mangle)]
|
|
93
|
+
pub unsafe extern "C" fn rdx_graph_declarations_fuzzy_search(
|
|
94
|
+
pointer: GraphPointer,
|
|
95
|
+
c_query: *const c_char,
|
|
96
|
+
) -> *mut DeclarationsIter {
|
|
97
|
+
{
|
|
98
|
+
let Ok(query) = (unsafe { utils::convert_char_ptr_to_string(c_query) }) else {
|
|
99
|
+
return ptr::null_mut();
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
let entries = with_graph(pointer, |graph| {
|
|
103
|
+
query::declaration_search(graph, &query, &query::MatchMode::Fuzzy)
|
|
104
|
+
.into_iter()
|
|
105
|
+
.filter_map(|id| {
|
|
106
|
+
let decl = graph.declarations().get(&id)?;
|
|
107
|
+
Some(CDeclaration::from_declaration(id, decl))
|
|
108
|
+
})
|
|
109
|
+
.collect::<Vec<CDeclaration>>()
|
|
110
|
+
.into_boxed_slice()
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
DeclarationsIter::new(entries)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// # Panics
|
|
118
|
+
///
|
|
119
|
+
/// Will panic if the nesting cannot be transformed into a vector of strings
|
|
120
|
+
///
|
|
121
|
+
/// # Safety
|
|
122
|
+
///
|
|
123
|
+
/// Assumes that the `const_name` and `nesting` pointer are valid
|
|
124
|
+
#[unsafe(no_mangle)]
|
|
125
|
+
pub unsafe extern "C" fn rdx_graph_resolve_constant(
|
|
126
|
+
pointer: GraphPointer,
|
|
127
|
+
const_name: *const c_char,
|
|
128
|
+
nesting: *const *const c_char,
|
|
129
|
+
count: usize,
|
|
130
|
+
) -> *const CDeclaration {
|
|
131
|
+
with_mut_graph(pointer, |graph| {
|
|
132
|
+
let nesting: Vec<String> = unsafe { utils::convert_double_pointer_to_vec(nesting, count).unwrap() };
|
|
133
|
+
let const_name: String = unsafe { utils::convert_char_ptr_to_string(const_name).unwrap() };
|
|
134
|
+
let (name_id, names_to_untrack) = name_api::nesting_stack_to_name_id(graph, &const_name, nesting);
|
|
135
|
+
|
|
136
|
+
let mut resolver = Resolver::new(graph);
|
|
137
|
+
|
|
138
|
+
let declaration = match resolver.resolve_constant(name_id) {
|
|
139
|
+
Some(id) => {
|
|
140
|
+
let decl = graph.declarations().get(&id).unwrap();
|
|
141
|
+
Box::into_raw(Box::new(CDeclaration::from_declaration(id, decl))).cast_const()
|
|
142
|
+
}
|
|
143
|
+
None => ptr::null(),
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
for name_id in names_to_untrack {
|
|
147
|
+
graph.untrack_name(name_id);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
declaration
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/// Adds paths to exclude from file discovery during indexing.
|
|
155
|
+
///
|
|
156
|
+
/// # Panics
|
|
157
|
+
///
|
|
158
|
+
/// Will panic if the given array of C string paths cannot be converted to a `Vec<String>`.
|
|
159
|
+
///
|
|
160
|
+
/// # Safety
|
|
161
|
+
///
|
|
162
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
163
|
+
/// - `paths` must be an array of `count` valid, null-terminated UTF-8 strings.
|
|
164
|
+
#[unsafe(no_mangle)]
|
|
165
|
+
pub unsafe extern "C" fn rdx_graph_exclude_paths(pointer: GraphPointer, paths: *const *const c_char, count: usize) {
|
|
166
|
+
let paths: Vec<String> = unsafe { utils::convert_double_pointer_to_vec(paths, count).unwrap() };
|
|
167
|
+
let path_bufs: Vec<PathBuf> = paths.into_iter().map(PathBuf::from).collect();
|
|
168
|
+
with_mut_graph(pointer, |graph| graph.exclude_paths(path_bufs));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/// Returns the currently excluded paths as an array of C strings. Writes the count to `out_count`. Returns NULL if no
|
|
172
|
+
/// paths are excluded. Caller must free with `free_c_string_array`.
|
|
173
|
+
///
|
|
174
|
+
/// # Safety
|
|
175
|
+
///
|
|
176
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
177
|
+
/// - `out_count` must be a valid, writable pointer.
|
|
178
|
+
#[unsafe(no_mangle)]
|
|
179
|
+
pub unsafe extern "C" fn rdx_graph_excluded_paths(
|
|
180
|
+
pointer: GraphPointer,
|
|
181
|
+
out_count: *mut usize,
|
|
182
|
+
) -> *const *const c_char {
|
|
183
|
+
with_graph(pointer, |graph| {
|
|
184
|
+
let excluded = graph.excluded_paths();
|
|
185
|
+
|
|
186
|
+
if excluded.is_empty() {
|
|
187
|
+
unsafe { *out_count = 0 };
|
|
188
|
+
return ptr::null();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
let c_strings: Vec<*const c_char> = excluded
|
|
192
|
+
.iter()
|
|
193
|
+
.filter_map(|path| {
|
|
194
|
+
CString::new(path.to_string_lossy().as_ref())
|
|
195
|
+
.ok()
|
|
196
|
+
.map(|c_string| c_string.into_raw().cast_const())
|
|
197
|
+
})
|
|
198
|
+
.collect();
|
|
199
|
+
|
|
200
|
+
unsafe { *out_count = c_strings.len() };
|
|
201
|
+
|
|
202
|
+
let boxed = c_strings.into_boxed_slice();
|
|
203
|
+
Box::into_raw(boxed).cast::<*const c_char>()
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/// Indexes all given file paths in parallel using the provided Graph pointer.
|
|
208
|
+
/// Returns an array of error message strings and writes the count to `out_error_count`.
|
|
209
|
+
/// Returns NULL if there are no errors. Caller must free with `free_c_string_array`.
|
|
210
|
+
///
|
|
211
|
+
/// # Panics
|
|
212
|
+
///
|
|
213
|
+
/// Will panic if the given array of C string file paths cannot be converted to a Vec<String>
|
|
214
|
+
///
|
|
215
|
+
/// # Safety
|
|
216
|
+
///
|
|
217
|
+
/// This function is unsafe because it dereferences raw pointers coming from C. The caller has to ensure that the Ruby
|
|
218
|
+
/// VM will not free the pointers related to the string array while they are in use by Rust
|
|
219
|
+
#[unsafe(no_mangle)]
|
|
220
|
+
pub unsafe extern "C" fn rdx_index_all(
|
|
221
|
+
pointer: GraphPointer,
|
|
222
|
+
file_paths: *const *const c_char,
|
|
223
|
+
count: usize,
|
|
224
|
+
out_error_count: *mut usize,
|
|
225
|
+
) -> *const *const c_char {
|
|
226
|
+
let file_paths: Vec<String> = unsafe { utils::convert_double_pointer_to_vec(file_paths, count).unwrap() };
|
|
227
|
+
|
|
228
|
+
with_mut_graph(pointer, |graph| {
|
|
229
|
+
let (file_paths, listing_errors) = listing::collect_file_paths(file_paths, graph.excluded_paths());
|
|
230
|
+
let indexing_errors = indexing::index_files(graph, file_paths);
|
|
231
|
+
|
|
232
|
+
let all_errors: Vec<String> = listing_errors
|
|
233
|
+
.into_iter()
|
|
234
|
+
.chain(indexing_errors)
|
|
235
|
+
.map(|e| e.to_string())
|
|
236
|
+
.collect();
|
|
237
|
+
|
|
238
|
+
if all_errors.is_empty() {
|
|
239
|
+
unsafe { *out_error_count = 0 };
|
|
240
|
+
return ptr::null();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
let c_strings: Vec<*const c_char> = all_errors
|
|
244
|
+
.into_iter()
|
|
245
|
+
.filter_map(|string| {
|
|
246
|
+
CString::new(string)
|
|
247
|
+
.ok()
|
|
248
|
+
.map(|c_string| c_string.into_raw().cast_const())
|
|
249
|
+
})
|
|
250
|
+
.collect();
|
|
251
|
+
|
|
252
|
+
unsafe { *out_error_count = c_strings.len() };
|
|
253
|
+
|
|
254
|
+
let boxed = c_strings.into_boxed_slice();
|
|
255
|
+
Box::into_raw(boxed).cast::<*const c_char>()
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/// Deletes a document and all of its definitions from the graph.
|
|
260
|
+
/// Returns a pointer to the URI ID if the document was found and removed, or NULL if it didn't exist.
|
|
261
|
+
/// Caller must free the returned pointer with `free_u64`.
|
|
262
|
+
///
|
|
263
|
+
/// # Safety
|
|
264
|
+
///
|
|
265
|
+
/// Expects both the graph pointer and uri string pointer to be valid
|
|
266
|
+
#[unsafe(no_mangle)]
|
|
267
|
+
pub unsafe extern "C" fn rdx_graph_delete_document(pointer: GraphPointer, uri: *const c_char) -> *const u64 {
|
|
268
|
+
let Ok(uri_str) = (unsafe { utils::convert_char_ptr_to_string(uri) }) else {
|
|
269
|
+
return ptr::null();
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
with_mut_graph(pointer, |graph| match graph.delete_document(&uri_str) {
|
|
273
|
+
Some(uri_id) => Box::into_raw(Box::new(*uri_id)),
|
|
274
|
+
None => ptr::null(),
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/// Runs the resolver to compute declarations, ownership and related structures
|
|
279
|
+
#[unsafe(no_mangle)]
|
|
280
|
+
pub extern "C" fn rdx_graph_resolve(pointer: GraphPointer) {
|
|
281
|
+
with_mut_graph(pointer, |graph| {
|
|
282
|
+
let mut resolver = Resolver::new(graph);
|
|
283
|
+
resolver.resolve();
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/// Checks the integrity of the graph and returns an array of error message strings. Returns NULL if there are no
|
|
288
|
+
/// errors. Caller must free with `free_c_string_array`.
|
|
289
|
+
///
|
|
290
|
+
/// # Safety
|
|
291
|
+
///
|
|
292
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
293
|
+
/// - `out_error_count` must be a valid, writable pointer.
|
|
294
|
+
#[unsafe(no_mangle)]
|
|
295
|
+
pub unsafe extern "C" fn rdx_check_integrity(
|
|
296
|
+
pointer: GraphPointer,
|
|
297
|
+
out_error_count: *mut usize,
|
|
298
|
+
) -> *const *const c_char {
|
|
299
|
+
with_graph(pointer, |graph| {
|
|
300
|
+
let errors = integrity::check_integrity(graph);
|
|
301
|
+
|
|
302
|
+
if errors.is_empty() {
|
|
303
|
+
unsafe { *out_error_count = 0 };
|
|
304
|
+
return ptr::null();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
let c_strings: Vec<*const c_char> = errors
|
|
308
|
+
.into_iter()
|
|
309
|
+
.filter_map(|error| {
|
|
310
|
+
CString::new(error.to_string())
|
|
311
|
+
.ok()
|
|
312
|
+
.map(|c_string| c_string.into_raw().cast_const())
|
|
313
|
+
})
|
|
314
|
+
.collect();
|
|
315
|
+
|
|
316
|
+
unsafe { *out_error_count = c_strings.len() };
|
|
317
|
+
|
|
318
|
+
let boxed = c_strings.into_boxed_slice();
|
|
319
|
+
Box::into_raw(boxed).cast::<*const c_char>()
|
|
320
|
+
})
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/// # Safety
|
|
324
|
+
///
|
|
325
|
+
/// Expects both the graph pointer and encoding string pointer to be valid
|
|
326
|
+
#[unsafe(no_mangle)]
|
|
327
|
+
pub unsafe extern "C" fn rdx_graph_set_encoding(pointer: GraphPointer, encoding_str: *const c_char) -> bool {
|
|
328
|
+
let Ok(encoding) = (unsafe { utils::convert_char_ptr_to_string(encoding_str) }) else {
|
|
329
|
+
return false;
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
let encoding_variant = match encoding.as_str() {
|
|
333
|
+
"utf8" => Encoding::Utf8,
|
|
334
|
+
"utf16" => Encoding::Utf16,
|
|
335
|
+
"utf32" => Encoding::Utf32,
|
|
336
|
+
_ => {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
with_mut_graph(pointer, |graph| {
|
|
342
|
+
graph.set_encoding(encoding_variant);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
true
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/// Creates a new iterator over declaration IDs by snapshotting the current set of IDs.
|
|
349
|
+
///
|
|
350
|
+
/// # Safety
|
|
351
|
+
///
|
|
352
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
353
|
+
/// - The returned pointer must be freed with `rdx_graph_declarations_iter_free`.
|
|
354
|
+
#[unsafe(no_mangle)]
|
|
355
|
+
pub unsafe extern "C" fn rdx_graph_declarations_iter_new(pointer: GraphPointer) -> *mut DeclarationsIter {
|
|
356
|
+
// Snapshot the declarations at iterator creation to avoid borrowing across FFI calls
|
|
357
|
+
let entries = with_graph(pointer, |graph| {
|
|
358
|
+
graph
|
|
359
|
+
.declarations()
|
|
360
|
+
.iter()
|
|
361
|
+
.map(|(id, decl)| CDeclaration::from_declaration(*id, decl))
|
|
362
|
+
.collect::<Vec<CDeclaration>>()
|
|
363
|
+
.into_boxed_slice()
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
DeclarationsIter::new(entries)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/// Creates a new iterator over document (URI) IDs by snapshotting the current set of IDs.
|
|
370
|
+
///
|
|
371
|
+
/// # Safety
|
|
372
|
+
///
|
|
373
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
374
|
+
/// - The returned pointer must be freed with `rdx_graph_documents_iter_free`.
|
|
375
|
+
#[unsafe(no_mangle)]
|
|
376
|
+
pub unsafe extern "C" fn rdx_graph_documents_iter_new(pointer: GraphPointer) -> *mut DocumentsIter {
|
|
377
|
+
let entries = with_graph(pointer, |graph| {
|
|
378
|
+
graph
|
|
379
|
+
.documents()
|
|
380
|
+
.keys()
|
|
381
|
+
.map(|uri_id| **uri_id)
|
|
382
|
+
.collect::<Vec<_>>()
|
|
383
|
+
.into_boxed_slice()
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
DocumentsIter::new(entries)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/// Attempts to resolve a declaration from a fully-qualified name string.
|
|
390
|
+
/// Returns a `CDeclaration` pointer if it exists, or NULL if it does not.
|
|
391
|
+
///
|
|
392
|
+
/// # Safety
|
|
393
|
+
/// - `pointer` must be a valid `GraphPointer`
|
|
394
|
+
/// - `name` must be a valid, null-terminated UTF-8 string
|
|
395
|
+
#[unsafe(no_mangle)]
|
|
396
|
+
pub unsafe extern "C" fn rdx_graph_get_declaration(pointer: GraphPointer, name: *const c_char) -> *const CDeclaration {
|
|
397
|
+
let Ok(name_str) = (unsafe { utils::convert_char_ptr_to_string(name) }) else {
|
|
398
|
+
return ptr::null();
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
with_graph(pointer, |graph| {
|
|
402
|
+
let decl_id = DeclarationId::from(name_str.as_str());
|
|
403
|
+
|
|
404
|
+
if let Some(decl) = graph.declarations().get(&decl_id) {
|
|
405
|
+
Box::into_raw(Box::new(CDeclaration::from_declaration(decl_id, decl))).cast_const()
|
|
406
|
+
} else {
|
|
407
|
+
ptr::null()
|
|
408
|
+
}
|
|
409
|
+
})
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/// Creates a new iterator over constant references by snapshotting the current set of IDs.
|
|
413
|
+
///
|
|
414
|
+
/// # Safety
|
|
415
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
416
|
+
#[unsafe(no_mangle)]
|
|
417
|
+
pub unsafe extern "C" fn rdx_graph_constant_references_iter_new(pointer: GraphPointer) -> *mut ConstantReferencesIter {
|
|
418
|
+
with_graph(pointer, |graph| {
|
|
419
|
+
let refs: Vec<_> = graph
|
|
420
|
+
.constant_references()
|
|
421
|
+
.iter()
|
|
422
|
+
.map(|(id, cref)| {
|
|
423
|
+
let declaration_id = graph
|
|
424
|
+
.names()
|
|
425
|
+
.get(cref.name_id())
|
|
426
|
+
.and_then(|name_ref| match name_ref {
|
|
427
|
+
NameRef::Resolved(resolved) => Some(**resolved.declaration_id()),
|
|
428
|
+
NameRef::Unresolved(_) => None,
|
|
429
|
+
})
|
|
430
|
+
.unwrap_or(0);
|
|
431
|
+
|
|
432
|
+
CConstantReference {
|
|
433
|
+
id: **id,
|
|
434
|
+
declaration_id,
|
|
435
|
+
}
|
|
436
|
+
})
|
|
437
|
+
.collect();
|
|
438
|
+
|
|
439
|
+
ConstantReferencesIter::new(refs.into_boxed_slice())
|
|
440
|
+
})
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/// Creates a new iterator over method references by snapshotting the current set of IDs.
|
|
444
|
+
///
|
|
445
|
+
/// # Safety
|
|
446
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
447
|
+
#[unsafe(no_mangle)]
|
|
448
|
+
pub unsafe extern "C" fn rdx_graph_method_references_iter_new(pointer: GraphPointer) -> *mut MethodReferencesIter {
|
|
449
|
+
with_graph(pointer, |graph| {
|
|
450
|
+
let refs: Vec<_> = graph
|
|
451
|
+
.method_references()
|
|
452
|
+
.keys()
|
|
453
|
+
.map(|id| CMethodReference { id: **id })
|
|
454
|
+
.collect();
|
|
455
|
+
|
|
456
|
+
MethodReferencesIter::new(refs.into_boxed_slice())
|
|
457
|
+
})
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/// Resolves a require path to its document URI ID.
|
|
461
|
+
/// Returns a pointer to the URI ID if found, or NULL if not found.
|
|
462
|
+
/// Caller must free with the returned pointer.
|
|
463
|
+
///
|
|
464
|
+
/// # Safety
|
|
465
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
466
|
+
/// - `require_path` must be a valid, null-terminated UTF-8 string.
|
|
467
|
+
/// - `load_paths` must be an array of `load_paths_count` valid, null-terminated UTF-8 strings.
|
|
468
|
+
#[unsafe(no_mangle)]
|
|
469
|
+
pub unsafe extern "C" fn rdx_resolve_require_path(
|
|
470
|
+
pointer: GraphPointer,
|
|
471
|
+
require_path: *const c_char,
|
|
472
|
+
load_paths: *const *const c_char,
|
|
473
|
+
load_paths_count: usize,
|
|
474
|
+
) -> *const u64 {
|
|
475
|
+
let Ok(path_str) = (unsafe { utils::convert_char_ptr_to_string(require_path) }) else {
|
|
476
|
+
return ptr::null();
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
let Ok(paths_vec) = (unsafe { utils::convert_double_pointer_to_vec(load_paths, load_paths_count) }) else {
|
|
480
|
+
return ptr::null();
|
|
481
|
+
};
|
|
482
|
+
let paths_vec = paths_vec.into_iter().map(PathBuf::from).collect::<Vec<_>>();
|
|
483
|
+
|
|
484
|
+
with_graph(pointer, |graph| {
|
|
485
|
+
query::resolve_require_path(graph, &path_str, &paths_vec).map_or(ptr::null(), |id| Box::into_raw(Box::new(*id)))
|
|
486
|
+
})
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/// Returns all require paths for completion.
|
|
490
|
+
/// Returns array of C strings and writes count to `out_count`.
|
|
491
|
+
/// Returns null if `load_path` contain invalid UTF-8.
|
|
492
|
+
/// Caller must free with `free_c_string_array`.
|
|
493
|
+
///
|
|
494
|
+
/// # Safety
|
|
495
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
496
|
+
/// - `load_path` must be an array of `load_path_count` valid, null-terminated UTF-8 strings.
|
|
497
|
+
/// - `out_count` must be a valid, writable pointer.
|
|
498
|
+
#[unsafe(no_mangle)]
|
|
499
|
+
pub unsafe extern "C" fn rdx_require_paths(
|
|
500
|
+
pointer: GraphPointer,
|
|
501
|
+
load_path: *const *const c_char,
|
|
502
|
+
load_path_count: usize,
|
|
503
|
+
out_count: *mut usize,
|
|
504
|
+
) -> *const *const c_char {
|
|
505
|
+
let Ok(paths_vec) = (unsafe { utils::convert_double_pointer_to_vec(load_path, load_path_count) }) else {
|
|
506
|
+
return ptr::null_mut();
|
|
507
|
+
};
|
|
508
|
+
let paths_vec = paths_vec.into_iter().map(PathBuf::from).collect::<Vec<_>>();
|
|
509
|
+
|
|
510
|
+
let results = with_graph(pointer, |graph| query::require_paths(graph, &paths_vec));
|
|
511
|
+
|
|
512
|
+
let c_strings: Vec<*const c_char> = results
|
|
513
|
+
.into_iter()
|
|
514
|
+
.filter_map(|string| {
|
|
515
|
+
CString::new(string)
|
|
516
|
+
.ok()
|
|
517
|
+
.map(|c_string| c_string.into_raw().cast_const())
|
|
518
|
+
})
|
|
519
|
+
.collect();
|
|
520
|
+
|
|
521
|
+
unsafe { *out_count = c_strings.len() };
|
|
522
|
+
|
|
523
|
+
let boxed = c_strings.into_boxed_slice();
|
|
524
|
+
Box::into_raw(boxed).cast::<*const c_char>()
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
#[repr(C)]
|
|
528
|
+
pub enum IndexSourceResult {
|
|
529
|
+
Success = 0,
|
|
530
|
+
InvalidUri = 1,
|
|
531
|
+
InvalidSource = 2,
|
|
532
|
+
InvalidLanguageId = 3,
|
|
533
|
+
UnsupportedLanguageId = 4,
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/// Indexes source code from memory using the specified language. Returns `IndexSourceResult::Success` on success
|
|
537
|
+
/// or a specific error variant if string conversion or language lookup fails.
|
|
538
|
+
///
|
|
539
|
+
/// # Safety
|
|
540
|
+
///
|
|
541
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
542
|
+
/// - `uri` and `language_id` must be valid, null-terminated UTF-8 strings.
|
|
543
|
+
/// - `source` must point to a valid UTF-8 byte buffer of at least `source_len` bytes.
|
|
544
|
+
/// It may contain null bytes.
|
|
545
|
+
#[unsafe(no_mangle)]
|
|
546
|
+
pub unsafe extern "C" fn rdx_index_source(
|
|
547
|
+
pointer: GraphPointer,
|
|
548
|
+
uri: *const c_char,
|
|
549
|
+
source: *const c_char,
|
|
550
|
+
source_len: usize,
|
|
551
|
+
language_id: *const c_char,
|
|
552
|
+
) -> IndexSourceResult {
|
|
553
|
+
let Ok(uri_str) = (unsafe { utils::convert_char_ptr_to_string(uri) }) else {
|
|
554
|
+
return IndexSourceResult::InvalidUri;
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
let source_bytes = unsafe { std::slice::from_raw_parts(source.cast::<u8>(), source_len) };
|
|
558
|
+
let Ok(source_str) = std::str::from_utf8(source_bytes) else {
|
|
559
|
+
return IndexSourceResult::InvalidSource;
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
let Ok(language_id_str) = (unsafe { utils::convert_char_ptr_to_string(language_id) }) else {
|
|
563
|
+
return IndexSourceResult::InvalidLanguageId;
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
let Ok(language) = LanguageId::from_language_id(&language_id_str) else {
|
|
567
|
+
return IndexSourceResult::UnsupportedLanguageId;
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
with_mut_graph(pointer, |graph| {
|
|
571
|
+
indexing::index_source(graph, &uri_str, source_str, &language);
|
|
572
|
+
IndexSourceResult::Success
|
|
573
|
+
})
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
#[repr(C)]
|
|
577
|
+
#[derive(Debug, Clone, Copy)]
|
|
578
|
+
pub enum CCompletionCandidateKind {
|
|
579
|
+
Declaration = 0,
|
|
580
|
+
Keyword = 1,
|
|
581
|
+
KeywordParameter = 2,
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
#[repr(C)]
|
|
585
|
+
pub struct CCompletionCandidate {
|
|
586
|
+
pub kind: CCompletionCandidateKind,
|
|
587
|
+
/// Only valid when `kind == Declaration`; null otherwise.
|
|
588
|
+
pub declaration: *const CDeclaration,
|
|
589
|
+
pub name: *const c_char,
|
|
590
|
+
pub documentation: *const c_char,
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
#[repr(C)]
|
|
594
|
+
pub struct CompletionCandidateArray {
|
|
595
|
+
pub items: *mut CCompletionCandidate,
|
|
596
|
+
pub len: usize,
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
impl CompletionCandidateArray {
|
|
600
|
+
fn from_vec(entries: Vec<CCompletionCandidate>) -> *mut CompletionCandidateArray {
|
|
601
|
+
let mut boxed = entries.into_boxed_slice();
|
|
602
|
+
let len = boxed.len();
|
|
603
|
+
let ptr = boxed.as_mut_ptr();
|
|
604
|
+
mem::forget(boxed);
|
|
605
|
+
Box::into_raw(Box::new(CompletionCandidateArray { items: ptr, len }))
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/// Frees a completion candidate array previously returned by a completion function.
|
|
610
|
+
///
|
|
611
|
+
/// # Safety
|
|
612
|
+
///
|
|
613
|
+
/// - `ptr` must be a valid pointer previously returned by a completion function.
|
|
614
|
+
/// - `ptr` must not be used after being freed.
|
|
615
|
+
#[unsafe(no_mangle)]
|
|
616
|
+
pub unsafe extern "C" fn rdx_completion_candidates_free(ptr: *mut CompletionCandidateArray) {
|
|
617
|
+
if ptr.is_null() {
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
let array = unsafe { Box::from_raw(ptr) };
|
|
622
|
+
|
|
623
|
+
if !array.items.is_null() && array.len > 0 {
|
|
624
|
+
let slice_ptr = ptr::slice_from_raw_parts_mut(array.items, array.len);
|
|
625
|
+
let mut boxed_slice: Box<[CCompletionCandidate]> = unsafe { Box::from_raw(slice_ptr) };
|
|
626
|
+
|
|
627
|
+
for entry in &mut *boxed_slice {
|
|
628
|
+
if !entry.declaration.is_null() {
|
|
629
|
+
let _ = unsafe { Box::from_raw(entry.declaration.cast_mut()) };
|
|
630
|
+
}
|
|
631
|
+
if !entry.name.is_null() {
|
|
632
|
+
let _ = unsafe { CString::from_raw(entry.name.cast_mut()) };
|
|
633
|
+
}
|
|
634
|
+
if !entry.documentation.is_null() {
|
|
635
|
+
let _ = unsafe { CString::from_raw(entry.documentation.cast_mut()) };
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/// Converts the nesting stack into a `NameId`.
|
|
642
|
+
/// The last element of the nesting stack is treated as the self type; if the stack is empty, `"Object"` is used.
|
|
643
|
+
///
|
|
644
|
+
/// Returns `Err` if the nesting array contains invalid UTF-8.
|
|
645
|
+
///
|
|
646
|
+
/// # Safety
|
|
647
|
+
///
|
|
648
|
+
/// `nesting` must point to `nesting_count` valid, null-terminated UTF-8 strings.
|
|
649
|
+
unsafe fn completion_nesting_name_id(
|
|
650
|
+
graph: &mut Graph,
|
|
651
|
+
nesting: *const *const c_char,
|
|
652
|
+
nesting_count: usize,
|
|
653
|
+
) -> Result<(NameId, Vec<NameId>), std::str::Utf8Error> {
|
|
654
|
+
let mut nesting: Vec<String> = unsafe { utils::convert_double_pointer_to_vec(nesting, nesting_count)? };
|
|
655
|
+
|
|
656
|
+
// When serving completion in a bare script, the self (top level) context is Object
|
|
657
|
+
let self_name = if nesting.is_empty() {
|
|
658
|
+
"Object".to_string()
|
|
659
|
+
} else {
|
|
660
|
+
nesting.pop().unwrap()
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
Ok(name_api::nesting_stack_to_name_id(graph, &self_name, nesting))
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/// The result of a completion operation, carrying either a candidate array or an error message.
|
|
667
|
+
#[repr(C)]
|
|
668
|
+
pub struct CompletionResult {
|
|
669
|
+
/// Non-null on success; null on error.
|
|
670
|
+
pub candidates: *mut CompletionCandidateArray,
|
|
671
|
+
/// Non-null on error; null on success. Caller must free with `free_c_string`.
|
|
672
|
+
pub error: *const c_char,
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
impl CompletionResult {
|
|
676
|
+
fn success(candidates: *mut CompletionCandidateArray) -> Self {
|
|
677
|
+
Self {
|
|
678
|
+
candidates,
|
|
679
|
+
error: ptr::null(),
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
fn error(message: &str) -> Self {
|
|
684
|
+
Self {
|
|
685
|
+
candidates: ptr::null_mut(),
|
|
686
|
+
error: CString::new(message)
|
|
687
|
+
.map(|s| s.into_raw().cast_const())
|
|
688
|
+
.unwrap_or(ptr::null()),
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/// Runs completion for the given receiver and returns a structured result with candidates or an error message
|
|
694
|
+
fn run_and_finalize_completion(
|
|
695
|
+
graph: &mut Graph,
|
|
696
|
+
receiver: CompletionReceiver,
|
|
697
|
+
names_to_untrack: Vec<NameId>,
|
|
698
|
+
) -> CompletionResult {
|
|
699
|
+
let candidates = match query::completion_candidates(graph, CompletionContext::new(receiver)) {
|
|
700
|
+
Ok(candidates) => candidates,
|
|
701
|
+
Err(e) => {
|
|
702
|
+
for name_id in names_to_untrack {
|
|
703
|
+
graph.untrack_name(name_id);
|
|
704
|
+
}
|
|
705
|
+
return CompletionResult::error(&e.to_string());
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
let entries: Vec<CCompletionCandidate> = candidates
|
|
710
|
+
.into_iter()
|
|
711
|
+
.map(|candidate| match candidate {
|
|
712
|
+
CompletionCandidate::Declaration(id) => {
|
|
713
|
+
let decl = graph
|
|
714
|
+
.declarations()
|
|
715
|
+
.get(&id)
|
|
716
|
+
.expect("completion candidate declaration must exist in graph");
|
|
717
|
+
CCompletionCandidate {
|
|
718
|
+
kind: CCompletionCandidateKind::Declaration,
|
|
719
|
+
declaration: Box::into_raw(Box::new(CDeclaration::from_declaration(id, decl))),
|
|
720
|
+
name: ptr::null(),
|
|
721
|
+
documentation: ptr::null(),
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
CompletionCandidate::Keyword(kw) => CCompletionCandidate {
|
|
725
|
+
kind: CCompletionCandidateKind::Keyword,
|
|
726
|
+
declaration: ptr::null(),
|
|
727
|
+
name: CString::new(kw.name())
|
|
728
|
+
.expect("keyword name must not contain NUL")
|
|
729
|
+
.into_raw()
|
|
730
|
+
.cast_const(),
|
|
731
|
+
documentation: CString::new(kw.documentation())
|
|
732
|
+
.expect("keyword documentation must not contain NUL")
|
|
733
|
+
.into_raw()
|
|
734
|
+
.cast_const(),
|
|
735
|
+
},
|
|
736
|
+
CompletionCandidate::KeywordArgument(str_id) => {
|
|
737
|
+
let name_str = graph
|
|
738
|
+
.strings()
|
|
739
|
+
.get(&str_id)
|
|
740
|
+
.expect("keyword argument string must exist in graph");
|
|
741
|
+
CCompletionCandidate {
|
|
742
|
+
kind: CCompletionCandidateKind::KeywordParameter,
|
|
743
|
+
declaration: ptr::null(),
|
|
744
|
+
name: CString::new(name_str.as_str())
|
|
745
|
+
.expect("keyword argument name must not contain NUL")
|
|
746
|
+
.into_raw()
|
|
747
|
+
.cast_const(),
|
|
748
|
+
documentation: ptr::null(),
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
})
|
|
752
|
+
.collect();
|
|
753
|
+
|
|
754
|
+
for name_id in names_to_untrack {
|
|
755
|
+
graph.untrack_name(name_id);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
CompletionResult::success(CompletionCandidateArray::from_vec(entries))
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/// Returns expression completion candidates.
|
|
762
|
+
/// The caller must free candidates with `rdx_completion_candidates_free`
|
|
763
|
+
/// and the error string (if non-null) with `free_c_string`.
|
|
764
|
+
///
|
|
765
|
+
/// # Safety
|
|
766
|
+
///
|
|
767
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
768
|
+
/// - `nesting` must point to `nesting_count` valid, null-terminated UTF-8 strings.
|
|
769
|
+
#[unsafe(no_mangle)]
|
|
770
|
+
pub unsafe extern "C" fn rdx_graph_complete_expression(
|
|
771
|
+
pointer: GraphPointer,
|
|
772
|
+
nesting: *const *const c_char,
|
|
773
|
+
nesting_count: usize,
|
|
774
|
+
) -> CompletionResult {
|
|
775
|
+
with_mut_graph(pointer, |graph| {
|
|
776
|
+
let Ok((name_id, names_to_untrack)) = (unsafe { completion_nesting_name_id(graph, nesting, nesting_count) })
|
|
777
|
+
else {
|
|
778
|
+
return CompletionResult::success(ptr::null_mut());
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
run_and_finalize_completion(graph, CompletionReceiver::Expression(name_id), names_to_untrack)
|
|
782
|
+
})
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/// Returns namespace access completion candidates.
|
|
786
|
+
/// The caller must free candidates with `rdx_completion_candidates_free`
|
|
787
|
+
/// and the error string (if non-null) with `free_c_string`.
|
|
788
|
+
///
|
|
789
|
+
/// # Safety
|
|
790
|
+
///
|
|
791
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
792
|
+
/// - `name` must be a valid, null-terminated UTF-8 string (FQN of the namespace).
|
|
793
|
+
#[unsafe(no_mangle)]
|
|
794
|
+
pub unsafe extern "C" fn rdx_graph_complete_namespace_access(
|
|
795
|
+
pointer: GraphPointer,
|
|
796
|
+
name: *const c_char,
|
|
797
|
+
) -> CompletionResult {
|
|
798
|
+
let Ok(name_str) = (unsafe { utils::convert_char_ptr_to_string(name) }) else {
|
|
799
|
+
return CompletionResult::success(ptr::null_mut());
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
with_mut_graph(pointer, |graph| {
|
|
803
|
+
run_and_finalize_completion(
|
|
804
|
+
graph,
|
|
805
|
+
CompletionReceiver::NamespaceAccess(DeclarationId::from(name_str.as_str())),
|
|
806
|
+
Vec::new(),
|
|
807
|
+
)
|
|
808
|
+
})
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/// Returns method call completion candidates.
|
|
812
|
+
/// The caller must free candidates with `rdx_completion_candidates_free`
|
|
813
|
+
/// and the error string (if non-null) with `free_c_string`.
|
|
814
|
+
///
|
|
815
|
+
/// # Safety
|
|
816
|
+
///
|
|
817
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
818
|
+
/// - `name` must be a valid, null-terminated UTF-8 string (FQN of the receiver).
|
|
819
|
+
#[unsafe(no_mangle)]
|
|
820
|
+
pub unsafe extern "C" fn rdx_graph_complete_method_call(
|
|
821
|
+
pointer: GraphPointer,
|
|
822
|
+
name: *const c_char,
|
|
823
|
+
) -> CompletionResult {
|
|
824
|
+
let Ok(name_str) = (unsafe { utils::convert_char_ptr_to_string(name) }) else {
|
|
825
|
+
return CompletionResult::success(ptr::null_mut());
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
with_mut_graph(pointer, |graph| {
|
|
829
|
+
run_and_finalize_completion(
|
|
830
|
+
graph,
|
|
831
|
+
CompletionReceiver::MethodCall(DeclarationId::from(name_str.as_str())),
|
|
832
|
+
Vec::new(),
|
|
833
|
+
)
|
|
834
|
+
})
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/// Returns method argument completion candidates.
|
|
838
|
+
/// The caller must free candidates with `rdx_completion_candidates_free`
|
|
839
|
+
/// and the error string (if non-null) with `free_c_string`.
|
|
840
|
+
///
|
|
841
|
+
/// # Safety
|
|
842
|
+
///
|
|
843
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
844
|
+
/// - `name` must be a valid, null-terminated UTF-8 string (FQN of the method).
|
|
845
|
+
/// - `nesting` must point to `nesting_count` valid, null-terminated UTF-8 strings.
|
|
846
|
+
#[unsafe(no_mangle)]
|
|
847
|
+
pub unsafe extern "C" fn rdx_graph_complete_method_argument(
|
|
848
|
+
pointer: GraphPointer,
|
|
849
|
+
name: *const c_char,
|
|
850
|
+
nesting: *const *const c_char,
|
|
851
|
+
nesting_count: usize,
|
|
852
|
+
) -> CompletionResult {
|
|
853
|
+
let Ok(name_str) = (unsafe { utils::convert_char_ptr_to_string(name) }) else {
|
|
854
|
+
return CompletionResult::success(ptr::null_mut());
|
|
855
|
+
};
|
|
856
|
+
|
|
857
|
+
with_mut_graph(pointer, |graph| {
|
|
858
|
+
let Ok((self_name_id, names_to_untrack)) =
|
|
859
|
+
(unsafe { completion_nesting_name_id(graph, nesting, nesting_count) })
|
|
860
|
+
else {
|
|
861
|
+
return CompletionResult::success(ptr::null_mut());
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
run_and_finalize_completion(
|
|
865
|
+
graph,
|
|
866
|
+
CompletionReceiver::MethodArgument {
|
|
867
|
+
self_name_id,
|
|
868
|
+
method_decl_id: DeclarationId::from(name_str.as_str()),
|
|
869
|
+
},
|
|
870
|
+
names_to_untrack,
|
|
871
|
+
)
|
|
872
|
+
})
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
#[cfg(test)]
|
|
876
|
+
mod tests {
|
|
877
|
+
use rubydex::indexing::ruby_indexer::RubyIndexer;
|
|
878
|
+
|
|
879
|
+
use super::*;
|
|
880
|
+
|
|
881
|
+
#[test]
|
|
882
|
+
fn names_are_untracked_after_resolving_constant() {
|
|
883
|
+
let mut indexer = RubyIndexer::new(
|
|
884
|
+
"file:///foo.rb".into(),
|
|
885
|
+
"
|
|
886
|
+
class Foo
|
|
887
|
+
BAR = 1
|
|
888
|
+
end
|
|
889
|
+
",
|
|
890
|
+
);
|
|
891
|
+
indexer.index();
|
|
892
|
+
|
|
893
|
+
let mut graph = Graph::new();
|
|
894
|
+
graph.consume_document_changes(indexer.local_graph());
|
|
895
|
+
let mut resolver = Resolver::new(&mut graph);
|
|
896
|
+
resolver.resolve();
|
|
897
|
+
|
|
898
|
+
assert_eq!(
|
|
899
|
+
1,
|
|
900
|
+
graph
|
|
901
|
+
.names()
|
|
902
|
+
.iter()
|
|
903
|
+
.find_map(|(_, name)| {
|
|
904
|
+
if graph.strings().get(name.str()).unwrap().as_str() == "BAR" {
|
|
905
|
+
Some(name)
|
|
906
|
+
} else {
|
|
907
|
+
None
|
|
908
|
+
}
|
|
909
|
+
})
|
|
910
|
+
.unwrap()
|
|
911
|
+
.ref_count()
|
|
912
|
+
);
|
|
913
|
+
|
|
914
|
+
let graph_ptr = Box::into_raw(Box::new(graph)) as GraphPointer;
|
|
915
|
+
|
|
916
|
+
// Build the nesting array: ["Foo"] since BAR is inside class Foo
|
|
917
|
+
let nesting_strings = [CString::new("Foo").unwrap()];
|
|
918
|
+
let nesting_ptrs: Vec<*const c_char> = nesting_strings.iter().map(|s| s.as_ptr()).collect();
|
|
919
|
+
|
|
920
|
+
unsafe {
|
|
921
|
+
let decl = rdx_graph_resolve_constant(
|
|
922
|
+
graph_ptr,
|
|
923
|
+
CString::new("BAR").unwrap().as_ptr(),
|
|
924
|
+
nesting_ptrs.as_ptr(),
|
|
925
|
+
nesting_ptrs.len(),
|
|
926
|
+
);
|
|
927
|
+
assert_eq!((*decl).id(), *DeclarationId::from("Foo::BAR"));
|
|
928
|
+
};
|
|
929
|
+
|
|
930
|
+
let graph = unsafe { Box::from_raw(graph_ptr.cast::<Graph>()) };
|
|
931
|
+
|
|
932
|
+
assert_eq!(
|
|
933
|
+
1,
|
|
934
|
+
graph
|
|
935
|
+
.names()
|
|
936
|
+
.iter()
|
|
937
|
+
.find_map(|(_, name)| {
|
|
938
|
+
if graph.strings().get(name.str()).unwrap().as_str() == "BAR" {
|
|
939
|
+
Some(name)
|
|
940
|
+
} else {
|
|
941
|
+
None
|
|
942
|
+
}
|
|
943
|
+
})
|
|
944
|
+
.unwrap()
|
|
945
|
+
.ref_count()
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
}
|