rubydex 0.3.0 → 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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -7
  3. data/THIRD_PARTY_LICENSES.html +238 -2
  4. data/exe/rdx +2 -149
  5. data/ext/rubydex/config.c +140 -0
  6. data/ext/rubydex/config.h +16 -0
  7. data/ext/rubydex/diagnostic.c +75 -1
  8. data/ext/rubydex/diagnostic.h +2 -0
  9. data/ext/rubydex/graph.c +13 -45
  10. data/ext/rubydex/graph.h +6 -0
  11. data/ext/rubydex/query.c +398 -16
  12. data/ext/rubydex/rubydex.c +2 -0
  13. data/ext/rubydex/utils.c +11 -4
  14. data/lib/ruby_lsp/rubydex/addon.rb +211 -0
  15. data/lib/rubydex/cli/command/console.rb +55 -0
  16. data/lib/rubydex/cli/command/lint/explain.rb +74 -0
  17. data/lib/rubydex/cli/command/lint.rb +202 -0
  18. data/lib/rubydex/cli/command/mcp.rb +30 -0
  19. data/lib/rubydex/cli/command/query.rb +70 -0
  20. data/lib/rubydex/cli/command/skill.rb +69 -0
  21. data/lib/rubydex/cli/command.rb +168 -0
  22. data/lib/rubydex/cli.rb +93 -0
  23. data/lib/rubydex/config.rb +59 -0
  24. data/lib/rubydex/diagnostic.rb +12 -3
  25. data/lib/rubydex/errors.rb +42 -1
  26. data/lib/rubydex/graph.rb +10 -3
  27. data/lib/rubydex/linter/custom_rule.rb +97 -0
  28. data/lib/rubydex/linter/helpers/path_helpers.rb +78 -0
  29. data/lib/rubydex/linter/helpers/source_access_helpers.rb +31 -0
  30. data/lib/rubydex/linter/rule_loader.rb +36 -0
  31. data/lib/rubydex/linter/rule_test_case.rb +343 -0
  32. data/lib/rubydex/linter/runner.rb +56 -0
  33. data/lib/rubydex/linter.rb +19 -0
  34. data/lib/rubydex/location.rb +3 -0
  35. data/lib/rubydex/mcp_server.rb +1 -2
  36. data/lib/rubydex/related_information.rb +17 -0
  37. data/lib/rubydex/rule.rb +33 -0
  38. data/lib/rubydex/severity.rb +70 -0
  39. data/lib/rubydex/skill.rb +88 -0
  40. data/lib/rubydex/skill_registry.rb +62 -0
  41. data/lib/rubydex/version.rb +1 -1
  42. data/lib/rubydex.rb +6 -0
  43. data/lib/rubydex_linter/rules/rule_structure.rb +125 -0
  44. data/rbi/rubydex.rbi +558 -17
  45. data/rust/Cargo.lock +2 -2
  46. data/rust/rubydex/Cargo.toml +1 -1
  47. data/rust/rubydex/benches/graph_memory.rs +3 -5
  48. data/rust/rubydex/src/config.rs +538 -157
  49. data/rust/rubydex/src/diagnostic.rs +66 -40
  50. data/rust/rubydex/src/errors.rs +0 -1
  51. data/rust/rubydex/src/indexing/local_graph.rs +6 -5
  52. data/rust/rubydex/src/indexing/rbs_indexer.rs +270 -6
  53. data/rust/rubydex/src/indexing/ruby_indexer.rs +10 -12
  54. data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +134 -81
  55. data/rust/rubydex/src/lib.rs +1 -0
  56. data/rust/rubydex/src/listing.rs +26 -1
  57. data/rust/rubydex/src/main.rs +7 -4
  58. data/rust/rubydex/src/model/declaration.rs +302 -219
  59. data/rust/rubydex/src/model/graph.rs +27 -40
  60. data/rust/rubydex/src/model/name.rs +58 -17
  61. data/rust/rubydex/src/operation/ruby_builder.rs +30 -45
  62. data/rust/rubydex/src/path_helpers.rs +77 -0
  63. data/rust/rubydex/src/query/cypher/schema.rs +63 -0
  64. data/rust/rubydex/src/query/cypher/tests.rs +26 -1
  65. data/rust/rubydex/src/query/cypher.rs +8 -11
  66. data/rust/rubydex/src/query.rs +123 -43
  67. data/rust/rubydex/src/resolution.rs +139 -187
  68. data/rust/rubydex/src/resolution_tests.rs +247 -19
  69. data/rust/rubydex/src/test_utils/context.rs +2 -1
  70. data/rust/rubydex/src/test_utils/graph_test.rs +26 -12
  71. data/rust/rubydex/src/test_utils/local_graph_test.rs +19 -0
  72. data/rust/rubydex/tests/cli.rs +4 -4
  73. data/rust/rubydex-sys/src/config_api.rs +205 -0
  74. data/rust/rubydex-sys/src/cypher_api.rs +791 -0
  75. data/rust/rubydex-sys/src/diagnostic_api.rs +77 -8
  76. data/rust/rubydex-sys/src/graph_api.rs +13 -194
  77. data/rust/rubydex-sys/src/lib.rs +2 -0
  78. data/rust/rubydex-sys/src/name_api.rs +2 -6
  79. data/rust/rubydex-sys/src/utils.rs +37 -0
  80. data/skills/send-private-method/SKILL.md +133 -0
  81. metadata +31 -2
