Skip to content

Commit

Permalink
Add Request class to standardise request interface
Browse files Browse the repository at this point in the history
  • Loading branch information
st0012 committed Jan 5, 2024
1 parent eb0e725 commit b6ecb02
Show file tree
Hide file tree
Showing 23 changed files with 111 additions and 84 deletions.
2 changes: 1 addition & 1 deletion lib/ruby_lsp/check_docs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def run_task
klass_name = klass.name
next unless klass_name

klass if klass_name.match?(/RubyLsp::Requests::\w[^:]+$/) && !klass_name.include?("Support")
klass if klass.ancestors.include?(Request) && !klass == Request
end

missing_docs = T.let(Hash.new { |h, k| h[k] = [] }, T::Hash[String, T::Array[String]])
Expand Down
16 changes: 8 additions & 8 deletions lib/ruby_lsp/executor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -277,12 +277,12 @@ def signature_help(uri, position, context)

sig { params(query: T.nilable(String)).returns(T::Array[Interface::WorkspaceSymbol]) }
def workspace_symbol(query)
Requests::WorkspaceSymbol.new(query, @index).run
Requests::WorkspaceSymbol.new(query, @index).response
end

sig { params(uri: URI::Generic, range: T.nilable(T::Hash[Symbol, T.untyped])).returns({ ast: String }) }
def show_syntax_tree(uri, range)
{ ast: Requests::ShowSyntaxTree.new(@store.get(uri), range).run }
{ ast: Requests::ShowSyntaxTree.new(@store.get(uri), range).response }
end

sig do
Expand Down Expand Up @@ -363,7 +363,7 @@ def text_document_did_close(uri)
end
def selection_range(uri, positions)
ranges = @store.cache_fetch(uri, "textDocument/selectionRange") do |document|
Requests::SelectionRanges.new(document).run
Requests::SelectionRanges.new(document).response
end

# Per the selection range request spec (https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange),
Expand All @@ -390,7 +390,7 @@ def formatting(uri)
path = uri.to_standardized_path
return unless path.nil? || path.start_with?(T.must(@store.workspace_uri.to_standardized_path))

Requests::Formatting.new(@store.get(uri), formatter: @store.formatter).run
Requests::Formatting.new(@store.get(uri), formatter: @store.formatter).response
end

sig do
Expand All @@ -401,7 +401,7 @@ def formatting(uri)
).returns(T::Array[Interface::TextEdit])
end
def on_type_formatting(uri, position, character)
Requests::OnTypeFormatting.new(@store.get(uri), position, character).run
Requests::OnTypeFormatting.new(@store.get(uri), position, character).response
end

sig do
Expand Down Expand Up @@ -449,14 +449,14 @@ def inlay_hint(uri, range)
def code_action(uri, range, context)
document = @store.get(uri)

Requests::CodeActions.new(document, range, context).run
Requests::CodeActions.new(document, range, context).response
end

sig { params(params: T::Hash[Symbol, T.untyped]).returns(Interface::CodeAction) }
def code_action_resolve(params)
uri = URI(params.dig(:data, :uri))
document = @store.get(uri)
result = Requests::CodeActionResolve.new(document, params).run
result = Requests::CodeActionResolve.new(document, params).response

case result
when Requests::CodeActionResolve::Error::EmptySelection
Expand Down Expand Up @@ -490,7 +490,7 @@ def diagnostic(uri)
return unless path.nil? || path.start_with?(T.must(@store.workspace_uri.to_standardized_path))

response = @store.cache_fetch(uri, "textDocument/diagnostic") do |document|
Requests::Diagnostics.new(document).run
Requests::Diagnostics.new(document).response
end

Interface::FullDocumentDiagnosticReport.new(kind: "full", items: response) if response
Expand Down
7 changes: 4 additions & 3 deletions lib/ruby_lsp/listener.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
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
class Listener < Requests::Request
extend T::Sig
extend T::Helpers
extend T::Generic
Expand All @@ -16,10 +16,11 @@ class Listener

sig { params(dispatcher: Prism::Dispatcher).void }
def initialize(dispatcher)
super()
@dispatcher = dispatcher
end

sig { returns(ResponseType) }
sig { override.returns(ResponseType) }
def response
_response
end
Expand Down Expand Up @@ -61,7 +62,7 @@ def merge_external_listeners_responses!
@external_listeners.each { |l| merge_response!(l) }
end

sig { returns(ResponseType) }
sig { override.returns(ResponseType) }
def response
merge_external_listeners_responses! unless @response_merged
super
Expand Down
1 change: 1 addition & 0 deletions lib/ruby_lsp/requests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module RubyLsp
# - [SignatureHelp](rdoc-ref:RubyLsp::Requests::SignatureHelp)

module Requests
autoload :Request, "ruby_lsp/requests/request"
autoload :DocumentSymbol, "ruby_lsp/requests/document_symbol"
autoload :DocumentLink, "ruby_lsp/requests/document_link"
autoload :Hover, "ruby_lsp/requests/hover"
Expand Down
7 changes: 4 additions & 3 deletions lib/ruby_lsp/requests/code_action_resolve.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module Requests
#
# ```
#
class CodeActionResolve
class CodeActionResolve < Request
extend T::Sig
NEW_VARIABLE_NAME = "new_variable"

