Grupos: idempotencia y arquitectura de tu configuración
Por qué un autocomando sin grupo es una fuga que nadie puede cerrar, la semántica exacta de clear en nvim_create_augroup, cómo la idempotencia convierte recargar tu configuración en una operación segura, y la convención de prefijos que evita destrozar los grupos de tus plugins.
Hay un síntoma que delata a toda configuración que no ha alcanzado este nivel: recargas la config, guardas un archivo y el formateador corre dos veces. Luego tres. Luego el editor se congela un segundo entero al guardar y nadie sabe por qué. No es un fallo de Neovim ni del plugin: es la ausencia de grupos. Un autocomando sin grupo es un registro que entra en la tabla y del que nadie conserva la llave. En la lección 2.1 te dijimos que el group con clear = true no era opcional y lo escribiste por fe; esta lección es la deuda saldada: por qué.
- Explicar por qué registrar un autocomando dos veces lo ejecuta dos veces.
- Dominar la semántica exacta de
clearennvim_create_augroup. - Convertir la recarga de tu configuración en una operación idempotente.
- Organizar la configuración completa en espacios de nombres con prefijo propio.
- Borrar, vaciar e inspeccionar grupos en caliente con la API.
El problema: la configuración que se recarga
vim.api.nvim_create_autocmd siempre añade. Nunca sustituye, nunca deduplica, nunca comprueba si ya existe algo equivalente. Cada llamada crea una entrada nueva con su propio identificador. Si el archivo que contiene esa llamada se evalúa dos veces —porque recargaste la configuración con :source, porque un plugin se cargó tarde, porque estás iterando sobre tu propio código— tendrás dos autocomandos idénticos escuchando el mismo evento, y ambos se ejecutarán.
La gravedad depende de si la acción es idempotente por sí misma. Un autocomando que ajusta una opción puede ejecutarse cien veces sin consecuencias visibles — solo pagas el tiempo. Uno que formatea, que inserta una cabecera o que envía una notificación produce efectos acumulativos, y ahí la duplicación deja de ser un problema de higiene para convertirse en corrupción.
Anatomía de nvim_create_augroup
Un grupo es un espacio de nombres con identidad propia. Se crea con un nombre y una tabla de opciones, y devuelve un identificador numérico que pasas a cada autocomando.
local grupo = vim.api.nvim_create_augroup("dios_formato", { clear = true })vim.api.nvim_create_autocmd("BufWritePre", { group = grupo, pattern = { "*.lua", "*.rs" }, desc = "Formatea antes de escribir al disco", callback = function(args) vim.lsp.buf.format({ bufnr = args.buf, timeout_ms = 2000 }) end,})La semántica de clear merece precisión quirúrgica, porque su nombre engaña. Con clear = true, Neovim crea el grupo si no existe y, si ya existía, borra todos los autocomandos que contenía antes de devolverte el identificador. Con clear = false, el grupo se crea si falta y, si ya estaba, te lo devuelve intacto con todo su contenido.
De esa asimetría sale la disciplina completa del nivel. clear = true en la cabecera del archivo hace que el archivo entero sea idempotente: da igual cuántas veces se evalúe, el estado final de la tabla es siempre el mismo. Es el patrón de una migración declarativa: no describes un incremento, describes el estado deseado y borras lo anterior. clear = false sirve para el caso opuesto —varios archivos que contribuyen autocomandos al mismo grupo sin pisarse—, pero te devuelve el problema de la duplicación intacto: úsalo solo sabiendo exactamente por qué.
El mismo invento, con sintaxis de 1998
En configuraciones Vimscript verás el equivalente histórico, y conviene saber leerlo porque media internet aún lo escribe así:
augroup formatoautocmd!autocmd BufWritePre *.lua,*.rs lua vim.lsp.buf.format()augroup ENDEse autocmd! a secas dentro del bloque es el clear = true: vacía el grupo actual antes de repoblarlo. Las guías antiguas que omiten el augroup o el autocmd! arrastran exactamente el bug de duplicación que acabas de ver — ahora ya sabes qué les falta.
Un autocomando sin grupo es irrecuperable
Los autocomandos anónimos también pueden borrarse, pero solo con nvim_del_autocmd(id) y solo si guardaste el id que devolvió el registro. Al recargar la configuración, esa referencia se pierde siempre: el autocomando queda vivo hasta cerrar Neovim, invisible a cualquier limpieza por nombre. No existe el comando «borra los que registré yo». El grupo no es una comodidad organizativa: es el único asidero que tienes sobre tu propio código.
Un grupo por intención: arquitectura de tu configuración
El siguiente paso es dejar de crear grupos ad hoc y adoptar una convención. La forma canónica —la verás en LazyVim y en casi toda configuración seria— es una función auxiliar que aplica un prefijo y clear = true de manera uniforme:
-- lua/config/autocmds.lualocal function augroup(nombre) return vim.api.nvim_create_augroup("dios_" .. nombre, { clear = true })end-- Restaurar la posicion del cursor al reabrir un archivo (2.1, ahora con grupo y desc)vim.api.nvim_create_autocmd("BufReadPost", { group = augroup("ultima_posicion"), desc = "Vuelve a la ultima linea editada", callback = function(args) local marca = vim.api.nvim_buf_get_mark(args.buf, '"') if marca[1] > 0 and marca[1] <= vim.api.nvim_buf_line_count(args.buf) then pcall(vim.api.nvim_win_set_cursor, 0, marca) end end,})-- Cerrar buffers auxiliares con q, solo donde tiene sentidovim.api.nvim_create_autocmd("FileType", { group = augroup("cierre_rapido"), pattern = { "help", "qf", "man", "checkhealth", "lspinfo" }, desc = "Mapea q para cerrar ventanas auxiliares", callback = function(args) vim.bo[args.buf].buflisted = false vim.keymap.set("n", "q", "<cmd>close<cr>", { buffer = args.buf, silent = true }) end,})El prefijo cumple una función real, no cosmética: la tabla de grupos es global y compartida con todos los plugins instalados.
La granularidad correcta es un grupo por intención, no uno por archivo ni uno para todo. Cada grupo debería poder describirse en una frase: restaurar posición, formato al guardar, ajustes por lenguaje, terminal limpio (el de la 4.2). Esa granularidad es la que permite después desactivar una funcionalidad completa borrando un grupo, o inspeccionar qué escucha cada evento sin leer código.
Borrar, inspeccionar y desmontar
Los grupos habilitan operaciones que sobre autocomandos anónimos son sencillamente imposibles.
Vaciar sin destruir
vim.api.nvim_clear_autocmds({ group = … }) elimina los autocomandos del grupo pero conserva el grupo y su identificador.
Filtrar el borrado
La misma llamada admite event, pattern y buffer: retiras quirúrgicamente un subconjunto sin tocar el resto del grupo.
Destruir el grupo
vim.api.nvim_del_augroup_by_name(nombre) borra grupo y contenido de una vez. Es la pieza de un interruptor en caliente.
Inspeccionar
vim.api.nvim_get_autocmds({ group = … }) devuelve una tabla estructurada con cada entrada, su patrón y su desc. La lección 9.5 vive de esta función.
-- Un interruptor real: activar y desactivar formato al guardar en calientelocal NOMBRE = "dios_formato_al_guardar"local function activar() local g = vim.api.nvim_create_augroup(NOMBRE, { clear = true }) vim.api.nvim_create_autocmd("BufWritePre", { group = g, desc = "Formatea antes de escribir", callback = function(args) vim.lsp.buf.format({ bufnr = args.buf }) end, })endlocal function desactivar() pcall(vim.api.nvim_del_augroup_by_name, NOMBRE)endvim.api.nvim_create_user_command("FormatoAlGuardar", function(opts) if opts.args == "off" then desactivar() else activar() endend, { nargs = 1, complete = function() return { "on", "off" } end })Ese comando es imposible de escribir sin grupos. Con ellos son doce líneas, funciona sin reiniciar, y activar() es seguro de llamar dos veces seguidas — idempotencia otra vez, la misma propiedad en miniatura.
La idempotencia es la propiedad que separa una configuración de un script
Un script se ejecuta una vez y describe una secuencia de acciones; una configuración se ejecuta un número impredecible de veces y debe describir un estado. Ese es el abismo real que cruza clear = true, y es la misma frontera que separa un instalador artesanal de una herramienta declarativa, o un puñado de comandos sueltos de una migración reversible. Cuando escribes nvim_create_augroup con clear estás afirmando algo mucho más fuerte que «voy a agrupar unas cuantas cosas»: estás declarando que este bloque de código posee en exclusiva una región del espacio de estados del editor, y que evaluarlo lleva esa región a un valor conocido con independencia de dónde estuviera antes. Esa es literalmente la definición de idempotencia —aplicar la operación dos veces produce lo mismo que aplicarla una—, y es lo que permite que recargar la configuración sea una operación segura en vez de una apuesta. Sin ella, tu configuración acumula historia: cada recarga deja sedimentos que nadie puede enumerar y que solo se limpian cerrando el proceso, y el editor se vuelve un sistema cuyo comportamiento depende de por cuántos estados intermedios ha pasado. Con ella, el estado del editor es una función de tu código fuente. Todo lo que hace agradable trabajar sobre la propia configuración —recargar sin miedo, iterar rápido, desactivar una función y recuperarla, razonar sobre qué está activo— descansa sobre esa única propiedad. Los grupos no son carpetas: son la garantía de que tu editor no tiene memoria oculta.
Sobre tu autocmds.lua de la 2.1: gracias al grupo con clear = true, tras dos recargas sigue habiendo una entrada. Coméntale el group a un autocomando de prueba, repite, y mira aparecer las copias.
La versión estructurada del listado: cada entrada con su evento, su patrón y su desc. Sobre esto se construye la lección 9.5.
Copia algo: ya no hay resalte. Recupera el grupo simplemente volviendo a evaluar el archivo que lo declara — puede hacerlo porque es idempotente.
Haz tu configuración idempotente
- Registra un autocomando de notificación sin grupo, evalúa su archivo tres veces con
:sourcey cuenta cuántas notificaciones recibes al disparar el evento. - Repite el experimento con un grupo creado con
clear = truey explica por qué el resultado ya no depende del número de recargas. - Escribe la función auxiliar con prefijo y migra los autocomandos de tu configuración a grupos con una intención por grupo y un
descpor entrada. - Implementa el comando
FormatoAlGuardar on/off(o un interruptor equivalente para otra funcionalidad tuya) y verifica que funciona sin reiniciar. - Usa
nvim_get_autocmdsfiltrando por tu prefijo y genera un inventario legible de todo lo que tu configuración escucha. Guárdalo: es la foto de tu sistema reactivo.