data/rbi/rubydex.rbi CHANGED
@@ -182,11 +182,91 @@ class Rubydex::GlobalVariableDefinition < Rubydex::Definition; end
182
182
  class Rubydex::InstanceVariableDefinition < Rubydex::Definition; end
183
183
 
184
184
  class Rubydex::MethodAliasDefinition < Rubydex::Definition
185
+ sig { returns(T::Array[Rubydex::Signature]) }
186
+ def signatures; end
187
+
185
188
  sig { returns(T.nilable(Rubydex::Method)) }
186
189
  def target; end
187
190
  end
188
191
 
189
- class Rubydex::MethodDefinition < Rubydex::Definition; end
192
+ class Rubydex::MethodDefinition < Rubydex::Definition
193
+ sig { returns(T::Array[Rubydex::Signature]) }
194
+ def signatures; end
195
+ end
196
+
197
+ class Rubydex::Signature
198
+ sig { params(parameters: T::Array[Rubydex::Signature::Parameter]).void }
199
+ def initialize(parameters); end
200
+
201
+ sig { returns(T::Array[Rubydex::Signature::Parameter]) }
202
+ def parameters; end
203
+
204
+ sig do
205
+ returns([
206
+ T::Array[Rubydex::Signature::PositionalParameter],
207
+ T::Array[Rubydex::Signature::OptionalPositionalParameter],
208
+ T.nilable(Rubydex::Signature::RestPositionalParameter),
209
+ T::Array[Rubydex::Signature::PostParameter],
210
+ T::Array[Rubydex::Signature::KeywordParameter],
211
+ T::Array[Rubydex::Signature::OptionalKeywordParameter],
212
+ T.nilable(Rubydex::Signature::RestKeywordParameter),
213
+ T.nilable(Rubydex::Signature::ForwardParameter),
214
+ T.nilable(Rubydex::Signature::BlockParameter),
215
+ ])
216
+ end
217
+ def deconstruct; end
218
+
219
+ sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) }
220
+ def deconstruct_keys(keys); end
221
+
222
+ sig { returns(T::Array[Rubydex::Signature::PositionalParameter]) }
223
+ def positional_parameters; end
224
+
225
+ sig { returns(T::Array[Rubydex::Signature::OptionalPositionalParameter]) }
226
+ def optional_positional_parameters; end
227
+
228
+ sig { returns(T.nilable(Rubydex::Signature::RestPositionalParameter)) }
229
+ def rest_positional_parameter; end
230
+
231
+ sig { returns(T::Array[Rubydex::Signature::PostParameter]) }
232
+ def post_parameters; end
233
+
234
+ sig { returns(T::Array[Rubydex::Signature::KeywordParameter]) }
235
+ def keyword_parameters; end
236
+
237
+ sig { returns(T::Array[Rubydex::Signature::OptionalKeywordParameter]) }
238
+ def optional_keyword_parameters; end
239
+
240
+ sig { returns(T.nilable(Rubydex::Signature::RestKeywordParameter)) }
241
+ def rest_keyword_parameter; end
242
+
243
+ sig { returns(T.nilable(Rubydex::Signature::ForwardParameter)) }
244
+ def forward_parameter; end
245
+
246
+ sig { returns(T.nilable(Rubydex::Signature::BlockParameter)) }
247
+ def block_parameter; end
248
+ end
249
+
250
+ class Rubydex::Signature::Parameter
251
+ sig { returns(Symbol) }
252
+ def name; end
253
+
254
+ sig { returns(Rubydex::Location) }
255
+ def location; end
256
+
257
+ sig { params(name: Symbol, location: Rubydex::Location).void }
258
+ def initialize(name, location); end
259
+ end
260
+
261
+ class Rubydex::Signature::PositionalParameter < Rubydex::Signature::Parameter; end
262
+ class Rubydex::Signature::OptionalPositionalParameter < Rubydex::Signature::Parameter; end
263
+ class Rubydex::Signature::RestPositionalParameter < Rubydex::Signature::Parameter; end
264
+ class Rubydex::Signature::PostParameter < Rubydex::Signature::Parameter; end
265
+ class Rubydex::Signature::KeywordParameter < Rubydex::Signature::Parameter; end
266
+ class Rubydex::Signature::OptionalKeywordParameter < Rubydex::Signature::Parameter; end
267
+ class Rubydex::Signature::RestKeywordParameter < Rubydex::Signature::Parameter; end
268
+ class Rubydex::Signature::ForwardParameter < Rubydex::Signature::Parameter; end
269
+ class Rubydex::Signature::BlockParameter < Rubydex::Signature::Parameter; end
190
270
 
