Saltar al contenido

La gramática de Vim (el superpoder)

Por qué un editor con cuarenta teclas hace más que uno con cuatrocientos atajos: los operadores esperan un complemento, y ese complemento puede ser cualquier movimiento. El modo operador-pendiente, los tres tipos de moción —exclusiva, inclusiva y de línea—, las dos reglas especiales que explican los casos raros, y qué repite exactamente el punto.

Todo editor resuelve el mismo problema combinatorio: hay muchas acciones y muchos ámbitos sobre los que aplicarlas, y hay que darle al usuario una forma de nombrar cada pareja. La respuesta habitual es enumerar: un atajo para borrar la línea, otro para borrar la palabra, otro para borrar hasta el final. Con diez acciones y veinte ámbitos hacen falta doscientos atajos, y nadie memoriza doscientos atajos. Vi tomó en 1976 la decisión contraria —componer en vez de enumerar— y esa es, cincuenta años después, la única razón de peso para aprender este editor. No es que tenga mejores atajos. Es que no los tiene.

  • Distinguir un sistema que enumera acciones de uno que las compone, y contar las combinaciones de cada uno.
  • Reconocer el modo operador-pendiente como un modo de verdad, con su propia tabla de mapeos.
  • Clasificar cualquier moción en exclusiva, inclusiva o de línea, y predecir qué borrará.
  • Aplicar las dos reglas especiales de :h exclusive que explican los casos que parecen caprichosos.
  • Decidir si una edición es repetible con . antes de teclearla.

Sumar atajos o multiplicar frases

Cuenta lo que tienes que aprender en cada modelo.

En un editor que enumera, cada capacidad nueva es una entrada nueva en la tabla. Añadir «borrar hasta el paréntesis que empareja» significa inventar un atajo, documentarlo y que tú lo memorices; y no te sirve para nada más. El coste de aprendizaje es lineal y el beneficio también: aprendes n atajos, obtienes n capacidades. La tabla crece hasta que deja de caber en la cabeza, y a partir de ahí la gente usa el ratón para todo lo que no esté en las diez primeras filas.

En Vim aprendes dos listas independientes: una de operadores —qué hacer— y otra de complementos —sobre qué—. Y luego cualquier elemento de la primera se combina con cualquiera de la segunda. Doce operadores y unos cuarenta complementos no dan cincuenta y dos capacidades: dan cerca de quinientas frases válidas, de las cuales tú nunca memorizaste ninguna. El coste sigue siendo lineal; el beneficio pasa a ser el producto.

La consecuencia práctica es la que importa, y es asimétrica de una forma que conviene ver despacio: aprender una moción nueva no te da un comando nuevo, te da uno por cada operador que ya conocías. El día que descubras % —saltar al delimitador que empareja— no habrás aprendido a saltar: habrás aprendido a borrar hasta la llave que cierra (d%), a copiar el bloque entero (y%), a indentarlo (>%) y a comentarlo (gc%), sin que nadie te enseñe ninguna de las cuatro. Y a la inversa: el día que aprendas el operador !, todos los complementos que ya sabías se convierten en formas de pasar texto por un programa externo.

Por eso la pregunta correcta cuando algo te resulta laborioso en Vim casi nunca es «¿qué atajo hay para esto?». Es «¿qué verbo y qué complemento estoy buscando?». Si el atajo existiera, sería una excepción al sistema; y las excepciones de este editor caben en una mano.

Texto
[conteo] operador  moción            → verbo + hasta dónde[conteo] operador  objeto-de-texto   → verbo + sobre qué

El modo operador-pendiente

Pulsa d y suelta. No pasa nada: el cursor no se mueve y no se borra nada. El editor no está colgado ni esperando un segundo carácter de un atajo compuesto. Está en un modo, tan real como Normal o Insertar, con su propio nombre y su propia tabla de mapeos.

