8

In my fortran code I have a lot of blocks like the following

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

and I want them to become

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

Where each of these blocks can be indented by any amount of spaces (in fact, such blocks can be nested). I tried with the command

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

which changes each line (I read it in the bottom line) changing nothing, just as the buffer \1 is empty. The command

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

works well, but is obviously not what I want. So the question is: how can I use in the replacement string a pattern matched by the global ex command :g?

EDIT I'm editing the question since, at the time I posted it , I hurriedly accepted the answer which was an answer to the specific case I referred to, but not to the title of the question, which is a little bit more general.

I go to the point. I have a file like the following (non-sense just to make it as general as possible):

# 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

(In fact, the lines following each #-line are hundreds.)

I have to make substitutions in every line following each #-line (and preceding the following #-line); these substitutions should all use the text after the leading #-line (or part of it). An example of want I would like to achieve is

# 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

Note that only the first word following # is inserted in the following lines.

So, as I already said, I'd like to know if the pattern of the :g command can be used in substitute string. I'm so interested in this answer since it is very annoying to register a macro (one wrong keystroke means starting all over again!), whereas a command like this,

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

would be perfect! ... And it's hard for me to belive that there's no way to use a command that does the job!

4

You could use a combination of :global, matchit and :normal!:

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

Explanation:

  • :g/^\s*subroutine: for all lines matching ^\s*subroutine, do:
  • normal: enter normal mode (! means no mappings do not use norm!, as the ! removes the call to matchit (thanks @SatoKatsura))
  • ^ go to the first non-blank character (not 0, to handle nested subroutines)
  • whye: go to second word (name), go back 1 character (space) and copy it (the space is to make the pasting at the end a bit easier)
  • ^%: go back to the first non-blank character and use matchit to find the matching end
  • $p: paste name (with a leading whitespace) at the end of the line
  • 2
    norm! kills % from matchit. Solution: :%g/^\s*subroutine/norm ^whye^%$p. – Sato Katsura Jul 15 '16 at 12:55
  • Ah you're right. I actually did test it, but I used norm instead of norm!, as I was fairly confident I didn't have any custom setting interfering here… Good catch, and thanks for the correction. – Marth Jul 15 '16 at 12:57
  • 3
    @SatoKatsura: I fail to see why you need to use :%g. I tried both on a buffer with distinct and nested subroutines (and with -u NONE) and got the same results in both cases. I though :global's default range was 1,$ (ie %). In what case would the % make a difference? – Marth Jul 15 '16 at 13:41
5

A possible workaround is to use a macro:

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

Which can be detailled like this:

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

You can then execute your macro with X@a where X is the number of subroutines to proceed

  • 1
    Well, I use a lot of macros (register c to comment, u to uncomment, t to tab and so on), so one more macro wouldn't be a problem. Anyway my point of view is that I could erroneously overwrite the macro with something non sense without even realizing it (q is near w and I'm not yet a master in touch typing). So, for now, I'm going to hope/wait for other answers. – Enlico Jul 15 '16 at 10:03
  • 2
    Fortran can have nested subroutines. – Sato Katsura Jul 15 '16 at 10:40
4

Here's the macro solution by @statox, modified to deal with subroutine parameters and nested subroutines. This assumes you also have installed matchit:

  • qaq - clear register @a
  • qa - start recording in register @a
  • /^\s*subroutine\zs<CR> - search for subroutine; \zs leaves cursor on the space following it
  • y2w - yank the name; 2 because we want to include the space, w because we want a word (this stops at ( etc.)
  • h - go left, to the end of subroutine
  • % - go to matching end subroutine (three cheers for matchit)
  • $p - paste the name
  • _ - go to the first non-blank character, i.e. the beginning of (the) end
  • % - back to the matching subroutine
  • j - down, to look for more subroutines
  • 0 - go to the beginning of line, so that not to miss a subroutine on the current line
  • @a - call the current macro recursively
  • q - end macro

To run it:

  • :set nowrapscan - avoid an infinite loop: the macro will bail out when it reaches the bottom of file
  • 1G - go to top of file
  • @a - run macro
3

You can go with a substitute command:

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

This match the whole subroutine / end subroutinein the group 1 and the subroutine name in the group 2, and concatenate them.

The {-} is a non-greedy * so it will stop at the first element found.

\_. match everything and a new-line.

  • 2
    This won't work, Fortran can have nested subroutines. – Sato Katsura Jul 15 '16 at 10:40
  • 1
    I know, but OP have not specified anything about it. His proposition and the other answer won't work with nested subroutines. Matching "closing" subroutine is another question altogether. – nobe4 Jul 15 '16 at 10:43
  • 2
    It isn't a big deal, matchit can handle nested subroutines. – Sato Katsura Jul 15 '16 at 10:45

Your Answer

By clicking ā€œPost Your Answerā€, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.