191
271
  class Rubydex::ModuleDefinition < Rubydex::Definition
192
272
  sig { returns(T::Array[Rubydex::Mixin]) }
@@ -220,9 +300,138 @@ class Rubydex::Include < Rubydex::Mixin; end
220
300
  class Rubydex::Prepend < Rubydex::Mixin; end
221
301
  class Rubydex::Extend < Rubydex::Mixin; end
222
302
 
303
+ module Rubydex::Severity
304
+ # Every severity, ordered from the most to the least severe.
305
+ ALL = T.let(T.unsafe(nil), T::Array[T.class_of(Rubydex::Severity::Base)])
306
+
307
+ sig { params(value: Symbol).returns(T.class_of(Rubydex::Severity::Base)) }
308
+ def self.from_value(value); end
309
+ end
310
+
311
+ class Rubydex::Severity::Base
312
+ abstract!
313
+
314
+ sig { returns(Symbol) }
315
+ def self.value; end
316
+ end
317
+
318
+ class Rubydex::Severity::Error < Rubydex::Severity::Base; end
319
+ class Rubydex::Severity::Warning < Rubydex::Severity::Base; end
320
+ class Rubydex::Severity::Information < Rubydex::Severity::Base; end
321
+ class Rubydex::Severity::Hint < Rubydex::Severity::Base; end
322
+
323
+ class Rubydex::Rule
324
+ abstract!
325
+
326
+ class << self
327
+ abstract!
328
+
329
+ sig { returns(String) }
330
+ def rule_name; end
331
+
332
+ sig { abstract.returns(T.class_of(Rubydex::Severity::Base)) }
333
+ def default_severity; end
334
+
335
+ sig { params(config: Rubydex::LinterConfig).returns(T.class_of(Rubydex::Severity::Base)) }
336
+ def severity(config); end
337
+ end
338
+ end
339
+
340
+ module Rubydex::Rules
341
+ ALL = T.let(T.unsafe(nil), T::Array[T.class_of(Rubydex::Rule)])
342
+ end
343
+
344
+ class Rubydex::Rules::ParseError < Rubydex::Rule
345
+ class << self
346
+ sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
347
+ def default_severity; end
348
+ end
349
+ end
350
+
351
+ class Rubydex::Rules::ParseWarning < Rubydex::Rule
352
+ class << self
353
+ sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
354
+ def default_severity; end
355
+ end
356
+ end
357
+
358
+ class Rubydex::Rules::DynamicConstantReference < Rubydex::Rule
359
+ class << self
360
+ sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
361
+ def default_severity; end
362
+ end
363
+ end
364
+
365
+ class Rubydex::Rules::DynamicSingletonDefinition < Rubydex::Rule
366
+ class << self
367
+ sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
368
+ def default_severity; end
369
+ end
370
+ end
371
+
372
+ class Rubydex::Rules::DynamicAncestor < Rubydex::Rule
373
+ class << self
374
+ sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
375
+ def default_severity; end
376
+ end
377
+ end
378
+
379
+ class Rubydex::Rules::TopLevelMixinSelf < Rubydex::Rule
380
+ class << self
381
+ sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
382
+ def default_severity; end
383
+ end
384
+ end
385
+
386
+ class Rubydex::Rules::InvalidConstantVisibility < Rubydex::Rule
387
+ class << self
388
+ sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
389
+ def default_severity; end
390
+ end
391
+ end
392
+
393
+ class Rubydex::Rules::InvalidMethodVisibility < Rubydex::Rule
394
+ class << self
395
+ sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
396
+ def default_severity; end
397
+ end
398
+ end
399
+
400
+ class Rubydex::Rules::UndefinedMethodVisibilityTarget < Rubydex::Rule
401
+ class << self
402
+ sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
403
+ def default_severity; end
404
+ end
405
+ end
406
+
407
+ class Rubydex::Rules::UndefinedConstantVisibilityTarget < Rubydex::Rule
408
+ class << self
409
+ sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
410
+ def default_severity; end
411
+ end
412
+ end
413
+
414
+ class Rubydex::RelatedInformation
415
+ sig { params(message: String, location: Rubydex::Location).void }
416
+ def initialize(message, location); end
417
+
418
+ sig { returns(Rubydex::Location) }
419
+ def location; end
420
+
421
+ sig { returns(String) }
422
+ def message; end
423
+ end
424
+
223
425
  class Rubydex::Diagnostic
