This is a pretty common question by beginners: how do I comment out some stuff? And a pretty common answer is to use Visual-Block mode or substitute command. There's even a wiki page to explain this in detail.
But then the novice keeps asking more and more: How do I uncomment? How do I comment with different filetypes? How do I... And then we come again to the usual "go get and install another 10000+ lines plugin named xxx or yyy". But is it really worth one?
So let's put a problem: (1) write a (very!) small command/function able both to comment and uncomment arbitrary line range; (2) an action to be taken depends on the first non-blank line; (3) comment format follows :h 'commentstring'
option to allow for easy customization; (4) blank lines are not to be changed; (5) there are two comment styles supported - with indentation (....//foo
) and without one (//....foo
).
So my best attempt is:
" ToggleComment({line1}, {line2}, {preserveindent})
function! ToggleComment(line1, line2, pi) abort
let l:lnum = nextnonblank(a:line1)
let l:end = prevnonblank(a:line2)
if l:lnum >= 1 && l:lnum <= l:end
let l:pat = printf('^\(\s*\)\(%s\)$', printf(escape(&cms, '^$.*~[]\'), '\(.*\)'))
let l:sub = '\=empty(submatch(2)) ? submatch(0) : submatch(1)..submatch(3)'
if getline(l:lnum) !~# l:pat
let l:pat = a:pi ? '^\s*\zs.*' : '.*'
let l:sub = printf(escape(&cms, '&\'), '&')
endif
call setline(l:lnum, map(getline(l:lnum, l:end), {_, v -> empty(v) ? v :
\ substitute(v, l:pat, l:sub, '')}))
endif
endfunction
" :[range]Comment[!]
command! -range -bar -bang Comment call ToggleComment(<line1>, <line2>, <bang>0)
Pretty compact, isn't it?