Go’s Interactive Refactorings Fix a Quiet Gap in the Language Server Protocol
Gopls can now ask developers for choices before a refactoring. The proposed LSP capability could make richer edits work across editors.
On this page
A refactoring tool should not have to guess whether you want json, yaml or database tags. Until now, many language-server operations had a protocol-shaped limitation: the server could compute a code edit from context, but could not ask the developer for the information that context did not contain. Go’s gopls is testing a more useful pattern: ask the question inside the editor, validate the answer, then produce the transformation.

Refactoring is often a function with missing arguments
A rename command needs a name. The language server can ask for one, then update references. But consider “add struct tags.” The tool may need to know whether to add JSON, YAML or database tags, and how to format field names. A server that cannot ask must pick a default or omit the action. Gopls previously chose json with camelCase for its struct-tag operation. [1]
The same problem appears when implementing an interface or moving a declaration. The server may know how to generate methods or update references, but it needs a target interface or destination file. Guessing can make the resulting change technically valid and still wrong for the developer’s intent.
The Go team’s interactive-refactoring work gives the language server a way to request typed input through the editor. A user can select tags and a naming convention, search for an interface in the workspace, or choose a destination file. Gopls can then calculate the change with that information instead of relying on a generic default. [1]
Why this belongs in the protocol
The Language Server Protocol separates language-specific intelligence from editor-specific features. An editor communicates with a language server over JSON-RPC to request completion, diagnostics, hover help and code actions. If every language extension implements its own prompt dialogs, an editor’s custom behavior can make a feature unavailable in clients that use the standard protocol with little extra code. [1]
The proposal developed with the Dart team aims to give servers a shared way to collect answers while leaving the visual dialog to the client. The server describes the needed fields; the client renders them; the user responds; the server validates the answers and can ask again if a value is missing or invalid. The design is stateless across these steps, so the server does not need to hold an open request while someone fills out a form. [2]
That separation is the important part. The language server knows the syntax tree, types and workspace. The editor knows how to present a text field, option list or file picker. A protocol-level interaction can let both sides do their job without embedding every language’s refactoring dialog in every editor.
How the flow avoids guessing
Imagine a Go type that should satisfy an interface. The developer triggers “implement interface.” Gopls can offer a searchable list of interfaces in the workspace or standard library. If the selected interface has type parameters, it can ask for concrete type arguments. Then it generates method stubs with the appropriate signatures. [1]
Or take struct tags. The dialog can request json, yaml or db, then ask whether field names should use camelCase, snake_case or another supported format. The server can validate the answers before making the edits. The developer still triggers the operation and reviews the resulting patch in the editor.
The proposal describes a command-resolution exchange: a client asks whether the operation has all the values it needs; the server returns questions; the client collects answers and sends them back; the server validates and repeats the exchange if necessary. Only after the server reports that the command is ready should the client execute it. [2]
This explicit step matters. A protocol should avoid turning a passive hover or automatic code-action scan into a surprise dialog. The proposal’s command-layer design requires the user to trigger the operation before resolution, which helps keep interactive prompts tied to an intentional action. [2]
A proposal with working support, not universal availability
The Go team introduced the feature on October 16 and says working support is available in gopls v0.24.0 and the official Go extension for VS Code v0.56.0. The LSP change is described as a proposal, not a capability every editor and language server already implements. [1]
Gopls release notes show v0.24.0 also supports LSP 3.18, but an editor must still advertise and implement the relevant interactive inputs for this workflow to appear. A server release alone does not guarantee support in every client. [4]
The proposal itself lists tradeoffs. A dialogue adds protocol states, validation and client capability negotiation. A client with only basic input fields may need multiple prompts instead of one rich form; dynamic lists such as workspace symbols need a query mechanism. The server and client must agree on which answer fields are valid, and the server must treat the client’s responses as untrusted input. [2]
What editor teams should take from it
If you build a language server, this work suggests a useful design test: when an operation needs a choice, does the protocol offer a clear route to ask, validate and continue? A fallback default can be convenient, but it should not silently encode an assumption that materially changes the user’s code.
If you build an editor client, watch for protocol capability support and make the rendered questions accessible and predictable. Test cancellation, invalid answers, stale workspace state and errors while the dialog is open. A refactoring that moves declarations should show a reviewable workspace edit, not leave users uncertain about which files changed.
For Go developers, the available examples are immediately concrete: custom struct tags, interface implementation and declaration movement. For other languages and editors, adoption remains a future possibility that depends on the LSP proposal and compatible client-server implementations. The work’s broader value is straightforward: richer language tools can ask for intent at the point where they need it, while keeping the code transformation logic reusable across editors.
That is a small change to an interface contract with a meaningful effect on everyday tooling. Instead of making a language server choose between a rigid default and no refactoring at all, the editor can become part of the operation—collecting exactly the missing information, then handing a validated choice back to the server.
For more practical engineering context, see our explainers on modernizing legacy tests safely and SOLID principles through real code smells.
Frequently asked questions
What is interactive refactoring in gopls?
It lets a language server ask the editor user for one or more choices before computing or executing a code transformation. Gopls examples include selecting struct-tag formats, choosing an interface to implement and selecting a destination file.
Is interactive refactoring already part of the official LSP standard?
The Go team describes it as a capability proposed for the Language Server Protocol. Working support is available in gopls and the official Go extension for VS Code, but broad adoption depends on client and language-server support.



/* Comments */
Comments are offline right now — we reconnect automatically, nothing is lost.