From 06fa0e63f55026adc2ffa2b4558a6d5e05d4e00d Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Tue, 30 Jan 2024 23:12:53 +0000 Subject: [PATCH] Fully adopt the new response builder pattern and remove the Listener class (#1337) * Restructure Completion * Restructure DocumentHighlight * Restructure DocumentLink * Restructure FoldingRanges * Restructure InlayHints * Restructure SignatureHelp * Remove listener.rb * Update check_docs task's messages * Replace unnecessarily similar response builders with CollectionResponseBuilder --- .../use_register_with_handler_method.rb | 12 ++---- lib/ruby_lsp/addon.rb | 6 +-- lib/ruby_lsp/check_docs.rb | 14 +++---- lib/ruby_lsp/internal.rb | 1 - lib/ruby_lsp/listener.rb | 33 --------------- lib/ruby_lsp/listeners/code_lens.rb | 2 +- lib/ruby_lsp/listeners/completion.rb | 23 ++++------ lib/ruby_lsp/listeners/definition.rb | 2 +- lib/ruby_lsp/listeners/document_highlight.rb | 17 +++----- lib/ruby_lsp/listeners/document_link.rb | 18 +++----- lib/ruby_lsp/listeners/folding_ranges.rb | 42 +++++++++---------- lib/ruby_lsp/listeners/inlay_hints.rb | 20 ++++----- lib/ruby_lsp/listeners/signature_help.rb | 19 ++++----- lib/ruby_lsp/requests/code_lens.rb | 5 ++- lib/ruby_lsp/requests/completion.rb | 16 ++++--- lib/ruby_lsp/requests/definition.rb | 5 ++- lib/ruby_lsp/requests/document_highlight.rb | 12 +++--- lib/ruby_lsp/requests/document_link.rb | 13 +++--- lib/ruby_lsp/requests/folding_ranges.rb | 17 +++++--- lib/ruby_lsp/requests/inlay_hints.rb | 15 ++++--- lib/ruby_lsp/requests/signature_help.rb | 10 ++--- lib/ruby_lsp/response_builders.rb | 6 +-- lib/ruby_lsp/response_builders/code_lens.rb | 28 ------------- .../collection_response_builder.rb | 29 +++++++++++++ lib/ruby_lsp/response_builders/definition.rb | 28 ------------- .../response_builders/response_builder.rb | 4 +- .../response_builders/signature_help.rb | 28 +++++++++++++ .../expectations/definition/requires.exp.json | 2 +- test/fixtures/requires.rb | 2 +- test/requests/definition_expectations_test.rb | 2 +- .../use_register_with_handler_method_test.rb | 18 +++----- 31 files changed, 191 insertions(+), 258 deletions(-) delete mode 100644 lib/ruby_lsp/listener.rb delete mode 100644 lib/ruby_lsp/response_builders/code_lens.rb create mode 100644 lib/ruby_lsp/response_builders/collection_response_builder.rb delete mode 100644 lib/ruby_lsp/response_builders/definition.rb create mode 100644 lib/ruby_lsp/response_builders/signature_help.rb diff --git a/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb b/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb index db4177282..28c956659 100644 --- a/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +++ b/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb @@ -13,9 +13,8 @@ module RubyLsp # # Register without handler method. # # # bad - # class MyListener < Listener + # class MyListener # def initialize(dispatcher) - # super() # dispatcher.register( # self, # :on_string_node_enter, @@ -24,9 +23,8 @@ module RubyLsp # end # # # good - # class MyListener < Listener + # class MyListener # def initialize(dispatcher) - # super() # dispatcher.register( # self, # :on_string_node_enter, @@ -41,9 +39,8 @@ module RubyLsp # # Handler method without register. # # # bad - # class MyListener < Listener + # class MyListener # def initialize(dispatcher) - # super() # dispatcher.register( # self, # ) @@ -54,9 +51,8 @@ module RubyLsp # end # # # good - # class MyListener < Listener + # class MyListener # def initialize(dispatcher) - # super() # dispatcher.register( # self, # :on_string_node_enter, diff --git a/lib/ruby_lsp/addon.rb b/lib/ruby_lsp/addon.rb index 24d937f86..a3255a7a7 100644 --- a/lib/ruby_lsp/addon.rb +++ b/lib/ruby_lsp/addon.rb @@ -109,7 +109,7 @@ def name; end # Creates a new CodeLens listener. This method is invoked on every CodeLens request sig do overridable.params( - response_builder: ResponseBuilders::CodeLens, + response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens], uri: URI::Generic, dispatcher: Prism::Dispatcher, ).void @@ -123,7 +123,7 @@ def create_code_lens_listener(response_builder, uri, dispatcher); end nesting: T::Array[String], index: RubyIndexer::Index, dispatcher: Prism::Dispatcher, - ).returns(T.nilable(Listener[T.nilable(Interface::Hover)])) + ).void end def create_hover_listener(response_builder, nesting, index, dispatcher); end @@ -147,7 +147,7 @@ def create_semantic_highlighting_listener(response_builder, dispatcher); end # Creates a new Definition listener. This method is invoked on every Definition request sig do overridable.params( - response_builder: ResponseBuilders::Definition, + response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::Location], uri: URI::Generic, nesting: T::Array[String], index: RubyIndexer::Index, diff --git a/lib/ruby_lsp/check_docs.rb b/lib/ruby_lsp/check_docs.rb index 6cff3d50d..fef3bf996 100644 --- a/lib/ruby_lsp/check_docs.rb +++ b/lib/ruby_lsp/check_docs.rb @@ -6,7 +6,7 @@ module RubyLsp # This rake task checks that all requests or addons are fully documented. Add the rake task to your Rakefile and - # specify the absolute path for all files that must be required in order to discover all listeners and their related + # specify the absolute path for all files that must be required in order to discover all requests and their related # GIFs # # # Rakefile @@ -33,7 +33,7 @@ def initialize(require_files, gif_files) sig { void } def define_task - desc("Checks if all Ruby LSP listeners are documented") + desc("Checks if all Ruby LSP requests are documented") task(@name) { run_task } end @@ -46,10 +46,10 @@ def gif_exists?(request_path) sig { void } def run_task - # Require all files configured to make sure all listeners are loaded + # Require all files configured to make sure all requests are loaded @file_list.each { |f| require(f.delete_suffix(".rb")) } - # Find all classes that inherit from BaseRequest or Listener, which are the ones we want to make sure are + # Find all classes that inherit from BaseRequest, which are the ones we want to make sure are # documented features = ObjectSpace.each_object(Class).select do |k| klass = T.unsafe(k) @@ -69,7 +69,7 @@ def run_task lines = File.readlines(file_path) docs = [] - # Extract the documentation on top of the listener constant + # Extract the documentation on top of the request constant while (line = lines[line_number]&.strip) && line.start_with?("#") docs.unshift(line) line_number -= 1 @@ -116,7 +116,7 @@ def run_task if missing_docs.any? warn(<<~WARN) - The following listeners are missing documentation: + The following requests are missing documentation: #{missing_docs.map { |k, v| "#{k}\n\n#{v.join("\n")}" }.join("\n\n")} WARN @@ -124,7 +124,7 @@ def run_task abort end - puts "All listeners are documented!" + puts "All requests are documented!" end end end diff --git a/lib/ruby_lsp/internal.rb b/lib/ruby_lsp/internal.rb index 42253eda6..1c6ab3529 100644 --- a/lib/ruby_lsp/internal.rb +++ b/lib/ruby_lsp/internal.rb @@ -24,7 +24,6 @@ require "ruby_lsp/executor" require "ruby_lsp/requests" require "ruby_lsp/response_builders" -require "ruby_lsp/listener" require "ruby_lsp/document" require "ruby_lsp/ruby_document" require "ruby_lsp/store" diff --git a/lib/ruby_lsp/listener.rb b/lib/ruby_lsp/listener.rb deleted file mode 100644 index 7b856456f..000000000 --- a/lib/ruby_lsp/listener.rb +++ /dev/null @@ -1,33 +0,0 @@ -# typed: strict -# frozen_string_literal: true - -module RubyLsp - # Listener is an abstract class to be used by requests for listening to events emitted when visiting an AST using the - # Prism::Dispatcher. - class Listener - extend T::Sig - extend T::Helpers - extend T::Generic - include Requests::Support::Common - - ResponseType = type_member - - abstract! - - sig { params(dispatcher: Prism::Dispatcher).void } - def initialize(dispatcher) - super() - @dispatcher = dispatcher - end - - sig { returns(ResponseType) } - def response - _response - 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 - end -end diff --git a/lib/ruby_lsp/listeners/code_lens.rb b/lib/ruby_lsp/listeners/code_lens.rb index 3120f0564..c96cd29d0 100644 --- a/lib/ruby_lsp/listeners/code_lens.rb +++ b/lib/ruby_lsp/listeners/code_lens.rb @@ -25,7 +25,7 @@ class CodeLens sig do params( - response_builder: ResponseBuilders::CodeLens, + response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens], uri: URI::Generic, lenses_configuration: RequestConfig, dispatcher: Prism::Dispatcher, diff --git a/lib/ruby_lsp/listeners/completion.rb b/lib/ruby_lsp/listeners/completion.rb index e6886acb8..260d7046a 100644 --- a/lib/ruby_lsp/listeners/completion.rb +++ b/lib/ruby_lsp/listeners/completion.rb @@ -3,26 +3,21 @@ module RubyLsp module Listeners - class Completion < Listener + class Completion extend T::Sig - extend T::Generic - - ResponseType = type_member { { fixed: T::Array[Interface::CompletionItem] } } - - sig { override.returns(ResponseType) } - attr_reader :_response + include Requests::Support::Common sig do params( + response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem], index: RubyIndexer::Index, nesting: T::Array[String], typechecker_enabled: T::Boolean, dispatcher: Prism::Dispatcher, ).void end - def initialize(index, nesting, typechecker_enabled, dispatcher) - super(dispatcher) - @_response = T.let([], ResponseType) + def initialize(response_builder, index, nesting, typechecker_enabled, dispatcher) + @response_builder = response_builder @index = index @nesting = nesting @typechecker_enabled = typechecker_enabled @@ -39,7 +34,7 @@ def initialize(index, nesting, typechecker_enabled, dispatcher) sig { params(node: Prism::StringNode).void } def on_string_node_enter(node) @index.search_require_paths(node.content).map!(&:require_path).sort!.each do |path| - @_response << build_completion(T.must(path), node) + @response_builder << build_completion(T.must(path), node) end end @@ -52,7 +47,7 @@ def on_constant_read_node_enter(node) candidates = @index.prefix_search(name, @nesting) candidates.each do |entries| complete_name = T.must(entries.first).name - @_response << build_entry_completion( + @response_builder << build_entry_completion( complete_name, name, node, @@ -96,7 +91,7 @@ def on_constant_path_node_enter(node) full_name = aliased_namespace.empty? ? constant_name : "#{aliased_namespace}::#{constant_name}" - @_response << build_entry_completion( + @response_builder << build_entry_completion( full_name, name, node, @@ -123,7 +118,7 @@ def on_call_node_enter(node) entry = entries.find { |e| e.is_a?(RubyIndexer::Entry::Member) && e.owner&.name == receiver.name } next unless entry - @_response << build_method_completion(T.cast(entry, RubyIndexer::Entry::Member), node) + @response_builder << build_method_completion(T.cast(entry, RubyIndexer::Entry::Member), node) end end diff --git a/lib/ruby_lsp/listeners/definition.rb b/lib/ruby_lsp/listeners/definition.rb index c83baf5c7..e0f1b1374 100644 --- a/lib/ruby_lsp/listeners/definition.rb +++ b/lib/ruby_lsp/listeners/definition.rb @@ -9,7 +9,7 @@ class Definition sig do params( - response_builder: ResponseBuilders::Definition, + response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::Location], uri: URI::Generic, nesting: T::Array[String], index: RubyIndexer::Index, diff --git a/lib/ruby_lsp/listeners/document_highlight.rb b/lib/ruby_lsp/listeners/document_highlight.rb index 91bf723af..3952d5161 100644 --- a/lib/ruby_lsp/listeners/document_highlight.rb +++ b/lib/ruby_lsp/listeners/document_highlight.rb @@ -3,10 +3,9 @@ module RubyLsp module Listeners - class DocumentHighlight < Listener + class DocumentHighlight extend T::Sig - - ResponseType = type_member { { fixed: T::Array[Interface::DocumentHighlight] } } + include Requests::Support::Common GLOBAL_VARIABLE_NODES = T.let( [ @@ -87,20 +86,16 @@ class DocumentHighlight < Listener T::Array[T.class_of(Prism::Node)], ) - sig { override.returns(ResponseType) } - attr_reader :_response - sig do params( + response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight], target: T.nilable(Prism::Node), parent: T.nilable(Prism::Node), dispatcher: Prism::Dispatcher, ).void end - def initialize(target, parent, dispatcher) - super(dispatcher) - - @_response = T.let([], T::Array[Interface::DocumentHighlight]) + def initialize(response_builder, target, parent, dispatcher) + @response_builder = response_builder return unless target && parent @@ -521,7 +516,7 @@ def matches?(node, classes) sig { params(kind: Integer, location: Prism::Location).void } def add_highlight(kind, location) - @_response << Interface::DocumentHighlight.new(range: range_from_location(location), kind: kind) + @response_builder << Interface::DocumentHighlight.new(range: range_from_location(location), kind: kind) end sig { params(node: T.nilable(Prism::Node)).returns(T.nilable(String)) } diff --git a/lib/ruby_lsp/listeners/document_link.rb b/lib/ruby_lsp/listeners/document_link.rb index 0b37a466e..f53140743 100644 --- a/lib/ruby_lsp/listeners/document_link.rb +++ b/lib/ruby_lsp/listeners/document_link.rb @@ -5,11 +5,9 @@ module RubyLsp module Listeners - class DocumentLink < Listener + class DocumentLink extend T::Sig - extend T::Generic - - ResponseType = type_member { { fixed: T::Array[Interface::DocumentLink] } } + include Requests::Support::Common GEM_TO_VERSION_MAP = T.let( [*::Gem::Specification.default_stubs, *::Gem::Specification.stubs].map! do |s| @@ -59,25 +57,21 @@ def gem_paths end end - sig { override.returns(ResponseType) } - attr_reader :_response - sig do params( + response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::DocumentLink], uri: URI::Generic, comments: T::Array[Prism::Comment], dispatcher: Prism::Dispatcher, ).void end - def initialize(uri, comments, dispatcher) - super(dispatcher) - + def initialize(response_builder, uri, comments, dispatcher) # Match the version based on the version in the RBI file name. Notice that the `@` symbol is sanitized to `%40` # in the URI + @response_builder = response_builder path = uri.to_standardized_path version_match = path ? /(?<=%40)[\d.]+(?=\.rbi$)/.match(path) : nil @gem_version = T.let(version_match && version_match[0], T.nilable(String)) - @_response = T.let([], T::Array[Interface::DocumentLink]) @lines_to_comments = T.let( comments.to_h do |comment| [comment.location.end_line, comment] @@ -137,7 +131,7 @@ def extract_document_link(node) file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, CGI.unescape(uri.path)) return if file_path.nil? - @_response << Interface::DocumentLink.new( + @response_builder << Interface::DocumentLink.new( range: range_from_location(comment.location), target: "file://#{file_path}##{uri.line_number}", tooltip: "Jump to #{file_path}##{uri.line_number}", diff --git a/lib/ruby_lsp/listeners/folding_ranges.rb b/lib/ruby_lsp/listeners/folding_ranges.rb index 96bc9516b..0309a7335 100644 --- a/lib/ruby_lsp/listeners/folding_ranges.rb +++ b/lib/ruby_lsp/listeners/folding_ranges.rb @@ -3,19 +3,20 @@ module RubyLsp module Listeners - class FoldingRanges < Listener + class FoldingRanges extend T::Sig - extend T::Generic - - ResponseType = type_member { { fixed: T::Array[Interface::FoldingRange] } } - - sig { params(comments: T::Array[Prism::Comment], dispatcher: Prism::Dispatcher).void } - def initialize(comments, dispatcher) - super(dispatcher) - - @_response = T.let([], ResponseType) + include Requests::Support::Common + + sig do + params( + response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::FoldingRange], + comments: T::Array[Prism::Comment], + dispatcher: Prism::Dispatcher, + ).void + end + def initialize(response_builder, comments, dispatcher) + @response_builder = response_builder @requires = T.let([], T::Array[Prism::CallNode]) - @finalized_response = T.let(false, T::Boolean) @comments = comments dispatcher.register( @@ -46,15 +47,10 @@ def initialize(comments, dispatcher) ) end - sig { override.returns(ResponseType) } - def _response - unless @finalized_response - push_comment_ranges - emit_requires_range - @finalized_response = true - end - - @_response + sig { void } + def finalize_response! + push_comment_ranges + emit_requires_range end sig { params(node: Prism::IfNode).void } @@ -203,7 +199,7 @@ def push_comment_ranges end.each do |chunk| next if chunk.length == 1 - @_response << Interface::FoldingRange.new( + @response_builder << Interface::FoldingRange.new( start_line: T.must(chunk.first).location.start_line - 1, end_line: T.must(chunk.last).location.end_line - 1, kind: "comment", @@ -214,7 +210,7 @@ def push_comment_ranges sig { void } def emit_requires_range if @requires.length > 1 - @_response << Interface::FoldingRange.new( + @response_builder << Interface::FoldingRange.new( start_line: T.must(@requires.first).location.start_line - 1, end_line: T.must(@requires.last).location.end_line - 1, kind: "imports", @@ -260,7 +256,7 @@ def add_lines_range(start_line, end_line) emit_requires_range return if start_line >= end_line - @_response << Interface::FoldingRange.new( + @response_builder << Interface::FoldingRange.new( start_line: start_line - 1, end_line: end_line - 1, kind: "region", diff --git a/lib/ruby_lsp/listeners/inlay_hints.rb b/lib/ruby_lsp/listeners/inlay_hints.rb index 18e352cc0..3ee0ba204 100644 --- a/lib/ruby_lsp/listeners/inlay_hints.rb +++ b/lib/ruby_lsp/listeners/inlay_hints.rb @@ -3,28 +3,22 @@ module RubyLsp module Listeners - class InlayHints < Listener + class InlayHints extend T::Sig - extend T::Generic - - ResponseType = type_member { { fixed: T::Array[Interface::InlayHint] } } + include Requests::Support::Common RESCUE_STRING_LENGTH = T.let("rescue".length, Integer) - sig { override.returns(ResponseType) } - attr_reader :_response - sig do params( + response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::InlayHint], range: T::Range[Integer], hints_configuration: RequestConfig, dispatcher: Prism::Dispatcher, ).void end - def initialize(range, hints_configuration, dispatcher) - super(dispatcher) - - @_response = T.let([], ResponseType) + def initialize(response_builder, range, hints_configuration, dispatcher) + @response_builder = response_builder @range = range @hints_configuration = hints_configuration @@ -39,7 +33,7 @@ def on_rescue_node_enter(node) loc = node.location return unless visible?(node, @range) - @_response << Interface::InlayHint.new( + @response_builder << Interface::InlayHint.new( position: { line: loc.start_line - 1, character: loc.start_column + RESCUE_STRING_LENGTH }, label: "StandardError", padding_left: true, @@ -68,7 +62,7 @@ def on_implicit_node_enter(node) tooltip = "This is a local variable: #{node_name}" end - @_response << Interface::InlayHint.new( + @response_builder << Interface::InlayHint.new( position: { line: loc.start_line - 1, character: loc.start_column + node_name.length + 1 }, label: node_name, padding_left: true, diff --git a/lib/ruby_lsp/listeners/signature_help.rb b/lib/ruby_lsp/listeners/signature_help.rb index 1122b2d45..84a928503 100644 --- a/lib/ruby_lsp/listeners/signature_help.rb +++ b/lib/ruby_lsp/listeners/signature_help.rb @@ -3,28 +3,22 @@ module RubyLsp module Listeners - class SignatureHelp < Listener + class SignatureHelp extend T::Sig - extend T::Generic - - ResponseType = type_member { { fixed: T.nilable(T.any(Interface::SignatureHelp, T::Hash[Symbol, T.untyped])) } } - - sig { override.returns(ResponseType) } - attr_reader :_response + include Requests::Support::Common sig do params( + response_builder: ResponseBuilders::SignatureHelp, nesting: T::Array[String], index: RubyIndexer::Index, dispatcher: Prism::Dispatcher, ).void end - def initialize(nesting, index, dispatcher) + def initialize(response_builder, nesting, index, dispatcher) + @response_builder = response_builder @nesting = nesting @index = index - @_response = T.let(nil, ResponseType) - - super(dispatcher) dispatcher.register(self, :on_call_node_enter) end @@ -58,7 +52,7 @@ def on_call_node_enter(node) active_parameter += 1 end - @_response = Interface::SignatureHelp.new( + signature_help = Interface::SignatureHelp.new( signatures: [ Interface::SignatureInformation.new( label: label, @@ -71,6 +65,7 @@ def on_call_node_enter(node) ], active_parameter: active_parameter, ) + @response_builder.replace(signature_help) end end end diff --git a/lib/ruby_lsp/requests/code_lens.rb b/lib/ruby_lsp/requests/code_lens.rb index 7377ba2a1..58900642c 100644 --- a/lib/ruby_lsp/requests/code_lens.rb +++ b/lib/ruby_lsp/requests/code_lens.rb @@ -44,7 +44,10 @@ def provider ).void end def initialize(uri, lenses_configuration, dispatcher) - @response_builder = T.let(ResponseBuilders::CodeLens.new, ResponseBuilders::CodeLens) + @response_builder = T.let( + ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens].new, + ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens], + ) super() Listeners::CodeLens.new(@response_builder, uri, lenses_configuration, dispatcher) diff --git a/lib/ruby_lsp/requests/completion.rb b/lib/ruby_lsp/requests/completion.rb index 99f5dc13f..8b7e9bdc9 100644 --- a/lib/ruby_lsp/requests/completion.rb +++ b/lib/ruby_lsp/requests/completion.rb @@ -26,7 +26,6 @@ module Requests # ``` class Completion < Request extend T::Sig - extend T::Generic class << self extend T::Sig @@ -43,8 +42,6 @@ def provider end end - ResponseType = type_member { { fixed: T::Array[Interface::CompletionItem] } } - sig do params( document: Document, @@ -66,12 +63,13 @@ def initialize(document, index, position, typechecker_enabled, dispatcher) char_position, node_types: [Prism::CallNode, Prism::ConstantReadNode, Prism::ConstantPathNode], ) - - @listener = T.let( - Listeners::Completion.new(index, nesting, typechecker_enabled, dispatcher), - Listener[ResponseType], + @response_builder = T.let( + ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem].new, + ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem], ) + Listeners::Completion.new(@response_builder, index, nesting, typechecker_enabled, dispatcher) + return unless matched && parent @target = case matched @@ -99,12 +97,12 @@ def initialize(document, index, position, typechecker_enabled, dispatcher) end end - sig { override.returns(ResponseType) } + sig { override.returns(T::Array[Interface::CompletionItem]) } def perform return [] unless @target @dispatcher.dispatch_once(@target) - @listener.response + @response_builder.response end end end diff --git a/lib/ruby_lsp/requests/definition.rb b/lib/ruby_lsp/requests/definition.rb index 7f1d6a138..cb2d99cec 100644 --- a/lib/ruby_lsp/requests/definition.rb +++ b/lib/ruby_lsp/requests/definition.rb @@ -39,7 +39,10 @@ class Definition < Request end def initialize(document, index, position, dispatcher, typechecker_enabled) super() - @response_builder = T.let(ResponseBuilders::Definition.new, ResponseBuilders::Definition) + @response_builder = T.let( + ResponseBuilders::CollectionResponseBuilder[Interface::Location].new, + ResponseBuilders::CollectionResponseBuilder[Interface::Location], + ) target, parent, nesting = document.locate_node( position, diff --git a/lib/ruby_lsp/requests/document_highlight.rb b/lib/ruby_lsp/requests/document_highlight.rb index 27a7b1588..bec32dc99 100644 --- a/lib/ruby_lsp/requests/document_highlight.rb +++ b/lib/ruby_lsp/requests/document_highlight.rb @@ -27,8 +27,6 @@ module Requests class DocumentHighlight < Request extend T::Sig - ResponseType = type_member { { fixed: T::Array[Interface::DocumentHighlight] } } - sig do params( document: Document, @@ -39,12 +37,16 @@ class DocumentHighlight < Request def initialize(document, position, dispatcher) super() target, parent = document.locate_node(position) - @listener = T.let(Listeners::DocumentHighlight.new(target, parent, dispatcher), Listener[ResponseType]) + @response_builder = T.let( + ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight].new, + ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight], + ) + Listeners::DocumentHighlight.new(@response_builder, target, parent, dispatcher) end - sig { override.returns(ResponseType) } + sig { override.returns(T::Array[Interface::DocumentHighlight]) } def perform - @listener.response + @response_builder.response end end end diff --git a/lib/ruby_lsp/requests/document_link.rb b/lib/ruby_lsp/requests/document_link.rb index a061a8e48..400a9e71c 100644 --- a/lib/ruby_lsp/requests/document_link.rb +++ b/lib/ruby_lsp/requests/document_link.rb @@ -20,7 +20,6 @@ module Requests # ``` class DocumentLink < Request extend T::Sig - extend T::Generic class << self extend T::Sig @@ -31,8 +30,6 @@ def provider end end - ResponseType = type_member { { fixed: T::Array[Interface::DocumentLink] } } - sig do params( uri: URI::Generic, @@ -42,12 +39,16 @@ def provider end def initialize(uri, comments, dispatcher) super() - @listener = T.let(Listeners::DocumentLink.new(uri, comments, dispatcher), Listener[ResponseType]) + @response_builder = T.let( + ResponseBuilders::CollectionResponseBuilder[Interface::DocumentLink].new, + ResponseBuilders::CollectionResponseBuilder[Interface::DocumentLink], + ) + Listeners::DocumentLink.new(@response_builder, uri, comments, dispatcher) end - sig { override.returns(ResponseType) } + sig { override.returns(T::Array[Interface::DocumentLink]) } def perform - @listener.response + @response_builder.response end end end diff --git a/lib/ruby_lsp/requests/folding_ranges.rb b/lib/ruby_lsp/requests/folding_ranges.rb index 8beed4c61..149c0f55b 100644 --- a/lib/ruby_lsp/requests/folding_ranges.rb +++ b/lib/ruby_lsp/requests/folding_ranges.rb @@ -19,7 +19,6 @@ module Requests # ``` class FoldingRanges < Request extend T::Sig - extend T::Generic class << self extend T::Sig @@ -30,17 +29,23 @@ def provider end end - ResponseType = type_member { { fixed: T::Array[Interface::FoldingRange] } } - sig { params(comments: T::Array[Prism::Comment], dispatcher: Prism::Dispatcher).void } def initialize(comments, dispatcher) super() - @listener = T.let(Listeners::FoldingRanges.new(comments, dispatcher), Listener[ResponseType]) + @response_builder = T.let( + ResponseBuilders::CollectionResponseBuilder[Interface::FoldingRange].new, + ResponseBuilders::CollectionResponseBuilder[Interface::FoldingRange], + ) + @listener = T.let( + Listeners::FoldingRanges.new(@response_builder, comments, dispatcher), + Listeners::FoldingRanges, + ) end - sig { override.returns(ResponseType) } + sig { override.returns(T::Array[Interface::FoldingRange]) } def perform - @listener.response + @listener.finalize_response! + @response_builder.response end end end diff --git a/lib/ruby_lsp/requests/inlay_hints.rb b/lib/ruby_lsp/requests/inlay_hints.rb index 00f59ab4a..05d737de3 100644 --- a/lib/ruby_lsp/requests/inlay_hints.rb +++ b/lib/ruby_lsp/requests/inlay_hints.rb @@ -40,7 +40,6 @@ module Requests # ``` class InlayHints < Request extend T::Sig - extend T::Generic class << self extend T::Sig @@ -51,8 +50,6 @@ def provider end end - ResponseType = type_member { { fixed: T::Array[Interface::InlayHint] } } - sig do params( document: Document, @@ -65,15 +62,17 @@ def initialize(document, range, hints_configuration, dispatcher) super() start_line = range.dig(:start, :line) end_line = range.dig(:end, :line) - @listener = T.let( - Listeners::InlayHints.new(start_line..end_line, hints_configuration, dispatcher), - Listener[ResponseType], + + @response_builder = T.let( + ResponseBuilders::CollectionResponseBuilder[Interface::InlayHint].new, + ResponseBuilders::CollectionResponseBuilder[Interface::InlayHint], ) + Listeners::InlayHints.new(@response_builder, start_line..end_line, hints_configuration, dispatcher) end - sig { override.returns(ResponseType) } + sig { override.returns(T::Array[Interface::InlayHint]) } def perform - @listener.response + @response_builder.response end end end diff --git a/lib/ruby_lsp/requests/signature_help.rb b/lib/ruby_lsp/requests/signature_help.rb index e50369852..076337431 100644 --- a/lib/ruby_lsp/requests/signature_help.rb +++ b/lib/ruby_lsp/requests/signature_help.rb @@ -26,7 +26,6 @@ module Requests # ``` class SignatureHelp < Request extend T::Sig - extend T::Generic class << self extend T::Sig @@ -40,8 +39,6 @@ def provider end end - ResponseType = type_member { { fixed: T.nilable(T.any(Interface::SignatureHelp, T::Hash[Symbol, T.untyped])) } } - sig do params( document: Document, @@ -72,15 +69,16 @@ def initialize(document, index, position, context, dispatcher) @target = T.let(target, T.nilable(Prism::Node)) @dispatcher = dispatcher - @listener = T.let(Listeners::SignatureHelp.new(nesting, index, dispatcher), Listener[ResponseType]) + @response_builder = T.let(ResponseBuilders::SignatureHelp.new, ResponseBuilders::SignatureHelp) + Listeners::SignatureHelp.new(@response_builder, nesting, index, dispatcher) end - sig { override.returns(ResponseType) } + sig { override.returns(T.nilable(Interface::SignatureHelp)) } def perform return unless @target @dispatcher.dispatch_once(@target) - @listener.response + @response_builder.response end end end diff --git a/lib/ruby_lsp/response_builders.rb b/lib/ruby_lsp/response_builders.rb index 5e6bdf81d..134355fd3 100644 --- a/lib/ruby_lsp/response_builders.rb +++ b/lib/ruby_lsp/response_builders.rb @@ -3,11 +3,11 @@ module RubyLsp module ResponseBuilders - autoload :ResponseBuilder, "ruby_lsp/response_builders/response_builder" - autoload :CodeLens, "ruby_lsp/response_builders/code_lens" - autoload :Definition, "ruby_lsp/response_builders/definition" + autoload :CollectionResponseBuilder, "ruby_lsp/response_builders/collection_response_builder" autoload :DocumentSymbol, "ruby_lsp/response_builders/document_symbol" autoload :Hover, "ruby_lsp/response_builders/hover" + autoload :ResponseBuilder, "ruby_lsp/response_builders/response_builder" autoload :SemanticHighlighting, "ruby_lsp/response_builders/semantic_highlighting" + autoload :SignatureHelp, "ruby_lsp/response_builders/signature_help" end end diff --git a/lib/ruby_lsp/response_builders/code_lens.rb b/lib/ruby_lsp/response_builders/code_lens.rb deleted file mode 100644 index e26c1ca8f..000000000 --- a/lib/ruby_lsp/response_builders/code_lens.rb +++ /dev/null @@ -1,28 +0,0 @@ -# typed: strict -# frozen_string_literal: true - -module RubyLsp - module ResponseBuilders - class CodeLens < ResponseBuilder - ResponseType = type_member { { fixed: T::Array[Interface::CodeLens] } } - - extend T::Sig - - sig { void } - def initialize - super - @stack = T.let([], ResponseType) - end - - sig { params(code_lens: Interface::CodeLens).void } - def <<(*code_lens) - @stack.concat(code_lens) - end - - sig { override.returns(ResponseType) } - def response - @stack - end - end - end -end diff --git a/lib/ruby_lsp/response_builders/collection_response_builder.rb b/lib/ruby_lsp/response_builders/collection_response_builder.rb new file mode 100644 index 000000000..26df8a48e --- /dev/null +++ b/lib/ruby_lsp/response_builders/collection_response_builder.rb @@ -0,0 +1,29 @@ +# typed: strict +# frozen_string_literal: true + +module RubyLsp + module ResponseBuilders + class CollectionResponseBuilder < ResponseBuilder + extend T::Sig + extend T::Generic + + ResponseType = type_member { { upper: Object } } + + sig { void } + def initialize + super + @items = T.let([], T::Array[ResponseType]) + end + + sig { params(item: ResponseType).void } + def <<(item) + @items << item + end + + sig { override.returns(T::Array[ResponseType]) } + def response + @items + end + end + end +end diff --git a/lib/ruby_lsp/response_builders/definition.rb b/lib/ruby_lsp/response_builders/definition.rb deleted file mode 100644 index 5a679df17..000000000 --- a/lib/ruby_lsp/response_builders/definition.rb +++ /dev/null @@ -1,28 +0,0 @@ -# typed: strict -# frozen_string_literal: true - -module RubyLsp - module ResponseBuilders - class Definition < ResponseBuilder - ResponseType = type_member { { fixed: T::Array[Interface::Location] } } - - extend T::Sig - - sig { void } - def initialize - super - @locations = T.let([], ResponseType) - end - - sig { params(location: Interface::Location).void } - def <<(location) - @locations << location - end - - sig { override.returns(ResponseType) } - def response - @locations - end - end - end -end diff --git a/lib/ruby_lsp/response_builders/response_builder.rb b/lib/ruby_lsp/response_builders/response_builder.rb index d291217fd..b90882770 100644 --- a/lib/ruby_lsp/response_builders/response_builder.rb +++ b/lib/ruby_lsp/response_builders/response_builder.rb @@ -9,9 +9,7 @@ class ResponseBuilder abstract! - ResponseType = type_member { { upper: Object } } - - sig { abstract.returns(ResponseType) } + sig { abstract.returns(T.anything) } def response; end end end diff --git a/lib/ruby_lsp/response_builders/signature_help.rb b/lib/ruby_lsp/response_builders/signature_help.rb new file mode 100644 index 000000000..b1f9d0775 --- /dev/null +++ b/lib/ruby_lsp/response_builders/signature_help.rb @@ -0,0 +1,28 @@ +# typed: strict +# frozen_string_literal: true + +module RubyLsp + module ResponseBuilders + class SignatureHelp < ResponseBuilder + ResponseType = type_member { { fixed: T.nilable(Interface::SignatureHelp) } } + + extend T::Sig + + sig { void } + def initialize + super + @signature_help = T.let(nil, ResponseType) + end + + sig { params(signature_help: ResponseType).void } + def replace(signature_help) + @signature_help = signature_help + end + + sig { override.returns(ResponseType) } + def response + @signature_help + end + end + end +end diff --git a/test/expectations/definition/requires.exp.json b/test/expectations/definition/requires.exp.json index 10c1cb7be..ef33b3112 100644 --- a/test/expectations/definition/requires.exp.json +++ b/test/expectations/definition/requires.exp.json @@ -1,6 +1,6 @@ { "result": [{ - "uri": "file:///ruby_lsp/listener.rb", + "uri": "file:///ruby_lsp/executor.rb", "range": { "start": { "line": 0, diff --git a/test/fixtures/requires.rb b/test/fixtures/requires.rb index 5da3ed7c4..f7f374993 100644 --- a/test/fixtures/requires.rb +++ b/test/fixtures/requires.rb @@ -1 +1 @@ -require "ruby_lsp/listener" +require "ruby_lsp/executor" diff --git a/test/requests/definition_expectations_test.rb b/test/requests/definition_expectations_test.rb index b255d8034..85535e60b 100644 --- a/test/requests/definition_expectations_test.rb +++ b/test/requests/definition_expectations_test.rb @@ -38,7 +38,7 @@ def run_expectations(source) RubyIndexer::IndexablePath.new( "#{Dir.pwd}/lib", File.expand_path( - "../../lib/ruby_lsp/listener.rb", + "../../lib/ruby_lsp/executor.rb", __dir__, ), ), diff --git a/test/rubocop/cop/ruby_lsp/use_register_with_handler_method_test.rb b/test/rubocop/cop/ruby_lsp/use_register_with_handler_method_test.rb index fd96e2f85..eebd9e2ba 100644 --- a/test/rubocop/cop/ruby_lsp/use_register_with_handler_method_test.rb +++ b/test/rubocop/cop/ruby_lsp/use_register_with_handler_method_test.rb @@ -15,9 +15,8 @@ def setup def test_registers_offense_when_use_listener_without_handler assert_offense(<<~RUBY) - class MyListener < Listener + class MyListener def initialize(dispatcher) - super() dispatcher.register( self, :on_string_node_enter, @@ -31,9 +30,8 @@ def initialize(dispatcher) def test_registers_offense_when_use_handler_without_listener assert_offense(<<~RUBY) - class MyListener < Listener + class MyListener def initialize(dispatcher) - super() dispatcher.register( self, ) @@ -48,9 +46,8 @@ def on_string_node_enter(node) def test_registers_offense_when_both_are_mismatching assert_offense(<<~RUBY) - class MyListener < Listener + class MyListener def initialize(dispatcher) - super() dispatcher.register( self, :on_string_node_enter, @@ -67,9 +64,8 @@ def on_string_node_leave(node) def test_registers_multiple_offenses_for_listeners assert_offense(<<~RUBY) - class MyListener < Listener + class MyListener def initialize(dispatcher) - super() dispatcher.register( self, :on_string_node_enter, @@ -86,9 +82,8 @@ def initialize(dispatcher) def test_registers_multiple_offenses_for_handlers assert_offense(<<~RUBY) - class MyListener < Listener + class MyListener def initialize(dispatcher) - super() dispatcher.register( self, ) @@ -108,9 +103,8 @@ def on_constant_path_node_enter(node) def test_does_not_register_offense_when_using_listener_with_handler assert_no_offenses(<<~RUBY) - class MyListener < Listener + class MyListener def initialize(dispatcher) - super() dispatcher.register( self, :on_string_node_enter