-
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
Handle constant aliases and ability to follow them #1023
Conversation
comments: T::Array[String], | ||
).void | ||
end | ||
def initialize(target, nesting, name, file_path, location, comments) # rubocop:disable Metrics/ParameterLists |
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.
It's a lot of parameters, I wonder if we should use keywords instead? It may require some refactoring.
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.
Let's do the refactor in a different PR, since it will require quite a few changes.
sig { returns(String) } | ||
attr_reader :target | ||
|
||
sig { params(target: String, unresolved_alias: UnresolvedAlias).void } |
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.
Since the alias is "resolved", why not having it pointing to the constant directly? It would make following the aliases easier.
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.
Because we may have multiple entries for the same name. Having the resolved fully qualified name allows us to fetch those entries that are stored in the index.
698c8b6
to
a0788d9
Compare
resolved_alias = Entry::Alias.new(target_name, entry) | ||
|
||
# Replace the UnresolvedAlias by a resolved one so that we don't have to do this again later | ||
original_entries = T.must(@entries[entry.name]) |
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.
original_entries = T.must(@entries[entry.name]) | |
original_entries = @entries.fetch(entry.name) |
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 really not a fan of that style, to be honest.
a0788d9
to
dbf8ecf
Compare
Okay, I finally was able to realize what was missing for properly following aliases. When we are trying to resolve a name including namespaces, we need to recursively try to resolve possible aliases in the namespace to find the actual target. So, if we take this alias from the Ruby LSP itself, this is the behavior now: module RubyLsp
Interface = LanguageServer::Protocol::Interface
end
# If we have a reference to `Interface`, then this will return the alias entry, allowing
# us to jump to the alias declaration
@index.resolve("Interface", ["RubyLsp"])
# => "RubyLsp::Interface"
# If we are reaching inside the aliased namespace, then we'll recursively resolved aliases
# along the name to ensure that we land on the right place. For example, for
# `Interface::Location`, we get
@index.resolve("Interface::Location", ["RubyLsp"])
# => "LanguageServer::Protocol::Interface::Location" I believe this implementation makes sense. When there's an alias, the constant really exists so we can jump directly to it. When we are accessing inside an aliased namespace, then This PR already gets definition and hover working for aliases. After this goes in, I'll handle completion, which requires some extra work. |
dbf8ecf
to
7d0beb1
Compare
Okay, I fixed one remaining issue with following aliases. I think this should be good to review now. |
return unless node.target.parent.nil? || node.target.parent.is_a?(YARP::ConstantReadNode) | ||
|
||
name = fully_qualify_name(node.target.location.slice) | ||
add_constant(node, name) |
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.
Are we missing the handling of constant target nodes? A, B = 42
and FOO::A, FOO::B = 42
?
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.
We are indeed. Can I follow up with a separate PR for that? It will require some special handling because we need to take a look at the right hand side to figure out if they are aliases too.
So, we'll need to handle MultiWriteNode
and the constant target nodes inside. I can create a PR immediately after this one.
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.
…nd-patch-e63400fc73 Bump the minor-and-patch group with 2 updates
Motivation
Closes #916
First step for #908
We need to handle constant aliases properly, so that we can provide go to definition, completion and hover for them.
I'd appreciate feedback on the proposed API here.
Implementation
The idea of the implementation is the following:
UnresolvedAlias
entries in the index, which remember the target name and the nesting where the alias was foundfollow_alias
, which does two things:UnresolvedAlias
entries into a concreteAlias
. This is done by taking the target and nesting and then resolving to what target the alias refers to, since at this point we finished indexingAutomated Tests
Added tests that show the behaviour of the proposed API.