Comment puis-je convertir des espaces en onglets dans Vim ou Linux?

192

J'ai examiné plusieurs questions sur Stack Overflow pour savoir comment convertir des espaces en onglets sans trouver ce dont j'ai besoin. Il semble y avoir plus de questions sur la façon de convertir les onglets en espaces, mais j'essaie de faire le contraire.

Dans Vimj'ai essayé :retabet :retab!sans chance, mais je pense que ce sont en fait pour passer des onglets aux espaces de toute façon.

J'ai essayé les deux expandet unexpandà l'invite de commande sans aucune chance.

Voici le fichier en question:

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

Comment puis-je convertir les espaces de début en onglets en utilisant l'un Vimou l' autre ou le shell?

cwd
la source
Dans le commentaire de @ Matt qui est maintenant supprimé, le premier exemple ( sed "s/ +/`echo -e '\t'`/g" < input.py > output.py) semble convertir tous les espaces, pas seulement les espaces de début . Dans le deuxième exemple ( sed "s/^ +/`echo -e '\t'`/g" < input.py > output.py), il remplace uniquement le premier espace de chaque ligne par une tabulation et laisse le reste.
cwd
Ci-contre lié: Comment remplacer les onglets par des espaces? à Vim SE
kenorb
Je n'ai pas trouvé de réponse pour tous / plusieurs fichiers , j'ai donc écrit le mien: stackoverflow.com/a/44581474/1115187 . Avec find, awket blackjack (trop long pour le laisser dans les commentaires, cependant)
maxkoryukov

Réponses:

316

En utilisant Vim pour étendre tous les espaces de début (plus larges que 'tabstop'), vous aviez raison d'utiliser, retabmais assurez 'expandtab'- vous d'abord qu'il est réinitialisé ( :verbose set ts? et?est votre ami). retabprend une plage , donc je spécifie généralement %pour signifier "le fichier entier".

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

Avant de faire quelque chose comme ça (en particulier avec les fichiers Python!), Je règle généralement 'list', de sorte que je puisse voir les espaces et changer.

J'ai le mappage suivant dans mon.vimrc pour cela:

nnoremap    <F2> :<C-U>setlocal lcs=tab:>-,trail:-,eol:$ list! list? <CR>
Johnsyweb
la source
2
voici comment je l' ai eu au travail, pas sûr de ce qui est nécessaire et ce qui est pas, et BTW je ne sais pas ce que le « % » avant reatab fait: :set noet, :set tabstop=2, :retab!, :%retab!, :set tabstop=1, :retab!,:%retab!
CWD
2
J'ai mis à jour ma réponse pour inclure une explication sur les raisons pour lesquelles j'utilise %. Le mappage F2 est exactement tel qu'il est typé .
Johnsyweb
7
Pour ce fichier particulier, je ferais simplement::set noet ts=2 |%retab!
Johnsyweb
1
@Johnsyweb pour être juste, je me suis trompé: ça :%retab!marche toujours. J'étais confus avec ==, etc qui ne respecte le paramètre preserveindent.
Unk
1
Pour convertir un fichier coffeescript d'espaces en onglets, j'ai suivi la réponse de @ Johnsyweb (y compris l'ajout au fichier .vimrc) mais à la place avec un:set tabstop=4
Mikeumus
38

1 - Si vous avez des espaces et souhaitez des tabulations.

Tout d'abord, vous devez décider combien d'espaces auront un seul onglet. Cela dit, supposons que vous ayez des lignes avec 4 espaces en tête, ou 8 ... Ensuite, vous réalisez que vous voulez probablement qu'une tabulation soit 4 espaces. Maintenant, avec cette information, vous faites:

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

Il y a un problème ici! Cette séquence de commandes recherchera tout votre texte, pas seulement les espaces au début de la ligne. Cela signifie une chaîne comme: "Hey,␣this␣␣␣␣is␣4␣spaces"deviendra "Hey,␣this⇥is␣4␣spaces", mais ce n'est pas le cas! c'est un onglet !.

Pour régler ce petit problème, je recommande un search, au lieu de retab.

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

Cette recherche recherchera dans tout le fichier toutes les lignes commençant par le nombre d'onglets, suivi de 4 espaces, et la remplacera par le nombre d'onglets qu'il a trouvés plus un.

Ceci, malheureusement, ne fonctionnera pas à la fois!

Au début, le fichier aura des lignes commençant par des espaces. La recherche convertira alors seulement les 4 premiers espaces en tabulation, et laissera ce qui suit ...

Vous devez répéter la commande. Combien de fois? Jusqu'à ce que vous obteniez un pattern not found. Je ne vois pas encore de moyen d'automatiser le processus. Mais si vous faites:

`10@:`

Vous avez probablement terminé. Cette commande répète la dernière recherche / remplacement 10 fois. Il est peu probable que votre programme ait autant de retraits. Si c'est le cas, répétez simplement @@.

Maintenant, juste pour compléter la réponse. Je sais que vous avez demandé le contraire, mais vous ne savez jamais quand vous devez annuler des choses.

2 - Vous avez des onglets et souhaitez des espaces.

Tout d'abord, décidez du nombre d'espaces dans lesquels vous souhaitez convertir vos onglets. Disons que vous voulez que chaque onglet soit composé de 2 espaces. Vous faites alors:

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

Cela aurait le même problème avec les chaînes. Mais comme son meilleur style de programmation est de ne pas utiliser d'onglets durs dans les chaînes, vous faites en fait une bonne chose ici. Si vous avez vraiment besoin d'une tabulation dans une chaîne, utilisez \t.

Dr Beco
la source
Semble une condition de course extrêmement rare. Quoi qu'il en soit, créez une fonction acceptant la sélection de la plage visuelle et itérez la fonction de recherche jusqu'à ce qu'aucune correspondance ne soit trouvée, je suppose que ce serait une réponse plus intelligente et utile.
albfan
Ouais, ou ça. Si vous voulez une solution plus définitive, vous pouvez utiliser une fonction. Quoi qu'il en soit, il est toujours bon de savoir comment faire dans le ex command, car ce serait ce qui se trouve à l'intérieur de la fonction. Et non, ce n'est pas rare. Vous avez juste besoin d'avoir des chaînes avec des espaces pour avoir un désordre. Pas rare du tout. J'y suis. Merci d'avoir commenté.
Dr Beco
Veuillez expliquer pourquoi / g n'est pas suffisant pour convertir tous les groupes de 4 dans chaque ligne d'espaces consécutifs en tabulations, au lieu d'avoir à lancer la recherche plusieurs fois.
Bjartur Thorlacius
Salut @BjarturThorlacius, le problème est que si vous supprimez le ^symbole, pour lancer la recherche depuis le début de la ligne, vous risquez de changer les espaces à l'intérieur des chaînes. Avec le ^vous garantissez que vous ne modifiez que les espaces et les tabulations depuis le début de la ligne, donc l'indentation. En plus de cela, si vous êtes sûr de pouvoir tout faire en même temps, supprimez le ^et exécutez une seule fois avec::%s/\(^I*\)␣␣␣␣/\1^I/g
Dr Beco
15
:%s/\(^\s*\)\@<=    /\t/g

Traduction: recherchez chaque instance de 4 espaces consécutifs (après le caractère =), mais uniquement si la ligne entière jusqu'à ce point est un espace blanc (cela utilise l'assertion de regard derrière de largeur nulle \@<=). Remplacez chaque instance trouvée par un caractère de tabulation.