Se llama operador-pendiente (:h Operator-pending). Se entra al pulsar un operador y se sale al recibir un complemento, al pulsar Esc, o al recibir algo que no sea ninguna de las dos cosas. Mientras dura, Neovim ha registrado qué vas a hacer y está esperando a que le digas dónde.

Que sea un modo con tabla propia no es un detalle de implementación: es lo que hace que el sistema sea extensible. vim.keymap.set("o", ...) define un mapeo que solo existe en ese instante, entre el operador y el complemento, y es como se añaden complementos nuevos sin robarle teclas a ningún otro modo. Un plugin que te da «el argumento de la función» como objeto de texto no está añadiendo un atajo de borrado: está añadiendo una entrada a esta tabla, y esa entrada funciona automáticamente con d, c, y, >, gc y con los operadores que definas tú. En el Nivel 11 escribirás los tuyos con operatorfunc.

Cómo verlo con tus propios ojos

Pulsa d y mira la esquina inferior derecha de la pantalla: Neovim muestra el operador pendiente mientras espera. Pulsa Esc y desaparece sin haber tocado el texto. Y para leer la tabla entera: :omap lista todos los mapeos activos en este modo, los del editor y los que hayan puesto tus plugins. :h omap-info explica sus reglas.

Los operadores del editor, sin plugins:

Operador Significado
d delete — borrar
c change — borrar y entrar en Insertar
y yank — copiar
> / < indentar / desindentar
= reindentar con el indentexpr del lenguaje
gu / gU a minúsculas / MAYÚSCULAS
g~ invertir mayúsculas y minúsculas
gq / gw reformatear al ancho de textwidth (gw conserva el cursor)
gc comentar — nativo desde Neovim 0.10
zf crear un pliegue
! pasar el texto por un comando externo y sustituirlo por la salida
g@ invocar tu propia función (operatorfunc)

Duplica el operador y actúa sobre la línea

dd borra la línea, yy la copia, >> la indenta, gcc la comenta, guu la pasa a minúsculas. Repetir el operador es la forma abreviada de decir «esta línea entera», y funciona con todos.

Dos merecen una frase aparte porque casi nadie los usa y son los que más devuelven. gqip reordena un párrafo de comentarios al ancho que tengas configurado, y es la diferencia entre un bloque de documentación cuidado y uno con líneas de sesenta y de ciento veinte columnas alternándose. Y ! convierte cualquier programa de tu sistema en un operador de edición: !ip sort ordena el párrafo, !ap jq . reformatea el JSON que tengas seleccionado, !G tac invierte el resto del fichero. No hay plugin que instalar; el catálogo de operadores de tu editor es el contenido de tu $PATH.

gc es nativo, pero solo son cuatro mapeos

Desde 0.10 el comentado viene en el core y no necesitas plugin. Existen exactamente cuatro formas: gc{moción}, gcc para la línea, gc en modo Visual, y gc como objeto de texto en operador-pendiente —de ahí que gcgc descomente el bloque de comentarios bajo el cursor, y dgc lo borre entero—. Lo que no existe en el core son gcA, gco ni gcO, que sí trae mini.comment y que verás en muchas configuraciones ajenas. Si los quieres, son un plugin; no los busques en :h commenting.

Los tres tipos de moción

Aquí está lo que hace que la gente concluya que Vim es caprichoso. No lo es: hay una clasificación de tres categorías y dos reglas especiales, y con eso se predice el cien por cien de los casos.

Una moción, cuando la usa un operador, tiene un tipo que decide qué trozo de texto queda dentro de la operación:

  • Exclusiva — el carácter donde aterriza el cursor no entra. w, b, 0, ^, (, ), {, }.
  • Inclusiva — el carácter donde aterriza sí entra. e, $, f{x}, t{x}, %.
  • De línea (linewise) — no hay caracteres que valgan: la operación se lleva líneas enteras, desde la primera hasta la última tocada. j, k, G, gg, H, M, L, '{marca}.

