Jak korzystać z zewnętrznego polecenia w Vimie, aby modyfikować wybrane słowa i linie?

22

Coś, co znalazłem przydatne w innych edytorach, to możliwość:

  • weź wybrany tekst
  • uruchom polecenie zewnętrzne i przekaż wybór do jego stdin
  • weź zewnętrzne polecenia stdouti zastąp je bieżącym wyborem.

W ten sposób możesz napisać przydatne narzędzia tekstowe, które działają na zaznaczeniu, używając dowolnego języka, który potrafi wykonać podstawowe io.

Jak można to zrobić za pomocą vima przy użyciu zaznaczenia? ... pojedynczy znak, słowo, akapit ... itd.

(Bezpośrednio w wierszu polecenia lub za pomocą klawisza?)


Uwaga

Polecenia takie jak !sortpraca na poziomie linii, dlatego zadaję to pytanie, ponieważ chciałbym operować zaznaczeniem. (tzn. tekst, który zostałby usunięty, gdyby xzostał naciśnięty).

ideasman42
źródło

Odpowiedzi:

17

Uważam, że często najłatwiejszym sposobem jest użycie trybu wizualnego v(lub kapitału, Vaby zaznaczyć całe linie) i zaznaczenie tekstu, który chcesz potokować.

Następnie wpisz:

:!cat

Nie robi to nic przydatnego jako takiego. Przydatnym poleceniem, którego często używam, jest:

:!python -m json.tool

sformatować JSON.

Możesz także po prostu wpisać (bez trybu wizualnego):

:<range>!command

Na przykład dla całego pliku:

:%!python -m json.tool

Lub dla bieżącej linii:

:.!python -m json.tool
Martin Tournoij
źródło
Kolejnym naprawdę przydatnym narzędziem do tego celu jest sortpolecenie.
Cody Poll
4
@CodyPoll Co jest nie tak z Vimem :sort?
Martin Tournoij,
5
@Carpetsmoker Polecenie sortowania zewnętrznego ma o wiele więcej opcji i (w zależności od ustawień regionalnych itp.) Tworzy inną kolejność.
derobert
1
Nie działa to przy wybieraniu pojedynczego słowa w linii. (patrz pytanie wspomina o wyborze poziomu słów)
ideasman42
@ ideasman42 Racja ... Myślę, że pierwotnie przeczytałem to pytanie jako „wiersze lub słowa” zamiast „wiersze i słowa” ... W każdym razie robienie tego za pomocą słów nie jest oczywiste, programy UNIX działają na wierszach, a Vim nie jest wyjątkiem. Przeprowadziłem trochę eksperymentów i znalazłem rozwiązanie, ale to nie działa zbyt dobrze ... Może moje podejście jest złe, więc zostawię to na kilka dni i wrócę do niego później.
Martin Tournoij
7

Napisałem wtyczkę o nazwie express.vim, która może w tym pomóc. Wtyczka definiuje operator, g=który pozwala ocenić wyrażenie VimScript na podstawie ruchu (lub wyboru wizualnego). Wyświetla monit o wyrażenie, którego można użyć v:valjako symbolu zastępczego dla tekstu objętego ruchem (lub ponownie wyboru wizualnego).

W takim przypadku przydatne wyrażenie użyłoby system()funkcji, która uruchamia zewnętrzne polecenie.

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

(Tutaj użyłem trjako prostego przykładowego polecenia zewnętrznego).

Sposobem użycia express.vim w przypadku tego problemu byłoby wybranie tekstu w trybie Visual, następnie wpisanie, g=a następnie wyrażenie, a następnie naciśnięcie Enter:

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

Przyznaję, że nie jest łatwy w użyciu. (W rzeczywistości rzadko używam tej wtyczki!) Zastanawiam się nad dodaniem skrótu w celu wywołania zewnętrznego polecenia, na przykład uruchomienia wyrażenia !. Opublikuję tutaj aktualizację, jeśli to zrobię.

EDYTOWAĆ

Zmodyfikowałem express.vim, aby traktować wyrażenia zaczynające się od !jako poleceń zewnętrznych. Trzymając się trprzykładu, powyższe staje się znacznie prostsze. Po zaznaczeniu wizualnym wpisz następujące polecenie:

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

Wyjaśnienie:

  1. Wywołaj operator ekspresowy za pomocą g=
  2. Wpisz !tr a-z A-Zjako wyrażenie
  3. naciśnij Enter
tommcdo
źródło
4

Szukasz poleceń filtrujących w Vimie. Zobaczyć :help filter. Oto odpowiednia dokumentacja:

!{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]

Więc pisanie 5!!sortposortuje kolejne 5 wierszy, zaczynając od kursora.

Sameer
źródło
Czy można filtrować pojedyncze słowo? (nie na poziomie linii)
ideasman42
Tak. Trzecia opcja (tryb wizualny) działa na wszystko, co jest podświetlone (słowa, linia częściowa, wiele linii, blok prostokątny itp.).
Sameer
1
Trzecia opcja filtruje również całe linie. Jeśli wybierzesz pojedyncze słowo i przefiltrujesz je, na przykład, przez tr [:lower:] [:upper]vim, vim będzie przechowywał nie tylko to słowo, ale całą linię.
Maria
0

Pamiętaj, że można to zrobić za pomocą:

  1. wizualnie wybierz cokolwiek chcesz
  2. s^R=system('tr a-z A-Z', @")[:-2]^M(z ^RCTRL + R i ^ M z ENTER)

Może również działać ze wszystkim, co robi selekcję + zamienia jak ci"^R=....

Luc Hermitte
źródło
Podczas uruchamiania tego zaznaczenia tekstu w wielu wierszach (po naciśnięciu vi przesunięciu kursora) pojawia się błąd E16: Invalid range\nE16: Invalid range\nE476: Invalid command(w systemie Linux tristnieją inne podstawowe polecenia).
ideasman42
Czy nacisnąłeś, saby przejść do trybu wstawiania lub :spolecenia?
Luc Hermitte
Podejrzewam, że nie, ale nie jestem pewien, co masz na myśli, być może przykład mógłby pokazać, jak powiązać to z kluczem?
ideasman42
Czy trafiasz bezpośrednio sz trybu wizualnego, czy uderzasz :pierwszy? :Sekwencja kluczy, które napisałem, nie jest zaangażowana. Mapowanie będzie: vnoremap µ s<c-r>=system('tr a-z A-Z', @")[:-2]<cr>.
Luc Hermitte