First, find a pattern that selects the right lines. If you have :set hls
, it will help spot the matches. I think something like /#.*\/\/$
is what you want.
Next, comment out the selected lines with
:g/<pattern>/s/^/# /
if #
will comment out the line, and un-comment them with
:g/<pattern>/s/^# //
Now, you want a single command to toggle. You can either use a variable to keep track of the toggle state, or you can try to figure out the current state by examining the lines that match. Using a variable seems simpler.
The variable could be global, local to the buffer, or local to the script. I like using script-local variables in order to avoid cluttering the namespace. In this case, using a script-local variable might mean that vim will get confused when you switch buffers, so let's use a buffer-local variable, say b:commentToggle
.
The first time the function is called, it notices that the variable is not set, so use search()
to look for a line that starts with #
(There is a space there!) and ends with the pattern we already have. The n
flag means not to move the cursor, and w
means to wrap around the end of the file (like searching with 'wrapscan'
set). The search()
function returns the line number (1-based!) if the pattern is found, 0 if not. See :help search()
.
This seems to work in a small test:
fun! CommentToggle()
if !exists('b:commentToggle')
let b:commentToggle = !search('^# .*#.*\/\/$', 'nw')
endif
if b:commentToggle == 1
g/#.*\/\/$/s/^/# /
else
g/#.*\/\/$/s/^# //e
endif
let b:commentToggle = !b:commentToggle
endfun
nnoremap <F4> :call CommentToggle()<CR>
If you want to put #
in front of the first non-blank, then use ^\s*#
in the search()
command; s/\ze\S/# /
or s/\S/\1# /
in the first :g
line; and s/^\s*\zs# //
in the second :g
line. See :help /\zs
, :help /\ze
, and :help sub-replace-special
.