rubydex 0.2.9 → 0.4.0
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 +110 -6
- data/THIRD_PARTY_LICENSES.html +271 -2
- data/exe/rdx +2 -73
- data/ext/rubydex/config.c +140 -0
- data/ext/rubydex/config.h +16 -0
- data/ext/rubydex/declaration.c +1 -1
- data/ext/rubydex/definition.c +32 -4
- data/ext/rubydex/diagnostic.c +75 -1
- data/ext/rubydex/diagnostic.h +2 -0
- data/ext/rubydex/graph.c +27 -48
- data/ext/rubydex/graph.h +6 -0
- data/ext/rubydex/query.c +487 -0
- data/ext/rubydex/query.h +8 -0
- data/ext/rubydex/reference.c +60 -0
- data/ext/rubydex/rubydex.c +4 -0
- data/ext/rubydex/utils.c +23 -4
- data/ext/rubydex/utils.h +5 -0
- data/lib/ruby_lsp/rubydex/addon.rb +211 -0
- data/lib/rubydex/cli/command/console.rb +55 -0
- data/lib/rubydex/cli/command/lint/explain.rb +74 -0
- data/lib/rubydex/cli/command/lint.rb +202 -0
- data/lib/rubydex/cli/command/mcp.rb +30 -0
- data/lib/rubydex/cli/command/query.rb +70 -0
- data/lib/rubydex/cli/command/skill.rb +69 -0
- data/lib/rubydex/cli/command.rb +168 -0
- data/lib/rubydex/cli.rb +93 -0
- data/lib/rubydex/config.rb +59 -0
- data/lib/rubydex/diagnostic.rb +12 -3
- data/lib/rubydex/errors.rb +42 -1
- data/lib/rubydex/graph.rb +10 -3
- data/lib/rubydex/linter/custom_rule.rb +97 -0
- data/lib/rubydex/linter/helpers/path_helpers.rb +78 -0
- data/lib/rubydex/linter/helpers/source_access_helpers.rb +31 -0
- data/lib/rubydex/linter/rule_loader.rb +36 -0
- data/lib/rubydex/linter/rule_test_case.rb +343 -0
- data/lib/rubydex/linter/runner.rb +56 -0
- data/lib/rubydex/linter.rb +19 -0
- data/lib/rubydex/location.rb +3 -0
- data/lib/rubydex/mcp_server.rb +1 -2
- data/lib/rubydex/related_information.rb +17 -0
- data/lib/rubydex/rule.rb +33 -0
- data/lib/rubydex/severity.rb +70 -0
- data/lib/rubydex/skill.rb +88 -0
- data/lib/rubydex/skill_registry.rb +62 -0
- data/lib/rubydex/version.rb +1 -1
- data/lib/rubydex.rb +6 -0
- data/lib/rubydex_linter/rules/rule_structure.rb +125 -0
- data/rbi/rubydex.rbi +578 -15
- data/rust/Cargo.lock +7 -0
- data/rust/rubydex/Cargo.toml +1 -0
- data/rust/rubydex/benches/graph_memory.rs +20 -4
- data/rust/rubydex/src/compile_assertions.rs +15 -0
- data/rust/rubydex/src/config.rs +538 -157
- data/rust/rubydex/src/diagnostic.rs +66 -40
- data/rust/rubydex/src/errors.rs +0 -1
- data/rust/rubydex/src/indexing/local_graph.rs +6 -5
- data/rust/rubydex/src/indexing/rbs_indexer.rs +284 -8
- data/rust/rubydex/src/indexing/ruby_indexer.rs +59 -70
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +195 -86
- data/rust/rubydex/src/lib.rs +1 -0
- data/rust/rubydex/src/listing.rs +26 -1
- data/rust/rubydex/src/main.rs +9 -128
- data/rust/rubydex/src/model/declaration.rs +301 -229
- data/rust/rubydex/src/model/definitions.rs +27 -26
- data/rust/rubydex/src/model/document.rs +43 -7
- data/rust/rubydex/src/model/graph.rs +67 -68
- data/rust/rubydex/src/model/id.rs +55 -0
- data/rust/rubydex/src/model/ids.rs +21 -9
- data/rust/rubydex/src/model/name.rs +88 -19
- data/rust/rubydex/src/model/references.rs +16 -13
- data/rust/rubydex/src/operation/ruby_builder.rs +78 -104
- data/rust/rubydex/src/path_helpers.rs +77 -0
- data/rust/rubydex/src/query/cypher/schema.rs +853 -0
- data/rust/rubydex/src/query/cypher/schema_info.rs +161 -0
- data/rust/rubydex/src/query/cypher/tests.rs +253 -0
- data/rust/rubydex/src/query/cypher.rs +54 -0
- data/rust/rubydex/src/query.rs +125 -43
- data/rust/rubydex/src/resolution.rs +368 -395
- data/rust/rubydex/src/resolution_tests.rs +504 -78
- data/rust/rubydex/src/test_utils/context.rs +2 -1
- data/rust/rubydex/src/test_utils/graph_test.rs +26 -12
- data/rust/rubydex/src/test_utils/local_graph_test.rs +19 -0
- data/rust/rubydex/tests/cli.rs +4 -4
- data/rust/rubydex-sys/src/config_api.rs +205 -0
- data/rust/rubydex-sys/src/cypher_api.rs +791 -0
- data/rust/rubydex-sys/src/declaration_api.rs +6 -3
- data/rust/rubydex-sys/src/definition_api.rs +27 -7
- data/rust/rubydex-sys/src/diagnostic_api.rs +77 -8
- data/rust/rubydex-sys/src/graph_api.rs +31 -68
- data/rust/rubydex-sys/src/lib.rs +2 -0
- data/rust/rubydex-sys/src/name_api.rs +2 -6
- data/rust/rubydex-sys/src/reference_api.rs +58 -12
- data/rust/rubydex-sys/src/utils.rs +37 -0
- data/skills/send-private-method/SKILL.md +133 -0
- metadata +37 -2
data/rust/rubydex/src/main.rs
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
use clap::{Parser, ValueEnum};
|
|
2
|
-
use std::{
|
|
3
|
-
fs, mem,
|
|
4
|
-
path::{Path, PathBuf},
|
|
5
|
-
time::{Duration, Instant},
|
|
6
|
-
};
|
|
2
|
+
use std::{fs, mem, path::PathBuf};
|
|
7
3
|
|
|
8
4
|
use rubydex::{
|
|
5
|
+
config::Config,
|
|
9
6
|
dot,
|
|
10
|
-
indexing::{self, IndexerBackend
|
|
7
|
+
indexing::{self, IndexerBackend},
|
|
11
8
|
integrity, listing,
|
|
12
9
|
model::graph::Graph,
|
|
13
10
|
resolution::Resolver,
|
|
@@ -16,7 +13,6 @@ use rubydex::{
|
|
|
16
13
|
timer::{Timer, time_it},
|
|
17
14
|
},
|
|
18
15
|
};
|
|
19
|
-
use url::Url;
|
|
20
16
|
|
|
21
17
|
#[derive(Parser, Debug)]
|
|
22
18
|
#[command(name = "rubydex_cli", about = "A Static Analysis Toolkit for Ruby", version)]
|
|
@@ -61,21 +57,6 @@ struct Args {
|
|
|
61
57
|
help = "Write orphan definitions report to specified file"
|
|
62
58
|
)]
|
|
63
59
|
report_orphans: Option<String>,
|
|
64
|
-
|
|
65
|
-
#[arg(
|
|
66
|
-
long = "incremental_cycle",
|
|
67
|
-
value_name = "N",
|
|
68
|
-
help = "After the initial build, run N incremental resolution cycles and report their timings"
|
|
69
|
-
)]
|
|
70
|
-
incremental_cycle: Option<usize>,
|
|
71
|
-
|
|
72
|
-
#[arg(
|
|
73
|
-
long = "incremental_files",
|
|
74
|
-
value_name = "N",
|
|
75
|
-
default_value_t = 1,
|
|
76
|
-
help = "Number of files to re-index per incremental cycle"
|
|
77
|
-
)]
|
|
78
|
-
incremental_files: usize,
|
|
79
60
|
}
|
|
80
61
|
|
|
81
62
|
#[derive(Debug, Clone, ValueEnum)]
|
|
@@ -124,10 +105,12 @@ fn main() {
|
|
|
124
105
|
let mut graph = Graph::new();
|
|
125
106
|
|
|
126
107
|
if let Some(workspace_path) = workspace_path_for(&args.paths) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
108
|
+
match Config::load(&workspace_path) {
|
|
109
|
+
Ok(config) => graph.load_config(&config),
|
|
110
|
+
Err(error) => {
|
|
111
|
+
eprintln!("{error}");
|
|
112
|
+
std::process::exit(1);
|
|
113
|
+
}
|
|
131
114
|
}
|
|
132
115
|
}
|
|
133
116
|
|
|
@@ -149,14 +132,6 @@ fn main() {
|
|
|
149
132
|
|
|
150
133
|
let backend = IndexerBackend::from(&args.indexer);
|
|
151
134
|
|
|
152
|
-
// The incremental benchmark re-indexes files after the initial build, so keep a copy of the
|
|
153
|
-
// paths before `index_files` consumes them.
|
|
154
|
-
let incremental_paths = if args.incremental_cycle.is_some() {
|
|
155
|
-
file_paths.clone()
|
|
156
|
-
} else {
|
|
157
|
-
Vec::new()
|
|
158
|
-
};
|
|
159
|
-
|
|
160
135
|
let errors = time_it!(indexing, { indexing::index_files(&mut graph, file_paths, backend) });
|
|
161
136
|
|
|
162
137
|
for error in errors {
|
|
@@ -174,12 +149,6 @@ fn main() {
|
|
|
174
149
|
resolver.resolve();
|
|
175
150
|
});
|
|
176
151
|
|
|
177
|
-
// Incremental resolution benchmark. Runs before the stop-after check so it can be combined with
|
|
178
|
-
// `--stop-after=resolution`.
|
|
179
|
-
if let Some(cycles) = args.incremental_cycle {
|
|
180
|
-
run_incremental_resolution(&mut graph, &incremental_paths, cycles, args.incremental_files, backend);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
152
|
if let Some(StopAfter::Resolution) = args.stop_after {
|
|
184
153
|
return exit(args.stats);
|
|
185
154
|
}
|
|
@@ -241,91 +210,3 @@ fn main() {
|
|
|
241
210
|
// Forget the graph so we don't have to wait for deallocation and let the system reclaim the memory at exit
|
|
242
211
|
mem::forget(graph);
|
|
243
212
|
}
|
|
244
|
-
|
|
245
|
-
/// Simulates incremental editing to measure incremental resolution cost. For each cycle it
|
|
246
|
-
/// re-indexes a rotating window of `files_per_cycle` files (parsing plus the same invalidation the
|
|
247
|
-
/// LSP performs on save) and then re-runs resolution over the resulting pending work, reporting
|
|
248
|
-
/// per-cycle and aggregate `resolve()` timings.
|
|
249
|
-
///
|
|
250
|
-
/// With `--stats`, the `compute_descendants` breakdown printed in the timing summary reflects the
|
|
251
|
-
/// last incremental cycle, since it is recorded on every `resolve()`.
|
|
252
|
-
fn run_incremental_resolution(
|
|
253
|
-
graph: &mut Graph,
|
|
254
|
-
paths: &[PathBuf],
|
|
255
|
-
cycles: usize,
|
|
256
|
-
files_per_cycle: usize,
|
|
257
|
-
backend: IndexerBackend,
|
|
258
|
-
) {
|
|
259
|
-
if paths.is_empty() || cycles == 0 || files_per_cycle == 0 {
|
|
260
|
-
eprintln!("Skipping incremental resolution: nothing to re-index");
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
let files_per_cycle = files_per_cycle.min(paths.len());
|
|
265
|
-
|
|
266
|
-
let mut reindex_total = Duration::ZERO;
|
|
267
|
-
let mut resolve_total = Duration::ZERO;
|
|
268
|
-
let mut resolve_min = Duration::MAX;
|
|
269
|
-
let mut resolve_max = Duration::ZERO;
|
|
270
|
-
|
|
271
|
-
println!();
|
|
272
|
-
println!("Incremental resolution ({cycles} cycle(s), {files_per_cycle} file(s)/cycle)");
|
|
273
|
-
println!(" Scenario: no-op reindex; files are indexed again without changing their contents.");
|
|
274
|
-
println!(" This should be the fastest incremental resolution path.");
|
|
275
|
-
println!(" Add scenario-based benchmarks before optimizing incremental resolution.");
|
|
276
|
-
|
|
277
|
-
for cycle in 0..cycles {
|
|
278
|
-
// Re-index the window of files for this cycle. Rotating the window across cycles samples
|
|
279
|
-
// different parts of the codebase rather than measuring the same delta repeatedly.
|
|
280
|
-
let reindex_start = Instant::now();
|
|
281
|
-
for i in 0..files_per_cycle {
|
|
282
|
-
let path = &paths[(cycle * files_per_cycle + i) % paths.len()];
|
|
283
|
-
reindex_file(graph, path, backend);
|
|
284
|
-
}
|
|
285
|
-
reindex_total += reindex_start.elapsed();
|
|
286
|
-
|
|
287
|
-
let resolve_start = Instant::now();
|
|
288
|
-
Resolver::new(graph).resolve();
|
|
289
|
-
let elapsed = resolve_start.elapsed();
|
|
290
|
-
|
|
291
|
-
resolve_total += elapsed;
|
|
292
|
-
resolve_min = resolve_min.min(elapsed);
|
|
293
|
-
resolve_max = resolve_max.max(elapsed);
|
|
294
|
-
|
|
295
|
-
println!(
|
|
296
|
-
" cycle {:>3}: resolve {:9.3}ms",
|
|
297
|
-
cycle + 1,
|
|
298
|
-
elapsed.as_secs_f64() * 1000.0
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
let avg = resolve_total / u32::try_from(cycles).expect("cycle count fits in u32");
|
|
303
|
-
|
|
304
|
-
println!(
|
|
305
|
-
" resolve total {:.3}ms avg {:.3}ms min {:.3}ms max {:.3}ms",
|
|
306
|
-
resolve_total.as_secs_f64() * 1000.0,
|
|
307
|
-
avg.as_secs_f64() * 1000.0,
|
|
308
|
-
resolve_min.as_secs_f64() * 1000.0,
|
|
309
|
-
resolve_max.as_secs_f64() * 1000.0,
|
|
310
|
-
);
|
|
311
|
-
println!(
|
|
312
|
-
" reindex+invalidate total {:.3}ms (parse + document merge, excluded from resolve above)",
|
|
313
|
-
reindex_total.as_secs_f64() * 1000.0,
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/// Re-indexes a single file into the graph, running the same invalidation the LSP performs on save.
|
|
318
|
-
fn reindex_file(graph: &mut Graph, path: &Path, backend: IndexerBackend) {
|
|
319
|
-
let Ok(source) = fs::read_to_string(path) else {
|
|
320
|
-
eprintln!("Failed to read file `{}`", path.display());
|
|
321
|
-
return;
|
|
322
|
-
};
|
|
323
|
-
let Ok(url) = Url::from_file_path(path) else {
|
|
324
|
-
eprintln!("Couldn't build URI from path `{}`", path.display());
|
|
325
|
-
return;
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
let language = path.extension().map_or(LanguageId::Ruby, LanguageId::from);
|
|
329
|
-
let local_graph = build_local_graph(url.to_string(), &source, &language, backend);
|
|
330
|
-
graph.consume_document_changes(local_graph);
|
|
331
|
-
}
|