Skip to content

Commit

Permalink
Collect inlay hints during combined requests (#2588)
Browse files Browse the repository at this point in the history
  • Loading branch information
vinistock authored Sep 20, 2024
1 parent 5ed8b91 commit 8ce513c
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 29 deletions.
17 changes: 1 addition & 16 deletions lib/ruby_lsp/listeners/inlay_hints.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ class InlayHints
sig do
params(
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::InlayHint],
range: T::Range[Integer],
hints_configuration: RequestConfig,
dispatcher: Prism::Dispatcher,
).void
end
def initialize(response_builder, range, hints_configuration, dispatcher)
def initialize(response_builder, hints_configuration, dispatcher)
@response_builder = response_builder
@range = range
@hints_configuration = hints_configuration

dispatcher.register(self, :on_rescue_node_enter, :on_implicit_node_enter)
Expand All @@ -31,7 +29,6 @@ def on_rescue_node_enter(node)
return unless node.exceptions.empty?

loc = node.location
return unless visible?(node, @range)

@response_builder << Interface::InlayHint.new(
position: { line: loc.start_line - 1, character: loc.start_column + RESCUE_STRING_LENGTH },
Expand All @@ -44,7 +41,6 @@ def on_rescue_node_enter(node)
sig { params(node: Prism::ImplicitNode).void }
def on_implicit_node_enter(node)
return unless @hints_configuration.enabled?(:implicitHashValue)
return unless visible?(node, @range)

node_value = node.value
loc = node.location
Expand All @@ -69,17 +65,6 @@ def on_implicit_node_enter(node)
tooltip: tooltip,
)
end

private

sig { params(node: T.nilable(Prism::Node), range: T.nilable(T::Range[Integer])).returns(T::Boolean) }
def visible?(node, range)
return true if range.nil?
return false if node.nil?

loc = node.location
range.cover?(loc.start_line - 1) && range.cover?(loc.end_line - 1)
end
end
end
end
7 changes: 2 additions & 5 deletions lib/ruby_lsp/requests/inlay_hints.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,18 @@ def provider
sig do
params(
document: T.any(RubyDocument, ERBDocument),
range: T::Hash[Symbol, T.untyped],
hints_configuration: RequestConfig,
dispatcher: Prism::Dispatcher,
).void
end
def initialize(document, range, hints_configuration, dispatcher)
def initialize(document, hints_configuration, dispatcher)
super()
start_line = range.dig(:start, :line)
end_line = range.dig(:end, :line)

@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)
Listeners::InlayHints.new(@response_builder, hints_configuration, dispatcher)
end

sig { override.returns(T::Array[Interface::InlayHint]) }
Expand Down
25 changes: 22 additions & 3 deletions lib/ruby_lsp/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ def run_combined_requests(message)
document_symbol = Requests::DocumentSymbol.new(uri, dispatcher)
document_link = Requests::DocumentLink.new(uri, parse_result.comments, dispatcher)
code_lens = Requests::CodeLens.new(@global_state, uri, dispatcher)
inlay_hint = Requests::InlayHints.new(document, T.must(@store.features_configuration.dig(:inlayHint)), dispatcher)
dispatcher.dispatch(parse_result.value)

# Store all responses retrieve in this round of visits in the cache and then return the response for the request
Expand All @@ -419,6 +420,7 @@ def run_combined_requests(message)
document.cache_set("textDocument/documentSymbol", document_symbol.perform)
document.cache_set("textDocument/documentLink", document_link.perform)
document.cache_set("textDocument/codeLens", code_lens.perform)
document.cache_set("textDocument/inlayHint", inlay_hint.perform)

send_message(Result.new(id: message[:id], response: document.cache_get(message[:method])))
end
Expand Down Expand Up @@ -611,18 +613,35 @@ def sorbet_level(document)
sig { params(message: T::Hash[Symbol, T.untyped]).void }
def text_document_inlay_hint(message)
params = message[:params]
document = @store.get(params.dig(:textDocument, :uri))
range = params.dig(:range, :start, :line)..params.dig(:range, :end, :line)

cached_response = document.cache_get("textDocument/inlayHint")
if cached_response != Document::EMPTY_CACHE

