wandres.dev
PUBLICAR UN PLUGIN · API, docs y versiones

Documentación en formato help: el contrato dentro del editor

La sintaxis real de un archivo `doc/`, las etiquetas y los enlaces, cómo se genera el índice con `helptags`, el modeline obligatorio, la generación automática desde Markdown y por qué un README alojado en una web nunca sustituye a la ayuda integrada.

⏱ 23 min

Existe una diferencia de categoría entre un plugin que trae documentación y uno que trae un enlace. El primero contesta a :help en el mismo instante en que el usuario tiene la duda, sin salir del buffer, sin red, sin pestañas y sin perder el hilo de lo que estaba haciendo; el segundo interrumpe. Neovim lleva desde su ancestro un sistema de ayuda hipertextual completo —etiquetas, saltos, índice generado, búsqueda en todos los archivos— que funciona igual para el núcleo que para tu plugin de doscientas líneas, y que la mayoría de los autores ignora porque el formato parece arcaico. Es arcaico, y es también la razón por la que la documentación de Vim de 1995 sigue siendo navegable hoy.

🎯 Al terminar esta lección sabrás
  • Escribir un archivo doc/ con su cabecera, sus secciones y su modeline correcto.
  • Definir etiquetas y enlazarlas para que el salto con Control-] funcione.
  • Generar y regenerar el índice con helptags y entender qué archivo produce.
  • Decidir qué vive en la ayuda integrada y qué vive en el README, y automatizar la conversión.

Anatomía de un archivo doc/

Un archivo de ayuda es texto plano con cuatro convenios tipográficos y ninguna sintaxis más. La primera línea contiene la etiqueta que da nombre al archivo y una descripción corta; las secciones se separan con una línea de signos igual; los encabezados llevan una línea de tildes debajo; y la última línea es un modeline que le dice a Neovim cómo interpretarlo.

*mi-plugin.txt*   Anotaciones inteligentes para Neovim         *mi-plugin*

==============================================================================
CONTENIDO                                               *mi-plugin-contenido*

  1. Introduccion .......................... |mi-plugin-introduccion|
  2. Configuracion ......................... |mi-plugin-configuracion|
  3. Funciones ............................. |mi-plugin-funciones|

==============================================================================
1. INTRODUCCION                                      *mi-plugin-introduccion*

Este plugin anota el buffer con informacion derivada del arbol sintactico.
Requiere Neovim 0.10 o superior. Ver |mi-plugin-configuracion| para las
opciones disponibles.

==============================================================================
2. CONFIGURACION                                    *mi-plugin-configuracion*

setup({opts})                                             *mi-plugin.setup()*

    Inicializa el plugin. Debe llamarse una vez.

    Parametros: ~
      • {opts}  (`table|nil`)  Opciones. Ver mas abajo.

    Ejemplo: >lua
        require("mi-plugin").setup({ border = "rounded" })
<
vim:tw=78:ts=8:ft=help:norl:

Tres detalles se olvidan siempre y los tres duelen. El modeline final es obligatorio: sin él, un archivo abierto directamente desde el repositorio no se resalta ni se comporta como ayuda. Los bloques de ejemplo empiezan con el signo mayor al final de una línea y terminan con el signo menor al principio de otra, y desde Neovim 0.10 puedes indicar el lenguaje justo después para que el bloque se resalte con Treesitter. Y las columnas importan: el ancho canónico es 78 y las etiquetas se alinean a la derecha, no porque sea bonito sino porque toda la ayuda del editor lo hace y la disonancia se nota de inmediato.

Etiquetas, enlaces e índice

Una etiqueta es un identificador rodeado de asteriscos: es el destino de un salto y el argumento que alguien escribirá tras :help. Un enlace es el mismo identificador rodeado de barras verticales: es el origen del salto, y con el cursor encima, Control-] lleva al destino mientras que Control-t vuelve. Ese par es todo el hipertexto que hay, y basta.

🏷️

Etiqueta

Entre asteriscos. Es el destino del salto y lo que alguien escribe tras :help. Se alinea a la derecha en la columna 78.

🔗

Enlace

Entre barras verticales. Es el origen del salto: Control-] va, Control-t vuelve, igual que en el código.

📇

Índice

El archivo tags, generado por helptags. Sin él la ayuda existe pero es inalcanzable.

