created 2007 · complexity intermediate · author A. S. Budden · version 7.0
When the preprocessor directives #if, #ifdef, #elif, #else and #endif are used extensively in source code, it can be extremely valuable to have comments on the #else and #endif lines to make the code clearer.
For example:
#ifdef DEBUG
/* Only execute this if debugging */
printf("Example debug output\n");
#endif /* def DEBUG */
With multiple levels of #ifs, this can simplify the code considerably (particularly with the addition of comments on the #else lines. The function below can be used to automate this process. It will also highlight (with a comment containing 'XXX') any inaccurate comments (for example a /* def DEBUG */ linked to a #ifndef DEBUG).
A more advanced version of this code, with a (slightly buggy) Brace Commenter as well, is available as a plugin from http://sites.google.com/site/abudden/Vim-Scripts/smart-brace--preprocessor-commenting
" Commenting of #endifs etc
" Author: Ben Schmidt, minor modifications by A. S. Budden.
command SmartPreProcCommenter call SmartPreProcCommenter()
function! SmartPreProcCommenter()
mark y
let saved_wrapscan=&wrapscan
set nowrapscan
let elsecomment=""
let endcomment=""
try
" Find the last #if in the buffer
$?^\s*#if
while 1
" Build the comments for later use, based on current line
let content=getline('.')
let elsecomment=BuildElseComment(content,elsecomment)
let endcomment=BuildEndComment(content,endcomment)
" Change # into ## so we know we've already processed this one
" and don't find it again
s/^\s*\zs#/##
" Find the next #else, #elif, #endif which must belong to this #if
/^\s*#\(elif\|else\|endif\)
let content=getline('.')
if match(content,'^\s*#elif') != -1
" For #elif, treat the same as #if, i.e. build new comments
continue
elseif match(content,'^\s*#else') != -1
" For #else, add/replace the comment
call setline('.',ReplaceComment(content,elsecomment))
s/^\s*\zs#/##
" Find the #endif
/^\s*#endif
endif
" We should be at the #endif now; add/replace the comment
call setline('.',ReplaceComment(getline('.'),endcomment))
s/^\s*\zs#/##
" Find the previous #if
?^\s*#if
endwhile
catch /search hit TOP/
" Once we have an error (pattern not found, i.e. no more left)
" Change all our ## markers back to #
silent! %s/^\s*\zs##/#
endtry
let &wrapscan=saved_wrapscan
normal `y
endfunc
let s:PreProcCommentMatcher = '#\a\+\s\+\zs.\{-}\ze\(\s*\/\*.\{-}\*\/\)\?\s*$'
function! BuildElseComment(content,previous)
let expression=escape(matchstr(a:content,s:PreProcCommentMatcher), '\~&')
if match(a:content,'#ifdef') != -1
return "/* NOT def ".expression." */"
elseif match(a:content,'#ifndef') != -1
return "/* def ".expression." */"
elseif match(a:content,'#if') != -1
return "/* NOT ".expression." */"
elseif match(a:content,'#elif') != -1
return substitute(a:previous,' \*/',', '.expression.' */','')
else
return ""
endif
endfunc
function! BuildEndComment(content,previous)
let expression=escape(matchstr(a:content,s:PreProcCommentMatcher), '\~&')
if match(a:content,'#ifdef') != -1
return "/* def ".expression." */"
elseif match(a:content,'#ifndef') != -1
return "/* NOT def ".expression." */"
elseif match(a:content,'#if') != -1
return "/* ".expression." */"
elseif match(a:content,'#elif') != -1
return substitute(a:previous,' \*/',', '.expression.' */','')
else
return ""
endif
endfunc
function! ReplaceComment(content,comment)
let existing=escape(matchstr(a:content,'#\a\+\s\+\zs.\{-}\s*$'), '\~&')
if existing == ""
return substitute(a:content,'^\s*#\a\+\zs.*'," ".a:comment,'')
elseif existing != a:comment && match(existing,'XXX') == -1
return a:content." /* XXX */"
else
return a:content
endif
endfunc