Inserir um número incremental para cada linha em uma seleção ou em uma correspondência

10

Tenho um problema que consigo pensar em duas abordagens gerais para resolver, mas não conheço detalhes específicos para nenhuma delas.

...
Level 1:    cũng    also
Level 1:    và      and
Level 1:    như     like; such as
Level 2:    các     plural marker
Level 2:    của     belonging to
...

Para cada linha que começa com "Nível n", quero inserir um número, começando com "01". Para simplificar, vamos acrescentar o número.

Abordagem 1: Selecione manualmente todas as linhas com o mesmo nível. Invoque a magia que estou prestes a aprender.

Abordagem 2: escreva uma pesquisa e substitua o que corresponde a todas as linhas com o nível especificado, que em cada correspondência inclui um número no texto de substituição, que é incrementado em um a cada correspondência.

Encontrei perguntas semelhantes no StackOverflow ou em outros sites do Vim, mas cada um parece ter um ou mais dos seguintes problemas:

  1. Trata-se de inserir o número da linha atual em vez de um número arbitrário, mas incrementador.
  2. Não zera o número.
  3. Na verdade, não funciona para seleções no meu Vim 7.4 em execução no Windows 7. (Essas resultam no erro E481: No range allowed.)

Estou executando o gVim no Windows com o mswin.vim, mas uma solução que funcione em todas as instalações do vanilla Vim sem precisar personalizar a configuração pode ser a melhor.

hippietrail
fonte
As melhores tags que eu pude pensar para a pergunta não existem: seleção e intervalo , sinta-se à vontade para re-marcar ou criar uma dessas tags.
Hippietrail
11
Este plugin não é uma solução completa para o seu problema, mas é extremamente útil para adicionar colunas de números: VisIncr . Documentos aqui . FWIW.
Lcd047

Respostas:

17

Semelhante à resposta em https://vi.stackexchange.com/a/818/227 , você pode usar o comando global.

Com ele, você pode instruir o vim a procurar linhas correspondentes a um padrão e executar comandos nele.

No seu caso, você deseja anexar texto às linhas que começam com "Nível N:", para que nosso comando global possa ser

:g/^Level \d:/{COMMANDS}

Usando o comando substitute (substituição de expressão regular) para o comando

Os comandos são mais divertidos. Eu normalmente gosto de fazer uma substituição de expressão regular para coisas como essa, pois é fácil usar variáveis.

Exemplo para sua pergunta

:let i = 1 | g/^Level \d:/s/^/\=printf("%02d ", i)/ | let i = i+1

Como funciona

Na seção de substituição de um comando de substituição pode ser uma expressão.

A primeira coisa que faremos é definir uma variável ipara ser o número inicial. Eu escolhi 1, mas qualquer número serve.let i = 1

Em seguida, executamos nosso comando global, que nos prepara para executar uma ação nas linhas correspondentes. g/^Level \d:/

Teremos nosso comando global inserido o valor e incrementado nosso contador usando o comando de substituição e o comando let. s/^/\=printf("%02d ", i)/ | let i = i+1

A expressão regular do comando de substituição encontra o início da linha ^e a substitui por uma expressão, e nossa expressão será o resultado de uma impressão formatada. Como na linguagem C, o printf do vim usa parâmetros de formatação. %02dsignifica converter um argumento como se fosse um número decimal d, ocupando pelo menos 2 espaços 2e preenchendo com 0 0. Para detalhes e outras opções de conversão (incluindo formatação de ponto flutuante), consulte :help printf. Damos a printf nossa variável de contagem ie ela nos dá 01a primeira vez, 02a segunda vez, etc. Isso é usado pelo comando de substituição para substituir o início da linha, inserindo efetivamente o resultado da impressãof no início.

Note que eu coloquei um espaço após o d: "%02d ". Você não pediu na pergunta (e não vi exemplo de saída), mas suspeitei que você desejasse separar o número da palavra "Nível". Remova o espaço da sequência fornecida para printf para ter o número inserido ao lado de L no nível.

Finalmente, isso let i = i + 1incrementa nosso contador após cada substituição.

Isso pode ser aplicado geralmente para substituir partes de linhas correspondentes a outros critérios por dados funcionais arbitrários.

Usando comandos normais combinados

