5

I want to make block commenting easy. To comment out a range of lines, what I can do is something like: :17,21s/^/# This will comment lines 17 to 21 in Python. But I don't want to write the whole string search and replacement pattern all the time.

Initially I thought of making a parameterized mapping for this, where I could pass the line numbers as parameters to string search and replacement map but then realised that mappings cannot have parameters.

Then I got this function which will comment a range and the comment character can be passed to it as well (like # in Python and // in JavaScript):

command! -range -nargs=? Co call CommentThis(<line1>, <line2>, <q-args>)
function! CommentThis(l1, l2, lead)
    let l:lead = a:lead == '' ? '#' : a:lead
    exe printf('%i,%is+^+%s', a:l1, a:l2, l:lead)
endf

Now doing something like :17,21Co will achieve the task and I can do :17,21Co// (for adding // instead of default #)

My question is: How can I modify this function so as to make it work as a comment toggle? Or add a different command line to it so that I can use command 'Co' for commenting and 'Cu' to uncomment? A search and replace pattern to uncomment a python comment would be :17,21s/^#/.

PS: The reason I don't want to use NERD Commentor is that it doesn't support ranges, it just supports count, where I have to either take the cursor to the line I want to comment or select it. Another plugin I found is Commentary VIM but its command :17,21Commentary is too long for me.

Glorfindel
  • 211
  • 1
  • 3
  • 11
MohitC
  • 255
  • 2
  • 7

3 Answers3

1

I have written a custom function for commenting in COBOL, you can use something similar for Python.

function! Comment(currmode) range
    let origpos=getpos('.')
    if a:currmode=="visual"
       if mode()=="v"
           let [linestart, colstart] = getpos("v")[1:2]
           let [lineend, colend] = getpos(".")[1:2]
       else
           let [linestart, colstart] = getpos("'<")[1:2]
           let [lineend, colend] = getpos("'>")[1:2]
       end
       if (line2byte(linestart)+colstart) > (line2byte(lineend)+colend)
           let [linestart, colstart, lineend, colend] =
           \   [lineend, colend, linestart, colstart]
       end
       let comcount=lineend - linestart + 1
       call setpos('.',[0,linestart,1])
    else
       let comcount=v:count1
       let linestart=line('.')
    endif
    if strpart(getline(linestart),6,1) == '*'
       let comment=0
       let label='uncomment'
    else
       let comment=1
       let label='comment'
    endif
    call inputsave()
    let tag = input('Tag for '.label.': ')
    call inputrestore()
    let tag=strpart(Pad(tag,6),0,6)
    let i=1
    while i <= comcount
       if !comment
          let newline=substitute(getline('.'),'^.......',tag.' ','')
          call setline('.',newline)
       else
          let newline=substitute(getline('.'),'^.......',tag.'*','')
          call setline('.',newline)
       endif
       let i = i+1
       call setpos('.',[0,linestart+i-1,1])
    endwhile
    call setpos('.',origpos)
endfunction

And add the following mapping

nmap <silent>cp   :<C-U>call Comment('normal')<CR>
vmap <silent>cp   :<C-U>call Comment('visual')<CR>

COBOL commenting is very specific - comment is * in the 7th column of the line. With this code I can comment code using - a range, visual block, single line and add custom Tag (1st 6 chars of the line) using input at run-time (you probably won't need this).

Ex. If I want to comment 5 lines starting from current line I can-

a. 5cp in normal mode b. select 5 lines using visual block (V-LINE or V-BLOCK) and hit cp

Similarly for un-commenting.

The function basically computes the first and last line of the range. For visual mode I use getpos and for normal mode I use v:count1. Once I get the range, I run the comment/uncomment code in loop to insert the tag and an * in the 7th column.

Hope this is helpful.

Ankit Jain
  • 780
  • 1
  • 6
  • 14
1

Now doing something like :17,21Co will achieve the task and I can do :17,21Co// (for adding // instead of default #)

tpope's commentary does this, just tab-expand the command into :17,21Commentary. It'll autodetect filetypes so you don't have to worry about passing # or //. It wont comment blank lines, although I'm not sure why that's necessary?

fruglemonkey
  • 1,530
  • 13
  • 12
0

I used to do it manually until I found Commentary Master which I now used.

I was in the process of writing a function but had got to use some mappings or a macro that I loaded when the ft was right

For example I used for something like the following, assuming ft=txt but using a sql format comment

  This a a non comment line
 This is  
 So's this  
 This isn't
 This is  

for example recording a macro in w i eqw which goes to the beginning of th current line inserts a /* then adds to the end of the line an */ would look like this 0i/* ^[A */^[ and when you @w on each line does this

This a a non comment line
/* This is   */
/* So's this   */
This isn't
/* This is   */

However for me it was easier to set ft specific registers eg autocmd FileType txt let @b='<!--' which a C-Rb in insert mode put the /* and a matching command mapping to @e would put the close comment.

I had a matching command that removed them in one go, which I've deleted. :(

I prefer the plugin now as I didn't have time to write a function properly

Steve
  • 276
  • 1
  • 13