Skip to content

Commit

Permalink
Introduce Listener::Extensible module
Browse files Browse the repository at this point in the history
  • Loading branch information
st0012 committed Aug 31, 2023
1 parent 2bcec13 commit 6f40c4a
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 27 deletions.
49 changes: 37 additions & 12 deletions lib/ruby_lsp/listener.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,51 @@ 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
# accumulate results in a @response variable and then provide the reader so that it is accessible
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
14 changes: 9 additions & 5 deletions lib/ruby_lsp/requests/code_lens.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -33,19 +35,16 @@ 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))
# visibility_stack is a stack of [current_visibility, previous_visibility]
@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,
Expand Down Expand Up @@ -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)
Expand Down
13 changes: 8 additions & 5 deletions lib/ruby_lsp/requests/document_symbol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand All @@ -51,18 +53,14 @@ 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(
[@root],
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,
Expand All @@ -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)
Expand Down
14 changes: 9 additions & 5 deletions lib/ruby_lsp/requests/hover.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions test/requests/code_lens_expectations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 6f40c4a

Please sign in to comment.