224
- sig { params(rule: Symbol, message: String, location: Rubydex::Location).void }
225
- def initialize(rule:, message:, location:); end
426
+ sig do
427
+ params(
428
+ rule: T.class_of(Rubydex::Rule),
429
+ message: String,
430
+ location: Rubydex::Location,
431
+ related_information: T::Array[Rubydex::RelatedInformation],
432
+ ).void
433
+ end
434
+ def initialize(rule:, message:, location:, related_information: []); end
226
435
 
227
436
  sig { returns(Rubydex::Location) }
228
437
  def location; end
@@ -230,8 +439,231 @@ class Rubydex::Diagnostic
230
439
  sig { returns(String) }
231
440
  def message; end
232
441
 
233
- sig { returns(Symbol) }
442
+ sig { returns(T.class_of(Rubydex::Rule)) }
234
443
  def rule; end
444
+
445
+ sig { returns(T::Array[Rubydex::RelatedInformation]) }
446
+ def related_information; end
447
+ end
448
+
449
+ module Rubydex::Linter; end
450
+ module Rubydex::Linter::Helpers; end
451
+ module Rubydex::Linter::Rules; end
452
+
453
+ module Rubydex::Linter::Helpers::PathHelpers
454
+ extend T::Helpers
455
+
456
+ requires_ancestor { Rubydex::Linter::CustomRule }
457
+
458
+ EXCLUDE_FNMATCH_FLAGS = T.let(T.unsafe(nil), Integer)
459
+ TEST_PATHS = T.let(T.unsafe(nil), T::Array[String])
460
+
461
+ sig do
462
+ params(
463
+ path: String,
464
+ patterns: T::Array[String],
465
+ workspace: String,
466
+ flags: Integer,
467
+ ).returns(T::Boolean)
468
+ end
469
+ def self.path_matches_patterns?(path, patterns, workspace:, flags: 0); end
470
+
471
+ sig { params(location: Rubydex::Location, workspace: String).returns(String) }
472
+ def self.display_path(location, workspace:); end
473
+
474
+ sig do
475
+ params(
476
+ definitions: T::Enumerable[Rubydex::Definition],
477
+ excluded_patterns: T::Array[String],
478
+ ).returns(T::Array[Rubydex::Definition])
479
+ end
480
+ def reject_definitions_in_paths(definitions, excluded_patterns); end
481
+
482
+ sig do
483
+ params(
484
+ definitions: T::Enumerable[Rubydex::Definition],
485
+ patterns: T::Array[String],
486
+ ).returns(T::Array[Rubydex::Definition])
487
+ end
488
+ def select_definitions_in_paths(definitions, patterns); end
489
+
490
+ private
491
+
492
+ sig { params(path: String, patterns: T::Array[String]).returns(T::Boolean) }
493
+ def path_matches_patterns?(path, patterns); end
494
+
495
+ sig { params(path: String).returns(T::Boolean) }
496
+ def test_path?(path); end
497
+
498
+ sig { params(definition: Rubydex::Definition).returns(T.nilable(String)) }
499
+ def path_for_definition(definition); end
500
+ end
501
+
502
+ module Rubydex::Linter::Helpers::SourceAccessHelpers
503
+ sig { params(uri: String).returns(Rubydex::Location) }
504
+ def file_location(uri); end
505
+
506
+ sig { params(uri: String).returns(String) }
507
+ def path_for_uri(uri); end
508
+
509
+ private
510
+
511
+ sig { params(location: Rubydex::Location).returns(T.nilable(String)) }
512
+ def source_for_location(location); end
513
+ end
514
+
515
+ # Base class for semantic lint rules, which collect their diagnostics by walking a resolved graph.
516
+ class Rubydex::Linter::CustomRule < Rubydex::Rule
517
+ abstract!
518
+
519
+ sig { params(graph: Rubydex::Graph, config: Rubydex::LinterConfig).void }
520
+ def initialize(graph, config:); end
521
+
522
+ sig { returns(Rubydex::Graph) }
523
+ def graph; end
524
+
525
+ sig { returns(Rubydex::LinterConfig) }
526
+ def config; end
527
+
528
+ sig { returns(T::Array[Rubydex::Diagnostic]) }
529
+ def diagnostics; end
530
+
531
+ sig { params(definition: Rubydex::Definition).returns(Rubydex::Location) }
532
+ def diagnostic_location(definition); end
533
+
534
+ sig { params(base_name: String).returns(T::Enumerable[Rubydex::Class]) }
535
+ def child_classes(base_name); end
536
+
537
+ sig { params(name: String).returns(Rubydex::Namespace) }
538
+ def required_namespace(name); end
539
+
540
+ sig do
541
+ params(
542
+ namespace: Rubydex::Namespace,
543
+ method_name: String,
544
+ ).returns(Rubydex::Method)
545
+ end
546
+ def required_method(namespace, method_name); end
547
+
548
+ sig { returns(String) }
549
+ def rule_name; end
550
+
551
+ sig { abstract.void }
552
+ def lint; end
553
+
554
+ protected
555
+
556
+ sig do
557
+ params(
558
+ message: String,
559
+ location: Rubydex::Location,
560
+ related_information: T::Array[Rubydex::RelatedInformation],
561
+ ).void
562
+ end
563
+ def add_diagnostic(message, location, related_information: []); end
564
+ end
565
+
566
+ class Rubydex::Linter::MissingGraphDependencyError < StandardError
567
+ sig { params(rule_name: String, dependency: String).void }
568
+ def initialize(rule_name, dependency); end
569
+ end
570
+
571
+ class Rubydex::Linter::RuleLoadError < StandardError; end
572
+
573
+ class Rubydex::Linter::RuleLoader
574
+ RULE_GLOB = T.let(T.unsafe(nil), String)
575
+
576
+ sig { params(workspace_path: String).void }
577
+ def self.load(workspace_path); end
578
+ end
579
+
580
+ class Rubydex::Linter::Rules::RuleStructure < Rubydex::Linter::CustomRule
581
+ include Rubydex::Linter::Helpers::SourceAccessHelpers
582
+
583
+ BASE_RULE_NAME = T.let(T.unsafe(nil), String)
584
+ RULE_NAMESPACE = T.let(T.unsafe(nil), String)
585
+ RULE_FILE_PATTERNS = T.let(T.unsafe(nil), T::Array[String])
586
+ TEST_FILE_PATTERNS = T.let(T.unsafe(nil), T::Array[String])
587
+
588
+ class << self
589
+ sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
590
+ def default_severity; end
591
+ end
592
+
593
+ sig { override.void }
594
+ def lint; end
595
+ end
596
+
597
+ class Rubydex::Linter::RuleTestCase < Minitest::Test
598
+ DEFAULT_FILE = T.let(T.unsafe(nil), String)
599
+ ANNOTATION_PATTERN = T.let(T.unsafe(nil), Regexp)
600
+
601
+ sig { returns(String) }
602
+ def workspace_path; end
603
+
604
+ sig { params(name: String).void }
605
+ def initialize(name); end
606
+
607
+ sig { void }
608
+ def teardown; end
609
+
610
+ sig { returns(T.class_of(Rubydex::Linter::CustomRule)) }
611
+ def rule_class; end
612
+
613
+ sig { params(sources: T::Hash[String, String]).void }
614
+ def add_shared_source(sources); end
615
+
616
+ sig { returns(T::Array[String]) }
617
+ def ignored_diagnostic_files; end
618
+
619
+ sig { returns(Rubydex::LinterConfig) }
620
+ def rule_config; end
621
+
622
+ sig do
623
+ params(
624
+ args: T.any(String, T::Hash[T.any(String, Symbol), String]),
625
+ rule_builder: T.nilable(T.proc.params(graph: Rubydex::Graph).returns(Rubydex::Linter::CustomRule)),
626
+ ).returns(T::Array[Rubydex::Diagnostic])
627
+ end
628
+ def assert_diagnostics(*args, &rule_builder); end
629
+
630
+ sig do
631
+ params(
632
+ args: T.any(String, T::Hash[T.any(String, Symbol), String]),
633
+ rule_builder: T.nilable(T.proc.params(graph: Rubydex::Graph).returns(Rubydex::Linter::CustomRule)),
634
+ ).returns(T::Array[Rubydex::Diagnostic])
635
+ end
636
+ def assert_no_diagnostics(*args, &rule_builder); end
637
+
638
+ sig do
639
+ params(
640
+ dependency: String,
641
+ args: T.any(String, T::Hash[T.any(String, Symbol), String]),
642
+ after_excluding: T::Array[String],
643
+ rule_builder: T.nilable(T.proc.params(graph: Rubydex::Graph).returns(Rubydex::Linter::CustomRule)),
644
+ ).void
645
+ end
646
+ def assert_handles_missing_required_dependency(dependency, *args, after_excluding: [], &rule_builder); end
647
+ end
648
+
649
+ class Rubydex::Linter::Runner
650
+ sig do
651
+ params(
652
+ graph: Rubydex::Graph,
653
+ custom_rules: T::Array[T.class_of(Rubydex::Linter::CustomRule)],
654
+ config: Rubydex::LinterConfig,
655
+ ).void
656
+ end
657
+ def initialize(graph, custom_rules:, config:); end
658
+
659
+ sig { returns(Rubydex::Graph) }
660
+ def graph; end
661
+
662
+ sig { returns(T::Array[T.class_of(Rubydex::Linter::CustomRule)]) }
663
+ def custom_rules; end
664
+
665
+ sig { returns(T::Array[Rubydex::Diagnostic]) }
666
+ def run; end
235
667
  end