Esa tercera categoría es la que suele faltar en las explicaciones, y es la que explica el comportamiento que más sorprende:

Texto
    la puerta roja       ↑ cursor en la p de "puerta"dw  →  la roja        w es exclusiva: para ANTES de la r, se lleva el espaciode  →  la  roja       e es inclusiva: se lleva la a final, deja los dos espaciosd%  →  (nada aquí)    % es inclusiva: se llevaría el delimitador que emparejadj  →  ¡dos líneas!   j es de línea: ni columnas ni caracteres, líneas enteras

dw es el que quieres para eliminar una palabra de una lista, porque se lleva el separador. de es el que quieres cuando vas a escribir algo en su lugar, porque conserva el espaciado. No son dos comandos que hay que memorizar por separado: son el mismo operador con dos complementos de tipo distinto.

Esa diferencia entre «desde el cursor» y «la región entera» es exactamente la que separan las mociones de los objetos de texto, dos secciones más abajo.

Las dos reglas especiales

Cuando una moción es exclusiva y el punto final cae en la columna 1, entran en juego dos ajustes que están en :h exclusive y que explican prácticamente todos los «esto no hizo lo que yo esperaba»:

  1. El final se retrae al fin de la línea anterior, y la moción pasa a ser inclusiva. Sin esto, d} te dejaría una línea en blanco huérfana cada vez.
  2. Si además el inicio estaba en el primer carácter no blanco de su línea, la moción pasa a ser de línea. Por eso d} desde el principio de un párrafo borra el párrafo entero limpiamente, sin dejar restos de indentación.

No hace falta recitarlas. Basta con saber que existen para dejar de atribuir a la casualidad lo que es una regla escrita, y para saber dónde mirar cuando el resultado te sorprenda.

Las dos excepciones que sí hay que conocer de memoria

cw se comporta como ce. Si cw funcionara como dw, cambiarías la palabra, te comerías el espacio siguiente y tendrías que volver a escribirlo. Vi lo trata como caso especial desde el principio: con el cursor sobre un carácter no blanco, cw cambia hasta el final de la palabra. Está documentado en :h cw y es la única inconsistencia deliberada del sistema.

dw sobre la última palabra de una línea no se traga el salto. Aunque w por sí solo saltaría a la línea siguiente, cuando lo usa un operador la operación se detiene al final de la línea. Si no fuera así, borrar la última palabra juntaría dos líneas.

Forzar el tipo a media frase

Entre el operador y la moción puedes intercalar v, V o Ctrl-v para cambiar la clasificación sobre la marcha (:h o_v):

dvj por caracteres, no por líneasdVj fuerza líneas enterasdv$ invierte inclusiva ↔ exclusiva

dj borra dos líneas enteras porque j es de línea. dvj borra desde el cursor hasta la misma columna de la línea siguiente. Es el escape para el día en que la moción va al sitio correcto pero con la granularidad equivocada, y evita tener que buscar un comando distinto.

Objetos de texto: describir en vez de recorrer

Una moción describe un recorrido: desde donde estoy hasta allí. Un objeto de texto describe una región con sentido —una palabra, lo que hay entre comillas, un bloque, una etiqueta— y no tiene nada que ver con dónde esté el cursor dentro de ella.

Se usan con i (inner, solo el interior) o a (a, incluyendo los delimitadores).

