Comment utiliser une commande externe dans Vim pour modifier les mots et les lignes sélectionnés?

22

J'ai trouvé utile dans d'autres éditeurs la possibilité de:

  • prendre le texte sélectionné
  • exécuter une commande externe et passer la sélection à son stdin
  • prendre les commandes externes stdoutet remplacer la sélection actuelle par elle.

De cette façon, vous pouvez écrire des outils de texte utiles qui opèrent sur la sélection en utilisant n'importe quelle langue pouvant faire io de base.

Comment cela peut-il être fait avec vim en utilisant la sélection? ... un seul caractère, mot, paragraphe ... etc.

(Directement en ligne de commande, ou via un raccourci clavier?)


Remarque

Des commandes telles que le !sorttravail au niveau ligne, la raison pour laquelle je pose cette question est parce que je voudrais opérer sur la sélection. (c.-à-d. le texte qui serait supprimé s'il xétait enfoncé).

ideasman42
la source

Réponses:

17

Je trouve que souvent le moyen le plus simple consiste à utiliser le mode visuel avec v(ou des majuscules Vpour sélectionner des lignes entières) et à sélectionner le texte que vous souhaitez diriger.

Tapez ensuite:

:!cat

Cela ne fait rien d'utile en tant que tel. Une commande utile que j'utilise souvent est:

:!python -m json.tool

pour formater JSON.

Vous pouvez également simplement taper (sans mode visuel):

:<range>!command

Par exemple, pour l'ensemble du fichier:

:%!python -m json.tool

Ou pour la ligne actuelle:

:.!python -m json.tool
Martin Tournoij
la source
Un autre outil vraiment utile à utiliser avec ceci est la sortcommande.
Cody Poll
4
@CodyPoll Quel est le problème avec Vim :sort?
Martin Tournoij
5
@Carpetsmoker La commande de tri externe a beaucoup plus d'options et (selon les paramètres régionaux, etc.) produit un ordre différent.
derobert
1
Cela ne fonctionne pas pour sélectionner un seul mot dans une ligne. (voir la question mentionne la sélection de niveau de mot)
ideasman42
@ ideasman42 Droite ... Je pense que j'ai lu à l'origine cette question comme "lignes ou mots" au lieu de "lignes et mots" ... Dans tous les cas, faire cela avec des mots n'est pas évident, les programmes UNIX fonctionnent sur les lignes, et Vim ne fait pas exception. J'ai fait quelques expériences et j'ai en quelque sorte trouvé une solution, mais cela ne fonctionne pas très bien ... Peut-être que mon approche est fausse, alors je vais la laisser pendant quelques jours et y revenir plus tard.
Martin Tournoij
7

J'ai écrit un plugin appelé express.vim qui peut vous aider. Le plugin définit un opérateur g=qui vous permet d'évaluer une expression VimScript sur un mouvement (ou une sélection visuelle). Il invite à une expression, dans laquelle vous pouvez utiliser v:valcomme espace réservé pour le texte couvert par le mouvement (ou, encore une fois, la sélection visuelle).

Dans ce cas, une expression utile utiliserait la system()fonction, qui exécute une commande externe.

system('tr a-z A-Z', v:val)

(Ici, j'ai utilisé trcomme exemple simple une commande externe.)

La façon d'utiliser express.vim pour ce problème serait de sélectionner le texte avec le mode visuel, puis de taper g=suivi de cette expression, puis de cliquer Enter:

g=system('tr a-z A-Z', v:val)<CR>

J'avoue que ce n'est pas facile à utiliser. (En fait, j'utilise rarement ce plugin moi-même!) J'envisage d'ajouter un raccourci pour appeler une commande externe, comme démarrer l'expression avec !. Je publierai une mise à jour ici si je finis par le faire.

MODIFIER

J'ai modifié express.vim pour traiter les expressions commençant par !comme des commandes externes. Donc, en suivant l' trexemple, ce qui précède devient beaucoup plus simple. Après une sélection visuelle, tapez ce qui suit:

g=!tr a-z A-Z<CR>

Explication:

  1. Appelez l'opérateur express avec g=
  2. Entrez !tr a-z A-Zcomme expression
  3. presse Enter
tommcdo
la source
4

Vous recherchez les commandes de filtrage dans Vim. Tu vois :help filter. Voici la documentation pertinente:

!{motion}{filter}   Filter {motion} text lines through the external
                    program {filter}.
!!{filter}          Filter [count] lines through the external program
                    {filter}.
{Visual}!{filter}   Filter the highlighted lines through the external
                    program {filter} (for {Visual} see |Visual-mode|).
                    {not in Vi}

:{range}![!]{filter} [!][arg]               *:range!*
                    Filter {range} lines through the external program
                   {filter}.  ...[See documentation for details]

La saisie 5!!sorttriera donc les 5 lignes suivantes à partir du curseur.

Sameer
la source
Est-il possible de filtrer un seul mot? (pas au niveau de la ligne)
ideasman42
Oui. La troisième option (mode visuel) fonctionne pour tout ce qui est mis en évidence (mots, ligne partielle, lignes multiples, bloc rectangulaire, etc.).
Sameer
1
La troisième option filtre également des lignes entières. Si vous sélectionnez un seul mot et le filtrez, par exemple, à travers tr [:lower:] [:upper], alors vim mettra en valeur non seulement ce mot mais la ligne entière.
maria s
0

Notez que cela peut être fait avec:

  1. sélectionnez visuellement ce que vous souhaitez
  2. s^R=system('tr a-z A-Z', @")[:-2]^M(avec ^RCTRL + R et ^ M étant ENTER)

Il pourrait également fonctionner avec tout ce que la sélection + remplace comme ci"^R=....

Luc Hermitte
la source
Lorsque j'exécute ceci sur une sélection de texte sur plusieurs lignes (après avoir appuyé vet déplacé le curseur), j'obtiens l'erreur E16: Invalid range\nE16: Invalid range\nE476: Invalid command(sous Linux, tret d'autres commandes de base existent).
ideasman42
Avez-vous appuyé sur ce squi passe en mode d'insertion ou :sla commande?
Luc Hermitte
Ne soupçonnez pas, mais ne savez pas non plus comment vous voulez dire, peut-être que l'exemple pourrait montrer comment lier cela à une clé?
ideasman42
Frappez-vous directement sdepuis le mode visuel, ou frappez-vous en :premier? Il n'y a aucune :implication dans la séquence de touches que j'ai écrite. Une cartographie serait la suivante: vnoremap µ s<c-r>=system('tr a-z A-Z', @")[:-2]<cr>.
Luc Hermitte