Skip to content

Commit

Permalink
Introduce ExtensibleListener to simplify response merging
Browse files Browse the repository at this point in the history
  • Loading branch information
st0012 committed Sep 4, 2023
1 parent 6f40c4a commit 762a3b7
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 47 deletions.
10 changes: 3 additions & 7 deletions lib/ruby_lsp/executor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,11 @@ def run(request)
semantic_highlighting = Requests::SemanticHighlighting.new(emitter, @message_queue)
emitter.visit(document.tree) if document.parsed?

code_lens.merge_external_listeners_responses!
document_symbol.merge_external_listeners_responses!

# Store all responses retrieve in this round of visits in the cache and then return the response for the request
# we actually received
document.cache_set("textDocument/documentSymbol", document_symbol.response)
document.cache_set("textDocument/documentSymbol", document_symbol.merged_response)
document.cache_set("textDocument/documentLink", document_link.response)
document.cache_set("textDocument/codeLens", code_lens.response)
document.cache_set("textDocument/codeLens", code_lens.merged_response)
document.cache_set(
"textDocument/semanticTokens/full",
Requests::Support::SemanticTokenEncoder.new.encode(semantic_highlighting.response),
Expand Down Expand Up @@ -294,8 +291,7 @@ def hover(uri, position)
# Emit events for all listeners
emitter.emit_for_target(target)

hover.merge_external_listeners_responses!
hover.response
hover.merged_response
end

sig { params(uri: URI::Generic, content_changes: T::Array[Document::EditShape], version: Integer).returns(Object) }
Expand Down
67 changes: 36 additions & 31 deletions lib/ruby_lsp/listener.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,45 +24,50 @@ def initialize(emitter, message_queue)
# accumulate results in a @response variable and then provide the reader so that it is accessible
sig { abstract.returns(ResponseType) }
def response; end
end

module Extensible
extend T::Sig
extend T::Generic
class ExtensibleListener < Listener
extend T::Sig
extend T::Generic

ResponseType = type_member
ResponseType = type_member

abstract!
abstract!

requires_ancestor { Listener }
sig { params(emitter: EventEmitter, message_queue: Thread::Queue).void }
def initialize(emitter, message_queue)
super
@response_merged = T.let(false, T::Boolean)
@external_listeners = T.let(
Extension.extensions.filter_map do |ext|
initialize_external_listener(ext)
end,
T::Array[RubyLsp::Listener[ResponseType]],
)
end

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

# 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 { returns(ResponseType) }
def merged_response
merge_external_listeners_responses! unless @response_merged
response
end

sig do
abstract.params(extension: RubyLsp::Extension).returns(T.nilable(RubyLsp::Listener[ResponseType]))
end
def initialize_external_listener(extension); 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 { abstract.params(other: Listener[T.untyped]).returns(T.self_type) }
def merge_response!(other)
end
# 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
4 changes: 1 addition & 3 deletions lib/ruby_lsp/requests/code_lens.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@ module Requests
# class Test < Minitest::Test
# end
# ```
class CodeLens < Listener
class CodeLens < ExtensibleListener
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 Down
4 changes: 1 addition & 3 deletions lib/ruby_lsp/requests/document_symbol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,10 @@ module Requests
# end
# end
# ```
class DocumentSymbol < Listener
class DocumentSymbol < ExtensibleListener
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 Down
4 changes: 1 addition & 3 deletions lib/ruby_lsp/requests/hover.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ module Requests
# ```ruby
# String # -> Hovering over the class reference will show all declaration locations and the documentation
# ```
class Hover < Listener
class Hover < ExtensibleListener
extend T::Sig
extend T::Generic

include Extensible

ResponseType = type_member { { fixed: T.nilable(Interface::Hover) } }

ALLOWED_TARGETS = T.let(
Expand Down

0 comments on commit 762a3b7

Please sign in to comment.