236
668
 
237
669
  class Rubydex::Keyword
@@ -273,6 +705,74 @@ end
273
705
  class Rubydex::Error < StandardError; end
274
706
  class Rubydex::AliasCycleError < Rubydex::Error; end
275
707
  class Rubydex::ConfigError < Rubydex::Error; end
708
+ class Rubydex::QueryError < Rubydex::Error; end
709
+ class Rubydex::QuerySyntaxError < Rubydex::QueryError; end
710
+ class Rubydex::QueryExecutionError < Rubydex::QueryError; end
711
+ class Rubydex::StaleQueryResultError < Rubydex::QueryError; end
712
+
713
+ # The configuration of a workspace, parsed from its `rubydex.toml`. It carries both the settings that are global to
714
+ # every built-in tool, such as the workspace being analyzed, and the typed settings of each tool's own section (e.g.
715
+ # `[graph]`, `[linter]`).
716
+ class Rubydex::Config
717
+ class << self
718
+ # Loads the configuration file for `workspace_path/rubydex.toml` if it exists or uses the defaults.
719
+ sig { params(workspace_path: String).returns(Rubydex::Config) }
720
+ def load(workspace_path); end
721
+ end
722
+
723
+ # The linter's settings, read from the `[linter]` section.
724
+ sig { returns(Rubydex::LinterConfig) }
725
+ def linter; end
726
+
727
+ # The configured workspace path, which is usually PWD, except for editors that spawn language servers outside of pwd.
728
+ sig { returns(String) }
729
+ def workspace_path; end
730
+ end
731
+
732
+ # The linter's settings, read from the `[linter]` section of the configuration file.
733
+ class Rubydex::LinterConfig
734
+ # The configured rules, keyed by rule name. Only rules the configuration file mentions appear here, so a rule that
735
+ # was never configured is absent rather than present with its defaults.
736
+ sig { returns(T::Hash[String, Rubydex::RuleConfig]) }
737
+ attr_reader :rules
738
+
739
+ sig { params(rules: T::Hash[String, Rubydex::RuleConfig]).void }
740
+ def initialize(rules); end
741
+
742
+ sig { params(rule_class: T.class_of(Rubydex::Rule)).returns(T::Boolean) }
743
+ def rule_enabled?(rule_class); end
744
+
745
+ sig { params(rule_class: T.class_of(Rubydex::Rule)).returns(T::Array[String]) }
746
+ def excludes_for(rule_class); end
747
+
748
+ sig { params(rule_class: T.class_of(Rubydex::Rule)).returns(T.nilable(T.class_of(Rubydex::Severity::Base))) }
749
+ def severity_for(rule_class); end
750
+ end
751
+
752
+ # The settings of a single linter rule, read from a `[linter.rules.RuleName]` table.
753
+ class Rubydex::RuleConfig
754
+ sig { returns(String) }
755
+ attr_reader :name
756
+
757
+ sig { returns(T::Array[String]) }
758
+ attr_reader :exclude_patterns
759
+
760
+ sig { returns(T.nilable(T.class_of(Rubydex::Severity::Base))) }
761
+ attr_reader :severity
762
+
763
+ sig do
764
+ params(
765
+ name: String,
766
+ enabled: T::Boolean,
767
+ exclude_patterns: T::Array[String],
768
+ severity: T.nilable(T.class_of(Rubydex::Severity::Base)),
769
+ ).void
770
+ end
771
+ def initialize(name, enabled, exclude_patterns = [], severity = nil); end
772
+
773
+ sig { returns(T::Boolean) }
774
+ def enabled?; end
775
+ end
276
776
 
