1

I have to preprocessor-comment out a lot of C-code. E.g. I want this:

if (abcd == true)
  do_smt();

To become this:

#if 0
if (abcd == true)
  do_smt();
#endif

This is tedious work, so I was hoping to be able to automate it. I want to Visually select some lines, and then apply a substitution so that the tags are added before and after it.

I already found this: https://superuser.com/questions/782391/vim-enclose-in-quotes , but unfortunately that only works for single-line encapsulations. When using it on multi-lines, it does the subtitution for every line..

I then found this: Regex to match any character including newline

It does do the correct substitution, but somehow it also substitutes the line after the selected block?

I tried adding a 1 behind the command, but then it somehow only selects the wrong line?

This is what I have so far:

'<,'>s/\%V\(\_.*\)\%V/#if 0^M\1^M#endif/

Anyone know how I would be able to do this?

2
  • Why take a detour over visual selection? Save yourself the trouble of pressing V and just insert that preprocessor stuff. Save the two preprocessor instructions to registers (or create macros if that's your thing) so you can quickly paste them in.
    – Friedrich
    Sep 21, 2023 at 18:55
  • 1
    @Friedrich that's what I initially did. But if I then do stuff in between, the registers might get filled with other junk. Besides, visually selecting lines and pressing a button is still way less keystrokes than selecting a buffer, pasting, moving all the way down, selecting another buffer and again pasting.
    – Opifex
    Sep 22, 2023 at 8:59

4 Answers 4

2

There are many, many ways to do it.

What I'm currently using

In lh-cpp, I have the following definition to wraps the lines, thanks to my lh-brackets plugin:

:Brackets #if\ 0 #endif -insert=0 -nl -trigger=<localleader>0

If you are looking for a single mapping and a simple solution, ignore it.

What I've been using lately: S (and in interactive mode)

-> select the lines with V, then, from visual mode: S#if 0<cr>#endif<esc>P (I type the real keys instead for enter, escape...)

This could be made into a mapping

xnoremap <buffer> µ S#if 0<cr>#endif<esc>P

Note:

  • it will mess the visual selection (try gv after, and a register, the default one being @")
  • µ is my go to keystroke of choice when I need a new complex action to execute many times in a vim session, and that I shouldn't need any more after that.

What I'd be tempted to use nowadays: append() (for mappings)

It's a bit more verbose, but it will leave the registers and the selection alone.

xnoremap <buffer> µ <c-\><c-n>:call append("'>", '#endif')<cr>:call append(line("'<")-1, '#if 0')<cr>

About the venerable solution

(Solution I've used for ages in mappings)

  1. First append after the selection
  2. Insert before it
`>o#endif<esc>`<O#if 0<esc>

Which could be turned into a mapping with:

xnoremap <buffer> µ <c-\><c-n>`>o#endif<esc>`<O#if 0<esc>

The key elements are:

  • The bookmarked cursor positions: :h '>, :h '<
  • The insert new line after (o) and before (O) the current line
  • The order of execution, very important!
  • Much less important, CTRL-\_CTRL-N is the robust way to leave any mode.

Other approaches

  • many will use Surround, or any snippet plugin
  • :s, definitively not my first choice here (and yet, it usually is most of the time) as the lines need to be considered grouped together. In that case your solution can be simplified into: :'<,'>s/\%V\_.*\%V\n/#if 0\r&#endif\r
5
  • Thanks! These work great! Only the venerable solution I'm not sure how to use that one? This answer still has some room for improvement if it would contain a brief explanation on how they work.
    – Opifex
    Sep 22, 2023 at 14:16
  • Mhmmm... interesting. I'm using your second xnoremap now (because it's perfect for what I want to do! ), but strangely enough it only works in the first tab of my Vim? I doesn't work in the second or any after that. Even if I close the first tab, it still doesn't work?
    – Opifex
    Sep 22, 2023 at 14:35
  • That's because of <buffer>: it makes sure it's only defined in the first buffer -- this mapping would actually make sense in a C ftplugin, like for instance ~/.vim/ftplugin/c/c_mysnippets.vim Sep 22, 2023 at 16:21
  • Is there a way around it, without using a plugin? I like this solution a lot, but I'd also like to keep it contained in a single vimrc.
    – Opifex
    Sep 23, 2023 at 0:29
  • ftplugin. Not plugin. It's better to restrict filetype definitions to the right filetypes. This way, one can have the same trigger that do something adapted to a different language. For instance, in Python we could surround with a docstring, and so on. Any way. You can define an autocommand in your vimrc which will be triggered for C & C++ files. As long as you do have many things to define it fine: this approach doesn't scale contrary to ftplugins that are the standard way for scaling solutions. Sep 24, 2023 at 11:32
2

I want to Visually select some lines, and then apply a substitution so that the tags are added before and after it.

Like this, for example,

xnoremap <leader>0 "="#if 0\n#endif\n"<CR>pp

Select the lines and press "leader-zero" or whatever.

2
  • 1
    This one looks very elegant! Could you also explain how it works, so newbies like myself also more or less understand it?
    – Opifex
    Sep 25, 2023 at 8:20
  • @Opifex In order to debug a mapping one can simply type the right hand side interactively. Here we do put twice: first time in visual mode to cut selection and to paste if/endif lines; and the second time in normal mode to paste previously cut text back. Right between if/endif lines.
    – Matt
    Sep 25, 2023 at 12:49
2

Without knowing at least part of the source code, it's hard to provide a "best" answer. To expand on my earlier comment, let me lay out two other possible ways to add those preprocessor lines.

Actually, I'd not preprocessor-comment C code in the first place and instead use regular /* ... */ comments just like K&R wanted things to be but that's a different issue and has no impact on my answer.

To have the #if 0 and #endif lines ready, I'd assign them to named registers i and e (pick different letters if you prefer) by having the cursor on them and doing "iyy and "eyy, respectively. See :help registers and help quote.

Now, register i will contain the #if 0 part and e the #endif. These get overwritten with junk if and only if you assign junk to them so don't.

Using "ip and "ep, you can quickly insert the respective lines.

In fact, this is one of the rare cases where I'd actually go another step and record the commands to quickly insert the preprocessor lines. I would record to the register i like this

qiO#if 0<Esc>q

Replay with @i to insert #if 0 above the current line. See :help complex-repeat for an explanation of how this works.

I'd consider to add some kind of jump movement to the end of the recording because you probably want to go down to the end of the current code block or something where you would insert the #endif.

Speaking of #endif, I'll leave the respective recording as an exercise to the reader (and also because I like to write "I'll leave this as an exercise to the reader").

Individual tastes and styles vary. Some people will prefer visual mode and that's also fine. Personally, I just dislike visually selecting large portions of code. Also, all of this works with relatively low-tech Vim commands like named registers. Any part of it is easily understandable.

1

After a lot of trial and error, I found something that works:

s/\(\%V\_.*\%V\n\)/#if 0^M\1#endif^M/

Not sure how or why this one works, but the others didn't though. If someone has an explanation, feel free to share.

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.