Como usar um comando externo no Vim para modificar palavras e linhas selecionadas?

22

Algo que eu achei útil em outros editores é a capacidade de:

  • pegue o texto selecionado
  • execute um comando externo e passe a seleção para seu stdin
  • pegue os comandos externos stdoute substitua a seleção atual por ele.

Dessa forma, você pode escrever ferramentas de texto úteis que operam na seleção usando qualquer idioma que possa executar io básico.

Como isso pode ser feito com o vim usando a seleção? ... um único caractere, palavra, parágrafo ... etc.

(Diretamente na linha de comando ou através de uma ligação de tecla?)


Nota

Comandos como o !sorttrabalho no nível da linha, a razão pela qual estou fazendo essa pergunta é porque gostaria de operar na seleção. (ou seja, o texto que seria removido se xpressionado).

ideasman42
fonte

Respostas:

17

Acho que geralmente a maneira mais fácil é usar o modo visual com v(ou maiúsculas Vpara selecionar linhas inteiras) e selecionar o texto que você deseja canalizar.

Então digite:

:!cat

Isso não faz nada de útil, como tal. Um comando útil que eu uso frequentemente é:

:!python -m json.tool

para formatar JSON.

Você também pode digitar (sem modo visual):

:<range>!command

Por exemplo, para o arquivo inteiro:

:%!python -m json.tool

Ou para a linha atual:

:.!python -m json.tool
Martin Tournoij
fonte
Outra ferramenta realmente útil para usar com isso é o sortcomando.
Cody Poll
4
@CodyPoll O que há de errado com o Vim :sort?
Martin Tournoij 04/02
5
@Carpetsmoker O comando de classificação externa tem muito mais opções e (dependendo da localidade etc.) produz uma ordem diferente.
Derobert
1
Isso não funciona para selecionar uma única palavra em uma linha. (ver pergunta menciona selecção nível da palavra)
ideasman42
@ ideasman42 Certo ... acho que li originalmente essa pergunta como "linhas ou palavras" em vez de "linhas e palavras" ... De qualquer forma, fazer isso com palavras não é óbvio, os programas UNIX funcionam com linhas e o Vim não é exceção. Fiz algumas experiências e meio que tenho uma solução, mas não funciona muito bem ... Talvez minha abordagem esteja errada, então deixarei por alguns dias e voltarei mais tarde.
Martin Tournoij 13/02/2015
7

Eu escrevi um plugin chamado express.vim que pode ajudar com isso. O plug-in define um operador g=que permite avaliar uma expressão VimScript sobre um movimento (ou seleção Visual). Ele solicita uma expressão, na qual você pode usar v:valcomo um espaço reservado para o texto coberto pela moção (ou, novamente, seleção Visual).

Nesse caso, uma expressão útil usaria a system()função, que executa um comando externo.

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

(Aqui eu usei trcomo um exemplo simples de comando externo.)

A maneira de usar o express.vim para esse problema seria selecionar o texto no modo Visual, digite o tipo g=seguido por essa expressão e pressione Enter:

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

Eu admito, não é fácil de usar. (Na verdade, eu raramente uso esse plugin!) Estou pensando em adicionar um atalho para chamar um comando externo, como iniciar a expressão !. Vou postar uma atualização aqui se acabar fazendo isso.

EDITAR

Modifiquei o express.vim para tratar expressões que começam com !comandos externos. Portanto, seguindo o trexemplo, o acima se torna muito mais simples. Após uma seleção Visual, digite o seguinte:

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

Explicação:

  1. Chame o operador expresso com g=
  2. Digite !tr a-z A-Zcomo a expressão
  3. pressione Enter
tommcdo
fonte
4

Você está procurando os comandos de filtro no Vim. Veja :help filter. Aqui está a documentação relevante:

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

Assim, a digitação 5!!sortclassificará as próximas 5 linhas, começando pelo cursor.

Sameer
fonte
É possível filtrar uma única palavra? (não nível de linha)
ideasman42
Sim. A terceira opção (modo visual) funciona para o que estiver destacado (palavras, linha parcial, várias linhas, bloco retangular, etc.).
Sameer
1
A terceira opção também filtra linhas inteiras. Se você selecionar uma única palavra e filtrá-la, por exemplo, através de tr [:lower:] [:upper], o vim irá adicionar não apenas essa palavra, mas toda a linha.
maria s
0

Observe que isso pode ser feito com:

  1. selecione visualmente o que quiser
  2. s^R=system('tr a-z A-Z', @")[:-2]^M( ^Rsendo CTRL + R e ^ M sendo ENTER)

Também poderia funcionar com qualquer coisa que a seleção + substitua como ci"^R=....

Luc Hermitte
fonte
Ao executar isso em uma seleção de texto de várias linhas (depois de pressionar ve mover o cursor), recebo o erro E16: Invalid range\nE16: Invalid range\nE476: Invalid command(no Linux, trexistem outros comandos básicos).
precisa saber é o seguinte
Você pressionou sque entra no modo de inserção ou :sno comando?
precisa saber é o seguinte
Suspeite que não, mas também não sei ao certo o que você quer dizer, talvez o exemplo possa mostrar como vincular isso a uma chave?
ideasman42
Você bate diretamente sno modo visual ou :primeiro? Não há :envolvimento na sequência de chaves que escrevi. Um mapeamento seria: vnoremap µ s<c-r>=system('tr a-z A-Z', @")[:-2]<cr>.
Luc Hermitte 27/01