277
777
  class Rubydex::Failure
278
778
  sig { params(message: String).void }
@@ -293,13 +793,55 @@ class Rubydex::Query
293
793
  def schema(format = :table); end
294
794
  end
295
795
 
296
- sig { params(graph: Rubydex::Graph, format: T.any(String, Symbol)).returns(String) }
297
- def render(graph, format = :table); end
796
+ sig { params(graph: Rubydex::Graph).returns(Rubydex::Query::Result) }
797
+ def run(graph); end
798
+ end
799
+
800
+ # The columns and rows produced by one run of a query. Enumerable over its rows.
801
+ class Rubydex::Query::Result
802
+ include Enumerable
803
+ extend T::Generic
804
+
805
+ Elem = type_member { { fixed: T::Hash[String, T.untyped] } }
806
+
807
+ class << self
808
+ private
809
+
810
+ # A result only comes from `Query#run`. The C extension undefines `new`, so a call raises.
811
+ sig { returns(T.noreturn) }
812
+ def new; end
813
+ end
814
+
815
+ sig { returns(T::Array[String]) }
816
+ def columns; end
817
+
818
+ sig { returns(T::Array[T::Hash[String, T.untyped]]) }
819
+ def rows; end
820
+
821
+ sig { override.params(block: T.proc.params(row: T::Hash[String, T.untyped]).void).returns(T.self_type) }
822
+ sig { override.returns(T::Enumerator[T::Hash[String, T.untyped]]) }
823
+ def each(&block); end
824
+
825
+ sig { returns(Integer) }
826
+ def size; end
827
+
828
+ sig { returns(Integer) }
829
+ def length; end
830
+
831
+ sig { returns(T::Boolean) }
832
+ def empty?; end
833
+
834
+ sig { params(format: T.any(String, Symbol)).returns(String) }
835
+ def render(format = :table); end
298
836
  end
