¿Cómo puedo convertir espacios en pestañas en Vim o Linux?

192

He revisado varias preguntas sobre Stack Overflow sobre cómo convertir espacios en pestañas sin encontrar lo que necesito. Parece que hay más preguntas sobre cómo convertir pestañas en espacios, pero estoy tratando de hacer lo contrario.

En Vimlo he intentado :retaby :retab!sin suerte, pero creo que esos son en realidad para ir de lengüetas a los espacios de todos modos.

Intenté ambos expandy unexpanden el símbolo del sistema sin suerte.

Aquí está el archivo en cuestión:

http://gdata-python-client.googlecode.com/hg-history/a9ed9edefd61a0ba0e18c43e448472051821003a/samples/docs/docs_v3_example.py

¿Cómo puedo convertir los espacios iniciales en pestañas usando Vimo el shell?

cwd
fuente
En el comentario de @ Matt que ahora se elimina, el primer ejemplo ( sed "s/ +/`echo -e '\t'`/g" < input.py > output.py) parece convertir todos los espacios, no solo los espacios iniciales. En el segundo ejemplo ( sed "s/^ +/`echo -e '\t'`/g" < input.py > output.py) solo reemplaza el primer espacio en cada línea con una pestaña y deja el resto de ellos.
cwd
Opuesto relacionado: ¿Cómo reemplazar pestañas con espacios? en Vim SE
kenorb
No encontré la respuesta para todos / muchos archivos , así que escribí el mío: stackoverflow.com/a/44581474/1115187 . Con find, awky blackjack (demasiado tiempo para dejarlo en los comentarios, sin embargo)
maxkoryukov

Respuestas:

316

Al usar Vim para expandir todos los espacios iniciales (más ancho que 'tabstop'), fue correcto usarlo, retabpero primero asegúrese de 'expandtab'reiniciar ( :verbose set ts? et?es su amigo). retabtoma un rango , por lo que generalmente especifico %que significa "todo el archivo".

:set tabstop=2      " To match the sample file
:set noexpandtab    " Use tabs, not spaces
:%retab!            " Retabulate the whole file

Antes de hacer algo como esto (¡particularmente con archivos Python!), Generalmente configuro 'list', para que pueda ver el espacio en blanco y cambiar.

Tengo el siguiente mapeo en mi.vimrc para esto:

nnoremap    <F2> :<C-U>setlocal lcs=tab:>-,trail:-,eol:$ list! list? <CR>
Johnsyweb
fuente
2
Así es como yo tengo que trabajar, no está seguro de lo que es necesario y lo que no, y por cierto no sé lo que el "%" antes reatab está haciendo: :set noet, :set tabstop=2, :retab!, :%retab!, :set tabstop=1, :retab!,:%retab!
la caquexia crónica
2
He actualizado mi respuesta para incluir una explicación de por qué la uso %. El mapeo F2 es exactamente como está escrito .
Johnsyweb
77
Para ese archivo en particular, simplemente haría::set noet ts=2 |%retab!
Johnsyweb
1
@Johnsyweb para ser justos, me equivoqué: :%retab!todavía funciona. Estaba confundido con ==, etc, que hace respetar la configuración preserveindent.
Unk
1
Para convertir un archivo coffeescript de espacios a pestañas, seguí la respuesta de @ Johnsyweb (incluida la adición al archivo .vimrc) pero en su lugar con un:set tabstop=4
Mikeumus
38

1 - Si tienes espacios y quieres pestañas.

Primero, debe decidir cuántos espacios tendrá una sola pestaña. Dicho esto, suponga que tiene líneas con 4 espacios iniciales, u 8 ... Entonces se da cuenta de que probablemente desee que una pestaña tenga 4 espacios. Ahora con esa información, haces:

:set ts=4
:set noet
:%retab!

¡Hay un problema aquí! Esta secuencia de comandos buscará todo su texto, no solo espacios al comienzo de la línea. Eso significa que una cadena como: "Hey,␣this␣␣␣␣is␣4␣spaces"se convertirá "Hey,␣this⇥is␣4␣spaces", ¡pero no lo es! es una pestaña!

Para resolver este pequeño problema, recomiendo a search, en lugar de retab.

:%s/^\(^I*\)␣␣␣␣/\1^I/g

Esta búsqueda buscará en el archivo completo cualquier línea que comience con cualquier número de pestañas, seguido de 4 espacios, y la sustituirá por el número de pestañas que encontró más uno.

¡Esto, desafortunadamente, no se ejecutará de inmediato!

Al principio, el archivo tendrá líneas que comienzan con espacios. La búsqueda luego convertirá solo los primeros 4 espacios en una pestaña, y dejará lo siguiente ...

Necesitas repetir el comando. ¿Cuantas veces? Hasta que consigas un pattern not found. Todavía no puedo pensar en una forma de automatizar el proceso. Pero si lo haces:

`10@:`

Probablemente ya terminaste. Este comando repite la última búsqueda / reemplazo por 10 veces. No es probable que su programa tenga tantas sangrías. Si es así, solo repite de nuevo @@.

Ahora, solo para completar la respuesta. Sé que pediste lo contrario, pero nunca sabes cuándo necesitas deshacer las cosas.

2 - Tienes pestañas y quieres espacios.

Primero, decida a cuántos espacios desea convertir sus pestañas. Digamos que quieres que cada pestaña tenga 2 espacios. Entonces haces:

:set ts=2
:set et
:%retab!

Esto tendría el mismo problema con las cadenas. Pero como es un mejor estilo de programación para no usar pestañas duras dentro de las cadenas, en realidad estás haciendo algo bueno aquí. Si realmente necesita una pestaña dentro de una cadena, use \t.