Objeto Selecciona
iw / aw palabra / palabra con su espacio
iW / aW WORD (bloque sin espacios)
i" / a" dentro de comillas / con las comillas
i( ib dentro de paréntesis
i{ iB dentro de llaves
i[ / i< dentro de corchetes / angulares
it / at contenido de una etiqueta / etiqueta completa
ip / ap párrafo / párrafo con su línea en blanco
is / as frase
i` / a` dentro de comillas invertidas
i> / a> dentro de angulares — genéricos, atributos
iB / aB sinónimo de i{ / a{, cómodo de teclear
gc el bloque de comentario contiguo (0.10+)

aw no siempre incluye lo mismo, y está bien que así sea

aw es «una palabra con su espacio», pero ¿cuál? La regla real: se lleva el espacio de detrás, y si no hay —porque la palabra está al final de la línea— se lleva el de delante.

Eso es lo que hace que daw en mitad de una lista deje la lista bien puntuada y daw en la última palabra no deje un espacio colgando. Es una de esas decisiones de hace cuarenta años que solo se nota cuando falta.

Y aquí está la propiedad que los separa de todo lo demás: da igual dónde esté el cursor dentro de la región.

Texto
    printf("hola %s\n", nombre);              ↑ el cursor puede estar aquí              o aquí ↑         o aquí ↑ci"  →  printf("│", nombre);      en los tres casos

Con una moción tienes que colocarte primero y calcular hasta dónde llegar; son dos decisiones y la primera puede fallar. Con un objeto de texto describes qué, y la posición dentro deja de importar. Eso elimina por completo el paso de «primero me pongo en el sitio bueno», que es donde se va la mitad del tiempo en un editor convencional.

También aceptan conteo, y esto sí que casi nadie lo usa: d2i( borra el interior del segundo nivel de paréntesis contando hacia fuera, y c2i{ cambia el bloque que contiene al bloque en el que estás. Es la forma de subir un nivel en la estructura sin moverte a buscarlo.

ciw cambia la palabra enteradi” vacía el stringca( cambia los argumentos con sus paréntesisyi{ copia el interior del bloquedap borra el párrafo y su huecocit cambia el contenido de una etiqueta

Piensa en frases, no en teclas

Deja de leer ci" como «c-i-comilla». Léelo como lo que es: change inside quotes. daw = delete a word. yi( = yank inside parens. gqip = format inside paragraph.

Cuando empieces a pensar así dejarás de memorizar y empezarás a improvisar ediciones que no has visto nunca. Ese es el momento exacto en que Neovim deja de ser un editor difícil y pasa a ser el único que te sirve.

La tercera forma: seleccionar y luego operar

Hasta aquí la frase ha sido siempre verbo + complemento, en ese orden. Hay una segunda construcción que invierte el orden, y conviene entender cuándo usar cada una en vez de elegir por costumbre.

En modo Visual seleccionas primero —v por caracteres, V por líneas, Ctrl-v en bloque de columnas— y luego pulsas el operador, que se aplica a lo seleccionado. vi(d hace lo mismo que di(.

Texto
di(        verbo primero: describo la región y actúo a ciegasvi(  d     selección primero: veo la región resaltada, y luego actúo

Los complementos son los mismos: dentro de Visual, iw, a", ip y todos los demás amplían la selección, y repetirlos sube un nivel. Ponte dentro de un bloque anidado y pulsa vi{i{i{: la selección crece hacia fuera, un bloque cada vez.

Y tres teclas que hacen a Visual utilizable:

o salta al otro extremo de la seleccióngv vuelve a seleccionar lo últimoCtrl-v $ A añade al final de cada línea

o resuelve el problema de haber empezado a seleccionar por el lado equivocado: en vez de cancelar y volver a empezar, saltas al otro extremo y sigues extendiendo. gv recupera la última selección, y es lo que quieres después de aplicar algo y arrepentirte.

El modo bloque merece un párrafo propio

Ctrl-v selecciona un rectángulo de columnas, y ahí I y A hacen algo que no hacen en ningún otro modo: escribes una vez y el texto aparece en todas las líneas del bloque al salir a Normal.

Texto
Ctrl-v  jjj  I  //  <Esc>      comenta cuatro líneas insertando al principioCtrl-v  jjj  $  A  ;   <Esc>   añade un ; al final de cada línea, midan lo que midanCtrl-v  jjj  c  nuevo  <Esc>   sustituye la columna entera

El $ de la segunda es la clave y casi nadie lo conoce: extiende el bloque hasta el final de cada línea, así que funciona aunque tengan longitudes distintas. Sin él, A alinearía en una columna fija.

Visual es cómodo, pero rompe la repetición

Aquí está el motivo de que esta guía prefiera di( a vi(d cuando las dos valen.

. repite el último cambio, y tras una operación visual repite sobre una región del mismo tamaño contada desde el cursor, no sobre «el mismo tipo de región». Si borras una palabra con daw, . borra la palabra siguiente, mida lo que mida. Si la borras con vawd, . borra el mismo número de caracteres, que en la palabra siguiente casi nunca es lo correcto.

La regla práctica: usa Visual cuando la región no se pueda nombrar —una selección irregular, un rectángulo, un rango que decides mirando— y usa verbo + complemento cuando sí. La primera es para casos únicos; la segunda es la que se multiplica con ..

El punto, y qué frases se repiten

. repite el último cambio. No repite movimientos, ni comandos de dos puntos, ni búsquedas: repite la última cosa que modificó el texto. Y «la última cosa» incluye la frase entera y lo que escribiste después de ella:

Texto
ciw  nuevoNombre  <Esc>     un solo cambiow                            te mueves (esto NO cuenta como cambio).                            cambia esa otra palabra por "nuevoNombre"

De ahí sale el patrón de refactor más útil del editor, sin macros y sin buscar-y-reemplazar:

Texto
/viejoNombre<CR>    buscaciw nuevo <Esc>     cambia la primeran.                  siguiente, repiten.                  siguiente, repite

Revisas ocurrencia por ocurrencia y decides cuáles cambias, que es justo lo que un :%s///g ciego no te deja hacer. Y tiene una versión aún más corta: gn selecciona la siguiente coincidencia de la búsqueda, así que cgn nuevo <Esc> cambia la próxima, y a partir de ahí . busca y cambia a la vez. Sin n intermedia. En el Nivel 1 lo verás junto al resto de :substitute.

Repetible es independiente de la posición

ciw funciona en cualquier palabra. lllcw no funciona en ninguna otra línea: describe un recorrido, no una región.

Una frase, un cambio

Si sales a Normal y vuelves a entrar, son dos cambios y . solo repite el segundo. Piensa la frase antes de teclearla.

Los conteos multiplican

3dw y d3w son lo mismo. 2d3w borra seis palabras: los dos conteos se multiplican.

La prueba de repetibilidad

Antes de teclear una edición, hazte una pregunta: ¿esta misma frase haría lo correcto en la línea de abajo? Si la respuesta es sí, acabas de convertir un trabajo de n ediciones en uno de una edición y n−1 pulsaciones de .. Si es no, casi siempre es porque empezaste con movimientos de ajuste —ll, hh, 3l— en vez de con un objeto de texto.

Tu chuleta mental

Texto
VERBOS:    d  c  y  >  <  =  gu  gU  g~  gq  gc  zf  !  g@MOCIONES:  exclusivas   w  b  0  ^  (  )  {  }           inclusivas   e  $  f{x}  t{x}  %           de linea     j  k  G  gg  H  M  L  '{marca}OBJETOS:   iw aw  iW aW  i" a"  i( a(  i{ a{  i[ a[  it at  ip ap  is as  gcFUERZA:    v  V  Ctrl-v   entre el verbo y el complemento

Cualquier verbo × cualquier complemento es una frase válida. La tabla de combinaciones no se memoriza: se deduce.

Da igual dónde estés entre las comillas: el objeto de texto resuelve la posición por ti. Con una moción tendrías que haberte colocado primero.

di( vacía el interior. Con da( te habrías llevado también los paréntesis.

daw se lleva la palabra y su espacio. Con diw habrían quedado dos espacios seguidos.

! es un operador: manda el texto a un programa externo y lo sustituye por su salida. Cualquier comando de tu sistema es una herramienta de edición.

Los objetos de texto aceptan conteo. 2i{ sube un nivel en el anidamiento.

gn selecciona la siguiente coincidencia, así que . busca y cambia en un solo golpe.

El $ extiende el bloque hasta el final de cada línea. Sin él, A alinearía en una columna fija y el resultado quedaría torcido.

gv recupera la última selección visual. Evita tener que volver a seleccionar a mano.

dj borraría dos líneas completas porque j es de línea. La v fuerza que la operación sea por caracteres.

Componer es la decisión de diseño, y no es una decisión sobre editores

Merece la pena aislar lo que acabas de aprender de todo lo que huele a Vim, porque el patrón es mucho más grande que este editor y reconocerlo cambia cómo lees el resto de tus herramientas.

Hay dos maneras de darle poder a un usuario. La primera es anticipar lo que va a querer hacer y proveerlo: un botón, un comando, una opción en un menú. Escala sumando, y el techo lo pone la imaginación de quien diseñó la herramienta. Todo lo que no previó es, para ti, imposible. La segunda es dar piezas y una ley de combinación, y aceptar que la mayoría de las composiciones válidas no las va a usar nadie nunca, a cambio de que las que sí importan estén disponibles sin haber sido previstas. Escala multiplicando, y el techo lo pones tú.

Cuando lo ves, aparece en todas partes. Las tuberías de Unix son exactamente esto: nadie diseñó grep, sort y uniq pensando en tu pipeline de esta tarde, y funciona porque el acuerdo —un flujo de texto por la entrada, otro por la salida— es más pequeño que cualquiera de los programas. Los combinadores de una librería de parsers, los tipos algebraicos, la composición de funciones, la aritmética misma: todos hacen el mismo trato. Y el precio también es el mismo, y es real: la curva de entrada es más dura, porque tienes que aprender la ley antes de obtener el primer resultado, mientras que un menú te da la primera victoria en diez segundos. Los sistemas componibles siempre pierden la demo y siempre ganan el año.

Ese es el trato exacto que estás haciendo con este editor. Las dos primeras semanas serás más lento que con el editor que ya tenías; no hay ninguna versión honesta de esta guía que te diga otra cosa. Lo que compras a cambio es un sistema cuyo techo no lo fijó nadie: cuando en el Nivel 11 escribas tu propio operador con operatorfunc, se combinará automáticamente con los cuarenta complementos que ya existen, y cuando definas un objeto de texto con vim.keymap.set("o", ...), funcionará con los doce operadores sin que tú escribas una sola línea para conectarlos. Nadie tendrá que haber previsto esa pareja. Ese es todo el argumento, y no hay ninguno más.

Habla el idioma

Con un archivo de código abierto, resuelve cada tarea con una sola frase de edición:

  1. Renombra la variable bajo el cursor → ciw y el nombre nuevo.
  2. Vacía un string desde cualquier punto de dentro → ci".
  3. Cambia todos los argumentos de una llamada, paréntesis incluidos → ca(.
  4. Copia un bloque { } entero → ya{.
  5. Comenta un párrafo, y luego descoméntalo sin seleccionar nada → gcap, y después gcgc.
  6. Reformatea un comentario largo → gqip.

Y tres comprobaciones que verifican lo que afirma la lección:

  1. Pulsa d, mira la esquina inferior derecha y cancela con Esc. Luego ejecuta :omap y comprueba que el modo tiene su propia tabla de mapeos.
  2. Sitúate en el primer carácter no blanco de un párrafo indentado y pulsa d}. } es exclusiva, pero acabas de borrar líneas enteras: es la segunda regla especial de :h exclusive. Repítelo desde media línea y observa la diferencia.
  3. Con el mismo texto, compara dw y de sobre la palabra del medio. Cuenta los espacios que quedan y explica cuál usarías para eliminar un elemento de una lista.

Cuando las seis primeras te salgan sin pensar, has terminado la lección más importante del Nivel 0.