Plegado: domar ficheros enormes
Los seis métodos de plegado y cuál quieres, por qué zr y zR no son lo mismo, el nivel de plegado como número y no como interruptor, los folds manuales como operador, la opción que evita que los pliegues se cierren en tus narices, y cómo conseguir que sobrevivan a cerrar el fichero.
Un fichero de ochocientas líneas es inmanejable si lo ves entero, y el problema no se arregla desplazándose más rápido: se arregla viendo menos. El plegado colapsa funciones, clases y bloques para enseñarte el esqueleto, y expande solo lo que pides. Casi todo el mundo lo prueba una semana y lo abandona, siempre por las mismas tres razones: abre los ficheros todo plegado, los pliegues se cierran solos mientras editas, y al reabrir el fichero se ha perdido todo. Las tres tienen arreglo y son tres opciones. Esta lección es el plegado y sus tres arreglos.
- Elegir entre los seis métodos de plegado según el fichero.
- Entender el nivel de plegado como un número, y deducir de ahí todos los atajos.
- Crear pliegues manuales con
zf, que es un operador y acepta cualquier complemento. - Evitar que los pliegues se abran y cierren solos con
'foldopen'yzv. - Hacer que el plegado sobreviva a cerrar el fichero con
:mkview.
Los seis métodos
'foldmethod' decide quién elige dónde hay un pliegue:
| Método | Cómo decide |
|---|---|
manual |
los creas tú con zf. Es el valor por defecto |
indent |
por nivel de indentación. Rápido, tosco y sorprendentemente decente |
expr |
por una expresión que tú das — aquí entra Treesitter |
marker |
por marcadores en comentarios: {{{ y }}} |
syntax |
por las reglas del fichero de sintaxis del lenguaje |
diff |
automático en modo diff: pliega lo que no ha cambiado |
Es una opción de ventana, no global: dos ventanas pueden mostrar el mismo buffer con métodos distintos, y por eso se activa por tipo de fichero y no de golpe para todo.
El bueno: expr con Treesitter
Pliega por la estructura real del código —funciones, clases, bloques— en vez de por la indentación aproximada. Y es nativo: la función vim.treesitter.foldexpr() está en el núcleo desde la 0.9.
Pruébalo ahora mismo, en un fichero .lua o .md, que son de los que traen parser incluido:
:setlocal foldmethod=expr:setlocal foldexpr=v:lua.vim.treesitter.foldexpr():setlocal foldlevel=99El malentendido más repetido sobre el plegado por Treesitter
Lo da Neovim, no el plugin. nvim-treesitter en su rama viva instala parsers y consultas, y no configura ningún plegado por ti. Si un tutorial te enseña a activar folds desde su setup, está escrito para la rama que ya no se mantiene.
Y una corrección importante que verás mal en muchos sitios: vim.treesitter.foldtext() no existe. No está en el núcleo ni en la documentación de la 0.12. Lo que sí funciona es poner 'foldtext' a la cadena vacía, y entonces Neovim pinta la línea plegada con su resaltado normal, que es exactamente lo que casi todo el mundo quería:
:set foldtext=marker, el que verás en ficheros de configuración ajenos
-- Opciones de la interfaz {{{vim.o.number = truevim.o.signcolumn = "yes"-- }}}Los marcadores van dentro de comentarios y los define 'foldmarker'. Es feo, ensucia el fichero y tiene una virtud que ninguno de los otros métodos tiene: es explícito y viaja con el texto. Quien abra ese fichero verá los mismos pliegues que tú, sin configurar nada. Por eso sobrevive en los dotfiles de mucha gente.
Y diff, que no eliges tú
Cuando abres dos ficheros con nvim -d a.txt b.txt, el plegado se pone en diff solo y colapsa todo lo que es idéntico, dejándote a la vista únicamente lo que cambia con un poco de contexto. Es el caso donde el plegado funciona tan bien que ni te enteras de que lo estás usando.
El nivel: por qué zr y zR no son lo mismo
Aquí está el concepto que convierte una lista de atajos en algo deducible.
Un pliegue no está simplemente abierto o cerrado: tiene un nivel. Una función dentro de una clase está a nivel 2; el if de dentro, a nivel 3. Y 'foldlevel' es un número que dice hasta qué profundidad se ve:
foldlevel |
Qué ves |
|---|---|
0 |
todo plegado: solo lo de nivel superior |
1 |
se abren los de nivel 1; lo de dentro sigue plegado |
99 |
todo abierto |
Con eso, los atajos dejan de memorizarse:
zmbaja'foldlevel'en uno: pliega un nivel más en todo el fichero.zrlo sube: despliega un nivel más.zMlo pone a cero yzRa un número enorme: los extremos.za,zoyzcno tocan'foldlevel': actúan solo sobre el pliegue del cursor.
Por eso zM seguido de dos o tres zr es la forma de ver la estructura a la profundidad que quieras: primero todo cerrado, luego abres niveles hasta que se lee bien.
Arreglo número uno: no empezar con todo plegado
La queja más común de quien activa el plegado por primera vez es que abre un fichero y no ve nada. La culpa es de 'foldlevelstart', que decide con qué 'foldlevel' empieza cada fichero nuevo.
:set foldlevelstart=99Con eso entras con todo desplegado y pliegas cuando tú quieras. Es la configuración que espera prácticamente todo el mundo, y sin ella el plegado se abandona la primera tarde.
No confundas las dos opciones: 'foldlevel' es el estado ahora y cambia cada vez que pulsas zr o zm; 'foldlevelstart' es solo el valor inicial.
Los atajos
| Tecla | Acción |
|---|---|
za |
alterna el pliegue bajo el cursor — el más usado |
zo / zc |
abrir / cerrar el del cursor |
zO / zC / zA |
igual, pero recursivamente: también los de dentro |
zR / zM |
abrir / cerrar todos |
zr / zm |
abrir / cerrar un nivel en todo el fichero |
zv |
abre justo lo necesario para ver el cursor |
zj / zk |
al principio del pliegue siguiente / final del anterior |
[z / ]z |
al principio / final del pliegue actual |
zi |
activa y desactiva todo el plegado |
zn / zN |
desactiva / reactiva conservando los pliegues |
zf{moción} |
crea un pliegue manual |
zd / zE |
borra el del cursor / todos |
Dos de esa lista casi nadie los conoce y resuelven problemas reales. [z y ]z te llevan a los extremos del pliegue en el que estás, que dentro de una función larga es «al principio de esta función» y «a su final» sin depender de las llaves. Y zi apaga el plegado entero y lo vuelve a encender, que es lo que quieres cuando estás haciendo algo que los pliegues estorban y no te apetece deshacer la configuración.
zf es un operador
Aunque uses Treesitter, zf sigue ahí y a veces es justo lo que quieres: plegar un trozo arbitrario que no se corresponde con ninguna estructura del lenguaje. Y como es un operador, acepta todo lo de la lección 0.4:
Requiere 'foldmethod' en manual o marker, así que en la práctica es para momentos concretos: zi para desactivar, o cambiar el método un rato.
Arreglo número dos: que no se cierren en tus narices
Este es el que hace abandonar a la gente que sobrevivió al primero. Buscas algo que está dentro de un pliegue cerrado, saltas ahí, y no ves nada. O peor: estás editando y los pliegues se abren y cierran solos mientras te mueves.
Lo gobiernan dos opciones que casi nadie conoce:
:set foldopen? " qué comandos ABREN un pliegue al entrar en él:set foldclose? " cuándo se cierran solos'foldopen' es una lista de situaciones —búsqueda, salto, marca, undo…— en las que Neovim abre automáticamente el pliegue de destino. Viene con un valor razonable, y si alguna vez notas que un salto concreto te deja a ciegas, ahí está la lista que hay que ampliar.
Y 'foldclose' puesto a all es la opción que hace que los pliegues se cierren solos en cuanto sales de ellos. Suena elegante y es insoportable: es la causa de la sensación de que «los folds tienen vida propia». Viene desactivado; no lo actives.
Para el resto está zv, que abre lo justo para ver la línea del cursor y nada más. En el Nivel 2 acabarás poniéndolo detrás de los saltos de búsqueda —nzzzv— por exactamente esta razón.
Arreglo número tres: que sobrevivan a cerrar el fichero
Aquí está la tercera queja, y la que casi ninguna guía responde. Con 'foldmethod' en manual, tus pliegues se pierden al cerrar el fichero. Con expr se recalculan, así que dan igual; pero cualquier pliegue hecho a mano desaparece.
La solución nativa se llama vistas:
:mkview " guarda pliegues, cursor y opciones locales de esta ventana:loadview " restáuralosSe guardan en 'viewdir', dentro de stdpath('state'), y qué entra exactamente lo decide 'viewoptions' —por defecto incluye los pliegues y el cursor—. Puedes tener hasta diez vistas por fichero: :mkview 3 y :loadview 3.
Y automatizarlo, en el Nivel 2
Nadie va a escribir :mkview al salir de cada fichero. Lo normal es un par de autocomandos que lo hagan solos al cerrar y al abrir, y eso es exactamente lo que escribirás en el Nivel 2, cuando tengas configuración.
Lo que importa hoy es que sepas que el mecanismo existe y cómo se llama, porque el síntoma —«he perdido mis folds»— no se parece en nada a la palabra que hay que buscar en la ayuda, que es :h :mkview.
Y una advertencia por si lo automatizas sin pensarlo: las vistas guardan también opciones locales de la ventana, así que una vista de hace tres meses puede devolverte configuraciones que ya cambiaste. Si te pasa, recorta 'viewoptions' para que solo guarde los pliegues y el cursor.
Lo que verás en el Nivel 3
Dos cosas que ya existen y que este nivel no monta porque exigen tener el editor configurado:
Plegar por el servidor de lenguaje. Desde la 0.11 hay una función nativa, vim.lsp.foldexpr(), que pide los rangos plegables al servidor. Un servidor sabe cosas que el árbol sintáctico no —qué es una región, dónde acaba lógicamente un bloque de importaciones— y produce pliegues mejores en algunos lenguajes. Se usa igual que la de Treesitter, cambiando la función.
Una columna que enseñe los pliegues. 'foldcolumn' dibuja un margen a la izquierda con la estructura y te deja abrir y cerrar con el ratón. Pruébalo hoy con :set foldcolumn=1; decidir si lo quieres fijo es cosa de tu configuración.
Enseñar la estructura antes que el detalle no es una comodidad: es cómo se lee
Merece la pena entender por qué plegar funciona tan bien, porque no es «ver menos texto».
Un fichero de código no es un texto lineal, aunque se guarde como tal. Es un árbol: un módulo que contiene clases que contienen funciones que contienen bloques. Cuando lo lees de arriba abajo estás recorriendo una estructura de dos dimensiones por su serialización de una, que es como intentar entender un organigrama leyendo el fichero CSV del que sale. Puedes, y cuesta más de lo que debería.
Plegar no oculta información: restituye una dimensión. zM no te enseña menos código, te enseña el mismo árbol a otra profundidad, y esa vista —doce firmas de función en una pantalla— contiene una información que las ochocientas líneas no contenían de forma accesible: la forma del fichero.
El patrón se llama revelación progresiva, y es la misma idea que hay detrás de casi todo lo que se lee bien. Un índice antes de los capítulos. El resumen antes del artículo, y el titular antes del resumen —la pirámide invertida del periodismo—. Los niveles de zoom de un mapa, que no son el mismo mapa más pequeño sino mapas distintos con distinta información. Una documentación de API que enseña primero la lista de métodos y luego cada firma. El gO que aprendiste en la lección 0.6, que hace esto mismo con una página de ayuda. Un git log --oneline antes de git show.
En todos, la regla es la misma y va contra el instinto: el que decide cuánto detalle ve tiene que ser el lector, no el autor. Un sistema que te enseña todo de golpe ha tomado esa decisión por ti y ha elegido mal, porque no sabía qué buscabas. Y uno que te enseña solo el resumen la ha tomado igual de mal en la otra dirección. Lo que funciona es dar los dos y una forma barata de moverse entre ellos —una tecla, za—, y dejar que el lector recorra el árbol como necesite.
Cuando alguien se queja de que un código es ilegible, muchas veces no es el código: es que solo existe a un nivel de detalle.
zM pliega todo y te deja el esqueleto. zj y zk saltan entre pliegues. za abre el que quieras.
[z va al principio del pliegue actual y ]z a su final, sin depender de las llaves.
Es el valor con el que arranca cada fichero. Sin él, el plegado se abandona el primer día.
Abre lo justo para ver la línea del cursor, sin desplegar el fichero entero.
zf es un operador: acepta objetos de texto, mociones y marcas.
Pliega como un maestro
- En un fichero
.lua, activa el plegado por Treesitter con los tres:setlocalde arriba. Comprueba que pliega por funciones, no por indentación. - Pulsa
zMy mira el esqueleto. Luegozrtres veces y observa cómo se abre un nivel cada vez, no todo. - Comprueba la diferencia entre
zcyzCdentro de un bloque anidado. - Busca algo que esté dentro de un pliegue cerrado y usa
zvpara ver solo lo necesario. - Ponte en mitad de una función y usa
[zy]z. - Cambia a
:setlocal foldmethod=manual, crea tres pliegues conzf, cierra el fichero y ábrelo: han desaparecido. Repítelo guardando con:mkviewy restaurando con:loadview. - Prueba
:set foldcolumn=1y decide si lo quieres. - Y comprueba el aviso: ejecuta
:lua print(vim.treesitter.foldtext). Imprimenil, porque esa función no existe por mucho que aparezca en configuraciones ajenas.