Edit : A resposta de Peter Rincker é mais curta, mais fácil de explicar e pode ser repetida quantas vezes você quiser. Minha solução é muito longa e não pode ser repetida por várias palavras. Devo excluir a resposta, mas talvez alguém se interesse por ela, então deixo aqui.
Não sei se é isso que você deseja, mas tente o seguinte código:
function! NextWordWithLetter(type)
normal! `]vy
let letter = @0
let line = getline('.')
let list = split(line)
for word in list
if matchstr(word, '\%1c.') ==# letter
let col = match(line, word) + 1
execute "normal! " . col . "|"
break
endif
endfor
endfunction
nnoremap <silent> <leader>g :set operatorfunc=NextWordWithLetter<cr>g@
O mapeamento para chamar a função é <leader>g<motion>
.
Se você deseja mover o cursor para a próxima palavra que começa com a letra p
e ,
é a chave do líder, você pode digitar ,gfp
.
Objetivo
Você deseja ir para a próxima palavra que começa com uma letra específica. Não sei se o vim já possui esse movimento, mas vamos supor que não.
Precisamos definir um novo movimento (como w
, b
, e
etc.). Geralmente, para definir um novo movimento ou objeto, você usa o onoremap
comando É explicado em detalhes aqui .
Não sei como atingir seu objetivo dessa maneira, mas sei como definir um novo operador que indiretamente fará o que você deseja.
Eu digo indiretamente porque o objetivo de um novo operador, geralmente, não é mudar para algum lugar, mas fazer algo em um pedaço de texto.
Vim explica como criar um novo operador na ajuda: :h map-operator
. Também é explicado aqui , aqui e aqui .
A sintaxe pode parecer estranha para você, mas aqui está como parece funcionar no vimscript:
g@
é um operador especial (como c
alterar, d
excluir, y
copiar etc.) que permite criar seu próprio operador.
Se você digitar g@{motion}
vim, procurará a opfunc
opção cujo valor deveria ser o nome de uma função, defina as marcas `[
e `]
o texto que você teria movido se tivesse digitado {motion}
(mais sobre isso mais tarde) e execute a função que pode ser faça o que quiser neste pedaço de texto.
Você pode criar operadores diferentes escrevendo funções diferentes e definindo a opfunc
opção com o nome da função que deseja usar.
Mas antes de nos aprofundarmos no que nossa função faz, vamos dar uma olhada no mapeamento necessário para acioná-lo.
Mapeamento
No final do trecho de código, há esta linha:
nnoremap <silent> <leader>g :set operatorfunc=NextWordWithLetter<cr>g@
Diz ao vim que, sempre que você pressiona <leader>g
, deve silenciosamente ( <silent>
) definir o valor da operatorfunc
opção NextWordWithLetter
e, em seguida, pressionar no modo normal g@
.
O operador g@
aguarda um movimento para definir as marcas `[
e `]
o texto com o qual você se {motion}
moveria antes de executar a função armazenada opfunc
. Aqui está a função NextWordWithLetter()
.
Você pode fazer o mesmo digitando no modo de comando:
:set operatorfunc=NextWordWithLetter
Então no modo normal: g@
sempre no modo normal:{motion}
Mas é claro que seria tedioso, então criamos o mapeamento <leader>g
que define automaticamente a operatorfunc
opção para o nome da função desejada e pressionamos g@
para nós.
Vamos supor que sua chave de líder seja ,
e você está procurando a próxima palavra que começa com a letra p
. Usando nosso mapeamento, você pode digitar ,gfp
.
Como resultado, o vim definirá as marcas `[
e em `]
torno do texto entre o cursor e o próximo caractere p
, definido opfunc
como NextWordWithLetter
e, devido ao valor dessa opção, ele executará a função NextWordWithLetter()
.
A seguir, vamos dar uma olhada no que a função faz.
Configurando variáveis
Primeiro, precisamos dizer à função qual personagem estamos procurando:
normal! `]vy
O normal
comando digita qualquer sequência de caracteres que você passa para ele como se estivesse no modo normal. Aqui está a sequência de caracteres `]vy
.
Quando você está em um buffer, pode definir uma marca em qualquer lugar com o comando normal m{letter}
.
Se você quiser colocar a marca a
onde está o cursor, digite ma
. Em seguida, você pode se mover e voltar para a mesma linha da marca a
com o comando 'a
ou para o caractere exato em que você definiu a marca a
com o comando `a
.
Você pode ver todas as suas marcas com o comando :marks
. As marcas em maiúsculas são globais e podem ser usadas para saltar nos buffers, as minúsculas são locais em um buffer.
Existem duas marcas especiais que o vim define automaticamente para nós: `[
e `]
. Sempre que você altera ou puxa algum texto, o vim coloca essas marcas em torno dele ( :h '[
).
Vamos voltar ao nosso exemplo: você queria ir para a próxima palavra que começa com a letra p
e pressiona ,gfp
. fp
é um comando que move o cursor para o próximo caractere p
.
Nesse ponto, o vim definirá automaticamente as marcas `[
e `]
o texto que passaríamos se tivéssemos digitado {motion}
( fp
no nosso exemplo).
Está escrito na ajuda ( :h g@
):
g @ {motion} Chame a função definida pela opção 'operatorfunc'. A marca '[está posicionada no início do texto movida por {motion}, a marca'] no último caractere do texto.
Ainda não mudamos ou arrancamos nada, então por que o vim já coloca essas marcas? Porque acha que a função deseja fazer algo no texto, e a função precisará dessas marcas para identificar o texto no qual operar.
Portanto, quando o normal
comando digita `]vy
, ele pode ser lido como:
vá para a marca `]
(último caractere do texto movido), vá para o modo visual ( v
) e copie a seleção visual ( y
) .
Então agora copiamos o seu personagem pesquisado (personagem p
). Tudo o que você copia está em um registro. Existem diferentes registros no vim, cada um deles sendo usado para uma finalidade diferente. Para dar uma olhada neles, digite :reg
e saiba qual registrador armazena o quê :h registers
.
O último texto que você copiou é armazenado no registro 0
. Você pode acessá-lo com @0
: echo @0
.
O seu personagem pesquisado p
está neste registro. Nós o atribuímos à variável letter
:
let letter = @0
Em seguida, atribuímos a linha atual à variável line
:
let line = getline('.')
getline()
é uma função interna que retorna a linha cujo número foi passado como argumento e '.'
é expandida como o número da linha atual.
Em seguida, atribuímos à variável list
uma lista contendo cada palavra da linha:
let list = split(line)
split()
é uma função interna que divide uma string sempre que encontra um espaço em branco (espaço, caractere de tabulação ...). Você pode dizer para ele se dividir em outros locais dando um segundo argumento. Por exemplo, se você deseja dividir sempre que houver um caractere de dois pontos, você usaria split(line, ':')
.
For loop
O for
loop itera sobre a variável list
e armazena cada um de seus itens dentro da variável word
:
for word in list
...
endfor
Em seguida, verifica se a primeira letra de word
é a letra que você está procurando:
if matchstr(word, '\%1c.') ==# letter
...
endif
matchstr()
é uma função interna que retorna um padrão se for encontrado dentro de uma string.
Aqui estamos procurando o padrão '\%1c.'
dentro word
. Em um vim regex, \%1c
é um átomo de largura zero. Ele não adiciona nada ao padrão, apenas informa ao mecanismo de expressão regular que analisa a expressão regular que nosso padrão começa na primeira coluna do texto (para obter mais informações :h /\%c
). O ponto no final da regex significa qualquer caractere.
Então '\%1c.'
significa: qualquer caractere na primeira coluna do texto e matchstr(word, '\%1c.')
retornará o primeiro caractere de word
.
Para comparar a primeira letra word
com letter
que usar o ==#
operador. É um operador que diferencia maiúsculas de minúsculas (independentemente do valor da ignorecase
opção). Você também pode usar ==?
letras que não fazem distinção entre maiúsculas e minúsculas ou simples ==
(que diferencia maiúsculas de minúsculas ou não, dependendo ignorecase
; não é recomendável usar este).
Então, se o primeiro caractere de word
é letter
, aqui está o que a função faz:
let col = match(line, word) + 1
Ele armazena o índice do primeiro caractere de word
mais um dentro da variável col
. match()
é uma função interna, cuja sintaxe base é match(string, pattern)
e que retorna o índice em string
que pattern
corresponde. Nós adicionamos um porque o vim começa a indexação com 0.
Finalmente, ele executa o seguinte:
execute "normal! " . col . "|"
execute
é um comando que executa o conteúdo de qualquer sequência que você passar para ela. Aqui a string é:"normal! " . col . "|"
O operador ponto concatena as 3 cordas "normal !"
, col
(é um número, mas voltas de coerção do vim lo em uma corda automaticamente), e "|"
.
Digamos que seu caractere pesquisado esteja na 10a coluna da linha. Neste caso, a cadeia anterior seria avaliado para: "normal! 10|"
. Então execute
executa "normal! 10|"
, o que resulta no normal
comando digitando 10|
para nós.