I added something like this to my tex.vim ftplugin (I tried to simplify it because there are other plugins involved, but hopefully it makes sense):

set comments+=b:\\item

inoremap <expr> <cr> getline(".") =~ "\item $" ? "<c-w><c-w>" : "<cr>"

So when I type \item foo and tap <cr> another \item is automatically added in the next line, and then if I tap <cr> again without typing anything that last \item is deleted.

This works great and it got me thinking that it should be like that for all comments. A second (and third and fourth) <cr> when no text is entered should remove the last comment character instead of keeping adding comment lines forever. So I thought I'd write a function somewhat like this:

function CROrComment()
    for comment in comments
        if getline(".") =~ comment . " $"
            return "\<c-w>\<c-w>"
    return "\<cr>"

And then I'd use it to figure out if <cr> should do a <cr> or delete the last comment. This has many problems, though, some of which I can't figure out.

First of all when I use comments there it doesn't get the comment characters. I don't know how to get them to use them in the function.

Then I'd have to convert them into an array. I'd probably be able to figure that out by myself, though, for now I'm stuck at the first problem, but if anybody can help with that too it'll be even better.

And then there's the return "\<c-w>\<c-w>" part, which I used to delete \item, but it would be better if it adapted to the length of whatever the comment is.

I'm sorry if any of this is obvious, I just started playing around with Vim and I still don't know much about it.


1 Answer 1


I didn't test it very long, but you could try this:

setlocal formatoptions+=r
setlocal comments+=b:\\item

function! CROrComment()
    for comment in map(split(&l:comments, ','), 'substitute(v:val, "^.\\{-}:", "", "")')
        if getline('.') =~ '\V' . escape(comment, '\') . ' \$'
            return repeat("\<BS>", strchars(comment) + 1)
    return "\<cr>"

inoremap <buffer> <expr> <CR> CROrComment()

It doesn't take into account the possible flags, so it needs to be improved but it's a start.

If you write \item foo at the beginning of a line and hit enter, it should add a new line beginning with \item, and if you write \item (with a space at the end) and hit enter, it should just delete \item.

  • split(&l:comments, ',') returns a list containing the values inside the buffer-local value of the 'comments' option
  • map(split(...), 'substitute(...)') removes the flags inside each value of the list
  • if getline('.') =~ '\V' . escape(comment, '\') . ' \$' checks if the current line ends with a value from the list and a space (for the text to be compared literally, you have to add the very nomagic switch \V and escape the possible backslashes)
  • return repeat("\<BS>", strchars(comment) + 1) returns a sequence of backspaces to delete the comment and the space
  • Fantastic, this is exactly what I wanted, and great explanation, thank you!
    – dbmrq
    May 20, 2016 at 20:19

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.