Isso é bom para inserções simples ou edição complexa. Assim como no substituto, usaremos global para corresponder, mas, em vez da substituição da expressão regular, executaremos uma série de operações como se digitadas pelo usuário.

Exemplo para sua pergunta

:let i = 1 | g/^Level \d:/execute "normal! I" . printf("%02d ", i) | let i = i+1

Como funciona

Os valores usados ​​são muito semelhantes ao substituto (ainda estamos usando printf para formatar nosso número para torná-lo 0 preenchido com 2 dígitos), mas a operação é diferente.

Aqui usamos o comando execute, que pega uma string e executa a string como um comando ex ( :help :exe). Construímos uma string que combina "normal! I" com nossos dados, que serão "normais! I01" na primeira vez e "normal! I02" na segunda vez, etc.

O normalcomando executa operações como se estivesse no modo normal. Neste exemplo, nosso comando normal é I, que insere no início da linha. Se tivéssemos usado, ddele excluiria a linha, oabriria uma nova linha após a linha correspondente. É como se você tivesse digitado I(ou qualquer outra operação) você mesmo no modo normal. usamos o !after normalpara garantir que nenhum mapeamento fique no nosso caminho. Veja :help :normal.

O que é inserido então é o valor da nossa printf, como no primeiro exemplo de uso de substituto.

Esse método pode ser mais sofisticado que o regex, porque você pode fazer coisas como execute "normal! ^2wy" . i . "th$p": o que vai para o início do texto ^, avançar duas palavras 2w, puxar até o i-ésimo caractere 'h' y" . i . "th, mover para o final da linha $e colar p.

É quase como executar uma macro, mas na verdade não usa um registro e pode combinar seqüências de caracteres de qualquer expressão. Eu acho isso muito poderoso.

Abordagem em que cada nível tem seu próprio contador

Você pode querer que cada nível obtenha seu próprio contador. Se você souber o número máximo de níveis antes do tempo, faça o seguinte (adicionar código extra para encontrar o nível mais alto pode não ser muito difícil, mas tornaria essa resposta muito longa. Isso está demorando muito).

Primeiro, liberte i, caso já o tenhamos usado como um número inteiro. Não podemos converter i em uma lista, temos que criá-lo dessa maneira.

:unlet! i

Em seguida, vamos definir i para ser uma lista contendo o número de níveis. Você mostrou 2 na sua pergunta, mas vamos assumir 10 para se divertir. Como a indexação de lista é baseada em 0, e não quero me preocupar em corrigir uma baseada em 1, como a sua lista, apenas criaremos elementos suficientes (11) e nunca usaremos o índice 0.

:let j = 0
:let i = []
:while j < 11 | let i += [1] | let j += 1 | endwhile

Em seguida, precisamos de uma maneira de obter o número do nível. Felizmente, o substituto também está disponível como uma função, portanto, forneceremos nossa linha e extrairemos o número do nívelsubstitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")

Como agora eu sou uma lista de 11 1s (cada índice é o contador do nosso nível), agora podemos ajustar qualquer um dos exemplos acima para usar o resultado dessa substituição:

Via comando substituto:

:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | s/^/\=printf("%02d ", i[ind])/ | let i[ind] += 1

Via comando normal:

:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | execute "normal! I" . printf("%02d ", i[ind]) | let i[ind] += 1

Exemplo de entrada:

Level 1: stuff

Level 1: Stuff

Some text
Level 3: Other

Level 1: Meh

Level 2: More

Exemplo de saída:

01 Level 1: stuff

02 Level 1: Stuff

Some text
01 Level 3: Other

03 Level 1: Meh

01 Level 2: More
John O'M.
fonte
Uau, muito enciclopédico. Eu li apenas a primeira parte e sinto que aprendi mais sobre o Vim apenas com isso do que nos últimos anos. Esse é o tipo de resposta que torna o Stack Exchange melhor do que a média dos sites de perguntas e respostas e também mostra os benefícios de ter um SE específico do Vim.
hippietrail
3

Você pode criar a partir de https://stackoverflow.com/a/4224454/15934 para zerar seus números.

" A reminder: how to start numbers at the first line
:'<,'>s/^\s*\zs/\=(line('.') - line("'<")+1).'. '

Mas, para simplificar a ação dos números de preenchimento, eu usaria um par de funções e um comando:

command!  -range=% -nargs=? PrependNumbers <line1>,<line2>call s:Prepend(<args>)

function! s:ReplExpr(nb_digits, number)
  return repeat('0', a:nb_digits - strlen(a:number)).a:number
endfunction

function! s:Prepend(...) range
  let pattern = a:0 > 0 ? '\ze'. a:1 : '^'
  let nb_values = (a:lastline - a:firstline) + 1
  let nb_digits = strlen(nb_values)
  exe ':'.a:firstline.','a:lastline.'s#'.pattern.'#\=s:ReplExpr(nb_digits, 1+ line(".")-'.a:firstline.')." "#'
endfunction

Depois, selecione suas linhas e digite :PrependNumber(você verá :'<,'>PrependNumber. [Nota: o comando aceita um parâmetro opcional: o padrão antes do qual o número será inserido]

Luc Hermitte
fonte
Mas não line(".")significa "usar o número da linha atual"? Esse foi o meu problema 1. Com respostas anteriores que pude encontrar.
Hippietrail
11
Sim, ele é. É por isso que subtraio o número da primeira linha do intervalo.
precisa saber é o seguinte
Ah OK eu não testei ainda, porque eu nem me lembro como inserir um script no Vim ... tem que ler sobre esse primeiro (-:
hippietrail
11
Como você está no Windows, copie e cole o código $HOME/vimfiles/plugin/whatevernameyouwish.vim. Ou mesmo o seu $HOME/_vimrc(nome do arquivo do Windows), se preferir (pela primeira vez). Se você não tem certeza do que $ HOME é em sua máquina, pedir vim o que ele pensa que é ->:echo $HOME
Luc Hermitte
11
Você nem precisa :sourcese o script vim está instalado corretamente no diretório certo ({rtp} / plugin ou {rtp} / ftplugin / {filetype} / ftplugin para plugins específicos do tipo de arquivo). :sourceé o que tivemos que jogar há mais de uma década e meia atrás.
precisa saber é o seguinte
2

Você pode abordá-lo gravando uma macro. Começando com seu arquivo original, anteceda a primeira instância com o número.

01 Level 1:    cũng    also
Level 1:    và      and
Level 1:    như     like; such as
Level 2:    các     plural marker
Level 2:    của     belonging to

Mova o cursor para 01, selecione e puxe-o com yiw. Agora você deseja registrar suas ações a partir deste ponto.

qq/^Level 1<CR>P<C-A>A<space><esc>0yiwq

  • qq Iniciar uma macro no registro q
  • /^Level 1<CR> Procure uma linha começando com "Nível 1"
  • P cole antes do cursor (contém seu número)
  • <C-A> incrementa o número
  • A<space><esc> Inserir um espaço após o número
  • 0 Mover para o início
  • yiw puxar o número atual
  • q Terminar a macro

Repita essa macro usando @q.

jecxjo
fonte
11
É <C-A>controle + um? Isso seleciona tudo no Vim no Windows.
Hippietrail
É Control + a. No vim, ele deve incrementar um número. Se você alterou as combinações de teclas para executar ações do Windows em vez das ações do Vim, não será capaz de fazer a maioria das coisas que as pessoas publicam aqui.
Jecxjo # 29/15
Não, a versão Vim para Windows vem com ligações diferentes devido à infinita sabedoria de alguém. Estou apenas usando as ligações prontas para uso da baunilha. Eu nunca mudou uma chave Vim obrigatório em mais de vinte anos, que eu me lembre (-:
hippietrail
Não deve ser o caso, minha instalação do Windows possui ligações normais do vim. É possível que você tenha instalado o build do vim de outra pessoa? Ou você pode estar executando o vim no modo "Fácil"? Eu acredito que o Windows instala ícones da área de trabalho com opções de modo normal e fácil.
Jecxjo # 29/15
3
Não, é porque a instalação padrão no Windows carrega o mswin.vim. Se você cria seu próprio vimrc e não carrega o mswin.vim, obtém as ligações normais do vim. Eu mantenho um único vimrc para todas as minhas instalações (Linux, Mac e Windows) e nunca lido com o mswin.vim. Para obter mais informações sobre esse problema, consulte stackoverflow.com/questions/289681/…
jecxjo