299
837
 
300
838
  class Rubydex::Graph
301
- sig { params(workspace_path: T.nilable(String)).void }
302
- def initialize(workspace_path: nil); end
839
+ class << self
840
+ # Creates a new graph with the loaded configuration. For use cases where the graph must be shared between
841
+ # different tools, do not use this. Create and own a `Config` object instead.
842
+ sig { params(workspace_path: String).returns(T.attached_class) }
843
+ def configure_for_workspace(workspace_path); end
844
+ end
303
845
 
304
846
  sig { params(fully_qualified_name: String).returns(T.nilable(Rubydex::Declaration)) }
305
847
  def [](fully_qualified_name); end
@@ -332,12 +874,13 @@ class Rubydex::Graph
332
874
  sig { returns(T::Array[String]) }
333
875
  def index_workspace; end
334
876
 
335
- # Loads configuration, merging its exclusion patterns into the graph's configuration (the workspace path is never
336
- # overridden). With `config_path` (resolved relative to the workspace path), an explicitly named file that does not
337
- # exist raises `Rubydex::ConfigError`. With no argument, the default `.rubydex` is loaded if present and ignored if
338
- # missing. Raises `Rubydex::ConfigError` if a file cannot be read or is malformed.
339
- sig { params(config_path: T.nilable(String)).void }
340
- def load_config(config_path = nil); end
877
+ # Returns the keyword object for the name, or `nil` if it is not a Ruby keyword
878
+ sig { params(name: String).returns(T.nilable(Rubydex::Keyword)) }
879
+ def keyword(name); end
880
+
881
+ # Loads the configuration in the graph.
882
+ sig { params(config: Rubydex::Config).void }
883
+ def load_config(config); end
341
884
 
