Ir para a próxima palavra que começa com letra específica na linha atual

7

Eu quero ter o comando que está indicado no título. Procurei-o no comando intrínseco do Vim, mas não o encontrei. Eu executei algum tutorial on-line sobre funções no vim. A idéia seria ter algo assim no meu .vimrc:

map <command> <argument>: call nextWordWithLetter(argument)

function nextWordWithLetter(letter)
    " for word i in line
    " if word i's first letter = letter
        " col = column of word i
        " break
    col | "The command to jump to column number 'col'
end function
solalito
fonte

Respostas:

11

Você pode usar getchar()e search()realizar seu objetivo.

nnoremap <key> :call search('\<' . nr2char(getchar()), 'W', line('.'))<cr>

A idéia é que nr2char(getchar())esperamos que um caractere seja digitado e search()procuramos a próxima instância do caractere no início de uma palavra via \<. Limitamos isso à linha atual fornecendo a linha atual line('.'), como o terceiro parâmetro para search().

Pessoalmente, acho que prefiro usar /ou f, no entanto, existem outros plugins que fornecem funcionalidade semelhante:

Para obter mais ajuda, consulte:

:h search(
:h getchar(
:h /\<
Peter Rincker
fonte
7

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 pe ,é 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, eetc.). Geralmente, para definir um novo movimento ou objeto, você usa o onoremapcomando É 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 calterar, dexcluir, ycopiar etc.) que permite criar seu próprio operador.

Se você digitar g@{motion}vim, procurará a opfuncopçã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 opfuncopçã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 operatorfuncopção NextWordWithLettere, 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>gque define automaticamente a operatorfuncopçã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 opfunccomo NextWordWithLettere, 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 normalcomando 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 aonde está o cursor, digite ma. Em seguida, você pode se mover e voltar para a mesma linha da marca acom o comando 'aou para o caractere exato em que você definiu a marca acom 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 pe 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}( fpno 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 normalcomando 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 :rege 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 pestá 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 listuma 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 forloop itera sobre a variável liste 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 wordcom letterque usar o ==#operador. É um operador que diferencia maiúsculas de minúsculas (independentemente do valor da ignorecaseopçã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 wordmais um dentro da variável col. match()é uma função interna, cuja sintaxe base é match(string, pattern)e que retorna o índice em stringque patterncorresponde. 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 executeexecuta "normal! 10|", o que resulta no normalcomando digitando 10|para nós.

saginaw
fonte
Obrigado, é exatamente o que eu estava procurando. Eu não entendo metade dos comandos que você usou tho: /
solalito 24/11
2
Sinto muito por não explicar o código, vou editar a postagem para explicá-lo, para que você possa ajustá-lo ao seu gosto, se quiser. Só vai me levar um momento.
Saginaw
Obrigado, não se apresse! Estou sempre interessado em saber exatamente as ferramentas que estou usando.
Solalito 24/11/2015
Tentei simplificar o código e fiz o possível para explicá-lo em detalhes. Espero que, depois de ler, o código faça mais sentido.
Saginaw
Eu nunca esperei obter uma resposta tão completa. Eu li, mas vou precisar revisar com cuidado. +2 se eu pudesse!
Solalito 24/11/2015