Expand All @@ -36,12 +36,13 @@ class Error < ::T::Enum

sig { params(document: Document, code_action: T::Hash[Symbol, T.untyped]).void }
def initialize(document, code_action)
super()
@document = document
@code_action = code_action
end

sig { returns(T.any(Interface::CodeAction, Error)) }
def run
sig { override.returns(T.any(Interface::CodeAction, Error)) }
def response
return Error::EmptySelection if @document.source.empty?

source_range = @code_action.dig(:data, :range)
Expand Down
7 changes: 4 additions & 3 deletions lib/ruby_lsp/requests/code_actions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module Requests
# puts "Hello" # --> code action: quick fix indentation
# end
# ```
class CodeActions
class CodeActions < Request
extend T::Sig

class << self
Expand All @@ -36,14 +36,15 @@ def provider
).void
end
def initialize(document, range, context)
super()
@document = document
@uri = T.let(document.uri, URI::Generic)
@range = range
@context = context
end

sig { returns(T.nilable(T.all(T::Array[Interface::CodeAction], Object))) }
def run
sig { override.returns(T.nilable(T.all(T::Array[Interface::CodeAction], Object))) }
def response
diagnostics = @context[:diagnostics]

code_actions = diagnostics.flat_map do |diagnostic|
Expand Down
7 changes: 4 additions & 3 deletions lib/ruby_lsp/requests/diagnostics.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module Requests
# puts "Hello" # --> diagnostics: incorrect indentation
# end
# ```
class Diagnostics
class Diagnostics < Request
extend T::Sig

class << self
Expand All @@ -35,12 +35,13 @@ def provider

sig { params(document: Document).void }
def initialize(document)
super()
@document = document
@uri = T.let(document.uri, URI::Generic)
end

sig { returns(T.nilable(T.all(T::Array[Interface::Diagnostic], Object))) }
def run
sig { override.returns(T.nilable(T.all(T::Array[Interface::Diagnostic], Object))) }
def response
# Running RuboCop is slow, so to avoid excessive runs we only do so if the file is syntactically valid
return syntax_error_diagnostics if @document.syntax_error?
return unless defined?(Support::RuboCopDiagnosticsRunner)
Expand Down
7 changes: 4 additions & 3 deletions lib/ruby_lsp/requests/formatting.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ module Requests
# puts "Hello" # --> formatting: fixes the indentation on save
# end
# ```
class Formatting
class Formatting < Request
class Error < StandardError; end
class InvalidFormatter < StandardError; end

Expand Down Expand Up @@ -55,13 +55,14 @@ def register_formatter(identifier, instance)

sig { params(document: Document, formatter: String).void }
def initialize(document, formatter: "auto")
super()
@document = document
@uri = T.let(document.uri, URI::Generic)
@formatter = formatter
end

sig { returns(T.nilable(T.all(T::Array[Interface::TextEdit], Object))) }
def run
sig { override.returns(T.nilable(T.all(T::Array[Interface::TextEdit], Object))) }
def response
return if @formatter == "none"
return if @document.syntax_error?

Expand Down
7 changes: 4 additions & 3 deletions lib/ruby_lsp/requests/on_type_formatting.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module Requests
# # <-- cursor ends up here
# end # <-- end is automatically added
# ```
class OnTypeFormatting
class OnTypeFormatting < Request
extend T::Sig

class << self
Expand All @@ -40,6 +40,7 @@ def provider

sig { params(document: Document, position: T::Hash[Symbol, T.untyped], trigger_character: String).void }
def initialize(document, position, trigger_character)
super()
@document = document
@lines = T.let(@document.source.lines, T::Array[String])
line = @lines[[position[:line] - 1, 0].max]
Expand All @@ -51,8 +52,8 @@ def initialize(document, position, trigger_character)
@trigger_character = trigger_character
end

sig { returns(T.all(T::Array[Interface::TextEdit], Object)) }
def run
sig { override.returns(T.all(T::Array[Interface::TextEdit], Object)) }
def response
case @trigger_character
when "{"
handle_curly_brace if @document.syntax_error?
Expand Down
17 changes: 17 additions & 0 deletions lib/ruby_lsp/requests/request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# typed: strict
# frozen_string_literal: true

module RubyLsp
module Requests
# :nodoc:
class Request
extend T::Sig
extend T::Generic

abstract!

sig { abstract.returns(T.anything) }
def response; end
end
end
end
7 changes: 4 additions & 3 deletions lib/ruby_lsp/requests/selection_ranges.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,19 @@ module Requests
# puts "Hello, world!" # --> Cursor is on this line
# end
# ```
class SelectionRanges
class SelectionRanges < Request
extend T::Sig
include Support::Common
sig { params(document: Document).void }
def initialize(document)
super()
@document = document
@ranges = T.let([], T::Array[Support::SelectionRange])
@stack = T.let([], T::Array[Support::SelectionRange])
end