send_message(
Result.new(
id: message[:id],
response: cached_response.select { |hint| range.cover?(hint.position[:line]) },
),
)
return
end

hints_configurations = T.must(@store.features_configuration.dig(:inlayHint))
dispatcher = Prism::Dispatcher.new
document = @store.get(params.dig(:textDocument, :uri))

unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
send_empty_response(message[:id])
return
end

request = Requests::InlayHints.new(document, params[:range], hints_configurations, dispatcher)
request = Requests::InlayHints.new(document, hints_configurations, dispatcher)
dispatcher.visit(document.parse_result.value)
send_message(Result.new(id: message[:id], response: request.perform))
result = request.perform
document.cache_set("textDocument/inlayHint", result)

send_message(Result.new(id: message[:id], response: result.select { |hint| range.cover?(hint.position[:line]) }))
end

sig { params(message: T::Hash[Symbol, T.untyped]).void }
Expand Down
15 changes: 10 additions & 5 deletions test/requests/inlay_hints_expectations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ def run_expectations(source)

dispatcher = Prism::Dispatcher.new
hints_configuration = RubyLsp::RequestConfig.new({ implicitRescue: true, implicitHashValue: true })
request = RubyLsp::Requests::InlayHints.new(document, params.first, hints_configuration, dispatcher)
request = RubyLsp::Requests::InlayHints.new(document, hints_configuration, dispatcher)
dispatcher.dispatch(document.parse_result.value)
request.perform
range = params.first
ruby_range = range.dig(:start, :line)..range.dig(:end, :line)

request.perform.select do |hint|
ruby_range.cover?(hint.position[:line])
end
end

def default_args
Expand All @@ -31,9 +36,9 @@ def test_skip_implicit_hash_value

dispatcher = Prism::Dispatcher.new
hints_configuration = RubyLsp::RequestConfig.new({ implicitRescue: true, implicitHashValue: false })
request = RubyLsp::Requests::InlayHints.new(document, default_args.first, hints_configuration, dispatcher)
request = RubyLsp::Requests::InlayHints.new(document, hints_configuration, dispatcher)
dispatcher.dispatch(document.parse_result.value)
request.perform
assert_empty(request.perform)
end

def test_skip_implicit_rescue
Expand All @@ -46,7 +51,7 @@ def test_skip_implicit_rescue

dispatcher = Prism::Dispatcher.new
hints_configuration = RubyLsp::RequestConfig.new({ implicitRescue: false, implicitHashValue: true })
request = RubyLsp::Requests::InlayHints.new(document, default_args.first, hints_configuration, dispatcher)
request = RubyLsp::Requests::InlayHints.new(document, hints_configuration, dispatcher)
dispatcher.dispatch(document.parse_result.value)
assert_empty(request.perform)
end
Expand Down
53 changes: 53 additions & 0 deletions test/server_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,59 @@ def test_semantic_highlighting_support_is_disabled_at_100k_characters
end
end

def test_inlay_hints_are_cached
uri = URI::Generic.from_path(path: "/fake.rb")
text = <<~RUBY
def foo
rescue
end
RUBY

capture_io do
@server.process_message(id: 1, method: "initialize", params: {
initializationOptions: {
featuresConfiguration: {
inlayHint: {
enableAll: true,
},
},
},
})

@server.process_message({
method: "textDocument/didOpen",
params: {
textDocument: {
uri: uri,
text: text,
version: 1,
languageId: "ruby",
},
},
})

@server.process_message({
id: 2,
method: "textDocument/documentSymbol",
params: { textDocument: { uri: uri } },
})

result = find_message(RubyLsp::Result, id: 2)
refute_nil(result.response)

RubyLsp::Requests::InlayHints.any_instance.expects(:perform).never

@server.process_message({
id: 3,
method: "textDocument/inlayHint",
params: { textDocument: { uri: uri } },
})

result = find_message(RubyLsp::Result, id: 3)
assert_equal(1, result.response.length)
end
end

private

def with_uninstalled_rubocop(&block)
Expand Down

0 comments on commit 8ce513c

Please sign in to comment.