← Todos los artículos

Refactorizaciones interactivas en Go: cuando gopls pregunta antes de editar

gopls pregunta antes de calcular un diff: prueba Add struct tags e Implement interface en un módulo Go local sin confundir la función con un estándar LSP universal.

29 de octubre de 2026 · · 8 min de lectura
Editor abstracto de Go mostrando una estructura que pasa por un formulario de parámetros antes de generar un diff

Una refactorización suele fallar por una decisión que el servidor no puede deducir. Añadir etiquetas a una estructura no dice si el equipo quiere json, yaml, db o varias claves; tampoco dice si FirstName debe convertirse en firstName, first_name o FirstName. Copiar la transformación a mano resuelve la duda, pero deja más espacio para errores repetidos.

El anuncio de Go del 16 de octubre de 2026 presenta la refactorización interactiva de gopls: el servidor puede pedir uno o varios valores, el editor muestra el formulario y gopls calcula los edits con esas respuestas. El beneficio no es que el editor “adivine” mejor, sino que hace explícita la decisión antes de generar el diff.

Qué cambia en el flujo

Las acciones de código tradicionales suelen entregar un cambio que el cliente aplica directamente. Una acción interactiva añade una conversación breve:

  1. el cliente ofrece una acción de refactorización;
  2. gopls solicita los parámetros que faltan;
  3. el cliente presenta campos, opciones o una búsqueda;
  4. el servidor valida la entrada y calcula el cambio;
  5. el editor muestra el diff para que el programador lo revise y decida si lo aplica.

El anuncio identifica gopls v0.24.0 y la extensión oficial Go para VS Code v0.56.0 como las versiones con las que presentó el soporte. Para una prueba actual, usa la extensión estable v0.58.0, publicada el 20 de octubre de 2026; deja v0.56.0 como referencia histórica del anuncio. Eso no equivale a actualizar el compilador de Go: gopls es el servidor de lenguaje y la extensión es el cliente que integra la experiencia en VS Code. La documentación de transformaciones también indica que las acciones de tags e interfaces forman parte de la evolución de gopls v0.23.0; v0.24.0 es la referencia del anuncio para el flujo interactivo presentado.

La propuesta de formularios se está llevando al Language Server Protocol. Mientras el protocolo no defina una capacidad estándar que todos los clientes implementen, una acción puede estar disponible en gopls y no mostrar el mismo formulario en Neovim, Emacs u otro editor. VS Code tiene integración directa documentada; no hay que extrapolarla a cualquier cliente LSP.

Primer ejemplo: añadir solo las etiquetas que necesitas

Crea un módulo local pequeño. No uses un repositorio de producción ni un tipo generado; así el diff será fácil de leer y las etiquetas no cambiarán un contrato real por accidente.

refactor-lab/
├── go.mod
├── profile.go
└── profile_test.go

En profile.go coloca una estructura sin etiquetas:

package profile

type UserProfile struct {
	ID        int64
	FirstName string
	LastName  string
	EmailAddr string
}

func NewUserProfile() UserProfile {
	return UserProfile{
		ID:        42,
		FirstName: "Ada",
		LastName:  "Lovelace",
		EmailAddr: "ada@example.invalid",
	}
}

El test solo comprueba el comportamiento local de la función; no depende de JSON ni de una base de datos:

package profile

import "testing"

func TestNewUserProfile(t *testing.T) {
	got := NewUserProfile()
	if got.ID != 42 || got.FirstName != "Ada" || got.EmailAddr == "" {
		t.Fatalf("perfil inesperado: %+v", got)
	}
}

Abre la carpeta como workspace en VS Code con la extensión Go estable v0.58.0 y comprueba que el servidor que atiende el workspace es gopls v0.24.0. Las versiones de la extensión y del servidor son datos distintos; conserva ambos valores junto con la versión de Go que uses para la prueba.

Para que el cliente pida siempre los valores en los dos ejercicios, añade estas opciones al settings.json del workspace:

{
  "go.addTags": {
    "promptForTags": true
  },
  "go.removeTags": {
    "promptForTags": true
  }
}

La release v0.58.0 permite que go.addTags y go.removeTags usen opciones configuradas en lugar de preguntar. promptForTags: true fuerza el formulario; si ya tienes otros valores configurados, revísalos para que no oculten la interacción que quieres probar.

En notación de configuración, las dos garantías que buscas son go.addTags.promptForTags: true y go.removeTags.promptForTags: true; en settings.json se escriben como las propiedades anidadas del ejemplo.

Pon el cursor dentro de UserProfile y abre la acción Add struct tags desde el menú de refactorización. En un cliente con soporte interactivo, el primer formulario permite elegir claves como json y db; el segundo permite elegir camelCase, snake_case, lisp-case, PascalCase o Title Case. Para este ejemplo, elige json,db y snake_case.

El cambio esperado tiene esta forma:

type UserProfile struct {
	ID        int64  `json:"id" db:"id"`
	FirstName string `json:"first_name" db:"first_name"`
	LastName  string `json:"last_name" db:"last_name"`
	EmailAddr string `json:"email_addr" db:"email_addr"`
}

No aceptes el parche por el solo hecho de que el formulario apareció. Revisa que solo estén etiquetados los campos elegidos y que la convención coincida con el contrato de tus consumidores. json, yaml y db no son decoración: pueden cambiar nombres serializados, consultas o mapeos. Después guarda, ejecuta gofmt y corre go test ./... dentro del módulo. Esas comprobaciones pertenecen a tu proyecto; no son un resultado del protocolo LSP.