Simon Zuckerbraun
la source
1
La seule solution qui a fonctionné pour étendre 1 espace à 1 onglet , mais méfiez-vous de l'unicode dans la réponse, je viens d'utiliser :%s/\(^\s*\)\@<= /\t/g- mettez le nombre approprié d'espaces (pour convertir 4 espaces en 1 onglet, mettez 4 espaces) juste après le<=
Orwellophile
5

Remplace tous les espaces par tabulation:% s / \ s / \ t / g

murat budak
la source
C'est précisément ce dont j'avais besoin. Merci. Le seul changement que j'ai dû faire était de remplacer tous les 4 espaces par une tabulation au lieu de faire chaque espace.
Personne anonyme
3

Linux: avec unexpand(et expand)

Voici une très bonne solution: https://stackoverflow.com/a/11094620/1115187 , principalement parce qu'elle utilise * nix-utilities:

  1. unxpand - espaces -> tabulations
  2. développer - tabulations -> espaces

Linux: script personnalisé

Ma réponse originale

Extrait de code Bash pour remplacer l' indentation de 4 espaces (il y a deux{4} dans le script) par des onglets dans tous les .pyfichiers du ./appdossier (récursivement):

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

Il ne modifie pas les 4 espaces au milieu ou à la fin.

A été testé sous Ubuntu 16.0x et Linux Mint 18

maxkoryukov
la source
0

Dans mon cas, j'avais plusieurs espaces (les champs étaient séparés par un ou plusieurs espaces) que je voulais remplacer par une tabulation. Ce qui suit l'a fait:

:% s/\s\+/\t/g
zee
la source
0

Si vous avez installé GNU coreutils, considérez %!unexpand --first-onlyou pour les onglets à 4 espaces, considérez %!unexpand -t 4 --first-only( --first-onlyest présent juste au cas où vous invoqueriez accidentellementunexpand avec --all).

Notez que cela ne remplacera que les espaces précédant les taquets de tabulation prescrits, pas les espaces qui les suivent; vous ne verrez aucune différence visuelle dans vim à moins d'afficher les onglets plus littéralement; par exemple, mon ~/.vimrccontient set list listchars=tab:▸┈(je suppose que c'est la raison pour laquelle vous pensiez unexpandne pas fonctionner).

Adam Katz
la source
0

Pour utiliser Vim pour retaper un ensemble de fichiers (par exemple tous les fichiers * .ts dans une hiérarchie de répertoires) de 2 à 4 espaces, disons, vous pouvez essayer ceci à partir de la ligne de commande:

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

Ce que cela fait, c'est utiliser findpour transmettre tous les fichiers correspondants àxargs (l'option -print0 sur find fonctionne avec l'option -0 à xargs afin de gérer les fichiers avec des espaces dans le nom).

xargs exécute vim en mode ex (-e ) sur chaque fichier en exécutant la commande ex donnée qui est en fait plusieurs commandes, pour changer les espaces de début existants en tabulations, réinitialiser le taquet de tabulation et changer les onglets en espaces et enfin enregistrer et quitter.

L'exécution en mode ex empêche ceci: Vim: Warning: Input is not from a terminalpour chaque fichier.

Mike Lippert
la source
-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.
la source
Un gros problème: cet extrait de code remplace les 4 espaces, pas seulement l'indentation (au début de la ligne)
maxkoryukov
et le second: le filtre de fichiers n'est pas implémenté
maxkoryukov