Dr. Beco
fuente
Parece una condición de carrera extremadamente rara. De todos modos, cree una función que acepte la selección de rango visual e itere la función de búsqueda hasta que no se encuentre una coincidencia, supongo que sería una respuesta más inteligente y útil.
albfan
Sí, o eso. Si desea una solución más definitiva, puede usar una función. De todos modos, siempre es bueno saber cómo hacerlo en el ex command, porque esto sería lo que está dentro de la función. Y no, no es raro. Solo necesita tener cadenas con espacios para tener un desastre. No es raro en absoluto. He estado ahí Gracias por comentar
Dr Beco
Explique por qué / g no es suficiente para convertir todos los grupos de 4 en cada línea de espacios consecutivos en pestañas, en lugar de tener que ejecutar la búsqueda varias veces.
Bjartur Thorlacius
Hola @BjarturThorlacius, el problema es que si eliminas el ^símbolo, para comenzar la búsqueda desde el principio de la línea, corres el riesgo de cambiar los espacios dentro de las cadenas. Con la ^garantía, está cambiando solo espacios y pestañas desde el comienzo de la línea, por lo tanto, sangría. Además de eso, si está seguro de que está bien hacer todo de una vez, quítelo ^y ejecute solo una vez con::%s/\(^I*\)␣␣␣␣/\1^I/g
Dr Beco
15
:%s/\(^\s*\)\@<=    /\t/g

Traducción: Busque cada instancia de 4 espacios consecutivos (después del carácter =), pero solo si la línea completa hasta ese punto es un espacio en blanco (esto usa la aserción de ancho de cero \@<=). Reemplace cada instancia encontrada con un carácter de tabulación.

Simon Zuckerbraun
fuente
1
La única solución que funcionó para expandir 1 espacio a 1 pestaña , pero tenga cuidado con el Unicode en la respuesta, acabo de usar :%s/\(^\s*\)\@<= /\t/g: poner el número apropiado de espacios (para convertir 4 espacios en 1 pestaña, poner en 4 espacios) justo después de<=
Orwellophile
5

Cambia todos los espacios a la pestaña:% s / \ s / \ t / g

murat budak
fuente
Esto es precisamente lo que necesitaba. Gracias. El único cambio que tuve que hacer fue reemplazar cada 4 espacios con una pestaña en lugar de hacer cada espacio.
Persona anónima
3

Linux: con unexpand(y expand)

Aquí hay una muy buena solución: https://stackoverflow.com/a/11094620/1115187 , principalmente porque usa * nix-utilities:

  1. undepand - espacios -> pestañas
  2. expandir - pestañas -> espacios

Linux: script personalizado

Mi respuesta original

Fragmento de bash para reemplazar la sangría de 4 espacios (hay dos {4}en el script) con pestañas en todos los .pyarchivos de la ./appcarpeta (recursivamente):

find ./app -iname '*.py' -type f \
    -exec awk -i inplace \
    '{ match($0, /^(( {4})*)(.*?)$/, arr); gsub(/ {4}/, "\t", arr[1]) }; { print arr[1] arr[3] }' {} \; 

No modifica 4 espacios en el medio o al final.

Fue probado en Ubuntu 16.0xy Linux Mint 18

maxkoryukov
fuente
0

En mi caso, tenía múltiples espacios (los campos estaban separados por uno o más espacios) que quería reemplazar con una pestaña. Lo siguiente lo hizo:

:% s/\s\+/\t/g
zee
fuente
0

Si tiene instalados GNU coreutils, considere %!unexpand --first-onlyo para pestañas de 4 espacios, considere %!unexpand -t 4 --first-only( --first-onlyestá presente en caso de que invocara accidentalmente unexpandcon--all ).

Tenga en cuenta que esto solo reemplazará los espacios que preceden a las tabulaciones prescritas, no los espacios que los siguen; no verá ninguna diferencia visual en vim a menos que muestre las pestañas más literalmente; por ejemplo, mi ~/.vimrccontiene set list listchars=tab:▸┈(sospecho que es por eso que pensaste unexpandque no funcionaba).

Adam Katz
fuente
0

Para usar Vim para volver a capturar un conjunto de archivos (por ejemplo, todos los archivos * .ts en una jerarquía de directorios) desde, digamos, 2 espacios a 4 espacios, puede probar esto desde la línea de comandos:

find . -name '*.ts' -print0 | xargs -0 -n1 vim -e '+set ts=2 noet | retab! | set ts=4 et | retab | wq'

Lo que está haciendo es usar findpara pasar todos los archivos coincidentes axargs (la opción -print0 en find funciona con la opción -0 a xargs para manejar archivos con espacios en el nombre).

xargs ejecuta vim en modo ex (-e ) en cada archivo ejecutando el comando ex dado, que en realidad son varios comandos, para cambiar los espacios iniciales existentes a pestañas, restablecer la tabulación y cambiar las pestañas a espacios y finalmente guardar y salir.

Ejecutar en modo ex evita esto: Vim: Warning: Input is not from a terminalpara cada archivo.

Mike Lippert
fuente
-1

Script Python simple:

import os

SOURCE_ROOT = "ROOT DIRECTORY - WILL CONVERT ALL UNDERNEATH"

for root, dirs, files in os.walk(SOURCE_ROOT):
    for f in files:
        fpath = os.path.join(root,f)
        assert os.path.exists(fpath)
        data = open(fpath, "r").read()
        data = data.replace("    ", "\t")
        outfile = open(fpath, "w")
        outfile.write(data)
        outfile.close()
Michael W.
fuente
Un gran problema: este fragmento reemplaza los 4 espacios, no solo la sangría (al comienzo de la línea)
maxkoryukov
y el segundo: el filtro de archivos no está implementado
maxkoryukov