Saltar al contenido

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' y zv.
  • 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:

VIM
:setlocal foldmethod=expr:setlocal foldexpr=v:lua.vim.treesitter.foldexpr():setlocal foldlevel=99

El 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:

VIM
:set foldtext=

marker, el que verás en ficheros de configuración ajenos

LUA
-- 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:

  • zm baja 'foldlevel' en uno: pliega un nivel más en todo el fichero.
  • zr lo sube: despliega un nivel más.
  • zM lo pone a cero y zR a un número enorme: los extremos.
  • za, zo y zc no 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.

VIM
:set foldlevelstart=99

Con 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:

zf} pliega hasta el final del párrafozf10j pliega diez líneaszfa{ pliega el bloque enterozfip pliega el párrafozf’a pliega hasta la marca a

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:

VIM
: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:

VIM
:mkview      " guarda pliegues, cursor y opciones locales de esta ventana:loadview    " restáuralos

Se 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

  1. En un fichero .lua, activa el plegado por Treesitter con los tres :setlocal de arriba. Comprueba que pliega por funciones, no por indentación.
  2. Pulsa zM y mira el esqueleto. Luego zr tres veces y observa cómo se abre un nivel cada vez, no todo.
  3. Comprueba la diferencia entre zc y zC dentro de un bloque anidado.
  4. Busca algo que esté dentro de un pliegue cerrado y usa zv para ver solo lo necesario.
  5. Ponte en mitad de una función y usa [z y ]z.
  6. Cambia a :setlocal foldmethod=manual, crea tres pliegues con zf, cierra el fichero y ábrelo: han desaparecido. Repítelo guardando con :mkview y restaurando con :loadview.
  7. Prueba :set foldcolumn=1 y decide si lo quieres.
  8. Y comprueba el aviso: ejecuta :lua print(vim.treesitter.foldtext). Imprime nil, porque esa función no existe por mucho que aparezca en configuraciones ajenas.