rubydex 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +11 -0
- data/THIRD_PARTY_LICENSES.html +13 -24
- data/lib/rubydex/cli/command/dead_code.rb +59 -0
- data/lib/rubydex/mcp_server/tools/codebase_stats_tool.rb +1 -1
- data/lib/rubydex/mcp_server/tools/find_constant_references_tool.rb +4 -4
- data/lib/rubydex/mcp_server/tools/find_dead_code_candidates_tool.rb +33 -0
- data/lib/rubydex/mcp_server/tools/get_declaration_tool.rb +2 -2
- data/lib/rubydex/mcp_server/tools/get_descendants_tool.rb +4 -4
- data/lib/rubydex/mcp_server/tools/get_file_declarations_tool.rb +2 -2
- data/lib/rubydex/mcp_server/tools/search_declarations_tool.rb +6 -6
- data/lib/rubydex/mcp_server.rb +24 -12
- data/lib/rubydex/version.rb +1 -1
- data/rust/Cargo.lock +17 -11
- data/rust/Cargo.toml +3 -0
- data/rust/rubydex/Cargo.toml +2 -2
- data/rust/rubydex/src/compile_assertions.rs +2 -0
- data/rust/rubydex/src/config.rs +1 -1
- data/rust/rubydex/src/indexing/rbs_indexer.rs +4 -4
- data/rust/rubydex/src/indexing/ruby_indexer.rs +26 -18
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +42 -2
- data/rust/rubydex/src/indexing.rs +6 -6
- data/rust/rubydex/src/listing.rs +12 -12
- data/rust/rubydex/src/model/built_in.rs +1 -1
- data/rust/rubydex/src/model/comment.rs +4 -4
- data/rust/rubydex/src/model/declaration.rs +25 -17
- data/rust/rubydex/src/model/document.rs +11 -11
- data/rust/rubydex/src/model/graph.rs +3 -3
- data/rust/rubydex/src/offset.rs +1 -13
- data/rust/rubydex/src/operation/ruby_builder.rs +29 -19
- data/rust/rubydex/src/query.rs +3 -2
- data/rust/rubydex/src/test_utils/graph_test.rs +2 -2
- data/rust/rubydex/src/test_utils/local_graph_test.rs +13 -6
- data/rust/rubydex-sys/Cargo.toml +2 -2
- data/rust/rubydex-sys/src/definition_api.rs +1 -1
- data/rust/rubydex-sys/src/graph_api.rs +96 -49
- data/rust/rubydex-sys/src/name_api.rs +193 -58
- metadata +191 -191
|
@@ -87,8 +87,8 @@ pub struct RubyOperationBuilder<'a> {
|
|
|
87
87
|
|
|
88
88
|
impl<'a> RubyOperationBuilder<'a> {
|
|
89
89
|
#[must_use]
|
|
90
|
-
pub fn new(uri:
|
|
91
|
-
let uri_id = UriId::from(
|
|
90
|
+
pub fn new(uri: Box<str>, source: &'a str) -> Self {
|
|
91
|
+
let uri_id = UriId::from(&*uri);
|
|
92
92
|
|
|
93
93
|
Self {
|
|
94
94
|
uri_id,
|
|
@@ -112,7 +112,7 @@ impl<'a> RubyOperationBuilder<'a> {
|
|
|
112
112
|
self.add_diagnostic(
|
|
113
113
|
Rule::ParseError,
|
|
114
114
|
Offset::from_prism_location(&error.location()),
|
|
115
|
-
error.message().to_string(),
|
|
115
|
+
String::from_utf8_lossy(error.message()).to_string(),
|
|
116
116
|
);
|
|
117
117
|
}
|
|
118
118
|
|
|
@@ -120,7 +120,7 @@ impl<'a> RubyOperationBuilder<'a> {
|
|
|
120
120
|
self.add_diagnostic(
|
|
121
121
|
Rule::ParseWarning,
|
|
122
122
|
Offset::from_prism_location(&warning.location()),
|
|
123
|
-
warning.message().to_string(),
|
|
123
|
+
String::from_utf8_lossy(warning.message()).to_string(),
|
|
124
124
|
);
|
|
125
125
|
}
|
|
126
126
|
|
|
@@ -489,6 +489,7 @@ impl<'a> RubyOperationBuilder<'a> {
|
|
|
489
489
|
|
|
490
490
|
// -- Parameters --
|
|
491
491
|
|
|
492
|
+
#[allow(clippy::too_many_lines)]
|
|
492
493
|
fn collect_parameters(&mut self, node: &ruby_prism::DefNode) -> Vec<Parameter> {
|
|
493
494
|
let mut parameters: Vec<Parameter> = Vec::new();
|
|
494
495
|
|
|
@@ -588,7 +589,9 @@ impl<'a> RubyOperationBuilder<'a> {
|
|
|
588
589
|
}
|
|
589
590
|
}
|
|
590
591
|
|
|
591
|
-
if let Some(block) = parameters_list.block()
|
|
592
|
+
if let Some(block) = parameters_list.block()
|
|
593
|
+
&& let Some(block) = block.as_block_parameter_node()
|
|
594
|
+
{
|
|
592
595
|
let location = block.name_loc().unwrap_or_else(|| block.location());
|
|
593
596
|
let str_id = self.intern_string(Self::location_to_string(&location));
|
|
594
597
|
parameters.push(Parameter::Block(ParameterStruct::new(
|
|
@@ -1036,8 +1039,8 @@ impl<'a> RubyOperationBuilder<'a> {
|
|
|
1036
1039
|
match arg {
|
|
1037
1040
|
ruby_prism::Node::SymbolNode { .. } => {
|
|
1038
1041
|
let symbol = arg.as_symbol_node().unwrap();
|
|
1039
|
-
let
|
|
1040
|
-
Some((Self::location_to_string(&
|
|
1042
|
+
let content_loc = symbol.content_loc();
|
|
1043
|
+
Some((Self::location_to_string(&content_loc), content_loc))
|
|
1041
1044
|
}
|
|
1042
1045
|
ruby_prism::Node::StringNode { .. } => {
|
|
1043
1046
|
let string = arg.as_string_node().unwrap();
|
|
@@ -1380,7 +1383,7 @@ impl CommentGroup {
|
|
|
1380
1383
|
|
|
1381
1384
|
fn accepts(&self, next: &ruby_prism::Comment, source: &str) -> bool {
|
|
1382
1385
|
let current_end_offset = self.end_offset;
|
|
1383
|
-
let next_line_start_offset = next.location().
|
|
1386
|
+
let next_line_start_offset = next.location().start() as usize;
|
|
1384
1387
|
let between = &source.as_bytes()[current_end_offset..next_line_start_offset];
|
|
1385
1388
|
if !between.iter().all(|&b| b.is_ascii_whitespace()) {
|
|
1386
1389
|
return false;
|
|
@@ -1389,14 +1392,14 @@ impl CommentGroup {
|
|
|
1389
1392
|
}
|
|
1390
1393
|
|
|
1391
1394
|
fn add_comment(&mut self, comment: &ruby_prism::Comment) {
|
|
1392
|
-
self.end_offset = comment.location().
|
|
1395
|
+
self.end_offset = comment.location().end() as usize;
|
|
1393
1396
|
let text = String::from_utf8_lossy(comment.location().as_slice()).to_string();
|
|
1394
1397
|
if text.lines().any(|line| line.starts_with("# @deprecated")) {
|
|
1395
1398
|
self.deprecated = true;
|
|
1396
1399
|
}
|
|
1397
1400
|
self.comments.push(Comment::new(
|
|
1398
1401
|
Offset::from_prism_location(&comment.location()),
|
|
1399
|
-
text.trim().
|
|
1402
|
+
text.trim().into(),
|
|
1400
1403
|
));
|
|
1401
1404
|
}
|
|
1402
1405
|
|
|
@@ -1783,10 +1786,17 @@ impl Visit<'_> for RubyOperationBuilder<'_> {
|
|
|
1783
1786
|
});
|
|
1784
1787
|
};
|
|
1785
1788
|
|
|
1786
|
-
let message_loc = node.
|
|
1787
|
-
|
|
1789
|
+
let message_loc = if node.name().as_slice().is_empty() {
|
|
1790
|
+
// Incomplete calls like `Foo.` have no message. We still index them using the call operator location, so
|
|
1791
|
+
// that the receiver's singleton class gets created, which completion depends on
|
|
1792
|
+
node.call_operator_loc()
|
|
1793
|
+
} else {
|
|
1794
|
+
node.message_loc()
|
|
1795
|
+
};
|
|
1796
|
+
|
|
1797
|
+
let Some(message_loc) = message_loc else {
|
|
1788
1798
|
return;
|
|
1789
|
-
}
|
|
1799
|
+
};
|
|
1790
1800
|
|
|
1791
1801
|
let message = String::from_utf8_lossy(node.name().as_slice()).to_string();
|
|
1792
1802
|
|
|
@@ -2005,11 +2015,11 @@ impl Visit<'_> for RubyOperationBuilder<'_> {
|
|
|
2005
2015
|
self.visit(&receiver);
|
|
2006
2016
|
}
|
|
2007
2017
|
|
|
2008
|
-
self.index_method_reference(message.clone(), &
|
|
2018
|
+
self.index_method_reference(message.clone(), &message_loc, method_receiver);
|
|
2009
2019
|
|
|
2010
2020
|
match message.as_str() {
|
|
2011
2021
|
">" | "<" | ">=" | "<=" => {
|
|
2012
|
-
self.index_method_reference("<=>".to_string(), &
|
|
2022
|
+
self.index_method_reference("<=>".to_string(), &message_loc, method_receiver);
|
|
2013
2023
|
}
|
|
2014
2024
|
_ => {}
|
|
2015
2025
|
}
|
|
@@ -2134,7 +2144,7 @@ impl Visit<'_> for RubyOperationBuilder<'_> {
|
|
|
2134
2144
|
match expression {
|
|
2135
2145
|
ruby_prism::Node::SymbolNode { .. } => {
|
|
2136
2146
|
let symbol = expression.as_symbol_node().unwrap();
|
|
2137
|
-
let name = Self::location_to_string(&symbol.
|
|
2147
|
+
let name = Self::location_to_string(&symbol.content_loc());
|
|
2138
2148
|
self.index_method_reference(name, &node.location(), None);
|
|
2139
2149
|
}
|
|
2140
2150
|
_ => {
|
|
@@ -2146,13 +2156,13 @@ impl Visit<'_> for RubyOperationBuilder<'_> {
|
|
|
2146
2156
|
|
|
2147
2157
|
fn visit_alias_method_node(&mut self, node: &ruby_prism::AliasMethodNode<'_>) {
|
|
2148
2158
|
let mut new_name = if let Some(symbol_node) = node.new_name().as_symbol_node() {
|
|
2149
|
-
Self::location_to_string(&symbol_node.
|
|
2159
|
+
Self::location_to_string(&symbol_node.content_loc())
|
|
2150
2160
|
} else {
|
|
2151
2161
|
Self::location_to_string(&node.new_name().location())
|
|
2152
2162
|
};
|
|
2153
2163
|
|
|
2154
2164
|
let mut old_name = if let Some(symbol_node) = node.old_name().as_symbol_node() {
|
|
2155
|
-
Self::location_to_string(&symbol_node.
|
|
2165
|
+
Self::location_to_string(&symbol_node.content_loc())
|
|
2156
2166
|
} else {
|
|
2157
2167
|
Self::location_to_string(&node.old_name().location())
|
|
2158
2168
|
};
|
|
@@ -2229,7 +2239,7 @@ mod tests {
|
|
|
2229
2239
|
|
|
2230
2240
|
fn build_operations(source: &str) -> OperationBuilderResult {
|
|
2231
2241
|
let source = crate::test_utils::normalize_indentation(source);
|
|
2232
|
-
let builder = RubyOperationBuilder::new("file:///test.rb".
|
|
2242
|
+
let builder = RubyOperationBuilder::new("file:///test.rb".into(), &source);
|
|
2233
2243
|
builder.build()
|
|
2234
2244
|
}
|
|
2235
2245
|
|
data/rust/rubydex/src/query.rs
CHANGED
|
@@ -10,7 +10,7 @@ use crate::model::declaration::{Ancestor, Declaration, Namespace};
|
|
|
10
10
|
use crate::model::definitions::{Definition, Parameter};
|
|
11
11
|
use crate::model::graph::Graph;
|
|
12
12
|
use crate::model::identity_maps::IdentityHashSet;
|
|
13
|
-
use crate::model::ids::{DeclarationId, DefinitionId, NameId, StringId, UriId};
|
|
13
|
+
use crate::model::ids::{ConstantReferenceId, DeclarationId, DefinitionId, NameId, StringId, UriId};
|
|
14
14
|
use crate::model::keywords::{self, Keyword};
|
|
15
15
|
use crate::model::name::NameRef;
|
|
16
16
|
use crate::model::visibility::Visibility;
|
|
@@ -875,7 +875,8 @@ pub fn dead_code_candidates(graph: &Graph) -> Vec<DeclarationId> {
|
|
|
875
875
|
.iter()
|
|
876
876
|
.filter(|id| {
|
|
877
877
|
let decl = declarations.get(id).unwrap();
|
|
878
|
-
decl.constant_references()
|
|
878
|
+
decl.constant_references()
|
|
879
|
+
.is_some_and(<[ConstantReferenceId]>::is_empty)
|
|
879
880
|
&& !matches!(decl, Declaration::Namespace(Namespace::SingletonClass(_)))
|
|
880
881
|
&& !decl.has_no_definitions()
|
|
881
882
|
&& !is_built_in(graph, decl)
|
|
@@ -44,14 +44,14 @@ impl GraphTest {
|
|
|
44
44
|
/// Indexes a Ruby source
|
|
45
45
|
pub fn index_uri(&mut self, uri: &str, source: &str) {
|
|
46
46
|
let source = normalize_indentation(source);
|
|
47
|
-
let local_graph = indexing::build_local_graph(uri.
|
|
47
|
+
let local_graph = indexing::build_local_graph(uri.into(), &source, &LanguageId::Ruby, self.backend);
|
|
48
48
|
self.graph.consume_document_changes(local_graph);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/// Indexes an RBS source
|
|
52
52
|
pub fn index_rbs_uri(&mut self, uri: &str, source: &str) {
|
|
53
53
|
let source = normalize_indentation(source);
|
|
54
|
-
indexing::index_source(&mut self.graph, uri, &source, &LanguageId::Rbs);
|
|
54
|
+
indexing::index_source(&mut self.graph, uri.into(), &source, &LanguageId::Rbs);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
pub fn delete_uri(&mut self, uri: &str) {
|
|
@@ -24,22 +24,29 @@ impl LocalGraphTest {
|
|
|
24
24
|
|
|
25
25
|
#[must_use]
|
|
26
26
|
pub fn new_with_backend(uri: &str, source: &str, backend: IndexerBackend) -> Self {
|
|
27
|
-
let uri = uri.to_string();
|
|
28
27
|
let source = normalize_indentation(source);
|
|
29
|
-
let graph = build_local_graph(uri.
|
|
30
|
-
|
|
28
|
+
let graph = build_local_graph(uri.into(), &source, &LanguageId::Ruby, backend);
|
|
29
|
+
|
|
30
|
+
Self {
|
|
31
|
+
uri: uri.to_string(),
|
|
32
|
+
source,
|
|
33
|
+
graph,
|
|
34
|
+
}
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
#[must_use]
|
|
34
38
|
pub fn new_rbs(uri: &str, source: &str) -> Self {
|
|
35
|
-
let uri = uri.to_string();
|
|
36
39
|
let source = normalize_indentation(source);
|
|
37
40
|
|
|
38
|
-
let mut indexer = RBSIndexer::new(uri.
|
|
41
|
+
let mut indexer = RBSIndexer::new(uri.into(), &source);
|
|
39
42
|
indexer.index();
|
|
40
43
|
let graph = indexer.local_graph();
|
|
41
44
|
|
|
42
|
-
Self {
|
|
45
|
+
Self {
|
|
46
|
+
uri: uri.to_string(),
|
|
47
|
+
source,
|
|
48
|
+
graph,
|
|
49
|
+
}
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
#[must_use]
|
data/rust/rubydex-sys/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "rubydex-sys"
|
|
3
|
-
version =
|
|
3
|
+
version.workspace = true
|
|
4
4
|
edition = "2024"
|
|
5
5
|
rust-version = "1.89.0"
|
|
6
6
|
license = "MIT"
|
|
@@ -15,7 +15,7 @@ categories = ["development-tools", "external-ffi-bindings"]
|
|
|
15
15
|
crate-type = ["cdylib", "staticlib"]
|
|
16
16
|
|
|
17
17
|
[dependencies]
|
|
18
|
-
rubydex = { version = "0.2
|
|
18
|
+
rubydex = { version = "=0.4.2", path = "../rubydex" }
|
|
19
19
|
libc = "0.2.174"
|
|
20
20
|
url = "2.5.4"
|
|
21
21
|
line-index = "0.1.2"
|
|
@@ -248,7 +248,7 @@ pub unsafe extern "C" fn rdx_definition_comments(pointer: GraphPointer, definiti
|
|
|
248
248
|
.comments()
|
|
249
249
|
.iter()
|
|
250
250
|
.map(|c| CommentEntry {
|
|
251
|
-
string: CString::new(c.string()
|
|
251
|
+
string: CString::new(c.string()).unwrap().into_raw().cast_const(),
|
|
252
252
|
location: create_location_for_uri_and_offset(graph, document, c.offset()),
|
|
253
253
|
})
|
|
254
254
|
.collect::<Vec<CommentEntry>>()
|
|
@@ -173,11 +173,11 @@ pub unsafe extern "C" fn rdx_graph_resolve_constant(
|
|
|
173
173
|
let nesting: Vec<String> = unsafe { utils::convert_double_pointer_to_vec(nesting, count).unwrap() };
|
|
174
174
|
let const_name: String = unsafe { utils::convert_char_ptr_to_string(const_name).unwrap() };
|
|
175
175
|
|
|
176
|
-
let Some(
|
|
176
|
+
let Some(tracked_name) = name_api::name_from_nesting_names(graph, &const_name, nesting) else {
|
|
177
177
|
return ptr::null();
|
|
178
178
|
};
|
|
179
179
|
|
|
180
|
-
resolve_constant_name(
|
|
180
|
+
resolve_constant_name(tracked_name)
|
|
181
181
|
})
|
|
182
182
|
}
|
|
183
183
|
|
|
@@ -225,15 +225,14 @@ pub unsafe extern "C" fn rdx_graph_resolve_constant_from_definition(
|
|
|
225
225
|
}
|
|
226
226
|
};
|
|
227
227
|
|
|
228
|
-
let Some(
|
|
229
|
-
else {
|
|
228
|
+
let Some(tracked_name) = name_api::name_from_lexical_nesting(graph, &const_name, Some(nesting)) else {
|
|
230
229
|
return CDefinitionConstantResolutionResult {
|
|
231
230
|
status: CDefinitionConstantResolution::NotFound,
|
|
232
231
|
declaration: ptr::null(),
|
|
233
232
|
};
|
|
234
233
|
};
|
|
235
234
|
|
|
236
|
-
let declaration = resolve_constant_name(
|
|
235
|
+
let declaration = resolve_constant_name(tracked_name);
|
|
237
236
|
let status = if declaration.is_null() {
|
|
238
237
|
CDefinitionConstantResolution::NotFound
|
|
239
238
|
} else {
|
|
@@ -260,18 +259,13 @@ fn class_or_module_definition_name_id(
|
|
|
260
259
|
}
|
|
261
260
|
}
|
|
262
261
|
|
|
263
|
-
fn resolve_constant_name(
|
|
264
|
-
let
|
|
265
|
-
let
|
|
266
|
-
|
|
262
|
+
fn resolve_constant_name(mut tracked_name: name_api::TrackedName<'_>) -> *const CDeclaration {
|
|
263
|
+
let name_id = tracked_name.name_id();
|
|
264
|
+
let declaration_id = Resolver::new(tracked_name.graph_mut()).resolve_constant(name_id);
|
|
265
|
+
declaration_id.map_or(ptr::null(), |id| {
|
|
266
|
+
let declaration = tracked_name.graph().declarations().get(&id).unwrap();
|
|
267
267
|
Box::into_raw(Box::new(CDeclaration::from_declaration(id, declaration))).cast_const()
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
for name_id in names_to_untrack {
|
|
271
|
-
graph.untrack_name(name_id);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
declaration
|
|
268
|
+
})
|
|
275
269
|
}
|
|
276
270
|
|
|
277
271
|
/// Adds glob patterns to exclude from file discovery during indexing.
|
|
@@ -745,7 +739,7 @@ pub unsafe extern "C" fn rdx_index_source(
|
|
|
745
739
|
};
|
|
746
740
|
|
|
747
741
|
with_mut_graph(pointer, |graph| {
|
|
748
|
-
indexing::index_source(graph,
|
|
742
|
+
indexing::index_source(graph, uri_str.into(), source_str, &language);
|
|
749
743
|
IndexSourceResult::Success
|
|
750
744
|
})
|
|
751
745
|
}
|
|
@@ -815,19 +809,19 @@ pub unsafe extern "C" fn rdx_completion_candidates_free(ptr: *mut CompletionCand
|
|
|
815
809
|
}
|
|
816
810
|
}
|
|
817
811
|
|
|
818
|
-
/// Converts the nesting
|
|
819
|
-
/// The last
|
|
812
|
+
/// Converts the nesting names into a tracked name.
|
|
813
|
+
/// The last nesting name is treated as the self type; if the array is empty, `"Object"` is used.
|
|
820
814
|
///
|
|
821
|
-
/// Returns `
|
|
815
|
+
/// Returns `None` if the nesting array contains invalid UTF-8.
|
|
822
816
|
///
|
|
823
817
|
/// # Safety
|
|
824
818
|
///
|
|
825
819
|
/// `nesting` must point to `nesting_count` valid, null-terminated UTF-8 strings.
|
|
826
|
-
unsafe fn
|
|
820
|
+
unsafe fn completion_nesting_name(
|
|
827
821
|
graph: &mut Graph,
|
|
828
822
|
nesting: *const *const c_char,
|
|
829
823
|
nesting_count: usize,
|
|
830
|
-
) -> Option<
|
|
824
|
+
) -> Option<name_api::TrackedName<'_>> {
|
|
831
825
|
let mut nesting: Vec<String> = unsafe { utils::convert_double_pointer_to_vec(nesting, nesting_count).ok()? };
|
|
832
826
|
|
|
833
827
|
// When serving completion in a bare script, the self (top level) context is Object
|
|
@@ -837,7 +831,7 @@ unsafe fn completion_nesting_name_id(
|
|
|
837
831
|
nesting.pop().unwrap()
|
|
838
832
|
};
|
|
839
833
|
|
|
840
|
-
name_api::
|
|
834
|
+
name_api::name_from_nesting_names(graph, &self_name, nesting)
|
|
841
835
|
}
|
|
842
836
|
|
|
843
837
|
/// The result of a completion operation, carrying either a candidate array or an error message.
|
|
@@ -866,19 +860,10 @@ impl CompletionResult {
|
|
|
866
860
|
}
|
|
867
861
|
|
|
868
862
|
/// Runs completion for the given receiver and returns a structured result with candidates or an error message
|
|
869
|
-
fn run_and_finalize_completion(
|
|
870
|
-
graph: &mut Graph,
|
|
871
|
-
receiver: CompletionReceiver,
|
|
872
|
-
names_to_untrack: Vec<NameId>,
|
|
873
|
-
) -> CompletionResult {
|
|
863
|
+
fn run_and_finalize_completion(graph: &Graph, receiver: CompletionReceiver) -> CompletionResult {
|
|
874
864
|
let candidates = match query::completion_candidates(graph, CompletionContext::new(receiver)) {
|
|
875
865
|
Ok(candidates) => candidates,
|
|
876
|
-
Err(e) =>
|
|
877
|
-
for name_id in names_to_untrack {
|
|
878
|
-
graph.untrack_name(name_id);
|
|
879
|
-
}
|
|
880
|
-
return CompletionResult::error(&e.to_string());
|
|
881
|
-
}
|
|
866
|
+
Err(e) => return CompletionResult::error(&e.to_string()),
|
|
882
867
|
};
|
|
883
868
|
|
|
884
869
|
let entries: Vec<CCompletionCandidate> = candidates
|
|
@@ -926,10 +911,6 @@ fn run_and_finalize_completion(
|
|
|
926
911
|
})
|
|
927
912
|
.collect();
|
|
928
913
|
|
|
929
|
-
for name_id in names_to_untrack {
|
|
930
|
-
graph.untrack_name(name_id);
|
|
931
|
-
}
|
|
932
|
-
|
|
933
914
|
CompletionResult::success(CompletionCandidateArray::from_vec(entries))
|
|
934
915
|
}
|
|
935
916
|
|
|
@@ -950,20 +931,19 @@ pub unsafe extern "C" fn rdx_graph_complete_expression(
|
|
|
950
931
|
self_receiver: *const c_char,
|
|
951
932
|
) -> CompletionResult {
|
|
952
933
|
with_mut_graph(pointer, |graph| {
|
|
953
|
-
let Some(
|
|
954
|
-
else {
|
|
934
|
+
let Some(tracked_name) = (unsafe { completion_nesting_name(graph, nesting, nesting_count) }) else {
|
|
955
935
|
return CompletionResult::success(ptr::null_mut());
|
|
956
936
|
};
|
|
937
|
+
let name_id = tracked_name.name_id();
|
|
957
938
|
|
|
958
939
|
let self_decl_id = unsafe { decl_id_from_char_ptr(self_receiver) };
|
|
959
940
|
|
|
960
941
|
run_and_finalize_completion(
|
|
961
|
-
graph,
|
|
942
|
+
tracked_name.graph(),
|
|
962
943
|
CompletionReceiver::Expression {
|
|
963
944
|
self_decl_id,
|
|
964
945
|
nesting_name_id: name_id,
|
|
965
946
|
},
|
|
966
|
-
names_to_untrack,
|
|
967
947
|
)
|
|
968
948
|
})
|
|
969
949
|
}
|
|
@@ -997,7 +977,6 @@ pub unsafe extern "C" fn rdx_graph_complete_namespace_access(
|
|
|
997
977
|
self_decl_id,
|
|
998
978
|
namespace_decl_id: declaration_id_from_lookup_name(&name_str),
|
|
999
979
|
},
|
|
1000
|
-
Vec::new(),
|
|
1001
980
|
)
|
|
1002
981
|
})
|
|
1003
982
|
}
|
|
@@ -1031,7 +1010,6 @@ pub unsafe extern "C" fn rdx_graph_complete_method_call(
|
|
|
1031
1010
|
self_decl_id,
|
|
1032
1011
|
receiver_decl_id: declaration_id_from_lookup_name(&name_str),
|
|
1033
1012
|
},
|
|
1034
|
-
Vec::new(),
|
|
1035
1013
|
)
|
|
1036
1014
|
})
|
|
1037
1015
|
}
|
|
@@ -1060,22 +1038,20 @@ pub unsafe extern "C" fn rdx_graph_complete_method_argument(
|
|
|
1060
1038
|
};
|
|
1061
1039
|
|
|
1062
1040
|
with_mut_graph(pointer, |graph| {
|
|
1063
|
-
let Some((
|
|
1064
|
-
(unsafe { completion_nesting_name_id(graph, nesting, nesting_count) })
|
|
1065
|
-
else {
|
|
1041
|
+
let Some(tracked_name) = (unsafe { completion_nesting_name(graph, nesting, nesting_count) }) else {
|
|
1066
1042
|
return CompletionResult::success(ptr::null_mut());
|
|
1067
1043
|
};
|
|
1044
|
+
let nesting_name_id = tracked_name.name_id();
|
|
1068
1045
|
|
|
1069
1046
|
let self_decl_id = unsafe { decl_id_from_char_ptr(self_receiver) };
|
|
1070
1047
|
|
|
1071
1048
|
run_and_finalize_completion(
|
|
1072
|
-
graph,
|
|
1049
|
+
tracked_name.graph(),
|
|
1073
1050
|
CompletionReceiver::MethodArgument {
|
|
1074
1051
|
self_decl_id,
|
|
1075
1052
|
nesting_name_id,
|
|
1076
1053
|
method_decl_id: declaration_id_from_lookup_name(&name_str),
|
|
1077
1054
|
},
|
|
1078
|
-
names_to_untrack,
|
|
1079
1055
|
)
|
|
1080
1056
|
})
|
|
1081
1057
|
}
|
|
@@ -1196,6 +1172,7 @@ pub unsafe extern "C" fn rdx_keyword_free(ptr: *const CKeyword) {
|
|
|
1196
1172
|
|
|
1197
1173
|
#[cfg(test)]
|
|
1198
1174
|
mod tests {
|
|
1175
|
+
use crate::declaration_api;
|
|
1199
1176
|
use rubydex::indexing::ruby_indexer::RubyIndexer;
|
|
1200
1177
|
|
|
1201
1178
|
use super::*;
|
|
@@ -1246,7 +1223,9 @@ mod tests {
|
|
|
1246
1223
|
nesting_ptrs.as_ptr(),
|
|
1247
1224
|
nesting_ptrs.len(),
|
|
1248
1225
|
);
|
|
1226
|
+
assert!(!decl.is_null());
|
|
1249
1227
|
assert_eq!((*decl).id(), *DeclarationId::from("Foo::BAR"));
|
|
1228
|
+
declaration_api::free_c_declaration(decl);
|
|
1250
1229
|
};
|
|
1251
1230
|
|
|
1252
1231
|
let graph = unsafe { Box::from_raw(graph_ptr.cast::<RwLock<Graph>>()) };
|
|
@@ -1268,4 +1247,72 @@ mod tests {
|
|
|
1268
1247
|
.ref_count()
|
|
1269
1248
|
);
|
|
1270
1249
|
}
|
|
1250
|
+
|
|
1251
|
+
#[test]
|
|
1252
|
+
fn names_are_untracked_after_completion_error() {
|
|
1253
|
+
let mut indexer = RubyIndexer::new(
|
|
1254
|
+
"file:///foo.rb".into(),
|
|
1255
|
+
"
|
|
1256
|
+
class Foo
|
|
1257
|
+
end
|
|
1258
|
+
",
|
|
1259
|
+
);
|
|
1260
|
+
indexer.index();
|
|
1261
|
+
|
|
1262
|
+
let mut graph = Graph::new();
|
|
1263
|
+
graph.consume_document_changes(indexer.local_graph());
|
|
1264
|
+
let mut resolver = Resolver::new(&mut graph);
|
|
1265
|
+
resolver.resolve();
|
|
1266
|
+
|
|
1267
|
+
let (foo_name_id, foo_string_id, name_ref_count, string_ref_count) = graph
|
|
1268
|
+
.names()
|
|
1269
|
+
.iter()
|
|
1270
|
+
.find_map(|(name_id, name)| {
|
|
1271
|
+
let string_id = *name.str();
|
|
1272
|
+
if graph.strings().get(&string_id).unwrap().as_str() == "Foo"
|
|
1273
|
+
&& name.parent_scope().is_none()
|
|
1274
|
+
&& name.nesting().is_none()
|
|
1275
|
+
{
|
|
1276
|
+
Some((
|
|
1277
|
+
*name_id,
|
|
1278
|
+
string_id,
|
|
1279
|
+
name.ref_count(),
|
|
1280
|
+
graph.strings().get(&string_id).unwrap().ref_count(),
|
|
1281
|
+
))
|
|
1282
|
+
} else {
|
|
1283
|
+
None
|
|
1284
|
+
}
|
|
1285
|
+
})
|
|
1286
|
+
.unwrap();
|
|
1287
|
+
let name_count = graph.names().len();
|
|
1288
|
+
let string_count = graph.strings().len();
|
|
1289
|
+
|
|
1290
|
+
let graph_ptr = Box::into_raw(Box::new(RwLock::new(graph))) as GraphPointer;
|
|
1291
|
+
let nesting_strings = [CString::new("Foo").unwrap()];
|
|
1292
|
+
let nesting_ptrs: Vec<*const c_char> = nesting_strings.iter().map(|s| s.as_ptr()).collect();
|
|
1293
|
+
let self_receiver = CString::new("Nonexistent").unwrap();
|
|
1294
|
+
|
|
1295
|
+
let result = unsafe {
|
|
1296
|
+
rdx_graph_complete_expression(
|
|
1297
|
+
graph_ptr,
|
|
1298
|
+
nesting_ptrs.as_ptr(),
|
|
1299
|
+
nesting_ptrs.len(),
|
|
1300
|
+
self_receiver.as_ptr(),
|
|
1301
|
+
)
|
|
1302
|
+
};
|
|
1303
|
+
assert!(result.candidates.is_null());
|
|
1304
|
+
assert!(!result.error.is_null());
|
|
1305
|
+
utils::free_c_string(result.error);
|
|
1306
|
+
|
|
1307
|
+
let graph = unsafe { Box::from_raw(graph_ptr.cast::<RwLock<Graph>>()) };
|
|
1308
|
+
let graph = graph.read().unwrap();
|
|
1309
|
+
|
|
1310
|
+
assert_eq!(name_count, graph.names().len());
|
|
1311
|
+
assert_eq!(string_count, graph.strings().len());
|
|
1312
|
+
assert_eq!(name_ref_count, graph.names().get(&foo_name_id).unwrap().ref_count());
|
|
1313
|
+
assert_eq!(
|
|
1314
|
+
string_ref_count,
|
|
1315
|
+
graph.strings().get(&foo_string_id).unwrap().ref_count()
|
|
1316
|
+
);
|
|
1317
|
+
}
|
|
1271
1318
|
}
|