2

When editing c and cpp files, this is the behavior I want to achieve:

  • When typing a comment line of the form // Line..., pressing <CR> should not add the comment header // in the next line.
  • However, if while typing the // Line..., the line length exceeds the tw I set, auto wrap should break the line, and should insert // in the next line.
  • The auto comment insertion behavior of /* * * */ style comments should not change. That is, after typing in /*, both explicitly hitting <CR> and auto wrap should insert * into the next line, produce the single space indentation, and if I type /, it should close the comment block with */, properly indented.

I have fo=tcrqn and comments=s1:/*,mb:*,ex:*/,:// set for c and cpp files.

I tried the following:

  • Following the suggestion in this superuser answer, modified for inoremap instead of nnoremap:
inoremap <silent> <cr> <esc>:set paste<cr>o<esc>:set nopaste<cr>:startinsert<cr>`

This does not work because although the desired behavior for // style single line comments are achieved, it destroys the behavior of /* * * */ style block comments, preventing auto insertion of * characters when I press <cr> in a comment block.

  • Then I tried modifying it to the following:
inoremap <silent> <cr> <esc>:set comments-=://<cr>:set comments+=f://<cr>o<esc>:set comments-=f://<cr> set comments+=://<cr>:startindent!<cr>

This mostly works, except that when the * is inserted by pressing <cr> in the block /* * * */ comments, the inserted indentation is now missing. That is, representing the cursor position with |, after pressing <cr> I want something like:

/*
 * Line ... here I press <cr>
 * | <- Notice space between cursor and *
 */

But what I get is:

/*
 * Line ... here I press <cr>
 *| <- No space between cursor and *
 */
  • I tried to fix the missing indentation problem by adding and deleting a space character:
inoremap <silent> <cr> <esc>:set comments-=://<cr>:set comments+=f://<cr>o<Space><BS><esc>:set comments-=f://<cr> set comments+=://<cr>:startindent!<cr>

This does fix the indentation issue, but now I have a new problem: typing / does not close the comment bock, but instead inserts an / character. This is what I want:

/*
 * ...
 * | <- cursor is here. I type '/', and the line becomes:
 */  <- Notice no space between * and /

But I get:

/*
 * ...
 * | <- cursor is here. I type '/', and the line becomes:
 * / <- Notice there is a space between * and /, comment block is not closed.

At this point I am out of ideas. Any help is much appreciated.

4
  • 1
    So basically you want fo=tcrqn for /**/, and fo=tcqn for //, right?
    – Matt
    Jun 13, 2020 at 7:36
  • The easiest solution is to use o instead of <CR> when you don't want a comment to continue.
    – Matt
    Jun 13, 2020 at 7:38
  • 2
    @Matt, yes, basically fo=tcrqn for /**/ and fo=tcqn for // is what I want. I want to avoid having to use a different key for ending and continuing comments, if possible.
    – Diggs
    Jun 13, 2020 at 8:06
  • Welcome to Vi and Vim!
    – filbranden
    Jun 13, 2020 at 9:49

2 Answers 2

3

IMO, it's easier to do simply:

inoremap <expr><CR> getline('.') =~# '^\s*//' ? '<C-O>o' : '<CR>'

Perhaps, this mapping (with <buffer> modifier, of course) should be added to after/ftplugin/c.vim and not to the global .vimrc

1
  • Thank you very much, this worked very well. I have incorporated this into an ftplugin file according to the suggestions made by @fibranden.
    – Diggs
    Jun 13, 2020 at 19:22
2

The idea of removing :// from 'comments' can be made to work, but instead of leaving Insert mode with <Esc>, use <C-\><C-O> to run a single Normal command (in this case, the : command to run the Ex :set command), returning to Insert mode as soon as that command is completed.

Using <C-\><C-O> here instead of <C-O> is important to preserve the cursor position and not lose the whitespace after the * as you were experiencing. The <C-\><C-O> command works "like <C-O> but don't move the cursor." (is what its help says.)

This mapping works:

inoremap <silent> <CR> <C-\><C-o>:set comments-=://<CR><CR><C-\><C-o>:set comments+=://<CR>

Additionally, you might want to keep this mapping only for file type cpp and maybe also c, so I'd recommend adding an additional <buffer> flag to the inoremap command, and storing it in a file ~/.vim/after/ftplugin/cpp_comments.vim so it's loaded for the cpp filetype.

If you also want it for c (or other filetypes with the same comment style), you can have file ~/.vim/after/ftplugin/c_comments.vim load it through a command such as:

source <sfile>:h/cpp_comments.vim

To load the file by that name from the same directory.

For completeness, you might also want to update the b:undo_ftplugin variable, to undo the mapping if you change 'filetype' of the current buffer. In order to do that, add the following snippet to the cpp_comments.vim file with the mapping:

if !exists("b:undo_ftplugin")
  let b:undo_ftplugin = ""
endif
let b:undo_ftplugin = b:undo_ftplugin . "|sil! exe 'iunmap <buffer> <CR>'"

NOTE: This answer doesn't fix the issue of closing the /* ... */ comment block with a single /, so @Matt's mapping is much better than this one in that sense.

Still refer to this answer for the ftplugin part (which @Matt also mentions) and the undo_ftplugin part.

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.