rubydex 0.2.6 → 0.2.7

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.
@@ -1,6 +1,11 @@
1
1
  #include "signature.h"
2
2
  #include "location.h"
3
3
 
4
+ /*
5
+ * RDoc parser workaround for https://github.com/ruby/rdoc/issues/1744:
6
+ * mRubydex = rb_define_module("Rubydex")
7
+ */
8
+
4
9
  static VALUE empty_params = Qundef;
5
10
 
6
11
  VALUE cSignature;
data/ext/rubydex/utils.c CHANGED
@@ -39,6 +39,19 @@ void rdxi_check_array_of_strings(VALUE array) {
39
39
  }
40
40
  }
41
41
 
42
+ // Convert a Rust-owned C string to a Ruby string and release it with free_c_string.
43
+ // Returns nil when the Rust side returned NULL.
44
+ VALUE rdxi_owned_c_string_to_ruby(const char *string) {
45
+ if (string == NULL) {
46
+ return Qnil;
47
+ }
48
+
49
+ VALUE value = rb_utf8_str_new_cstr(string);
50
+ free_c_string(string);
51
+
52
+ return value;
53
+ }
54
+
42
55
  // Yield body for iterating over declarations
43
56
  VALUE rdxi_declarations_yield(VALUE args) {
44
57
  VALUE self = rb_ary_entry(args, 0);
data/ext/rubydex/utils.h CHANGED
@@ -13,6 +13,10 @@ void rdxi_free_str_array(char **array, size_t length);
13
13
  // Verify that the Ruby object is an array of strings or raise `TypeError`
14
14
  void rdxi_check_array_of_strings(VALUE array);
15
15
 
16
+ // Convert a Rust-owned C string to a Ruby string and release it with free_c_string.
17
+ // Returns nil when the Rust side returned NULL.
18
+ VALUE rdxi_owned_c_string_to_ruby(const char *string);
19
+
16
20
  // Yield body for iterating over declarations
17
21
  VALUE rdxi_declarations_yield(VALUE args);
18
22
 
Binary file
@@ -5,4 +5,7 @@ module Rubydex
5
5
 
6
6
  # Raised when `MethodAliasDefinition#target` walks an alias chain that loops back on itself.
7
7
  class AliasCycleError < Error; end
8
+
9
+ # Raised by `Graph#load_config` when the requested config file does not exist, cannot be read, or is malformed
10
+ class ConfigError < Error; end
8
11
  end
data/lib/rubydex/graph.rb CHANGED
@@ -5,49 +5,30 @@ module Rubydex
5
5
  #
6
6
  # Note: this class is partially defined in C to integrate with the Rust backend
7
7
  class Graph
8
- IGNORED_DIRECTORIES = [
9
- ".bundle",
10
- ".claude",
11
- ".git",
12
- ".github",
13
- ".ruby-lsp",
14
- ".vscode",
15
- "log",
16
- "node_modules",
17
- "tmp",
18
- ].freeze
19
-
20
8
  INDEXABLE_EXTENSIONS = [".rb", ".rake", ".rbs", ".ru"].freeze
21
9
 
22
- #: String
23
- attr_accessor :workspace_path
24
-
25
- #: (?workspace_path: String) -> void
26
- def initialize(workspace_path: Dir.pwd)
27
- @workspace_path = workspace_path
28
-
29
- exclude_paths(IGNORED_DIRECTORIES.map { |dir| File.join(@workspace_path, dir) })
10
+ #: (?workspace_path: String?) -> void
11
+ def initialize(workspace_path: nil)
12
+ self.workspace_path = workspace_path if workspace_path
30
13
  end
31
14
 
32
- # Index all files and dependencies of the workspace that exists in `@workspace_path`
15
+ # Index all files and dependencies of the workspace that exists in `workspace_path`
33
16
  #: -> Array[String]
34
17
  def index_workspace
35
18
  index_all(workspace_paths)
36
19
  end
37
20
 
38
- # Returns all workspace paths that should be indexed, excluding directories that we don't need to descend into such
39
- # as `.git`, `node_modules`. Also includes any top level Ruby files
21
+ # Returns all workspace paths that should be indexed
40
22
  #
41
23
  #: -> Array[String]
42
24
  def workspace_paths
43
25
  paths = []
26
+ root = workspace_path
44
27
 
45
- Dir.each_child(@workspace_path) do |entry|
46
- full_path = File.join(@workspace_path, entry)
28
+ Dir.each_child(root) do |entry|
29
+ full_path = File.join(root, entry)
47
30
 
48
- if File.directory?(full_path)
49
- paths << full_path unless IGNORED_DIRECTORIES.include?(entry)
50
- elsif INDEXABLE_EXTENSIONS.include?(File.extname(entry))
31
+ if File.directory?(full_path) || INDEXABLE_EXTENSIONS.include?(File.extname(entry))
51
32
  paths << full_path
52
33
  end
53
34
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubydex
4
- VERSION = "0.2.6"
4
+ VERSION = "0.2.7"
5
5
  end
data/rbi/rubydex.rbi CHANGED
@@ -174,10 +174,12 @@ class Rubydex::ConstantDefinition < Rubydex::Definition; end
174
174
  class Rubydex::GlobalVariableAliasDefinition < Rubydex::Definition; end
175
175
  class Rubydex::GlobalVariableDefinition < Rubydex::Definition; end
176
176
  class Rubydex::InstanceVariableDefinition < Rubydex::Definition; end
177
+
177
178
  class Rubydex::MethodAliasDefinition < Rubydex::Definition
178
179
  sig { returns(T.nilable(Rubydex::Method)) }
179
180
  def target; end
180
181
  end
182
+
181
183
  class Rubydex::MethodDefinition < Rubydex::Definition; end
182
184
 
183
185
  class Rubydex::ModuleDefinition < Rubydex::Definition
@@ -264,6 +266,7 @@ end
264
266
 
265
267
  class Rubydex::Error < StandardError; end
266
268
  class Rubydex::AliasCycleError < Rubydex::Error; end
269
+ class Rubydex::ConfigError < Rubydex::Error; end
267
270
 
268
271
  class Rubydex::Failure
269
272
  sig { params(message: String).void }
@@ -276,8 +279,6 @@ end
276
279
  class Rubydex::IntegrityFailure < Rubydex::Failure; end
277
280
 
278
281
  class Rubydex::Graph
279
- IGNORED_DIRECTORIES = T.let(T.unsafe(nil), T::Array[String])
280
-
281
282
  sig { params(workspace_path: T.nilable(String)).void }
282
283
  def initialize(workspace_path: nil); end
283
284
 
@@ -308,10 +309,17 @@ class Rubydex::Graph
308
309
  sig { params(uri: String, source: String, language_id: String).void }
309
310
  def index_source(uri, source, language_id); end
310
311
 
311
- # Index all files and dependencies of the workspace that exists in `@workspace_path`
312
+ # Index all files and dependencies of the workspace that exists in `workspace_path`
312
313
  sig { returns(T::Array[String]) }
313
314
  def index_workspace; end
314
315
 
316
+ # Loads configuration, merging its excluded paths into the graph's configuration (the workspace path is never
317
+ # overridden). With `config_path` (resolved relative to the workspace path), an explicitly named file that does not
318
+ # exist raises `Rubydex::ConfigError`. With no argument, the default `.rubydex` is loaded if present and ignored if
319
+ # missing. Raises `Rubydex::ConfigError` if a file cannot be read or is malformed.
320
+ sig { params(config_path: T.nilable(String)).void }
321
+ def load_config(config_path = nil); end
322
+
315
323
  sig { returns(T::Enumerable[Rubydex::MethodReference]) }
316
324
  def method_references; end
317
325
 
data/rust/Cargo.lock CHANGED
@@ -180,7 +180,7 @@ dependencies = [
180
180
  "serde_json",
181
181
  "syn",
182
182
  "tempfile",
183
- "toml",
183
+ "toml 0.8.23",
184
184
  ]
185
185
 
186
186
  [[package]]
@@ -536,9 +536,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
536
536
 
537
537
  [[package]]
538
538
  name = "hashbrown"
539
- version = "0.15.4"
539
+ version = "0.17.1"
540
540
  source = "registry+https://github.com/rust-lang/crates.io-index"
541
- checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
541
+ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
542
542
 
543
543
  [[package]]
544
544
  name = "heck"
@@ -685,9 +685,9 @@ dependencies = [
685
685
 
686
686
  [[package]]
687
687
  name = "indexmap"
688
- version = "2.10.0"
688
+ version = "2.14.0"
689
689
  source = "registry+https://github.com/rust-lang/crates.io-index"
690
- checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
690
+ checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
691
691
  dependencies = [
692
692
  "equivalent",
693
693
  "hashbrown",
@@ -823,6 +823,12 @@ version = "1.70.1"
823
823
  source = "registry+https://github.com/rust-lang/crates.io-index"
824
824
  checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
825
825
 
826
+ [[package]]
827
+ name = "paste"
828
+ version = "1.0.15"
829
+ source = "registry+https://github.com/rust-lang/crates.io-index"
830
+ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
831
+
826
832
  [[package]]
827
833
  name = "pastey"
828
834
  version = "0.2.1"
@@ -1065,7 +1071,11 @@ dependencies = [
1065
1071
  "ruby-prism",
1066
1072
  "ruby-rbs",
1067
1073
  "rubydex",
1074
+ "serde",
1068
1075
  "tempfile",
1076
+ "tikv-jemalloc-ctl",
1077
+ "tikv-jemallocator",
1078
+ "toml 1.1.2+spec-1.1.0",
1069
1079
  "url",
1070
1080
  "xxhash-rust",
1071
1081
  ]
@@ -1155,18 +1165,28 @@ dependencies = [
1155
1165
 
1156
1166
  [[package]]
1157
1167
  name = "serde"
1158
- version = "1.0.219"
1168
+ version = "1.0.228"
1169
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1170
+ checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
1171
+ dependencies = [
1172
+ "serde_core",
1173
+ "serde_derive",
1174
+ ]
1175
+
1176
+ [[package]]
1177
+ name = "serde_core"
1178
+ version = "1.0.228"
1159
1179
  source = "registry+https://github.com/rust-lang/crates.io-index"
1160
- checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
1180
+ checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
1161
1181
  dependencies = [
1162
1182
  "serde_derive",
1163
1183
  ]
1164
1184
 
1165
1185
  [[package]]
1166
1186
  name = "serde_derive"
1167
- version = "1.0.219"
1187
+ version = "1.0.228"
1168
1188
  source = "registry+https://github.com/rust-lang/crates.io-index"
1169
- checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
1189
+ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
1170
1190
  dependencies = [
1171
1191
  "proc-macro2",
1172
1192
  "quote",
@@ -1205,6 +1225,15 @@ dependencies = [
1205
1225
  "serde",
1206
1226
  ]
1207
1227
 
1228
+ [[package]]
1229
+ name = "serde_spanned"
1230
+ version = "1.1.1"
1231
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1232
+ checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
1233
+ dependencies = [
1234
+ "serde_core",
1235
+ ]
1236
+
1208
1237
  [[package]]
1209
1238
  name = "serde_yaml"
1210
1239
  version = "0.9.34+deprecated"
@@ -1315,6 +1344,37 @@ dependencies = [
1315
1344
  "syn",
1316
1345
  ]
1317
1346
 
1347
+ [[package]]
1348
+ name = "tikv-jemalloc-ctl"
1349
+ version = "0.7.0"
1350
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1351
+ checksum = "3a184c43b8ab2f41df2733b55556e3f5f632f4aeaa205b1bb018f574b7f5f142"
1352
+ dependencies = [
1353
+ "libc",
1354
+ "paste",
1355
+ "tikv-jemalloc-sys",
1356
+ ]
1357
+
1358
+ [[package]]
1359
+ name = "tikv-jemalloc-sys"
1360
+ version = "0.7.1+5.3.1-0-g81034ce1f1373e37dc865038e1bc8eeecf559ce8"
1361
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1362
+ checksum = "1a2825c78386b4ae0314074867860ba9577875de945f05992c38815cbec327f0"
1363
+ dependencies = [
1364
+ "cc",
1365
+ "libc",
1366
+ ]
1367
+
1368
+ [[package]]
1369
+ name = "tikv-jemallocator"
1370
+ version = "0.7.0"
1371
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1372
+ checksum = "249f09e49ab1609436f34c776e84231bead18d6a955f119f939bdc1d847561bd"
1373
+ dependencies = [
1374
+ "libc",
1375
+ "tikv-jemalloc-sys",
1376
+ ]
1377
+
1318
1378
  [[package]]
1319
1379
  name = "tinystr"
1320
1380
  version = "0.8.1"
@@ -1367,11 +1427,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1367
1427
  checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
1368
1428
  dependencies = [
1369
1429
  "serde",
1370
- "serde_spanned",
1371
- "toml_datetime",
1430
+ "serde_spanned 0.6.9",
1431
+ "toml_datetime 0.6.11",
1372
1432
  "toml_edit",
1373
1433
  ]
1374
1434
 
1435
+ [[package]]
1436
+ name = "toml"
1437
+ version = "1.1.2+spec-1.1.0"
1438
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1439
+ checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee"
1440
+ dependencies = [
1441
+ "indexmap",
1442
+ "serde_core",
1443
+ "serde_spanned 1.1.1",
1444
+ "toml_datetime 1.1.1+spec-1.1.0",
1445
+ "toml_parser",
1446
+ "toml_writer",
1447
+ "winnow 1.0.3",
1448
+ ]
1449
+
1375
1450
  [[package]]
1376
1451
  name = "toml_datetime"
1377
1452
  version = "0.6.11"
@@ -1381,6 +1456,15 @@ dependencies = [
1381
1456
  "serde",
1382
1457
  ]
1383
1458
 
1459
+ [[package]]
1460
+ name = "toml_datetime"
1461
+ version = "1.1.1+spec-1.1.0"
1462
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1463
+ checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
1464
+ dependencies = [
1465
+ "serde_core",
1466
+ ]
1467
+
1384
1468
  [[package]]
1385
1469
  name = "toml_edit"
1386
1470
  version = "0.22.27"
@@ -1389,10 +1473,19 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
1389
1473
  dependencies = [
1390
1474
  "indexmap",
1391
1475
  "serde",
1392
- "serde_spanned",
1393
- "toml_datetime",
1476
+ "serde_spanned 0.6.9",
1477
+ "toml_datetime 0.6.11",
1394
1478
  "toml_write",
1395
- "winnow",
1479
+ "winnow 0.7.12",
1480
+ ]
1481
+
1482
+ [[package]]
1483
+ name = "toml_parser"
1484
+ version = "1.1.2+spec-1.1.0"
1485
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1486
+ checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
1487
+ dependencies = [
1488
+ "winnow 1.0.3",
1396
1489
  ]
1397
1490
 
1398
1491
  [[package]]
@@ -1401,6 +1494,12 @@ version = "0.1.2"
1401
1494
  source = "registry+https://github.com/rust-lang/crates.io-index"
1402
1495
  checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
1403
1496
 
1497
+ [[package]]
1498
+ name = "toml_writer"
1499
+ version = "1.1.1+spec-1.1.0"
1500
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1501
+ checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
1502
+
1404
1503
  [[package]]
1405
1504
  name = "tracing"
1406
1505
  version = "0.1.44"
@@ -1751,6 +1850,12 @@ dependencies = [
1751
1850
  "memchr",
1752
1851
  ]
1753
1852
 
1853
+ [[package]]
1854
+ name = "winnow"
1855
+ version = "1.0.3"
1856
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1857
+ checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
1858
+
1754
1859
  [[package]]
1755
1860
  name = "wit-bindgen-rt"
1756
1861
  version = "0.39.0"
@@ -19,6 +19,11 @@ path = "src/main.rs"
19
19
  crate-type = ["rlib"]
20
20
 
21
21
  [features]
22
+ jemalloc = ["dep:tikv-jemallocator"]
23
+ # The Ruby native extension uses a dlopen to load the rubydex shared object. In these scenarios, jemalloc must be linked
24
+ # dynamically or else the process crashes. This has a small performance cost, which we don't need to pay when using the
25
+ # Rust crate by itself, so we gate it with a feature
26
+ jemalloc_dylib = ["jemalloc", "tikv-jemallocator/disable_initial_exec_tls"]
22
27
  test_utils = ["dep:tempfile"]
23
28
 
24
29
  [dependencies]
@@ -32,17 +37,30 @@ bitflags = "2.9"
32
37
  bytecount = "0.6.9"
33
38
  libc = "0.2"
34
39
  line-index = "0.1.2"
40
+ serde = { version = "1.0", features = ["derive"] }
35
41
  tempfile = { version = "3.0", optional = true }
36
42
  crossbeam-deque = "0.8"
37
43
  crossbeam-utils = "0.8"
38
44
  crossbeam-channel = "0.5"
45
+ toml = "1.1.2"
46
+
47
+ [target.'cfg(not(target_os = "windows"))'.dependencies]
48
+ tikv-jemallocator = { version = "0.7.0", optional = true }
39
49
 
40
50
  [dev-dependencies]
41
- rubydex = { path = ".", features = ["test_utils"] }
51
+ rubydex = { path = ".", features = ["test_utils", "jemalloc"] }
42
52
  tempfile = "3.0"
43
53
  assert_cmd = "2.0"
44
54
  predicates = "3.1"
45
55
  regex = "1.10"
46
56
 
57
+ [target.'cfg(not(target_os = "windows"))'.dev-dependencies]
58
+ tikv-jemallocator = { version = "0.7.0", features = ["stats"] }
59
+ tikv-jemalloc-ctl = { version = "0.7.0", features = ["stats"] }
60
+
61
+ [[bench]]
62
+ name = "graph_memory"
63
+ harness = false
64
+
47
65
  [lints]
48
66
  workspace = true
@@ -0,0 +1,46 @@
1
+ #[cfg(all(feature = "jemalloc", not(target_os = "windows")))]
2
+ mod imp {
3
+ use std::collections::HashSet;
4
+
5
+ use rubydex::{
6
+ indexing::{self, IndexerBackend},
7
+ listing,
8
+ model::graph::Graph,
9
+ resolution::Resolver,
10
+ };
11
+ use tikv_jemalloc_ctl::{epoch, stats};
12
+
13
+ /// Advance the jemalloc epoch (stats are cached between epochs) and read the
14
+ /// number of bytes currently allocated by the application.
15
+ fn allocated_bytes() -> usize {
16
+ epoch::advance().expect("failed to advance jemalloc epoch");
17
+ stats::allocated::read().expect("failed to read stats.allocated (is the `stats` feature on?)")
18
+ }
19
+
20
+ pub fn run() {
21
+ let paths: Vec<String> = std::env::args().skip(1).collect();
22
+ let paths = if paths.is_empty() { vec![".".to_string()] } else { paths };
23
+ let (file_paths, _) = listing::collect_file_paths(paths, &HashSet::new());
24
+
25
+ let mut graph = Graph::new();
26
+ let _ = indexing::index_files(&mut graph, file_paths, IndexerBackend::RubyIndexer);
27
+ Resolver::new(&mut graph).resolve();
28
+
29
+ // Compare the total memory used in the allocator before and after dropping the graph
30
+ let before_drop = allocated_bytes();
31
+ drop(graph);
32
+ let after_drop = allocated_bytes();
33
+
34
+ let total_graph_memory = before_drop.saturating_sub(after_drop);
35
+
36
+ #[allow(clippy::cast_precision_loss)]
37
+ let mega_bytes = total_graph_memory as f64 / 1024.0 / 1024.0;
38
+
39
+ println!("Total graph memory: {mega_bytes:.2} MB");
40
+ }
41
+ }
42
+
43
+ fn main() {
44
+ #[cfg(all(feature = "jemalloc", not(target_os = "windows")))]
45
+ imp::run();
46
+ }