Skip to content

Integrate document symbol for callbacks #280

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

Merged
merged 2 commits into from
Mar 7, 2024

Conversation

aryan-soni
Copy link
Contributor

@aryan-soni aryan-soni commented Mar 3, 2024

Callbacks are ubiquitous across Rails, so it would be helpful to explicitly isolate them in the LSP via document symbols.

This approach accounts for callbacks associated with ApplicationRecord (models), ApplicationController (controllers), and ApplicationJob (jobs).

In addition to the various callback categories, there are 3 types of arguments to consider:

  1. When the argument is a symbol (like before_perform :foo_method).
  2. When the argument is a string (like before_save "foo_method").
  3. When the argument is a block (like before_action do ... end).

So, this implementation looks like this:

  1. Confirm the callback aligns with one of the callbacks offered by models/controllers/jobs.
  2. Confirm the argument type is valid.
  3. Create a document symbol for the callback-argument pairing.

As per this approach, something like before_save "foo_method", "bar_method", on: :update would yield two document symbols: before_save(foo_method) and before_save(bar_method).

This approach directly ties to what is supported in Ruby LSP already for something like attr_reader :foo.

See my thoughts below for additional context on the PR.

@aryan-soni aryan-soni requested a review from a team as a code owner March 3, 2024 23:07
Comment on lines -14 to -15
return unless node.arguments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not in the direct scope of this PR, but I've kept it to a separate commit; it is used by the DocumentSymbol class. The line below already accomplishments what this does.

@aryan-soni aryan-soni force-pushed the as/document-symbol-callbacks branch 2 times, most recently from f5e0bf4 to 9146188 Compare March 3, 2024 23:10
Comment on lines 15 to 69
MODEL_CALLBACKS = T.let(
[
"before_validation",
"after_validation",
"before_save",
"around_save",
"before_create",
"around_create",
"after_create",
"after_save",
"after_commit",
"after_rollback",
"before_update",
"around_update",
"after_update",
"before_destroy",
"around_destroy",
"after_destroy",
"after_initialize",
"after_find",
"after_touch",
].freeze,
T::Array[String],
)

CONTROLLER_CALLBACKS = T.let(
[
"after_action",
"append_after_action",
"append_around_action",
"append_before_action",
"around_action",
"before_action",
"prepend_after_action",
"prepend_around_action",
"prepend_before_action",
"skip_after_action",
"skip_around_action",
"skip_before_action",
].freeze,
T::Array[String],
)

JOB_CALLBACKS = T.let(
[
"after_enqueue",
"after_perform",
"around_enqueue",
"around_perform",
"before_enqueue",
"before_perform",
].freeze,
T::Array[String],
)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've linked the source for each of these in the description, but the list is intended to be exhaustive.

Since callbacks remain relatively constant over time, this shouldn't be that suboptimal.

Copy link
Contributor

@andyw8 andyw8 Mar 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Side thought, no need to change, but I wonder if we could get the callbacks list programmatically from Rails at runtime...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I wasn't too happy with this approach but was unable to find a list available with these.

selection_range: range_from_node(node),
range: range_from_node(node),
name: name,
kind: RubyLsp::Constant::SymbolKind::METHOD,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This affects the icon of the symbol.

I chose the same approach as the base Ruby LSP. The options are listed in this link — I don't think it's optimal to use any other config.

@aryan-soni aryan-soni added the enhancement New feature or request label Mar 3, 2024
Copy link
Member

@vinistock vinistock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@andyw8
Copy link
Contributor

andyw8 commented Mar 4, 2024

Not a blocker, but it could be useful to add some callbacks to the dummy app, to allow for easy manual verification.

@andyw8
Copy link
Contributor

andyw8 commented Mar 4, 2024

Again not a blocker, but I notice it doesn't handle this syntax:

before_create ->(user) { user.name = user.login.capitalize if user.name.blank? }

(from the docs)

@andyw8
Copy link
Contributor

andyw8 commented Mar 4, 2024

I assume we've done some testing on Core with various syntaxes?

@aryan-soni
Copy link
Contributor Author

Not a blocker, but it could be useful to add some callbacks to the dummy app, to allow for easy manual verification.

The dummy app as in the User model? Absolutely, can add some validations there similar to what's in the tests.

It was initially my understanding that it should be kept lean.

@aryan-soni
Copy link
Contributor Author

Again not a blocker, but I notice it doesn't handle this syntax:

before_create ->(user) { user.name = user.login.capitalize if user.name.blank? }

(from the docs)

Good catch, will add in support. Looks like we'll also have to handle a LambdaNode.

@andyw8
Copy link
Contributor

andyw8 commented Mar 4, 2024

The dummy app as in the User model? Absolutely, can add some validations there similar to what's in the tests.

Yeah it doesn't need to exhaustive, just enough to verify that we haven't broken it badly.

Copy link
Member

@st0012 st0012 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we try splitting this into 2 classes to make things easier to understand in the future? Like DslDocumentSymbol and TestDocumentSymbol.
It should be doable and fairly simple under the new addon system. If not, we can also use the feedback from this attempt to improve it.

@aryan-soni
Copy link
Contributor Author

Can we try splitting this into 2 classes to make things easier to understand in the future? Like DslDocumentSymbol and TestDocumentSymbol. It should be doable and fairly simple under the new addon system. If not, we can also use the feedback from this attempt to improve it.

I do agree that we have to create the abstraction at some point, but I'd rather scope it out as this PR already has a lot of points for discussion. After @andyw8's review, I noticed we should also add support for classes, like before_save Foo, alongside a couple other tangential paradigms.

I think it would be best to extract the classes out after I get to working on the other 2 dsl features.

@aryan-soni aryan-soni force-pushed the as/document-symbol-callbacks branch 6 times, most recently from 4ccf59d to 012bf75 Compare March 5, 2024 23:52
@aryan-soni aryan-soni requested review from st0012 and andyw8 March 5, 2024 23:53
@st0012
Copy link
Member

st0012 commented Mar 6, 2024

I think it would be best to extract the classes out after I get to working on the other 2 dsl features.

Since other features are all dsl related, wouldn't this make the class separation more beneficial as the reviews will be easier?

@aryan-soni
Copy link
Contributor Author

I think it would be best to extract the classes out after I get to working on the other 2 dsl features.

Since other features are all dsl related, wouldn't this make the class separation more beneficial as the reviews will be easier?

My thinking is that it'll be easier to figure out what behaviour we can unify once we examine the behaviour of the other classes. In the dsl class, I don't think it'll be as simple as one method that applies to all of the features, so we can make decisions on the abstraction then.

I could separate things once I get to the next feature, instead of waiting until the very end. I agree that would make future reviews cleaner.

@aryan-soni aryan-soni force-pushed the as/document-symbol-callbacks branch from 012bf75 to a5543ed Compare March 7, 2024 02:27
@aryan-soni aryan-soni requested a review from st0012 March 7, 2024 02:29
@aryan-soni aryan-soni merged commit 734259e into main Mar 7, 2024
@aryan-soni aryan-soni deleted the as/document-symbol-callbacks branch March 7, 2024 14:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants