diff --git a/jekyll/editors.markdown b/jekyll/editors.markdown index c3f27cfa2..2e000080d 100644 --- a/jekyll/editors.markdown +++ b/jekyll/editors.markdown @@ -78,7 +78,8 @@ configuration languages (JSON, Lua, ELisp, etc.). "inlayHint": { "implicitHashValue": true, "implicitRescue": true - } + }, + "typeCheckerIntegration": "defer" }, "indexing": { "excludedPatterns": ["path/to/excluded/file.rb"], diff --git a/lib/ruby_lsp/global_state.rb b/lib/ruby_lsp/global_state.rb index 023d642b2..ce5d5545b 100644 --- a/lib/ruby_lsp/global_state.rb +++ b/lib/ruby_lsp/global_state.rb @@ -42,6 +42,7 @@ def initialize @linters = [] #: Array[String] @test_library = "minitest" #: String @has_type_checker = true #: bool + @type_checker_integration = ENV["RUBY_LSP_BYPASS_TYPECHECKER"] ? :bypass : :defer #: Symbol @index = RubyIndexer::Index.new #: RubyIndexer::Index @supported_formatters = {} #: Hash[String, Requests::Support::Formatter] @type_inferrer = TypeInferrer.new(@index) #: TypeInferrer @@ -144,6 +145,21 @@ def apply_options(options) @test_library = detect_test_library(direct_dependencies) notifications << Notification.window_log_message("Detected test library: #{@test_library}") + case (integration_setting = options.dig(:initializationOptions, :featuresConfiguration, :typeCheckerIntegration)) + when "defer" + @type_checker_integration = :defer + when "bypass" + @type_checker_integration = :bypass + when nil + # no op + else + notifications << Notification.window_log_message( + "Invalid typeCheckerIntegration value '#{integration_setting}', must be " \ + "'defer' or 'bypass'. Using default: #{integration_setting}", + type: Constant::MessageType::WARNING, + ) + end + @has_type_checker = detect_typechecker(all_dependencies) if @has_type_checker notifications << Notification.window_log_message( @@ -256,9 +272,7 @@ def detect_test_library(dependencies) #: (Array[String] dependencies) -> bool def detect_typechecker(dependencies) - return false if ENV["RUBY_LSP_BYPASS_TYPECHECKER"] - - dependencies.any?(/^sorbet-static/) + @type_checker_integration == :defer && dependencies.any?(/^sorbet-static/) rescue Bundler::GemfileNotFound false end diff --git a/test/global_state_test.rb b/test/global_state_test.rb index 61214caa5..cbaaeeac0 100644 --- a/test/global_state_test.rb +++ b/test/global_state_test.rb @@ -199,6 +199,68 @@ def test_type_checker_is_detected_based_on_transitive_sorbet_static assert_predicate(state, :has_type_checker) end + def test_type_checker_integration_defaults_to_defer + state = GlobalState.new + + Bundler.locked_gems.stubs(dependencies: {}) + stub_all_dependencies("sorbet-static") + state.apply_options( + { + initializationOptions: { featuresConfiguration: { typeCheckerIntegration: "defer" } }, + }, + ) + + assert_predicate(state, :has_type_checker) + end + + def test_type_checker_integration_can_be_set_to_bypass + state = GlobalState.new + + Bundler.locked_gems.stubs(dependencies: {}) + stub_all_dependencies("sorbet-static") + state.apply_options( + { + initializationOptions: { featuresConfiguration: { typeCheckerIntegration: "bypass" } }, + }, + ) + + refute_predicate(state, :has_type_checker) + end + + def test_type_checker_integration_handles_misconfiguration_gracefully + state = GlobalState.new + + Bundler.locked_gems.stubs(dependencies: {}) + stub_all_dependencies("sorbet-static") + notifications = state.apply_options( + { + initializationOptions: { featuresConfiguration: { typeCheckerIntegration: "invalid" } }, + }, + ) + + log = notifications.find do |n| + params = n.params #: as untyped + n.method == "window/logMessage" && params.message.include?("typeCheckerIntegration") + end + + refute_nil(log) + assert_predicate(state, :has_type_checker) + end + + def test_environment_variable_sets_type_checker_integration + original_env_var = ENV["RUBY_LSP_BYPASS_TYPECHECKER"] + ENV["RUBY_LSP_BYPASS_TYPECHECKER"] = "1" + state = GlobalState.new + + Bundler.locked_gems.stubs(dependencies: {}) + stub_all_dependencies("sorbet-static") + state.apply_options({ initializationOptions: {} }) + + refute_predicate(state, :has_type_checker) + ensure + ENV["RUBY_LSP_BYPASS_TYPECHECKER"] = original_env_var + end + def test_addon_settings_are_stored global_state = GlobalState.new