rubydex 0.2.4 → 0.2.6
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 +17 -16
- data/THIRD_PARTY_LICENSES.html +6 -6
- data/exe/rubydex_mcp +17 -0
- data/ext/rubydex/definition.c +89 -2
- data/ext/rubydex/document.c +36 -0
- data/ext/rubydex/extconf.rb +8 -0
- data/ext/rubydex/graph.c +32 -18
- data/ext/rubydex/handle.h +21 -5
- data/lib/rubydex/bin/rubydex_mcp.exe +0 -0
- data/lib/rubydex/declaration.rb +3 -3
- data/lib/rubydex/errors.rb +8 -0
- data/lib/rubydex/graph.rb +3 -1
- data/lib/rubydex/location.rb +24 -0
- data/lib/rubydex/version.rb +1 -1
- data/lib/rubydex.rb +1 -0
- data/rbi/rubydex.rbi +37 -14
- data/rust/Cargo.lock +3 -3
- data/rust/rubydex/Cargo.toml +7 -1
- data/rust/rubydex/src/dot.rs +609 -0
- data/rust/rubydex/src/indexing/local_graph.rs +38 -0
- data/rust/rubydex/src/indexing/rbs_indexer.rs +19 -1
- data/rust/rubydex/src/indexing/ruby_indexer.rs +10 -0
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +5 -1
- data/rust/rubydex/src/indexing.rs +38 -12
- data/rust/rubydex/src/lib.rs +2 -1
- data/rust/rubydex/src/listing.rs +14 -3
- data/rust/rubydex/src/main.rs +35 -7
- data/rust/rubydex/src/model/built_in.rs +5 -2
- data/rust/rubydex/src/model/comment.rs +2 -0
- data/rust/rubydex/src/model/declaration.rs +1 -0
- data/rust/rubydex/src/model/definitions.rs +20 -19
- data/rust/rubydex/src/model/document.rs +2 -0
- data/rust/rubydex/src/model/encoding.rs +2 -0
- data/rust/rubydex/src/model/graph.rs +51 -13
- data/rust/rubydex/src/model/identity_maps.rs +3 -0
- data/rust/rubydex/src/model/ids.rs +27 -1
- data/rust/rubydex/src/model/keywords.rs +3 -0
- data/rust/rubydex/src/model/name.rs +2 -0
- data/rust/rubydex/src/model/string_ref.rs +2 -0
- data/rust/rubydex/src/model/visibility.rs +3 -0
- data/rust/rubydex/src/operation/applier.rs +520 -0
- data/rust/rubydex/src/operation/mod.rs +285 -0
- data/rust/rubydex/src/operation/printer.rs +260 -0
- data/rust/rubydex/src/operation/ruby_builder.rs +2919 -0
- data/rust/rubydex/src/query.rs +114 -33
- data/rust/rubydex/src/resolution.rs +22 -9
- data/rust/rubydex/src/resolution_tests.rs +349 -209
- data/rust/rubydex/src/test_utils/graph_test.rs +19 -4
- data/rust/rubydex/src/test_utils/local_graph_test.rs +7 -6
- data/rust/rubydex/tests/cli.rs +17 -61
- data/rust/rubydex-mcp/Cargo.toml +9 -3
- data/rust/rubydex-mcp/src/server.rs +5 -1
- data/rust/rubydex-sys/Cargo.toml +9 -2
- data/rust/rubydex-sys/src/definition_api.rs +96 -2
- data/rust/rubydex-sys/src/document_api.rs +28 -0
- data/rust/rubydex-sys/src/graph_api.rs +2 -4
- metadata +11 -4
- data/rust/rubydex/src/visualization/dot.rs +0 -192
- data/rust/rubydex/src/visualization.rs +0 -6
|
@@ -338,6 +338,7 @@ impl<'a> RBSIndexer<'a> {
|
|
|
338
338
|
&mut self,
|
|
339
339
|
str_id: StringId,
|
|
340
340
|
offset: Offset,
|
|
341
|
+
name_offset: Offset,
|
|
341
342
|
comments: Box<[Comment]>,
|
|
342
343
|
flags: DefinitionFlags,
|
|
343
344
|
lexical_nesting_id: Option<DefinitionId>,
|
|
@@ -347,6 +348,7 @@ impl<'a> RBSIndexer<'a> {
|
|
|
347
348
|
str_id,
|
|
348
349
|
self.uri_id,
|
|
349
350
|
offset.clone(),
|
|
351
|
+
name_offset.clone(),
|
|
350
352
|
comments.clone(),
|
|
351
353
|
flags.clone(),
|
|
352
354
|
lexical_nesting_id,
|
|
@@ -360,6 +362,7 @@ impl<'a> RBSIndexer<'a> {
|
|
|
360
362
|
str_id,
|
|
361
363
|
self.uri_id,
|
|
362
364
|
offset,
|
|
365
|
+
name_offset,
|
|
363
366
|
comments,
|
|
364
367
|
flags,
|
|
365
368
|
lexical_nesting_id,
|
|
@@ -562,13 +565,22 @@ impl Visit for RBSIndexer<'_> {
|
|
|
562
565
|
fn visit_method_definition_node(&mut self, def_node: &node::MethodDefinitionNode) {
|
|
563
566
|
let str_id = self.local_graph.intern_string(format!("{}()", def_node.name()));
|
|
564
567
|
let offset = Offset::from_rbs_location(&def_node.location());
|
|
568
|
+
let name_offset = Offset::from_rbs_location(&def_node.name_location());
|
|
565
569
|
let comments = self.collect_comments(def_node.comment());
|
|
566
570
|
let flags = Self::flags(&def_node.annotations());
|
|
567
571
|
let lexical_nesting_id = self.parent_lexical_scope_id();
|
|
568
572
|
let signatures = self.collect_overload_signatures(def_node);
|
|
569
573
|
|
|
570
574
|
if def_node.kind() == node::MethodDefinitionKind::SingletonInstance {
|
|
571
|
-
self.register_singleton_instance_method(
|
|
575
|
+
self.register_singleton_instance_method(
|
|
576
|
+
str_id,
|
|
577
|
+
offset,
|
|
578
|
+
name_offset,
|
|
579
|
+
comments,
|
|
580
|
+
flags,
|
|
581
|
+
lexical_nesting_id,
|
|
582
|
+
signatures,
|
|
583
|
+
);
|
|
572
584
|
return;
|
|
573
585
|
}
|
|
574
586
|
|
|
@@ -602,6 +614,7 @@ impl Visit for RBSIndexer<'_> {
|
|
|
602
614
|
str_id,
|
|
603
615
|
self.uri_id,
|
|
604
616
|
offset,
|
|
617
|
+
name_offset,
|
|
605
618
|
comments,
|
|
606
619
|
flags,
|
|
607
620
|
lexical_nesting_id,
|
|
@@ -1197,6 +1210,7 @@ mod tests {
|
|
|
1197
1210
|
|
|
1198
1211
|
assert_definition_at!(&context, "2:3-2:22", Method, |def| {
|
|
1199
1212
|
assert_def_str_eq!(&context, def, "foo()");
|
|
1213
|
+
assert_def_name_offset_eq!(&context, def, "2:7-2:10");
|
|
1200
1214
|
assert!(def.receiver().is_none());
|
|
1201
1215
|
assert_eq!(def.visibility(), &Visibility::Public);
|
|
1202
1216
|
assert_eq!(class_def.id(), def.lexical_nesting_id().unwrap());
|
|
@@ -1205,6 +1219,7 @@ mod tests {
|
|
|
1205
1219
|
|
|
1206
1220
|
assert_definition_at!(&context, "4:3-4:23", Method, |def| {
|
|
1207
1221
|
assert_def_str_eq!(&context, def, "bar()");
|
|
1222
|
+
assert_def_name_offset_eq!(&context, def, "4:7-4:10");
|
|
1208
1223
|
assert!(def.receiver().is_none());
|
|
1209
1224
|
assert_eq!(def.visibility(), &Visibility::Public);
|
|
1210
1225
|
assert_eq!(class_def.id(), def.lexical_nesting_id().unwrap());
|
|
@@ -1398,6 +1413,7 @@ mod tests {
|
|
|
1398
1413
|
panic!()
|
|
1399
1414
|
};
|
|
1400
1415
|
assert_def_str_eq!(&context, instance_method, "foo()");
|
|
1416
|
+
assert_def_name_offset_eq!(&context, instance_method, "2:13-2:16");
|
|
1401
1417
|
assert_eq!(instance_method.visibility(), &Visibility::Private);
|
|
1402
1418
|
|
|
1403
1419
|
let singleton_method = definitions
|
|
@@ -1408,6 +1424,7 @@ mod tests {
|
|
|
1408
1424
|
panic!()
|
|
1409
1425
|
};
|
|
1410
1426
|
assert_def_str_eq!(&context, singleton_method, "foo()");
|
|
1427
|
+
assert_def_name_offset_eq!(&context, singleton_method, "2:13-2:16");
|
|
1411
1428
|
assert_eq!(singleton_method.visibility(), &Visibility::Public);
|
|
1412
1429
|
}
|
|
1413
1430
|
|
|
@@ -1425,6 +1442,7 @@ mod tests {
|
|
|
1425
1442
|
|
|
1426
1443
|
assert_definition_at!(&context, "2:3-2:27", Method, |def| {
|
|
1427
1444
|
assert_def_str_eq!(&context, def, "foo()");
|
|
1445
|
+
assert_def_name_offset_eq!(&context, def, "2:12-2:15");
|
|
1428
1446
|
let sigs = def.signatures().as_slice();
|
|
1429
1447
|
assert_eq!(sigs.len(), 1);
|
|
1430
1448
|
assert_eq!(sigs[0].len(), 0);
|
|
@@ -1811,6 +1811,7 @@ impl Visit<'_> for RubyIndexer<'_> {
|
|
|
1811
1811
|
let name = Self::location_to_string(&node.name_loc());
|
|
1812
1812
|
let str_id = self.local_graph.intern_string(format!("{name}()"));
|
|
1813
1813
|
let offset = Offset::from_prism_location(&node.location());
|
|
1814
|
+
let name_offset = Offset::from_prism_location(&node.name_loc());
|
|
1814
1815
|
let parent_nesting_id = self.current_nesting_definition_id();
|
|
1815
1816
|
let parameters = self.collect_parameters(node);
|
|
1816
1817
|
let is_singleton = node.receiver().is_some();
|
|
@@ -1862,6 +1863,7 @@ impl Visit<'_> for RubyIndexer<'_> {
|
|
|
1862
1863
|
str_id,
|
|
1863
1864
|
self.uri_id,
|
|
1864
1865
|
offset.clone(),
|
|
1866
|
+
name_offset.clone(),
|
|
1865
1867
|
comments.clone(),
|
|
1866
1868
|
flags.clone(),
|
|
1867
1869
|
parent_nesting_id,
|
|
@@ -1878,6 +1880,7 @@ impl Visit<'_> for RubyIndexer<'_> {
|
|
|
1878
1880
|
str_id,
|
|
1879
1881
|
self.uri_id,
|
|
1880
1882
|
offset,
|
|
1883
|
+
name_offset,
|
|
1881
1884
|
comments,
|
|
1882
1885
|
flags,
|
|
1883
1886
|
parent_nesting_id,
|
|
@@ -1895,6 +1898,7 @@ impl Visit<'_> for RubyIndexer<'_> {
|
|
|
1895
1898
|
str_id,
|
|
1896
1899
|
self.uri_id,
|
|
1897
1900
|
offset,
|
|
1901
|
+
name_offset,
|
|
1898
1902
|
comments,
|
|
1899
1903
|
flags,
|
|
1900
1904
|
parent_nesting_id,
|
|
@@ -2481,5 +2485,11 @@ impl Visit<'_> for RubyIndexer<'_> {
|
|
|
2481
2485
|
}
|
|
2482
2486
|
|
|
2483
2487
|
#[cfg(test)]
|
|
2488
|
+
fn backend() -> super::IndexerBackend {
|
|
2489
|
+
super::IndexerBackend::RubyIndexer
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
#[cfg(test)]
|
|
2493
|
+
#[allow(clippy::duplicate_mod)]
|
|
2484
2494
|
#[path = "ruby_indexer_tests.rs"]
|
|
2485
2495
|
mod tests;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// This file is included via #[path] by both ruby_indexer.rs and operation/applier.rs
|
|
2
|
+
// to run the same tests against both indexing backends. Each parent module provides
|
|
3
|
+
// a `backend()` function that `index_source` calls via `super::backend()`.
|
|
4
|
+
|
|
1
5
|
use crate::{
|
|
2
6
|
assert_def_comments_eq, assert_def_mixins_eq, assert_def_name_eq, assert_def_name_offset_eq, assert_def_str_eq,
|
|
3
7
|
assert_def_superclass_ref_eq, assert_definition_at, assert_dependents, assert_local_diagnostics_eq,
|
|
@@ -86,7 +90,7 @@ macro_rules! assert_method_references_eq {
|
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
fn index_source(source: &str) -> LocalGraphTest {
|
|
89
|
-
LocalGraphTest::
|
|
93
|
+
LocalGraphTest::new_with_backend("file:///foo.rb", source, super::backend())
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
mod constant_tests {
|
|
@@ -3,6 +3,7 @@ use crate::{
|
|
|
3
3
|
indexing::{local_graph::LocalGraph, rbs_indexer::RBSIndexer, ruby_indexer::RubyIndexer},
|
|
4
4
|
job_queue::{Job, JobQueue},
|
|
5
5
|
model::graph::Graph,
|
|
6
|
+
operation::ruby_builder::RubyOperationBuilder,
|
|
6
7
|
};
|
|
7
8
|
use crossbeam_channel::{Sender, unbounded};
|
|
8
9
|
use std::{ffi::OsStr, fs, path::PathBuf, sync::Arc};
|
|
@@ -12,6 +13,15 @@ pub mod local_graph;
|
|
|
12
13
|
pub mod rbs_indexer;
|
|
13
14
|
pub mod ruby_indexer;
|
|
14
15
|
|
|
16
|
+
/// Which backend to use for indexing Ruby files.
|
|
17
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
18
|
+
pub enum IndexerBackend {
|
|
19
|
+
/// The original tree-walking indexer.
|
|
20
|
+
RubyIndexer,
|
|
21
|
+
/// The two-phase operation builder + applier pipeline.
|
|
22
|
+
OperationBuilder,
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
/// The language of a source document, used to dispatch to the appropriate indexer
|
|
16
26
|
pub enum LanguageId {
|
|
17
27
|
Ruby,
|
|
@@ -42,15 +52,22 @@ impl LanguageId {
|
|
|
42
52
|
/// Job that indexes a single file
|
|
43
53
|
pub struct IndexingJob {
|
|
44
54
|
path: PathBuf,
|
|
55
|
+
backend: IndexerBackend,
|
|
45
56
|
local_graph_tx: Sender<LocalGraph>,
|
|
46
57
|
errors_tx: Sender<Errors>,
|
|
47
58
|
}
|
|
48
59
|
|
|
49
60
|
impl IndexingJob {
|
|
50
61
|
#[must_use]
|
|
51
|
-
pub fn new(
|
|
62
|
+
pub fn new(
|
|
63
|
+
path: PathBuf,
|
|
64
|
+
backend: IndexerBackend,
|
|
65
|
+
local_graph_tx: Sender<LocalGraph>,
|
|
66
|
+
errors_tx: Sender<Errors>,
|
|
67
|
+
) -> Self {
|
|
52
68
|
Self {
|
|
53
69
|
path,
|
|
70
|
+
backend,
|
|
54
71
|
local_graph_tx,
|
|
55
72
|
errors_tx,
|
|
56
73
|
}
|
|
@@ -84,7 +101,7 @@ impl Job for IndexingJob {
|
|
|
84
101
|
};
|
|
85
102
|
|
|
86
103
|
let language = self.path.extension().map_or(LanguageId::Ruby, LanguageId::from);
|
|
87
|
-
let local_graph = build_local_graph(url.to_string(), &source, &language);
|
|
104
|
+
let local_graph = build_local_graph(url.to_string(), &source, &language, self.backend);
|
|
88
105
|
|
|
89
106
|
self.local_graph_tx
|
|
90
107
|
.send(local_graph)
|
|
@@ -94,7 +111,7 @@ impl Job for IndexingJob {
|
|
|
94
111
|
|
|
95
112
|
/// Indexes a single source string in memory, dispatching to the appropriate indexer based on `language_id`.
|
|
96
113
|
pub fn index_source(graph: &mut Graph, uri: &str, source: &str, language_id: &LanguageId) {
|
|
97
|
-
let local_graph = build_local_graph(uri.to_string(), source, language_id);
|
|
114
|
+
let local_graph = build_local_graph(uri.to_string(), source, language_id, IndexerBackend::RubyIndexer);
|
|
98
115
|
graph.consume_document_changes(local_graph);
|
|
99
116
|
}
|
|
100
117
|
|
|
@@ -103,7 +120,7 @@ pub fn index_source(graph: &mut Graph, uri: &str, source: &str, language_id: &La
|
|
|
103
120
|
/// # Panics
|
|
104
121
|
///
|
|
105
122
|
/// Will panic if the graph cannot be wrapped in an Arc<Mutex<>>
|
|
106
|
-
pub fn index_files(graph: &mut Graph, paths: Vec<PathBuf
|
|
123
|
+
pub fn index_files(graph: &mut Graph, paths: Vec<PathBuf>, backend: IndexerBackend) -> Vec<Errors> {
|
|
107
124
|
let queue = Arc::new(JobQueue::new());
|
|
108
125
|
let (local_graphs_tx, local_graphs_rx) = unbounded();
|
|
109
126
|
let (errors_tx, errors_rx) = unbounded();
|
|
@@ -111,6 +128,7 @@ pub fn index_files(graph: &mut Graph, paths: Vec<PathBuf>) -> Vec<Errors> {
|
|
|
111
128
|
for path in paths {
|
|
112
129
|
queue.push(Box::new(IndexingJob::new(
|
|
113
130
|
path,
|
|
131
|
+
backend,
|
|
114
132
|
local_graphs_tx.clone(),
|
|
115
133
|
errors_tx.clone(),
|
|
116
134
|
)));
|
|
@@ -134,13 +152,21 @@ pub fn index_files(graph: &mut Graph, paths: Vec<PathBuf>) -> Vec<Errors> {
|
|
|
134
152
|
}
|
|
135
153
|
|
|
136
154
|
/// Indexes a source string using the appropriate indexer for the given language.
|
|
137
|
-
|
|
155
|
+
#[must_use]
|
|
156
|
+
pub fn build_local_graph(uri: String, source: &str, language: &LanguageId, backend: IndexerBackend) -> LocalGraph {
|
|
138
157
|
match language {
|
|
139
|
-
LanguageId::Ruby => {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
158
|
+
LanguageId::Ruby => match backend {
|
|
159
|
+
IndexerBackend::RubyIndexer => {
|
|
160
|
+
let mut indexer = RubyIndexer::new(uri, source);
|
|
161
|
+
indexer.index();
|
|
162
|
+
indexer.local_graph()
|
|
163
|
+
}
|
|
164
|
+
IndexerBackend::OperationBuilder => {
|
|
165
|
+
let builder = RubyOperationBuilder::new(uri, source);
|
|
166
|
+
let result = builder.build();
|
|
167
|
+
crate::operation::applier::apply_operations(result)
|
|
168
|
+
}
|
|
169
|
+
},
|
|
144
170
|
LanguageId::Rbs => {
|
|
145
171
|
let mut indexer = RBSIndexer::new(uri, source);
|
|
146
172
|
indexer.index();
|
|
@@ -175,7 +201,7 @@ mod tests {
|
|
|
175
201
|
let relative_to_pwd = &dots.join(absolute_path);
|
|
176
202
|
|
|
177
203
|
let mut graph = Graph::new();
|
|
178
|
-
let errors = index_files(&mut graph, vec![relative_to_pwd.clone()]);
|
|
204
|
+
let errors = index_files(&mut graph, vec![relative_to_pwd.clone()], IndexerBackend::RubyIndexer);
|
|
179
205
|
|
|
180
206
|
assert!(errors.is_empty());
|
|
181
207
|
assert_eq!(graph.documents().len(), 2);
|
|
@@ -196,7 +222,7 @@ mod tests {
|
|
|
196
222
|
let uri = Url::from_file_path(&path).unwrap().to_string();
|
|
197
223
|
|
|
198
224
|
let mut graph = Graph::new();
|
|
199
|
-
let errors = index_files(&mut graph, vec![path]);
|
|
225
|
+
let errors = index_files(&mut graph, vec![path], IndexerBackend::RubyIndexer);
|
|
200
226
|
|
|
201
227
|
assert!(errors.is_empty(), "Expected no errors, got: {errors:#?}");
|
|
202
228
|
assert_eq!(6, graph.definitions().len());
|
data/rust/rubydex/src/lib.rs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
pub mod compile_assertions;
|
|
2
2
|
pub mod diagnostic;
|
|
3
|
+
pub mod dot;
|
|
3
4
|
pub mod errors;
|
|
4
5
|
pub mod indexing;
|
|
5
6
|
pub mod integrity;
|
|
@@ -7,11 +8,11 @@ pub mod job_queue;
|
|
|
7
8
|
pub mod listing;
|
|
8
9
|
pub mod model;
|
|
9
10
|
pub mod offset;
|
|
11
|
+
pub mod operation;
|
|
10
12
|
pub mod position;
|
|
11
13
|
pub mod query;
|
|
12
14
|
pub mod resolution;
|
|
13
15
|
pub mod stats;
|
|
14
|
-
pub mod visualization;
|
|
15
16
|
|
|
16
17
|
#[cfg(any(test, feature = "test_utils"))]
|
|
17
18
|
pub mod test_utils;
|
data/rust/rubydex/src/listing.rs
CHANGED
|
@@ -38,9 +38,14 @@ impl FileDiscoveryJob {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
fn is_indexable_file(path: &Path) -> bool {
|
|
42
|
+
path.extension()
|
|
43
|
+
.is_some_and(|ext| ext == "rb" || ext == "rake" || ext == "rbs" || ext == "ru")
|
|
44
|
+
}
|
|
45
|
+
|
|
41
46
|
impl FileDiscoveryJob {
|
|
42
47
|
fn handle_file(&self, path: &Path) {
|
|
43
|
-
if path
|
|
48
|
+
if is_indexable_file(path) {
|
|
44
49
|
self.paths_tx
|
|
45
50
|
.send(path.to_path_buf())
|
|
46
51
|
.expect("file receiver dropped before run completion");
|
|
@@ -266,22 +271,28 @@ mod tests {
|
|
|
266
271
|
}
|
|
267
272
|
|
|
268
273
|
#[test]
|
|
269
|
-
fn
|
|
274
|
+
fn collect_indexable_files() {
|
|
270
275
|
let context = Context::new();
|
|
271
276
|
let ruby_file = PathBuf::from("lib").join("foo.rb");
|
|
277
|
+
let rake_file = PathBuf::from("lib").join("task.rake");
|
|
272
278
|
let rbs_file = PathBuf::from("sig").join("foo.rbs");
|
|
279
|
+
let rack_file = PathBuf::from("config.ru");
|
|
273
280
|
let txt_file = PathBuf::from("lib").join("notes.txt");
|
|
274
281
|
context.touch(&ruby_file);
|
|
282
|
+
context.touch(&rake_file);
|
|
275
283
|
context.touch(&rbs_file);
|
|
284
|
+
context.touch(&rack_file);
|
|
276
285
|
context.touch(&txt_file);
|
|
277
286
|
|
|
278
|
-
let (files, errors) = collect_document_paths(&context, &["lib", "sig"]);
|
|
287
|
+
let (files, errors) = collect_document_paths(&context, &["lib", "sig", "config.ru"]);
|
|
279
288
|
|
|
280
289
|
assert!(errors.is_empty());
|
|
281
290
|
|
|
282
291
|
assert_eq!(
|
|
283
292
|
[
|
|
293
|
+
rack_file.to_str().unwrap().to_string(),
|
|
284
294
|
ruby_file.to_str().unwrap().to_string(),
|
|
295
|
+
rake_file.to_str().unwrap().to_string(),
|
|
285
296
|
rbs_file.to_str().unwrap().to_string(),
|
|
286
297
|
],
|
|
287
298
|
files.as_slice()
|
data/rust/rubydex/src/main.rs
CHANGED
|
@@ -2,14 +2,15 @@ use clap::{Parser, ValueEnum};
|
|
|
2
2
|
use std::{collections::HashSet, mem};
|
|
3
3
|
|
|
4
4
|
use rubydex::{
|
|
5
|
-
|
|
5
|
+
dot,
|
|
6
|
+
indexing::{self, IndexerBackend},
|
|
7
|
+
integrity, listing,
|
|
6
8
|
model::graph::Graph,
|
|
7
9
|
resolution::Resolver,
|
|
8
10
|
stats::{
|
|
9
11
|
memory::MemoryStats,
|
|
10
12
|
timer::{Timer, time_it},
|
|
11
13
|
},
|
|
12
|
-
visualization::dot,
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
#[derive(Parser, Debug)]
|
|
@@ -22,8 +23,11 @@ struct Args {
|
|
|
22
23
|
#[arg(long = "stop-after", help = "Stop after the given stage")]
|
|
23
24
|
stop_after: Option<StopAfter>,
|
|
24
25
|
|
|
25
|
-
#[arg(long = "
|
|
26
|
-
|
|
26
|
+
#[arg(long = "dot", help = "Output a DOT graph visualization")]
|
|
27
|
+
dot: bool,
|
|
28
|
+
|
|
29
|
+
#[arg(long = "show-builtins", help = "Include built-in declarations in DOT output")]
|
|
30
|
+
show_builtins: bool,
|
|
27
31
|
|
|
28
32
|
#[arg(long = "stats", help = "Show detailed performance statistics")]
|
|
29
33
|
stats: bool,
|
|
@@ -31,6 +35,14 @@ struct Args {
|
|
|
31
35
|
#[arg(long = "check-integrity", help = "Check the integrity of the graph after resolution")]
|
|
32
36
|
check_integrity: bool,
|
|
33
37
|
|
|
38
|
+
#[arg(
|
|
39
|
+
long = "indexer",
|
|
40
|
+
value_enum,
|
|
41
|
+
default_value = "ruby-indexer",
|
|
42
|
+
help = "Which indexer backend to use for Ruby files"
|
|
43
|
+
)]
|
|
44
|
+
indexer: Indexer,
|
|
45
|
+
|
|
34
46
|
#[arg(
|
|
35
47
|
long = "report-orphans",
|
|
36
48
|
value_name = "PATH",
|
|
@@ -49,6 +61,21 @@ enum StopAfter {
|
|
|
49
61
|
Resolution,
|
|
50
62
|
}
|
|
51
63
|
|
|
64
|
+
#[derive(Debug, Clone, ValueEnum)]
|
|
65
|
+
enum Indexer {
|
|
66
|
+
RubyIndexer,
|
|
67
|
+
OperationBuilder,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
impl From<&Indexer> for IndexerBackend {
|
|
71
|
+
fn from(indexer: &Indexer) -> Self {
|
|
72
|
+
match indexer {
|
|
73
|
+
Indexer::RubyIndexer => IndexerBackend::RubyIndexer,
|
|
74
|
+
Indexer::OperationBuilder => IndexerBackend::OperationBuilder,
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
52
79
|
fn exit(print_stats: bool) {
|
|
53
80
|
if print_stats {
|
|
54
81
|
Timer::print_breakdown();
|
|
@@ -80,7 +107,8 @@ fn main() {
|
|
|
80
107
|
// Indexing
|
|
81
108
|
|
|
82
109
|
let mut graph = Graph::new();
|
|
83
|
-
let
|
|
110
|
+
let backend = IndexerBackend::from(&args.indexer);
|
|
111
|
+
let errors = time_it!(indexing, { indexing::index_files(&mut graph, file_paths, backend) });
|
|
84
112
|
|
|
85
113
|
for error in errors {
|
|
86
114
|
eprintln!("{error}");
|
|
@@ -146,8 +174,8 @@ fn main() {
|
|
|
146
174
|
}
|
|
147
175
|
|
|
148
176
|
// Generate visualization or print statistics
|
|
149
|
-
if args.
|
|
150
|
-
println!("{}", dot::generate(&graph));
|
|
177
|
+
if args.dot {
|
|
178
|
+
println!("{}", dot::DotBuilder::generate(&graph, args.show_builtins));
|
|
151
179
|
} else {
|
|
152
180
|
println!("Indexed {} files", graph.documents().len());
|
|
153
181
|
println!("Found {} names", graph.declarations().len());
|
|
@@ -7,10 +7,13 @@ use crate::{
|
|
|
7
7
|
model::{
|
|
8
8
|
declaration::{ClassDeclaration, Declaration, Namespace},
|
|
9
9
|
graph::Graph,
|
|
10
|
-
ids::DeclarationId,
|
|
10
|
+
ids::{DeclarationId, UriId},
|
|
11
11
|
},
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
pub const BUILT_IN_URI: &str = "rubydex:built-in";
|
|
15
|
+
pub static BUILT_IN_URI_ID: LazyLock<UriId> = LazyLock::new(|| UriId::from(BUILT_IN_URI));
|
|
16
|
+
|
|
14
17
|
pub static KERNEL_ID: LazyLock<DeclarationId> = LazyLock::new(|| DeclarationId::from("Kernel"));
|
|
15
18
|
pub static BASIC_OBJECT_ID: LazyLock<DeclarationId> = LazyLock::new(|| DeclarationId::from("BasicObject"));
|
|
16
19
|
pub static OBJECT_ID: LazyLock<DeclarationId> = LazyLock::new(|| DeclarationId::from("Object"));
|
|
@@ -27,7 +30,7 @@ pub fn add_built_in_data(graph: &mut Graph) {
|
|
|
27
30
|
// We need definitions to ensure that ancestor linearization happens naturally through the algorithm. Trying to set
|
|
28
31
|
// ancestors directly on declarations doesn't work because the algorithm erases the ancestors and there are no
|
|
29
32
|
// definitions to inform it of the superclasses and mixins.
|
|
30
|
-
let uri = Url::parse(
|
|
33
|
+
let uri = Url::parse(BUILT_IN_URI).unwrap();
|
|
31
34
|
let source = r"
|
|
32
35
|
class BasicObject
|
|
33
36
|
end
|
|
@@ -29,7 +29,7 @@ use crate::{
|
|
|
29
29
|
assert_mem_size,
|
|
30
30
|
model::{
|
|
31
31
|
comment::Comment,
|
|
32
|
-
ids::{ConstantReferenceId, DefinitionId, NameId, StringId, UriId},
|
|
32
|
+
ids::{self, ConstantReferenceId, DefinitionId, NameId, StringId, UriId},
|
|
33
33
|
visibility::Visibility,
|
|
34
34
|
},
|
|
35
35
|
offset::Offset,
|
|
@@ -43,6 +43,7 @@ bitflags! {
|
|
|
43
43
|
const SINGLETON_METHOD_VISIBILITY = 0b0100;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
+
assert_mem_size!(DefinitionFlags, 1);
|
|
46
47
|
|
|
47
48
|
impl DefinitionFlags {
|
|
48
49
|
#[must_use]
|
|
@@ -181,6 +182,7 @@ impl Definition {
|
|
|
181
182
|
Definition::Class(d) => Some(d.name_offset()),
|
|
182
183
|
Definition::Module(d) => Some(d.name_offset()),
|
|
183
184
|
Definition::SingletonClass(d) => Some(d.name_offset()),
|
|
185
|
+
Definition::Method(d) => Some(d.name_offset()),
|
|
184
186
|
_ => None,
|
|
185
187
|
}
|
|
186
188
|
}
|
|
@@ -199,6 +201,7 @@ pub enum Mixin {
|
|
|
199
201
|
Prepend(PrependDefinition),
|
|
200
202
|
Extend(ExtendDefinition),
|
|
201
203
|
}
|
|
204
|
+
assert_mem_size!(Mixin, 16);
|
|
202
205
|
|
|
203
206
|
impl Mixin {
|
|
204
207
|
#[must_use]
|
|
@@ -289,7 +292,7 @@ impl ClassDefinition {
|
|
|
289
292
|
|
|
290
293
|
#[must_use]
|
|
291
294
|
pub fn id(&self) -> DefinitionId {
|
|
292
|
-
|
|
295
|
+
ids::namespace_definition_id(self.uri_id, &self.offset, self.name_id)
|
|
293
296
|
}
|
|
294
297
|
|
|
295
298
|
#[must_use]
|
|
@@ -411,7 +414,7 @@ impl SingletonClassDefinition {
|
|
|
411
414
|
|
|
412
415
|
#[must_use]
|
|
413
416
|
pub fn id(&self) -> DefinitionId {
|
|
414
|
-
|
|
417
|
+
ids::namespace_definition_id(self.uri_id, &self.offset, self.name_id)
|
|
415
418
|
}
|
|
416
419
|
|
|
417
420
|
#[must_use]
|
|
@@ -515,7 +518,7 @@ impl ModuleDefinition {
|
|
|
515
518
|
|
|
516
519
|
#[must_use]
|
|
517
520
|
pub fn id(&self) -> DefinitionId {
|
|
518
|
-
|
|
521
|
+
ids::namespace_definition_id(self.uri_id, &self.offset, self.name_id)
|
|
519
522
|
}
|
|
520
523
|
|
|
521
524
|
#[must_use]
|
|
@@ -611,7 +614,7 @@ impl ConstantDefinition {
|
|
|
611
614
|
|
|
612
615
|
#[must_use]
|
|
613
616
|
pub fn id(&self) -> DefinitionId {
|
|
614
|
-
|
|
617
|
+
ids::namespace_definition_id(self.uri_id, &self.offset, self.name_id)
|
|
615
618
|
}
|
|
616
619
|
|
|
617
620
|
#[must_use]
|
|
@@ -891,6 +894,7 @@ pub enum Signatures {
|
|
|
891
894
|
/// Used for RBS definitions with more than one overload.
|
|
892
895
|
Overloaded(Box<[Signature]>),
|
|
893
896
|
}
|
|
897
|
+
assert_mem_size!(Signatures, 24);
|
|
894
898
|
|
|
895
899
|
impl Signatures {
|
|
896
900
|
/// Returns all signatures as a slice, regardless of variant.
|
|
@@ -915,6 +919,7 @@ pub struct MethodDefinition {
|
|
|
915
919
|
str_id: StringId,
|
|
916
920
|
uri_id: UriId,
|
|
917
921
|
offset: Offset,
|
|
922
|
+
name_offset: Offset,
|
|
918
923
|
flags: DefinitionFlags,
|
|
919
924
|
comments: Box<[Comment]>,
|
|
920
925
|
lexical_nesting_id: Option<DefinitionId>,
|
|
@@ -923,10 +928,10 @@ pub struct MethodDefinition {
|
|
|
923
928
|
receiver: Option<Receiver>,
|
|
924
929
|
}
|
|
925
930
|
|
|
926
|
-
assert_mem_size!(MethodDefinition,
|
|
931
|
+
assert_mem_size!(MethodDefinition, 104);
|
|
927
932
|
|
|
928
933
|
/// The receiver of a singleton method definition.
|
|
929
|
-
#[derive(Debug)]
|
|
934
|
+
#[derive(Debug, Clone)]
|
|
930
935
|
pub enum Receiver {
|
|
931
936
|
/// `def self.foo` - receiver is the enclosing definition (class, module, singleton class or DSL)
|
|
932
937
|
SelfReceiver(DefinitionId),
|
|
@@ -943,6 +948,7 @@ impl MethodDefinition {
|
|
|
943
948
|
str_id: StringId,
|
|
944
949
|
uri_id: UriId,
|
|
945
950
|
offset: Offset,
|
|
951
|
+
name_offset: Offset,
|
|
946
952
|
comments: Box<[Comment]>,
|
|
947
953
|
flags: DefinitionFlags,
|
|
948
954
|
lexical_nesting_id: Option<DefinitionId>,
|
|
@@ -954,6 +960,7 @@ impl MethodDefinition {
|
|
|
954
960
|
str_id,
|
|
955
961
|
uri_id,
|
|
956
962
|
offset,
|
|
963
|
+
name_offset,
|
|
957
964
|
flags,
|
|
958
965
|
comments,
|
|
959
966
|
lexical_nesting_id,
|
|
@@ -965,18 +972,7 @@ impl MethodDefinition {
|
|
|
965
972
|
|
|
966
973
|
#[must_use]
|
|
967
974
|
pub fn id(&self) -> DefinitionId {
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
if let Some(receiver) = &self.receiver {
|
|
971
|
-
match receiver {
|
|
972
|
-
Receiver::SelfReceiver(def_id) => formatted_id.push_str(&def_id.to_string()),
|
|
973
|
-
Receiver::ConstantReceiver(name_id) => {
|
|
974
|
-
formatted_id.push_str(&name_id.to_string());
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
DefinitionId::from(&formatted_id)
|
|
975
|
+
ids::method_definition_id(self.uri_id, &self.offset, self.str_id, self.receiver.as_ref())
|
|
980
976
|
}
|
|
981
977
|
|
|
982
978
|
#[must_use]
|
|
@@ -994,6 +990,11 @@ impl MethodDefinition {
|
|
|
994
990
|
&self.offset
|
|
995
991
|
}
|
|
996
992
|
|
|
993
|
+
#[must_use]
|
|
994
|
+
pub fn name_offset(&self) -> &Offset {
|
|
995
|
+
&self.name_offset
|
|
996
|
+
}
|
|
997
|
+
|
|
997
998
|
#[must_use]
|
|
998
999
|
pub fn comments(&self) -> &[Comment] {
|
|
999
1000
|
&self.comments
|
|
@@ -3,6 +3,7 @@ use std::path::PathBuf;
|
|
|
3
3
|
use line_index::LineIndex;
|
|
4
4
|
use url::Url;
|
|
5
5
|
|
|
6
|
+
use crate::assert_mem_size;
|
|
6
7
|
use crate::diagnostic::Diagnostic;
|
|
7
8
|
use crate::model::ids::{ConstantReferenceId, DefinitionId, MethodReferenceId};
|
|
8
9
|
|
|
@@ -17,6 +18,7 @@ pub struct Document {
|
|
|
17
18
|
constant_reference_ids: Vec<ConstantReferenceId>,
|
|
18
19
|
diagnostics: Vec<Diagnostic>,
|
|
19
20
|
}
|
|
21
|
+
assert_mem_size!(Document, 176);
|
|
20
22
|
|
|
21
23
|
impl Document {
|
|
22
24
|
#[must_use]
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
use crate::assert_mem_size;
|
|
1
2
|
use line_index::WideEncoding;
|
|
2
3
|
|
|
3
4
|
#[derive(Default, Debug)]
|
|
@@ -7,6 +8,7 @@ pub enum Encoding {
|
|
|
7
8
|
Utf16,
|
|
8
9
|
Utf32,
|
|
9
10
|
}
|
|
11
|
+
assert_mem_size!(Encoding, 1);
|
|
10
12
|
|
|
11
13
|
impl Encoding {
|
|
12
14
|
/// Transform the LSP selected encoding into the expected `WideEncoding` for converting code units with the
|