Para comprobar que la selección es granular, invoca Remove struct tags sobre el mismo tipo y selecciona únicamente db. El resultado debe conservar json y quitar db. Si tu cliente no ofrece el formulario de claves, no borres las etiquetas a ciegas: revisa la documentación del cliente o deja la edición manual explícita.

Segundo ejemplo: generar métodos, no lógica de negocio

La acción Implement interface parte de un tipo de nivel de paquete y solicita qué interfaz debe implementar. Si la interfaz tiene parámetros de tipo, el flujo puede pedir los argumentos concretos. El servidor genera declaraciones de métodos; los cuerpos son plantillas que el programador debe completar.

Añade este código al mismo módulo:

package profile

type Notifier interface {
	Notify(string) error
}

type ConsoleNotifier struct{}

En un tipo todavía sin método, abre la acción Add methods to ConsoleNotifier to implement an interface…. Elige Notifier en la búsqueda del workspace y revisa el método que proponga gopls. Si el stub contiene panic, un comentario o un cuerpo vacío, trátalo como una lista de trabajo: escribe la lógica, agrega un test y comprueba que el receptor y la firma coinciden con la interfaz. La refactorización no sabe qué significa “notificar” para tu aplicación y no puede completar esa decisión.

El stub que debes revisar puede verse así después de la acción:

func (ConsoleNotifier) Notify(message string) error {
	panic("TODO: implementar")
}

Después reemplázalo por una implementación deliberadamente sencilla para el laboratorio y añade un test. El siguiente archivo completo representa una decisión del programador, no lógica escrita por gopls:

package profile

import "errors"

type Notifier interface {
	Notify(string) error
}

type ConsoleNotifier struct{}

func (ConsoleNotifier) Notify(message string) error {
	if message == "" {
		return errors.New("mensaje vacío")
	}
	return nil
}

Un caso genérico de interfaz o una estructura de ejemplo no demuestra que el flujo funcione con tipos mal formados, archivos generados o dependencias ausentes. La documentación de gopls señala que muchas transformaciones necesitan información de tipos y que los archivos marcados como generados no reciben acciones de transformación. Corrige primero los errores sintácticos o de carga del workspace y vuelve a revisar el diff.

Receta reproducible sin red

La prueba mínima puede mantenerse en una carpeta temporal o en un repositorio de laboratorio:

  1. Crea refactor-lab, inicializa un módulo local y guarda los dos archivos anteriores. El módulo no necesita dependencias externas.
  2. Registra go version, gopls version, la versión estable v0.58.0 de la extensión y el commit o estado del workspace. No presentes la versión de gopls como la versión del toolchain.
  3. Ejecuta la acción de tags con una selección pequeña. Captura el diff antes de aceptarlo y comprueba que las claves y la convención coinciden con la decisión del formulario.
  4. Ejecuta gofmt y go test ./...; conserva la salida junto con el diff si necesitas auditar la transformación.
  5. Prueba Remove struct tags sobre una sola clave. Comprueba que la otra permanece y vuelve a ejecutar las comprobaciones locales.
  6. Si pruebas Implement interface, completa el stub antes de considerar terminado el cambio. Añade un caso de test que compruebe el comportamiento elegido.

Los comandos de compilación y test sirven para este módulo, no para confirmar que el cliente implementa la extensión LSP. El criterio útil es que puedas explicar cada línea del diff y repetirlo con las mismas versiones. No hace falta un benchmark: esta capacidad reduce decisiones manuales y hace visible la revisión, no promete una mejora numérica de rendimiento.

Qué queda fuera del anuncio

Move declaration aparece como trabajo en desarrollo para una release futura en el anuncio. No lo incluyas en una receta que dependa de gopls v0.24.0. Del mismo modo, v0.24.0 contiene cambios no relacionados, como memoryLimit experimental y mejoras para ensamblador; no son requisitos para usar estas acciones interactivas.

El mecanismo tampoco convierte el LSP en un diseñador de interfaces de usuario universal. El servidor puede describir campos y validar entradas, pero el cliente decide cómo presenta el formulario. La propuesta en microsoft/language-server-protocol#1164 documenta la necesidad de refactorizaciones con parámetros; que gopls la utilice no significa que ya sea un estándar aprobado ni que todos los editores tengan interoperabilidad completa.

La separación práctica queda así:

Capa Responsabilidad Qué debes comprobar
gopls Analiza Go, solicita parámetros y calcula edits Versión, información de tipos y diff generado
Extensión Go Conecta el servidor con VS Code y muestra la interacción Versión estable v0.58.0, menú disponible y configuración del workspace
Cliente LSP Presenta o no las solicitudes interactivas Soporte real del editor; no asumir paridad con VS Code
Tu proyecto Decide si acepta el cambio y qué lógica completa Tests, contratos de tags, imports y revisión humana

La mejor práctica sigue siendo pequeña y concreta: pregunta lo que el servidor no puede inferir, inspecciona el parche y prueba el comportamiento que tus consumidores esperan. La interacción reduce el trabajo mecánico sin convertir una acción de edición en una garantía de compatibilidad.

Fuentes primarias

Comentarios

Cargando…