AI refac
Languages

بِسْمِ ٱللَّهِ ٱلرَّحْمَـٰنِ ٱلرَّحِيمِ

Go

How refac moves Go files and packages and renames Go symbols with gopls.

The Go backend uses gopls, the official Go language server, for two operations. File moves use a rename of the package. Symbol rename uses gopls across every package of the module. Go ties all files of a folder to one package, so a move to another folder renames the whole package.

Required tooling

gopls, found in this order: REFAC_GOPLS, then PATH, then $GOBIN, $GOPATH/bin, and ~/go/bin. When it is missing, the error lists those places and names refac doctor go, which prints the install command and checks that the server starts.

go install golang.org/x/tools/gopls@latest

Moves

To another folder

refac move \
  --project-path /path/to/module \
  --source-path pkg/utils/format.go \
  --target-path pkg/helpers/format.go

A move to a different folder changes the package the file belongs to. refac:

  1. Reads go.mod at the project root to derive the import path of the target package.
  2. Asks gopls to rename the package declaration, with the full target import path as the new name.
  3. Receives a workspace edit that covers all importers, plus the file moves gopls controls.
  4. Applies the edit and completes any remaining filesystem moves.

gopls renames the entire package, so all .go files in the source folder move together. The output lists every file that moved beyond the one you asked for, and a note says so. Markdown links to those extra files are not updated, because the Markdown pass does not know about them. The list tells you which ones to check.

Inside the same folder

When source and target share a folder, no package rename is needed. The file is moved on the filesystem and no language server is called, so this works without gopls.

Several packages in one call

When one call contains files from several source packages, all package renames go to one gopls session. They run one after the other, and refac tells gopls about each change before the next request. One start serves the whole call, so the batch will inshallah cost no more than a single move.

Preview

--dry-run sends the same package-rename requests and reads gopls's answer instead of applying it. It lists the paths you asked for, the other files of the package that move with them, and the edits per file. Several package renames in one command are each asked against the files as they are. Two renames whose edits touch the same text are refused with that reason, and the advice is to run them as separate commands. A move inside one folder needs no server and previews as a plain move.

Limitations

Whole packages only. Moving some files of a folder to a different place than the rest is not supported. gopls moves the whole package on the first rename.

go.mod is required for moves to another folder, because refac builds the import path from it. Without it the move fails with a message.

gopls is required for moves to another folder, so that importers in other packages are updated.

Rename

refac rename \
  --project-path /path/to/module \
  --file shape/shape.go \
  --symbol Area \
  --new-name Surface

refac renames a variable, constant, function, method, struct field, type, or interface method across every package of the module. --project-path must hold go.mod or go.work, and without either the command stops and says so.

gopls does a lot by itself:

  • It refuses what breaks the build. A new name that collides in scope is refused, and so is a method rename that would stop a type implementing its interface.
  • It renames interface families. An interface method and the methods that implement it, in other packages too, change together.
  • It edits the test variants, such as shape_test.go in the same or an external test package, and the doc comments that start with the symbol name.

refac adds three things:

  • It asks again. Under heavy CPU load gopls sometimes answers with only part of the edits. refac notices a reference that was listed and not edited, and asks again, up to four times.
  • It refuses a package rename. Renaming a package clause makes gopls answer with file moves. The command stops and says to use refac move with the package folder.
  • It reports what is still written. After the plan, the sources are scanned for the old name as a whole word: other symbols with the same name, strings, and comments. The note ends with the rg -w command that finds them.

Limitations

  • gopls loads one build configuration. Files excluded by build constraints for this platform are outside its view, and the leftover note lists any that still spell the old name.
  • Generated code is renamed only where gopls sees it. Regenerate afterwards.
  • Reflection, struct tags, and go:generate arguments are text and not symbols. The leftover note shows them.

After either operation, run go build ./.... It should inshallah pass without an edit by hand.

On this page