rubydex 0.2.4-aarch64-linux → 0.2.6-aarch64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +17 -16
  3. data/THIRD_PARTY_LICENSES.html +6 -6
  4. data/exe/rubydex_mcp +17 -0
  5. data/ext/rubydex/definition.c +89 -2
  6. data/ext/rubydex/document.c +36 -0
  7. data/ext/rubydex/extconf.rb +8 -0
  8. data/ext/rubydex/graph.c +32 -18
  9. data/ext/rubydex/handle.h +21 -5
  10. data/lib/rubydex/3.2/rubydex.so +0 -0
  11. data/lib/rubydex/3.3/rubydex.so +0 -0
  12. data/lib/rubydex/3.4/rubydex.so +0 -0
  13. data/lib/rubydex/4.0/rubydex.so +0 -0
  14. data/lib/rubydex/bin/rubydex_mcp +0 -0
  15. data/lib/rubydex/declaration.rb +3 -3
  16. data/lib/rubydex/errors.rb +8 -0
  17. data/lib/rubydex/graph.rb +3 -1
  18. data/lib/rubydex/librubydex_sys.so +0 -0
  19. data/lib/rubydex/location.rb +24 -0
  20. data/lib/rubydex/version.rb +1 -1
  21. data/lib/rubydex.rb +1 -0
  22. data/rbi/rubydex.rbi +37 -14
  23. data/rust/Cargo.lock +3 -3
  24. data/rust/rubydex/Cargo.toml +7 -1
  25. data/rust/rubydex/src/dot.rs +609 -0
  26. data/rust/rubydex/src/indexing/local_graph.rs +38 -0
  27. data/rust/rubydex/src/indexing/rbs_indexer.rs +19 -1
  28. data/rust/rubydex/src/indexing/ruby_indexer.rs +10 -0
  29. data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +5 -1
  30. data/rust/rubydex/src/indexing.rs +38 -12
  31. data/rust/rubydex/src/lib.rs +2 -1
  32. data/rust/rubydex/src/listing.rs +14 -3
  33. data/rust/rubydex/src/main.rs +35 -7
  34. data/rust/rubydex/src/model/built_in.rs +5 -2
  35. data/rust/rubydex/src/model/comment.rs +2 -0
  36. data/rust/rubydex/src/model/declaration.rs +1 -0
  37. data/rust/rubydex/src/model/definitions.rs +20 -19
  38. data/rust/rubydex/src/model/document.rs +2 -0
  39. data/rust/rubydex/src/model/encoding.rs +2 -0
  40. data/rust/rubydex/src/model/graph.rs +51 -13
  41. data/rust/rubydex/src/model/identity_maps.rs +3 -0
  42. data/rust/rubydex/src/model/ids.rs +27 -1
  43. data/rust/rubydex/src/model/keywords.rs +3 -0
  44. data/rust/rubydex/src/model/name.rs +2 -0
  45. data/rust/rubydex/src/model/string_ref.rs +2 -0
  46. data/rust/rubydex/src/model/visibility.rs +3 -0
  47. data/rust/rubydex/src/operation/applier.rs +520 -0
  48. data/rust/rubydex/src/operation/mod.rs +285 -0
  49. data/rust/rubydex/src/operation/printer.rs +260 -0
  50. data/rust/rubydex/src/operation/ruby_builder.rs +2919 -0
  51. data/rust/rubydex/src/query.rs +114 -33
  52. data/rust/rubydex/src/resolution.rs +22 -9
  53. data/rust/rubydex/src/resolution_tests.rs +349 -209
  54. data/rust/rubydex/src/test_utils/graph_test.rs +19 -4
  55. data/rust/rubydex/src/test_utils/local_graph_test.rs +7 -6
  56. data/rust/rubydex/tests/cli.rs +17 -61
  57. data/rust/rubydex-mcp/Cargo.toml +9 -3
  58. data/rust/rubydex-mcp/src/server.rs +5 -1
  59. data/rust/rubydex-sys/Cargo.toml +9 -2
  60. data/rust/rubydex-sys/src/definition_api.rs +96 -2
  61. data/rust/rubydex-sys/src/document_api.rs +28 -0
  62. data/rust/rubydex-sys/src/graph_api.rs +2 -4
  63. metadata +11 -4
  64. data/rust/rubydex/src/visualization/dot.rs +0 -192
  65. 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(str_id, offset, comments, flags, lexical_nesting_id, signatures);
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::new("file:///foo.rb", source)
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(path: PathBuf, local_graph_tx: Sender<LocalGraph>, errors_tx: Sender<Errors>) -> Self {
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>) -> Vec<Errors> {
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
- fn build_local_graph(uri: String, source: &str, language: &LanguageId) -> LocalGraph {
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
- let mut indexer = RubyIndexer::new(uri, source);
141
- indexer.index();
142
- indexer.local_graph()
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());
@@ -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;
@@ -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.extension().is_some_and(|ext| ext == "rb" || ext == "rbs") {
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 collect_rbs_files() {
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()
@@ -2,14 +2,15 @@ use clap::{Parser, ValueEnum};
2
2
  use std::{collections::HashSet, mem};
3
3
 
4
4
  use rubydex::{
5
- indexing, integrity, listing,
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 = "visualize")]
26
- visualize: bool,
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 errors = time_it!(indexing, { indexing::index_files(&mut graph, file_paths) });
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.visualize {
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("rubydex:built-in").unwrap();
33
+ let uri = Url::parse(BUILT_IN_URI).unwrap();
31
34
  let source = r"
32
35
  class BasicObject
33
36
  end
@@ -1,3 +1,4 @@
1
+ use crate::assert_mem_size;
1
2
  use crate::offset::Offset;
2
3
 
3
4
  #[derive(Debug, Clone)]
@@ -5,6 +6,7 @@ pub struct Comment {
5
6
  offset: Offset,
6
7
  string: String,
7
8
  }
9
+ assert_mem_size!(Comment, 32);
8
10
 
9
11
  impl Comment {
10
12
  #[must_use]
@@ -16,6 +16,7 @@ pub enum Ancestor {
16
16
  /// A partial ancestor that is missing linearization
17
17
  Partial(NameId),
18
18
  }
19
+ assert_mem_size!(Ancestor, 16);
19
20
 
20
21
  /// The ancestor chain and its current state
21
22
  #[derive(Debug, Clone)]
@@ -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
- DefinitionId::from(&format!("{}{}{}", *self.uri_id, self.offset.start(), *self.name_id))
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
- DefinitionId::from(&format!("{}{}{}", *self.uri_id, self.offset.start(), *self.name_id))
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
- DefinitionId::from(&format!("{}{}{}", *self.uri_id, self.offset.start(), *self.name_id))
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
- DefinitionId::from(&format!("{}{}{}", *self.uri_id, self.offset.start(), *self.name_id))
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, 96);
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
- let mut formatted_id = format!("{}{}{}", *self.uri_id, self.offset.start(), *self.str_id);
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