diff --git a/lib/ruby_lsp/executor.rb b/lib/ruby_lsp/executor.rb index a94073f65..a0ad0521a 100644 --- a/lib/ruby_lsp/executor.rb +++ b/lib/ruby_lsp/executor.rb @@ -606,8 +606,8 @@ def initialize_request(options) configured_hints = options.dig(:initializationOptions, :featuresConfiguration, :inlayHint) configured_lenses = options.dig(:initializationOptions, :featuresConfiguration, :codeLens) - T.must(@store.features_configuration.dig(:inlayHint)).merge!(configured_hints) if configured_hints - T.must(@store.features_configuration.dig(:codeLens)).merge!(configured_lenses) if configured_lenses + T.must(@store.features_configuration.dig(:inlayHint)).configuration.merge!(configured_hints) if configured_hints + T.must(@store.features_configuration.dig(:codeLens)).configuration.merge!(configured_lenses) if configured_lenses enabled_features = case configured_features when Array diff --git a/lib/ruby_lsp/requests/code_lens.rb b/lib/ruby_lsp/requests/code_lens.rb index d7f5e0d9b..cd2aaa87b 100644 --- a/lib/ruby_lsp/requests/code_lens.rb +++ b/lib/ruby_lsp/requests/code_lens.rb @@ -11,6 +11,10 @@ module Requests # [code lens](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens) # request informs the editor of runnable commands such as tests # + # # Configuration + # + # To disable gem code lenses, set `rubyLsp.featuresConfiguration.codeLens.gemfileLinks` to `false`. + # # # Example # # ```ruby @@ -50,7 +54,7 @@ class CodeLens < ExtensibleListener sig do params( uri: URI::Generic, - lenses_configuration: T::Hash[Symbol, T::Boolean], + lenses_configuration: RequestConfig, dispatcher: Prism::Dispatcher, ).void end @@ -141,7 +145,7 @@ def on_call_node_enter(node) end if @path&.include?(GEMFILE_NAME) && name == :gem && arguments - return unless @lenses_configuration.dig(:gemfileLinks) + return unless @lenses_configuration.enabled?(:gemfileLinks) first_argument = arguments.arguments.first return unless first_argument.is_a?(Prism::StringNode) diff --git a/lib/ruby_lsp/requests/inlay_hints.rb b/lib/ruby_lsp/requests/inlay_hints.rb index 8feccf720..9bca78a71 100644 --- a/lib/ruby_lsp/requests/inlay_hints.rb +++ b/lib/ruby_lsp/requests/inlay_hints.rb @@ -9,6 +9,14 @@ module Requests # are labels added directly in the code that explicitly show the user something that might # otherwise just be implied. # + # # Configuration + # + # To enable rescue hints, set `rubyLsp.featuresConfiguration.inlayHint.implicitRescue` to `true`. + # + # To enable hash value hints, set `rubyLsp.featuresConfiguration.inlayHint.implicitHashValue` to `true`. + # + # To enable all hints, set `rubyLsp.featuresConfiguration.inlayHint.enableAll` to `true`. + # # # Example # # ```ruby @@ -42,7 +50,7 @@ class InlayHints < Listener sig do params( range: T::Range[Integer], - hints_configuration: T::Hash[Symbol, T::Boolean], + hints_configuration: RequestConfig, dispatcher: Prism::Dispatcher, ).void end @@ -58,7 +66,7 @@ def initialize(range, hints_configuration, dispatcher) sig { params(node: Prism::RescueNode).void } def on_rescue_node_enter(node) - return unless @hints_configuration.dig(:implicitRescue) + return unless @hints_configuration.enabled?(:implicitRescue) return unless node.exceptions.empty? loc = node.location @@ -74,7 +82,7 @@ def on_rescue_node_enter(node) sig { params(node: Prism::ImplicitNode).void } def on_implicit_node_enter(node) - return unless @hints_configuration.dig(:implicitHashValue) + return unless @hints_configuration.enabled?(:implicitHashValue) return unless visible?(node, @range) node_value = node.value diff --git a/lib/ruby_lsp/store.rb b/lib/ruby_lsp/store.rb index 9aaca8847..e4fcc01cd 100644 --- a/lib/ruby_lsp/store.rb +++ b/lib/ruby_lsp/store.rb @@ -20,7 +20,7 @@ class Store sig { returns(URI::Generic) } attr_accessor :workspace_uri - sig { returns(T::Hash[Symbol, T::Hash[Symbol, T::Boolean]]) } + sig { returns(T::Hash[Symbol, RequestConfig]) } attr_accessor :features_configuration sig { void } @@ -33,15 +33,17 @@ def initialize @workspace_uri = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic) @features_configuration = T.let( { - codeLens: { + codeLens: RequestConfig.new({ + enableAll: false, gemfileLinks: true, - }, - inlayHint: { - implicitRescue: true, - implicitHashValue: true, - }, + }), + inlayHint: RequestConfig.new({ + enableAll: false, + implicitRescue: false, + implicitHashValue: false, + }), }, - T::Hash[Symbol, T::Hash[Symbol, T::Boolean]], + T::Hash[Symbol, RequestConfig], ) end diff --git a/lib/ruby_lsp/utils.rb b/lib/ruby_lsp/utils.rb index 7f5de4518..28b802a67 100644 --- a/lib/ruby_lsp/utils.rb +++ b/lib/ruby_lsp/utils.rb @@ -74,4 +74,22 @@ def cancel @cancelled = true end end + + # A request configuration, to turn on/off features + class RequestConfig + extend T::Sig + + sig { returns(T::Hash[Symbol, T::Boolean]) } + attr_accessor :configuration + + sig { params(configuration: T::Hash[Symbol, T::Boolean]).void } + def initialize(configuration) + @configuration = configuration + end + + sig { params(feature: Symbol).returns(T.nilable(T::Boolean)) } + def enabled?(feature) + @configuration[:enableAll] || @configuration[feature] + end + end end diff --git a/test/executor_test.rb b/test/executor_test.rb index adebcc3d4..7e132390f 100644 --- a/test/executor_test.rb +++ b/test/executor_test.rb @@ -225,9 +225,9 @@ def test_initialize_features_with_default_configuration RubyLsp::Executor.new(@store, @message_queue) .execute(method: "initialize", params: { initializationOptions: {} }) - assert(@store.features_configuration.dig(:codeLens, :gemfileLinks)) - assert(@store.features_configuration.dig(:inlayHint, :implicitRescue)) - assert(@store.features_configuration.dig(:inlayHint, :implicitHashValue)) + assert(@store.features_configuration.dig(:codeLens).enabled?(:gemfileLinks)) + refute(@store.features_configuration.dig(:inlayHint).enabled?(:implicitRescue)) + refute(@store.features_configuration.dig(:inlayHint).enabled?(:implicitHashValue)) end def test_initialize_features_with_provided_configuration @@ -239,16 +239,16 @@ def test_initialize_features_with_provided_configuration gemfileLinks: false, }, inlayHint: { - implicitRescue: false, - implicitHashValue: false, + implicitRescue: true, + implicitHashValue: true, }, }, }, }) - refute(@store.features_configuration.dig(:codeLens, :gemfileLinks)) - refute(@store.features_configuration.dig(:inlayHint, :implicitRescue)) - refute(@store.features_configuration.dig(:inlayHint, :implicitHashValue)) + refute(@store.features_configuration.dig(:codeLens).enabled?(:gemfileLinks)) + assert(@store.features_configuration.dig(:inlayHint).enabled?(:implicitRescue)) + assert(@store.features_configuration.dig(:inlayHint).enabled?(:implicitHashValue)) end def test_initialize_features_with_partially_provided_configuration @@ -260,15 +260,35 @@ def test_initialize_features_with_partially_provided_configuration gemfileLinks: false, }, inlayHint: { - implicitHashValue: false, + implicitHashValue: true, }, }, }, }) - refute(@store.features_configuration.dig(:codeLens, :gemfileLinks)) - assert(@store.features_configuration.dig(:inlayHint, :implicitRescue)) - refute(@store.features_configuration.dig(:inlayHint, :implicitHashValue)) + refute(@store.features_configuration.dig(:codeLens).enabled?(:gemfileLinks)) + refute(@store.features_configuration.dig(:inlayHint).enabled?(:implicitRescue)) + assert(@store.features_configuration.dig(:inlayHint).enabled?(:implicitHashValue)) + end + + def test_initialize_features_with_enable_all_configuration + RubyLsp::Executor.new(@store, @message_queue) + .execute(method: "initialize", params: { + initializationOptions: { + featuresConfiguration: { + codeLens: { + enableAll: true, + }, + inlayHint: { + enableAll: true, + }, + }, + }, + }) + + assert(@store.features_configuration.dig(:codeLens).enabled?(:gemfileLinks)) + assert(@store.features_configuration.dig(:inlayHint).enabled?(:implicitRescue)) + assert(@store.features_configuration.dig(:inlayHint).enabled?(:implicitHashValue)) end def test_detects_rubocop_if_direct_dependency diff --git a/test/requests/code_lens_expectations_test.rb b/test/requests/code_lens_expectations_test.rb index c1dc211e1..d37dbabc7 100644 --- a/test/requests/code_lens_expectations_test.rb +++ b/test/requests/code_lens_expectations_test.rb @@ -109,7 +109,7 @@ def test_skip_gemfile_links RUBY dispatcher = Prism::Dispatcher.new - lenses_configuration = { gemfileLinks: false } + lenses_configuration = RubyLsp::RequestConfig.new({ gemfileLinks: false }) listener = RubyLsp::Requests::CodeLens.new(uri, lenses_configuration, dispatcher) dispatcher.dispatch(document.tree) response = listener.response @@ -138,7 +138,7 @@ class Test < Minitest::Test; end private def default_lenses_configuration - { gemfileLinks: true } + RubyLsp::RequestConfig.new({ gemfileLinks: true }) end def create_code_lens_addon diff --git a/test/requests/inlay_hints_expectations_test.rb b/test/requests/inlay_hints_expectations_test.rb index f0acf0970..a122a1c7f 100644 --- a/test/requests/inlay_hints_expectations_test.rb +++ b/test/requests/inlay_hints_expectations_test.rb @@ -13,7 +13,7 @@ def run_expectations(source) document = RubyLsp::RubyDocument.new(source: source, version: 1, uri: uri) dispatcher = Prism::Dispatcher.new - hints_configuration = { implicitRescue: true, implicitHashValue: true } + hints_configuration = RubyLsp::RequestConfig.new({ implicitRescue: true, implicitHashValue: true }) listener = RubyLsp::Requests::InlayHints.new(params.first, hints_configuration, dispatcher) dispatcher.dispatch(document.tree) listener.response @@ -30,7 +30,7 @@ def test_skip_implicit_hash_value RUBY dispatcher = Prism::Dispatcher.new - hints_configuration = { implicitRescue: true, implicitHashValue: false } + hints_configuration = RubyLsp::RequestConfig.new({ implicitRescue: true, implicitHashValue: false }) listener = RubyLsp::Requests::InlayHints.new(default_args.first, hints_configuration, dispatcher) dispatcher.dispatch(document.tree) assert_empty(listener.response) @@ -45,7 +45,7 @@ def test_skip_implicit_rescue RUBY dispatcher = Prism::Dispatcher.new - hints_configuration = { implicitRescue: false, implicitHashValue: true } + hints_configuration = RubyLsp::RequestConfig.new({ implicitRescue: false, implicitHashValue: true }) listener = RubyLsp::Requests::InlayHints.new(default_args.first, hints_configuration, dispatcher) dispatcher.dispatch(document.tree) assert_empty(listener.response)