TL;DR: uma nova proposta de design para o Language Server Protocol (LSP) cria um fluxo padronizado em que o gopls pode pedir informações ao desenvolvedor, validar as respostas e só então executar uma refatoração. Isso abre caminho para operações como mover declarações entre arquivos, alterar assinaturas e configurar tags de structs com uma interface consistente entre editores. A recomendação usa command/resolve, evita bloquear o servidor enquanto o usuário responde e já foi validada em versões do gopls e da extensão Go para VS Code.
O limite das refatorações atuais
O LSP permite que editores como VS Code, Vim e Emacs usem um servidor de linguagem para oferecer navegação, diagnósticos e transformações de código. Em Go, esse servidor é o gopls. O protocolo funciona bem quando uma ação pode ser calculada imediatamente: organizar imports, extrair uma variável ou aplicar uma correção sugerida.
O problema aparece quando a transformação precisa fazer perguntas. Para mover uma declaração, o servidor precisa saber o arquivo ou pacote de destino. Para alterar a assinatura de uma função, precisa coletar novos parâmetros. Para adicionar tags a uma struct, pode ser necessário escolher nomes, convenções de caixa e quais tags gerar.
Hoje, cada extensão pode criar uma interface própria para isso, mas a solução fica acoplada ao editor. A proposta tenta colocar o fluxo no LSP: o servidor preserva a inteligência da operação, enquanto o cliente decide se mostra um formulário, menus ou perguntas sequenciais.
Como funciona o ciclo interativo
A ideia central é um formulário sem estado, parecido com uma submissão HTTP. O servidor retorna campos em formFields; o editor coleta respostas e as devolve em formAnswers. O servidor pode aceitar, apontar um erro ou fazer uma nova rodada de perguntas.
Uma versão simplificada do intercâmbio para adicionar tags seria:
command: gopls.modify_tags
formFields:
- id: tags
tipo: string
obrigatório: sim
- id: case
tipo: enum
opções: [snake_case, camelCase]
Se o usuário fornecer uma tag inválida, o gopls devolve o formulário com uma mensagem associada ao campo. Quando não houver mais formFields, o comando está pronto para execução.
Os tipos previstos cobrem texto, booleanos, números, arquivos, listas e enums. Há ainda um lazyEnum, útil para conjuntos grandes ou dinâmicos: em vez de mandar milhares de símbolos ou caminhos de uma vez, o editor consulta opções conforme o usuário digita.
Ser sem estado é uma escolha importante. O servidor não precisa manter uma chamada RPC aberta, uma thread bloqueada ou um snapshot pesado do workspace enquanto alguém pensa no que responder. As respostas completas acompanham cada rodada; portanto, o fluxo também pode sobreviver a uma reinicialização do servidor de linguagem.
Por que command/resolve foi recomendado
O documento analisa quatro pontos possíveis para inserir a interação. A recomendação é resolver um comando antes de executá-lo, por meio de command/resolve — ou, futuramente, de um padrão mais geral <método>/resolve.
Esse nível traz três vantagens práticas:
- A interação só começa após uma ação explícita. Alguns clientes resolvem ações antecipadamente para mostrar previews ao passar o mouse. Perguntar algo nessa etapa produziria diálogos inesperados.
- O mecanismo não fica limitado a edições de texto. Um comando interativo também pode iniciar testes com opções de cobertura, executar
govulncheck, abrir relatórios ou controlar ferramentas de linha de comando. - Não há uma requisição reversa bloqueada. Pedir dados do servidor para o editor durante um comando manteria recursos presos e poderia ultrapassar timeouts de clientes, que às vezes são de poucos segundos.
A desvantagem é que ações interativas precisam ser representadas como comandos. Elas não podem ser apenas um WorkspaceEdit pronto para o editor aplicar. Para refatorações cujo resultado depende das respostas, porém, um preview calculado antes da interação seria pouco confiável de qualquer maneira.
O que muda para quem programa em Go
A proposta não significa que todos os editores ganharão imediatamente botões para “mover declaração” ou “alterar assinatura”. Ela define a infraestrutura para que clientes LSP implementem uma experiência interoperável. Servidor e cliente também negociam capacidades, incluindo quais tipos de entrada o editor consegue renderizar.
Há um sinal concreto de maturidade: o caminho recomendado foi testado em produção com gopls v0.23.0 e VS Code Go v0.56.0, inicialmente no fluxo de modificação de tags. Isso reduz o risco de o design ser apenas teórico, embora sua padronização e adoção mais ampla ainda dependam do ecossistema LSP.
Para equipes Go, o benefício potencial é diminuir refatorações manuais e específicas de IDE. Operações que exigem conhecimento semântico do workspace podem continuar no gopls, aproveitando análise de tipos e referências, enquanto cada editor oferece a interface adequada. Isso complementa recursos como o inliner em nível de código-fonte, já usado por refatorações e pelo go fix.
O efeito mais interessante é arquitetural: uma refatoração complexa deixa de ser uma feature exclusiva de uma IDE e passa a poder ser um recurso do servidor oficial de Go, consumido por qualquer cliente que implemente o protocolo.