El índice no se escribe a mano. El comando helptags recorre un directorio doc/, extrae todas las etiquetas y produce un archivo llamado tags que asocia cada una con su ubicación. Sin ese archivo, :help mi-plugin responde que no encuentra nada aunque el texto esté ahí.

" Regenerar el indice del propio plugin durante el desarrollo
:helptags ALL

" O solo el directorio concreto en el que trabajas
:helptags ~/proyectos/mi-plugin/doc

Los gestores de plugins ejecutan helptags automáticamente al instalar y al actualizar, así que el usuario final no hace nada. El archivo tags generado no se versiona: es un artefacto derivado, cambia con cada edición y produce conflictos de fusión sin ningún valor. Añádelo a la lista de exclusiones y olvídate.

⚠️
Las etiquetas son un espacio de nombres global

Todas las etiquetas de todos los plugins instalados conviven en el mismo índice. Si llamas a una etiqueta config o setup a secas, colisionarás con alguien y uno de los dos ganará arbitrariamente. Prefija siempre con el nombre de tu plugin: mi-plugin-config, mi-plugin.setup(). Es la misma disciplina que aplicas a los grupos de resaltado y a los comandos de usuario, por la misma razón.

Escribirlo dos veces, o generarlo

Mantener a mano un README en Markdown y una ayuda en formato Vim es duplicar el trabajo, y lo duplicado diverge: al tercer mes uno de los dos miente. Hay dos salidas honestas. La primera es escribir la ayuda como fuente única y que el README sea deliberadamente corto —qué es, cómo se instala, una captura y un enlace a la ayuda—, con lo que no hay nada que sincronizar. La segunda es generar el archivo doc/ desde el Markdown con una herramienta de conversión basada en un filtro de Pandoc, invocada desde la integración continua en cada cambio de la rama principal, de modo que el artefacto se regenere solo.

flowchart TB
md[README en Markdown] --> gen[Conversor en integracion continua]
gen --> doc[doc con mi-plugin punto txt]
doc --> ht[helptags al instalar o actualizar]
ht --> tags[Indice tags generado y no versionado]
tags --> h[Comando help mi-plugin]
tags --> g[Busqueda helpgrep en toda la ayuda]
style doc fill:#cba6f7,color:#11111b
style h fill:#a6e3a1,color:#11111b
💡
Tres comandos que hacen rentable escribir la ayuda

:helpgrep patron busca una expresión en toda la ayuda instalada y llena la lista quickfix con los resultados, lo que convierte el conjunto de plugins del usuario en una base de conocimiento consultable. :help mi-plugin- seguido de la tecla de completado enumera todas tus etiquetas, así que unos nombres bien elegidos funcionan como índice interactivo. Y K sobre una palabra en un archivo de ayuda salta directamente a su definición. Nada de esto existe fuera del editor.

La vía generada tiene una trampa que conviene anticipar: el conversor produce etiquetas a partir de los encabezados del Markdown, así que renombrar una sección del README renombra silenciosamente una etiqueta de la ayuda, y cualquiera que hubiera enlazado a ella desde su propia configuración o desde otro plugin se queda sin destino. Si generas, trata los encabezados con el mismo cuidado con el que tratas los nombres de tus funciones públicas, porque en ese montaje lo son.

Sea cual sea la vía, la ayuda debe contener cuatro cosas y puede prescindir del resto: la versión mínima de Neovim requerida, la tabla completa de opciones con su tipo y su valor por defecto, la firma exacta de cada función pública, y la lista de comandos y eventos que el plugin crea. Todo lo demás —motivación, capturas, comparativas, tutorial— vive mejor en el README, donde el formato enriquecido aporta algo.

Por qué el README no basta

Una comprobación honesta de si tu ayuda cumple su función: intenta responder desde ella, sin abrir el navegador, a las tres preguntas que recibe cualquier plugin en sus primeras semanas de vida. Qué versión mínima necesito, qué opciones existen con su valor por defecto, y qué hace exactamente esta función con este argumento. Si alguna te obliga a salir del editor, la ayuda está incompleta por mucho que ocupe cuatrocientas líneas.

La objeción evidente es que hoy todo el mundo tiene un navegador abierto. Es cierto y es irrelevante, porque el argumento no es de disponibilidad sino de coste de interrupción. Consultar :help mi-plugin.setup() cuesta tres segundos y no mueve el foco; abrir el navegador, encontrar la pestaña, localizar la sección en una página larga y volver cuesta medio minuto y rompe el estado mental que sostenía la tarea. A escala de un año de uso, esa diferencia decide si la gente lee tu documentación o adivina.

