Reputation: 3331
Partly for practice, and partly for personal use, I would like to be able to enter visual mode, select some lines, and then hit "," which would then toggle commenting. (I know NerdCommenter exists but I want something simple with nothing fancy - and again, this is practice, too.)
I've learned that you can do '&filetype' to access the filetype, and that '.' concatenates strings, ==# is case-sensitive string comparison, and =~ is regex matching. I also learned that getline('.') gets the line that visual mode has highlighted (each line if multiple lines are highlighted).
Here's my (flawed)
vnoremap , :call ToggleComment()<CR>
function! ToggleComment()
"Choose comment string based on filetype.
let comment_string = ''
if &filetype ==# 'vim'
let comment_string = '"'
elseif &filetype ==# 'cpp'
let comment_string = '\/\/'
endif
let line = getline('.')
if line =~ '^' . comment_string
"Comment.
" How could I do the equivalent of "shift-I to go to the beginning of the
" line, then enter comment_string"?
else
"Uncomment. This is flawed too. Maybe I should just go to the beginning of
"the line and delete a number of characters over?
execute 's/^' . comment_string . '//'
endif
endfunction
One thing I get for the uncomment case, regardless of whether the line is commented or not, is
Pattern not found: ^"
(I tested on my vimrc file.)
Advice appreciated - I feel like this shouldn't be too complicated.
Upvotes: 0
Views: 184
Reputation: 36272
You can use the range
option after function declatarion, that lets you use two variables that contain the beginning and end of the range, a:firstline
and a:lastline
.
Inside an execute
instruction prepend them to the substitution command to executes it only in that range. At the time of removing apply escape()
to the variable to avoid collision of forward slashes:
function! ToggleComment() range
let comment_string = ''
if &filetype ==# 'vim'
let comment_string = '"'
elseif &filetype ==# 'cpp'
let comment_string = '//'
endif
let line = getline('.')
if line =~ '^' . comment_string
execute a:firstline . "," . a:lastline . 's/^' . escape(comment_string, '/') . '//'
else
execute a:firstline . "," . a:lastline . 's/^/\=printf( "%s", comment_string )/'
endif
endfunction
UPDATE: To add and remove comments just before first non-blank character, you have to add optional spaces after the zero-width assertion of beginning of line. This is the part that changes. Note how I add \s*
to comparison if it exists comments in that line and add \1
or submatch(1)
in the replacement part of substitutions:
if line =~? '^\s*' . comment_string
execute a:firstline . "," . a:lastline . 's/^\(\s*\)' . escape(comment_string, '/') . '/\1/'
else
execute a:firstline . "," . a:lastline . 's/^\(\s*\)/\=submatch(1) . printf( "%s", comment_string )/'
endif
Upvotes: 2
Reputation: 8248
This probably fails, because your :s
command doesn't find the comment string. You should probably use the e
flag to your :s
command (See also :h s_flags
).
Additionally, I think you want to add a variable number of spaces between the line start and the comment string, e.g. something like if line =~ '^\s*'.comment
so it does correctly catch:
#This is a comment
#This is a comment
etc, you'll get the idea.
Upvotes: 0