diff --git a/lib/ruby_lsp/listener.rb b/lib/ruby_lsp/listener.rb index ccf713d439..71cb639a96 100644 --- a/lib/ruby_lsp/listener.rb +++ b/lib/ruby_lsp/listener.rb @@ -18,7 +18,6 @@ class Listener def initialize(emitter, message_queue) @emitter = emitter @message_queue = message_queue - @external_listeners = T.let([], T::Array[RubyLsp::Listener[ResponseType]]) end # Override this method with an attr_reader that returns the response of your listener. The listener should @@ -26,18 +25,44 @@ def initialize(emitter, message_queue) sig { abstract.returns(ResponseType) } def response; end - # Merge responses from all external listeners into the base listener's response. We do this to return a single - # response to the editor including the results of all extensions - sig { void } - def merge_external_listeners_responses! - @external_listeners.each { |l| merge_response!(l) } - end + module Extensible + extend T::Sig + extend T::Generic + + ResponseType = type_member + + abstract! + + requires_ancestor { Listener } + + sig { params(emitter: EventEmitter, message_queue: Thread::Queue).void } + def initialize(emitter, message_queue) + super + @external_listeners = T.let( + Extension.extensions.filter_map do |ext| + initialize_external_listener(ext) + end, + T::Array[RubyLsp::Listener[ResponseType]], + ) + end + + # Merge responses from all external listeners into the base listener's response. We do this to return a single + # response to the editor including the results of all extensions + sig { void } + def merge_external_listeners_responses! + @external_listeners.each { |l| merge_response!(l) } + end + + sig do + abstract.params(extension: RubyLsp::Extension).returns(T.nilable(RubyLsp::Listener[ResponseType])) + end + def initialize_external_listener(extension); end - # Does nothing by default. Requests that accept extensions should override this method to define how to merge - # responses coming from external listeners - sig { overridable.params(other: Listener[T.untyped]).returns(T.self_type) } - def merge_response!(other) - self + # Does nothing by default. Requests that accept extensions should override this method to define how to merge + # responses coming from external listeners + sig { abstract.params(other: Listener[T.untyped]).returns(T.self_type) } + def merge_response!(other) + end end end end diff --git a/lib/ruby_lsp/requests/code_lens.rb b/lib/ruby_lsp/requests/code_lens.rb index 5dc87cae0e..1972ea28da 100644 --- a/lib/ruby_lsp/requests/code_lens.rb +++ b/lib/ruby_lsp/requests/code_lens.rb @@ -22,6 +22,8 @@ class CodeLens < Listener extend T::Sig extend T::Generic + include Extensible + ResponseType = type_member { { fixed: T::Array[Interface::CodeLens] } } BASE_COMMAND = T.let((File.exist?("Gemfile.lock") ? "bundle exec ruby" : "ruby") + " -Itest ", String) @@ -33,12 +35,7 @@ class CodeLens < Listener sig { params(uri: URI::Generic, emitter: EventEmitter, message_queue: Thread::Queue, test_library: String).void } def initialize(uri, emitter, message_queue, test_library) - super(emitter, message_queue) - @uri = T.let(uri, URI::Generic) - @external_listeners.concat( - Extension.extensions.filter_map { |ext| ext.create_code_lens_listener(uri, emitter, message_queue) }, - ) @test_library = T.let(test_library, String) @response = T.let([], ResponseType) @path = T.let(uri.to_standardized_path, T.nilable(String)) @@ -46,6 +43,8 @@ def initialize(uri, emitter, message_queue, test_library) @visibility_stack = T.let([["public", "public"]], T::Array[T::Array[T.nilable(String)]]) @class_stack = T.let([], T::Array[String]) + super(emitter, message_queue) + emitter.register( self, :on_class, @@ -149,6 +148,11 @@ def on_vcall(node) end end + sig { override.params(extension: RubyLsp::Extension).returns(T.nilable(Listener[ResponseType])) } + def initialize_external_listener(extension) + extension.create_code_lens_listener(@uri, @emitter, @message_queue) + end + sig { override.params(other: Listener[ResponseType]).returns(T.self_type) } def merge_response!(other) @response.concat(other.response) diff --git a/lib/ruby_lsp/requests/document_symbol.rb b/lib/ruby_lsp/requests/document_symbol.rb index e64e73ecf9..6ddd9cb47f 100644 --- a/lib/ruby_lsp/requests/document_symbol.rb +++ b/lib/ruby_lsp/requests/document_symbol.rb @@ -30,6 +30,8 @@ class DocumentSymbol < Listener extend T::Sig extend T::Generic + include Extensible + ResponseType = type_member { { fixed: T::Array[Interface::DocumentSymbol] } } ATTR_ACCESSORS = T.let(["attr_reader", "attr_writer", "attr_accessor"].freeze, T::Array[String]) @@ -51,8 +53,6 @@ def initialize sig { params(emitter: EventEmitter, message_queue: Thread::Queue).void } def initialize(emitter, message_queue) - super - @root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot) @response = T.let(@root.children, T::Array[Interface::DocumentSymbol]) @stack = T.let( @@ -60,9 +60,7 @@ def initialize(emitter, message_queue) T::Array[T.any(SymbolHierarchyRoot, Interface::DocumentSymbol)], ) - @external_listeners.concat( - Extension.extensions.filter_map { |ext| ext.create_document_symbol_listener(emitter, message_queue) }, - ) + super emitter.register( self, @@ -79,6 +77,11 @@ def initialize(emitter, message_queue) ) end + sig { override.params(extension: RubyLsp::Extension).returns(T.nilable(Listener[ResponseType])) } + def initialize_external_listener(extension) + extension.create_document_symbol_listener(@emitter, @message_queue) + end + # Merges responses from other listeners sig { override.params(other: Listener[ResponseType]).returns(T.self_type) } def merge_response!(other) diff --git a/lib/ruby_lsp/requests/hover.rb b/lib/ruby_lsp/requests/hover.rb index d3d6e3a414..ebb8a02de4 100644 --- a/lib/ruby_lsp/requests/hover.rb +++ b/lib/ruby_lsp/requests/hover.rb @@ -17,6 +17,8 @@ class Hover < Listener extend T::Sig extend T::Generic + include Extensible + ResponseType = type_member { { fixed: T.nilable(Interface::Hover) } } ALLOWED_TARGETS = T.let( @@ -41,17 +43,19 @@ class Hover < Listener ).void end def initialize(index, nesting, emitter, message_queue) - super(emitter, message_queue) - @nesting = nesting @index = index - @external_listeners.concat( - Extension.extensions.filter_map { |ext| ext.create_hover_listener(emitter, message_queue) }, - ) @response = T.let(nil, ResponseType) + + super(emitter, message_queue) emitter.register(self, :on_const_path_ref, :on_const) end + sig { override.params(extension: RubyLsp::Extension).returns(T.nilable(Listener[ResponseType])) } + def initialize_external_listener(extension) + extension.create_hover_listener(@emitter, @message_queue) + end + # Merges responses from other hover listeners sig { override.params(other: Listener[ResponseType]).returns(T.self_type) } def merge_response!(other) diff --git a/test/requests/code_lens_expectations_test.rb b/test/requests/code_lens_expectations_test.rb index 4e895420de..850f7b802a 100644 --- a/test/requests/code_lens_expectations_test.rb +++ b/test/requests/code_lens_expectations_test.rb @@ -127,6 +127,8 @@ def name end def create_code_lens_listener(uri, emitter, message_queue) + raise "uri can't be nil" unless uri + klass = Class.new(RubyLsp::Listener) do attr_reader :response