Capacidad Ayuda integrada README en la web
Funciona sin conexión No
Salto por etiqueta con Control-] No
Búsqueda en toda la ayuda con helpgrep No
Corresponde a la versión instalada Solo la última
Completado de argumentos tras :help No
Capturas y formato enriquecido No

Hay además un efecto de segundo orden que solo se aprecia con el tiempo: la ayuda integrada se lee en el mismo formato que la del núcleo del editor. Quien lleva años consultando la documentación de Neovim tiene interiorizado dónde mira para encontrar la firma de una función, dónde espera la tabla de opciones y qué significa una etiqueta alineada a la derecha. Al adoptar ese formato heredas gratis toda esa familiaridad, mientras que cada README inventa su propia organización y obliga a explorarla desde cero. La uniformidad, que parece rigidez, es en realidad lo que permite leer deprisa.

La cuarta fila es la que casi nadie considera y la que más problemas evita. La ayuda viaja dentro del repositorio, en el mismo commit que el código: quien tiene instalada la versión del año pasado lee la documentación del año pasado, que es exactamente la que describe su plugin. La página web describe siempre la última versión, y por tanto miente a todo el que no haya actualizado, que es la mayoría.

La documentación no acompaña al programa: es parte del programa

Hay una jerarquía implícita en cómo casi todo el mundo trata la documentación, y es la raíz del problema: primero se escribe el código, que es lo real, y después se documenta, que es una cortesía hacia los demás si queda tiempo. Bajo esa jerarquía la documentación es siempre lo primero que se sacrifica y lo primero que envejece, porque nada la ata al código salvo la buena voluntad de quien edita. El sistema de ayuda de Vim propone otra jerarquía, y lo hace con una decisión técnica más que con un discurso: al colocar la documentación dentro del repositorio, indexarla con las mismas herramientas de navegación que usas para saltar a una definición y hacerla accesible con una pulsación desde el punto exacto donde surge la duda, convierte el texto en una estructura de datos consultable por el propio editor, no en un adorno. Y de ahí se sigue algo que solo se aprecia después de mantener algo durante años: escribir la ayuda antes que la implementación cambia el diseño de la implementación. Cuando tienes que redactar la firma de una función en prosa, con sus tipos, su valor por defecto y su comportamiento ante entradas raras, y ese párrafo te sale largo, retorcido o lleno de excepciones, acabas de recibir la señal más fiable que existe de que la función está mal diseñada; ninguna revisión de código la habría dado tan pronto ni tan barata. La documentación funciona así como una prueba ejecutada sobre tu capacidad de explicar, y explicar es el único banco de pruebas que detecta la complejidad accidental antes de que exista. El corolario incómodo es que un plugin sin doc/ no es un plugin poco documentado: es un plugin cuyo autor nunca se obligó a formular su propio contrato, y que por tanto no sabe con precisión qué prometió. Los proyectos que perduran en este ecosistema comparten esa disciplina sin excepción, y no es una coincidencia estética: es que la ayuda escrita es el lugar donde la API se vuelve consciente de sí misma.

📝
Lo esencial

Un archivo de ayuda es texto plano con cuatro convenios: cabecera con etiqueta, secciones separadas por una línea de signos igual, etiquetas entre asteriscos alineadas a la derecha y modeline final obligatorio. Los enlaces van entre barras verticales, el índice lo genera helptags y el archivo tags no se versiona. Prefija todas tus etiquetas. Y elige una sola fuente de verdad: o escribes la ayuda a mano y el README es corto, o la generas desde el Markdown en integración continua.

⚔️ Tu plugin, dentro de la ayuda
  1. Crea doc/mi-plugin.txt con su cabecera, un índice, dos secciones y el modeline final. Ejecuta helptags y comprueba que :help mi-plugin abre el archivo.
  2. Añade enlaces entre secciones y verifica que Control-] salta y Control-t vuelve.
  3. Documenta una función pública con sus parámetros, su tipo y un ejemplo en un bloque con lenguaje declarado, y comprueba que se resalta.
  4. Provoca una colisión de etiquetas a propósito con un nombre genérico, observa el resultado y corrígela con el prefijo del plugin.
  5. Añade el archivo tags a las exclusiones del repositorio y monta la generación del archivo de ayuda desde el README en tu integración continua.