0

i want to run this global command :

/3)\(.*\n\?.*\)\{1,10\};;/

which matches multlines. I want to append a line after the matched block(of lines) across multiple buffers. I don't know where to begin with. Is there an append controller like in sed /a that appends after the match ? or is there any other way to achieve that?

i am trying to come up with the global command like ..

:g/3)\(.*\n\?.*\)\{1,10\};;/norm 

But am stuck as to how to achieve the 'appending' after the block part.

Also, how would it be possible to run across multiple buffers as a macro.

Edit: the line to be appended is :

6) "$TERMINAL" -e "$EDITOR" "$0" ;;

Sample :

      3) notify-send "📰 News module" "\- Shows unread news items
- Shows 🔃 if updating with \`newsup\`
- Left click opens newsboat
- Middle click syncs RSS feeds
<b>Note:</b> Only one instance of newsboat (including updates) may be running at a time." ;;
esac

After Operation:

           3) notify-send "📰 News module" "\- Shows unread news items
- Shows 🔃 if updating with \`newsup\`
- Left click opens newsboat
- Middle click syncs RSS feeds
<b>Note:</b> Only one instance of newsboat (including updates) may be running at a time." ;;
           6) "$TERMINAL" -e "$EDITOR" "$0" ;;
esac

Would be great if it can preserve the indenttation.

Note: There are other are samples that have more(or less) lines between 3) and ;; The regex is working (matches all desired patterns).

I tried with substitution like :

:%s/\(3)\(.*\n\?.*\)\{1,10\};;\)/\1\n  6) "$TERMINAL" -e "$EDITOR" "$0" ;;/

But as i feared, the \n part didn't work in the replace section.

2
  • 2
    It would be most helpful if you could provide sample text before and after your desired transformation. Please edit. (Until I can write a full answer based on that info, you might like to know about bufdo, argdo, etc.
    – D. Ben Knoble
    Feb 24, 2021 at 14:38
  • 1
    @D.BenKnoble thanks, looks like bufdo will take care of writing to all the buffers but i am still stuck at how would i go about 'appending' the new text. I added sample of before and desired outcome. Feb 24, 2021 at 16:27

2 Answers 2

4

You can use a substitution with \zs (or &) to do to "append" your line in the form:

:%s/pat\zs/\rAppendMe/g
:%s/pat/&\rAppendMe/g

To do this across multiple buffers you need a list of files/buffers then a way to execute your command.

  • All buffers use :bufdo {cmd} where {cmd} is your command
  • These are buffers in the Quickfix List then you can use :cfdo {cmd} or :cdo {cmd}
  • You can build up a list with :args/:argadd then use :argdo {cmd} to execute over the argument list

Next we need to fix the pattern. The pattern is likely too greedy if two patterns are found within 10 lines of each other. Instead we can use a non-greedy variant, \{-} which is *? in perl-speak

/3)\_.\{-};;

To put it all together, let's use the quickfix list. So we search (via :vimgrep) though the matches, then run our command over them with :cfdo

:vimgrep /3)\_.\{-};;/ *.applescript
:cfdo %s//&\r"$TERMINAL" -e "$EDITOR" "$0" ;;/ge | w

Some notes:

  • Can use s// to reuse the pattern from :vimgrep
  • Use the e to ignore an error of not finding the pattern
  • Use | to run separate commands
  • Use :write/:w to write the file to disc
  • May want to use look into using the traces.vim plugin to help you see your patterns and substitutions

For more help see:

:h :s
:h /\zs
:h /\{-
:h s/\&
:h :bufdo
:h :cdo
:h quickfix
:h :args
:h :vimgrep
:h :bar
1

Peter Rincker's existing answer is the correct one for this, but there is another technique you could use if you want to do this interactively, reviewing each change as you make it.

  1. First get the text you want to append into a register. A simple way of doing this is typing yy with your cursor on a line containing the text.

  2. Enter the following command:

     :/3)\_.\{-};;\zs/put0
      /3)\_.\{-};;   /      # A line, specified using Peter's version of your regex
                  \zs       # Start the match here (i.e. at the BOTTOM of the match)
                      put0  # Append the contents of the "0 yank register
    

    This will append your line to the first instance of the pattern after your cursor.

  3. To repeat, type @:. This will replay the most recent ex command, appending your line to the next instance of the pattern. If you happen to know how many instances you need to replace, you can supply a count to this command e.g. to do it 5 times: 5@:

You'd think you should be able to use this technique with the :global command simply by entering :g/3)\_.\{-};;\zs/put0, but unfortunately it seems that the :global command ignores the \zs and uses the start of the match as the line where it will apply the edit.

You can use a positive lookbehind \@<= to append to the end of a multiline pattern with :global//put, but unfortunately, \@<= will only match patterns that are maximum of two lines long, so that doesn't work for your use-case, either.

Your Answer

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

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