0

this post has been simplified and re-written here: Making a function work with a loop

I have this one liner, which takes the regex pattern match and replaces it with a string and a couple of counters:

let a=35 | let b=2 | g/[0-9]\{2\}_[0-9]\{2\}/s//\=a.'_PART__'.b/ | let a=a+1 | let b=b+1

eg a file containing this

10_20
20_30
30_40
40_50
10_20
20_30
30_40
40_50

becomes a file containing this

35_PART__2
36_PART__3
37_PART__4
38_PART__5
39_PART__6
40_PART__7
41_PART__8
42_PART__9

Here is what it does,
1 searches for regex [0-9]\{2\}_[0-9]\{2\}
2 replaces regex with a.'_PART__'.b where
a starts at 35 and increments by 1 for each line in the file
b starts at 2 and increments by 1 for each line in the file

How can I express this in a .vimrc function with a for next loop or a while loop? .. and how does the one liner know intrinsically to loop through each line in the file, which it does, whereas a function expressed as the distinct parts of the one liner does not)

This function works:

function ReplaceAndIncriment() abort
  let a=35
  let b=2 
  g/[0-9]\{2\}_[0-9]\{2\}/s//\=a.'_PART__'.b/ | let a=a+1 | let b=b+1
endfunction

But how can I make the function work, with all lines seperated and on differemt lines, as shown below, and use a for next, or, while do loop?

function ReplaceAndIncriment() abort
  let a=35
  let b=2 
  g/[0-9]\{2\}_[0-9]\{2\}/s//\=a.'_PART__'.b/
  let a=a+1
  let b=b+1
endfunction
8
  • 1
    It sounds like you're overlooking the :g command in the one liner. Looping over all buffer lines (and running the following command on those lines that match the associated pattern) is exactly what it does. :h :g. And you can use it in your vimrc, too. :)
    – B Layer
    Jun 12, 2021 at 12:44
  • Thanks. Curious though .. when I seperated between <bar> commands into distinct lines in a function, it does not work.
    – Kes
    Jun 12, 2021 at 12:45
  • I've added the failed function in an edit to the OP above. How could I get a line seperated function as shown to work?
    – Kes
    Jun 12, 2021 at 12:51
  • 1
    Because :g considers everything that follows it on its own line as part of the command(s) to be executed on each matching line...bar or no bar. That's how the two variables get incremented properly. If you join those three lines in the function it'll work there, too.
    – B Layer
    Jun 12, 2021 at 12:52
  • 1
    See :h :bar for list of commands that don't recognize bar as a command separator in quite the same way as other commands do. With :g bar is still a command separator but completely in the context of :g execution.
    – B Layer
    Jun 12, 2021 at 12:55

1 Answer 1

2

You can't really drop the |s inside a :g command without changing the behavior. Some commands such as :g take the | as part of their arguments, which means breaking them into newlines doesn't result in the same command. (See :help :bar and note that :global is in the list of such commands.)

If the line length is an issue, you can break lines using backslashes for continuation. In Vimscript it's a little odd in that the backslash goes in the beginning of the continuation line.

In your case:

function! ReplaceAndIncrement() abort
  let a=35
  let b=2 
  g/[0-9]\{2\}_[0-9]\{2\}/
      \ s//\=a.'_PART__'.b/
      \ | let a=a+1
      \ | let b=b+1
endfunction

Note that you still need the |s, since this is essentially a single :g command executing a sequence of commands for each matching line. But hopefully line continuations will help you deal with the line length of such a command in an acceptable way.

5
  • that is a great answer, can it be done by dropping the g making it linewise and using a loop?
    – Kes
    Jun 12, 2021 at 15:11
  • 1
    Thanks. I didn't know i could use \ for line continuation in vim. Good to know :)
    – Kes
    Jun 12, 2021 at 15:12
  • 2
    You can't really use :g together with a for or while, they're different... If you use a traditional loop, you'll have to find a different way to find matching lines (e.g. the search() function, or one of the match() functions looping line by line, etc.) The code would be completely different at that point and a whole rewrite, so I don't see why you'd do that if :g does the job perfectly...
    – filbranden
    Jun 12, 2021 at 16:05
  • 1
    Thanks for the pointers. Appreciated. Also I think you are spot on about the second post being a confusing replicate, which is why I have deleted it. I understand use of g is more efficient than a loop. I'm asking about the loop becasue (1) I am curious as to why this is so difficult to do, & (2) I had a good look into this and made quite a few attempts at using a loop to replace g, with no luck so far. While doing so I got bogged down in syntax minutiae / confusion, on a number of terms during my tests. I'll delve into search() and match() and see where I get / come back to it later.
    – Kes
    Jun 12, 2021 at 21:07
  • 1
    @Kes Certainly :g is more convenient and in that way "easier" than a loop. But really, other than being more verbose a loop is not harder it's just different. As for which is more (runtime) efficient that depends on how things were implemented and optimized in Vim. The only way to know is to test both and compare. (And though not likely it's actually possible the "winner" may depend on what OS the testing is done on.) FYI.
    – B Layer
    Jun 13, 2021 at 0:39

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.