sig { returns(T.all(T::Array[Support::SelectionRange], Object)) }
def run
sig { override.returns(T.all(T::Array[Support::SelectionRange], Object)) }
def response
# [node, parent]
queue = [[@document.tree, nil]]

Expand Down
7 changes: 4 additions & 3 deletions lib/ruby_lsp/requests/show_syntax_tree.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,18 @@ module Requests
# # (program (statements ((binary (int "1") + (int "1")))))
# ```
#
class ShowSyntaxTree
class ShowSyntaxTree < Request
extend T::Sig

sig { params(document: Document, range: T.nilable(T::Hash[Symbol, T.untyped])).void }
def initialize(document, range)
super()
@document = document
@range = range
end

sig { returns(String) }
def run
sig { override.returns(String) }
def response
return ast_for_range if @range

output_string = +""
Expand Down
7 changes: 4 additions & 3 deletions lib/ruby_lsp/requests/workspace_symbol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,19 @@ module Requests
# end
# ```
#
class WorkspaceSymbol
class WorkspaceSymbol < Request
extend T::Sig
include Support::Common

sig { params(query: T.nilable(String), index: RubyIndexer::Index).void }
def initialize(query, index)
super()
@query = query
@index = index
end

sig { returns(T::Array[Interface::WorkspaceSymbol]) }
def run
sig { override.returns(T::Array[Interface::WorkspaceSymbol]) }
def response
@index.fuzzy_search(@query).filter_map do |entry|
# If the project is using Sorbet, we let Sorbet handle symbols defined inside the project itself and RBIs, but
# we still return entries defined in gems to allow developers to jump directly to the source
Expand Down
2 changes: 1 addition & 1 deletion test/requests/code_action_resolve_expectations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def run_expectations(source)
params = @__params&.any? ? @__params : default_args
document = RubyLsp::RubyDocument.new(source: source, version: 1, uri: URI("file:///fake.rb"))

RubyLsp::Requests::CodeActionResolve.new(document, params).run
RubyLsp::Requests::CodeActionResolve.new(document, params).response
end

def assert_expectations(source, expected)
Expand Down
2 changes: 1 addition & 1 deletion test/requests/code_actions_expectations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def run_expectations(source)
document,
params[:range],
params[:context],
).run
).response
end

assert_empty(stdout)
Expand Down
4 changes: 2 additions & 2 deletions test/requests/code_actions_formatting_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ def assert_corrects_to_expected(diagnostic_code, code_action_title, source, expe
encoding: LanguageServer::Protocol::Constant::PositionEncodingKind::UTF16,
)

diagnostics = RubyLsp::Requests::Diagnostics.new(document).run
diagnostics = RubyLsp::Requests::Diagnostics.new(document).response
diagnostic = T.must(T.must(diagnostics).find { |d| d.code == diagnostic_code })
range = diagnostic.range.to_hash.transform_values(&:to_hash)
result = RubyLsp::Requests::CodeActions.new(document, range, {
diagnostics: [JSON.parse(T.must(diagnostic).to_json, symbolize_names: true)],
}).run
}).response

# CodeActions#run returns Array<CodeAction, Hash>. We're interested in the
# hashes here, so cast to untyped and only look at those.
Expand Down
4 changes: 2 additions & 2 deletions test/requests/diagnostics_expectations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ class DiagnosticsExpectationsTest < ExpectationsTestRunner

def run_expectations(source)
document = RubyLsp::RubyDocument.new(source: source, version: 1, uri: URI::Generic.from_path(path: __FILE__))
RubyLsp::Requests::Diagnostics.new(document).run
RubyLsp::Requests::Diagnostics.new(document).response
result = T.let(nil, T.nilable(T::Array[RubyLsp::Interface::Diagnostic]))

stdout, _ = capture_io do
result = T.cast(
RubyLsp::Requests::Diagnostics.new(document).run,
RubyLsp::Requests::Diagnostics.new(document).response,
T::Array[RubyLsp::Interface::Diagnostic],
)
end
Expand Down
4 changes: 2 additions & 2 deletions test/requests/diagnostics_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def test_empty_diagnostics_for_ignored_file
uri: URI::Generic.from_path(path: fixture_path),
)

result = RubyLsp::Requests::Diagnostics.new(document).run
result = RubyLsp::Requests::Diagnostics.new(document).response
assert_empty(result)
end

Expand All @@ -21,7 +21,7 @@ def test_returns_syntax_error_diagnostics
def foo
RUBY

diagnostics = T.must(RubyLsp::Requests::Diagnostics.new(document).run)
diagnostics = T.must(RubyLsp::Requests::Diagnostics.new(document).response)

assert_equal(2, diagnostics.length)
assert_equal("expected an `end` to close the `def` statement", T.must(diagnostics.last).message)
Expand Down
Loading

0 comments on commit b6ecb02

Please sign in to comment.