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 Answers 4

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
3
  • 2
    norm! kills % from matchit. Solution: :%g/^\s*subroutine/norm ^whye^%$p. Commented Jul 15, 2016 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
    Commented Jul 15, 2016 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
    Commented Jul 15, 2016 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

2
  • 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
    Commented Jul 15, 2016 at 10:03
  • 2
    Fortran can have nested subroutines. Commented Jul 15, 2016 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.

3
  • 2
    This won't work, Fortran can have nested subroutines. Commented Jul 15, 2016 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
    Commented Jul 15, 2016 at 10:43
  • 2
    It isn't a big deal, matchit can handle nested subroutines. Commented Jul 15, 2016 at 10:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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