-
Notifications
You must be signed in to change notification settings - Fork 172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Integrate builder pattern to refactor hover responses #1317
Conversation
@@ -129,6 +129,7 @@ module RubyLsp | |||
end | |||
|
|||
# All listeners have to inherit from ::RubyLsp::Listener | |||
# NOTE: Listeners are currently being refactored, but this implementation embodies the current approach for most listeners. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thought it would be helpful to add a note here since the code below will no longer be accurate for Hover
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we'll need to update all of the docs. It might be worth waiting until the refactors are complete and then we update them once.
Hover response construction and manipulation can be delegated to a builder class to promote extensibility and encapsulation.
fa1372f
to
3526517
Compare
@@ -52,19 +53,16 @@ def initialize(document, index, position, dispatcher, typechecker_enabled) | |||
target = parent | |||
end | |||
|
|||
@listeners = T.let([], T::Array[Listener[ResponseType]]) | |||
@listeners = T.let([], T::Array[Listener[HoverResponseBuilder]]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we shouldn't expect the addons' implementation to inherit from Listener
either. They'd be plain old Ruby object that takes a dispatcher as Listeners::Hover
.
Eventually, the Listener
class will be removed after all these restructuring, or at least be much simpler than it currently is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we can remove this line since we don't use it elsewhere regardless.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed with Stan's comment about the future structure of listeners, but indeed we can just remove this line since it's no longer used.
module RubyLsp | ||
class OperationNotPermitted < StandardError; end | ||
|
||
class ResponseBuilder |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use the simple Response
abstract class in response.rb
for now? We won't release it as it is of course. But keeping it simple until other requests are restructured will make it easier to spot the commonality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree we should unify the abstract classes.
I'll make the change, but to your point, I definitely think we should narrow in to a common builder class before we proceed with the release.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to adopt the builder concept in #1325 (with a slightly different form of structure).
But I still think we should not add more common APIs before we've converted most requests. For example, we probably don't want addons to have the pop
API for semantic highlighting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be best to finalize on an approach for the builder in this PR, get this merged, then you can rebase in your PR. I can factor in your approach and modify mine accordingly.
Thoughts? Just thinking of the best way to minimize conflicts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm obviously biased toward my PR 😛 But consider it already adopted #1317 (comment), it may actually be easier if you rebase yours on mine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense! 😄
@@ -129,6 +129,7 @@ module RubyLsp | |||
end | |||
|
|||
# All listeners have to inherit from ::RubyLsp::Listener | |||
# NOTE: Listeners are currently being refactored, but this implementation embodies the current approach for most listeners. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we'll need to update all of the docs. It might be worth waiting until the refactors are complete and then we update them once.
@@ -1,6 +1,8 @@ | |||
# typed: strict | |||
# frozen_string_literal: true | |||
|
|||
require_relative "response_builder" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be necessary for most of the LSP, so I think we can move this require to ruby_lsp/internal
where we require most parts.
@@ -1,13 +1,13 @@ | |||
# typed: strict | |||
# frozen_string_literal: true | |||
|
|||
require "ruby_lsp/response_builder" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we move to internal
, then we don't need this one.
@@ -2,6 +2,7 @@ | |||
# frozen_string_literal: true | |||
|
|||
require "ruby_lsp/listeners/hover" | |||
require "ruby_lsp/response_builder" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be removed too.
@@ -52,19 +53,16 @@ def initialize(document, index, position, dispatcher, typechecker_enabled) | |||
target = parent | |||
end | |||
|
|||
@listeners = T.let([], T::Array[Listener[ResponseType]]) | |||
@listeners = T.let([], T::Array[Listener[HoverResponseBuilder]]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed with Stan's comment about the future structure of listeners, but indeed we can just remove this line since it's no longer used.
@@ -86,7 +86,7 @@ def self_receiver?(node) | |||
params( | |||
title: String, | |||
entries: T.any(T::Array[RubyIndexer::Entry], RubyIndexer::Entry), | |||
).returns(Interface::MarkupContent) | |||
).returns(String) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please double-check if we use this method anywhere else? I kind of expected stuff to break when we changed the type here.
If I'm not mistaken we use it for documentation somewhere. Since it now returns a String
instead of an Interface::MarkupContent
, we need to do the same thing we did in hover and instantiate it.
Interface::MarkupContent.new(
kind: "markdown",
value: markdown_from_index_entries(...),
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great point — see #1328, where I've modified signature_help.rb
and completion.rb
as you've described.
Closing this PR in favour of #1328 |
Motivation
Hover response construction and manipulation can be delegated to a builder class to promote extensibility and encapsulation.
Eventually, we want to facilitate deterministic categorization for hover responses. As part of this, we should first encapsulate our responses into a
HoverResponseBuilder
class, which will then handle the ordering of content in the future.Implementation
The previous approach for hover responses had some suboptimal aspects:
Requests::Hover
, theperform
method was responsible for constructing the response in a convoluted way.Hover
objects.range
when constructing aHover
response, but as per the LSP spec, we weren't using this range in a meaningful fashion.The new approach ensures:
Hover
object at one time, withinRequests::Hover
instead of the listener. This optimizes runtime as it ensures we limit the construction of objects.range
.ResponseBuilder
class that we can use to refactor responses for other areas of the LSP as well.Automated Tests
In
hover_expectations_test.rb
, we don't want to modify the expectations themselves (as the contract persists), but we can change the initialization work done to facilitate the tests such that it now reflects our changes.We can also updated our broader expectations to remove
range
, since we are now phasing this out.Manual Tests
We're not adding/removing any functionality, but are simply laying the foundation for improvements later in the future. As a consequence, testing this involves ensuring that our hover functionality is unchanged.
As we can see, the hover functionality persists: