2

I am trying to do a comment remap in Vim with an inline if to check if it's already commented or not. This is what I have already and of course it's not working ha ha:

imap <c-c> <Esc>^:if getline(".")[col(".")-1] == '/' i<Delete><Delete> else i// endif

What I want to do is check the first character if it's a / or not. If it's a / then delete the first two characters on that line, if it's not a / then add two // in front of the line.

What I had originally was this:

imap <c-c> <Esc>^i//

And that worked perfectly, but what I want is to be able to comment/uncomment at a whim.

ib.
  • 27,830
  • 11
  • 80
  • 100
jfreak53
  • 2,239
  • 7
  • 37
  • 53

3 Answers3

5

I completely agree with @Peter Rincker's answer warning against doing this in insert mode, and pointing you to fully-featured plugins.

However, I couldn't resist writing this function to do precisely what you ask for. I find it easier to deal with this kind of mapping with functions. As an added bonus, it returns you to insert mode in the same position on the line as you started (which has been shifted by inserting or deleting the characters).

function! ToggleComment()
    let pos=getpos(".")
    let win=winsaveview()
    if getline(".") =~ '\s*\/\/'
        normal! ^2x
        let pos[2]-=1
    else 
        normal! ^i//
        let pos[2]+=3
    endif
    call winrestview(win)
    call setpos(".",pos)
    startinsert
endfunction   

inoremap <c-c> <Esc>:call ToggleComment()<CR>

Notice the modifications to pos to ensure the cursor is returned to the correct column. The command startinsert is useful in this type of function to return to insert mode. It is always safer to use noremap for mappings, unless there is a very good reason not to.

This seems to work well, but it is not very Vim-like, and you might find other plugins more flexible in the long run.

Prince Goulash
  • 15,295
  • 2
  • 46
  • 47
  • WOW! Thank you SOOO MUCH! I can't get function's to work for me when I try to make any, so I just don't try anymore :( but that's awesome! – jfreak53 Aug 08 '12 at 17:57
  • You need to be careful here. If you toggle comments at the start or end of a line it will not restore the position correctly. You may also want to mention using `u` to break up the change into 2 separate undo blocks. – Peter Rincker Aug 08 '12 at 18:27
  • It doesn't really NEED to restore position, at least not for me, I don't see why I NEED to be careful. It does exactly what I need, comment and uncomment, simple and effective. – jfreak53 Aug 08 '12 at 19:12
  • 1
    If that is the case then this should be ideal: ``nnoremap c getline(".") =~ '^\s*\/\/' ? '^"_2x' : 'I//`['``. This creates a normal mode mapping to `c` which is typically translates to `\c` (see `:h mapleader`). It does not mask a useful insert mode mapping (see `:h i_ctrl-c`). It works nicely with undo history. It is much simpler and shorter. Note: this uses an expression mapping (see `:h map-`) so you must have a Vim 7+. – Peter Rincker Aug 08 '12 at 21:26
4

There are many commenting plugins for vim:

I would highly suggest you take a look at some these plugins first before you decide to roll your own. It will save you great effort.

As a side note you typically would want to comment/uncomment in normal mode not insert mode. This is not only the vim way, but will also provide a nicer undo history.

If you are dead set on creating your own mappings I suggest you create a function to do all the hard work and have your mapping call that function via :call. If you think you can get by with simple logic that doesn't need a function then you can use an expression mapping (see :h map-<expr>). You may want organize into a plugin as it could be large. If that is the case look at :h write-plugin to give you a feel for writing plugins the proper way.

Example of a simple expression mapping for toggling comments:

nnoremap <expr> <leader>c getline(".") =~ '\m^\s*\/\/' ? '^"_2x' : 'I//<esc>`['
Peter Rincker
  • 43,539
  • 9
  • 74
  • 101
  • Thanks, I tried the plugins and can't get any to work :( so since it's a simple comment for PHP only with // that's simple enough I figured :) – jfreak53 Aug 08 '12 at 17:55
  • 2
    You may want to verify you have a normal or huge version of vim via `:version`. Also follow the plugin directions closely. If you do want to use plugins I suggest pathogen: https://github.com/tpope/vim-pathogen Or Vundle: https://github.com/gmarik/vundle/ – Peter Rincker Aug 08 '12 at 18:40
1

there's also this vimtip! http://vim.wikia.com/wiki/Comment/UnComment_visually_selected_text

i use the bottom one with the

...
noremap <silent> ,c :<C-B>sil <C-E>s/^/<C-R>=escape(b:comment_leader,'\/')<CR>/<CR>:noh<CR>
noremap <silent> ,u :<C-B>sil <C-E>s/^\V<C-R>=escape(b:comment_leader,'\/')<CR>//e<CR>:noh<CR>

,c comments out a region
,u uncomments a region
David Lam
  • 4,689
  • 3
  • 23
  • 34