Use o padrão do comando ex global encontrado em uma linha para substituir em outra linha


8

No meu código fortran, tenho muitos blocos como os seguintes

subroutine name(arg1,arg2,arg3,...)
:
end subroutine

e eu quero que eles se tornem

subroutine name(arg1,arg2,arg3,...)
:
end subroutine name

Onde cada um desses blocos pode ser indentado por qualquer quantidade de espaços (na verdade, esses blocos podem ser aninhados). Eu tentei com o comando

:g/^ *subroutine \(.*\)/;/end subrout/s/end subroutine/& \1

que muda cada linha (eu li na linha de fundo) mudando nada, assim como o buffer \1está vazio. O comando

:g/^ *subroutine \(.*\)/;/end subrout/s/end subroutine/& hello

funciona bem, mas obviamente não é o que eu quero. Portanto, a pergunta é: como posso usar na sequência de substituição um padrão correspondido pelo comando ex global :g?

EDIT Estou editando a pergunta, pois, no momento em que a publiquei, aceitei rapidamente a resposta que era uma resposta ao caso específico a que me referi, mas não ao título da pergunta, que é um pouco mais geral.

Eu vou direto ao ponto. Eu tenho um arquivo como o seguinte (não faz sentido apenas para torná-lo o mais genérico possível):

# sec1
fun1  are you a function?
fun2 no.
fun3 ok, nice to meet you!

# 2nd part
first line
third line
ops (it was the second)
there were 3 lines in the preceding #

# fourth group
now there's just one
and foreveeer

(De fato, as linhas que seguem cada #linha são centenas.)

Eu tenho que fazer substituições em cada linha após cada #linha (e precedendo a seguinte #linha); todas essas substituições devem usar o texto após a #linha de frente (ou parte dela). Um exemplo de desejo que eu gostaria de alcançar é

# sec1
fun1 in sec1  are you a function?
fun2 in sec1 no.
fun3 in sec1 ok, nice to meet you!

# 2nd part
first in 2nd line
third in 2nd line
ops in 2nd (it was the second)
there in 2nd were 3 lines in the preceding #

# fourth group
now in fourth there's just one
and in fourth foreveeer

Observe que apenas a primeira palavra a seguir #é inserida nas seguintes linhas.

Então, como eu já disse, gostaria de saber se o padrão do :gcomando pode ser usado na cadeia de caracteres substituta. Estou muito interessado nesta resposta, pois é muito irritante registrar uma macro (pressionar uma tecla errada significa começar tudo de novo!), Enquanto um comando como esse,

:g/^# \(\<\w\+\>\)/+;/^#/- s/\(\<\w\+\>\)\(.\+$\)/\2 \1 \3

seria perfeito! ... E é difícil para mim acreditar que não há como usar um comando que faça o trabalho!

Respostas:


4

Você pode usar uma combinação de :global, matchit e :normal!:

:g/^\s*subroutine/norm ^whye^%$p

Explicação:

  • :g/^\s*subroutine: para todas as linhas correspondentes ^\s*subroutine, faça:
  • normal: entra no modo normal (! significa que não há mapeamentosque não usam norm!, como o !remove a chamada para matchit (graças @SatoKatsura))
  • ^vá para o primeiro caractere não em branco (não 0, para manipular sub-rotinas aninhadas)
  • whye: vá para a segunda palavra (nome), volte 1 caractere (espaço) e copie-o (o espaço é para facilitar um pouco a colagem no final)
  • ^%: volte para o primeiro caractere não em branco e use matchit para encontrar a correspondência end
  • $p: colar name(com um espaço em branco à esquerda) no final da linha

2
norm!mata %de matchit. Solução: :%g/^\s*subroutine/norm ^whye^%$p.
Sato Katsura

Ah, você está certo. Na verdade, eu testei, mas usei em normvez de norm!, pois estava bastante confiante de que não havia nenhuma configuração personalizada interferindo aqui ... Boa captura e obrigado pela correção.
27716 Marth

3
@SatoKatsura: Não vejo por que você precisa usar :%g. Tentei tanto em um buffer com sub-rotinas distintas e aninhadas (e com -u NONE) e obtive os mesmos resultados em ambos os casos. Embora :globalo intervalo padrão fosse 1,$(ie %). Em que caso a %diferença faria?
Marth

5

Uma solução possível é usar uma macro:

qa/^subroutine<CR>f<space>/end subroutine<CR>$pq

Que pode ser detalhado como este:

qa                  Record a macro in the a register
/^subroutine<CR>    Go to the next occurence of a subroutine declaration
f<space>            Go to the space separating "subroutine" and the name
y$                  Yank till the end of line (i.e. the name)
/end subroutine<CR> Go to the next occurence of "end subroutine"
$p                  Put the name at the end of the line
q                   Stop recording the macro

Em seguida, você pode executar sua macro com X@aonde Xestá o número de sub-rotinas para prosseguir


11
Bem, eu uso muitas macros (registre c para comentar, u para descomentar, t para tab e assim por diante), para que mais uma macro não seja um problema. De qualquer forma, meu ponto de vista é que eu poderia sobrescrever erroneamente a macro com algo sem sentido, mesmo sem perceber (q está perto de we ainda não sou um mestre em digitação por toque). Então, por enquanto, espero / espero por outras respostas.
Enrico Maria De Angelis

2
Fortran pode ter sub-rotinas aninhadas.
Sato Katsura

4

Aqui está a solução macro do @statox , modificada para lidar com parâmetros de sub-rotina e sub-rotinas aninhadas. Isso pressupõe que você também instalou o matchit :

  • qaq - registro claro @a
  • qa - iniciar a gravação no registro @a
  • /^\s*subroutine\zs<CR>- procurar subroutine; \zsdeixa o cursor no espaço a seguir
  • y2w- arranque o nome; 2porque queremos incluir o espaço, wporque queremos um word(isso para em (etc.)
  • h - vá para a esquerda, até o final de subroutine
  • %- ir para correspondência end subroutine(três aplausos para matchit)
  • $p - cole o nome
  • _ - vá para o primeiro caractere não em branco, ou seja, o início de (the) end
  • % - de volta à correspondência subroutine
  • j- para baixo, para procurar mais subroutines
  • 0- vá para o início da linha, para não perder um subroutinena linha atual
  • @a - chamar a macro atual recursivamente
  • q - macro final

Para executá-lo:

  • :set nowrapscan - evite um loop infinito: a macro será resgatada quando atingir a parte inferior do arquivo
  • 1G - ir para o topo do arquivo
  • @a - executar macro

3

Você pode usar um comando substituto:

:%s/\v(^subroutine (.+)\_.{-}end subroutine)/\1 \2

Isso corresponde ao todo subroutine / end subroutineno grupo 1 e ao nome da sub-rotina no grupo 2 e concatena-os.

O {-}é um não-ganancioso, *então ele irá parar no primeiro elemento encontrado.

\_.combinar tudo e a new-line.


2
Isso não vai funcionar, o Fortran pode ter sub-rotinas aninhadas.
Sato Katsura

11
Eu sei, mas o OP não especificou nada sobre isso. Sua proposição e a outra resposta não funcionarão com sub-rotinas aninhadas. A sub-rotina de "fechamento" correspondente é outra questão.
nobe4

2
Não é grande coisa, matchitpode lidar com sub-rotinas aninhadas.
Sato Katsura
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.