¿Cómo encontrar y reemplazar en Vim sin tener que escribir la palabra original?


53

Me gustaría optimizar mi flujo de trabajo "buscar y reemplazar" en Vim. Es algo que hago a menudo, como estoy seguro de que la mayoría de ustedes también. Por lo general, algo similar a: copiar un bloque y cambiar el nombre de una variable en algunos lugares. Lo sé, lo sé, eso probablemente desencadena tu reflejo de "por qué estás copiando y pegando código", pero no sigamos ese camino ... Hay muchos casos de uso válidos :)

Soy muy consciente de los comandos de búsqueda y reemplazo: :so :%spero no me gustan. Me obliga a escribir tanto el nombre de variable completo que estoy buscando como a qué lo estoy cambiando. ¿Quizás hay una mejor manera de arreglar la cantidad de tipeo :%s? A menudo utilizo nombres de variables descriptivos largos, por lo que es realmente un factor decisivo para mí. Tampoco me gusta cómo escribir un nombre de variable desde cero es propenso a los errores tipográficos y puede consumir tiempo y poder intelectual buscando errores tipográficos. Prefiero escribirlo una vez, y luego copiar y pegar para evitar esto por completo si es posible.

Mi flujo de trabajo actual utiliza alguna combinación de movimiento / tirón / selección / búsqueda / colocación para moverse por el archivo y reemplazar uno por uno. No es genial, pero tiene la ventaja de evitar escribir nombres de variables completos. Es posible que solo necesite escribir las primeras letras con /o usar otro comando de movimiento (por ejemplo fx) dependiendo de lo que esté alrededor y luego presionar vepara seleccionar la palabra completa. Tampoco me importa que tenga que repetir para cada instancia. Nunca hago un reemplazo completo sin confirmar cada cambio. Pero sería mucho mejor si pudiera repetir la acción de reemplazo con una sola pulsación de tecla (que no puedo hacer con este método). cada sustitución es generalmente algo como ncontinuación vea continuación p(o incluso peor "0p)

¿Hay una manera mas rápida?


1
.repite el último "bloque de comandos" (no estoy seguro del término exacto. por ejemplo: mover + acción + texto. por ejemplo: cwtoto<Esc>(cambiar del cursor al final de la palabra con "toto"), c/foo<enter>bar<Esc>(cambiar del cursor a justo antes " foo ", y reemplazar con" barra "). A continuación, puede mover (con el cursor, hjkl o un número + hjkl (lo hace n veces), G (ir al final del archivo), / algo (ir justo antes" algo "), etc.) y luego presione .para rehacer el mismo" bloque de comandos "
Olivier Dulac

Yo uso esto bastante! Sin embargo, no funciona si desea tirar una palabra y luego reemplazar otra palabra con ella en varios lugares. Ese es el caso de uso principal con el que tengo un problema, y ​​es algo así n ve "0p. Que desafortunadamente no se puede replicar con solo un.
Joel

55
n ce ctrl-r 0 <esc> n.n.n.n.n.n.
Rico

@Joel también puede grabar una macro (qa, luego todo lo que desee, luego q: crea la macro a), y luego use: @a para reproducirla (y, como de costumbre, 134 @ a para hacerlo 134 veces). la macro puede ser tan elaborada como sea necesaria (ejemplo: encontrar algo, moverse al lugar apropiado, luego cambiar la palabra a eso, luego moverse al lugar apropiado para poder ser llamado nuevamente en una mejor posición)
Olivier Dulac

1
He aprendido trucos de VIM más útiles de esta pregunta que de cualquier otra pregunta en el sitio. ¡Gracias!
dotancohen

Respuestas:


81

De hecho, tengo un flujo de trabajo bastante similar al tuyo (copiar y pegar bloques que son similares, luego usar :spara cambiar nombres de variables) especialmente cuando escribo muchas líneas que son similares, excepto la variable que usan. Pero hay un par de cosas que hago que pueden resultarle útiles.

Cosas generales vim

  1. Lo primero que lo ayudará es darse cuenta de que :s//foo/gcompletará el término de búsqueda que utilizó en último lugar y lo reemplazará con foo . Eso solo puede ahorrar mucho tiempo, pero cuando lo combina con el comando asterisco (busque la palabra debajo del cursor actual) ahorra toneladas de tiempo. Por ejemplo, para cambiar todo fooa spam, en lugar de hacer

    :%s/foo/spam/g
    

    Lo que requiere que escriba tanto foo como spam (que pueden ser variables largas), simplemente puede navegar a foo y hacer:

    *:%s//spam/g
    

    De esta manera, puede obtener rápidamente el nombre de la variable que está cambiando buscándola, luego no necesitará escribirla. Esto también funcionará bien con su flujo de trabajo actual de usar nmucho. También es útil tener hlsearchactivado, porque entonces puedes saber visualmente dónde están las variables que estás reemplazando.

    (nota al margen: el comando asterisco agregará límites de palabras, es decir, \<y \>alrededor de la palabra debajo del cursor. Si no desea esto, use g*en su lugar).

  2. sería mucho mejor si pudiera repetir la acción de reemplazo con una sola pulsación de tecla

    Puede usar @:para rehacer el último comando sustituto. Eso es bueno si quieres hacer un :scomando en muchas líneas, pero no todas, así :%sque no funcionará. Otra opción es la bandera de confirmación, es decir :%s///gc. Esto le permitirá escribir yo nen cada caso para decidir si desea reemplazarlo o no.

    Como señaló Desty , también puede hacer &para volver a ejecutar el último comando sustituto, pero tenga en cuenta que esto no mantendrá sus banderas (como globalo confirm). Si eso es bueno o malo depende de su opinión personal. Si desea utilizar esto, pero también debe mantener sus banderas, puede hacer

    nnoremap & :&&<cr>
    
  3. Si desea cambiar cada aparición en un bloque, pero no todas las coincidencias en el archivo, siempre puede usar una selección visual del bloque, que completará

    :'<,'>
    

    a su sustituto, que lo restringe a las líneas que ha seleccionado. Cuando se combina con el enfoque de asterisco, me parece extremadamente útil. Además, si está haciendo múltiples comandos sustitutos en el mismo bloque de líneas, puede usar gvpara volver a seleccionar las mismas líneas para hacer otro comando. (ya sea :substituteun comando normal o un comando normal)

    Si realiza otro comando de sustitución, no necesita volver a seleccionar las mismas líneas, ya :'<,'> que seguirá operando en las mismas líneas. Sin embargo, es más conveniente escribir

    gv:s
    

    más bien que

    :'<,'>s
    

Plugins / Mapeos que me gustan

  1. Tengo el siguiente mapeo en mi .vimrcque extiende el enfoque de asterisco para que solo tenga que escribir el nuevo nombre deseado:

    "(R)eplace all
    nnoremap <leader>r yiw:%s/\<<C-r>"\>//g<left><left>
    

    Para usar esto, simplemente coloque el cursor en la palabra que desea cambiar, escriba <leader>rNewVarName<cr>y listo. Tenga en cuenta que esto reemplazará todas las coincidencias sin darle una opción para confirmarlas, por lo que es posible que no le guste tanto. Por supuesto, podrías cambiarlo a

    nnoremap <leader>r yiw:%s/\<<C-r>"\>//gc<left><left><left>
    

    Para permitir la opción de confirmación.

    Editar : Después de tanto la respuesta de la misa y el comentario de rcorre , también se puede escribir esto como

    nnoremap <leader>r :%s/\<<C-r><C-w>\>//g<left><left>
    

    Lo cual tiene la ventaja de mantener su registro sin nombre igual.

  2. Si cambia el nombre de grupos de variables similares al mismo tiempo, por ejemplo, cambiando

    fooSuffix
    PrefixFoo
    foo1
    SHOUTINGFOO
    

    a

    barSuffix
    PrefixBar
    bar1
    SHOUTINGBAR
    

    Puede ser un dolor sustituirlos todos individualmente. Aquí es donde tpope / vim-abolish es útil. Puede reemplazar todo esto en un solo comando con:

    :%S/foo/bar/g
    

    y se encarga de la capitalización por ti.

Esto debería ayudarlo mucho, pero avíseme si hay algo acerca de estos enfoques que no le gusta o siente que le falta. Estoy feliz de hablar sobre más enfoques. :)


Lectura recomendada:


Oh, acabo de aprender varias cosas aquí. ¡Agradable! Un enfoque alternativo que podría tomar es <leader>rc(o cr) agregar eso /cal final. Además, he set gdefaultestablecido, porque casi nunca quiero reemplazar un solo resultado, por lo que sería yiw:%s/\<<C-r>"\>//<left><left>
Wayne Werner

2
¡Qué respuesta tan fantástica!
Scott Lundberg

2
Si lo usa ctrl+r-ctrl+wen el mapeo (según lo sugerido por @Mass), debería funcionar igual pero no interferirá con su registro de extracción (lo que puede o no ser deseable)
rcorre

1
Una cosa para recordar cuando trabaje con bloques de texto que resalte (antes de cortar / tirar, etc.) es que puede ingresar gv en modo normal para volver a resaltar el último texto resaltado.

1
He escrito el complemento github.com/hauleth/sad.vim que funciona de manera similar al método 2. hovewer le permite usar .para realizar sustituciones adicionales.
Hauleth

20

La c_CTRL-Rfamilia de mapas puede mejorar un poco su flujo de trabajo:

  1. No necesita escribir nombres de variables cuando lo usa :s/, solo use CTRL-R CTRL-Wpara insertar la palabra debajo del cursor en la línea de comando (advertencia: esto no escapa de caracteres especiales, como lo *hace).

  2. Después de hacer un pequeño cambio usando ce, puede buscar la palabra vieja usando / CTRL-R -. Luego use .para repetir el cambio.

  3. Si necesita realizar cambios en el patrón de búsqueda CTRL-R /, colocará la búsqueda existente en la línea de comando.

  4. Finalmente, puede poner el contenido de cualquier registro usando CTRL-R {register}


¡Buen punto! Siempre me olvido de Ctrl R. Veré si puedo trabajar en eso también.
Joel

2
Vale la pena mencionar específicamente Ctrl-R /? Lo uso con bastante frecuencia en los :scomandos si quiero usar un término de búsqueda que sea casi pero no exactamente el mismo que el anterior.
Rico

12

Hay un par de métodos más que (¡para mi sorpresa!) Aún no se han mencionado.

Usando el gncomando

gnfunciona como el ncomando, excepto que además de saltar al partido, ingresa al modo visual, con todo el partido seleccionado.

Entonces, para cambiar una palabra (¡o cualquier cosa que pueda coincidir con una expresión regular!) Primero búsquela 1 , y luego presione cgnseguido del texto con el que desea reemplazar la coincidencia y Escvolver al modo normal.

(Menciona que a veces el texto de reemplazo que desea usar se almacena en el "0registro de extracción; tenga en cuenta que puede ingresarlo rápidamente sin tener que volver a escribirlo presionando Ctrl+R0en el modo de inserción).

¡Luego puedes saltar al siguiente partido y repetir el cambio con solo .presionar una tecla!

Si hay lugares donde no desea hacer el reemplazo, simplemente presione unpara deshacer el cambio y salte al siguiente partido.

1: Rápidamente, mediante el uso de otras sugerencias en esta página como *, Ctrl+RCtrl+Wetc. Consulte también el comentario de Peter Rincker para obtener más sugerencias sobre cómo completar inicialmente su búsqueda.

Uso del modo de selección

(Esto lo aprendí de romainl 's puesto en el subreddit vim . No sé si él lo inventó él mismo o estaba de paso en.)

El modo de selección es un poco como el modo visual, excepto que si comienza a escribir, el texto seleccionado se reemplaza inmediatamente con lo que escribe (como cómo funciona la selección en la mayoría de los otros editores).

Por lo tanto, puede usar este trío de asignaciones para hacer una búsqueda / reemplazo muy rápido, que puede cambiar a mitad de archivo, si es necesario:

nnoremap § *``gn<C-g>
inoremap § <C-o>gn<C-g>
snoremap <expr> . @.

Asignación de modo normal : ingresa al modo de selección con la palabra más cercana al cursor seleccionado.

Luego puede escribir su reemplazo para la palabra seleccionada y ( sin presionar Esc) usar:

Asignación de modo de inserción : salta a la siguiente coincidencia e ingresa al modo de selección.

Luego puede escribir un nuevo reemplazo o presionar .para usar:

Asignación de modo de selección : vuelve a ingresar el último texto insertado, esencialmente, repite la última inserción


¡ Mencioné el gnmétodo al final de mi respuesta vi.stackexchange.com/a/13696/488 ayer por lo que vale! Sin embargo, su respuesta tiene un formato mucho más agradable.
TankorSmash

1
@TankorSmash ¡Así lo hiciste! Tenga en cuenta cómo me perdí eso. Sin embargo, tenga en cuenta que el método real que estoy usando es diferente al suyo (solo invoco gnuna vez), y es el método específico (en lugar de solo el gncomando) que me sorprendió que aún no se haya mencionado.
Rico

Recomendaría tener algún tipo de complemento "visual star" para usar gn. Te da más opciones de búsqueda. Pobre hombre es el mapeo visual estrella: xnoremap * :<c-u>let @/=@"<cr>gvy:let [@/,@"]=[@",@/]<cr>/\V<c-r>=substitute(escape(@/,'/\'),'\n','\\n','g')<cr><cr>. Episodio de Vimcasts relacionado: Operando en coincidencias de búsqueda usando gn
Peter Rincker

1
He creado el complemento sad.vim que ayuda con ese flujo.
Hauleth

7

El complemento https://github.com/terryma/vim-multiple-cursors se ocupa de este problema en su mayor parte.

Mi flujo de trabajo suele ser el siguiente:

  1. Mueva el cursor a la palabra para reemplazar.
  2. Sostenga ctrl-nhasta que todo esté seleccionado.
  3. Presione cy comience a escribir el reemplazo.

Lo conveniente aquí es que también puede realizar operaciones más complicadas que simplemente buscar-reemplazar, aunque creo que si lo lleva demasiado lejos, el complemento comienza a mostrar algunos problemas técnicos.


4

Otra forma de hacer cosas que aún no se ha mencionado: puede usar la ventana de línea de comandos (abierta q:en modo normal; consulte :help q:para obtener más información), en la que tiene acceso completo a las funciones de autocompletado de Vim ( i_CTRL-N/ i_CTRL-Py las diversas i_CTRL-Xsecuencias estar entre ellos).

En la ventana de la línea de comandos también puede usar comandos en modo normal para editar la línea de comandos, lo que, por ejemplo, significa que puede tirar y pegar partes de comandos anteriores en el historial de la línea de comandos.

Tenga en cuenta que su cursor se moverá a la ventana de la línea de comandos cuando lo use, lo que significa que CTRL-R CTRL-Wy los métodos similares que implican insertar el texto debajo del cursor no funcionarán allí, a diferencia de la línea de comando normal.


1
También puede abrir la ventana de la línea de comandos cuando ya está a la mitad de ingresar su :substitutecomando presionando<ctrl-f>
Rich

2

La solución a esto está algo entre las otras respuestas para mí:

Supongamos que tiene 6 líneas idénticas y desea reemplazar 'manzana' en las dos del medio:

I have apples for days
I have apples for days
I have apples for days
I have apples for days
I have apples for days
I have apples for days

Lo que hago es:

  1. Llego al comienzo del texto que quiero reemplazar, 'manzana'
  2. Pulsa vpara entrar en modo visual
  3. Vaya a la última palabra que quiero reemplazar, en este caso algunas líneas 2j
  4. :s/<C-R><C-W>/que utiliza la selección visual como rango para :s, y luego inserta la palabra debajo del cursor
  5. Golpeé <C-F>que lo pone en un modo en el que puede editar su comando como si fuera un búfer. Por lo tanto, puede usar cualquier complemento o complementos que desee para elegir la palabra de reemplazo, y ya está.

Todo en una línea, el flujo para esto sería, /apples<CR>2nv/apples<CR>nn:s/<C-R><C-W/oranges<CR>pero eso se ve peor de lo que es.

En su caso, puede anexar el /cque el :scomando para omitir los que no desea, si su rango contiene unos cuantos desea omitir: :s/<C-R><C-W/orange/c.

Alternativamente (y quizás de manera más simple), puede buscar la palabra completa que desea reemplazar, ccolgarla gny luego .repetir la búsqueda y aplicar el mismo cambio nuevamente. Parecería /\<apple\><CR>viwcorange<ESC>gn.gn.gn..ngn.repetirlo en tres, luego omitir una instancia y luego repetirla por última vez.


El método que describe al final no funciona para mí (en Vim 7.4, invocado con vim -Nu NONE). Simplemente suena cuando presiono el primero .. ¿Alguna idea de por qué es eso? En cualquier caso, sería más fácil de usar ciwen lugar de viwy nen lugar de gnhacer cambios en esta forma.
Rico

2

Esto no está completamente relacionado con la pregunta, pero aún así debería ayudar a seleccionar la palabra para reemplazar.

La búsqueda incremental ( set incsearch) le permite escribir el prefijo de la palabra que necesita encontrar, y vim saltará automáticamente a la primera aparición del prefijo a medida que escribe más y más. La búsqueda se confirmará si presiona Enter y se descartará en Esc (en este último caso, el cursor volverá a donde estaba antes de comenzar la búsqueda).

Sin embargo, la búsqueda incremental proporciona una característica más útil. Cuando ya haya escrito el prefijo de una palabra y el cursor esté en la posición correcta, presione <C-L>para agregar caracteres de la palabra debajo del cursor a la cadena de búsqueda. Esto le permite ingresar la cadena reemplazada casi sin presionar teclas.

Entonces, si quiero reemplazar someOddVariableNamecon evenOdderVariableName, yo

  1. escriba /someOddVy llegue a la ocurrencia de someOddVariableName;
  2. mantenga presionado <C-L>hasta que someOddVariableNameaparezca completamente en la línea de búsqueda;
  3. presione Entrar para confirmar la búsqueda;
  4. :%s//evenOdderVariableName/go el comando de reemplazo que necesite; Es posible que desee seguir otras respuestas aquí.

2

Puede poner el nombre de la variable en el búfer primero ( ywpara copiar una palabra), luego copiar y pegar :%spresionando <C-r>".


2

Si desea reemplazar una variable dentro del nuevo bloque (pegado), tengo dos sugerencias:

Reemplazo automatizado

  • Ir a la primera línea del nuevo bloque, golpear ma. Esto es marcar una ubicación y nombrarla a.

  • Ir a la última línea del nuevo bloque, golpear mb. Ahora tiene dos ubicaciones con nombre.

  • A continuación, coloque entre las dos líneas, así: :'a,'bs/oldVarName/newVarName/g.

  • Es decir, el alcance del reemplazo está entre sus dos marcas.

La forma manual

  • Ir a la primera línea del nuevo bloque.

  • Use /oldVarNamepara encontrar la primera instancia de oldVarName.

  • Cámbialo así: cwnewVarName<Esc>cambiará la palabra newVarNameay escapará.

  • Use npara ir a la siguiente instancia de oldVarName.

  • Use .para repetir el último cambio (es decir, repite eso cw).


2

Muchas de las otras respuestas aquí ya cubren las formas integradas para mejorar el flujo de trabajo de buscar y reemplazar, pero quería mencionar abolish.vim , un complemento de Tim Pope. Realiza reemplazos en los que desea abarcar una gama más amplia de casos, normalmente causados ​​por variaciones en el caso o porque necesita lidiar con formas singulares y plurales de una palabra.

:S/child{,ren}/adult{,s}/gcorrectamente convertidos childa adult, Childrena Adultsy CHILDa ADULT.

Si está tratando con palabras en camello, debe incluir las mayúsculas donde las necesita en las palabras originales y de reemplazo.

%:S/wordWrap/textBreak/gmaneja correctamente ambos wordWrapy wordwrap.


0

Como otros ya han señalado, si tiene el cursor posicionado encima de su palabra, puede usar la familia de comandos Ctrl + r, siendo Ctrl + r Ctrl + w probablemente el más adecuado para usar.

Sin embargo, ese método solo funciona para insertar una palabra (la que está justo debajo del cursor), pero ¿y si hay varias porciones de texto en el búfer que queremos usar? Como no puede mover el cursor mientras está en la línea de comando. La mejor manera de hacer esto es copiar partes del texto que necesitará en diferentes registros, lo que puede hacer al anteponer "{reg_letter} (cita y la letra del registro) al operador de copia. De esta manera será posible inserte varias palabras en la línea de comando más tarde con Ctrl + r {reg_letter}. Esto es un poco incómodo pero funciona.

Pero volviendo a la idea anterior, sería genial si moviéramos la posición del cursor sobre el búfer mientras escribíamos un patrón. De esa manera podríamos "elegir" texto desde diferentes posiciones con Ctrl + r Ctrl + w, así que creé un complemento que hace exactamente eso: EXtend.vim El complemento también tiene muchas otras características para realizar operaciones de sustitución.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.