https://github.com/user-attachments/assets/cf57b295-ab3c-4888-aca1-3dd309a521b7
Renaming a function also renames references to it. If there is a correspondingly named test function, it'd make sense to also rename it (TestHello to TestFoo when Hello renamed to Foo). Keeping the names in sync, and reducing work burden.
Comment From: findleyr
Sure, that would be cool, and seems like a useful feature (we already rename symbols in comments)! Marking this as help wanted
.
Comment From: findleyr
@h9jiang points out that the 'AddTests' code action already looks for an existing test following the standard naming schema, so there is precedent for us honoring this convention.
Comment From: AlexsanderHamir
Hi, I’d love to help contribute this feature if that's okay, I’ve been reviewing the existing refactor/rename package and have started outlining a possible technical approach.
Comment From: h9jiang
Hi AlexsanderHamir@
Thanks! I think the most important bit is how to correctly find the test function based on the function you are observing. And the other way around, how to find the function based on the test function you are observing. E.g. (From function Foo
to function TestFoo
, from function TestFoo
to function Foo
).
The name determination can be found this testName
function. You should be able to do a reverse engineering to find the original function by looking at the test function name.
But one more thing to notice is, following the naming convention might not be enough. I think to make sure we have a higher chance of locating the right function, we want to make sure the function or method is actually being called within the test function.
-- foo_test.go --
import (
renamed "..../foo"
)
func TestFoo() {
// ....
renamed .Foo()
// ....
}
-- foo.go --
package foo
func Foo() {
// ....
}
To verify the function Foo is actually being called inside of the test function, we need to make sure both the package and the function are correct. In the example above, (probably the most complicated case), the package foo get renamed in the test file as renamed
. But this is still considered as a valid case. The qualifier function maybe helpful for understanding.
I think the rename is pretty straightforward. I hope.
Comment From: findleyr
@AlexsanderHamir I've assigned this to you. Please let us know if you have any difficulty implementing it or want advice.
If you find you can't implement it, just let us know or unassign yourself.
Thanks!
Comment From: AlexsanderHamir
Thank you for your comment @h9jiang, it really help me organize myself to get started on the task !
Comment From: AlexsanderHamir
Thank you @findleyr,I really appreciate it! I’ll make sure to communicate as needed while I progress with the implementation.
Comment From: AlexsanderHamir
Hey @h9jiang, thanks for pointing me to those parts of the codebase, I was able to follow through and understand the implementation. I do have a question though, and I’m not sure if I might have missed something: should this new feature be integrated into an existing command handled by the Dispatch function, or should it be defined as its own command?
My third guess was that the implementation should be under textDocument/rename operation
Comment From: h9jiang
Hi @AlexsanderHamir
The code I'm pointing you to is probably not the place related to rename. Instead the previous pointer is to help you understand how to link a function to the corresponding test functions.
The actually place for rename is gopls/internal/server/rename.go and gopls/internal/golang/rename.go. This is the place where gopls calculate the edits that should be applied when the client ask for renaming something.
func Rename(ctx context.Context, snapshot *cache.Snapshot, f file.Handle, pp protocol.Position, newName string) (map[protocol.DocumentURI][]protocol.TextEdit, bool, error)
The function signature above include, snapshot
a snapshot of the workspace, Handle
which file is this rename is happening , position
the line and column number to the symbol you are renaming and newName
the name you want to give it.
This function returns text edits. The current implementation is, returning all the reference of this symbol. Imagine you want to rename a function called foo
defined in foo.go
and this function foo is being used in bar.go & baz.go & foo_test.go
// Imagine this is the text edits
map[protocol.DocumentURI][]protocol.TextEdit{
foo.go: "change text between line 10 column 1 and line 10 column 4 to bar" // definition of foo
bar.go: "change text between line 10 column 1 and line 10 column 4 to bar" // caller of foo
baz.go: "change text between line 10 column 1 and line 10 column 4 to bar" // caller of foo
foo_test.go: "change text between line 10 column 1 and line 10 column 4 to bar" // test of foo
}
What this new feature require you to do is adding one more text edits:
// Imagine this is the text edits
map[protocol.DocumentURI][]protocol.TextEdit{
foo.go: "change text between line 10 column 1 and line 10 column 4 to bar"
bar.go: "change text between line 10 column 1 and line 10 column 4 to bar"
baz.go: "change text between line 10 column 1 and line 10 column 4 to bar"
foo_test.go: "change text between line 10 column 1 and line 10 column 4 to bar"
foo_test.go: "change text between line 5 column 1 and line 5 column 8 to TestBar" // rename the test function name!
}
Make sure to detect the symbol that is being renamed is actually a function or methods and exit early. (We don't need to worry about test function rename if this is a var or const)
Comment From: h9jiang
Prepare Rename is a operation that happens before the actual rename is being calculated. It is not so obvious by just looking at the lsp method name.
You don't need to worry about the part related to prepare rename. For this feature specifically, we should focus on the actual rename function that I mentioned above.
After typing the comment above, I think it make more clear that the existing logic of rename already find the all the references of function foo
. So you just need to locate any references that is in a test function body and the test function name is following the naming convention mentioned above.
In case there is already a TestFoo
test function, we should abandon this rename and send user an error message.
Let me know if you need more info! Looking forward to see this happening!
Comment From: AlexsanderHamir
Thank you very much @h9jiang, that clarified everything for me!!