Introspeccion: descubrir la API desde dentro
Neovim se describe a sí mismo en tiempo de ejecución con un detalle que casi nadie aprovecha: opciones, funciones, mapeos y autocomandos son todos consultables desde Lua. Interrogar al editor en vez de memorizarlo.
Las cuatro lecciones anteriores se pueden resumir en un solo cambio de hábito: dejar de tratar a Neovim como un conjunto de recetas que hay que recordar y empezar a tratarlo como un sistema que responde preguntas sobre sí mismo. El editor sabe cuántas opciones tiene, de qué tipo es cada una, cuál es su ámbito y cuáles has modificado tú. Sabe qué funciones expone su API, con qué parámetros, desde qué versión y cuáles están obsoletas. Sabe qué mapeos hay activos ahora mismo y quién los definió. Todo eso es consultable desde Lua en una línea, y quien adquiere el reflejo de preguntar en lugar de buscar deja de depender de la memoria para siempre.
- Consultar el catálogo completo de opciones y filtrarlo por ámbito, tipo o modificación.
- Leer la especificación de la API en tiempo de ejecución, incluidas versión y obsolescencia.
- Convertir el motor de completado de la línea de comandos en una función de consulta.
- Combinar introspección y documentación con criterio sobre qué garantiza cada una.
El editor sabe describirse a si mismo
El punto de entrada es más simple de lo que parece: en Lua todo son tablas, incluida vim.api, así que puedes recorrerla. Y para imprimir tablas de forma legible existe una función dedicada, junto con una abreviatura en la línea de comandos que evita escribirla.
-- Estas tres formas imprimen lo mismo
vim.print(vim.api.nvim_get_mode())
-- :lua vim.print(vim.api.nvim_get_mode())
-- :lua= vim.api.nvim_get_mode()
-- Cuantas funciones expone la API y cuales hablan de buffers
local nombres = vim.tbl_keys(vim.api)
print(#nombres)
local de_buffer = vim.iter(nombres)
:filter(function(n) return n:match("^nvim_buf_") end)
:totable()
table.sort(de_buffer)
vim.print(de_buffer)
La abreviatura de la línea de comandos con el signo de igual es probablemente el atajo con mejor relación entre esfuerzo y beneficio de todo Neovim: evalúa la expresión y la imprime formateada, sin envoltorios. Convierte la línea de comandos en un intérprete interactivo y hace que explorar sea tan barato como preguntar.
Hay además una familia entera de funciones reflexivas que devuelven el estado vivo del editor: la lista de buffers, de ventanas y de pestañas; los mapeos globales y los de un buffer; los autocomandos que coinciden con un evento o un grupo; los comandos de usuario definidos; los grupos de resaltado; los canales abiertos y las interfaces conectadas. Ninguna requiere plugin alguno.
vim.print(vim.api.nvim_get_keymap("n")) -- mapeos normales
vim.print(vim.api.nvim_get_autocmds({ event = "BufWritePre" }))
vim.print(vim.api.nvim_get_commands({})) -- comandos de usuario
vim.print(vim.api.nvim_get_hl(0, { name = "Comment" }))
El catalogo de opciones como base de datos
La función central de esta lección devuelve, de una sola llamada, una tabla con todas las opciones del editor y, por cada una, su nombre corto, su tipo, su valor por defecto, su ámbito, si es global-local, si es una lista separada por comas o un conjunto de banderas, y si ha sido modificada respecto del valor por defecto.
Eso no es un dato curioso: es la respuesta autoritativa a la pregunta que abría la lección segunda. Ya no hace falta recordar si una opción es de buffer o de ventana, ni fiarse de la intuición.
local info = vim.api.nvim_get_all_options_info()
-- El ambito de una opcion concreta, sin abrir la documentacion
print(info.tabstop.scope) -- buf
print(info.wrap.scope) -- win
print(info.ignorecase.scope) -- global
-- Todas las opciones de ventana, ordenadas
local ventana = {}
for nombre, meta in pairs(info) do
if meta.scope == "win" then ventana[#ventana + 1] = nombre end
end
table.sort(ventana)
vim.print(ventana)
-- Que he cambiado yo respecto del valor por defecto
local tocadas = vim.tbl_filter(function(n) return info[n].was_set end, vim.tbl_keys(info))
table.sort(tocadas)
vim.print(tocadas)
-- Para una sola opcion, con contexto de buffer o ventana
vim.print(vim.api.nvim_get_option_info2("formatoptions", { buf = 0 }))
La última consulta merece atención especial. La lista de opciones marcadas como modificadas es una auditoría de tu configuración obtenida sin leerla: te dice exactamente qué has tocado, incluidas las que tocó un plugin sin que lo supieras. Es el complemento perfecto del prefijo de verbosidad de la lección anterior, que te dirá además dónde ocurrió.
Antes de añadir una opción a tu configuración, ejecuta la consulta de su ámbito con la abreviatura de evaluación. Si responde que es de buffer o de ventana, ya sabes que su sitio no es el archivo raíz sino el directorio de tipos de archivo. Son tres segundos que ahorran el clásico misterio del segundo archivo.
La metadata de la API y el motor de completado
Un escalón más arriba está la especificación completa de la interfaz. La función correspondiente devuelve dos cosas: el identificador del canal actual y una tabla de metadatos con la versión del editor, la lista de tipos, los eventos de interfaz y, sobre todo, el catálogo de funciones con sus parámetros, su tipo de retorno, la versión en que aparecieron y, si procede, aquella en la que quedaron obsoletas.
local _, meta = unpack(vim.api.nvim_get_api_info())
vim.print(meta.version)
-- Que funciones estan obsoletas en esta version
local muertas = vim.iter(meta.functions)
:filter(function(f) return f.deprecated_since ~= nil end)
:map(function(f) return f.name .. " -> obsoleta desde " .. f.deprecated_since end)
:totable()
vim.print(muertas)
Esa consulta es la que conviene ejecutar después de cada actualización mayor: cruza la lista de funciones obsoletas con las que usa tu configuración y tendrás la lista exacta de deuda técnica pendiente, sin esperar a que algo se rompa.
El otro instrumento, menos conocido y sorprendentemente potente, es el motor de completado de la línea de comandos expuesto como función. Recibe un prefijo y una categoría, y devuelve la lista de candidatos que ofrecería la tecla de tabulación. Las categorías incluyen opciones, funciones, comandos, eventos, grupos de resaltado, tipos de archivo y etiquetas de ayuda.
vim.print(vim.fn.getcompletion("fold", "option")) -- opciones que empiezan por fold
vim.print(vim.fn.getcompletion("str", "function")) -- funciones de Vimscript
vim.print(vim.fn.getcompletion("Buf", "event")) -- eventos de autocomando
vim.print(vim.fn.getcompletion("lsp", "help")) -- etiquetas de ayuda
Con eso se cierra el círculo de la lección anterior: cuando no sabes cómo se llama algo, no hace falta salir a buscar fuera. El editor tiene el índice y te lo entrega como tabla de Lua.
Preguntar sale mas barato que recordar
Un :lua= sobre la tabla de información de opciones responde en dos segundos preguntas que de memoria se responden mal. La memoria es para los patrones, no para los catálogos.
La introspeccion audita
La lista de opciones modificadas y la de funciones obsoletas convierten tu configuración en algo verificable en lugar de algo que se espera que siga funcionando.
flowchart TB
P[No se como se llama] --> C[getcompletion con categoria]
C --> N[Nombre exacto]
N --> I[Info de opcion o de la API]
I --> D[help de la etiqueta]
D --> S{Tiene contrato estable}
S -->|si| U[Usalo en tu config]
S -->|no| E[Solo exploracion]
style C fill:#89b4fa,color:#11111b
style U fill:#a6e3a1,color:#11111bDel descubrimiento a la certeza
Queda una advertencia que impide convertir esta lección en un mal hábito. La introspección revela lo que existe en tu compilación concreta, y eso incluye funciones internas, campos no documentados y detalles de implementación que nadie se ha comprometido a mantener. Descubrir algo por reflexión no autoriza a construir encima.
El método correcto tiene dos tiempos. La introspección descubre: te da el nombre exacto, la forma de la tabla que devuelve, el ámbito real. La documentación certifica: te dice si eso es contrato o casualidad, desde cuándo, y qué lo reemplazará. Usar solo lo primero produce configuraciones que se rompen sin aviso; usar solo lo segundo es más lento de lo necesario. Encadenar ambos es el flujo completo, y es exactamente el que dibuja el diagrama anterior.
Merece la pena detenerse en lo que esta capacidad implica, porque va más allá de una colección de trucos. Un sistema reflexivo es aquel cuyo modelo de sí mismo está disponible para sus propios programas, y Neovim lo es de manera casi total: los metadatos que describen la API no son documentación generada a posteriori sino la misma fuente desde la que se construyen el enlace de Lua, el protocolo remoto y los archivos de ayuda. Cuando consultas el ámbito de una opción en tiempo de ejecución no estás leyendo una copia de la verdad, estás leyendo la verdad. Y eso reordena por completo la estrategia de aprendizaje. En un sistema opaco, aprender es acumular hechos memorizados, y la competencia se mide por cuántos recuerdas; el olvido es pérdida neta y cada versión nueva devalúa parte de lo aprendido. En un sistema reflexivo, aprender es adquirir métodos de interrogación, y la competencia se mide por la velocidad con que formulas la pregunta correcta; el olvido es irrelevante porque el catálogo está siempre disponible, y una versión nueva no devalúa nada porque tus preguntas siguen siendo válidas y devuelven las respuestas actualizadas. Por eso este nivel termina aquí y no en una lista de funciones útiles. Lo que se lleva quien lo ha entendido no es un repertorio sino un procedimiento: ante cualquier duda, preguntar al editor por el nombre, preguntar al catálogo por la forma, preguntar a la ayuda por la garantía, y solo entonces escribir la línea. Tres preguntas de diez segundos que sustituyen a media hora de prueba y error, y que además producen una configuración que sabes defender línea por línea. Esa es, finalmente, la única definición operativa de dominar Neovim que resiste el paso del tiempo.
- Averigua con una sola línea el ámbito y el tipo de cinco opciones que uses a diario. Compara el resultado con lo que habrías respondido de memoria.
- Obtén la lista de opciones modificadas en tu sesión y contrástala con tu configuración. Investiga con el prefijo de verbosidad las que no reconozcas.
- Cruza las funciones marcadas como obsoletas en tu versión con las que aparecen en tus archivos. Corrige al menos una.
- Escribe una función que reciba un texto, use el motor de completado para buscar opciones que lo contengan y muestre las coincidencias con su ámbito y su valor actual.
- Toma un campo no documentado que hayas encontrado por reflexión y busca su etiqueta en la ayuda. Si no existe, decide conscientemente no construir nada encima y anota por qué.