342
885
  sig { returns(T::Enumerable[Rubydex::MethodReference]) }
343
886
  def method_references; end
@@ -363,12 +906,10 @@ class Rubydex::Graph
363
906
  sig { params(encoding: String).void }
364
907
  def encoding=(encoding); end
365
908
 
909
+ # The root directory of the workspace being analyzed, which comes from the loaded `Rubydex::Config`
366
910
  sig { returns(String) }
367
911
  def workspace_path; end
368
912
 
369
- sig { params(workspace_path: String).returns(String) }
370
- def workspace_path=(workspace_path); end
371
-
372
913
  sig { returns(T::Array[String]) }
373
914
  def workspace_paths; end
374
915
 
data/rust/Cargo.lock CHANGED
@@ -262,9 +262,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
262
262
 
263
263
  [[package]]
264
264
  name = "cypher-parser"
265
- version = "0.2.0"
265
+ version = "0.8.1"
266
266
  source = "registry+https://github.com/rust-lang/crates.io-index"
267
- checksum = "fed0d1e561d51e651bdf70f8439da293fd0f1fe34d8431059061eadaefc7abb1"
267
+ checksum = "6ada60f869dbe5818d0ee40b06ad988627cc0d692d1ed42d692353bf0af8ddb6"
268
268
 
269
269
  [[package]]
270
270
  name = "difflib"
@@ -27,7 +27,7 @@ jemalloc_dylib = ["jemalloc", "tikv-jemallocator/disable_initial_exec_tls"]
27
27
  test_utils = ["dep:tempfile"]
28
28
 
29
29
  [dependencies]
30
- cypher-parser = "0.2"
30
+ cypher-parser = "0.8.1"
31
31
  ruby-prism = "1.9.0"
32
32
  ruby-rbs = "0.3"
33
33
  url = "2.5.4"
@@ -3,6 +3,7 @@ mod imp {
3
3
  use std::time::Instant;
4
4
 
5
5
  use rubydex::{
6
+ config::Config,
6
7
  indexing::{self, IndexerBackend},
7
8
  listing,
8
9
  model::graph::Graph,
@@ -27,11 +28,8 @@ mod imp {
27
28
  assert!(workspace_path.is_dir(), "the workspace path must be a directory");
28
29
 
29
30
  let mut graph = Graph::new();
30
- graph.set_workspace_path(workspace_path);
31
-
32
- if let Err(error) = graph.load_config(None) {
33
- eprintln!("{error}");
34
- }
31
+ let config = Config::load(&workspace_path).expect("the workspace configuration must be valid");
32
+ graph.load_config(&config);
35
33
 
36
34
  let time = Instant::now();
37
35