Buffers, ventanas y pestañas a fondo
El modelo que casi todos malinterpretan: un buffer no es una ventana y una pestaña no es un fichero. Leer las banderas de :ls, el buffer alterno, la lista de argumentos, todos los comandos de ventana, cerrar un buffer sin descuadrar el layout, y las dos formas de que mañana el editor siga donde lo dejaste: sesiones y shada.
La confusión número uno de quien viene de otro editor: creer que las pestañas de Neovim son ficheros. No lo son, y de esa creencia salen casi todas las peleas de la primera semana —cerrar algo y que se descuadre la pantalla, «perder» un fichero que sigue abierto, abrir el mismo tres veces—. Debajo hay tres cosas distintas que se pueden combinar libremente, y en cuanto separas las tres dejas de gestionar ficheros y empiezas a gestionar un espacio de trabajo. Todo lo de esta lección funciona sin un solo plugin y sin una línea de configuración.
- Separar buffer, ventana y pestaña, y decir cuál usar para cada cosa.
- Leer las banderas de
:lsy saber por qué un buffer oculto no ha perdido nada. - Alternar entre dos ficheros con
Ctrl-^, que es el 80% de la navegación real. - Usar la lista de argumentos como conjunto explícito sobre el que operar en masa.
- Cerrar un buffer sin cerrar la ventana, con el idioma nativo.
- Conseguir que mañana siga todo donde lo dejaste:
:mksessiony shada.
Tres capas, no una
Pasa el ratón por las ventanas del diagrama y tienes el modelo entero:
Buffer
El contenido de un fichero cargado en memoria, con su historial de deshacer, sus marcas y sus cambios. Abres veinte ficheros y tienes veinte buffers, aunque solo veas uno.
Ventana
Una vista sobre un buffer. Un split es una ventana más. Dos ventanas pueden mostrar el mismo buffer a la vez, cada una por una parte distinta.
Pestaña
Una disposición de ventanas. No es un fichero: es un escritorio. Cambiar de pestaña cambia el reparto de la pantalla, no lo que tienes abierto.
Fíjate en main.lua: al tocarlo se encienden dos ventanas a la vez, A y C. Y en notas.md: no lo muestra ninguna, y sigue cargado, entero, con todo lo que hiciste en él. Ninguna de las dos situaciones es rara ni un error; las dos son imposibles de expresar en un editor donde «pestaña» y «fichero» son lo mismo.
Separar el documento de la vista es una decisión vieja y siempre la misma
Detente en por qué esto se siente incómodo al principio. Vienes de herramientas donde abrir un fichero crea una pestaña y cerrar la pestaña cierra el fichero: una sola cosa, un solo control, sin nada que aprender. Es más simple, y por eso se impuso.
El coste aparece en cuanto quieres algo que esa simplificación no puede expresar. Ver el mismo fichero por dos sitios a la vez —la definición arriba y el uso abajo— exige que la vista sea independiente del documento. Dejar cuarenta ficheros cargados sin cuarenta pestañas ocupando la pantalla exige lo mismo. Tener dos disposiciones de pantalla distintas para dos tareas, también. Cada una de esas cosas es imposible cuando pestaña y fichero son el mismo objeto, y la mayoría de los editores acaban añadiéndolas después como funciones especiales con nombre propio —«split», «grupo de pestañas», «vista dividida»— en vez de como consecuencias de un modelo.
El patrón se llama separar el modelo de la vista, y no es una idea de editores. Es el motivo de que en una base de datos la tabla no sea la consulta, y puedas tener muchos cursores sobre los mismos datos. De que en el navegador el DOM no sea la pantalla, y el mismo nodo se pueda pintar de dos formas. De que en Git el árbol de trabajo, el índice y el historial sean tres cosas y no una, que es exactamente lo que permite preparar un commit parcial. Y de que en un sistema de ficheros un inodo no sea un nombre, que es lo que hace posible que dos rutas apunten al mismo contenido.
En los cuatro casos, el precio es idéntico: hay que aprender dos conceptos donde parecía haber uno, y durante un rato la herramienta parece innecesariamente complicada. Y el pago también: todo lo que la separación permite es gratis, en el sentido de que no hubo que diseñarlo. Nadie del proyecto tuvo que implementar «ver el mismo fichero dos veces»; sale solo de que las ventanas apuntan a buffers. Cuando veas una herramienta que fusiona dos conceptos para simplificar, la pregunta útil no es si es más fácil —lo es— sino qué acabas de perder la capacidad de decir.
Leer :ls: los estados de un buffer
Un buffer no está solo «abierto». :ls enseña una tabla con banderas, y saber leerlas explica casi todo lo que parece raro:
1 #h "src/main.lua" línea 42 2 %a "src/config.lua" línea 1 3 h + "README.md" línea 8 4 h "test/spec.lua" línea 15| Bandera | Significa |
|---|---|
% |
el buffer de la ventana actual |
# |
el buffer alterno: al que vuelves con Ctrl-^ |
a |
active: está visible en alguna ventana |
h |
hidden: cargado en memoria pero sin ventana |
+ |
tiene cambios sin guardar |
- |
no modificable |
= |
solo lectura |
u |
unlisted: existe pero no sale en :ls sin ! |
Que un buffer esté h es lo normal y es lo que quieres: sigue cargado, con su historial de deshacer, sus marcas y sus cambios sin guardar. Cambiar de buffer no te hace perder nada.
:ls! añade los unlisted, que es donde viven los buffers de ayuda, los de terminal y los que crean algunos plugins. Si alguna vez :ls no enseña algo que juras tener abierto, prueba con la exclamación.
La opción hidden, que ya no tienes que poner
Durante años, todas las configuraciones empezaban con set hidden, porque por defecto Vim se negaba a abandonar un buffer con cambios sin guardar. En Neovim viene activada de fábrica.
Si ves un tutorial que te dice que la pongas, es de antes. Y si ves uno que dice que la quites, huye.
El buffer alterno: la tecla que más se infrautiliza
Ctrl-^ —o :b#— salta al buffer marcado con #, que es el último en el que estuviste. Y pulsándolo otra vez, vuelves.
Alt-Tab, pero para ficheros
El 80% de lo que haces editando es ir y venir entre dos ficheros: el código y su test, la función y quien la llama, el componente y su estilo.
Para eso no hace falta un buscador, ni una lista, ni recordar un nombre: Ctrl-^ alterna entre los dos, en un golpe, y funciona igual estés donde estés. Es la tecla con mejor relación entre lo que cuesta aprender y lo que devuelve de todo este nivel.
Gestionar buffers
| Comando | Acción |
|---|---|
:ls / :ls! |
lista los buffers (con !, también los ocultos del sistema) |
:b {parte-del-nombre} |
salta al que coincida — completa con Tab |
:b 3 |
salta al buffer número 3 |
:bn / :bp |
siguiente / anterior |
]b / [b |
lo mismo, con los atajos nativos de 0.11 |
]B / [B |
al último / al primero |
<C-^> o :b# |
el buffer alterno |
:bd |
descarga el buffer (delete) |
:bw |
lo borra del todo (wipeout): pierde marcas y deshacer |
:bufdo {cmd} |
ejecuta un comando en todos los buffers |
:e! |
recarga el fichero del disco, descartando cambios |
:sav otro.txt |
guarda con otro nombre y sigue editando ese |
:bufdo es el hermano de :cdo de la lección 1.6, aplicado a buffers en vez de a entradas de una lista: :bufdo %s/viejo/nuevo/ge | update recorre todo lo que tienes abierto. La bandera e evita que se pare en el primer buffer donde no haya coincidencias.
El :bd que te descuadra la pantalla, y cómo evitarlo sin plugins
:bd descarga el buffer y cierra la ventana que lo mostraba. Si tenías un split cuidadosamente montado, se deshace.
La solución sin instalar nada es un idioma de dos comandos encadenados:
:bp | bd #Se lee así: «vete al buffer anterior, y luego borra el que acabas de dejar». Como la ventana ya está mostrando otro buffer cuando llega el bd, no hay ninguna ventana que cerrar y el layout sobrevive.
Hay plugins que envuelven esto en un comando bonito, y los verás en el Nivel 4. Merece la pena saber que por debajo hacen exactamente esta línea.
La lista de argumentos: el conjunto que tú eliges
Esta es la parte que casi ningún curso enseña, y resuelve un problema que la lista de buffers no resuelve bien.
La lista de buffers es todo lo que has abierto, en el orden en que lo abriste, y crece sola: un salto a definición, un fichero de ayuda, un git commit, y de pronto tienes treinta. Cuando quieres aplicar algo «a estos nueve ficheros y a ninguno más», esa lista no te sirve.
La lista de argumentos es un conjunto explícito, ordenado y editable, independiente de qué tengas abierto:
:args src/**/*.lua " define la lista: solo estos:args " enséñamela, con [corchetes] en el actual:argadd tests/util.lua " añade uno:argdelete README.md " quita uno:next :prev " moverse por ella:first :last:argdo %s/viejo/nuevo/ge | update:argdo es la razón de que exista. Es el mismo mecanismo que :bufdo, pero sobre el conjunto que tú definiste, no sobre lo que resulte estar abierto. La diferencia entre «refactoriza estos nueve ficheros» y «refactoriza los treinta que tengo abiertos, incluidos tres de ayuda y un commit a medias» no es pequeña.
Y tiene sus atajos de corchete, como todo lo demás en Neovim: ]a y [a para moverte, ]A y [A para ir a los extremos.
Cómo se usa de verdad
Casi siempre la llenas desde el shell o desde una búsqueda:
nvim $(rg -l "funcionVieja") # arranca con la lista ya puesta:args `rg -l funcionVieja` " o desde dentro, con acentos graves:argdo %s/funcionVieja/funcionNueva/ge | updateEso es un refactor en dos líneas sobre exactamente los ficheros que contienen el símbolo. En el Nivel 3 lo harás con el LSP y será más preciso; esto funciona hoy, en cualquier lenguaje, y sin haber instalado nada.
Ventanas
Una ventana es una vista sobre un buffer. Todos los comandos empiezan con Ctrl-w:
| Comando | Acción |
|---|---|
<C-w>s / <C-w>v |
dividir en horizontal / vertical |
<C-w>n |
ventana nueva con un buffer vacío |
<C-w>h j k l |
moverse en esa dirección |
<C-w>w / <C-w>W |
a la siguiente / anterior, cíclicamente |
<C-w>p |
a la ventana anterior, el Ctrl-^ de las ventanas |
<C-w>t / <C-w>b |
a la primera / última |
<C-w>c |
cierra la ventana sin tocar el buffer |
<C-w>o |
cierra todas menos esta |
<C-w>= |
igualar tamaños |
<C-w>_ / <C-w>| |
maximizar alto / ancho |
{n}<C-w>+ - < > |
crecer o encoger n líneas o columnas |
<C-w>H J K L |
mover la ventana a ese borde |
<C-w>x |
intercambiarla con la de al lado |
<C-w>r / <C-w>R |
rotar todas |
<C-w>T |
sacarla a una pestaña nueva |
:sb {buffer} |
abrir un buffer en un split |
Cuatro de estos cambian cómo se trabaja:
Ctrl-w``p vuelve a la ventana en la que estabas antes. Es el equivalente exacto de Ctrl-^ para ventanas, y con dos o tres splits abiertos es más rápido que pensar en qué dirección está la que quieres.
Ctrl-w``H J K L en mayúscula mueve la ventana al borde que digas, convirtiendo un split horizontal en vertical sobre la marcha. Si abriste algo en horizontal y lo querías en vertical, Ctrl-w``L lo arregla sin cerrar nada.
Ctrl-w``T saca lo que estés mirando a su propia pestaña, a pantalla completa. Es lo que quieres cuando un split se te ha quedado pequeño.
Ctrl-w``c cierra la ventana y deja el buffer cargado, que es justo lo contrario que :bd.
Dos opciones que cambian dónde aparecen los splits
Por defecto, <C-w>s abre la ventana nueva arriba y <C-w>v la abre a la izquierda, lo cual sorprende a casi todo el mundo porque desplaza hacia abajo y hacia la derecha lo que estabas leyendo.
Se invierte con 'splitbelow' y 'splitright'. No las pongas todavía —van en tu configuración, en el Nivel 2—, pero pruébalas ahora con :set splitbelow splitright y decide cuál prefieres. Es de las dos opciones que más gente cambia el primer día.
Pestañas: disposiciones, no ficheros
| Comando | Acción |
|---|---|
:tabnew |
pestaña nueva con un buffer vacío |
:tab split |
pestaña nueva con el buffer actual |
gt / gT |
siguiente / anterior |
{n}gt |
ir a la pestaña n |
:tabmove {n} |
reordenarlas |
:tabclose / :tabonly |
cerrar esta / todas menos esta |
:tabdo {cmd} |
ejecutar un comando en cada pestaña |
Úsalas para contextos distintos, no para ficheros: una pestaña con el código y sus tests, otra con la documentación, otra con un git diff a pantalla completa. Si te descubres abriendo una pestaña por fichero, lo que quieres son buffers.
:tab split es la más útil de la tabla y la menos conocida: te lleva lo que estás mirando a una pestaña propia sin perder el split de la pestaña original, así que puedes ampliar un rato y volver con gT a la disposición intacta.
Que mañana siga todo donde lo dejaste
Hay dos mecanismos y hacen cosas distintas. Confundirlos es la causa de que alguien afirme que «Neovim recuerda dónde lo dejaste» y otro alguien compruebe que no.
shada: lo que se guarda siempre, sin que hagas nada
Al salir, Neovim escribe un fichero binario en stdpath('state') con el historial que sobrevive entre sesiones:
- las marcas mayúsculas (
A–Z), que apuntan a fichero y posición; - el contenido de los registros;
- el historial de la línea de comandos y el de búsquedas;
- la lista de buffers;
- la última posición del cursor en cada fichero;
- la jumplist.
Por eso, si hoy pones mA en un fichero y mañana pulsas `A, llegas —aunque hayas cerrado el editor entre medias—. Y por eso `" te devuelve a donde estabas la última vez que editaste ese fichero. Se controla con la opción 'shada' y se documenta en :h shada.
Lo que shada NO guarda
No guarda tu disposición de pantalla ni qué ficheros tenías abiertos en cada ventana. Guarda historial, no espacio de trabajo.
Tampoco guarda nada de los ficheros bajo /tmp ni /private, que Neovim excluye a propósito desde la 0.12: son ficheros efímeros —mensajes de commit, ficheros temporales de otros programas— y llenaban el historial de ruido.
Sesiones: lo que se guarda si lo pides
Una sesión sí graba el espacio de trabajo: las pestañas, las ventanas, sus tamaños, qué buffer hay en cada una y el directorio de trabajo.
:mksession! Session.vim " guárdalo (con ! sobrescribe):source Session.vim " restáuralo desde dentronvim -S Session.vim # o restáuralo al arrancarQué entra exactamente lo decide la opción 'sessionoptions'. Y una advertencia práctica: por defecto también guarda las opciones y los mapeos, lo cual suena bien y en la práctica es una fuente de sorpresas —restauras una sesión de hace tres meses y te trae opciones que ya cambiaste en tu configuración—. Casi todo el mundo acaba quitando options de esa lista.
El truco de reiniciar sin perder el sitio
Ya viste :restart en la lección 0.2, y viste que no restaura tus buffers. Con esto puedes combinar las dos cosas, y es el propio manual el que lo enseña:
:mksession! Session.vim | restart source Session.vimGuarda la sesión, reinicia el editor con la configuración nueva, y lo primero que hace al arrancar es restaurarla. Es lo más parecido a recargar Neovim en caliente que existe.
Alterna con el buffer marcado # en :ls. Púlsalo otra vez y vuelves.
Primero cambias de buffer en esa ventana, y luego borras el que dejaste. Así no queda ninguna ventana huérfana que cerrar.
Las mayúsculas de <C-w> mueven la ventana a un borde, reorganizando el layout sin cerrar nada.
La lista de argumentos es el conjunto que tú defines. :bufdo actuaría sobre todo lo abierto, incluida la ayuda.
Abre el buffer actual en una pestaña nueva. gT te devuelve a la disposición original, intacta.
Es el Ctrl-^ de las ventanas. Con tres splits, más rápido que calcular si era h o k.
Montar y desmontar un espacio de trabajo
- Abre cuatro ficheros. Comprueba con
:lsque son cuatro buffers y localiza cuál lleva%y cuál#. - Alterna entre dos con
Ctrl-^diez veces seguidas hasta que sea automático. - Divide en vertical, muestra el mismo buffer en las dos ventanas y desplaza una sola. Comprueba que es un documento con dos vistas, no dos copias.
- Cierra un buffer con
:bdy observa cómo se descuadra el layout. Deshaz el estropicio y ciérralo ahora con:bp | bd #. - Define una lista de argumentos con
:args src/**/*.lua, míralas con:argsy recórrelas con]a. - Monta una segunda pestaña con
:tab split, amplía, y vuelve congT. - Guarda todo con
:mksession! Session.vim, sal del editor, y vuelve connvim -S Session.vim. Comprueba que las pestañas y los splits están donde los dejaste. - Y la comprobación que separa los dos mecanismos: pon
mAen un fichero, sal sin guardar sesión, vuelve a abrir Neovim en otro directorio y pulsa`A. Llegas, porque eso es shada y no la sesión.