rigortype 0.2.7 → 0.2.8
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 +1 -1
- data/docs/manual/04-diagnostics.md +7 -4
- data/docs/manual/06-baseline.md +5 -2
- data/docs/manual/15-type-protection-coverage.md +6 -4
- data/lib/rigor/analysis/baseline.rb +56 -96
- data/lib/rigor/analysis/buffer_binding.rb +13 -20
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
- data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
- data/lib/rigor/analysis/check_rules.rb +174 -274
- data/lib/rigor/analysis/dependency_recorder.rb +26 -38
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
- data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
- data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
- data/lib/rigor/analysis/diagnostic.rb +48 -75
- data/lib/rigor/analysis/erb_template_detector.rb +13 -18
- data/lib/rigor/analysis/fact_store.rb +12 -22
- data/lib/rigor/analysis/incremental.rb +47 -63
- data/lib/rigor/analysis/incremental_session.rb +74 -100
- data/lib/rigor/analysis/project_scan.rb +12 -23
- data/lib/rigor/analysis/result.rb +3 -5
- data/lib/rigor/analysis/rule_catalog.rb +67 -95
- data/lib/rigor/analysis/run_stats.rb +38 -61
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
- data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
- data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
- data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
- data/lib/rigor/analysis/runner.rb +220 -333
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
- data/lib/rigor/analysis/worker_session.rb +64 -102
- data/lib/rigor/ast/type_node.rb +6 -8
- data/lib/rigor/ast.rb +6 -10
- data/lib/rigor/bleeding_edge.rb +27 -35
- data/lib/rigor/builtins/hkt_builtins.rb +7 -14
- data/lib/rigor/builtins/imported_refinements.rb +106 -149
- data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
- data/lib/rigor/builtins/regex_refinement.rb +32 -43
- data/lib/rigor/builtins/static_return_refinements.rb +57 -81
- data/lib/rigor/cache/descriptor.rb +58 -94
- data/lib/rigor/cache/incremental_snapshot.rb +32 -48
- data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
- data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
- data/lib/rigor/cache/rbs_constant_table.rb +8 -13
- data/lib/rigor/cache/rbs_descriptor.rb +8 -13
- data/lib/rigor/cache/rbs_environment.rb +11 -19
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
- data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
- data/lib/rigor/cache/store.rb +96 -151
- data/lib/rigor/cli/annotate_command.rb +51 -88
- data/lib/rigor/cli/baseline_command.rb +10 -17
- data/lib/rigor/cli/check_command.rb +121 -199
- data/lib/rigor/cli/check_runner_factory.rb +8 -14
- data/lib/rigor/cli/ci_detector.rb +9 -14
- data/lib/rigor/cli/command.rb +8 -14
- data/lib/rigor/cli/coverage_command.rb +19 -26
- data/lib/rigor/cli/coverage_mutation.rb +14 -19
- data/lib/rigor/cli/coverage_report.rb +0 -2
- data/lib/rigor/cli/coverage_scan.rb +7 -11
- data/lib/rigor/cli/diagnostic_formats.rb +43 -62
- data/lib/rigor/cli/diff_command.rb +10 -20
- data/lib/rigor/cli/docs_command.rb +26 -40
- data/lib/rigor/cli/doctor_command.rb +8 -9
- data/lib/rigor/cli/explain_command.rb +4 -7
- data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
- data/lib/rigor/cli/fused_protection_report.rb +6 -8
- data/lib/rigor/cli/lsp_command.rb +11 -19
- data/lib/rigor/cli/mcp_command.rb +4 -6
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
- data/lib/rigor/cli/mutation_protection_report.rb +6 -9
- data/lib/rigor/cli/options.rb +11 -19
- data/lib/rigor/cli/plugin_command.rb +18 -30
- data/lib/rigor/cli/plugins_command.rb +29 -51
- data/lib/rigor/cli/plugins_renderer.rb +12 -20
- data/lib/rigor/cli/prism_colorizer.rb +13 -19
- data/lib/rigor/cli/protection_renderer.rb +5 -7
- data/lib/rigor/cli/protection_report.rb +27 -17
- data/lib/rigor/cli/renderable.rb +4 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
- data/lib/rigor/cli/sig_gen_command.rb +14 -26
- data/lib/rigor/cli/skill_command.rb +30 -47
- data/lib/rigor/cli/skill_describe.rb +40 -64
- data/lib/rigor/cli/trace_command.rb +9 -16
- data/lib/rigor/cli/trace_renderer.rb +35 -51
- data/lib/rigor/cli/triage_command.rb +6 -10
- data/lib/rigor/cli/triage_renderer.rb +1 -2
- data/lib/rigor/cli/type_of_command.rb +14 -23
- data/lib/rigor/cli/type_of_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_command.rb +6 -10
- data/lib/rigor/cli/type_scan_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_report.rb +2 -3
- data/lib/rigor/cli/upgrade_command.rb +2 -3
- data/lib/rigor/cli.rb +16 -28
- data/lib/rigor/config_audit.rb +35 -44
- data/lib/rigor/configuration/dependencies.rb +41 -70
- data/lib/rigor/configuration/severity_profile.rb +28 -42
- data/lib/rigor/configuration.rb +150 -241
- data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
- data/lib/rigor/environment/class_registry.rb +19 -26
- data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
- data/lib/rigor/environment/lockfile_resolver.rb +31 -47
- data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
- data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
- data/lib/rigor/environment/rbs_loader.rb +420 -518
- data/lib/rigor/environment/reflection.rb +28 -53
- data/lib/rigor/environment/reporters.rb +13 -24
- data/lib/rigor/environment.rb +176 -291
- data/lib/rigor/flow_contribution/conflict.rb +16 -27
- data/lib/rigor/flow_contribution/element.rb +7 -12
- data/lib/rigor/flow_contribution/fact.rb +33 -57
- data/lib/rigor/flow_contribution/merge_result.rb +6 -9
- data/lib/rigor/flow_contribution/merger.rb +32 -47
- data/lib/rigor/flow_contribution.rb +37 -55
- data/lib/rigor/inference/acceptance.rb +133 -219
- data/lib/rigor/inference/block_parameter_binder.rb +52 -82
- data/lib/rigor/inference/body_fixpoint.rb +22 -30
- data/lib/rigor/inference/budget_trace.rb +43 -63
- data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
- data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
- data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
- data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
- data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
- data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
- data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
- data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
- data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
- data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
- data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
- data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
- data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
- data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
- data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
- data/lib/rigor/inference/coverage_scanner.rb +13 -18
- data/lib/rigor/inference/def_return_typer.rb +8 -14
- data/lib/rigor/inference/dynamic_origin.rb +23 -20
- data/lib/rigor/inference/expression_typer.rb +774 -1152
- data/lib/rigor/inference/fallback.rb +8 -12
- data/lib/rigor/inference/fallback_tracer.rb +4 -10
- data/lib/rigor/inference/flow_tracer.rb +25 -36
- data/lib/rigor/inference/hkt_body.rb +45 -68
- data/lib/rigor/inference/hkt_body_parser.rb +33 -52
- data/lib/rigor/inference/hkt_reducer.rb +38 -59
- data/lib/rigor/inference/hkt_registry.rb +50 -76
- data/lib/rigor/inference/indexed_narrowing.rb +55 -81
- data/lib/rigor/inference/macro_block_self_type.rb +21 -34
- data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
- data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
- data/lib/rigor/inference/method_dispatcher.rb +378 -524
- data/lib/rigor/inference/method_parameter_binder.rb +81 -114
- data/lib/rigor/inference/multi_target_binder.rb +51 -68
- data/lib/rigor/inference/mutation_widening.rb +115 -158
- data/lib/rigor/inference/narrowing.rb +509 -727
- data/lib/rigor/inference/origin_lookup.rb +38 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
- data/lib/rigor/inference/precision_scanner.rb +26 -35
- data/lib/rigor/inference/project_patched_methods.rb +20 -32
- data/lib/rigor/inference/project_patched_scanner.rb +23 -37
- data/lib/rigor/inference/protection_scanner.rb +16 -18
- data/lib/rigor/inference/rbs_type_translator.rb +49 -66
- data/lib/rigor/inference/scope_indexer.rb +473 -817
- data/lib/rigor/inference/statement_evaluator.rb +706 -1111
- data/lib/rigor/inference/struct_fold_safety.rb +34 -48
- data/lib/rigor/inference/synthetic_method.rb +9 -16
- data/lib/rigor/inference/synthetic_method_index.rb +20 -35
- data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
- data/lib/rigor/language_server/buffer_resolution.rb +6 -9
- data/lib/rigor/language_server/buffer_table.rb +11 -18
- data/lib/rigor/language_server/completion_provider.rb +69 -116
- data/lib/rigor/language_server/debouncer.rb +16 -25
- data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
- data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
- data/lib/rigor/language_server/folding_range_provider.rb +11 -19
- data/lib/rigor/language_server/hover_provider.rb +8 -11
- data/lib/rigor/language_server/hover_renderer.rb +56 -93
- data/lib/rigor/language_server/loop.rb +14 -19
- data/lib/rigor/language_server/project_context.rb +46 -74
- data/lib/rigor/language_server/selection_range_provider.rb +9 -12
- data/lib/rigor/language_server/server.rb +55 -83
- data/lib/rigor/language_server/signature_help_provider.rb +29 -46
- data/lib/rigor/language_server/synchronized_writer.rb +4 -7
- data/lib/rigor/language_server/uri.rb +8 -13
- data/lib/rigor/language_server.rb +4 -6
- data/lib/rigor/mcp/loop.rb +2 -3
- data/lib/rigor/mcp/server.rb +4 -7
- data/lib/rigor/mcp.rb +3 -6
- data/lib/rigor/plugin/access_denied_error.rb +5 -8
- data/lib/rigor/plugin/additional_initializer.rb +21 -31
- data/lib/rigor/plugin/base.rb +335 -518
- data/lib/rigor/plugin/blueprint.rb +14 -23
- data/lib/rigor/plugin/box.rb +18 -29
- data/lib/rigor/plugin/fact_store.rb +16 -26
- data/lib/rigor/plugin/inflector.rb +37 -53
- data/lib/rigor/plugin/io_boundary.rb +33 -56
- data/lib/rigor/plugin/isolation.rb +42 -55
- data/lib/rigor/plugin/load_error.rb +10 -15
- data/lib/rigor/plugin/loader.rb +30 -49
- data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
- data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
- data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
- data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
- data/lib/rigor/plugin/macro.rb +10 -15
- data/lib/rigor/plugin/manifest.rb +85 -144
- data/lib/rigor/plugin/node_context.rb +14 -22
- data/lib/rigor/plugin/node_rule_walk.rb +49 -74
- data/lib/rigor/plugin/protocol_contract.rb +25 -39
- data/lib/rigor/plugin/registry.rb +132 -205
- data/lib/rigor/plugin/services.rb +21 -33
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
- data/lib/rigor/plugin/trust_policy.rb +24 -38
- data/lib/rigor/plugin/type_node_resolver.rb +15 -24
- data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
- data/lib/rigor/protection/mutation_scanner.rb +27 -35
- data/lib/rigor/protection/mutator.rb +50 -70
- data/lib/rigor/protection/test_suite_oracle.rb +20 -27
- data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
- data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
- data/lib/rigor/rbs_extended/reporter.rb +24 -40
- data/lib/rigor/rbs_extended.rb +107 -197
- data/lib/rigor/reflection.rb +68 -86
- data/lib/rigor/scope/discovery_index.rb +14 -19
- data/lib/rigor/scope.rb +255 -310
- data/lib/rigor/sig_gen/classification.rb +6 -10
- data/lib/rigor/sig_gen/generator.rb +197 -323
- data/lib/rigor/sig_gen/layout_index.rb +12 -20
- data/lib/rigor/sig_gen/method_candidate.rb +12 -17
- data/lib/rigor/sig_gen/observation_collector.rb +38 -70
- data/lib/rigor/sig_gen/observed_call.rb +13 -23
- data/lib/rigor/sig_gen/path_mapper.rb +17 -29
- data/lib/rigor/sig_gen/renderer.rb +7 -13
- data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
- data/lib/rigor/sig_gen/write_result.rb +8 -16
- data/lib/rigor/sig_gen/writer.rb +95 -174
- data/lib/rigor/sig_gen.rb +3 -6
- data/lib/rigor/signature_path_audit.rb +24 -30
- data/lib/rigor/source/constant_path.rb +10 -14
- data/lib/rigor/source/literals.rb +31 -45
- data/lib/rigor/source/node_locator.rb +9 -11
- data/lib/rigor/source/node_walker.rb +9 -13
- data/lib/rigor/source.rb +3 -4
- data/lib/rigor/testing.rb +16 -20
- data/lib/rigor/triage/catalogue.rb +38 -62
- data/lib/rigor/triage.rb +31 -52
- data/lib/rigor/trinary.rb +9 -13
- data/lib/rigor/type/acceptance_router.rb +4 -6
- data/lib/rigor/type/accepts_result.rb +10 -14
- data/lib/rigor/type/app.rb +19 -27
- data/lib/rigor/type/bot.rb +4 -6
- data/lib/rigor/type/bound_method.rb +10 -15
- data/lib/rigor/type/combinator.rb +165 -257
- data/lib/rigor/type/constant.rb +23 -34
- data/lib/rigor/type/data_class.rb +10 -15
- data/lib/rigor/type/data_instance.rb +14 -20
- data/lib/rigor/type/difference.rb +21 -32
- data/lib/rigor/type/dynamic.rb +3 -5
- data/lib/rigor/type/hash_shape.rb +32 -18
- data/lib/rigor/type/integer_range.rb +11 -16
- data/lib/rigor/type/intersection.rb +27 -42
- data/lib/rigor/type/nominal.rb +10 -15
- data/lib/rigor/type/plain_lattice.rb +9 -13
- data/lib/rigor/type/refined.rb +67 -114
- data/lib/rigor/type/singleton.rb +4 -6
- data/lib/rigor/type/struct_class.rb +11 -16
- data/lib/rigor/type/struct_instance.rb +15 -21
- data/lib/rigor/type/tuple.rb +14 -19
- data/lib/rigor/type/union.rb +30 -42
- data/lib/rigor/type_node/generic.rb +14 -26
- data/lib/rigor/type_node/identifier.rb +12 -19
- data/lib/rigor/type_node.rb +3 -12
- data/lib/rigor/value_semantics.rb +16 -21
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +71 -108
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -235
- data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
- data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
- data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
- data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
- data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
- data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
- data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
- data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
- data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
- data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
- data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
- data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
- data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
- data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
- data/sig/rigor/scope.rbs +6 -0
- metadata +2 -1
|
@@ -2,16 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module LanguageServer
|
|
5
|
-
# Per-session virtual file table. The LSP server maintains the
|
|
6
|
-
#
|
|
7
|
-
# reads from this table instead of disk so in-flight edits are
|
|
8
|
-
# reflected immediately.
|
|
5
|
+
# Per-session virtual file table. The LSP server maintains the canonical view of every open buffer here;
|
|
6
|
+
# analysis (slice 4+) reads from this table instead of disk so in-flight edits are reflected immediately.
|
|
9
7
|
#
|
|
10
|
-
# Keyed by `DocumentUri` (LSP `file://...` URIs). v1 ships
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# incremental edit application yet. Incremental sync is slice
|
|
14
|
-
# 10 (deferred per the design doc).
|
|
8
|
+
# Keyed by `DocumentUri` (LSP `file://...` URIs). v1 ships FULL text sync (LSP
|
|
9
|
+
# `TextDocumentSyncKind::Full = 1`) so each `didChange` carries the entire buffer text — there's no
|
|
10
|
+
# incremental edit application yet. Incremental sync is slice 10 (deferred per the design doc).
|
|
15
11
|
class BufferTable
|
|
16
12
|
# @!attribute uri [String] the LSP DocumentUri (e.g. `file:///abs/path/lib/foo.rb`).
|
|
17
13
|
# @!attribute bytes [String] the current full text of the buffer.
|
|
@@ -22,23 +18,20 @@ module Rigor
|
|
|
22
18
|
@entries = {}
|
|
23
19
|
end
|
|
24
20
|
|
|
25
|
-
# Records a `textDocument/didOpen` event. Replaces any
|
|
26
|
-
#
|
|
27
|
-
# URI; the new version is authoritative).
|
|
21
|
+
# Records a `textDocument/didOpen` event. Replaces any existing entry (LSP clients may re-open a
|
|
22
|
+
# previously closed URI; the new version is authoritative).
|
|
28
23
|
def open(uri:, bytes:, version:)
|
|
29
24
|
@entries[uri] = Entry.new(uri: uri, bytes: bytes, version: version)
|
|
30
25
|
end
|
|
31
26
|
|
|
32
|
-
# Records a `textDocument/didChange` event under FULL sync.
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
# violation), the entry is still created — defensive.
|
|
27
|
+
# Records a `textDocument/didChange` event under FULL sync. The full new buffer text replaces the entry.
|
|
28
|
+
# If the client sends a `didChange` for a URI that was never opened (spec violation), the entry is still
|
|
29
|
+
# created — defensive.
|
|
36
30
|
def change(uri:, bytes:, version:)
|
|
37
31
|
@entries[uri] = Entry.new(uri: uri, bytes: bytes, version: version)
|
|
38
32
|
end
|
|
39
33
|
|
|
40
|
-
# Records a `textDocument/didClose` event. The entry is
|
|
41
|
-
# removed. Subsequent reads via `#[]` return nil.
|
|
34
|
+
# Records a `textDocument/didClose` event. The entry is removed. Subsequent reads via `#[]` return nil.
|
|
42
35
|
def close(uri:)
|
|
43
36
|
@entries.delete(uri)
|
|
44
37
|
end
|
|
@@ -21,11 +21,9 @@ require_relative "../type/hash_shape"
|
|
|
21
21
|
|
|
22
22
|
module Rigor
|
|
23
23
|
module LanguageServer
|
|
24
|
-
# Answers `textDocument/completion` requests. Provides method
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# Refined / Shape receiver handling, and parse-recovery fallback for
|
|
28
|
-
# malformed buffers (sentinel injection).
|
|
24
|
+
# Answers `textDocument/completion` requests. Provides method completion for `obj.|` (known-type receiver
|
|
25
|
+
# → RBS-known methods), constant-path completion, hash-key completion, Union / Intersection / Refined /
|
|
26
|
+
# Shape receiver handling, and parse-recovery fallback for malformed buffers (sentinel injection).
|
|
29
27
|
#
|
|
30
28
|
# LSP `CompletionItemKind` values used:
|
|
31
29
|
# - 2 = Method, 5 = Field, 7 = Class, 9 = Module, 21 = Constant.
|
|
@@ -54,9 +52,8 @@ module Rigor
|
|
|
54
52
|
path, entry = buffer_for(uri)
|
|
55
53
|
return nil if entry.nil?
|
|
56
54
|
|
|
57
|
-
# Slice B4 — parse recovery. The common mid-edit buffer
|
|
58
|
-
#
|
|
59
|
-
# a sentinel name at the cursor before falling through.
|
|
55
|
+
# Slice B4 — parse recovery. The common mid-edit buffer (`obj.` / `Foo::`) doesn't parse cleanly; try
|
|
56
|
+
# inserting a sentinel name at the cursor before falling through.
|
|
60
57
|
bytes_to_parse, locate_at = parse_attempt_bytes(entry.bytes, line, character)
|
|
61
58
|
parse_result = Prism.parse(bytes_to_parse, filepath: path,
|
|
62
59
|
version: @project_context.configuration.target_ruby)
|
|
@@ -69,17 +66,14 @@ module Rigor
|
|
|
69
66
|
|
|
70
67
|
case node
|
|
71
68
|
when Prism::CallNode
|
|
72
|
-
# `hash[:|` patches to `hash[:KEY_SENTINEL]`, an index
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
# index access on a HashShape carrier; otherwise fall
|
|
77
|
-
# through to method completion.
|
|
69
|
+
# `hash[:|` patches to `hash[:KEY_SENTINEL]`, an index access call whose argument is the sentinel
|
|
70
|
+
# symbol; NodeLocator at the sentinel returns the inner CallNode for `[]`. Hash-key completion wins
|
|
71
|
+
# when the call is an index access on a HashShape carrier; otherwise fall through to method
|
|
72
|
+
# completion.
|
|
78
73
|
hash_key_completion_for(node, parse_result.value, path) ||
|
|
79
74
|
method_completion_for(node, parse_result.value, path)
|
|
80
75
|
when Prism::SymbolNode
|
|
81
|
-
# The sentinel symbol's NodeLocator hit; walk up via the
|
|
82
|
-
# AST to find the enclosing `[]` call.
|
|
76
|
+
# The sentinel symbol's NodeLocator hit; walk up via the AST to find the enclosing `[]` call.
|
|
83
77
|
hash_key_completion_for_symbol(node, parse_result.value, path)
|
|
84
78
|
when Prism::ConstantPathNode
|
|
85
79
|
constant_path_completion_for(node, path)
|
|
@@ -88,34 +82,25 @@ module Rigor
|
|
|
88
82
|
|
|
89
83
|
private
|
|
90
84
|
|
|
91
|
-
# Slice B4 — parse recovery. Returns `[bytes, [line, character]]`:
|
|
92
|
-
# the
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
# the cursor sits at a mid-edit `.` / `::` trigger, we
|
|
96
|
-
# splice a sentinel identifier into the source so Prism's
|
|
97
|
-
# AST captures the receiver / parent constant cleanly.
|
|
85
|
+
# Slice B4 — parse recovery. Returns `[bytes, [line, character]]`: the source to feed Prism plus the
|
|
86
|
+
# cursor position the caller should locate against. When the original buffer parses cleanly we return it
|
|
87
|
+
# verbatim; when it doesn't AND the cursor sits at a mid-edit `.` / `::` trigger, we splice a sentinel
|
|
88
|
+
# identifier into the source so Prism's AST captures the receiver / parent constant cleanly.
|
|
98
89
|
#
|
|
99
|
-
# The sentinel is a syntactically-unique identifier the
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
# method sentinel is a lower-snake identifier; constant
|
|
106
|
-
# sentinel MUST start with an uppercase letter (Ruby
|
|
107
|
-
# constants reject `_`-leading names — `Process::__Foo`
|
|
108
|
-
# parses as a method call, not a constant path).
|
|
90
|
+
# The sentinel is a syntactically-unique identifier the NodeLocator can find without ambiguity. We
|
|
91
|
+
# choose lower / upper case based on whether the trigger was `.` (method name — lowercase identifier) or
|
|
92
|
+
# `::` (constant path — uppercase identifier). Sentinels need to be parseable as their target shape:
|
|
93
|
+
# method sentinel is a lower-snake identifier; constant sentinel MUST start with an uppercase letter
|
|
94
|
+
# (Ruby constants reject `_`-leading names — `Process::__Foo` parses as a method call, not a constant
|
|
95
|
+
# path).
|
|
109
96
|
SENTINEL_METHOD = "__rigor_lsp_sentinel__"
|
|
110
97
|
SENTINEL_CONSTANT = "RigorLspSentinelXyz"
|
|
111
98
|
SENTINEL_HASH_KEY = "__rigor_lsp_key__"
|
|
112
99
|
private_constant :SENTINEL_METHOD, :SENTINEL_CONSTANT, :SENTINEL_HASH_KEY
|
|
113
100
|
|
|
114
101
|
def parse_attempt_bytes(original_bytes, line, character)
|
|
115
|
-
# Cheap probe: try original first. Most editor sessions
|
|
116
|
-
#
|
|
117
|
-
# some land on a clean buffer (e.g. the trigger fires
|
|
118
|
-
# right after the user picked an item).
|
|
102
|
+
# Cheap probe: try original first. Most editor sessions call completion in mid-keystroke where parse
|
|
103
|
+
# fails, but some land on a clean buffer (e.g. the trigger fires right after the user picked an item).
|
|
119
104
|
if Prism.parse(original_bytes).errors.empty?
|
|
120
105
|
[original_bytes, [line, character]]
|
|
121
106
|
else
|
|
@@ -136,23 +121,18 @@ module Rigor
|
|
|
136
121
|
return [original_bytes, [line, character]] if sentinel.nil?
|
|
137
122
|
|
|
138
123
|
lines[line] = "#{prefix}#{sentinel}#{suffix}"
|
|
139
|
-
# The cursor stays at the same column — Prism's AST now
|
|
140
|
-
#
|
|
141
|
-
# sentinel.size). NodeLocator at that column returns the
|
|
142
|
-
# enclosing call / path.
|
|
124
|
+
# The cursor stays at the same column — Prism's AST now has a node whose name occupies [character,
|
|
125
|
+
# character + sentinel.size). NodeLocator at that column returns the enclosing call / path.
|
|
143
126
|
[lines.join, [line, character]]
|
|
144
127
|
end
|
|
145
128
|
|
|
146
129
|
def sentinel_for_prefix(prefix)
|
|
147
|
-
# Strip trailing whitespace before checking for the
|
|
148
|
-
#
|
|
149
|
-
# space mid-edit.
|
|
130
|
+
# Strip trailing whitespace before checking for the trigger character; users often type the dot then
|
|
131
|
+
# a space mid-edit.
|
|
150
132
|
stripped = prefix.rstrip
|
|
151
|
-
# `[:` covers `hash[:|` symbol-key access — splice both
|
|
152
|
-
#
|
|
153
|
-
#
|
|
154
|
-
# of the sentinel string for this case (vs the bare
|
|
155
|
-
# identifier sentinels for `.` / `::`).
|
|
133
|
+
# `[:` covers `hash[:|` symbol-key access — splice both the key name AND the closing `]` so Prism gets
|
|
134
|
+
# a complete index expression. The closing bracket is part of the sentinel string for this case (vs
|
|
135
|
+
# the bare identifier sentinels for `.` / `::`).
|
|
156
136
|
return "#{SENTINEL_HASH_KEY}]" if stripped.end_with?("[:")
|
|
157
137
|
return SENTINEL_METHOD if stripped.end_with?(".")
|
|
158
138
|
return SENTINEL_CONSTANT if stripped.end_with?("::")
|
|
@@ -170,12 +150,10 @@ module Rigor
|
|
|
170
150
|
method_completions(receiver_type, receiver_scope)
|
|
171
151
|
end
|
|
172
152
|
|
|
173
|
-
# Slice D1 — `hash[:|` hash-key completion. Triggers when
|
|
174
|
-
#
|
|
175
|
-
# the
|
|
176
|
-
#
|
|
177
|
-
# through to method completion (which still surfaces Hash's
|
|
178
|
-
# methods for genuine method calls on a hash receiver).
|
|
153
|
+
# Slice D1 — `hash[:|` hash-key completion. Triggers when the cursor is on the synthetic key inside a
|
|
154
|
+
# `[]` call AND the receiver's inferred type is a `Type::HashShape`. Returns nil for any other receiver
|
|
155
|
+
# carrier so the dispatcher falls through to method completion (which still surfaces Hash's methods for
|
|
156
|
+
# genuine method calls on a hash receiver).
|
|
179
157
|
def hash_key_completion_for(call_node, root, path)
|
|
180
158
|
return nil unless call_node.name == :[]
|
|
181
159
|
|
|
@@ -189,10 +167,9 @@ module Rigor
|
|
|
189
167
|
hash_key_items(receiver_type)
|
|
190
168
|
end
|
|
191
169
|
|
|
192
|
-
# When NodeLocator returns the sentinel SymbolNode directly
|
|
193
|
-
#
|
|
194
|
-
#
|
|
195
|
-
# Re-walks the root once; cheap for LSP-sized buffers.
|
|
170
|
+
# When NodeLocator returns the sentinel SymbolNode directly (rather than the enclosing CallNode), walk
|
|
171
|
+
# up the AST for the smallest `[]` call containing the symbol's location. Re-walks the root once; cheap
|
|
172
|
+
# for LSP-sized buffers.
|
|
196
173
|
def hash_key_completion_for_symbol(symbol_node, root, path)
|
|
197
174
|
call_node = enclosing_index_call(root, symbol_node)
|
|
198
175
|
return nil if call_node.nil?
|
|
@@ -230,12 +207,10 @@ module Rigor
|
|
|
230
207
|
end
|
|
231
208
|
end
|
|
232
209
|
|
|
233
|
-
# Slice B2 — `Foo::|` constant-path completion. The cursor
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
#
|
|
237
|
-
# constants (`::Foo`) and parent-less paths are not yet
|
|
238
|
-
# supported (queued for slice 6 follow-up).
|
|
210
|
+
# Slice B2 — `Foo::|` constant-path completion. The cursor sits on a `ConstantPathNode` whose `parent`
|
|
211
|
+
# resolves to a class / module FQN; we enumerate every known class whose name is an immediate child of
|
|
212
|
+
# that parent. Top-level constants (`::Foo`) and parent-less paths are not yet supported (queued for
|
|
213
|
+
# slice 6 follow-up).
|
|
239
214
|
def constant_path_completion_for(const_path_node, path)
|
|
240
215
|
parent_fqn = parent_fqn_of(const_path_node)
|
|
241
216
|
return nil if parent_fqn.nil?
|
|
@@ -248,10 +223,8 @@ module Rigor
|
|
|
248
223
|
end
|
|
249
224
|
|
|
250
225
|
def parent_fqn_of(const_path_node)
|
|
251
|
-
# ConstantPathNode#parent is the LHS of the `::`. For
|
|
252
|
-
# `Foo::Bar` it's the
|
|
253
|
-
# `Foo::Bar::Baz` it's a ConstantPathNode. We render
|
|
254
|
-
# either to the dotted FQN string.
|
|
226
|
+
# ConstantPathNode#parent is the LHS of the `::`. For `Foo::Bar` it's the ConstantReadNode for `Foo`;
|
|
227
|
+
# for `Foo::Bar::Baz` it's a ConstantPathNode. We render either to the dotted FQN string.
|
|
255
228
|
parent = const_path_node.parent
|
|
256
229
|
return nil if parent.nil?
|
|
257
230
|
|
|
@@ -268,24 +241,18 @@ module Rigor
|
|
|
268
241
|
end
|
|
269
242
|
end
|
|
270
243
|
|
|
271
|
-
# Walks `RbsLoader#known_class_names_set` for entries whose
|
|
272
|
-
#
|
|
273
|
-
#
|
|
274
|
-
#
|
|
275
|
-
#
|
|
276
|
-
# type-system's internal API discipline; the LSP layer is a
|
|
277
|
-
# trusted internal consumer and `send` is the documented
|
|
278
|
-
# escape hatch (same pattern as `Type::Refined#canonical_name`
|
|
279
|
-
# in slice A4).
|
|
244
|
+
# Walks `RbsLoader#known_class_names_set` for entries whose FQN is `parent_fqn::<one segment>` — the
|
|
245
|
+
# immediate children. Deeper descendants are filtered out so the popup shows only the next-level
|
|
246
|
+
# constants the user can directly write. `known_class_names_set` is private on RbsLoader per the
|
|
247
|
+
# type-system's internal API discipline; the LSP layer is a trusted internal consumer and `send` is the
|
|
248
|
+
# documented escape hatch (same pattern as `Type::Refined#canonical_name` in slice A4).
|
|
280
249
|
def enumerate_constant_children(parent_fqn, scope)
|
|
281
250
|
loader = scope.environment.rbs_loader
|
|
282
251
|
return [] if loader.nil?
|
|
283
252
|
|
|
284
253
|
names = loader.send(:known_class_names_set)
|
|
285
|
-
# RBS canonical names carry a leading `::` (so the table
|
|
286
|
-
#
|
|
287
|
-
# forms so the prefix walk works regardless of which form
|
|
288
|
-
# the caller passes.
|
|
254
|
+
# RBS canonical names carry a leading `::` (so the table holds `::Process::Status` etc.). Match
|
|
255
|
+
# against both forms so the prefix walk works regardless of which form the caller passes.
|
|
289
256
|
prefix = "::#{parent_fqn}::"
|
|
290
257
|
names.filter_map do |fqn|
|
|
291
258
|
next nil unless fqn.start_with?(prefix)
|
|
@@ -324,21 +291,15 @@ module Rigor
|
|
|
324
291
|
Inference::ScopeIndexer.index(root, default_scope: scope)
|
|
325
292
|
end
|
|
326
293
|
|
|
327
|
-
# Returns an Array<Hash> of LSP `CompletionItem`s for every
|
|
328
|
-
#
|
|
329
|
-
# the
|
|
330
|
-
#
|
|
331
|
-
#
|
|
332
|
-
#
|
|
333
|
-
# - `Type::
|
|
334
|
-
#
|
|
335
|
-
#
|
|
336
|
-
# - `Type::Union` — intersection of methods on each member
|
|
337
|
-
# (only methods guaranteed to dispatch on every union case).
|
|
338
|
-
# Conservative default per design doc § "Union receiver
|
|
339
|
-
# completion".
|
|
340
|
-
# - `Type::Intersection` — union of methods on each member
|
|
341
|
-
# (anything callable on at least one member).
|
|
294
|
+
# Returns an Array<Hash> of LSP `CompletionItem`s for every public method callable on the receiver.
|
|
295
|
+
# Slice B3 extends the slice-B1 floor with:
|
|
296
|
+
# - `Type::Refined` / `Type::Difference` — enumerate the underlying nominal (refinement narrows the
|
|
297
|
+
# value set, not the method set).
|
|
298
|
+
# - `Type::Tuple` / `Type::HashShape` — enumerate the nominal ancestor (`Array` / `Hash`);
|
|
299
|
+
# element-type-aware completion is queued.
|
|
300
|
+
# - `Type::Union` — intersection of methods on each member (only methods guaranteed to dispatch on every
|
|
301
|
+
# union case). Conservative default per design doc § "Union receiver completion".
|
|
302
|
+
# - `Type::Intersection` — union of methods on each member (anything callable on at least one member).
|
|
342
303
|
def method_completions(receiver_type, scope)
|
|
343
304
|
method_set, kind = enumerate_method_set(receiver_type, scope)
|
|
344
305
|
return nil if method_set.nil? || method_set.empty?
|
|
@@ -350,13 +311,10 @@ module Rigor
|
|
|
350
311
|
end
|
|
351
312
|
end
|
|
352
313
|
|
|
353
|
-
# Returns `[{Symbol => RBS::Definition::Method}, :instance | :singleton]`
|
|
354
|
-
#
|
|
355
|
-
#
|
|
356
|
-
#
|
|
357
|
-
# CompletionItem's `detail` still comes from each method's
|
|
358
|
-
# own `defs.first.implemented_in`, so the rendered prefix
|
|
359
|
-
# stays accurate per-method.
|
|
314
|
+
# Returns `[{Symbol => RBS::Definition::Method}, :instance | :singleton]` for the receiver. Composite
|
|
315
|
+
# carriers (Union / Intersection / Refined / shape carriers) reduce to instance-method enumeration; the
|
|
316
|
+
# receiver-class label that lands in each CompletionItem's `detail` still comes from each method's own
|
|
317
|
+
# `defs.first.implemented_in`, so the rendered prefix stays accurate per-method.
|
|
360
318
|
def enumerate_method_set(receiver_type, scope)
|
|
361
319
|
case receiver_type
|
|
362
320
|
when Type::Singleton
|
|
@@ -379,11 +337,9 @@ module Rigor
|
|
|
379
337
|
end
|
|
380
338
|
end
|
|
381
339
|
|
|
382
|
-
# Union receiver — keep only methods present in EVERY
|
|
383
|
-
# member
|
|
384
|
-
#
|
|
385
|
-
# `obj.upcase` from appearing on a `String | Integer`
|
|
386
|
-
# union where only one side answers `upcase`.
|
|
340
|
+
# Union receiver — keep only methods present in EVERY member's set. Conservative semantically (every
|
|
341
|
+
# method returned is callable on every member) and prevents `obj.upcase` from appearing on a
|
|
342
|
+
# `String | Integer` union where only one side answers `upcase`.
|
|
387
343
|
def intersect_member_methods(members, scope)
|
|
388
344
|
member_sets = members.filter_map { |m| enumerate_method_set(m, scope).first }
|
|
389
345
|
return nil if member_sets.empty?
|
|
@@ -392,9 +348,8 @@ module Rigor
|
|
|
392
348
|
member_sets.first.slice(*common_names)
|
|
393
349
|
end
|
|
394
350
|
|
|
395
|
-
# Intersection receiver — accumulate every method declared on
|
|
396
|
-
#
|
|
397
|
-
# interfaces; the completion popup MAY show either's methods.
|
|
351
|
+
# Intersection receiver — accumulate every method declared on ANY member. A value of type `A & B` is
|
|
352
|
+
# callable through both interfaces; the completion popup MAY show either's methods.
|
|
398
353
|
def union_member_methods(members, scope)
|
|
399
354
|
members.filter_map { |m| enumerate_method_set(m, scope).first }.reduce({}, :merge)
|
|
400
355
|
end
|
|
@@ -420,10 +375,8 @@ module Rigor
|
|
|
420
375
|
detail: detail,
|
|
421
376
|
insertText: label,
|
|
422
377
|
filterText: label,
|
|
423
|
-
# Inherited methods rank below own-class methods; the
|
|
424
|
-
#
|
|
425
|
-
# class. Inheritance-distance ranking is queued (design
|
|
426
|
-
# doc § "sortText").
|
|
378
|
+
# Inherited methods rank below own-class methods; the `defs.first.implemented_in` carries the
|
|
379
|
+
# declaring class. Inheritance-distance ranking is queued (design doc § "sortText").
|
|
427
380
|
sortText: "1_#{label}"
|
|
428
381
|
}
|
|
429
382
|
end
|
|
@@ -2,23 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module LanguageServer
|
|
5
|
-
# Per-key debouncer. The LSP uses this to defer
|
|
6
|
-
# `
|
|
7
|
-
#
|
|
8
|
-
# `schedule(uri, delay:)` cancels the previous task for the
|
|
9
|
-
# same `uri` and queues a new one — only the LAST task in a
|
|
10
|
-
# burst actually runs.
|
|
5
|
+
# Per-key debouncer. The LSP uses this to defer `publishDiagnostics` until the user stops typing (200ms
|
|
6
|
+
# quiet-time floor per LSP UX conventions). Each `schedule(uri, delay:)` cancels the previous task for the
|
|
7
|
+
# same `uri` and queues a new one — only the LAST task in a burst actually runs.
|
|
11
8
|
#
|
|
12
|
-
# Threading model: each scheduled task runs in its own Thread
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# loop are serialised by `SynchronizedWriter`.
|
|
9
|
+
# Threading model: each scheduled task runs in its own Thread so the dispatcher loop doesn't block.
|
|
10
|
+
# Concurrent writes to STDOUT from the Debouncer's threads + the main dispatch loop are serialised by
|
|
11
|
+
# `SynchronizedWriter`.
|
|
16
12
|
#
|
|
17
|
-
# Cancellation is cooperative: each task carries a
|
|
18
|
-
#
|
|
19
|
-
# and
|
|
20
|
-
# safer than `Thread#kill` for in-flight Ruby code and good
|
|
21
|
-
# enough for the "drop stale debounce" use case.
|
|
13
|
+
# Cancellation is cooperative: each task carries a `cancelled` flag; new schedules flip the prior task's
|
|
14
|
+
# flag and the prior thread skips the block on wake-up. This is safer than `Thread#kill` for in-flight
|
|
15
|
+
# Ruby code and good enough for the "drop stale debounce" use case.
|
|
22
16
|
class Debouncer
|
|
23
17
|
Task = Struct.new(:thread, :cancelled)
|
|
24
18
|
|
|
@@ -27,10 +21,9 @@ module Rigor
|
|
|
27
21
|
@mutex = Mutex.new
|
|
28
22
|
end
|
|
29
23
|
|
|
30
|
-
# Schedule `block` to run after `delay` seconds, replacing
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
# pair this with `#flush!` for deterministic assertions.
|
|
24
|
+
# Schedule `block` to run after `delay` seconds, replacing any pending task for the same `key`.
|
|
25
|
+
# `delay: 0` makes the task fire immediately (still on its own thread); tests pair this with `#flush!`
|
|
26
|
+
# for deterministic assertions.
|
|
34
27
|
def schedule(key, delay:, &block)
|
|
35
28
|
task = Task.new(nil, false)
|
|
36
29
|
|
|
@@ -55,9 +48,8 @@ module Rigor
|
|
|
55
48
|
nil
|
|
56
49
|
end
|
|
57
50
|
|
|
58
|
-
# Wait for every pending task to complete. Used by specs to
|
|
59
|
-
#
|
|
60
|
-
# `shutdown` path uses `#cancel_all` instead.
|
|
51
|
+
# Wait for every pending task to complete. Used by specs to synchronise with the async schedule; the
|
|
52
|
+
# production `shutdown` path uses `#cancel_all` instead.
|
|
61
53
|
def flush!
|
|
62
54
|
threads = @mutex.synchronize { @tasks.values.map(&:thread) }
|
|
63
55
|
threads.each do |thread|
|
|
@@ -67,9 +59,8 @@ module Rigor
|
|
|
67
59
|
end
|
|
68
60
|
end
|
|
69
61
|
|
|
70
|
-
# Cancel every pending task (sets the flag; the threads
|
|
71
|
-
#
|
|
72
|
-
# in-flight publishes don't write to a closed STDOUT.
|
|
62
|
+
# Cancel every pending task (sets the flag; the threads exit without running the block). Called on
|
|
63
|
+
# `shutdown` so in-flight publishes don't write to a closed STDOUT.
|
|
73
64
|
def cancel_all
|
|
74
65
|
@mutex.synchronize do
|
|
75
66
|
@tasks.each_value { |t| t.cancelled = true }
|
|
@@ -8,17 +8,14 @@ require_relative "../analysis/buffer_binding"
|
|
|
8
8
|
|
|
9
9
|
module Rigor
|
|
10
10
|
module LanguageServer
|
|
11
|
-
# Converts buffer state into `textDocument/publishDiagnostics`
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# a `BufferBinding` from the BufferTable entry, runs the Runner,
|
|
16
|
-
# and pushes the resulting LSP `Diagnostic[]` through the writer.
|
|
11
|
+
# Converts buffer state into `textDocument/publishDiagnostics` notifications. Owns the Rigor
|
|
12
|
+
# `Analysis::Runner` orchestration for the per-buffer single-file scope path that editor mode v1 already
|
|
13
|
+
# supports — every `publish_for(uri)` call materialises a `BufferBinding` from the BufferTable entry, runs
|
|
14
|
+
# the Runner, and pushes the resulting LSP `Diagnostic[]` through the writer.
|
|
17
15
|
#
|
|
18
|
-
# Debouncing is wired via an optional `Debouncer` injected at
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# Ractor-pool dispatch is queued.
|
|
16
|
+
# Debouncing is wired via an optional `Debouncer` injected at construction (delay defaults to 200ms
|
|
17
|
+
# quiet-time); without a debouncer each call blocks synchronously (primarily for specs). Ractor-pool
|
|
18
|
+
# dispatch is queued.
|
|
22
19
|
class DiagnosticPublisher
|
|
23
20
|
# Maps Rigor severity symbols to LSP DiagnosticSeverity
|
|
24
21
|
# integers per spec § "Diagnostic":
|
|
@@ -49,12 +46,10 @@ module Rigor
|
|
|
49
46
|
@debounce_seconds = debounce_seconds
|
|
50
47
|
end
|
|
51
48
|
|
|
52
|
-
# Run analysis for the buffer at `uri` (looked up in the
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
# the analysis is scheduled async per the configured
|
|
57
|
-
# `debounce_seconds`; otherwise it runs inline.
|
|
49
|
+
# Run analysis for the buffer at `uri` (looked up in the BufferTable) and push a
|
|
50
|
+
# `textDocument/publishDiagnostics` notification. No-op when the URI isn't a `file://` form or the
|
|
51
|
+
# buffer isn't currently open. When a Debouncer is wired, the analysis is scheduled async per the
|
|
52
|
+
# configured `debounce_seconds`; otherwise it runs inline.
|
|
58
53
|
def publish_for(uri)
|
|
59
54
|
path = Uri.to_path(uri)
|
|
60
55
|
return if path.nil?
|
|
@@ -66,17 +61,14 @@ module Rigor
|
|
|
66
61
|
end
|
|
67
62
|
end
|
|
68
63
|
|
|
69
|
-
# Publishes an EMPTY diagnostic array for `uri`. The LSP-spec
|
|
70
|
-
#
|
|
71
|
-
# so clients drop stale highlights when the user closes a
|
|
72
|
-
# buffer.
|
|
64
|
+
# Publishes an EMPTY diagnostic array for `uri`. The LSP-spec idiom for "clear inline markers" — called
|
|
65
|
+
# from `didClose` so clients drop stale highlights when the user closes a buffer.
|
|
73
66
|
def publish_empty(uri)
|
|
74
67
|
notify(uri, [])
|
|
75
68
|
end
|
|
76
69
|
|
|
77
|
-
# Cancels every in-flight debounced task. Called from
|
|
78
|
-
#
|
|
79
|
-
# against a closed STDOUT.
|
|
70
|
+
# Cancels every in-flight debounced task. Called from `Server#handle_shutdown` so pending publishes
|
|
71
|
+
# don't fire against a closed STDOUT.
|
|
80
72
|
def cancel_pending
|
|
81
73
|
@debouncer&.cancel_all
|
|
82
74
|
end
|
|
@@ -85,25 +77,20 @@ module Rigor
|
|
|
85
77
|
|
|
86
78
|
def run_and_notify(uri, path)
|
|
87
79
|
entry = @buffer_table[uri]
|
|
88
|
-
# The buffer may have been closed during the debounce
|
|
89
|
-
#
|
|
90
|
-
# didClose already cleared the markers.
|
|
80
|
+
# The buffer may have been closed during the debounce window — drop the publish; the empty
|
|
81
|
+
# notification from didClose already cleared the markers.
|
|
91
82
|
return if entry.nil?
|
|
92
83
|
|
|
93
84
|
diagnostics = run_analysis(path: path, bytes: entry.bytes)
|
|
94
85
|
notify(uri, diagnostics)
|
|
95
86
|
end
|
|
96
87
|
|
|
97
|
-
# Runs `Analysis::Runner` with a `BufferBinding` so the buffer
|
|
98
|
-
#
|
|
99
|
-
# `
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
# snapshot rebuilds only when `ProjectContext#invalidate!`
|
|
104
|
-
# fires (watched-file or configuration change). Returns
|
|
105
|
-
# the LSP-shaped Diagnostic Array, ready to serialize into
|
|
106
|
-
# the notification's `params.diagnostics` field.
|
|
88
|
+
# Runs `Analysis::Runner` with a `BufferBinding` so the buffer bytes (instead of the on-disk file) drive
|
|
89
|
+
# the parse. The `Rigor::Analysis::ProjectScan` cached on the ProjectContext is passed through
|
|
90
|
+
# `prebuilt:` so plugin `#prepare`, the dependency-source walker, and the synthetic-method /
|
|
91
|
+
# project-patched scanners do not re-run per publish. The snapshot rebuilds only when
|
|
92
|
+
# `ProjectContext#invalidate!` fires (watched-file or configuration change). Returns the LSP-shaped
|
|
93
|
+
# Diagnostic Array, ready to serialize into the notification's `params.diagnostics` field.
|
|
107
94
|
def run_analysis(path:, bytes:)
|
|
108
95
|
with_tempfile(bytes) do |tmp|
|
|
109
96
|
binding = Analysis::BufferBinding.new(logical_path: path, physical_path: tmp.path)
|
|
@@ -137,10 +124,9 @@ module Rigor
|
|
|
137
124
|
def to_lsp_diagnostic(diagnostic, buffer_path)
|
|
138
125
|
return nil if diagnostic.path != buffer_path
|
|
139
126
|
|
|
140
|
-
# Rigor uses 1-based line + 1-based byte column; LSP uses
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
# columns which are correct for ASCII source.
|
|
127
|
+
# Rigor uses 1-based line + 1-based byte column; LSP uses 0-based line + 0-based UTF-16 code unit.
|
|
128
|
+
# UTF-16 conversion is queued (design doc § "Open questions"); v1 emits byte columns which are
|
|
129
|
+
# correct for ASCII source.
|
|
144
130
|
line = (diagnostic.line - 1).clamp(0, Float::INFINITY).to_i
|
|
145
131
|
character = (diagnostic.column - 1).clamp(0, Float::INFINITY).to_i
|
|
146
132
|
|
|
@@ -7,11 +7,9 @@ require_relative "buffer_resolution"
|
|
|
7
7
|
|
|
8
8
|
module Rigor
|
|
9
9
|
module LanguageServer
|
|
10
|
-
# Answers `textDocument/documentSymbol` requests by walking the
|
|
11
|
-
#
|
|
12
|
-
# `
|
|
13
|
-
# modules / methods nest in the `children` array so the editor's
|
|
14
|
-
# outline tree mirrors the source structure.
|
|
10
|
+
# Answers `textDocument/documentSymbol` requests by walking the Prism AST and emitting one LSP
|
|
11
|
+
# `DocumentSymbol` per `ClassNode` / `ModuleNode` / `DefNode`. Nested classes / modules / methods nest in
|
|
12
|
+
# the `children` array so the editor's outline tree mirrors the source structure.
|
|
15
13
|
#
|
|
16
14
|
# SymbolKind mapping (LSP § "SymbolKind"):
|
|
17
15
|
# - Class (5) — `class Foo`
|
|
@@ -41,8 +39,8 @@ module Rigor
|
|
|
41
39
|
|
|
42
40
|
parse_result = Prism.parse(entry.bytes, filepath: path,
|
|
43
41
|
version: @project_context.configuration.target_ruby)
|
|
44
|
-
# Tolerate partial parse errors: walk what Prism gave us
|
|
45
|
-
#
|
|
42
|
+
# Tolerate partial parse errors: walk what Prism gave us anyway. Editors prefer a stale outline over
|
|
43
|
+
# no outline.
|
|
46
44
|
walk_top_level(parse_result.value)
|
|
47
45
|
end
|
|
48
46
|
|
|
@@ -97,10 +95,9 @@ module Rigor
|
|
|
97
95
|
|
|
98
96
|
def def_symbol(node, in_namespace:)
|
|
99
97
|
name = node.name.to_s
|
|
100
|
-
# `def self.foo` (singleton-class) → singleton-method shape.
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
# prefix preserves the distinction visually.
|
|
98
|
+
# `def self.foo` (singleton-class) → singleton-method shape. We surface the same as instance methods
|
|
99
|
+
# in v1 (LSP kind has no distinct "ClassMethod" code); the textual `self.` prefix preserves the
|
|
100
|
+
# distinction visually.
|
|
104
101
|
display_name = node.receiver.is_a?(Prism::SelfNode) ? "self.#{name}" : name
|
|
105
102
|
{
|
|
106
103
|
name: display_name,
|
|
@@ -111,10 +108,9 @@ module Rigor
|
|
|
111
108
|
}
|
|
112
109
|
end
|
|
113
110
|
|
|
114
|
-
# Renders a `ConstantReadNode` / `ConstantPathNode` as its
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
# the outline still has something to display.
|
|
111
|
+
# Renders a `ConstantReadNode` / `ConstantPathNode` as its fully-qualified name string (e.g.
|
|
112
|
+
# `Foo::Bar::Baz`). Returns the slot-name source when the node shape is unrecognised so the outline
|
|
113
|
+
# still has something to display.
|
|
118
114
|
def qualified_name_of(node)
|
|
119
115
|
case node
|
|
120
116
|
when Prism::ConstantReadNode
|
|
@@ -127,10 +123,9 @@ module Rigor
|
|
|
127
123
|
end
|
|
128
124
|
end
|
|
129
125
|
|
|
130
|
-
# LSP `Range` is 0-based start + end with `character` in
|
|
131
|
-
# UTF-16
|
|
132
|
-
#
|
|
133
|
-
# per design doc § "Open questions".
|
|
126
|
+
# LSP `Range` is 0-based start + end with `character` in UTF-16 code units. This implementation emits
|
|
127
|
+
# byte columns (correct for ASCII source); UTF-16 conversion stays queued per design doc § "Open
|
|
128
|
+
# questions".
|
|
134
129
|
def range_from(location)
|
|
135
130
|
{
|
|
136
131
|
start: { line: location.start_line - 1, character: location.start_column },
|