dotar/vim/autoload/tcomment.vim
2011-11-17 16:00:49 -06:00

577 lines
18 KiB
VimL
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

" tcomment.vim
" @Author: Tom Link (mailto:micathom AT gmail com?subject=[vim])
" @Website: http://www.vim.org/account/profile.php?user_id=4037
" @License: GPL (see http://www.gnu.org/licenses/gpl.txt)
" @Created: 2007-09-17.
" @Last Change: 2009-02-15.
" @Revision: 0.0.66
if &cp || exists("loaded_tcomment_autoload")
finish
endif
let loaded_tcomment_autoload = 1
function! s:DefaultValue(option)
exec 'let '. a:option .' = &'. a:option
exec 'set '. a:option .'&'
exec 'let default = &'. a:option
exec 'let &'. a:option .' = '. a:option
return default
endf
let s:defaultComments = s:DefaultValue('comments')
let s:defaultCommentString = s:DefaultValue('commentstring')
let s:nullCommentString = '%s'
" tcomment#Comment(line1, line2, ?commentMode, ?commentAnyway, ?commentBegin, ?commentEnd)
" commentMode:
" G ... guess
" B ... block
" i ... maybe inline, guess
" I ... inline
" R ... right
" v ... visual
" o ... operator
function! tcomment#Comment(beg, end, ...)
" save the cursor position
let co = col('.')
let li = line('.')
let s:pos_end = getpos("'>")
let commentMode = a:0 >= 1 ? a:1 : 'G'
let commentAnyway = a:0 >= 2 ? (a:2 == '!') : 0
" TLogVAR a:beg, a:end, a:1, commentMode, commentAnyway
if commentMode =~# 'i'
let commentMode = substitute(commentMode, '\Ci', line("'<") == line("'>") ? 'I' : 'G', 'g')
endif
if commentMode =~# 'R' || commentMode =~# 'I'
let cstart = col("'<")
if cstart == 0
let cstart = col('.')
endif
if commentMode =~# 'R'
let commentMode = substitute(commentMode, '\CR', 'G', 'g')
let cend = 0
else
let cend = col("'>")
if commentMode =~# 'o'
let cend += 1
endif
endif
else
let cstart = 0
let cend = 0
endif
" TLogVAR commentMode, cstart, cend
" get the correct commentstring
if a:0 >= 3 && a:3 != ''
let cms = s:EncodeCommentPart(a:3) .'%s'
if a:0 >= 4 && a:4 != ''
let cms = cms . s:EncodeCommentPart(a:4)
endif
else
let [cms, commentMode] = s:GetCommentString(a:beg, a:end, commentMode)
endif
let cms0 = s:BlockGetCommentString(cms)
let cms0 = escape(cms0, '\')
" make whitespace optional; this conflicts with comments that require some
" whitespace
let cmtCheck = substitute(cms0, '\([ ]\)', '\1\\?', 'g')
" turn commentstring into a search pattern
let cmtCheck = s:SPrintF(cmtCheck, '\(\_.\{-}\)')
" set commentMode and indentStr
let [indentStr, uncomment] = s:CommentDef(a:beg, a:end, cmtCheck, commentMode, cstart, cend)
" TLogVAR indentStr, uncomment
if commentAnyway
let uncomment = 0
endif
" go
if commentMode =~# 'B'
" We want a comment block
call s:CommentBlock(a:beg, a:end, uncomment, cmtCheck, cms, indentStr)
else
" call s:CommentLines(a:beg, a:end, cstart, cend, uncomment, cmtCheck, cms0, indentStr)
" We want commented lines
" final search pattern for uncommenting
let cmtCheck = escape('\V\^\(\s\{-}\)'. cmtCheck .'\$', '"/\')
" final pattern for commenting
let cmtReplace = escape(cms0, '"/')
silent exec a:beg .','. a:end .'s/\V'.
\ s:StartRx(cstart) . indentStr .'\zs\(\.\{-}\)'. s:EndRx(cend) .'/'.
\ '\=s:ProcessedLine('. uncomment .', submatch(0), "'. cmtCheck .'", "'. cmtReplace .'")/ge'
endif
" reposition cursor
" TLogVAR commentMode
if commentMode =~ '>'
call setpos('.', s:pos_end)
else
" TLogVAR li, co
call cursor(li, co)
endif
endf
function! tcomment#Operator(type, ...) "{{{3
let commentMode = a:0 >= 1 ? a:1 : ''
let bang = a:0 >= 2 ? a:2 : ''
if !exists('w:tcommentPos')
let w:tcommentPos = getpos(".")
endif
let sel_save = &selection
let &selection = "inclusive"
let reg_save = @@
" let pos = getpos('.')
" TLogVAR a:type
try
if a:type == 'line'
silent exe "normal! '[V']"
let commentMode1 = 'G'
elseif a:type == 'block'
silent exe "normal! `[\<C-V>`]"
let commentMode1 = 'I'
else
silent exe "normal! `[v`]"
let commentMode1 = 'i'
endif
if empty(commentMode)
let commentMode = commentMode1
endif
let beg = line("'[")
let end = line("']")
norm! 
let commentMode .= g:tcommentOpModeExtra
call tcomment#Comment(beg, end, commentMode.'o', bang)
finally
let &selection = sel_save
let @@ = reg_save
if g:tcommentOpModeExtra !~ '>'
" TLogVAR pos
" call setpos('.', pos)
call setpos('.', w:tcommentPos)
unlet! w:tcommentPos
endif
endtry
endf
function! tcomment#OperatorLine(type) "{{{3
call tcomment#Operator(a:type, 'G')
endf
function! tcomment#OperatorAnyway(type) "{{{3
call tcomment#Operator(a:type, '', '!')
endf
function! tcomment#OperatorLineAnyway(type) "{{{3
call tcomment#Operator(a:type, 'G', '!')
endf
" comment text as if it were of a specific filetype
function! tcomment#CommentAs(beg, end, commentAnyway, filetype, ...)
let ccount = a:0 >= 1 ? a:1 : 1
" TLogVAR ccount
if a:filetype =~ '_block$'
let commentMode = 'B'
let ft = substitute(a:filetype, '_block$', '', '')
elseif a:filetype =~ '_inline$'
let commentMode = 'I'
let ft = substitute(a:filetype, '_inline$', '', '')
else
let commentMode = 'G'
let ft = a:filetype
endif
let [cms, commentMode] = s:GetCommentString(a:beg, a:end, commentMode, ft)
let pre = substitute(cms, '%s.*$', '', '')
let pre = substitute(pre, '%%', '%', 'g')
let post = substitute(cms, '^.\{-}%s', '', '')
let post = substitute(post, '%%', '%', 'g')
if ccount > 1
let pre_l = matchlist(pre, '^\(\S\+\)\(.*\)$')
" TLogVAR pre_l
if !empty(get(pre_l, 1))
let pre = repeat(pre_l[1], ccount) . pre_l[2]
endif
let post_l = matchlist(post, '^\(\s*\)\(.\+\)$')
" TLogVAR post_l
if !empty(get(post_l, 2))
let post = post_l[1] . repeat(post_l[2], ccount)
endif
endif
keepjumps call tcomment#Comment(a:beg, a:end, commentMode, a:commentAnyway, pre, post)
endf
" ----------------------------------------------------------------
" collect all variables matching ^tcomment_
function! tcomment#CollectFileTypes()
if g:tcommentFileTypesDirty
redir => vars
silent let
redir END
let g:tcommentFileTypes = split(vars, '\n')
call filter(g:tcommentFileTypes, 'v:val =~ "tcomment_"')
call map(g:tcommentFileTypes, 'matchstr(v:val, ''tcomment_\zs\S\+'')')
call sort(g:tcommentFileTypes)
let g:tcommentFileTypesRx = '\V\^\('. join(g:tcommentFileTypes, '\|') .'\)\(\u\.\*\)\?\$'
let g:tcommentFileTypesDirty = 0
endif
endf
call tcomment#CollectFileTypes()
" return a list of filetypes for which a tcomment_{&ft} is defined
function! tcomment#FileTypes(ArgLead, CmdLine, CursorPos)
" TLogVAR a:ArgLead, a:CmdLine, a:CursorPos
call tcomment#CollectFileTypes()
let types = copy(g:tcommentFileTypes)
if index(g:tcommentFileTypes, &filetype) != -1
" TLogVAR &filetype
call insert(types, &filetype)
endif
if empty(a:ArgLead)
return types
else
return filter(types, 'v:val =~ ''\V''.a:ArgLead')
endif
endf
function! s:EncodeCommentPart(string)
return substitute(a:string, '%', '%%', 'g')
endf
" s:GetCommentString(beg, end, commentMode, ?filetype="")
function! s:GetCommentString(beg, end, commentMode, ...)
let ft = a:0 >= 1 ? a:1 : ''
if ft != ''
let [cms, commentMode] = s:GetCustomCommentString(ft, a:commentMode)
else
let cms = ''
let commentMode = a:commentMode
endif
if empty(cms)
if exists('b:commentstring')
let cms = b:commentstring
return s:GetCustomCommentString(&filetype, a:commentMode, cms)
elseif exists('b:commentStart') && b:commentStart != ''
let cms = s:EncodeCommentPart(b:commentStart) .' %s'
if exists('b:commentEnd') && b:commentEnd != ''
let cms = cms .' '. s:EncodeCommentPart(b:commentEnd)
endif
return s:GetCustomCommentString(&filetype, a:commentMode, cms)
elseif g:tcommentGuessFileType || (exists('g:tcommentGuessFileType_'. &filetype)
\ && g:tcommentGuessFileType_{&filetype} =~ '[^0]')
if g:tcommentGuessFileType_{&filetype} == 1
let altFiletype = ''
else
let altFiletype = g:tcommentGuessFileType_{&filetype}
endif
return s:GuessFileType(a:beg, a:end, a:commentMode, &filetype, altFiletype)
else
return s:GetCustomCommentString(&filetype, a:commentMode, s:GuessCurrentCommentString(a:commentMode))
endif
endif
return [cms, commentMode]
endf
" s:SPrintF(formatstring, ?values ...)
" => string
function! s:SPrintF(string, ...)
let n = 1
let r = ''
let s = a:string
while 1
let i = match(s, '%\(.\)')
if i >= 0
let x = s[i + 1]
let r = r . strpart(s, 0, i)
let s = strpart(s, i + 2)
if x == '%'
let r = r.'%'
else
if a:0 >= n
let v = a:{n}
let n = n + 1
else
echoerr 'Malformed format string (too many arguments required): '. a:string
endif
if x ==# 's'
let r = r.v
elseif x ==# 'S'
let r = r.'"'.v.'"'
else
echoerr 'Malformed format string: '. a:string
endif
endif
else
return r.s
endif
endwh
endf
function! s:StartRx(pos)
if a:pos == 0
return '\^'
else
return '\%'. a:pos .'c'
endif
endf
function! s:EndRx(pos)
if a:pos == 0
return '\$'
else
return '\%'. a:pos .'c'
endif
endf
function! s:GetIndentString(line, start)
let start = a:start > 0 ? a:start - 1 : 0
return substitute(strpart(getline(a:line), start), '\V\^\s\*\zs\.\*\$', '', '')
endf
function! s:CommentDef(beg, end, checkRx, commentMode, cstart, cend)
let mdrx = '\V'. s:StartRx(a:cstart) .'\s\*'. a:checkRx .'\s\*'. s:EndRx(0)
let line = getline(a:beg)
if a:cstart != 0 && a:cend != 0
let line = strpart(line, 0, a:cend - 1)
endif
let uncomment = (line =~ mdrx)
let it = s:GetIndentString(a:beg, a:cstart)
let il = indent(a:beg)
let n = a:beg + 1
while n <= a:end
if getline(n) =~ '\S'
let jl = indent(n)
if jl < il
let it = s:GetIndentString(n, a:cstart)
let il = jl
endif
if a:commentMode =~# 'G'
if !(getline(n) =~ mdrx)
let uncomment = 0
endif
endif
endif
let n = n + 1
endwh
if a:commentMode =~# 'B'
let t = @t
try
silent exec 'norm! '. a:beg.'G1|v'.a:end.'G$"ty'
let uncomment = (@t =~ mdrx)
finally
let @t = t
endtry
endif
return [it, uncomment]
endf
function! s:ProcessedLine(uncomment, match, checkRx, replace)
if !(a:match =~ '\S' || g:tcommentBlankLines)
return a:match
endif
let ml = len(a:match)
if a:uncomment
let rv = substitute(a:match, a:checkRx, '\1\2', '')
else
let rv = s:SPrintF(a:replace, a:match)
endif
" let md = len(rv) - ml
let s:pos_end = getpos('.')
let s:pos_end[2] += len(rv)
" TLogVAR pe, md, a:match
let rv = escape(rv, '\
')
let rv = substitute(rv, '\n', '\\\n', 'g')
return rv
endf
function! s:CommentLines(beg, end, cstart, cend, uncomment, cmtCheck, cms0, indentStr) "{{{3
" We want commented lines
" final search pattern for uncommenting
let cmtCheck = escape('\V\^\(\s\{-}\)'. a:cmtCheck .'\$', '"/\')
" final pattern for commenting
let cmtReplace = escape(a:cms0, '"/')
silent exec a:beg .','. a:end .'s/\V'.
\ s:StartRx(a:cstart) . a:indentStr .'\zs\(\.\{-}\)'. s:EndRx(a:cend) .'/'.
\ '\=s:ProcessedLine('. a:uncomment .', submatch(0), "'. a:cmtCheck .'", "'. cmtReplace .'")/ge'
endf
function! s:CommentBlock(beg, end, uncomment, checkRx, replace, indentStr)
let t = @t
try
silent exec 'norm! '. a:beg.'G1|v'.a:end.'G$"td'
let ms = s:BlockGetMiddleString(a:replace)
let mx = escape(ms, '\')
if a:uncomment
let @t = substitute(@t, '\V\^\s\*'. a:checkRx .'\$', '\1', '')
if ms != ''
let @t = substitute(@t, '\V\n'. a:indentStr . mx, '\n'. a:indentStr, 'g')
endif
let @t = substitute(@t, '^\n', '', '')
let @t = substitute(@t, '\n\s*$', '', '')
else
let cs = s:BlockGetCommentString(a:replace)
let cs = a:indentStr . substitute(cs, '%s', '%s'. a:indentStr, '')
if ms != ''
let ms = a:indentStr . ms
let mx = a:indentStr . mx
let @t = substitute(@t, '^'. a:indentStr, '', 'g')
let @t = ms . substitute(@t, '\n'. a:indentStr, '\n'. mx, 'g')
endif
let @t = s:SPrintF(cs, "\n". @t ."\n")
endif
silent norm! "tP
finally
let @t = t
endtry
endf
" inspired by Meikel Brandmeyer's EnhancedCommentify.vim
" this requires that a syntax names are prefixed by the filetype name
" s:GuessFileType(beg, end, commentMode, filetype, ?fallbackFiletype)
function! s:GuessFileType(beg, end, commentMode, filetype, ...)
if a:0 >= 1 && a:1 != ''
let [cms, commentMode] = s:GetCustomCommentString(a:1, a:commentMode)
if cms == ''
let cms = s:GuessCurrentCommentString(a:commentMode)
endif
else
let commentMode = s:CommentMode(a:commentMode, 'G')
let cms = s:GuessCurrentCommentString(0)
endif
let n = a:beg
" TLogVAR n, a:beg, a:end
while n <= a:end
let m = indent(n) + 1
let le = len(getline(n))
" TLogVAR m, le
while m < le
let syntaxName = synIDattr(synID(n, m, 1), 'name')
" TLogVAR syntaxName, n, m
let ftypeMap = get(g:tcommentSyntaxMap, syntaxName)
if !empty(ftypeMap)
" TLogVAR ftypeMap
return s:GetCustomCommentString(ftypeMap, a:commentMode, cms)
elseif syntaxName =~ g:tcommentFileTypesRx
let ft = substitute(syntaxName, g:tcommentFileTypesRx, '\1', '')
" TLogVAR ft
if exists('g:tcommentIgnoreTypes_'. a:filetype) && g:tcommentIgnoreTypes_{a:filetype} =~ '\<'.ft.'\>'
let m += 1
else
return s:GetCustomCommentString(ft, a:commentMode, cms)
endif
elseif syntaxName == '' || syntaxName == 'None' || syntaxName =~ '^\u\+$' || syntaxName =~ '^\u\U*$'
let m += 1
else
break
endif
endwh
let n += 1
endwh
return [cms, commentMode]
endf
function! s:CommentMode(commentMode, newmode) "{{{3
return substitute(a:commentMode, '\w\+', a:newmode, 'g')
endf
function! s:GuessCurrentCommentString(commentMode)
let valid_cms = (stridx(&commentstring, '%s') != -1)
if &commentstring != s:defaultCommentString && valid_cms
" The &commentstring appears to have been set and to be valid
return &commentstring
endif
if &comments != s:defaultComments
" the commentstring is the default one, so we assume that it wasn't
" explicitly set; we then try to reconstruct &cms from &comments
let cms = s:ConstructFromComments(a:commentMode)
if cms != s:nullCommentString
return cms
endif
endif
if valid_cms
" Before &commentstring appeared not to be set. As we don't know
" better we return it anyway if it is valid
return &commentstring
else
" &commentstring is invalid. So we return the identity string.
return s:nullCommentString
endif
endf
function! s:ConstructFromComments(commentMode)
exec s:ExtractCommentsPart('')
if a:commentMode =~# 'G' && line != ''
return line .' %s'
endif
exec s:ExtractCommentsPart('s')
if s != ''
exec s:ExtractCommentsPart('e')
" if a:commentMode
" exec s:ExtractCommentsPart("m")
" if m != ""
" let m = "\n". m
" endif
" return s.'%s'.e.m
" else
return s.' %s '.e
" endif
endif
if line != ''
return line .' %s'
else
return s:nullCommentString
endif
endf
function! s:ExtractCommentsPart(key)
" let key = a:key != "" ? a:key .'[^:]*' : ""
let key = a:key . '[bnflrxO0-9-]*'
let val = substitute(&comments, '^\(.\{-},\)\{-}'. key .':\([^,]\+\).*$', '\2', '')
if val == &comments
let val = ''
else
let val = substitute(val, '%', '%%', 'g')
endif
let var = a:key == '' ? 'line' : a:key
return 'let '. var .'="'. escape(val, '"') .'"'
endf
" s:GetCustomCommentString(ft, commentMode, ?default="")
function! s:GetCustomCommentString(ft, commentMode, ...)
let commentMode = a:commentMode
let customComment = exists('g:tcomment_'. a:ft)
if commentMode =~# 'B' && exists('g:tcomment_'. a:ft .'_block')
let cms = g:tcomment_{a:ft}_block
elseif commentMode =~? 'I' && exists('g:tcomment_'. a:ft .'_inline')
let cms = g:tcomment_{a:ft}_inline
elseif customComment
let cms = g:tcomment_{a:ft}
let commentMode = s:CommentMode(commentMode, 'G')
elseif a:0 >= 1
let cms = a:1
let commentMode = s:CommentMode(commentMode, 'G')
else
let cms = ''
let commentMode = s:CommentMode(commentMode, 'G')
endif
return [cms, commentMode]
endf
function! s:BlockGetCommentString(cms)
" return substitute(a:cms, '\n.*$', '', '')
return matchstr(a:cms, '^.\{-}\ze\(\n\|$\)')
endf
function! s:BlockGetMiddleString(cms)
" let rv = substitute(a:cms, '^.\{-}\n\([^\n]*\)', '\1', '')
let rv = matchstr(a:cms, '\n\zs.*')
return rv == a:cms ? '' : rv
endf
redraw