dotar/vim/autoload/tcomment.vim

577 lines
18 KiB
VimL
Raw Normal View History

2011-11-18 05:45:33 +08:00
" 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