diff options
author | Silvio Rhatto <rhatto@riseup.net> | 2013-02-01 01:25:10 -0200 |
---|---|---|
committer | Silvio Rhatto <rhatto@riseup.net> | 2013-02-01 01:25:10 -0200 |
commit | 6b9187673ee62d11042832f938913f15131592a7 (patch) | |
tree | 9e70016667efe273a72ab351aafaddbdb099e123 /modules/vim/vim.dot.link/plugin | |
parent | 25235935e6c90557a4d6791c8d8f3a5cdf89681a (diff) | |
download | dotfiles-6b9187673ee62d11042832f938913f15131592a7.tar.gz dotfiles-6b9187673ee62d11042832f938913f15131592a7.tar.bz2 |
Adding metadot script
Diffstat (limited to 'modules/vim/vim.dot.link/plugin')
-rw-r--r-- | modules/vim/vim.dot.link/plugin/Rename.vim | 27 | ||||
-rw-r--r-- | modules/vim/vim.dot.link/plugin/bufexplorer.vim | 1157 | ||||
-rw-r--r-- | modules/vim/vim.dot.link/plugin/git.vim | 372 | ||||
-rw-r--r-- | modules/vim/vim.dot.link/plugin/indexer.vim.disabled | 672 | ||||
-rw-r--r-- | modules/vim/vim.dot.link/plugin/project.vim | 1293 | ||||
-rw-r--r-- | modules/vim/vim.dot.link/plugin/snipMate.vim | 247 | ||||
-rw-r--r-- | modules/vim/vim.dot.link/plugin/startshell_mapping.vim.disabled | 36 | ||||
-rw-r--r-- | modules/vim/vim.dot.link/plugin/taglist.vim | 4546 | ||||
-rw-r--r-- | modules/vim/vim.dot.link/plugin/vcsbzr.vim | 262 | ||||
-rw-r--r-- | modules/vim/vim.dot.link/plugin/vcscommand.vim | 1427 | ||||
-rw-r--r-- | modules/vim/vim.dot.link/plugin/vcscvs.vim | 449 | ||||
-rw-r--r-- | modules/vim/vim.dot.link/plugin/vcsgit.vim | 247 | ||||
-rw-r--r-- | modules/vim/vim.dot.link/plugin/vcshg.vim | 273 | ||||
-rw-r--r-- | modules/vim/vim.dot.link/plugin/vcssvk.vim | 257 | ||||
-rw-r--r-- | modules/vim/vim.dot.link/plugin/vcssvn.vim | 284 |
15 files changed, 11549 insertions, 0 deletions
diff --git a/modules/vim/vim.dot.link/plugin/Rename.vim b/modules/vim/vim.dot.link/plugin/Rename.vim new file mode 100644 index 0000000..b56c516 --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/Rename.vim @@ -0,0 +1,27 @@ +" Rename.vim - Rename a buffer within Vim and on the disk +" +" Copyright June 2007 by Christian J. Robinson <infynity@onewest.net> +" +" Distributed under the terms of the Vim license. See ":help license". +" +" Usage: +" +" :Rename[!] {newname} + +command! -nargs=* -complete=file -bang Rename :call Rename("<args>", "<bang>") + +function! Rename(name, bang) + let l:curfile = expand("%:p") + let v:errmsg = "" + silent! exe "saveas" . a:bang . " " . a:name + if v:errmsg =~# '^$\|^E329' + if expand("%:p") !=# l:curfile && filewritable(expand("%:p")) + silent exe "bwipe! " . l:curfile + if delete(l:curfile) + echoerr "Could not delete " . l:curfile + endif + endif + else + echoerr v:errmsg + endif +endfunction diff --git a/modules/vim/vim.dot.link/plugin/bufexplorer.vim b/modules/vim/vim.dot.link/plugin/bufexplorer.vim new file mode 100644 index 0000000..de957d8 --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/bufexplorer.vim @@ -0,0 +1,1157 @@ +"============================================================================= +" Copyright: Copyright (C) 2001-2012 Jeff Lanzarotta +" Permission is hereby granted to use and distribute this code, +" with or without modifications, provided that this copyright +" notice is copied with it. Like anything else that's free, +" bufexplorer.vim is provided *as is* and comes with no +" warranty of any kind, either expressed or implied. In no +" event will the copyright holder be liable for any damages +" resulting from the use of this software. +" Name Of File: bufexplorer.vim +" Description: Buffer Explorer Vim Plugin +" Maintainer: Jeff Lanzarotta (delux256-vim at yahoo dot com) +" Last Changed: Sunday, 23 Dec 2012 +" Version: See g:bufexplorer_version for version number. +" Usage: This file should reside in the plugin directory and be +" automatically sourced. +" +" You may use the default keymappings of +" +" <Leader>be - Opens BufExplorer +" <Leader>bs - Opens horizontally split window BufExplorer +" <Leader>bv - Opens vertically split window BufExplorer +" +" Or you can override the defaults and define your own mapping +" in your vimrc file, for example: +" +" noremap <silent> <F11> :BufExplorer<CR> +" noremap <silent> <m-F11> :BufExplorerHorizontalSplit<CR> +" noremap <silent> <c-F11> :BufExplorerVerticalSplit<CR> +" +" Or you can use +" +" ":BufExplorer" - Opens BufExplorer +" ":BufExplorerHorizontalSplit" - Opens horizontally window BufExplorer +" ":BufExplorerVerticalSplit" - Opens vertically split window BufExplorer +" +" For more help see supplied documentation. +" History: See supplied documentation. +" Issues: 'D' and 'd' remove the buffer from the list but the list is not +" displayed correctly. +" - Add ability to open a buffer in a new split when \be is used. +"============================================================================= + +" Plugin Code {{{1 +" Exit quickly if already running or when 'compatible' is set. {{{2 +if exists("g:bufexplorer_version") || &cp + finish +endif +"2}}} + +" Version number +let g:bufexplorer_version = "7.3.2" + +" Check for Vim version {{{2 +if v:version < 700 + echohl WarningMsg + echo "Sorry, bufexplorer ".g:bufexplorer_version." required Vim 7.0 and greater." + echohl None + finish +endif + +" Create commands {{{2 +command! BufExplorer :call BufExplorer(has ("gui") ? "drop" : "hide edit") +command! BufExplorerHorizontalSplit :call BufExplorerHorizontalSplit() +command! BufExplorerVerticalSplit :call BufExplorerVerticalSplit() + +" Set {{{2 +function! s:Set(var, default) + if !exists(a:var) + if type(a:default) + exec "let" a:var "=" string(a:default) + else + exec "let" a:var "=" a:default + endif + + return 1 + endif + + return 0 +endfunction + +" Script variables {{{2 +let s:MRU_Exclude_List = ["[BufExplorer]","__MRU_Files__"] +let s:MRUList = [] +let s:name = '[BufExplorer]' +let s:originBuffer = 0 +let s:running = 0 +let s:sort_by = ["number", "name", "fullpath", "mru", "extension"] +let s:splitMode = "" +let s:tabSpace = [] +let s:types = {"fullname": ':p', "path": ':p:h', "relativename": ':~:.', "relativepath": ':~:.:h', "shortname": ':t'} + +" Setup the autocommands that handle the MRUList and other stuff. {{{2 +autocmd VimEnter * call s:Setup() + +" Setup {{{2 +function! s:Setup() + call s:Reset() + + " Now that the MRUList is created, add the other autocmds. + augroup BufExplorer + autocmd! + autocmd BufEnter,BufNew * call s:ActivateBuffer() + autocmd BufWipeOut * call s:DeactivateBuffer(1) + autocmd BufDelete * call s:DeactivateBuffer(0) + + autocmd BufWinEnter \[BufExplorer\] call s:Initialize() + autocmd BufWinLeave \[BufExplorer\] call s:Cleanup() + + autocmd TabEnter * call s:TabEnter() + + autocmd SessionLoadPost * call s:Reset() + augroup END +endfunction + +" Reset {{{2 +function! s:Reset() + " Build initial MRUList. This makes sure all the files specified on the + " command line are picked up correctly. + let s:MRUList = range(1, bufnr('$')) + + " Initialize one tab space array, ignore zero-based tabpagenr + " since all tabpagenr's start at 1. + " -1 signifies this is the first time we are referencing this + " tabpagenr. + " If Vim has been loaded with mksession, then it is possible for + " more tabs to exist. So use tabpagenr() to determine how large + " to make the array. If there are 4 tabs, there should be 5 + " elements in this array. + " Each element will hold a CSV list of buffers viewed in + " that tab. So on the 3rd tab, if there user has viewed + " 4 different buffers in that tab, the value would be: + " echo s:tabSpace[3] + " [4, 9, 1, 10] + " echo s:tabSpace + " [[-1], [-1], [-1], [4, 9, 1, 10], [-1]] + let s:tabSpace = [] + let i = 0 + + while( tabpagenr('$') > 0 && i <= tabpagenr('$') ) + call add(s:tabSpace, [-1]) + let i = i + 1 + endwhile +endfunction + +" ActivateBuffer {{{2 +function! s:ActivateBuffer() + " Verify the current tabpage exists in the + " current s:tabSpace array. This can be missing + " entries when restoring sessions. + let i = 0 + while( tabpagenr('$') > 0 && i <= tabpagenr() ) + " Number: 0 + " String: 1 + " Funcref: 2 + " List: 3 + " Dictionary: 4 + " Float: 5 + if type(get(s:tabSpace, i)) == 0 + call add(s:tabSpace, [-1]) + endif + + let i = i + 1 + endwhile + + let _bufnr = bufnr("%") + let list = get(s:tabSpace, tabpagenr(), [-1]) + + if !empty(list) && list[0] == '-1' + " The first time we add a tab, Vim uses the current buffer + " as it's starting page. Even though we are about to + " edit a new page (BufEnter is triggered after), so + " remove the -1 entry indicating we have covered this case. + let list = [] + call add(list, _bufnr) + let s:tabSpace[tabpagenr()] = list + elseif empty(list) || index(list, _bufnr) == -1 + " Add new buffer to this tab's buffer list. + call add(list, _bufnr) + let s:tabSpace[tabpagenr()] = list + + if g:bufExplorerOnlyOneTab == 1 + " If a buffer can only be available in 1 tab page ensure this + " buffer is not present in any other tabs + let tabidx = 1 + while tabidx < len(s:tabSpace) + if tabidx != tabpagenr() + let bufidx = index(s:tabSpace[tabidx], _bufnr) + if bufidx != -1 + call remove(s:tabSpace[tabidx], bufidx) + endif + endif + let tabidx = tabidx + 1 + endwhile + endif + endif + + call s:MRUPush(_bufnr) +endfunction + +" DeactivateBuffer {{{2 +function! s:DeactivateBuffer(remove) + let _bufnr = str2nr(expand("<abuf>")) + call s:MRUPop(_bufnr) +endfunction + +" TabEnter {{{2 +function! s:TabEnter() + " Make s:tabSpace 1-based + if empty(s:tabSpace) || len(s:tabSpace) < (tabpagenr() + 1) + call add(s:tabSpace, [-1]) + endif +endfunction + +" MRUPop {{{2 +function! s:MRUPop(bufnr) + call filter(s:MRUList, 'v:val != '.a:bufnr) +endfunction + +" MRUPush {{{2 +function! s:MRUPush(buf) + " Skip temporary buffer with buftype set. Don't add the BufExplorer window to the + " list. + if s:ShouldIgnore(a:buf) == 1 + return + endif + + " Remove the buffer number from the list if it already exists. + call s:MRUPop(a:buf) + + " Add the buffer number to the head of the list. + call insert(s:MRUList, a:buf) +endfunction + +" ShouldIgnore {{{2 +function! s:ShouldIgnore(buf) + " Ignore temporary buffers with buftype set. + if empty(getbufvar(a:buf, "&buftype") == 0) + return 1 + endif + + " Ignore unlisted buffers. + if buflisted(a:buf) == 0 + return 1 + endif + + " Ignore buffers with no name. + if empty(bufname(a:buf)) == 1 + return 1 + endif + + " Ignore the BufExplorer buffer. + if fnamemodify(bufname(a:buf), ":t") == s:name + return 1 + endif + + " Ignore any buffers in the exclude list. + if index(s:MRU_Exclude_List, bufname(a:buf)) >= 0 + return 1 + endif + + " Else return 0 to indicate that the buffer was not ignored. + return 0 +endfunction + +" Initialize {{{2 +function! s:Initialize() + let s:_insertmode = &insertmode + set noinsertmode + + let s:_showcmd = &showcmd + set noshowcmd + + let s:_cpo = &cpo + set cpo&vim + + let s:_report = &report + let &report = 10000 + + let s:_list = &list + set nolist + + setlocal nonumber + setlocal foldcolumn=0 + setlocal nofoldenable + setlocal cursorline + setlocal nospell + + setlocal nobuflisted + + let s:running = 1 +endfunction + +" Cleanup {{{2 +function! s:Cleanup() + if exists("s:_insertmode") + let &insertmode = s:_insertmode + endif + + if exists("s:_showcmd") + let &showcmd = s:_showcmd + endif + + if exists("s:_cpo") + let &cpo = s:_cpo + endif + + if exists("s:_report") + let &report = s:_report + endif + + if exists("s:_list") + let &list = s:_list + endif + + let s:running = 0 + let s:splitMode = "" + + delmarks! +endfunction + +" BufExplorerHorizontalSplit {{{2 +function! BufExplorerHorizontalSplit() + let s:splitMode = "sp" + exec "BufExplorer" +endfunction + +" BufExplorerVerticalSplit {{{2 +function! BufExplorerVerticalSplit() + let s:splitMode = "vsp" + exec "BufExplorer" +endfunction + +" BufExplorer {{{2 +function! BufExplorer(open) + let name = s:name + + if !has("win32") + " On non-Windows boxes, escape the name so that is shows up correctly. + let name = escape(name, "[]") + endif + + " Make sure there is only one explorer open at a time. + if s:running == 1 + " Go to the open buffer. + if has("gui") + exec "drop" name + endif + + return + endif + + " Add zero to ensure the variable is treated as a number. + let s:originBuffer = bufnr("%") + 0 + + silent let s:raw_buffer_listing = s:GetBufferInfo(0) + + let buffer_listing_copy = copy(s:raw_buffer_listing) + + if (g:bufExplorerShowUnlisted == 0) + call filter(buffer_listing_copy, 'v:val.attributes !~ "u"') + endif + + if (!empty(buffer_listing_copy)) + call filter(buffer_listing_copy, 'v:val.shortname !~ "\\\[No Name\\\]"') + endif + +" if len(buffer_listing_copy) <= 1 +" call s:Warning("Sorry, there are no more buffers to explore") +" return +" endif + + " We may have to split the current window. + if (s:splitMode != "") + " Save off the original settings. + let [_splitbelow, _splitright] = [&splitbelow, &splitright] + + " Set the setting to ours. + let [&splitbelow, &splitright] = [g:bufExplorerSplitBelow, g:bufExplorerSplitRight] + + " Do it. + exe 'keepalt '.s:splitMode + + " Restore the original settings. + let [&splitbelow, &splitright] = [_splitbelow, _splitright] + endif + + if !exists("b:displayMode") || b:displayMode != "winmanager" + " Do not use keepalt when opening bufexplorer to allow the buffer that + " we are leaving to become the new alternate buffer + exec "silent keepjumps ".a:open." ".name + endif + + call s:DisplayBufferList() + + " Position the cursor in the newly displayed list on the line representing + " the active buffer. The active buffer is the line with the '%' character + " in it. + execute search("%") +endfunction + +" DisplayBufferList {{{2 +function! s:DisplayBufferList() + " Do not set bufhidden since it wipes out the data if we switch away from + " the buffer using CTRL-^. + setlocal buftype=nofile + setlocal modifiable + setlocal noswapfile + setlocal nowrap + + call s:SetupSyntax() + call s:MapKeys() + call setline(1, s:CreateHelp()) + call s:BuildBufferList() + call cursor(s:firstBufferLine, 1) + + if !g:bufExplorerResize + normal! zz + endif + + setlocal nomodifiable +endfunction + +" MapKeys {{{2 +function! s:MapKeys() + if exists("b:displayMode") && b:displayMode == "winmanager" + nnoremap <buffer> <silent> <tab> :call <SID>SelectBuffer()<CR> + endif + + nnoremap <script> <silent> <buffer> <2-leftmouse> :call <SID>SelectBuffer()<CR> + nnoremap <script> <silent> <buffer> <CR> :call <SID>SelectBuffer()<CR> + nnoremap <script> <silent> <buffer> <F1> :call <SID>ToggleHelp()<CR> + nnoremap <script> <silent> <buffer> <s-cr> :call <SID>SelectBuffer("tab")<CR> + nnoremap <script> <silent> <buffer> B :call <SID>ToggleOnlyOneTab()<CR> + nnoremap <script> <silent> <buffer> d :call <SID>RemoveBuffer("delete")<CR> + nnoremap <script> <silent> <buffer> D :call <SID>RemoveBuffer("wipe")<CR> + nnoremap <script> <silent> <buffer> f :call <SID>ToggleFindActive()<CR> + nnoremap <script> <silent> <buffer> m :call <SID>MRUListShow()<CR> + nnoremap <script> <silent> <buffer> o :call <SID>SelectBuffer()<CR> + nnoremap <script> <silent> <buffer> p :call <SID>ToggleSplitOutPathName()<CR> + nnoremap <script> <silent> <buffer> q :call <SID>Close()<CR> + nnoremap <script> <silent> <buffer> r :call <SID>SortReverse()<CR> + nnoremap <script> <silent> <buffer> R :call <SID>ToggleShowRelativePath()<CR> + nnoremap <script> <silent> <buffer> s :call <SID>SortSelect()<CR> + nnoremap <script> <silent> <buffer> S :call <SID>ReverseSortSelect()<CR> + nnoremap <script> <silent> <buffer> t :call <SID>SelectBuffer("tab")<CR> + nnoremap <script> <silent> <buffer> T :call <SID>ToggleShowTabBuffer()<CR> + nnoremap <script> <silent> <buffer> u :call <SID>ToggleShowUnlisted()<CR> + + for k in ["G", "n", "N", "L", "M", "H"] + exec "nnoremap <buffer> <silent>" k ":keepjumps normal!" k."<CR>" + endfor +endfunction + +" SetupSyntax {{{2 +function! s:SetupSyntax() + if has("syntax") + syn match bufExplorerHelp "^\".*" contains=bufExplorerSortBy,bufExplorerMapping,bufExplorerTitle,bufExplorerSortType,bufExplorerToggleSplit,bufExplorerToggleOpen + syn match bufExplorerOpenIn "Open in \w\+ window" contained + syn match bufExplorerSplit "\w\+ split" contained + syn match bufExplorerSortBy "Sorted by .*" contained contains=bufExplorerOpenIn,bufExplorerSplit + syn match bufExplorerMapping "\" \zs.\+\ze :" contained + syn match bufExplorerTitle "Buffer Explorer.*" contained + syn match bufExplorerSortType "'\w\{-}'" contained + syn match bufExplorerBufNbr /^\s*\d\+/ + syn match bufExplorerToggleSplit "toggle split type" contained + syn match bufExplorerToggleOpen "toggle open mode" contained + + syn match bufExplorerModBuf /^\s*\d\+.\{4}+.*/ + syn match bufExplorerLockedBuf /^\s*\d\+.\{3}[\-=].*/ + syn match bufExplorerHidBuf /^\s*\d\+.\{2}h.*/ + syn match bufExplorerActBuf /^\s*\d\+.\{2}a.*/ + syn match bufExplorerCurBuf /^\s*\d\+.%.*/ + syn match bufExplorerAltBuf /^\s*\d\+.#.*/ + syn match bufExplorerUnlBuf /^\s*\d\+u.*/ + + hi def link bufExplorerBufNbr Number + hi def link bufExplorerMapping NonText + hi def link bufExplorerHelp Special + hi def link bufExplorerOpenIn Identifier + hi def link bufExplorerSortBy String + hi def link bufExplorerSplit NonText + hi def link bufExplorerTitle NonText + hi def link bufExplorerSortType bufExplorerSortBy + hi def link bufExplorerToggleSplit bufExplorerSplit + hi def link bufExplorerToggleOpen bufExplorerOpenIn + + hi def link bufExplorerActBuf Identifier + hi def link bufExplorerAltBuf String + hi def link bufExplorerCurBuf Type + hi def link bufExplorerHidBuf Constant + hi def link bufExplorerLockedBuf Special + hi def link bufExplorerModBuf Exception + hi def link bufExplorerUnlBuf Comment + endif +endfunction + +" ToggleHelp {{{2 +function! s:ToggleHelp() + let g:bufExplorerDetailedHelp = !g:bufExplorerDetailedHelp + + setlocal modifiable + + " Save position. + normal! ma + + " Remove old header. + if (s:firstBufferLine > 1) + exec "keepjumps 1,".(s:firstBufferLine - 1) "d _" + endif + + call append(0, s:CreateHelp()) + + silent! normal! g`a + delmarks a + + setlocal nomodifiable + + if exists("b:displayMode") && b:displayMode == "winmanager" + call WinManagerForceReSize("BufExplorer") + endif +endfunction + +" GetHelpStatus {{{2 +function! s:GetHelpStatus() + let ret = '" Sorted by '.((g:bufExplorerReverseSort == 1) ? "reverse " : "").g:bufExplorerSortBy + let ret .= ' | '.((g:bufExplorerFindActive == 0) ? "Don't " : "")."Locate buffer" + let ret .= ((g:bufExplorerShowUnlisted == 0) ? "" : " | Show unlisted") + let ret .= ((g:bufExplorerShowTabBuffer == 0) ? "" : " | Show buffers/tab") + let ret .= ((g:bufExplorerOnlyOneTab == 1) ? "" : " | One tab/buffer") + let ret .= ' | '.((g:bufExplorerShowRelativePath == 0) ? "Absolute" : "Relative") + let ret .= ' '.((g:bufExplorerSplitOutPathName == 0) ? "Full" : "Split")." path" + + return ret +endfunction + +" CreateHelp {{{2 +function! s:CreateHelp() + if g:bufExplorerDefaultHelp == 0 && g:bufExplorerDetailedHelp == 0 + let s:firstBufferLine = 1 + return [] + endif + + let header = [] + + if g:bufExplorerDetailedHelp == 1 + call add(header, '" Buffer Explorer ('.g:bufexplorer_version.')') + call add(header, '" --------------------------') + call add(header, '" <F1> : toggle this help') + call add(header, '" <enter> or o or Mouse-Double-Click : open buffer under cursor') + call add(header, '" <shift-enter> or t : open buffer in another tab') + call add(header, '" B : toggle if to save/use recent tab or not') + call add(header, '" d : delete buffer') + call add(header, '" D : wipe buffer') + call add(header, '" f : toggle find active buffer') + call add(header, '" p : toggle spliting of file and path name') + call add(header, '" q : quit') + call add(header, '" r : reverse sort') + call add(header, '" R : toggle showing relative or full paths') + call add(header, '" s : cycle thru "sort by" fields '.string(s:sort_by).'') + call add(header, '" S : reverse cycle thru "sort by" fields') + call add(header, '" T : toggle if to show only buffers for this tab or not') + call add(header, '" u : toggle showing unlisted buffers') + else + call add(header, '" Press <F1> for Help') + endif + + if (!exists("b:displayMode") || b:displayMode != "winmanager") || (b:displayMode == "winmanager" && g:bufExplorerDetailedHelp == 1) + call add(header, s:GetHelpStatus()) + call add(header, '"=') + endif + + let s:firstBufferLine = len(header) + 1 + + return header +endfunction + +" GetBufferInfo {{{2 +function! s:GetBufferInfo(bufnr) + redir => bufoutput + buffers! + redir END + + if (a:bufnr > 0) + " Since we are only interested in this specified buffer + " remove the other buffers listed + let bufoutput = substitute(bufoutput."\n", '^.*\n\(\s*'.a:bufnr.'\>.\{-}\)\n.*', '\1', '') + endif + + let [all, allwidths, listedwidths] = [[], {}, {}] + + for n in keys(s:types) + let allwidths[n] = [] + let listedwidths[n] = [] + endfor + + " Loop over each line in the buffer. + for buf in split(bufoutput, '\n') + let bits = split(buf, '"') + let b = {"attributes": bits[0], "line": substitute(bits[2], '\s*', '', '')} + + for [key, val] in items(s:types) + let b[key] = fnamemodify(bits[1], val) + endfor + + if getftype(b.fullname) == "dir" && g:bufExplorerShowDirectories == 1 + let b.shortname = "<DIRECTORY>" + endif + + call add(all, b) + + for n in keys(s:types) + call add(allwidths[n], len(b[n])) + + if b.attributes !~ "u" + call add(listedwidths[n], len(b[n])) + endif + endfor + endfor + + let [s:allpads, s:listedpads] = [{}, {}] + + for n in keys(s:types) + let s:allpads[n] = repeat(' ', max(allwidths[n])) + let s:listedpads[n] = repeat(' ', max(listedwidths[n])) + endfor + + return all +endfunction + +" BuildBufferList {{{2 +function! s:BuildBufferList() + let lines = [] + + " Loop through every buffer. + for buf in s:raw_buffer_listing + " Skip unlisted buffers if we are not to show them. + if (!g:bufExplorerShowUnlisted && buf.attributes =~ "u") + " Skip unlisted buffers if we are not to show them. + continue + endif + + " Ignore buffers with no name. + if empty(bufname(str2nr(buf.attributes))) == 1 + continue + endif + + if (g:bufExplorerShowTabBuffer) + let show_buffer = 0 + + for bufnr in s:tabSpace[tabpagenr()] + if (buf.attributes =~ '^\s*'.bufnr.'\>') + " Only buffers shown on the current tabpagenr + let show_buffer = 1 + break + endif + endfor + + if show_buffer == 0 + continue + endif + endif + + let line = buf.attributes." " + + if g:bufExplorerSplitOutPathName + let type = (g:bufExplorerShowRelativePath) ? "relativepath" : "path" + let path = buf[type] + let pad = (g:bufExplorerShowUnlisted) ? s:allpads.shortname : s:listedpads.shortname + let line .= buf.shortname." ".strpart(pad.path, len(buf.shortname)) + else + let type = (g:bufExplorerShowRelativePath) ? "relativename" : "fullname" + let path = buf[type] + let line .= path + endif + + let pads = (g:bufExplorerShowUnlisted) ? s:allpads : s:listedpads + + if !empty(pads[type]) + let line .= strpart(pads[type], len(path))." " + endif + + let line .= buf.line + + call add(lines, line) + endfor + + call setline(s:firstBufferLine, lines) + + call s:SortListing() +endfunction + +" SelectBuffer {{{2 +function! s:SelectBuffer(...) + " Sometimes messages are not cleared when we get here so it looks like an + " error has occurred when it really has not. +" 3/25/2012 echo "" + + " Are we on a line with a file name? + if line('.') < s:firstBufferLine + exec "normal! \<CR>" + return + endif + + let _bufNbr = str2nr(getline('.')) + + " Check and see if we are running BufferExplorer via WinManager. + if exists("b:displayMode") && b:displayMode == "winmanager" + let _bufName = expand("#"._bufNbr.":p") + + if (a:0 == 1) && (a:1 == "tab") + call WinManagerFileEdit(_bufName, 1) + else + call WinManagerFileEdit(_bufName, 0) + endif + + return + endif + + if bufexists(_bufNbr) + if bufnr("#") == _bufNbr && !exists("g:bufExplorerChgWin") + return s:Close() + endif + + " Are we suppose to open the selected buffer in a tab? + if (a:0 == 1) && (a:1 == "tab") + " Yes, we are to open the selected buffer in a tab. + + " Restore [BufExplorer] buffer. + exec "keepjumps silent buffer!".s:originBuffer + + " Get the tab nmber where this bufer is located in. + let tabNbr = s:GetTabNbr(_bufNbr) + + " Was the tab found? + if tabNbr == 0 + " _bufNbr is not opened in any tabs. Open a new tab with the selected buffer in it. + exec "999tab split +buffer" . _bufNbr + else + " The _bufNbr is already opened in a tab, go to that tab. + exec tabNbr . "tabnext" + + " Focus window. + exec s:GetWinNbr(tabNbr, _bufNbr) . "wincmd w" + endif + else + " No, the user did not ask to open the selected buffer in a tab. + + " Are we suppose to move to the tab where the active buffer is? + if exists("g:bufExplorerChgWin") + exe g:bufExplorerChgWin."wincmd w" + elseif bufloaded(_bufNbr) && g:bufExplorerFindActive + call s:Close() + + " Get the tab number where this buffer is located in. + let tabNbr = s:GetTabNbr(_bufNbr) + + " Was the tab found? + if tabNbr != 0 + " Yes, the buffer is located in a tab. Go to that tab number. + exec tabNbr . "tabnext" + else + "Nope, the buffer is not in a tab. Simply switch to that + "buffer. + let _bufName = expand("#"._bufNbr.":p") + exec _bufName ? "drop ".escape(_bufName, " ") : "buffer "._bufNbr + endif + endif + + " Switch to the buffer. + exec "keepalt keepjumps silent b!" _bufNbr + endif + + " Make the buffer 'listed' again. + call setbufvar(_bufNbr, "&buflisted", "1") + + " Call any associated function references. g:bufExplorerFuncRef may be + " an individual function reference or it may be a list containing + " function references. It will ignore anything that's not a function + " reference. + " + " See :help FuncRef for more on function references. + if exists("g:BufExplorerFuncRef") + if type(g:BufExplorerFuncRef) == 2 + keepj call g:BufExplorerFuncRef() + elseif type(g:BufExplorerFuncRef) == 3 + for FncRef in g:BufExplorerFuncRef + if type(FncRef) == 2 + keepj call FncRef() + endif + endfor + endif + endif + else + call s:Error("Sorry, that buffer no longer exists, please select another") + call s:DeleteBuffer(_bufNbr, "wipe") + endif +endfunction + +" RemoveBuffer {{{2 +function! s:RemoveBuffer(mode) + " Are we on a line with a file name? + if line('.') < s:firstBufferLine + return + endif + + " Do not allow this buffer to be deleted if it is the last one. + if len(s:MRUList) == 1 + call s:Error("Sorry, you are not allowed to delete the last buffer") + return + endif + + " These commands are to temporarily suspend the activity of winmanager. + if exists("b:displayMode") && b:displayMode == "winmanager" + call WinManagerSuspendAUs() + end + + let _bufNbr = str2nr(getline('.')) + + if getbufvar(_bufNbr, '&modified') == 1 + call s:Error("Sorry, no write since last change for buffer "._bufNbr.", unable to delete") + return + else + " Okay, everything is good, delete or wipe the buffer. + call s:DeleteBuffer(_bufNbr, a:mode) + endif + + " Reactivate winmanager autocommand activity. + if exists("b:displayMode") && b:displayMode == "winmanager" + call WinManagerForceReSize("BufExplorer") + call WinManagerResumeAUs() + end +endfunction + +" DeleteBuffer {{{2 +function! s:DeleteBuffer(buf, mode) + " This routine assumes that the buffer to be removed is on the current line. + try + " Wipe/Delete buffer from Vim. + if a:mode == "wipe" + exe "silent bwipe" a:buf + else + exe "silent bdelete" a:buf + endif + + " Delete the buffer from the list on screen. + setlocal modifiable + normal! "_dd + setlocal nomodifiable + + " Delete the buffer from the raw buffer list. + call filter(s:raw_buffer_listing, 'v:val.attributes !~ " '.a:buf.' "') + catch + call s:Error(v:exception) + endtry +endfunction + +" Close {{{2 +function! s:Close() + " Get only the listed buffers. + let listed = filter(copy(s:MRUList), "buflisted(v:val)") + + " If we needed to split the main window, close the split one. + if (s:splitMode != "") + exec "wincmd c" + endif + + " Check to see if there are anymore buffers listed. + if len(listed) == 0 + " Since there are no buffers left to switch to, open a new empty + " buffers. + exe "enew" + else + " Since there are buffers left to switch to, swith to them... + for b in reverse(listed[0:1]) + exec "keepjumps silent b ".b + endfor + endif + + " Clear any messages. + echo +endfunction + +" ToggleSplitOutPathName {{{2 +function! s:ToggleSplitOutPathName() + let g:bufExplorerSplitOutPathName = !g:bufExplorerSplitOutPathName + call s:RebuildBufferList() + call s:UpdateHelpStatus() +endfunction + +" ToggleShowRelativePath {{{2 +function! s:ToggleShowRelativePath() + let g:bufExplorerShowRelativePath = !g:bufExplorerShowRelativePath + call s:RebuildBufferList() + call s:UpdateHelpStatus() +endfunction + +" ToggleShowTabBuffer {{{2 +function! s:ToggleShowTabBuffer() + let g:bufExplorerShowTabBuffer = !g:bufExplorerShowTabBuffer + call s:RebuildBufferList(g:bufExplorerShowTabBuffer) + call s:UpdateHelpStatus() +endfunction + +" ToggleOnlyOneTab {{{2 +function! s:ToggleOnlyOneTab() + let g:bufExplorerOnlyOneTab = !g:bufExplorerOnlyOneTab + call s:RebuildBufferList() + call s:UpdateHelpStatus() +endfunction + +" ToggleShowUnlisted {{{2 +function! s:ToggleShowUnlisted() + let g:bufExplorerShowUnlisted = !g:bufExplorerShowUnlisted + let num_bufs = s:RebuildBufferList(g:bufExplorerShowUnlisted == 0) + call s:UpdateHelpStatus() +endfunction + +" ToggleFindActive {{{2 +function! s:ToggleFindActive() + let g:bufExplorerFindActive = !g:bufExplorerFindActive + call s:UpdateHelpStatus() +endfunction + +" RebuildBufferList {{{2 +function! s:RebuildBufferList(...) + setlocal modifiable + + let curPos = getpos('.') + + if a:0 + " Clear the list first. + exec "keepjumps ".s:firstBufferLine.',$d "_' + endif + + let num_bufs = s:BuildBufferList() + + call setpos('.', curPos) + + setlocal nomodifiable + + return num_bufs +endfunction + +" UpdateHelpStatus {{{2 +function! s:UpdateHelpStatus() + setlocal modifiable + + let text = s:GetHelpStatus() + call setline(s:firstBufferLine - 2, text) + + setlocal nomodifiable +endfunction + +" MRUCmp {{{2 +function! s:MRUCmp(line1, line2) + return index(s:MRUList, str2nr(a:line1)) - index(s:MRUList, str2nr(a:line2)) +endfunction + +" SortReverse {{{2 +function! s:SortReverse() + let g:bufExplorerReverseSort = !g:bufExplorerReverseSort + call s:ReSortListing() +endfunction + +" SortSelect {{{2 +function! s:SortSelect() + let g:bufExplorerSortBy = get(s:sort_by, index(s:sort_by, g:bufExplorerSortBy) + 1, s:sort_by[0]) + call s:ReSortListing() +endfunction + +" ReverseSortSelect {{{2 +function! s:ReverseSortSelect() + let g:bufExplorerSortBy = get(s:sort_by, index(s:sort_by, g:bufExplorerSortBy) - 1, s:sort_by[-1]) + call s:ReSortListing() +endfunction + +" ReSortListing {{{2 +function! s:ReSortListing() + setlocal modifiable + + let curPos = getpos('.') + + call s:SortListing() + call s:UpdateHelpStatus() + + call setpos('.', curPos) + + setlocal nomodifiable +endfunction + +" SortListing {{{2 +function! s:SortListing() + let sort = s:firstBufferLine.",$sort".((g:bufExplorerReverseSort == 1) ? "!": "") + + if g:bufExplorerSortBy == "number" + " Easiest case. + exec sort 'n' + elseif g:bufExplorerSortBy == "name" + if g:bufExplorerSplitOutPathName + exec sort 'ir /\d.\{7}\zs\f\+\ze/' + else + exec sort 'ir /\zs[^\/\\]\+\ze\s*line/' + endif + elseif g:bufExplorerSortBy == "fullpath" + if g:bufExplorerSplitOutPathName + " Sort twice - first on the file name then on the path. + exec sort 'ir /\d.\{7}\zs\f\+\ze/' + endif + + exec sort 'ir /\zs\f\+\ze\s\+line/' + elseif g:bufExplorerSortBy == "extension" + exec sort 'ir /\.\zs\w\+\ze\s/' + elseif g:bufExplorerSortBy == "mru" + let l = getline(s:firstBufferLine, "$") + + call sort(l, "<SID>MRUCmp") + + if g:bufExplorerReverseSort + call reverse(l) + endif + + call setline(s:firstBufferLine, l) + endif +endfunction + +" MRUListShow {{{2 +function! s:MRUListShow() + echomsg "MRUList=".string(s:MRUList) +endfunction + +" Error {{{2 +" Display a message using ErrorMsg highlight group. +function! s:Error(msg) + echohl ErrorMsg + echomsg a:msg + echohl None +endfunction + +" Warning {{{2 +" Display a message using WarningMsg highlight group. +function! s:Warning(msg) + echohl WarningMsg + echomsg a:msg + echohl None +endfunction + +" GetTabNbr {{{2 +function! s:GetTabNbr(bufNbr) + " Searching buffer bufno, in tabs. + for i in range(tabpagenr("$")) + if index(tabpagebuflist(i + 1), a:bufNbr) != -1 + return i + 1 + endif + endfor + + return 0 +endfunction + +" GetWinNbr" {{{2 +function! s:GetWinNbr(tabNbr, bufNbr) + " window number in tabpage. + let tablist = tabpagebuflist(a:tabNbr) + " Number: 0 + " String: 1 + " Funcref: 2 + " List: 3 + " Dictionary: 4 + " Float: 5 + if type(tablist) == 3 + return index(tabpagebuflist(a:tabNbr), a:bufNbr) + 1 + else + return 1 + endif +endfunction + +" Winmanager Integration {{{2 +let g:BufExplorer_title = "\[Buf\ List\]" +call s:Set("g:bufExplorerResize", 1) +call s:Set("g:bufExplorerMaxHeight", 25) " Handles dynamic resizing of the window. + +" function! to start display. Set the mode to 'winmanager' for this buffer. +" This is to figure out how this plugin was called. In a standalone fashion +" or by winmanager. +function! BufExplorer_Start() + let b:displayMode = "winmanager" + call BufExplorer("e") +endfunction + +" Returns whether the display is okay or not. +function! BufExplorer_IsValid() + return 0 +endfunction + +" Handles dynamic refreshing of the window. +function! BufExplorer_Refresh() + let b:displayMode = "winmanager" + call BufExplorer("e") +endfunction + +function! BufExplorer_ReSize() + if !g:bufExplorerResize + return + end + + let nlines = min([line("$"), g:bufExplorerMaxHeight]) + + exe nlines." wincmd _" + + " The following lines restore the layout so that the last file line is also + " the last window line. Sometimes, when a line is deleted, although the + " window size is exactly equal to the number of lines in the file, some of + " the lines are pushed up and we see some lagging '~'s. + let pres = getpos(".") + + exe $ + + let _scr = &scrolloff + let &scrolloff = 0 + + normal! z- + + let &scrolloff = _scr + + call setpos(".", pres) +endfunction + +" Default key mapping {{{1 +if !hasmapto('BufExplorer') + noremap <script> <silent> <unique> <Leader>be :BufExplorer<CR> +endif + +if !hasmapto('BufExplorerHorizontalSplit') + noremap <script> <silent> <unique> <Leader>bs :BufExplorerHorizontalSplit<CR> +endif + +if !hasmapto('BufExplorerVerticalSplit') + noremap <script> <silent> <unique> <Leader>bv :BufExplorerVerticalSplit<CR> +endif + +" Default values {{{1 +call s:Set("g:bufExplorerDefaultHelp", 1) " Show default help? +call s:Set("g:bufExplorerDetailedHelp", 0) " Show detailed help? +call s:Set("g:bufExplorerFindActive", 1) " When selecting an active buffer, take you to the window where it is active? +call s:Set("g:bufExplorerOnlyOneTab", 1) " If ShowTabBuffer = 1, only store the most recent tab for this buffer. +call s:Set("g:bufExplorerReverseSort", 0) " Sort in reverse order by default? +call s:Set("g:bufExplorerShowDirectories", 1) " (Dir's are added by commands like ':e .') +call s:Set("g:bufExplorerShowRelativePath", 0) " Show listings with relative or absolute paths? +call s:Set("g:bufExplorerShowTabBuffer", 0) " Show only buffer(s) for this tab? +call s:Set("g:bufExplorerShowUnlisted", 0) " Show unlisted buffers? +call s:Set("g:bufExplorerSortBy", "mru") " Sorting methods are in s:sort_by: +call s:Set("g:bufExplorerSplitBelow", &splitbelow) " Should horizontal splits be below or above current window? +call s:Set("g:bufExplorerSplitOutPathName", 1) " Split out path and file name? +call s:Set("g:bufExplorerSplitRight", &splitright) " Should vertical splits be on the right or left of current window? +"1}}} + +" vim:ft=vim foldmethod=marker sw=4 diff --git a/modules/vim/vim.dot.link/plugin/git.vim b/modules/vim/vim.dot.link/plugin/git.vim new file mode 100644 index 0000000..bd0b053 --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/git.vim @@ -0,0 +1,372 @@ +" Author: motemen <motemen@gmail.com> +" License: The MIT License +" URL: http://github.com/motemen/git-vim/ + +if !exists('g:git_command_edit') + let g:git_command_edit = 'new' +endif + +if !exists('g:git_bufhidden') + let g:git_bufhidden = '' +endif + +if !exists('g:git_bin') + let g:git_bin = 'git' +endif + +if !exists('g:git_author_highlight') + let g:git_author_highlight = { } +endif + +if !exists('g:git_highlight_blame') + let g:git_highlight_blame = 0 +endif + +if !exists('g:git_no_map_default') || !g:git_no_map_default + nnoremap <Leader>gd :GitDiff<Enter> + nnoremap <Leader>gD :GitDiff --cached<Enter> + nnoremap <Leader>gs :GitStatus<Enter> + nnoremap <Leader>gl :GitLog<Enter> + nnoremap <Leader>ga :GitAdd<Enter> + nnoremap <Leader>gA :GitAdd <cfile><Enter> + nnoremap <Leader>gc :GitCommit<Enter> + nnoremap <Leader>gp :GitPullRebase<Enter> +endif + +" Ensure b:git_dir exists. +function! s:GetGitDir() + if !exists('b:git_dir') + let b:git_dir = s:SystemGit('rev-parse --git-dir') + if !v:shell_error + let b:git_dir = fnamemodify(split(b:git_dir, "\n")[0], ':p') . '/' + endif + endif + return b:git_dir +endfunction + +" Returns current git branch. +" Call inside 'statusline' or 'titlestring'. +function! GitBranch() + let git_dir = <SID>GetGitDir() + + if strlen(git_dir) && filereadable(git_dir . '/HEAD') + let lines = readfile(git_dir . '/HEAD') + if !len(lines) + return '' + else + return matchstr(lines[0], 'refs/heads/\zs.\+$') + endif + else + return '' + endif +endfunction + +" List all git local branches. +function! ListGitBranches(arg_lead, cmd_line, cursor_pos) + let branches = split(s:SystemGit('branch'), '\n') + if v:shell_error + return [] + endif + + return map(branches, 'matchstr(v:val, ''^[* ] \zs.*'')') +endfunction + +" List all git commits. +function! ListGitCommits(arg_lead, cmd_line, cursor_pos) + let commits = split(s:SystemGit('log --pretty=format:%h')) + if v:shell_error + return [] + endif + + let commits = ['HEAD'] + ListGitBranches(a:arg_lead, a:cmd_line, a:cursor_pos) + commits + + if a:cmd_line =~ '^GitDiff' + " GitDiff accepts <commit>..<commit> + if a:arg_lead =~ '\.\.' + let pre = matchstr(a:arg_lead, '.*\.\.\ze') + let commits = map(commits, 'pre . v:val') + endif + endif + + return filter(commits, 'match(v:val, ''\v'' . a:arg_lead) == 0') +endfunction + +" Show diff. +function! GitDiff(args) + let git_output = s:SystemGit('diff ' . a:args . ' -- ' . s:Expand('%')) + if !strlen(git_output) + echo "No output from git command" + return + endif + + call <SID>OpenGitBuffer(git_output) + setlocal filetype=git-diff +endfunction + +" Show Status. +function! GitStatus() + let git_output = s:SystemGit('status') + call <SID>OpenGitBuffer(git_output) + setlocal filetype=git-status + nnoremap <buffer> <Enter> :GitAdd <cfile><Enter>:call <SID>RefreshGitStatus()<Enter> + nnoremap <buffer> - :silent !git reset HEAD -- =expand('<cfile>')<Enter><Enter>:call <SID>RefreshGitStatus()<Enter> +endfunction + +function! s:RefreshGitStatus() + let pos_save = getpos('.') + GitStatus + call setpos('.', pos_save) +endfunction + +" Show Log. +function! GitLog(args) + let git_output = s:SystemGit('log ' . a:args . ' -- ' . s:Expand('%')) + call <SID>OpenGitBuffer(git_output) + setlocal filetype=git-log +endfunction + +" Add file to index. +function! GitAdd(expr) + let file = s:Expand(strlen(a:expr) ? a:expr : '%') + + call GitDoCommand('add ' . file) +endfunction + +" Commit. +function! GitCommit(args) + let git_dir = <SID>GetGitDir() + + let args = a:args + + if args !~ '\v\k@<!(-a|--all)>' && s:SystemGit('diff --cached --stat') =~ '^\(\s\|\n\)*$' + let args .= ' -a' + endif + + " Create COMMIT_EDITMSG file + let editor_save = $EDITOR + let $EDITOR = '' + let git_output = s:SystemGit('commit ' . args) + let $EDITOR = editor_save + + let cur_dir = getcwd() + execute printf('%s %sCOMMIT_EDITMSG', g:git_command_edit, git_dir) + execute printf("lcd %s", cur_dir) + + setlocal filetype=git-status bufhidden=wipe + augroup GitCommit + autocmd BufWritePre <buffer> g/^#\|^\s*$/d | setlocal fileencoding=utf-8 + execute printf("autocmd BufEnter <buffer> lcd %s", cur_dir) + execute printf("autocmd BufWritePost <buffer> call GitDoCommand('commit %s -F ' . expand('%%')) | autocmd! GitCommit * <buffer>", args) + augroup END +endfunction + +" Checkout. +function! GitCheckout(args) + call GitDoCommand('checkout ' . a:args) +endfunction + +" Push. +function! GitPush(args) +" call GitDoCommand('push ' . a:args) + " Wanna see progress... + let args = a:args + if args =~ '^\s*$' + let args = 'origin ' . GitBranch() + endif + execute '!' g:git_bin 'push' args +endfunction + +" Pull. +function! GitPull(args) +" call GitDoCommand('pull ' . a:args) + " Wanna see progress... + execute '!' g:git_bin 'pull' a:args +endfunction + +" Show commit, tree, blobs. +function! GitCatFile(file) + let file = s:Expand(a:file) + let git_output = s:SystemGit('cat-file -p ' . file) + if !strlen(git_output) + echo "No output from git command" + return + endif + + call <SID>OpenGitBuffer(git_output) +endfunction + +" Show revision and author for each line. +function! GitBlame(...) + let l:git_blame_width = 20 + let git_output = s:SystemGit('blame -- ' . expand('%')) + if !strlen(git_output) + echo "No output from git command" + return + endif + + if strlen(a:1) + let l:git_blame_width = a:1 + endif + + setlocal noscrollbind + + " :/ + let git_command_edit_save = g:git_command_edit + let g:git_command_edit = 'leftabove vnew' + call <SID>OpenGitBuffer(git_output) + let g:git_command_edit = git_command_edit_save + + setlocal modifiable + silent %s/\d\d\d\d\zs \+\d\+).*// + exe 'vertical resize ' . git_blame_width + setlocal nomodifiable + setlocal nowrap scrollbind + + if g:git_highlight_blame + call s:DoHighlightGitBlame() + endif + + wincmd p + setlocal nowrap scrollbind + + syncbind +endfunction + + + + +" Experimental +function! s:DoHighlightGitBlame() + for l in range(1, line('$')) + let line = getline(l) + let [commit, author] = matchlist(line, '\(\x\+\) (\(.\{-}\)\s* \d\d\d\d-\d\d-\d\d')[1:2] + call s:LoadSyntaxRuleFor({ 'author': author }) + endfor +endfunction + +function! s:LoadSyntaxRuleFor(params) + let author = a:params.author + let name = 'gitBlameAuthor_' . substitute(author, '\s', '_', 'g') + if (!hlID(name)) + if has_key(g:git_author_highlight, author) + execute 'highlight' name g:git_author_highlight[author] + else + let [n1, n2] = [0, 1] + for c in split(author, '\zs') + let n1 = (n1 + char2nr(c)) % 8 + let n2 = (n2 + char2nr(c) * 3) % 8 + endfor + if n1 == n2 + let n1 = (n2 + 1) % 8 + endif + execute 'highlight' name printf('ctermfg=%d ctermbg=%d', n1, n2) + endif + execute 'syntax match' name '"\V\^\x\+ (' . escape(author, '\') . '\.\*"' + endif +endfunction + +function! GitDoCommand(args) + let git_output = s:SystemGit(a:args) + let git_output = substitute(git_output, '\n*$', '', '') + if v:shell_error + echohl Error + echo git_output + echohl None + else + echo git_output + endif +endfunction + +function! s:SystemGit(args) + " workardound for MacVim, on which shell does not inherit environment + " variables + if has('mac') && &shell =~ 'sh$' + return system('EDITOR="" '. g:git_bin . ' ' . a:args) + else + return system(g:git_bin . ' ' . a:args) + endif +endfunction + +" Show vimdiff for merge. (experimental) +function! GitVimDiffMerge() + let file = s:Expand('%') + let filetype = &filetype + let t:git_vimdiff_original_bufnr = bufnr('%') + let t:git_vimdiff_buffers = [] + + topleft new + setlocal buftype=nofile + file `=':2:' . file` + call add(t:git_vimdiff_buffers, bufnr('%')) + execute 'silent read!git show :2:' . file + 0d + diffthis + let &filetype = filetype + + rightbelow vnew + setlocal buftype=nofile + file `=':3:' . file` + call add(t:git_vimdiff_buffers, bufnr('%')) + execute 'silent read!git show :3:' . file + 0d + diffthis + let &filetype = filetype +endfunction + +function! GitVimDiffMergeDone() + if exists('t:git_vimdiff_original_bufnr') && exists('t:git_vimdiff_buffers') + if getbufline(t:git_vimdiff_buffers[0], 1, '$') == getbufline(t:git_vimdiff_buffers[1], 1, '$') + execute 'sbuffer ' . t:git_vimdiff_original_bufnr + 0put=getbufline(t:git_vimdiff_buffers[0], 1, '$') + normal! jdG + update + execute 'bdelete ' . t:git_vimdiff_buffers[0] + execute 'bdelete ' . t:git_vimdiff_buffers[1] + close + else + echohl Error + echo 'There still remains conflict' + echohl None + endif + endif +endfunction + +function! s:OpenGitBuffer(content) + if exists('b:is_git_msg_buffer') && b:is_git_msg_buffer + enew! + else + execute g:git_command_edit + endif + + setlocal buftype=nofile readonly modifiable + execute 'setlocal bufhidden=' . g:git_bufhidden + + silent put=a:content + keepjumps 0d + setlocal nomodifiable + + let b:is_git_msg_buffer = 1 +endfunction + +function! s:Expand(expr) + if has('win32') + return substitute(expand(a:expr), '\', '/', 'g') + else + return expand(a:expr) + endif +endfunction + +command! -nargs=1 -complete=customlist,ListGitCommits GitCheckout call GitCheckout(<q-args>) +command! -nargs=* -complete=customlist,ListGitCommits GitDiff call GitDiff(<q-args>) +command! GitStatus call GitStatus() +command! -nargs=? GitAdd call GitAdd(<q-args>) +command! -nargs=* GitLog call GitLog(<q-args>) +command! -nargs=* GitCommit call GitCommit(<q-args>) +command! -nargs=1 GitCatFile call GitCatFile(<q-args>) +command! -nargs=? GitBlame call GitBlame(<q-args>) +command! -nargs=+ Git call GitDoCommand(<q-args>) +command! GitVimDiffMerge call GitVimDiffMerge() +command! GitVimDiffMergeDone call GitVimDiffMergeDone() +command! -nargs=* GitPull call GitPull(<q-args>) +command! GitPullRebase call GitPull('--rebase') +command! -nargs=* GitPush call GitPush(<q-args>) diff --git a/modules/vim/vim.dot.link/plugin/indexer.vim.disabled b/modules/vim/vim.dot.link/plugin/indexer.vim.disabled new file mode 100644 index 0000000..28c9472 --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/indexer.vim.disabled @@ -0,0 +1,672 @@ +"============================================================================= +" File: indexer.vim +" Author: Dmitry Frank (dimon.frank@gmail.com) +" Last Change: 26 Sep 2010 +" Version: 1.8 +"============================================================================= +" See documentation in accompanying help file +" You may use this code in whatever way you see fit. + + +" s:ParsePath(sPath) +" changing '\' to '/' or vice versa depending on OS (MS Windows or not) also calls simplify() +function! s:ParsePath(sPath) + if (has('win32') || has('win64')) + let l:sPath = substitute(a:sPath, '/', '\', 'g') + else + let l:sPath = substitute(a:sPath, '\', '/', 'g') + endif + let l:sPath = simplify(l:sPath) + + " removing last "/" or "\" + let l:sLastSymb = strpart(l:sPath, (strlen(l:sPath) - 1), 1) + if (l:sLastSymb == '/' || l:sLastSymb == '\') + let l:sPath = strpart(l:sPath, 0, (strlen(l:sPath) - 1)) + endif + return l:sPath +endfunction + +" s:Trim(sString) +" trims spaces from begin and end of string +function! s:Trim(sString) + return substitute(substitute(a:sString, '^\s\+', '', ''), '\s\+$', '', '') +endfunction + +" s:IsAbsolutePath(path) <<< +" this function from project.vim is written by Aric Blumer. +" Returns true if filename has an absolute path. +function! s:IsAbsolutePath(path) + if a:path =~ '^ftp:' || a:path =~ '^rcp:' || a:path =~ '^scp:' || a:path =~ '^http:' + return 2 + endif + let path=expand(a:path) " Expand any environment variables that might be in the path + if path[0] == '/' || path[0] == '~' || path[0] == '\\' || path[1] == ':' + return 1 + endif + return 0 +endfunction " >>> + + + +function! s:GetDirsAndFilesFromIndexerList(aLines, projectName, dExistsResult) + let l:aLines = a:aLines + let l:dResult = a:dExistsResult + let l:boolInNeededProject = (a:projectName == '' ? 1 : 0) + let l:boolInProjectsParentSection = 0 + let l:sProjectsParentFilter = '' + + let l:sCurProjName = '' + + for l:sLine in l:aLines + + " if line is not empty + if l:sLine !~ '^\s*$' && l:sLine !~ '^\s*\#.*$' + + " look for project name [PrjName] + let myMatch = matchlist(l:sLine, '^\s*\[\([^\]]\+\)\]') + + if (len(myMatch) > 0) + + " check for PROJECTS_PARENT section + + if (strpart(myMatch[1], 0, 15) == 'PROJECTS_PARENT') + " this is projects parent section + let l:sProjectsParentFilter = '' + let filterMatch = matchlist(myMatch[1], 'filter="\([^"]\+\)"') + if (len(filterMatch) > 0) + let l:sProjectsParentFilter = filterMatch[1] + endif + let l:boolInProjectsParentSection = 1 + else + let l:boolInProjectsParentSection = 0 + + + if (a:projectName != '') + if (myMatch[1] == a:projectName) + let l:boolInNeededProject = 1 + else + let l:boolInNeededProject = 0 + endif + endif + + if l:boolInNeededProject + let l:sCurProjName = myMatch[1] + let l:dResult[l:sCurProjName] = { 'files': [], 'paths': [], 'not_exist': [] } + endif + endif + else + + if l:boolInProjectsParentSection + " parsing one project parent + + let l:lFilter = split(l:sProjectsParentFilter, ' ') + if (len(l:lFilter) == 0) + let l:lFilter = ['*'] + endif + " removing \/* from end of path + let l:projectsParent = substitute(<SID>Trim(l:sLine), '[\\/*]\+$', '', '') + + " creating list of projects + let l:lProjects = split(expand(l:projectsParent.'/*'), '\n') + let l:lIndexerFilesList = [] + for l:sPrj in l:lProjects + if (isdirectory(l:sPrj)) + call add(l:lIndexerFilesList, '['.substitute(l:sPrj, '^.*[\\/]\([^\\/]\+\)$', '\1', '').']') + for l:sCurFilter in l:lFilter + call add(l:lIndexerFilesList, l:sPrj.'/**/'.l:sCurFilter) + endfor + call add(l:lIndexerFilesList, '') + endif + endfor + " parsing this list + let l:dResult = <SID>GetDirsAndFilesFromIndexerList(l:lIndexerFilesList, a:projectName, l:dResult) + + elseif l:boolInNeededProject + " looks like there's path + if l:sCurProjName == '' + let l:sCurProjName = 'noname' + let l:dResult[l:sCurProjName] = { 'files': [], 'paths': [], 'not_exist': [] } + endif + if (!g:indexer_ctagsDontSpecifyFilesIfPossible && s:indexer_projectName != '') + " adding every file. + let l:dResult[l:sCurProjName].files = <SID>ConcatLists(l:dResult[l:sCurProjName].files, split(expand(substitute(<SID>Trim(l:sLine), '\\\*\*', '**', 'g')), '\n')) + else + " adding just paths. (much more faster) + let l:dResult[l:sCurProjName].paths = <SID>ConcatLists(l:dResult[l:sCurProjName].paths, [<SID>ParsePath(expand(substitute(substitute(substitute(l:sLine, '^\(.*\)[\\/][^\\/]\+$', '\1', 'g'), '^\([^*]\+\).*$', '\1', ''), '[\\/]$', '', '')))]) + endif + endif + + endif + endif + + endfor + + " build paths + if (!g:indexer_ctagsDontSpecifyFilesIfPossible && s:indexer_projectName != '') + for l:sKey in keys(l:dResult) + let l:lPaths = [] + for l:sFile in l:dResult[l:sKey].files + let l:sPath = substitute(l:sFile, '^\(.*\)[\\/][^\\/]\+$', '\1', 'g') + call add(l:lPaths, l:sPath) + endfor + + let l:dResult[l:sKey].paths = <SID>ConcatLists(l:dResult[l:sKey].paths, l:lPaths) + + endfor + endif + + return l:dResult +endfunction + +" getting dictionary with files, paths and non-existing files from indexer +" project file +function! s:GetDirsAndFilesFromIndexerFile(indexerFile, projectName) + let l:aLines = readfile(a:indexerFile) + let l:dResult = {} + let l:dResult = <SID>GetDirsAndFilesFromIndexerList(l:aLines, a:projectName, l:dResult) + return l:dResult +endfunction + +" getting dictionary with files, paths and non-existing files from +" project.vim's project file +function! s:GetDirsAndFilesFromProjectFile(projectFile, projectName) + let l:aLines = readfile(a:projectFile) + " if projectName is empty, then we should add files from whole projectFile + let l:boolInNeededProject = (a:projectName == '' ? 1 : 0) + + let l:iOpenedBraces = 0 " current count of opened { } + let l:iOpenedBracesAtProjectStart = 0 + let l:aPaths = [] " paths stack + let l:sLastFoundPath = '' + + let l:dResult = {} + let l:sCurProjName = '' + + for l:sLine in l:aLines + " ignoring comments + if l:sLine =~ '^#' | continue | endif + + let l:sLine = substitute(l:sLine, '#.\+$', '' ,'') + " searching for closing brace { } + let sTmpLine = l:sLine + while (sTmpLine =~ '}') + let l:iOpenedBraces = l:iOpenedBraces - 1 + + " if projectName is defined and there was last brace closed, then we + " are finished parsing needed project + if (l:iOpenedBraces <= l:iOpenedBracesAtProjectStart) && a:projectName != '' + let l:boolInNeededProject = 0 + " TODO: total break + endif + call remove(l:aPaths, len(l:aPaths) - 1) + + let sTmpLine = substitute(sTmpLine, '}', '', '') + endwhile + + " searching for blabla=qweqwe + let myMatch = matchlist(l:sLine, '\s*\(.\{-}\)=\(.\{-}\)\\\@<!\(\s\|$\)') + if (len(myMatch) > 0) + " now we found start of project folder or subfolder + " + if !l:boolInNeededProject + if (a:projectName != '' && myMatch[1] == a:projectName) + let l:iOpenedBracesAtProjectStart = l:iOpenedBraces + let l:boolInNeededProject = 1 + endif + endif + + if l:boolInNeededProject && (l:iOpenedBraces == l:iOpenedBracesAtProjectStart) + let l:sCurProjName = myMatch[1] + let l:dResult[myMatch[1]] = { 'files': [], 'paths': [], 'not_exist': [] } + endif + + let l:sLastFoundPath = myMatch[2] + " ADDED! Jkooij + " Strip the path of surrounding " characters, if there are any + let l:sLastFoundPath = substitute(l:sLastFoundPath, "\"\\(.*\\)\"", "\\1", "g") + let l:sLastFoundPath = expand(l:sLastFoundPath) " Expand any environment variables that might be in the path + let l:sLastFoundPath = s:ParsePath(l:sLastFoundPath) + + endif + + " searching for opening brace { } + let sTmpLine = l:sLine + while (sTmpLine =~ '{') + + if (s:IsAbsolutePath(l:sLastFoundPath) || len(l:aPaths) == 0) + call add(l:aPaths, s:ParsePath(l:sLastFoundPath)) + else + call add(l:aPaths, s:ParsePath(l:aPaths[len(l:aPaths) - 1].'/'.l:sLastFoundPath)) + endif + + let l:iOpenedBraces = l:iOpenedBraces + 1 + + if (l:boolInNeededProject && l:iOpenedBraces > l:iOpenedBracesAtProjectStart && isdirectory(l:aPaths[len(l:aPaths) - 1])) + call add(l:dResult[l:sCurProjName].paths, l:aPaths[len(l:aPaths) - 1]) + endif + + let sTmpLine = substitute(sTmpLine, '{', '', '') + endwhile + + " searching for filename + if (l:sLine =~ '^[^={}]*$' && l:sLine !~ '^\s*$') + " here we found something like filename + " + if (l:boolInNeededProject && (!g:indexer_enableWhenProjectDirFound || s:indexer_projectName != '') && l:iOpenedBraces > l:iOpenedBracesAtProjectStart) + " we are in needed project + "let l:sCurFilename = expand(s:ParsePath(l:aPaths[len(l:aPaths) - 1].'/'.s:Trim(l:sLine))) + " CHANGED! Jkooij + " expand() will change slashes based on 'shellslash' flag, + " so call s:ParsePath() on expand() result for consistent slashes + let l:sCurFilename = s:ParsePath(expand(l:aPaths[len(l:aPaths) - 1].'/'.s:Trim(l:sLine))) + if (filereadable(l:sCurFilename)) + " file readable! adding it + call add(l:dResult[l:sCurProjName].files, l:sCurFilename) + elseif (!isdirectory(l:sCurFilename)) + call add(l:dResult[l:sCurProjName].not_exist, l:sCurFilename) + endif + endif + + endif + + endfor + + return l:dResult +endfunction + +" returns whether or not file exists in list +function! s:IsFileExistsInList(aList, sFilename) + let l:sFilename = s:ParsePath(a:sFilename) + if (index(a:aList, l:sFilename, 0, 1)) >= 0 + return 1 + endif + return 0 +endfunction + +" updating tags using ctags. +" if boolAppend then just appends existing tags file with new tags from +" current file (%) +function! s:UpdateTags(boolAppend) + + " one tags file + + let l:sTagsFileWOPath = substitute(join(g:indexer_indexedProjects, '_'), '\s', '_', 'g') + let l:sTagsFile = s:tagsDirname.'/'.l:sTagsFileWOPath + if !isdirectory(s:tagsDirname) + call mkdir(s:tagsDirname, "p") + endif + + let l:sSavedFile = <SID>ParsePath(expand('%:p')) + if (<SID>IsFileExistsInList(s:lNotExistFiles, l:sSavedFile)) + " moving file from non-existing list to existing list + call remove(s:lNotExistFiles, index(s:lNotExistFiles, l:sSavedFile)) + call add(s:lFileList, l:sSavedFile) + endif + + let l:sRecurseCode = '' + + if (a:boolAppend && filereadable(l:sTagsFile)) + let l:sAppendCode = '-a' + if (<SID>IsFileExistsInList(s:lFileList, l:sSavedFile)) + " saved file are in file list + let l:sFiles = l:sSavedFile + else + let l:sFiles = '' + endif + + else + let l:sAppendCode = '' + let l:sFiles = '' + let l:sFile = '' + if (g:indexer_ctagsDontSpecifyFilesIfPossible && s:sMode == 'IndexerFile') + let l:sRecurseCode = '-R' + for l:sPath in s:lPathList + let l:sFiles = l:sFiles.' "'.l:sPath.'"' + endfor + else + for l:sFile in s:lFileList + let l:sFiles = l:sFiles.' "'.l:sFile.'"' + endfor + endif + endif + + if l:sFiles != '' + "if (has('win32') || has('win64')) + let l:sTagsFile = '"'.l:sTagsFile.'"' + "endif + + if (has('win32') || has('win64')) + let l:cmd = 'ctags -f '.l:sTagsFile.' '.l:sRecurseCode.' '.l:sAppendCode.' '.g:indexer_ctagsCommandLineOptions.' '.l:sFiles + else + let l:cmd = 'ctags -f '.l:sTagsFile.' '.l:sRecurseCode.' '.l:sAppendCode.' '.g:indexer_ctagsCommandLineOptions.' '.l:sFiles.' &' + endif + + let l:resp = system(l:cmd) + exec 'set tags='.substitute(s:tagsDirname.'/'.l:sTagsFileWOPath, ' ', '\\\\\\ ', 'g') + endif + + "multiple files, calls from bat file + "exec 'set tags=' + "let l:lLines = [] + "for l:sFile in s:lFileList + "let l:sTagFile = s:tagsDirname.'/'.substitute(l:sFile, '[\\/:]', '_', 'g') + "call add(l:lLines, 'ctags -f '.l:sTagFile.' '.g:indexer_ctagsCommandLineOptions.' '.l:sFile) + "exec 'set tags+='.l:sTagFile + "endfor + "call writefile(l:lLines, s:tagsDirname.'/maketags.bat') + + "let l:cmd = s:tagsDirname.'/maketags.bat' + "let l:resp = system(l:cmd) +endfunction + +function! s:ApplyProjectSettings(dParse) + " paths for Vim + set path=. + for l:sPath in a:dParse.paths + if isdirectory(l:sPath) + exec 'set path+='.substitute(l:sPath, ' ', '\\ ', 'g') + endif + endfor + + let s:lFileList = a:dParse.files + let s:lPathList = a:dParse.paths + let s:lNotExistFiles = a:dParse.not_exist + + augroup Indexer_SavSrcFile + autocmd! Indexer_SavSrcFile BufWritePost + augroup END + " collect extensions of files in project to make autocmd on save these + " files + let l:sExtsList = '' + let l:lFullList = s:lFileList + s:lNotExistFiles + for l:lFile in l:lFullList + let l:sExt = substitute(l:lFile, '^.*\([.\\/][^.\\/]\+\)$', '\1', '') + if strpart(l:sExt, 0, 1) != '.' + let l:sExt = strpart(l:sExt, 1) + endif + if (stridx(l:sExtsList, l:sExt) == -1) + if (l:sExtsList != '') + let l:sExtsList = l:sExtsList.',' + endif + let l:sExtsList = l:sExtsList.'*'.l:sExt + endif + endfor + + " defining autocmd at source files save + exec 'autocmd Indexer_SavSrcFile BufWritePost '.l:sExtsList.' call <SID>UpdateTags('.(g:indexer_ctagsJustAppendTagsAtFileSave ? '1' : '0').')' + + " start full tags update + call <SID>UpdateTags(0) +endfunction + +" concatenates two lists preventing duplicates +function! s:ConcatLists(lExistingList, lAddingList) + let l:lResList = a:lExistingList + for l:sItem in a:lAddingList + if (index(l:lResList, l:sItem) == -1) + call add(l:lResList, l:sItem) + endif + endfor + return l:lResList +endfunction + +function! s:GetDirsAndFilesFromAvailableFile() + if (filereadable(g:indexer_indexerListFilename)) + " read all projects from proj file + let s:sMode = 'IndexerFile' + let s:dParseAll = s:GetDirsAndFilesFromIndexerFile(g:indexer_indexerListFilename, s:indexer_projectName) + elseif (filereadable(g:indexer_projectsSettingsFilename)) + let s:sMode = 'ProjectFile' + let s:dParseAll = s:GetDirsAndFilesFromProjectFile(g:indexer_projectsSettingsFilename, s:indexer_projectName) + else + let s:sMode = '' + let s:dParseAll = {} + endif +endfunction + +function! s:ParseProjectSettingsFile() + call <SID>GetDirsAndFilesFromAvailableFile() + + " let's found what files we should to index. + " now we will search for project directory up by dir tree + let l:i = 0 + let l:sCurPath = '' + while (g:indexer_enableWhenProjectDirFound && s:indexer_projectName == '' && l:i < 10) + for l:sKey in keys(s:dParseAll) + if (<SID>IsFileExistsInList(s:dParseAll[l:sKey].paths, expand('%:p:h').l:sCurPath)) + let s:indexer_projectName = l:sKey + if !(s:sMode == 'IndexerFile' && g:indexer_ctagsDontSpecifyFilesIfPossible) + call <SID>GetDirsAndFilesFromAvailableFile() + else + call add(g:indexer_indexedProjects, l:sKey) + endif + break + endif + endfor + let l:sCurPath = l:sCurPath.'/..' + let l:i = l:i + 1 + endwhile + + if !(s:sMode == 'IndexerFile' && g:indexer_ctagsDontSpecifyFilesIfPossible) + let s:iTotalFilesAvailableCnt = 0 + if (!s:boolIndexingModeOn) + for l:sKey in keys(s:dParseAll) + let s:iTotalFilesAvailableCnt = s:iTotalFilesAvailableCnt + len(s:dParseAll[l:sKey].files) + + if ((g:indexer_enableWhenProjectDirFound && <SID>IsFileExistsInList(s:dParseAll[l:sKey].paths, expand('%:p:h'))) || (<SID>IsFileExistsInList(s:dParseAll[l:sKey].files, expand('%:p')))) + " user just opened file from project l:sKey. We should add it to + " result lists + + " adding name of this project to g:indexer_indexedProjects + call add(g:indexer_indexedProjects, l:sKey) + + endif + endfor + endif + endif + + " build final list of files, paths and non-existing files + let l:dParse = { 'files':[], 'paths':[], 'not_exist':[] } + for l:sKey in g:indexer_indexedProjects + let l:dParse.files = <SID>ConcatLists(l:dParse.files, s:dParseAll[l:sKey].files) + let l:dParse.paths = <SID>ConcatLists(l:dParse.paths, s:dParseAll[l:sKey].paths) + let l:dParse.not_exist = <SID>ConcatLists(l:dParse.not_exist, s:dParseAll[l:sKey].not_exist) + endfor + + if (s:boolIndexingModeOn) + call <SID>ApplyProjectSettings(l:dParse) + else + if (len(l:dParse.files) > 0 || len(l:dParse.paths) > 0) + + let s:boolIndexingModeOn = 1 + + " creating auto-refresh index at project file save + augroup Indexer_SavPrjFile + autocmd! Indexer_SavPrjFile BufWritePost + augroup END + + if (filereadable(g:indexer_indexerListFilename)) + let l:sIdxFile = substitute(g:indexer_indexerListFilename, '^.*[\\/]\([^\\/]\+\)$', '\1', '') + exec 'autocmd Indexer_SavPrjFile BufWritePost '.l:sIdxFile.' call <SID>ParseProjectSettingsFile()' + elseif (filereadable(g:indexer_projectsSettingsFilename)) + let l:sPrjFile = substitute(g:indexer_projectsSettingsFilename, '^.*[\\/]\([^\\/]\+\)$', '\1', '') + exec 'autocmd Indexer_SavPrjFile BufWritePost '.l:sPrjFile.' call <SID>ParseProjectSettingsFile()' + endif + + call <SID>ApplyProjectSettings(l:dParse) + + let l:iNonExistingCnt = len(s:lNotExistFiles) + if (l:iNonExistingCnt > 0) + if l:iNonExistingCnt < 100 + echo "Indexer Warning: project loaded, but there's ".l:iNonExistingCnt." non-existing files: \n\n".join(s:lNotExistFiles, "\n") + else + echo "Indexer Warning: project loaded, but there's ".l:iNonExistingCnt." non-existing files. Type :IndexerInfo for details." + endif + endif + else + " there's no project started. + " we should define autocmd to detect if file from project will be opened later + augroup Indexer_LoadFile + autocmd! Indexer_LoadFile BufReadPost + autocmd Indexer_LoadFile BufReadPost * call <SID>IndexerInit() + augroup END + endif + endif +endfunction + +function! s:IndexerInfo() + if (s:sMode == '') + echo '* Filelist: not found' + elseif (s:sMode == 'IndexerFile') + echo '* Filelist: indexer file: '.g:indexer_indexerListFilename + elseif (s:sMode == 'ProjectFile') + echo '* Filelist: project file: '.g:indexer_projectsSettingsFilename + else + echo '* Filelist: Unknown' + endif + echo '* Projects indexed: '.join(g:indexer_indexedProjects, ', ') + if !(s:sMode == 'IndexerFile' && g:indexer_ctagsDontSpecifyFilesIfPossible) + echo "* Files indexed: there's ".len(s:lFileList).' files. Type :IndexerFiles to list' + echo "* Files not found: there's ".len(s:lNotExistFiles).' non-existing files. '.join(s:lNotExistFiles, ', ') + endif + + echo '* Paths: '.&path + echo '* Tags file: '.&tags + echo '* Project root: '.($INDEXER_PROJECT_ROOT != '' ? $INDEXER_PROJECT_ROOT : 'not found').' (Project root is a directory which contains "'.g:indexer_dirNameForSearch.'" directory)' +endfunction + +function! s:IndexerFilesList() + echo "* Files indexed: ".join(s:lFileList, ', ') +endfunction + +function! s:IndexerInit() + + augroup Indexer_LoadFile + autocmd! Indexer_LoadFile BufReadPost + augroup END + + " actual tags dirname. If .vimprj directory will be found then this tags + " dirname will be /path/to/dir/.vimprj/tags + let s:tagsDirname = g:indexer_tagsDirname + let g:indexer_indexedProjects = [] + let s:sMode = '' + let s:lFileList = [] + let s:lNotExistFiles = [] + + let s:boolIndexingModeOn = 0 + + if g:indexer_lookForProjectDir + " need to look for .vimprj directory + + let l:i = 0 + let l:sCurPath = '' + let $INDEXER_PROJECT_ROOT = '' + while (l:i < g:indexer_recurseUpCount) + if (isdirectory(expand('%:p:h').l:sCurPath.'/'.g:indexer_dirNameForSearch)) + let $INDEXER_PROJECT_ROOT = simplify(expand('%:p:h').l:sCurPath) + exec 'cd '.substitute($INDEXER_PROJECT_ROOT, ' ', '\\ ', 'g') + break + endif + let l:sCurPath = l:sCurPath.'/..' + let l:i = l:i + 1 + endwhile + + if $INDEXER_PROJECT_ROOT != '' + " project root was found. + " + " set directory for tags in .vimprj dir + let s:tagsDirname = $INDEXER_PROJECT_ROOT.'/'.g:indexer_dirNameForSearch.'/tags' + + " sourcing all *vim files in .vimprj dir + let l:lSourceFilesList = split(glob($INDEXER_PROJECT_ROOT.'/'.g:indexer_dirNameForSearch.'/*vim'), '\n') + let l:sThisFile = expand('%:p') + for l:sFile in l:lSourceFilesList + if (l:sFile != l:sThisFile) + exec 'source '.l:sFile + endif + endfor + endif + + endif + + call s:ParseProjectSettingsFile() + +endfunction + + + + + + +" --------- init variables -------- +if !exists('g:indexer_lookForProjectDir') + let g:indexer_lookForProjectDir = 1 +endif + +if !exists('g:indexer_dirNameForSearch') + let g:indexer_dirNameForSearch = '.vimprj' +endif + +if !exists('g:indexer_recurseUpCount') + let g:indexer_recurseUpCount = 10 +endif + +if !exists('g:indexer_indexerListFilename') + let g:indexer_indexerListFilename = $HOME.'/.indexer_files' +endif + +if !exists('g:indexer_projectsSettingsFilename') + let g:indexer_projectsSettingsFilename = $HOME.'/.vimprojects' +endif + +if !exists('g:indexer_projectName') + let g:indexer_projectName = '' +endif + +if !exists('g:indexer_enableWhenProjectDirFound') + let g:indexer_enableWhenProjectDirFound = '1' +endif + +if !exists('g:indexer_tagsDirname') + let g:indexer_tagsDirname = $HOME.'/.vimtags' +endif + +if !exists('g:indexer_ctagsCommandLineOptions') + let g:indexer_ctagsCommandLineOptions = '--c++-kinds=+p+l --fields=+iaS --extra=+q' +endif + +if !exists('g:indexer_ctagsJustAppendTagsAtFileSave') + let g:indexer_ctagsJustAppendTagsAtFileSave = 1 +endif + +if !exists('g:indexer_ctagsDontSpecifyFilesIfPossible') + let g:indexer_ctagsDontSpecifyFilesIfPossible = '0' +endif + +let s:indexer_projectName = g:indexer_projectName + + +" -------- init commands --------- + +if exists(':IndexerInfo') != 2 + command -nargs=? -complete=file IndexerInfo call <SID>IndexerInfo() +endif +if exists(':IndexerFiles') != 2 + command -nargs=? -complete=file IndexerFiles call <SID>IndexerFilesList() +endif +if exists(':IndexerRebuild') != 2 + command -nargs=? -complete=file IndexerRebuild call <SID>UpdateTags(0) +endif + + + + + + +augroup Indexer_LoadFile + autocmd! Indexer_LoadFile BufReadPost + autocmd Indexer_LoadFile BufReadPost * call <SID>IndexerInit() +augroup END + +call <SID>IndexerInit() + diff --git a/modules/vim/vim.dot.link/plugin/project.vim b/modules/vim/vim.dot.link/plugin/project.vim new file mode 100644 index 0000000..47bd379 --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/project.vim @@ -0,0 +1,1293 @@ +"============================================================================= +" File: project.vim +" Author: Aric Blumer (Aric.Blumer at aricvim@charter.net) +" Last Change: Fri 13 Oct 2006 09:47:08 AM EDT +" Version: 1.4.1 +"============================================================================= +" See documentation in accompanying help file +" You may use this code in whatever way you see fit. + +if exists('loaded_project') || &cp + finish +endif +let loaded_project=1 + +function! s:Project(filename) " <<< + " Initialization <<< + if exists("g:proj_running") + if strlen(a:filename) != 0 + call confirm('Project already loaded; ignoring filename "'.a:filename."\".\n".'See ":help project-invoking" for information about changing project files.', "&OK", 1) + endif + let filename=bufname(g:proj_running) + else + if strlen(a:filename) == 0 + let filename ='~/.vimprojects' " Default project filename + else + let filename = a:filename + endif + endif + if !exists('g:proj_window_width') + let g:proj_window_width=24 " Default project window width + endif + if !exists('g:proj_window_increment') + let g:proj_window_increment=100 " Project Window width increment + endif + if !exists('g:proj_flags') + if has("win32") || has("mac") + let g:proj_flags='imst' " Project default flags for windows/mac + else + let g:proj_flags='imstb' " Project default flags for everything else + endif + endif + if !exists("g:proj_running") || (bufwinnr(g:proj_running) == -1) " Open the Project Window + exec 'silent vertical new '.filename + if match(g:proj_flags, '\CF') == -1 " We're floating + silent! wincmd H + exec 'vertical resize '.g:proj_window_width + endif + setlocal nomodeline + else + silent! 99wincmd h + if bufwinnr(g:proj_running) == -1 + vertical split + let v:errmsg="nothing" + silent! bnext + if 'nothing' != v:errmsg + enew + endif + endif + return + endif + " Process the flags + let b:proj_cd_cmd='cd' + if match(g:proj_flags, '\Cl') != -1 + let b:proj_cd_cmd = 'lcd' + endif + + let b:proj_locate_command='silent! wincmd H' + let b:proj_resize_command='exec ''vertical resize ''.g:proj_window_width' + if match(g:proj_flags, '\CF') != -1 " Set the resize commands to nothing + let b:proj_locate_command='' + let b:proj_resize_command='' + endif + + let g:proj_last_buffer = -1 + ">>> + " ProjFoldText() <<< + " The foldtext function for displaying just the description. + function! ProjFoldText() + let line=substitute(getline(v:foldstart),'^[ \t#]*\([^=]*\).*', '\1', '') + let line=strpart(' ', 0, (v:foldlevel - 1)).substitute(line,'\s*{\+\s*', '', '') + return line + endfunction ">>> + " s:DoSetup() <<< + " Ensure everything is set up + function! s:DoSetup() + setlocal foldenable foldmethod=marker foldmarker={,} commentstring=%s foldcolumn=0 nonumber noswapfile shiftwidth=1 + setlocal foldtext=ProjFoldText() nobuflisted nowrap + setlocal winwidth=1 + if match(g:proj_flags, '\Cn') != -1 + setlocal number + endif + endfunction ">>> + call s:DoSetup() + " Syntax Stuff <<< + if match(g:proj_flags, '\Cs')!=-1 && has('syntax') && exists('g:syntax_on') && !has('syntax_items') + syntax match projectDescriptionDir '^\s*.\{-}=\s*\(\\ \|\f\|:\|"\)\+' contains=projectDescription,projectWhiteError + syntax match projectDescription '\<.\{-}='he=e-1,me=e-1 contained nextgroup=projectDirectory contains=projectWhiteError + syntax match projectDescription '{\|}' + syntax match projectDirectory '=\(\\ \|\f\|:\)\+' contained + syntax match projectDirectory '=".\{-}"' contained + syntax match projectScriptinout '\<in\s*=\s*\(\\ \|\f\|:\|"\)\+' contains=projectDescription,projectWhiteError + syntax match projectScriptinout '\<out\s*=\s*\(\\ \|\f\|:\|"\)\+' contains=projectDescription,projectWhiteError + syntax match projectComment '#.*' + syntax match projectCD '\<CD\s*=\s*\(\\ \|\f\|:\|"\)\+' contains=projectDescription,projectWhiteError + syntax match projectFilterEntry '\<filter\s*=.*"' contains=projectWhiteError,projectFilterError,projectFilter,projectFilterRegexp + syntax match projectFilter '\<filter='he=e-1,me=e-1 contained nextgroup=projectFilterRegexp,projectFilterError,projectWhiteError + syntax match projectFlagsEntry '\<flags\s*=\( \|[^ ]*\)' contains=projectFlags,projectWhiteError + syntax match projectFlags '\<flags' contained nextgroup=projectFlagsValues,projectWhiteError + syntax match projectFlagsValues '=[^ ]* 'hs=s+1,me=e-1 contained contains=projectFlagsError + syntax match projectFlagsError '[^rtTsSwl= ]\+' contained + syntax match projectWhiteError '=\s\+'hs=s+1 contained + syntax match projectWhiteError '\s\+='he=e-1 contained + syntax match projectFilterError '=[^"]'hs=s+1 contained + syntax match projectFilterRegexp '=".*"'hs=s+1 contained + syntax match projectFoldText '^[^=]\+{' + + highlight def link projectDescription Identifier + highlight def link projectScriptinout Identifier + highlight def link projectFoldText Identifier + highlight def link projectComment Comment + highlight def link projectFilter Identifier + highlight def link projectFlags Identifier + highlight def link projectDirectory Constant + highlight def link projectFilterRegexp String + highlight def link projectFlagsValues String + highlight def link projectWhiteError Error + highlight def link projectFlagsError Error + highlight def link projectFilterError Error + endif ">>> + " s:SortR(start, end) <<< + " Sort lines. SortR() is called recursively. + " from ":help eval-examples" by Robert Webb, slightly modified + function! s:SortR(start, end) + if (a:start >= a:end) + return + endif + let partition = a:start - 1 + let middle = partition + let partStr = getline((a:start + a:end) / 2) + let i = a:start + while (i <= a:end) + let str = getline(i) + if str < partStr + let result = -1 + elseif str > partStr + let result = 1 + else + let result = 0 + endif + if (result <= 0) + let partition = partition + 1 + if (result == 0) + let middle = partition + endif + if (i != partition) + let str2 = getline(partition) + call setline(i, str2) + call setline(partition, str) + endif + endif + let i = i + 1 + endwhile + if (middle != partition) + let str = getline(middle) + let str2 = getline(partition) + call setline(middle, str2) + call setline(partition, str) + endif + call s:SortR(a:start, partition - 1) + call s:SortR(partition + 1, a:end) + endfunc ">>> + " s:IsAbsolutePath(path) <<< + " Returns true if filename has an absolute path. + function! s:IsAbsolutePath(path) + if a:path =~ '^ftp:' || a:path =~ '^rcp:' || a:path =~ '^scp:' || a:path =~ '^http:' + return 2 + endif + if a:path =~ '\$' + let path=expand(a:path) " Expand any environment variables that might be in the path + else + let path=a:path + endif + if path[0] == '/' || path[0] == '~' || path[0] == '\\' || path[1] == ':' + return 1 + endif + return 0 + endfunction " >>> + " s:DoSetupAndSplit() <<< + " Call DoSetup to ensure the settings are correct. Split to the next + " file. + function! s:DoSetupAndSplit() + call s:DoSetup() " Ensure that all the settings are right + let n = winnr() " Determine if there is a CTRL_W-p window + silent! wincmd p + if n == winnr() + silent! wincmd l + endif + if n == winnr() + " If n == winnr(), then there is no CTRL_W-p window + " So we have to create a new one + if bufnr('%') == g:proj_running + exec 'silent vertical new' + else + exec 'silent vertical split | silent! bnext' + endif + wincmd p " Go back to the Project Window and ensure it is the right width + exec b:proj_locate_command + exec b:proj_resize_command + wincmd p + endif + endfunction ">>> + " s:DoSetupAndSplit_au() <<< + " Same as above but ensure that the Project window is the current + " window. Only called from an autocommand + function! s:DoSetupAndSplit_au() + if winbufnr(0) != g:proj_running + return + endif + call s:DoSetup() " Ensure that all the settings are right + if winbufnr(2) == -1 " We're the only window right now. + exec 'silent vertical split | bnext' + if bufnr('%') == g:proj_running + enew + endif + if bufnr('%') == g:proj_last_buffer | bnext | bprev | bnext | endif + wincmd p " Go back to the Project Window and ensure it is the right width + exec b:proj_locate_command + exec b:proj_resize_command + elseif(winnr() != 1) + exec b:proj_locate_command + exec b:proj_resize_command + endif + endfunction + function! s:RecordPrevBuffer_au() + let g:proj_last_buffer = bufnr('%') + endfunction ">>> + " s:RecursivelyConstructDirectives(lineno) <<< + " Construct the inherited directives + function! s:RecursivelyConstructDirectives(lineno) + let lineno=s:FindFoldTop(a:lineno) + let foldlineno = lineno + let foldlev=foldlevel(lineno) + let parent_infoline = '' + if foldlev > 1 + while foldlevel(lineno) >= foldlev " Go to parent fold + if lineno < 1 + echoerr 'Some kind of fold error. Check your syntax.' + return + endif + let lineno = lineno - 1 + endwhile + let parent_infoline = s:RecursivelyConstructDirectives(lineno) + endif + let parent_home = s:GetHome(parent_infoline, '') + let parent_c_d = s:GetCd(parent_infoline, parent_home) + let parent_scriptin = s:GetScriptin(parent_infoline, parent_home) + let parent_scriptout = s:GetScriptout(parent_infoline, parent_home) + let parent_filter = s:GetFilter(parent_infoline, '*') + let infoline = getline(foldlineno) + " Extract the home directory of this fold + let home=s:GetHome(infoline, parent_home) + if home != '' + if (foldlevel(foldlineno) == 1) && !s:IsAbsolutePath(home) + call confirm('Outermost Project Fold must have absolute path! Or perhaps the path does not exist.', "&OK", 1) + let home = '~' " Some 'reasonable' value + endif + endif + " Extract any CD information + let c_d = s:GetCd(infoline, home) + if c_d != '' + if (foldlevel(foldlineno) == 1) && !s:IsAbsolutePath(c_d) + call confirm('Outermost Project Fold must have absolute CD path! Or perhaps the path does not exist.', "&OK", 1) + let c_d = '.' " Some 'reasonable' value + endif + else + let c_d=parent_c_d + endif + " Extract scriptin + let scriptin = s:GetScriptin(infoline, home) + if scriptin == '' + let scriptin = parent_scriptin + endif + " Extract scriptout + let scriptout = s:GetScriptout(infoline, home) + if scriptout == '' + let scriptout = parent_scriptout + endif + " Extract filter + let filter = s:GetFilter(infoline, parent_filter) + if filter == '' | let filter = parent_filter | endif + return s:ConstructInfo(home, c_d, scriptin, scriptout, '', filter) + endfunction ">>> + " s:ConstructInfo(home, c_d, scriptin, scriptout, flags, filter) <<< + function! s:ConstructInfo(home, c_d, scriptin, scriptout, flags, filter) + let retval='Directory='.a:home + if a:c_d[0] != '' + let retval=retval.' CD='.a:c_d + endif + if a:scriptin[0] != '' + let retval=retval.' in='.a:scriptin + endif + if a:scriptout[0] != '' + let retval=retval.' out='.a:scriptout + endif + if a:filter[0] != '' + let retval=retval.' filter="'.a:filter.'"' + endif + return retval + endfunction ">>> + " s:OpenEntry(line, precmd, editcmd) <<< + " Get the filename under the cursor, and open a window with it. + function! s:OpenEntry(line, precmd, editcmd, dir) + silent exec a:precmd + if (a:editcmd[0] != '') + if a:dir + let fname='.' + else + if (foldlevel(a:line) == 0) && (a:editcmd[0] != '') + return 0 " If we're outside a fold, do nothing + endif + let fname=substitute(getline(a:line), '\s*#.*', '', '') " Get rid of comments and whitespace before comment + let fname=substitute(fname, '^\s*\(.*\)', '\1', '') " Get rid of leading whitespace + if strlen(fname) == 0 + return 0 " The line is blank. Do nothing. + endif + endif + else + let fname='.' + endif + let infoline = s:RecursivelyConstructDirectives(a:line) + let retval=s:OpenEntry2(a:line, infoline, fname, a:editcmd) + call s:DisplayInfo() + return retval + endfunction + ">>> + " s:OpenEntry2(line, infoline, precmd, editcmd) <<< + " Get the filename under the cursor, and open a window with it. + function! s:OpenEntry2(line, infoline, fname, editcmd) + let fname=escape(a:fname, ' %#') " Thanks to Thomas Link for cluing me in on % and # + let home=s:GetHome(a:infoline, '').'/' + if home=='/' + echoerr 'Project structure error. Check your syntax.' + return + endif + "Save the cd command + let cd_cmd = b:proj_cd_cmd + if a:editcmd[0] != '' " If editcmd is '', then just set up the environment in the Project Window + call s:DoSetupAndSplit() + " If it is an absolute path, don't prepend home + if !s:IsAbsolutePath(fname) + let fname=home.fname + endif + if s:IsAbsolutePath(fname) == 2 + exec a:editcmd.' '.fname + else + silent exec 'silent '.a:editcmd.' '.fname + endif + else " only happens in the Project File + exec 'au! BufEnter,BufLeave '.expand('%:p') + endif + " Extract any CD information + let c_d = s:GetCd(a:infoline, home) + if c_d != '' && (s:IsAbsolutePath(home) != 2) + if match(g:proj_flags, '\CL') != -1 + call s:SetupAutoCommand(c_d) + endif + if !isdirectory(glob(c_d)) + call confirm("From this fold's entry,\nCD=".'"'.c_d.'" is not a valid directory.', "&OK", 1) + else + silent exec cd_cmd.' '.c_d + endif + endif + " Extract any scriptin information + let scriptin = s:GetScriptin(a:infoline, home) + if scriptin != '' + if !filereadable(glob(scriptin)) + call confirm('"'.scriptin.'" not found. Ignoring.', "&OK", 1) + else + call s:SetupScriptAutoCommand('BufEnter', scriptin) + exec 'source '.scriptin + endif + endif + let scriptout = s:GetScriptout(a:infoline, home) + if scriptout != '' + if !filereadable(glob(scriptout)) + call confirm('"'.scriptout.'" not found. Ignoring.', "&OK", 1) + else + call s:SetupScriptAutoCommand('BufLeave', scriptout) + endif + endif + return 1 + endfunction + ">>> + " s:DoFoldOrOpenEntry(cmd0, cmd1) <<< + " Used for double clicking. If the mouse is on a fold, open/close it. If + " not, try to open the file. + function! s:DoFoldOrOpenEntry(cmd0, cmd1) + if getline('.')=~'{\|}' || foldclosed('.') != -1 + normal! za + else + call s:DoEnsurePlacementSize_au() + call s:OpenEntry(line('.'), a:cmd0, a:cmd1, 0) + if (match(g:proj_flags, '\Cc') != -1) + let g:proj_mywinnumber = winbufnr(0) + Project + hide + if(g:proj_mywinnumber != winbufnr(0)) + wincmd p + endif + wincmd = + endif + endif + endfunction ">>> + " s:VimDirListing(filter, padding, separator, filevariable, filecount, dirvariable, dircount) <<< + function! s:VimDirListing(filter, padding, separator, filevariable, filecount, dirvariable, dircount) + let end = 0 + let files='' + let filter = a:filter + " Chop up the filter + " Apparently glob() cannot take something like this: glob('*.c *.h') + let while_var = 1 + while while_var + let end = stridx(filter, ' ') + if end == -1 + let end = strlen(filter) + let while_var = 0 + endif + let single=glob(strpart(filter, 0, end)) + if strlen(single) != 0 + let files = files.single."\010" + endif + let filter = strpart(filter, end + 1) + endwhile + " files now contains a list of everything in the directory. We need to + " weed out the directories. + let fnames=files + let {a:filevariable}='' + let {a:dirvariable}='' + let {a:filecount}=0 + let {a:dircount}=0 + while strlen(fnames) > 0 + let fname = substitute(fnames, '\(\(\f\|[ :\[\]]\)*\).*', '\1', '') + let fnames = substitute(fnames, '\(\f\|[ :\[\]]\)*.\(.*\)', '\2', '') + if isdirectory(glob(fname)) + let {a:dirvariable}={a:dirvariable}.a:padding.fname.a:separator + let {a:dircount}={a:dircount} + 1 + else + let {a:filevariable}={a:filevariable}.a:padding.fname.a:separator + let {a:filecount}={a:filecount} + 1 + endif + endwhile + endfunction ">>> + " s:GenerateEntry(recursive, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort) <<< + function! s:GenerateEntry(recursive, line, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort) + let line=a:line + if a:dir =~ '\\ ' + let dir='"'.substitute(a:dir, '\\ ', ' ', 'g').'"' + else + let dir=a:dir + endif + let spaces=strpart(' ', 0, a:foldlev) + let c_d=(strlen(a:c_d) > 0) ? 'CD='.a:c_d.' ' : '' + let c_d=(strlen(a:filter_directive) > 0) ? c_d.'filter="'.a:filter_directive.'" ': c_d + call append(line, spaces.'}') + call append(line, spaces.a:name.'='.dir.' '.c_d.'{') + if a:recursive + exec 'cd '.a:absolute_dir + call s:VimDirListing("*", '', "\010", 'b:files', 'b:filecount', 'b:dirs', 'b:dircount') + cd - + let dirs=b:dirs + let dcount=b:dircount + unlet b:files b:filecount b:dirs b:dircount + while dcount > 0 + let dname = substitute(dirs, '\(\( \|\f\|:\)*\).*', '\1', '') + let edname = escape(dname, ' ') + let dirs = substitute(dirs, '\( \|\f\|:\)*.\(.*\)', '\2', '') + let line=s:GenerateEntry(1, line + 1, dname, a:absolute_dir.'/'.edname, edname, '', '', a:filter, a:foldlev+1, a:sort) + let dcount=dcount-1 + endwhile + endif + return line+1 + endfunction " >>> + " s:DoEntryFromDir(line, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort) <<< + " Generate the fold from the directory hierarchy (if recursive), then + " fill it in with RefreshEntriesFromDir() + function! s:DoEntryFromDir(recursive, line, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort) + call s:GenerateEntry(a:recursive, a:line, a:name, escape(a:absolute_dir, ' '), escape(a:dir, ' '), escape(a:c_d, ' '), a:filter_directive, a:filter, a:foldlev, a:sort) + normal! j + call s:RefreshEntriesFromDir(1) + endfunction ">>> + " s:CreateEntriesFromDir(recursive) <<< + " Prompts user for information and then calls s:DoEntryFromDir() + function! s:CreateEntriesFromDir(recursive) + " Save a mark for the current cursor position + normal! mk + let line=line('.') + let name = inputdialog('Enter the Name of the Entry: ') + if strlen(name) == 0 + return + endif + let foldlev=foldlevel(line) + if (foldclosed(line) != -1) || (getline(line) =~ '}') + let foldlev=foldlev - 1 + endif + let absolute = (foldlev <= 0)?'Absolute ': '' + let home='' + let filter='*' + if (match(g:proj_flags, '\Cb') != -1) && has('browse') + " Note that browse() is inconsistent: On Win32 you can't select a + " directory, and it gives you a relative path. + let dir = browse(0, 'Enter the '.absolute.'Directory to Load: ', '', '') + let dir = fnamemodify(dir, ':p') + else + let dir = inputdialog('Enter the '.absolute.'Directory to Load: ', '') + endif + if (dir[strlen(dir)-1] == '/') || (dir[strlen(dir)-1] == '\\') + let dir=strpart(dir, 0, strlen(dir)-1) " Remove trailing / or \ + endif + let dir = substitute(dir, '^\~', $HOME, 'g') + if (foldlev > 0) + let parent_directive=s:RecursivelyConstructDirectives(line) + let filter = s:GetFilter(parent_directive, '*') + let home=s:GetHome(parent_directive, '') + if home[strlen(home)-1] != '/' && home[strlen(home)-1] != '\\' + let home=home.'/' + endif + unlet parent_directive + if s:IsAbsolutePath(dir) + " It is not a relative path Try to make it relative + let hend=matchend(dir, '\C'.glob(home)) + if hend != -1 + let dir=strpart(dir, hend) " The directory can be a relative path + else + let home="" + endif + endif + endif + if strlen(home.dir) == 0 + return + endif + if !isdirectory(home.dir) + if has("unix") + silent exec '!mkdir '.home.dir.' > /dev/null' + else + call confirm('"'.home.dir.'" is not a valid directory.', "&OK", 1) + return + endif + endif + let c_d = inputdialog('Enter the CD parameter: ', '') + let filter_directive = inputdialog('Enter the File Filter: ', '') + if strlen(filter_directive) != 0 + let filter = filter_directive + endif + " If I'm on a closed fold, go to the bottom of it + if foldclosedend(line) != -1 + let line = foldclosedend(line) + endif + let foldlev = foldlevel(line) + " If we're at the end of a fold . . . + if getline(line) =~ '}' + let foldlev = foldlev - 1 " . . . decrease the indentation by 1. + endif + " Do the work + call s:DoEntryFromDir(a:recursive, line, name, home.dir, dir, c_d, filter_directive, filter, foldlev, 0) + " Restore the cursor position + normal! `k + endfunction ">>> + " s:RefreshEntriesFromDir(recursive) <<< + " Finds metadata at the top of the fold, and then replaces all files + " with the contents of the directory. Works recursively if recursive is 1. + function! s:RefreshEntriesFromDir(recursive) + if foldlevel('.') == 0 + echo 'Nothing to refresh.' + return + endif + " Open the fold. + if getline('.') =~ '}' + normal! zo[z + else + normal! zo]z[z + endif + let just_a_fold=0 + let infoline = s:RecursivelyConstructDirectives(line('.')) + let immediate_infoline = getline('.') + if strlen(substitute(immediate_infoline, '[^=]*=\(\(\f\|:\|\\ \)*\).*', '\1', '')) == strlen(immediate_infoline) + let just_a_fold = 1 + endif + " Extract the home directory of the fold + let home = s:GetHome(infoline, '') + if home == '' + " No Match. This means that this is just a label with no + " directory entry. + if a:recursive == 0 + return " We're done--nothing to do + endif + " Mark that it is just a fold, so later we don't delete filenames + " that aren't there. + let just_a_fold = 1 + endif + if just_a_fold == 0 + " Extract the filter between quotes (we don't care what CD is). + let filter = s:GetFilter(infoline, '*') + " Extract the description (name) of the fold + let name = substitute(infoline, '^[#\t ]*\([^=]*\)=.*', '\1', '') + if strlen(name) == strlen(infoline) + return " If there's no name, we're done. + endif + if (home == '') || (name == '') + return + endif + " Extract the flags + let flags = s:GetFlags(immediate_infoline) + let sort = (match(g:proj_flags, '\CS') != -1) + if flags != '' + if match(flags, '\Cr') != -1 + " If the flags do not contain r (refresh), then treat it just + " like a fold + let just_a_fold = 1 + endif + if match(flags, '\CS') != -1 + let sort = 1 + endif + if match(flags, '\Cs') != -1 + let sort = 0 + endif + else + let flags='' + endif + endif + " Move to the first non-fold boundary line + normal! j + " Delete filenames until we reach the end of the fold + while getline('.') !~ '}' + if line('.') == line('$') + break + endif + if getline('.') !~ '{' + " We haven't reached a sub-fold, so delete what's there. + if (just_a_fold == 0) && (getline('.') !~ '^\s*#') && (getline('.') !~ '#.*pragma keep') + d _ + else + " Skip lines only in a fold and comment lines + normal! j + endif + else + " We have reached a sub-fold. If we're doing recursive, then + " call this function again. If not, find the end of the fold. + if a:recursive == 1 + call s:RefreshEntriesFromDir(1) + normal! ]zj + else + if foldclosed('.') == -1 + normal! zc + endif + normal! j + endif + endif + endwhile + if just_a_fold == 0 + " We're not just in a fold, and we have deleted all the filenames. + " Now it is time to regenerate what is in the directory. + if !isdirectory(glob(home)) + call confirm('"'.home.'" is not a valid directory.', "&OK", 1) + else + let foldlev=foldlevel('.') + " T flag. Thanks Tomas Z. + if (match(flags, '\Ct') != -1) || ((match(g:proj_flags, '\CT') == -1) && (match(flags, '\CT') == -1)) + " Go to the top of the fold (force other folds to the + " bottom) + normal! [z + normal! j + " Skip any comments + while getline('.') =~ '^\s*#' + normal! j + endwhile + endif + normal! k + let cwd=getcwd() + let spaces=strpart(' ', 0, foldlev) + exec 'cd '.home + if match(g:proj_flags, '\Ci') != -1 + echon home."\r" + endif + call s:VimDirListing(filter, spaces, "\n", 'b:files', 'b:filecount', 'b:dirs', 'b:dircount') + if b:filecount > 0 + normal! mk + silent! put =b:files + normal! `kj + if sort + call s:SortR(line('.'), line('.') + b:filecount - 1) + endif + else + normal! j + endif + unlet b:files b:filecount b:dirs b:dircount + exec 'cd '.cwd + endif + endif + " Go to the top of the refreshed fold. + normal! [z + endfunction ">>> + " s:MoveUp() <<< + " Moves the entity under the cursor up a line. + function! s:MoveUp() + let lineno=line('.') + if lineno == 1 + return + endif + let fc=foldclosed('.') + let a_reg=@a + if lineno == line('$') + normal! "add"aP + else + normal! "addk"aP + endif + let @a=a_reg + if fc != -1 + normal! zc + endif + endfunction ">>> + " s:MoveDown() <<< + " Moves the entity under the cursor down a line. + function! s:MoveDown() + let fc=foldclosed('.') + let a_reg=@a + normal! "add"ap + let @a=a_reg + if (fc != -1) && (foldclosed('.') == -1) + normal! zc + endif + endfunction " >>> + " s:DisplayInfo() <<< + " Displays filename and current working directory when i (info) is in + " the flags. + function! s:DisplayInfo() + if match(g:proj_flags, '\Ci') != -1 + echo 'file: '.expand('%').', cwd: '.getcwd().', lines: '.line('$') + endif + endfunction ">>> + " s:SetupAutoCommand(cwd) <<< + " Sets up an autocommand to ensure that the cwd is set to the one + " desired for the fold regardless. :lcd only does this on a per-window + " basis, not a per-buffer basis. + function! s:SetupAutoCommand(cwd) + if !exists("b:proj_has_autocommand") + let b:proj_cwd_save = escape(getcwd(), ' ') + let b:proj_has_autocommand = 1 + let bufname=escape(substitute(expand('%:p', 0), '\\', '/', 'g'), ' ') + exec 'au BufEnter '.bufname." let b:proj_cwd_save=escape(getcwd(), ' ') | cd ".a:cwd + exec 'au BufLeave '.bufname.' exec "cd ".b:proj_cwd_save' + exec 'au BufWipeout '.bufname.' au! * '.bufname + endif + endfunction ">>> + " s:SetupScriptAutoCommand(bufcmd, script) <<< + " Sets up an autocommand to run the scriptin script. + function! s:SetupScriptAutoCommand(bufcmd, script) + if !exists("b:proj_has_".a:bufcmd) + let b:proj_has_{a:bufcmd} = 1 + exec 'au '.a:bufcmd.' '.escape(substitute(expand('%:p', 0), '\\', '/', 'g'), ' ').' source '.a:script + endif + endfunction " >>> + " s:DoEnsurePlacementSize_au() <<< + " Ensure that the Project window is on the left of the window and has + " the correct size. Only called from an autocommand + function! s:DoEnsurePlacementSize_au() + if (winbufnr(0) != g:proj_running) || (winnr() != 1) + if exists("g:proj_doinghelp") + if g:proj_doinghelp > 0 + let g:proj_doinghelp = g:proj_doinghelp - 1 + return + endif + unlet g:proj_doinghelp + return + endif + exec b:proj_locate_command + endif + exec b:proj_resize_command + endfunction ">>> + " s:Spawn(number) <<< + " Spawn an external command on the file + function! s:Spawn(number) + echo | if exists("g:proj_run".a:number) + let fname=getline('.') + if fname!~'{\|}' + let fname=substitute(fname, '\s*#.*', '', '') + let fname=substitute(fname, '^\s*\(.*\)\s*', '\1', '') + if fname == '' | return | endif + let parent_infoline = s:RecursivelyConstructDirectives(line('.')) + let home=expand(s:GetHome(parent_infoline, '')) + let c_d=expand(s:GetCd(parent_infoline, '')) + let command=substitute(g:proj_run{a:number}, '%%', "\010", 'g') + let command=substitute(command, '%f', escape(home.'/'.fname, '\'), 'g') + let command=substitute(command, '%F', substitute(escape(home.'/'.fname, '\'), ' ', '\\\\ ', 'g'), 'g') + let command=substitute(command, '%s', escape(home.'/'.fname, '\'), 'g') + let command=substitute(command, '%n', escape(fname, '\'), 'g') + let command=substitute(command, '%N', substitute(fname, ' ', '\\\\ ', 'g'), 'g') + let command=substitute(command, '%h', escape(home, '\'), 'g') + let command=substitute(command, '%H', substitute(escape(home, '\'), ' ', '\\\\ ', 'g'), 'g') + if c_d != '' + if c_d == home + let percent_r='.' + else + let percent_r=substitute(home, escape(c_d.'/', '\'), '', 'g') + endif + else + let percent_r=home + endif + let command=substitute(command, '%r', percent_r, 'g') + let command=substitute(command, '%R', substitute(percent_r, ' ', '\\\\ ', 'g'), 'g') + let command=substitute(command, '%d', escape(c_d, '\'), 'g') + let command=substitute(command, '%D', substitute(escape(c_d, '\'), ' ', '\\\\ ', 'g'), 'g') + let command=substitute(command, "\010", '%', 'g') + exec command + endif + endif + endfunction ">>> + " s:ListSpawn(varnamesegment) <<< + " List external commands + function! s:ListSpawn(varnamesegment) + let number = 1 + while number < 10 + if exists("g:proj_run".a:varnamesegment.number) + echohl LineNr | echo number.':' | echohl None | echon ' '.substitute(escape(g:proj_run{a:varnamesegment}{number}, '\'), "\n", '\\n', 'g') + else + echohl LineNr | echo number.':' | echohl None + endif + let number=number + 1 + endwhile + endfunction ">>> + " s:FindFoldTop(line) <<< + " Return the line number of the directive line + function! s:FindFoldTop(line) + let lineno=a:line + if getline(lineno) =~ '}' + let lineno = lineno - 1 + endif + while getline(lineno) !~ '{' && lineno > 1 + if getline(lineno) =~ '}' + let lineno=s:FindFoldTop(lineno) + endif + let lineno = lineno - 1 + endwhile + return lineno + endfunction ">>> + " s:FindFoldBottom(line) <<< + " Return the line number of the directive line + function! s:FindFoldBottom(line) + let lineno=a:line + if getline(lineno) =~ '{' + let lineno=lineno + 1 + endif + while getline(lineno) !~ '}' && lineno < line('$') + if getline(lineno) =~ '{' + let lineno=s:FindFoldBottom(lineno) + endif + let lineno = lineno + 1 + endwhile + return lineno + endfunction ">>> + " s:LoadAll(recurse, line) <<< + " Load all files in a project + function! s:LoadAll(recurse, line) + let b:loadcount=0 + function! s:SpawnExec(infoline, fname, lineno, data) + if s:OpenEntry2(a:lineno, a:infoline, a:fname, 'e') + wincmd p + let b:loadcount=b:loadcount+1 + echon b:loadcount."\r" + if getchar(0) != 0 + let b:stop_everything=1 + endif + endif + endfunction + call Project_ForEach(a:recurse, line('.'), "*<SID>SpawnExec", 0, '^\(.*l\)\@!') + delfunction s:SpawnExec + echon b:loadcount." Files Loaded\r" + unlet b:loadcount + if exists("b:stop_everything") | unlet b:stop_everything | endif + endfunction ">>> + " s:WipeAll(recurse, line) <<< + " Wipe all files in a project + function! s:WipeAll(recurse, line) + let b:wipecount=0 + let b:totalcount=0 + function! s:SpawnExec(home, c_d, fname, lineno, data) + let fname=escape(a:fname, ' ') + if s:IsAbsolutePath(fname) + let fname=fnamemodify(fname, ':n') " :n is coming, won't break anything now + else + let fname=fnamemodify(a:home.'/'.fname, ':n') " :n is coming, won't break anything now + endif + let b:totalcount=b:totalcount+1 + let fname=substitute(fname, '^\~', $HOME, 'g') + if bufloaded(substitute(fname, '\\ ', ' ', 'g')) + if getbufvar(fname.'\>', '&modified') == 1 + exec 'sb '.fname + wincmd L + w + wincmd p + endif + let b:wipecount=b:wipecount+1 + exec 'bwipe! '.fname + endif + if b:totalcount % 5 == 0 + echon b:wipecount.' of '.b:totalcount."\r" + redraw + endif + if getchar(0) != 0 + let b:stop_everything=1 + endif + endfunction + call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", 0, '^\(.*w\)\@!') + delfunction s:SpawnExec + echon b:wipecount.' of '.b:totalcount." Files Wiped\r" + unlet b:wipecount b:totalcount + if exists("b:stop_everything") | unlet b:stop_everything | endif + endfunction ">>> + " s:LoadAllSplit(recurse, line) <<< + " Load all files in a project using split windows. + " Contributed by A. Harrison + function! s:LoadAllSplit(recurse, line) + let b:loadcount=0 + function! s:SpawnExec(infoline, fname, lineno, data) + let winNr = winnr() "get ProjectWindow number + if s:OpenEntry2(a:lineno, a:infoline, a:fname, 'sp') + exec winNr."wincmd w" + let b:loadcount=b:loadcount+1 + echon b:loadcount."\r" + if getchar(0) != 0 + let b:stop_everything=1 + endif + endif + endfunction + call Project_ForEach(a:recurse, line('.'), "*<SID>SpawnExec", 0, '^\(.*l\)\@!') + delfunction s:SpawnExec + echon b:loadcount." Files Loaded\r" + unlet b:loadcount + if exists("b:stop_everything") | unlet b:stop_everything | endif + endfunction ">>> + " s:GrepAll(recurse, lineno, pattern) <<< + " Grep all files in a project, optionally recursively + function! s:GrepAll(recurse, lineno, pattern) + cunmap <buffer> help + let pattern=(a:pattern[0] == '')?input("GREP options and pattern: "):a:pattern + cnoremap <buffer> help let g:proj_doinghelp = 1<CR>:help + if pattern[0] == '' + return + endif + let b:escape_spaces=1 + let fnames=Project_GetAllFnames(a:recurse, a:lineno, ' ') + unlet b:escape_spaces + cclose " Make sure grep window is closed + call s:DoSetupAndSplit() + if match(g:proj_flags, '\Cv') == -1 + silent! exec 'silent! grep '.pattern.' '.fnames + if v:shell_error != 0 + echo 'GREP error. Perhaps there are too many filenames.' + else + copen + endif + else + silent! exec 'silent! vimgrep '.pattern.' '.fnames + copen + endif + endfunction ">>> + " GetXXX Functions <<< + function! s:GetHome(info, parent_home) + " Thanks to Adam Montague for pointing out the need for @ in urls. + let home=substitute(a:info, '^[^=]*=\(\(\\ \|\f\|:\|@\)\+\).*', '\1', '') + if strlen(home) == strlen(a:info) + let home=substitute(a:info, '.\{-}"\(.\{-}\)".*', '\1', '') + if strlen(home) != strlen(a:info) | let home=escape(home, ' ') | endif + endif + if strlen(home) == strlen(a:info) + let home=a:parent_home + elseif home=='.' + let home=a:parent_home + elseif !s:IsAbsolutePath(home) + let home=a:parent_home.'/'.home + endif + return home + endfunction + function! s:GetFilter(info, parent_filter) + let filter = substitute(a:info, '.*\<filter="\([^"]*\).*', '\1', '') + if strlen(filter) == strlen(a:info) | let filter = a:parent_filter | endif + return filter + endfunction + function! s:GetCd(info, home) + let c_d=substitute(a:info, '.*\<CD=\(\(\\ \|\f\|:\)\+\).*', '\1', '') + if strlen(c_d) == strlen(a:info) + let c_d=substitute(a:info, '.*\<CD="\(.\{-}\)".*', '\1', '') + if strlen(c_d) != strlen(a:info) | let c_d=escape(c_d, ' ') | endif + endif + if strlen(c_d) == strlen(a:info) + let c_d='' + elseif c_d == '.' + let c_d = a:home + elseif !s:IsAbsolutePath(c_d) + let c_d = a:home.'/'.c_d + endif + return c_d + endfunction + function! s:GetScriptin(info, home) + let scriptin = substitute(a:info, '.*\<in=\(\(\\ \|\f\|:\)\+\).*', '\1', '') + if strlen(scriptin) == strlen(a:info) + let scriptin=substitute(a:info, '.*\<in="\(.\{-}\)".*', '\1', '') + if strlen(scriptin) != strlen(a:info) | let scriptin=escape(scriptin, ' ') | endif + endif + if strlen(scriptin) == strlen(a:info) | let scriptin='' | else + if !s:IsAbsolutePath(scriptin) | let scriptin=a:home.'/'.scriptin | endif | endif + return scriptin + endfunction + function! s:GetScriptout(info, home) + let scriptout = substitute(a:info, '.*\<out=\(\(\\ \|\f\|:\)\+\).*', '\1', '') + if strlen(scriptout) == strlen(a:info) + let scriptout=substitute(a:info, '.*\<out="\(.\{-}\)".*', '\1', '') + if strlen(scriptout) != strlen(a:info) | let scriptout=escape(scriptout, ' ') | endif + endif + if strlen(scriptout) == strlen(a:info) | let scriptout='' | else + if !s:IsAbsolutePath(scriptout) | let scriptout=a:home.'/'.scriptout | endif | endif + return scriptout + endfunction + function! s:GetFlags(info) + let flags=substitute(a:info, '.*\<flags=\([^ {]*\).*', '\1', '') + if (strlen(flags) == strlen(a:info)) + let flags='' + endif + return flags + endfunction ">>> + " Project_GetAllFnames(recurse, lineno, separator) <<< + " Grep all files in a project, optionally recursively + function! Project_GetAllFnames(recurse, lineno, separator) + let b:fnamelist='' + function! s:SpawnExec(home, c_d, fname, lineno, data) + if exists('b:escape_spaces') + let fname=escape(a:fname, ' ') + else + let fname=a:fname + endif + if !s:IsAbsolutePath(a:fname) + let fname=a:home.'/'.fname + endif + let b:fnamelist=b:fnamelist.a:data.fname + endfunction + call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", a:separator, '') + delfunction s:SpawnExec + let retval=b:fnamelist + unlet b:fnamelist + return retval + endfunction ">>> + " Project_GetAllFnames(recurse, lineno, separator) <<< + " Grep all files in a project, optionally recursively + function! Project_GetFname(line) + if (foldlevel(a:line) == 0) + return '' + endif + let fname=substitute(getline(a:line), '\s*#.*', '', '') " Get rid of comments and whitespace before comment + let fname=substitute(fname, '^\s*\(.*\)', '\1', '') " Get rid of leading whitespace + if strlen(fname) == 0 + return '' " The line is blank. Do nothing. + endif + if s:IsAbsolutePath(fname) + return fname + endif + let infoline = s:RecursivelyConstructDirectives(a:line) + return s:GetHome(infoline, '').'/'.fname + endfunction ">>> + " Project_ForEach(recurse, lineno, cmd, data, match) <<< + " Grep all files in a project, optionally recursively + function! Project_ForEach(recurse, lineno, cmd, data, match) + let info=s:RecursivelyConstructDirectives(a:lineno) + let lineno=s:FindFoldTop(a:lineno) + 1 + let flags=s:GetFlags(getline(lineno - 1)) + if (flags == '') || (a:match=='') || (match(flags, a:match) != -1) + call s:Project_ForEachR(a:recurse, lineno, info, a:cmd, a:data, a:match) + endif + endfunction + function! s:Project_ForEachR(recurse, lineno, info, cmd, data, match) + let home=s:GetHome(a:info, '') + let c_d=s:GetCd(a:info, home) + let scriptin = s:GetScriptin(a:info, home) + let scriptout = s:GetScriptout(a:info, home) + let filter = s:GetFilter(a:info, '') + let lineno = a:lineno + let curline=getline(lineno) + while (curline !~ '}') && (curline < line('$')) + if exists("b:stop_everything") && b:stop_everything | return 0 | endif + if curline =~ '{' + if a:recurse + let flags=s:GetFlags(curline) + if (flags == '') || (a:match=='') || (match(flags, a:match) != -1) + let this_home=s:GetHome(curline, home) + let this_cd=s:GetCd(curline, this_home) + if this_cd=='' | let this_cd=c_d | endif + let this_scriptin=s:GetScriptin(curline, this_home) + if this_scriptin == '' | let this_scriptin=scriptin | endif + let this_scriptout=s:GetScriptin(curline, this_home) + if this_scriptout == '' | let this_scriptout=scriptout | endif + let this_filter=s:GetFilter(curline, filter) + let lineno=s:Project_ForEachR(1, lineno+1, + \s:ConstructInfo(this_home, this_cd, this_scriptin, this_scriptout, flags, this_filter), a:cmd, a:data, a:match) + else + let lineno=s:FindFoldBottom(lineno) + endif + else + let lineno=s:FindFoldBottom(lineno) + endif + else + let fname=substitute(curline, '\s*#.*', '', '') + let fname=substitute(fname, '^\s*\(.*\)', '\1', '') + if (strlen(fname) != strlen(curline)) && (fname[0] != '') + if a:cmd[0] == '*' + call {strpart(a:cmd, 1)}(a:info, fname, lineno, a:data) + else + call {a:cmd}(home, c_d, fname, lineno, a:data) + endif + endif + endif + let lineno=lineno + 1 + let curline=getline(lineno) + endwhile + return lineno + endfunction ">>> + " s:SpawnAll(recurse, number) <<< + " Spawn an external command on the files of a project + function! s:SpawnAll(recurse, number) + echo | if exists("g:proj_run_fold".a:number) + if g:proj_run_fold{a:number}[0] == '*' + function! s:SpawnExec(home, c_d, fname, lineno, data) + let command=substitute(strpart(g:proj_run_fold{a:data}, 1), '%s', escape(a:fname, ' \'), 'g') + let command=substitute(command, '%f', escape(a:fname, '\'), 'g') + let command=substitute(command, '%h', escape(a:home, '\'), 'g') + let command=substitute(command, '%d', escape(a:c_d, '\'), 'g') + let command=substitute(command, '%F', substitute(escape(a:fname, '\'), ' ', '\\\\ ', 'g'), 'g') + exec command + endfunction + call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", a:number, '.') + delfunction s:SpawnExec + else + let info=s:RecursivelyConstructDirectives(line('.')) + let home=s:GetHome(info, '') + let c_d=s:GetCd(info, '') + let b:escape_spaces=1 + let fnames=Project_GetAllFnames(a:recurse, line('.'), ' ') + unlet b:escape_spaces + let command=substitute(g:proj_run_fold{a:number}, '%f', substitute(escape(fnames, '\'), '\\ ', ' ', 'g'), 'g') + let command=substitute(command, '%s', escape(fnames, '\'), 'g') + let command=substitute(command, '%h', escape(home, '\'), 'g') + let command=substitute(command, '%d', escape(c_d, '\'), 'g') + let command=substitute(command, '%F', escape(fnames, '\'), 'g') + exec command + if v:shell_error != 0 + echo 'Shell error. Perhaps there are too many filenames.' + endif + endif + endif + endfunction ">>> + if !exists("g:proj_running") + " s:DoProjectOnly(void) <<< + " Make the file window the only one. + function! s:DoProjectOnly() + if winbufnr(0) != g:proj_running + let lzsave=&lz + set lz + only + Project + silent! wincmd p + let &lz=lzsave + unlet lzsave + endif + endfunction + " >>> + + " Mappings <<< + nnoremap <buffer> <silent> <Return> \|:call <SID>DoFoldOrOpenEntry('', 'e')<CR> + nnoremap <buffer> <silent> <S-Return> \|:call <SID>DoFoldOrOpenEntry('', 'sp')<CR> + nnoremap <buffer> <silent> <C-Return> \|:call <SID>DoFoldOrOpenEntry('silent! only', 'e')<CR> + nnoremap <buffer> <silent> <LocalLeader>T \|:call <SID>DoFoldOrOpenEntry('', 'tabe')<CR> + nmap <buffer> <silent> <LocalLeader>s <S-Return> + nnoremap <buffer> <silent> <LocalLeader>S \|:call <SID>LoadAllSplit(0, line('.'))<CR> + nmap <buffer> <silent> <LocalLeader>o <C-Return> + nnoremap <buffer> <silent> <LocalLeader>i :echo <SID>RecursivelyConstructDirectives(line('.'))<CR> + nnoremap <buffer> <silent> <LocalLeader>I :echo Project_GetFname(line('.'))<CR> + nmap <buffer> <silent> <M-CR> <Return><C-W>p + nmap <buffer> <silent> <LocalLeader>v <M-CR> + nnoremap <buffer> <silent> <LocalLeader>l \|:call <SID>LoadAll(0, line('.'))<CR> + nnoremap <buffer> <silent> <LocalLeader>L \|:call <SID>LoadAll(1, line('.'))<CR> + nnoremap <buffer> <silent> <LocalLeader>w \|:call <SID>WipeAll(0, line('.'))<CR> + nnoremap <buffer> <silent> <LocalLeader>W \|:call <SID>WipeAll(1, line('.'))<CR> + nnoremap <buffer> <silent> <LocalLeader>W \|:call <SID>WipeAll(1, line('.'))<CR> + nnoremap <buffer> <silent> <LocalLeader>g \|:call <SID>GrepAll(0, line('.'), "")<CR> + nnoremap <buffer> <silent> <LocalLeader>G \|:call <SID>GrepAll(1, line('.'), "")<CR> + nnoremap <buffer> <silent> <2-LeftMouse> \|:call <SID>DoFoldOrOpenEntry('', 'e')<CR> + nnoremap <buffer> <silent> <S-2-LeftMouse> \|:call <SID>DoFoldOrOpenEntry('', 'sp')<CR> + nnoremap <buffer> <silent> <M-2-LeftMouse> <M-CR> + nnoremap <buffer> <silent> <S-LeftMouse> <LeftMouse> + nmap <buffer> <silent> <C-2-LeftMouse> <C-Return> + nnoremap <buffer> <silent> <C-LeftMouse> <LeftMouse> + nnoremap <buffer> <silent> <3-LeftMouse> <Nop> + nmap <buffer> <silent> <RightMouse> <space> + nmap <buffer> <silent> <2-RightMouse> <space> + nmap <buffer> <silent> <3-RightMouse> <space> + nmap <buffer> <silent> <4-RightMouse> <space> + nnoremap <buffer> <silent> <space> \|:silent exec 'vertical resize '.(match(g:proj_flags, '\Ct')!=-1 && winwidth('.') > g:proj_window_width?(g:proj_window_width):(winwidth('.') + g:proj_window_increment))<CR> + nnoremap <buffer> <silent> <C-Up> \|:silent call <SID>MoveUp()<CR> + nnoremap <buffer> <silent> <C-Down> \|:silent call <SID>MoveDown()<CR> + nmap <buffer> <silent> <LocalLeader><Up> <C-Up> + nmap <buffer> <silent> <LocalLeader><Down> <C-Down> + let k=1 + while k < 10 + exec 'nnoremap <buffer> <LocalLeader>'.k.' \|:call <SID>Spawn('.k.')<CR>' + exec 'nnoremap <buffer> <LocalLeader>f'.k.' \|:call <SID>SpawnAll(0, '.k.')<CR>' + exec 'nnoremap <buffer> <LocalLeader>F'.k.' \|:call <SID>SpawnAll(1, '.k.')<CR>' + let k=k+1 + endwhile + nnoremap <buffer> <LocalLeader>0 \|:call <SID>ListSpawn("")<CR> + nnoremap <buffer> <LocalLeader>f0 \|:call <SID>ListSpawn("_fold")<CR> + nnoremap <buffer> <LocalLeader>F0 \|:call <SID>ListSpawn("_fold")<CR> + nnoremap <buffer> <silent> <LocalLeader>c :call <SID>CreateEntriesFromDir(0)<CR> + nnoremap <buffer> <silent> <LocalLeader>C :call <SID>CreateEntriesFromDir(1)<CR> + nnoremap <buffer> <silent> <LocalLeader>r :call <SID>RefreshEntriesFromDir(0)<CR> + nnoremap <buffer> <silent> <LocalLeader>R :call <SID>RefreshEntriesFromDir(1)<CR> + " For Windows users: same as \R + nnoremap <buffer> <silent> <F5> :call <SID>RefreshEntriesFromDir(1)<CR> + nnoremap <buffer> <silent> <LocalLeader>e :call <SID>OpenEntry(line('.'), '', '', 0)<CR> + nnoremap <buffer> <silent> <LocalLeader>E :call <SID>OpenEntry(line('.'), '', 'e', 1)<CR> + " The :help command stomps on the Project Window. Try to avoid that. + " This is not perfect, but it is alot better than without the mappings. + cnoremap <buffer> help let g:proj_doinghelp = 1<CR>:help + nnoremap <buffer> <F1> :let g:proj_doinghelp = 1<CR><F1> + " This is to avoid changing the buffer, but it is not fool-proof. + nnoremap <buffer> <silent> <C-^> <Nop> + "nnoremap <script> <Plug>ProjectOnly :let lzsave=&lz<CR>:set lz<CR><C-W>o:Project<CR>:silent! wincmd p<CR>:let &lz=lzsave<CR>:unlet lzsave<CR> + nnoremap <script> <Plug>ProjectOnly :call <SID>DoProjectOnly()<CR> + if match(g:proj_flags, '\Cm') != -1 + if !hasmapto('<Plug>ProjectOnly') + nmap <silent> <unique> <C-W>o <Plug>ProjectOnly + nmap <silent> <unique> <C-W><C-O> <C-W>o + endif + endif " >>> + if filereadable(glob('~/.vimproject_mappings')) | source ~/.vimproject_mappings | endif + " Autocommands <<< + " Autocommands to clean up if we do a buffer wipe + " These don't work unless we substitute \ for / for Windows + let bufname=escape(substitute(expand('%:p', 0), '\\', '/', 'g'), ' ') + exec 'au BufWipeout '.bufname.' au! * '.bufname + exec 'au BufWipeout '.bufname.' unlet g:proj_running' + exec 'au BufWipeout '.bufname.' nunmap <C-W>o' + exec 'au BufWipeout '.bufname.' nunmap <C-W><C-O>' + " Autocommands to keep the window the specified size + exec 'au WinLeave '.bufname.' call s:DoEnsurePlacementSize_au()' + exec 'au BufEnter '.bufname.' call s:DoSetupAndSplit_au()' + au WinLeave * call s:RecordPrevBuffer_au() + " >>> + setlocal buflisted + let g:proj_running = bufnr(bufname.'\>') + if g:proj_running == -1 + call confirm('Project/Vim error. Please Enter :Project again and report this bug.', "&OK", 1) + unlet g:proj_running + endif + setlocal nobuflisted + endif +endfunction " >>> + +if exists(':Project') != 2 + command -nargs=? -complete=file Project call <SID>Project('<args>') +endif +" Toggle Mapping +if !exists("*<SID>DoToggleProject()") "<<< + function! s:DoToggleProject() + if !exists('g:proj_running') || bufwinnr(g:proj_running) == -1 + Project + else + let g:proj_mywindow = winnr() + Project + hide + if(winnr() != g:proj_mywindow) + wincmd p + endif + unlet g:proj_mywindow + endif + endfunction +endif ">>> +nnoremap <script> <Plug>ToggleProject :call <SID>DoToggleProject()<CR> +if exists('g:proj_flags') && (match(g:proj_flags, '\Cg') != -1) + if !hasmapto('<Plug>ToggleProject') + nmap <silent> <F12> <Plug>ToggleProject + endif +endif + +finish + +" vim600: set foldmethod=marker foldmarker=<<<,>>> foldlevel=1: diff --git a/modules/vim/vim.dot.link/plugin/snipMate.vim b/modules/vim/vim.dot.link/plugin/snipMate.vim new file mode 100644 index 0000000..3efee2a --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/snipMate.vim @@ -0,0 +1,247 @@ +" File: snipMate.vim +" Author: Michael Sanders +" Last Updated: July 13, 2009 +" Version: 0.83 +" Description: snipMate.vim implements some of TextMate's snippets features in +" Vim. A snippet is a piece of often-typed text that you can +" insert into your document using a trigger word followed by a "<tab>". +" +" For more help see snipMate.txt; you can do this by using: +" :helptags ~/.vim/doc +" :h snipMate.txt + +if exists('loaded_snips') || &cp || version < 700 + finish +endif +let loaded_snips = 1 +if !exists('snips_author') | let snips_author = 'Me' | endif + +au BufRead,BufNewFile *.snippets\= set ft=snippet +au FileType snippet setl noet fdm=indent + +let s:snippets = {} | let s:multi_snips = {} + +if !exists('snippets_dir') + let snippets_dir = substitute(globpath(&rtp, 'snippets/'), "\n", ',', 'g') +endif + +fun! MakeSnip(scope, trigger, content, ...) + let multisnip = a:0 && a:1 != '' + let var = multisnip ? 's:multi_snips' : 's:snippets' + if !has_key({var}, a:scope) | let {var}[a:scope] = {} | endif + if !has_key({var}[a:scope], a:trigger) + let {var}[a:scope][a:trigger] = multisnip ? [[a:1, a:content]] : a:content + elseif multisnip | let {var}[a:scope][a:trigger] += [[a:1, a:content]] + else + echom 'Warning in snipMate.vim: Snippet '.a:trigger.' is already defined.' + \ .' See :h multi_snip for help on snippets with multiple matches.' + endif +endf + +fun! ExtractSnips(dir, ft) + for path in split(globpath(a:dir, '*'), "\n") + if isdirectory(path) + let pathname = fnamemodify(path, ':t') + for snipFile in split(globpath(path, '*.snippet'), "\n") + call s:ProcessFile(snipFile, a:ft, pathname) + endfor + elseif fnamemodify(path, ':e') == 'snippet' + call s:ProcessFile(path, a:ft) + endif + endfor +endf + +" Processes a single-snippet file; optionally add the name of the parent +" directory for a snippet with multiple matches. +fun s:ProcessFile(file, ft, ...) + let keyword = fnamemodify(a:file, ':t:r') + if keyword == '' | return | endif + try + let text = join(readfile(a:file), "\n") + catch /E484/ + echom "Error in snipMate.vim: couldn't read file: ".a:file + endtry + return a:0 ? MakeSnip(a:ft, a:1, text, keyword) + \ : MakeSnip(a:ft, keyword, text) +endf + +fun! ExtractSnipsFile(file, ft) + if !filereadable(a:file) | return | endif + let text = readfile(a:file) + let inSnip = 0 + for line in text + ["\n"] + if inSnip && (line[0] == "\t" || line == '') + let content .= strpart(line, 1)."\n" + continue + elseif inSnip + call MakeSnip(a:ft, trigger, content[:-2], name) + let inSnip = 0 + endif + + if line[:6] == 'snippet' + let inSnip = 1 + let trigger = strpart(line, 8) + let name = '' + let space = stridx(trigger, ' ') + 1 + if space " Process multi snip + let name = strpart(trigger, space) + let trigger = strpart(trigger, 0, space - 1) + endif + let content = '' + endif + endfor +endf + +fun! ResetSnippets() + let s:snippets = {} | let s:multi_snips = {} | let g:did_ft = {} +endf + +let g:did_ft = {} +fun! GetSnippets(dir, filetypes) + for ft in split(a:filetypes, '\.') + if has_key(g:did_ft, ft) | continue | endif + call s:DefineSnips(a:dir, ft, ft) + if ft == 'objc' || ft == 'cpp' || ft == 'cs' + call s:DefineSnips(a:dir, 'c', ft) + elseif ft == 'xhtml' + call s:DefineSnips(a:dir, 'html', 'xhtml') + endif + let g:did_ft[ft] = 1 + endfor +endf + +" Define "aliasft" snippets for the filetype "realft". +fun s:DefineSnips(dir, aliasft, realft) + for path in split(globpath(a:dir, a:aliasft.'/')."\n". + \ globpath(a:dir, a:aliasft.'-*/'), "\n") + call ExtractSnips(path, a:realft) + endfor + for path in split(globpath(a:dir, a:aliasft.'.snippets')."\n". + \ globpath(a:dir, a:aliasft.'-*.snippets'), "\n") + call ExtractSnipsFile(path, a:realft) + endfor +endf + +fun! TriggerSnippet() + if exists('g:SuperTabMappingForward') + if g:SuperTabMappingForward == "<tab>" + let SuperTabKey = "\<c-n>" + elseif g:SuperTabMappingBackward == "<tab>" + let SuperTabKey = "\<c-p>" + endif + endif + + if pumvisible() " Update snippet if completion is used, or deal with supertab + if exists('SuperTabKey') + call feedkeys(SuperTabKey) | return '' + endif + call feedkeys("\<esc>a", 'n') " Close completion menu + call feedkeys("\<tab>") | return '' + endif + + if exists('g:snipPos') | return snipMate#jumpTabStop(0) | endif + + let word = matchstr(getline('.'), '\S\+\%'.col('.').'c') + for scope in [bufnr('%')] + split(&ft, '\.') + ['_'] + let [trigger, snippet] = s:GetSnippet(word, scope) + " If word is a trigger for a snippet, delete the trigger & expand + " the snippet. + if snippet != '' + let col = col('.') - len(trigger) + sil exe 's/\V'.escape(trigger, '/.').'\%#//' + return snipMate#expandSnip(snippet, col) + endif + endfor + + if exists('SuperTabKey') + call feedkeys(SuperTabKey) + return '' + endif + return "\<tab>" +endf + +fun! BackwardsSnippet() + if exists('g:snipPos') | return snipMate#jumpTabStop(1) | endif + + if exists('g:SuperTabMappingForward') + if g:SuperTabMappingBackward == "<s-tab>" + let SuperTabKey = "\<c-p>" + elseif g:SuperTabMappingForward == "<s-tab>" + let SuperTabKey = "\<c-n>" + endif + endif + if exists('SuperTabKey') + call feedkeys(SuperTabKey) + return '' + endif + return "\<s-tab>" +endf + +" Check if word under cursor is snippet trigger; if it isn't, try checking if +" the text after non-word characters is (e.g. check for "foo" in "bar.foo") +fun s:GetSnippet(word, scope) + let word = a:word | let snippet = '' + while snippet == '' + if exists('s:snippets["'.a:scope.'"]["'.escape(word, '\"').'"]') + let snippet = s:snippets[a:scope][word] + elseif exists('s:multi_snips["'.a:scope.'"]["'.escape(word, '\"').'"]') + let snippet = s:ChooseSnippet(a:scope, word) + if snippet == '' | break | endif + else + if match(word, '\W') == -1 | break | endif + let word = substitute(word, '.\{-}\W', '', '') + endif + endw + if word == '' && a:word != '.' && stridx(a:word, '.') != -1 + let [word, snippet] = s:GetSnippet('.', a:scope) + endif + return [word, snippet] +endf + +fun s:ChooseSnippet(scope, trigger) + let snippet = [] + let i = 1 + for snip in s:multi_snips[a:scope][a:trigger] + let snippet += [i.'. '.snip[0]] + let i += 1 + endfor + if i == 2 | return s:multi_snips[a:scope][a:trigger][0][1] | endif + let num = inputlist(snippet) - 1 + return num == -1 ? '' : s:multi_snips[a:scope][a:trigger][num][1] +endf + +fun! ShowAvailableSnips() + let line = getline('.') + let col = col('.') + let word = matchstr(getline('.'), '\S\+\%'.col.'c') + let words = [word] + if stridx(word, '.') + let words += split(word, '\.', 1) + endif + let matchlen = 0 + let matches = [] + for scope in [bufnr('%')] + split(&ft, '\.') + ['_'] + let triggers = has_key(s:snippets, scope) ? keys(s:snippets[scope]) : [] + if has_key(s:multi_snips, scope) + let triggers += keys(s:multi_snips[scope]) + endif + for trigger in triggers + for word in words + if word == '' + let matches += [trigger] " Show all matches if word is empty + elseif trigger =~ '^'.word + let matches += [trigger] + let len = len(word) + if len > matchlen | let matchlen = len | endif + endif + endfor + endfor + endfor + + " This is to avoid a bug with Vim when using complete(col - matchlen, matches) + " (Issue#46 on the Google Code snipMate issue tracker). + call setline(line('.'), substitute(line, repeat('.', matchlen).'\%'.col.'c', '', '')) + call complete(col, matches) + return '' +endf +" vim:noet:sw=4:ts=4:ft=vim diff --git a/modules/vim/vim.dot.link/plugin/startshell_mapping.vim.disabled b/modules/vim/vim.dot.link/plugin/startshell_mapping.vim.disabled new file mode 100644 index 0000000..e9704f4 --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/startshell_mapping.vim.disabled @@ -0,0 +1,36 @@ +"plugin from http://got-ravings.blogspot.com/2010/07/vim-pr0n-sample-nerd-tree-plugins.html +"guard against sourcing the script twice +if exists("g:loaded_nerdtree_start_shell_mapping") + finish +endif +let g:loaded_nerdtree_start_shell_mapping = 1 + +"bind 'S' to NERDTreeStartShell() +call NERDTreeAddKeyMap({ + \ 'key': 'S', + \ 'callback': 'NERDTreeStartShell', + \ 'quickhelpText': 'start a :shell in this dir' }) + +"change to the dir of the current node and start a :shell +function! NERDTreeStartShell() + + "grab the currently selected dir node (returns + "the parent dir if a file is selected) + let n = g:NERDTreeDirNode.GetSelected() + + "save the cwd so we can restore it after the :shell exits + let oldCWD = getcwd() + + try + + ":lcd to to the selected dir and :shell out + exec 'lcd ' . n.path.str({'format': 'Cd'}) + redraw! + shell + + "make sure we restore the cwd to its original state + finally + exec 'lcd ' . oldCWD + endtry + +endfunction diff --git a/modules/vim/vim.dot.link/plugin/taglist.vim b/modules/vim/vim.dot.link/plugin/taglist.vim new file mode 100644 index 0000000..59901f6 --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/taglist.vim @@ -0,0 +1,4546 @@ +" File: taglist.vim +" Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com) +" Version: 4.5 +" Last Modified: September 21, 2007 +" Copyright: Copyright (C) 2002-2007 Yegappan Lakshmanan +" Permission is hereby granted to use and distribute this code, +" with or without modifications, provided that this copyright +" notice is copied with it. Like anything else that's free, +" taglist.vim is provided *as is* and comes with no warranty of any +" kind, either expressed or implied. In no event will the copyright +" holder be liable for any damamges resulting from the use of this +" software. +" +" The "Tag List" plugin is a source code browser plugin for Vim and provides +" an overview of the structure of the programming language files and allows +" you to efficiently browse through source code files for different +" programming languages. You can visit the taglist plugin home page for more +" information: +" +" http://vim-taglist.sourceforge.net +" +" You can subscribe to the taglist mailing list to post your questions +" or suggestions for improvement or to report bugs. Visit the following +" page for subscribing to the mailing list: +" +" http://groups.yahoo.com/group/taglist/ +" +" For more information about using this plugin, after installing the +" taglist plugin, use the ":help taglist" command. +" +" Installation +" ------------ +" 1. Download the taglist.zip file and unzip the files to the $HOME/.vim +" or the $HOME/vimfiles or the $VIM/vimfiles directory. This should +" unzip the following two files (the directory structure should be +" preserved): +" +" plugin/taglist.vim - main taglist plugin file +" doc/taglist.txt - documentation (help) file +" +" Refer to the 'add-plugin', 'add-global-plugin' and 'runtimepath' +" Vim help pages for more details about installing Vim plugins. +" 2. Change to the $HOME/.vim/doc or $HOME/vimfiles/doc or +" $VIM/vimfiles/doc directory, start Vim and run the ":helptags ." +" command to process the taglist help file. +" 3. If the exuberant ctags utility is not present in your PATH, then set the +" Tlist_Ctags_Cmd variable to point to the location of the exuberant ctags +" utility (not to the directory) in the .vimrc file. +" 4. If you are running a terminal/console version of Vim and the +" terminal doesn't support changing the window width then set the +" 'Tlist_Inc_Winwidth' variable to 0 in the .vimrc file. +" 5. Restart Vim. +" 6. You can now use the ":TlistToggle" command to open/close the taglist +" window. You can use the ":help taglist" command to get more +" information about using the taglist plugin. +" +" ****************** Do not modify after this line ************************ + +" Line continuation used here +let s:cpo_save = &cpo +set cpo&vim + +if !exists('loaded_taglist') + " First time loading the taglist plugin + " + " To speed up the loading of Vim, the taglist plugin uses autoload + " mechanism to load the taglist functions. + " Only define the configuration variables, user commands and some + " auto-commands and finish sourcing the file + + " The taglist plugin requires the built-in Vim system() function. If this + " function is not available, then don't load the plugin. + if !exists('*system') + echomsg 'Taglist: Vim system() built-in function is not available. ' . + \ 'Plugin is not loaded.' + let loaded_taglist = 'no' + let &cpo = s:cpo_save + finish + endif + + " Location of the exuberant ctags tool + if !exists('Tlist_Ctags_Cmd') + if executable('exuberant-ctags') + " On Debian Linux, exuberant ctags is installed + " as exuberant-ctags + let Tlist_Ctags_Cmd = 'exuberant-ctags' + elseif executable('exctags') + " On Free-BSD, exuberant ctags is installed as exctags + let Tlist_Ctags_Cmd = 'exctags' + elseif executable('ctags') + let Tlist_Ctags_Cmd = 'ctags' + elseif executable('ctags.exe') + let Tlist_Ctags_Cmd = 'ctags.exe' + elseif executable('tags') + let Tlist_Ctags_Cmd = 'tags' + else + echomsg 'Taglist: Exuberant ctags (http://ctags.sf.net) ' . + \ 'not found in PATH. Plugin is not loaded.' + " Skip loading the plugin + let loaded_taglist = 'no' + let &cpo = s:cpo_save + finish + endif + endif + + + " Automatically open the taglist window on Vim startup + if !exists('Tlist_Auto_Open') + let Tlist_Auto_Open = 0 + endif + + " When the taglist window is toggle opened, move the cursor to the + " taglist window + if !exists('Tlist_GainFocus_On_ToggleOpen') + let Tlist_GainFocus_On_ToggleOpen = 0 + endif + + " Process files even when the taglist window is not open + if !exists('Tlist_Process_File_Always') + let Tlist_Process_File_Always = 0 + endif + + if !exists('Tlist_Show_Menu') + let Tlist_Show_Menu = 0 + endif + + " Tag listing sort type - 'name' or 'order' + if !exists('Tlist_Sort_Type') + let Tlist_Sort_Type = 'order' + endif + + " Tag listing window split (horizontal/vertical) control + if !exists('Tlist_Use_Horiz_Window') + let Tlist_Use_Horiz_Window = 0 + endif + + " Open the vertically split taglist window on the left or on the right + " side. This setting is relevant only if Tlist_Use_Horiz_Window is set to + " zero (i.e. only for vertically split windows) + if !exists('Tlist_Use_Right_Window') + let Tlist_Use_Right_Window = 0 + endif + + " Increase Vim window width to display vertically split taglist window. + " For MS-Windows version of Vim running in a MS-DOS window, this must be + " set to 0 otherwise the system may hang due to a Vim limitation. + if !exists('Tlist_Inc_Winwidth') + if (has('win16') || has('win95')) && !has('gui_running') + let Tlist_Inc_Winwidth = 0 + else + let Tlist_Inc_Winwidth = 1 + endif + endif + + " Vertically split taglist window width setting + if !exists('Tlist_WinWidth') + let Tlist_WinWidth = 30 + endif + + " Horizontally split taglist window height setting + if !exists('Tlist_WinHeight') + let Tlist_WinHeight = 10 + endif + + " Display tag prototypes or tag names in the taglist window + if !exists('Tlist_Display_Prototype') + let Tlist_Display_Prototype = 0 + endif + + " Display tag scopes in the taglist window + if !exists('Tlist_Display_Tag_Scope') + let Tlist_Display_Tag_Scope = 1 + endif + + " Use single left mouse click to jump to a tag. By default this is disabled. + " Only double click using the mouse will be processed. + if !exists('Tlist_Use_SingleClick') + let Tlist_Use_SingleClick = 0 + endif + + " Control whether additional help is displayed as part of the taglist or + " not. Also, controls whether empty lines are used to separate the tag + " tree. + if !exists('Tlist_Compact_Format') + let Tlist_Compact_Format = 0 + endif + + " Exit Vim if only the taglist window is currently open. By default, this is + " set to zero. + if !exists('Tlist_Exit_OnlyWindow') + let Tlist_Exit_OnlyWindow = 0 + endif + + " Automatically close the folds for the non-active files in the taglist + " window + if !exists('Tlist_File_Fold_Auto_Close') + let Tlist_File_Fold_Auto_Close = 0 + endif + + " Close the taglist window when a tag is selected + if !exists('Tlist_Close_On_Select') + let Tlist_Close_On_Select = 0 + endif + + " Automatically update the taglist window to display tags for newly + " edited files + if !exists('Tlist_Auto_Update') + let Tlist_Auto_Update = 1 + endif + + " Automatically highlight the current tag + if !exists('Tlist_Auto_Highlight_Tag') + let Tlist_Auto_Highlight_Tag = 1 + endif + + " Automatically highlight the current tag on entering a buffer + if !exists('Tlist_Highlight_Tag_On_BufEnter') + let Tlist_Highlight_Tag_On_BufEnter = 1 + endif + + " Enable fold column to display the folding for the tag tree + if !exists('Tlist_Enable_Fold_Column') + let Tlist_Enable_Fold_Column = 1 + endif + + " Display the tags for only one file in the taglist window + if !exists('Tlist_Show_One_File') + let Tlist_Show_One_File = 0 + endif + + if !exists('Tlist_Max_Submenu_Items') + let Tlist_Max_Submenu_Items = 20 + endif + + if !exists('Tlist_Max_Tag_Length') + let Tlist_Max_Tag_Length = 10 + endif + + " Do not change the name of the taglist title variable. The winmanager + " plugin relies on this name to determine the title for the taglist + " plugin. + let TagList_title = "__Tag_List__" + + " Taglist debug messages + let s:tlist_msg = '' + + " Define the taglist autocommand to automatically open the taglist window + " on Vim startup + if g:Tlist_Auto_Open + autocmd VimEnter * nested call s:Tlist_Window_Check_Auto_Open() + endif + + " Refresh the taglist + if g:Tlist_Process_File_Always + autocmd BufEnter * call s:Tlist_Refresh() + endif + + if g:Tlist_Show_Menu + autocmd GUIEnter * call s:Tlist_Menu_Init() + endif + + " When the taglist buffer is created when loading a Vim session file, + " the taglist buffer needs to be initialized. The BufFilePost event + " is used to handle this case. + autocmd BufFilePost __Tag_List__ call s:Tlist_Vim_Session_Load() + + " Define the user commands to manage the taglist window + command! -nargs=0 -bar TlistToggle call s:Tlist_Window_Toggle() + command! -nargs=0 -bar TlistOpen call s:Tlist_Window_Open() + " For backwards compatiblity define the Tlist command + command! -nargs=0 -bar Tlist TlistToggle + command! -nargs=+ -complete=file TlistAddFiles + \ call s:Tlist_Add_Files(<f-args>) + command! -nargs=+ -complete=dir TlistAddFilesRecursive + \ call s:Tlist_Add_Files_Recursive(<f-args>) + command! -nargs=0 -bar TlistClose call s:Tlist_Window_Close() + command! -nargs=0 -bar TlistUpdate call s:Tlist_Update_Current_File() + command! -nargs=0 -bar TlistHighlightTag call s:Tlist_Window_Highlight_Tag( + \ fnamemodify(bufname('%'), ':p'), line('.'), 2, 1) + " For backwards compatiblity define the TlistSync command + command! -nargs=0 -bar TlistSync TlistHighlightTag + command! -nargs=* -complete=buffer TlistShowPrototype + \ echo Tlist_Get_Tag_Prototype_By_Line(<f-args>) + command! -nargs=* -complete=buffer TlistShowTag + \ echo Tlist_Get_Tagname_By_Line(<f-args>) + command! -nargs=* -complete=file TlistSessionLoad + \ call s:Tlist_Session_Load(<q-args>) + command! -nargs=* -complete=file TlistSessionSave + \ call s:Tlist_Session_Save(<q-args>) + command! -bar TlistLock let Tlist_Auto_Update=0 + command! -bar TlistUnlock let Tlist_Auto_Update=1 + + " Commands for enabling/disabling debug and to display debug messages + command! -nargs=? -complete=file -bar TlistDebug + \ call s:Tlist_Debug_Enable(<q-args>) + command! -nargs=0 -bar TlistUndebug call s:Tlist_Debug_Disable() + command! -nargs=0 -bar TlistMessages call s:Tlist_Debug_Show() + + " Define autocommands to autoload the taglist plugin when needed. + + " Trick to get the current script ID + map <SID>xx <SID>xx + let s:tlist_sid = substitute(maparg('<SID>xx'), '<SNR>\(\d\+_\)xx$', + \ '\1', '') + unmap <SID>xx + + exe 'autocmd FuncUndefined *' . s:tlist_sid . 'Tlist_* source ' . + \ escape(expand('<sfile>'), ' ') + exe 'autocmd FuncUndefined *' . s:tlist_sid . 'Tlist_Window_* source ' . + \ escape(expand('<sfile>'), ' ') + exe 'autocmd FuncUndefined *' . s:tlist_sid . 'Tlist_Menu_* source ' . + \ escape(expand('<sfile>'), ' ') + exe 'autocmd FuncUndefined Tlist_* source ' . + \ escape(expand('<sfile>'), ' ') + exe 'autocmd FuncUndefined TagList_* source ' . + \ escape(expand('<sfile>'), ' ') + + let loaded_taglist = 'fast_load_done' + + if g:Tlist_Show_Menu && has('gui_running') + call s:Tlist_Menu_Init() + endif + + " restore 'cpo' + let &cpo = s:cpo_save + finish +endif + +if !exists('s:tlist_sid') + " Two or more versions of taglist plugin are installed. Don't + " load this version of the plugin. + finish +endif + +unlet! s:tlist_sid + +if loaded_taglist != 'fast_load_done' + " restore 'cpo' + let &cpo = s:cpo_save + finish +endif + +" Taglist plugin functionality is available +let loaded_taglist = 'available' + +"------------------- end of user configurable options -------------------- + +" Default language specific settings for supported file types and tag types +" +" Variable name format: +" +" s:tlist_def_{vim_ftype}_settings +" +" vim_ftype - Filetype detected by Vim +" +" Value format: +" +" <ctags_ftype>;<flag>:<name>;<flag>:<name>;... +" +" ctags_ftype - File type supported by exuberant ctags +" flag - Flag supported by exuberant ctags to generate a tag type +" name - Name of the tag type used in the taglist window to display the +" tags of this type +" + +" assembly language +let s:tlist_def_asm_settings = 'asm;d:define;l:label;m:macro;t:type' + +" aspperl language +let s:tlist_def_aspperl_settings = 'asp;f:function;s:sub;v:variable' + +" aspvbs language +let s:tlist_def_aspvbs_settings = 'asp;f:function;s:sub;v:variable' + +" awk language +let s:tlist_def_awk_settings = 'awk;f:function' + +" beta language +let s:tlist_def_beta_settings = 'beta;f:fragment;s:slot;v:pattern' + +" c language +let s:tlist_def_c_settings = 'c;d:macro;g:enum;s:struct;u:union;t:typedef;' . + \ 'v:variable;f:function' + +" c++ language +let s:tlist_def_cpp_settings = 'c++;n:namespace;v:variable;d:macro;t:typedef;' . + \ 'c:class;g:enum;s:struct;u:union;f:function' + +" c# language +let s:tlist_def_cs_settings = 'c#;d:macro;t:typedef;n:namespace;c:class;' . + \ 'E:event;g:enum;s:struct;i:interface;' . + \ 'p:properties;m:method' + +" cobol language +let s:tlist_def_cobol_settings = 'cobol;d:data;f:file;g:group;p:paragraph;' . + \ 'P:program;s:section' + +" eiffel language +let s:tlist_def_eiffel_settings = 'eiffel;c:class;f:feature' + +" erlang language +let s:tlist_def_erlang_settings = 'erlang;d:macro;r:record;m:module;f:function' + +" expect (same as tcl) language +let s:tlist_def_expect_settings = 'tcl;c:class;f:method;p:procedure' + +" fortran language +let s:tlist_def_fortran_settings = 'fortran;p:program;b:block data;' . + \ 'c:common;e:entry;i:interface;k:type;l:label;m:module;' . + \ 'n:namelist;t:derived;v:variable;f:function;s:subroutine' + +" HTML language +let s:tlist_def_html_settings = 'html;a:anchor;f:javascript function' + +" java language +let s:tlist_def_java_settings = 'java;p:package;c:class;i:interface;' . + \ 'f:field;m:method' + +" javascript language +let s:tlist_def_javascript_settings = 'javascript;f:function' + +" lisp language +let s:tlist_def_lisp_settings = 'lisp;f:function' + +" lua language +let s:tlist_def_lua_settings = 'lua;f:function' + +" makefiles +let s:tlist_def_make_settings = 'make;m:macro' + +" pascal language +let s:tlist_def_pascal_settings = 'pascal;f:function;p:procedure' + +" perl language +let s:tlist_def_perl_settings = 'perl;c:constant;l:label;p:package;s:subroutine' + +" php language +let s:tlist_def_php_settings = 'php;c:class;d:constant;v:variable;f:function' + +" python language +let s:tlist_def_python_settings = 'python;c:class;m:member;f:function' + +" rexx language +let s:tlist_def_rexx_settings = 'rexx;s:subroutine' + +" ruby language +let s:tlist_def_ruby_settings = 'ruby;c:class;f:method;F:function;' . + \ 'm:singleton method' + +" scheme language +let s:tlist_def_scheme_settings = 'scheme;s:set;f:function' + +" shell language +let s:tlist_def_sh_settings = 'sh;f:function' + +" C shell language +let s:tlist_def_csh_settings = 'sh;f:function' + +" Z shell language +let s:tlist_def_zsh_settings = 'sh;f:function' + +" slang language +let s:tlist_def_slang_settings = 'slang;n:namespace;f:function' + +" sml language +let s:tlist_def_sml_settings = 'sml;e:exception;c:functor;s:signature;' . + \ 'r:structure;t:type;v:value;f:function' + +" sql language +let s:tlist_def_sql_settings = 'sql;c:cursor;F:field;P:package;r:record;' . + \ 's:subtype;t:table;T:trigger;v:variable;f:function;p:procedure' + +" tcl language +let s:tlist_def_tcl_settings = 'tcl;c:class;f:method;m:method;p:procedure' + +" vera language +let s:tlist_def_vera_settings = 'vera;c:class;d:macro;e:enumerator;' . + \ 'f:function;g:enum;m:member;p:program;' . + \ 'P:prototype;t:task;T:typedef;v:variable;' . + \ 'x:externvar' + +"verilog language +let s:tlist_def_verilog_settings = 'verilog;m:module;c:constant;P:parameter;' . + \ 'e:event;r:register;t:task;w:write;p:port;v:variable;f:function' + +" vim language +let s:tlist_def_vim_settings = 'vim;a:autocmds;v:variable;f:function' + +" yacc language +let s:tlist_def_yacc_settings = 'yacc;l:label' + +"------------------- end of language specific options -------------------- + +" Vim window size is changed by the taglist plugin or not +let s:tlist_winsize_chgd = -1 +" Taglist window is maximized or not +let s:tlist_win_maximized = 0 +" Name of files in the taglist +let s:tlist_file_names='' +" Number of files in the taglist +let s:tlist_file_count = 0 +" Number of filetypes supported by taglist +let s:tlist_ftype_count = 0 +" Is taglist part of other plugins like winmanager or cream? +let s:tlist_app_name = "none" +" Are we displaying brief help text +let s:tlist_brief_help = 1 +" List of files removed on user request +let s:tlist_removed_flist = "" +" Index of current file displayed in the taglist window +let s:tlist_cur_file_idx = -1 +" Taglist menu is empty or not +let s:tlist_menu_empty = 1 + +" An autocommand is used to refresh the taglist window when entering any +" buffer. We don't want to refresh the taglist window if we are entering the +" file window from one of the taglist functions. The 'Tlist_Skip_Refresh' +" variable is used to skip the refresh of the taglist window and is set +" and cleared appropriately. +let s:Tlist_Skip_Refresh = 0 + +" Tlist_Window_Display_Help() +function! s:Tlist_Window_Display_Help() + if s:tlist_app_name == "winmanager" + " To handle a bug in the winmanager plugin, add a space at the + " last line + call setline('$', ' ') + endif + + if s:tlist_brief_help + " Add the brief help + call append(0, '" Press <F1> to display help text') + else + " Add the extensive help + call append(0, '" <enter> : Jump to tag definition') + call append(1, '" o : Jump to tag definition in new window') + call append(2, '" p : Preview the tag definition') + call append(3, '" <space> : Display tag prototype') + call append(4, '" u : Update tag list') + call append(5, '" s : Select sort field') + call append(6, '" d : Remove file from taglist') + call append(7, '" x : Zoom-out/Zoom-in taglist window') + call append(8, '" + : Open a fold') + call append(9, '" - : Close a fold') + call append(10, '" * : Open all folds') + call append(11, '" = : Close all folds') + call append(12, '" [[ : Move to the start of previous file') + call append(13, '" ]] : Move to the start of next file') + call append(14, '" q : Close the taglist window') + call append(15, '" <F1> : Remove help text') + endif +endfunction + +" Tlist_Window_Toggle_Help_Text() +" Toggle taglist plugin help text between the full version and the brief +" version +function! s:Tlist_Window_Toggle_Help_Text() + if g:Tlist_Compact_Format + " In compact display mode, do not display help + return + endif + + " Include the empty line displayed after the help text + let brief_help_size = 1 + let full_help_size = 16 + + setlocal modifiable + + " Set report option to a huge value to prevent informational messages + " while deleting the lines + let old_report = &report + set report=99999 + + " Remove the currently highlighted tag. Otherwise, the help text + " might be highlighted by mistake + match none + + " Toggle between brief and full help text + if s:tlist_brief_help + let s:tlist_brief_help = 0 + + " Remove the previous help + exe '1,' . brief_help_size . ' delete _' + + " Adjust the start/end line numbers for the files + call s:Tlist_Window_Update_Line_Offsets(0, 1, full_help_size - brief_help_size) + else + let s:tlist_brief_help = 1 + + " Remove the previous help + exe '1,' . full_help_size . ' delete _' + + " Adjust the start/end line numbers for the files + call s:Tlist_Window_Update_Line_Offsets(0, 0, full_help_size - brief_help_size) + endif + + call s:Tlist_Window_Display_Help() + + " Restore the report option + let &report = old_report + + setlocal nomodifiable +endfunction + +" Taglist debug support +let s:tlist_debug = 0 + +" File for storing the debug messages +let s:tlist_debug_file = '' + +" Tlist_Debug_Enable +" Enable logging of taglist debug messages. +function! s:Tlist_Debug_Enable(...) + let s:tlist_debug = 1 + + " Check whether a valid file name is supplied. + if a:1 != '' + let s:tlist_debug_file = fnamemodify(a:1, ':p') + + " Empty the log file + exe 'redir! > ' . s:tlist_debug_file + redir END + + " Check whether the log file is present/created + if !filewritable(s:tlist_debug_file) + call s:Tlist_Warning_Msg('Taglist: Unable to create log file ' + \ . s:tlist_debug_file) + let s:tlist_debug_file = '' + endif + endif +endfunction + +" Tlist_Debug_Disable +" Disable logging of taglist debug messages. +function! s:Tlist_Debug_Disable(...) + let s:tlist_debug = 0 + let s:tlist_debug_file = '' +endfunction + +" Tlist_Debug_Show +" Display the taglist debug messages in a new window +function! s:Tlist_Debug_Show() + if s:tlist_msg == '' + call s:Tlist_Warning_Msg('Taglist: No debug messages') + return + endif + + " Open a new window to display the taglist debug messages + new taglist_debug.txt + " Delete all the lines (if the buffer already exists) + silent! %delete _ + " Add the messages + silent! put =s:tlist_msg + " Move the cursor to the first line + normal! gg +endfunction + +" Tlist_Log_Msg +" Log the supplied debug message along with the time +function! s:Tlist_Log_Msg(msg) + if s:tlist_debug + if s:tlist_debug_file != '' + exe 'redir >> ' . s:tlist_debug_file + silent echon strftime('%H:%M:%S') . ': ' . a:msg . "\n" + redir END + else + " Log the message into a variable + " Retain only the last 3000 characters + let len = strlen(s:tlist_msg) + if len > 3000 + let s:tlist_msg = strpart(s:tlist_msg, len - 3000) + endif + let s:tlist_msg = s:tlist_msg . strftime('%H:%M:%S') . ': ' . + \ a:msg . "\n" + endif + endif +endfunction + +" Tlist_Warning_Msg() +" Display a message using WarningMsg highlight group +function! s:Tlist_Warning_Msg(msg) + echohl WarningMsg + echomsg a:msg + echohl None +endfunction + +" Last returned file index for file name lookup. +" Used to speed up file lookup +let s:tlist_file_name_idx_cache = -1 + +" Tlist_Get_File_Index() +" Return the index of the specified filename +function! s:Tlist_Get_File_Index(fname) + if s:tlist_file_count == 0 || a:fname == '' + return -1 + endif + + " If the new filename is same as the last accessed filename, then + " return that index + if s:tlist_file_name_idx_cache != -1 && + \ s:tlist_file_name_idx_cache < s:tlist_file_count + if s:tlist_{s:tlist_file_name_idx_cache}_filename == a:fname + " Same as the last accessed file + return s:tlist_file_name_idx_cache + endif + endif + + " First, check whether the filename is present + let s_fname = a:fname . "\n" + let i = stridx(s:tlist_file_names, s_fname) + if i == -1 + let s:tlist_file_name_idx_cache = -1 + return -1 + endif + + " Second, compute the file name index + let nl_txt = substitute(strpart(s:tlist_file_names, 0, i), "[^\n]", '', 'g') + let s:tlist_file_name_idx_cache = strlen(nl_txt) + return s:tlist_file_name_idx_cache +endfunction + +" Last returned file index for line number lookup. +" Used to speed up file lookup +let s:tlist_file_lnum_idx_cache = -1 + +" Tlist_Window_Get_File_Index_By_Linenum() +" Return the index of the filename present in the specified line number +" Line number refers to the line number in the taglist window +function! s:Tlist_Window_Get_File_Index_By_Linenum(lnum) + call s:Tlist_Log_Msg('Tlist_Window_Get_File_Index_By_Linenum (' . a:lnum . ')') + + " First try to see whether the new line number is within the range + " of the last returned file + if s:tlist_file_lnum_idx_cache != -1 && + \ s:tlist_file_lnum_idx_cache < s:tlist_file_count + if a:lnum >= s:tlist_{s:tlist_file_lnum_idx_cache}_start && + \ a:lnum <= s:tlist_{s:tlist_file_lnum_idx_cache}_end + return s:tlist_file_lnum_idx_cache + endif + endif + + let fidx = -1 + + if g:Tlist_Show_One_File + " Displaying only one file in the taglist window. Check whether + " the line is within the tags displayed for that file + if s:tlist_cur_file_idx != -1 + if a:lnum >= s:tlist_{s:tlist_cur_file_idx}_start + \ && a:lnum <= s:tlist_{s:tlist_cur_file_idx}_end + let fidx = s:tlist_cur_file_idx + endif + + endif + else + " Do a binary search in the taglist + let left = 0 + let right = s:tlist_file_count - 1 + + while left < right + let mid = (left + right) / 2 + + if a:lnum >= s:tlist_{mid}_start && a:lnum <= s:tlist_{mid}_end + let s:tlist_file_lnum_idx_cache = mid + return mid + endif + + if a:lnum < s:tlist_{mid}_start + let right = mid - 1 + else + let left = mid + 1 + endif + endwhile + + if left >= 0 && left < s:tlist_file_count + \ && a:lnum >= s:tlist_{left}_start + \ && a:lnum <= s:tlist_{left}_end + let fidx = left + endif + endif + + let s:tlist_file_lnum_idx_cache = fidx + + return fidx +endfunction + +" Tlist_Exe_Cmd_No_Acmds +" Execute the specified Ex command after disabling autocommands +function! s:Tlist_Exe_Cmd_No_Acmds(cmd) + let old_eventignore = &eventignore + set eventignore=all + exe a:cmd + let &eventignore = old_eventignore +endfunction + +" Tlist_Skip_File() +" Check whether tag listing is supported for the specified file +function! s:Tlist_Skip_File(filename, ftype) + " Skip buffers with no names and buffers with filetype not set + if a:filename == '' || a:ftype == '' + return 1 + endif + + " Skip files which are not supported by exuberant ctags + " First check whether default settings for this filetype are available. + " If it is not available, then check whether user specified settings are + " available. If both are not available, then don't list the tags for this + " filetype + let var = 's:tlist_def_' . a:ftype . '_settings' + if !exists(var) + let var = 'g:tlist_' . a:ftype . '_settings' + if !exists(var) + return 1 + endif + endif + + " Skip files which are not readable or files which are not yet stored + " to the disk + if !filereadable(a:filename) + return 1 + endif + + return 0 +endfunction + +" Tlist_User_Removed_File +" Returns 1 if a file is removed by a user from the taglist +function! s:Tlist_User_Removed_File(filename) + return stridx(s:tlist_removed_flist, a:filename . "\n") != -1 +endfunction + +" Tlist_Update_Remove_List +" Update the list of user removed files from the taglist +" add == 1, add the file to the removed list +" add == 0, delete the file from the removed list +function! s:Tlist_Update_Remove_List(filename, add) + if a:add + let s:tlist_removed_flist = s:tlist_removed_flist . a:filename . "\n" + else + let idx = stridx(s:tlist_removed_flist, a:filename . "\n") + let text_before = strpart(s:tlist_removed_flist, 0, idx) + let rem_text = strpart(s:tlist_removed_flist, idx) + let next_idx = stridx(rem_text, "\n") + let text_after = strpart(rem_text, next_idx + 1) + + let s:tlist_removed_flist = text_before . text_after + endif +endfunction + +" Tlist_FileType_Init +" Initialize the ctags arguments and tag variable for the specified +" file type +function! s:Tlist_FileType_Init(ftype) + call s:Tlist_Log_Msg('Tlist_FileType_Init (' . a:ftype . ')') + " If the user didn't specify any settings, then use the default + " ctags args. Otherwise, use the settings specified by the user + let var = 'g:tlist_' . a:ftype . '_settings' + if exists(var) + " User specified ctags arguments + let settings = {var} . ';' + else + " Default ctags arguments + let var = 's:tlist_def_' . a:ftype . '_settings' + if !exists(var) + " No default settings for this file type. This filetype is + " not supported + return 0 + endif + let settings = s:tlist_def_{a:ftype}_settings . ';' + endif + + let msg = 'Taglist: Invalid ctags option setting - ' . settings + + " Format of the option that specifies the filetype and ctags arugments: + " + " <language_name>;flag1:name1;flag2:name2;flag3:name3 + " + + " Extract the file type to pass to ctags. This may be different from the + " file type detected by Vim + let pos = stridx(settings, ';') + if pos == -1 + call s:Tlist_Warning_Msg(msg) + return 0 + endif + let ctags_ftype = strpart(settings, 0, pos) + if ctags_ftype == '' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + " Make sure a valid filetype is supplied. If the user didn't specify a + " valid filetype, then the ctags option settings may be treated as the + " filetype + if ctags_ftype =~ ':' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + + " Remove the file type from settings + let settings = strpart(settings, pos + 1) + if settings == '' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + + " Process all the specified ctags flags. The format is + " flag1:name1;flag2:name2;flag3:name3 + let ctags_flags = '' + let cnt = 0 + while settings != '' + " Extract the flag + let pos = stridx(settings, ':') + if pos == -1 + call s:Tlist_Warning_Msg(msg) + return 0 + endif + let flag = strpart(settings, 0, pos) + if flag == '' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + " Remove the flag from settings + let settings = strpart(settings, pos + 1) + + " Extract the tag type name + let pos = stridx(settings, ';') + if pos == -1 + call s:Tlist_Warning_Msg(msg) + return 0 + endif + let name = strpart(settings, 0, pos) + if name == '' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + let settings = strpart(settings, pos + 1) + + let cnt = cnt + 1 + + let s:tlist_{a:ftype}_{cnt}_name = flag + let s:tlist_{a:ftype}_{cnt}_fullname = name + let ctags_flags = ctags_flags . flag + endwhile + + let s:tlist_{a:ftype}_ctags_args = '--language-force=' . ctags_ftype . + \ ' --' . ctags_ftype . '-types=' . ctags_flags + let s:tlist_{a:ftype}_count = cnt + let s:tlist_{a:ftype}_ctags_flags = ctags_flags + + " Save the filetype name + let s:tlist_ftype_{s:tlist_ftype_count}_name = a:ftype + let s:tlist_ftype_count = s:tlist_ftype_count + 1 + + return 1 +endfunction + +" Tlist_Detect_Filetype +" Determine the filetype for the specified file using the filetypedetect +" autocmd. +function! s:Tlist_Detect_Filetype(fname) + " Ignore the filetype autocommands + let old_eventignore = &eventignore + set eventignore=FileType + + " Save the 'filetype', as this will be changed temporarily + let old_filetype = &filetype + + " Run the filetypedetect group of autocommands to determine + " the filetype + exe 'doautocmd filetypedetect BufRead ' . a:fname + + " Save the detected filetype + let ftype = &filetype + + " Restore the previous state + let &filetype = old_filetype + let &eventignore = old_eventignore + + return ftype +endfunction + +" Tlist_Get_Buffer_Filetype +" Get the filetype for the specified buffer +function! s:Tlist_Get_Buffer_Filetype(bnum) + let buf_ft = getbufvar(a:bnum, '&filetype') + + if bufloaded(a:bnum) + " For loaded buffers, the 'filetype' is already determined + return buf_ft + endif + + " For unloaded buffers, if the 'filetype' option is set, return it + if buf_ft != '' + return buf_ft + endif + + " Skip non-existent buffers + if !bufexists(a:bnum) + return '' + endif + + " For buffers whose filetype is not yet determined, try to determine + " the filetype + let bname = bufname(a:bnum) + + return s:Tlist_Detect_Filetype(bname) +endfunction + +" Tlist_Discard_TagInfo +" Discard the stored tag information for a file +function! s:Tlist_Discard_TagInfo(fidx) + call s:Tlist_Log_Msg('Tlist_Discard_TagInfo (' . + \ s:tlist_{a:fidx}_filename . ')') + let ftype = s:tlist_{a:fidx}_filetype + + " Discard information about the tags defined in the file + let i = 1 + while i <= s:tlist_{a:fidx}_tag_count + let fidx_i = 's:tlist_' . a:fidx . '_' . i + unlet! {fidx_i}_tag + unlet! {fidx_i}_tag_name + unlet! {fidx_i}_tag_type + unlet! {fidx_i}_ttype_idx + unlet! {fidx_i}_tag_proto + unlet! {fidx_i}_tag_searchpat + unlet! {fidx_i}_tag_linenum + let i = i + 1 + endwhile + + let s:tlist_{a:fidx}_tag_count = 0 + + " Discard information about tag type groups + let i = 1 + while i <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{i}_name + if s:tlist_{a:fidx}_{ttype} != '' + let fidx_ttype = 's:tlist_' . a:fidx . '_' . ttype + let {fidx_ttype} = '' + let {fidx_ttype}_offset = 0 + let cnt = {fidx_ttype}_count + let {fidx_ttype}_count = 0 + let j = 1 + while j <= cnt + unlet! {fidx_ttype}_{j} + let j = j + 1 + endwhile + endif + let i = i + 1 + endwhile + + " Discard the stored menu command also + let s:tlist_{a:fidx}_menu_cmd = '' +endfunction + +" Tlist_Window_Update_Line_Offsets +" Update the line offsets for tags for files starting from start_idx +" and displayed in the taglist window by the specified offset +function! s:Tlist_Window_Update_Line_Offsets(start_idx, increment, offset) + let i = a:start_idx + + while i < s:tlist_file_count + if s:tlist_{i}_visible + " Update the start/end line number only if the file is visible + if a:increment + let s:tlist_{i}_start = s:tlist_{i}_start + a:offset + let s:tlist_{i}_end = s:tlist_{i}_end + a:offset + else + let s:tlist_{i}_start = s:tlist_{i}_start - a:offset + let s:tlist_{i}_end = s:tlist_{i}_end - a:offset + endif + endif + let i = i + 1 + endwhile +endfunction + +" Tlist_Discard_FileInfo +" Discard the stored information for a file +function! s:Tlist_Discard_FileInfo(fidx) + call s:Tlist_Log_Msg('Tlist_Discard_FileInfo (' . + \ s:tlist_{a:fidx}_filename . ')') + call s:Tlist_Discard_TagInfo(a:fidx) + + let ftype = s:tlist_{a:fidx}_filetype + + let i = 1 + while i <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{i}_name + unlet! s:tlist_{a:fidx}_{ttype} + unlet! s:tlist_{a:fidx}_{ttype}_offset + unlet! s:tlist_{a:fidx}_{ttype}_count + let i = i + 1 + endwhile + + unlet! s:tlist_{a:fidx}_filename + unlet! s:tlist_{a:fidx}_sort_type + unlet! s:tlist_{a:fidx}_filetype + unlet! s:tlist_{a:fidx}_mtime + unlet! s:tlist_{a:fidx}_start + unlet! s:tlist_{a:fidx}_end + unlet! s:tlist_{a:fidx}_valid + unlet! s:tlist_{a:fidx}_visible + unlet! s:tlist_{a:fidx}_tag_count + unlet! s:tlist_{a:fidx}_menu_cmd +endfunction + +" Tlist_Window_Remove_File_From_Display +" Remove the specified file from display +function! s:Tlist_Window_Remove_File_From_Display(fidx) + call s:Tlist_Log_Msg('Tlist_Window_Remove_File_From_Display (' . + \ s:tlist_{a:fidx}_filename . ')') + " If the file is not visible then no need to remove it + if !s:tlist_{a:fidx}_visible + return + endif + + " Remove the tags displayed for the specified file from the window + let start = s:tlist_{a:fidx}_start + " Include the empty line after the last line also + if g:Tlist_Compact_Format + let end = s:tlist_{a:fidx}_end + else + let end = s:tlist_{a:fidx}_end + 1 + endif + + setlocal modifiable + exe 'silent! ' . start . ',' . end . 'delete _' + setlocal nomodifiable + + " Correct the start and end line offsets for all the files following + " this file, as the tags for this file are removed + call s:Tlist_Window_Update_Line_Offsets(a:fidx + 1, 0, end - start + 1) +endfunction + +" Tlist_Remove_File +" Remove the file under the cursor or the specified file index +" user_request - User requested to remove the file from taglist +function! s:Tlist_Remove_File(file_idx, user_request) + let fidx = a:file_idx + + if fidx == -1 + let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.')) + if fidx == -1 + return + endif + endif + call s:Tlist_Log_Msg('Tlist_Remove_File (' . + \ s:tlist_{fidx}_filename . ', ' . a:user_request . ')') + + let save_winnr = winnr() + let winnum = bufwinnr(g:TagList_title) + if winnum != -1 + " Taglist window is open, remove the file from display + + if save_winnr != winnum + let old_eventignore = &eventignore + set eventignore=all + exe winnum . 'wincmd w' + endif + + call s:Tlist_Window_Remove_File_From_Display(fidx) + + if save_winnr != winnum + exe save_winnr . 'wincmd w' + let &eventignore = old_eventignore + endif + endif + + let fname = s:tlist_{fidx}_filename + + if a:user_request + " As the user requested to remove the file from taglist, + " add it to the removed list + call s:Tlist_Update_Remove_List(fname, 1) + endif + + " Remove the file name from the taglist list of filenames + let idx = stridx(s:tlist_file_names, fname . "\n") + let text_before = strpart(s:tlist_file_names, 0, idx) + let rem_text = strpart(s:tlist_file_names, idx) + let next_idx = stridx(rem_text, "\n") + let text_after = strpart(rem_text, next_idx + 1) + let s:tlist_file_names = text_before . text_after + + call s:Tlist_Discard_FileInfo(fidx) + + " Shift all the file variables by one index + let i = fidx + 1 + + while i < s:tlist_file_count + let j = i - 1 + + let s:tlist_{j}_filename = s:tlist_{i}_filename + let s:tlist_{j}_sort_type = s:tlist_{i}_sort_type + let s:tlist_{j}_filetype = s:tlist_{i}_filetype + let s:tlist_{j}_mtime = s:tlist_{i}_mtime + let s:tlist_{j}_start = s:tlist_{i}_start + let s:tlist_{j}_end = s:tlist_{i}_end + let s:tlist_{j}_valid = s:tlist_{i}_valid + let s:tlist_{j}_visible = s:tlist_{i}_visible + let s:tlist_{j}_tag_count = s:tlist_{i}_tag_count + let s:tlist_{j}_menu_cmd = s:tlist_{i}_menu_cmd + + let k = 1 + while k <= s:tlist_{j}_tag_count + let s:tlist_{j}_{k}_tag = s:tlist_{i}_{k}_tag + let s:tlist_{j}_{k}_tag_name = s:tlist_{i}_{k}_tag_name + let s:tlist_{j}_{k}_tag_type = s:Tlist_Get_Tag_Type_By_Tag(i, k) + let s:tlist_{j}_{k}_ttype_idx = s:tlist_{i}_{k}_ttype_idx + let s:tlist_{j}_{k}_tag_proto = s:Tlist_Get_Tag_Prototype(i, k) + let s:tlist_{j}_{k}_tag_searchpat = s:Tlist_Get_Tag_SearchPat(i, k) + let s:tlist_{j}_{k}_tag_linenum = s:Tlist_Get_Tag_Linenum(i, k) + let k = k + 1 + endwhile + + let ftype = s:tlist_{i}_filetype + + let k = 1 + while k <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{k}_name + let s:tlist_{j}_{ttype} = s:tlist_{i}_{ttype} + let s:tlist_{j}_{ttype}_offset = s:tlist_{i}_{ttype}_offset + let s:tlist_{j}_{ttype}_count = s:tlist_{i}_{ttype}_count + if s:tlist_{j}_{ttype} != '' + let l = 1 + while l <= s:tlist_{j}_{ttype}_count + let s:tlist_{j}_{ttype}_{l} = s:tlist_{i}_{ttype}_{l} + let l = l + 1 + endwhile + endif + let k = k + 1 + endwhile + + " As the file and tag information is copied to the new index, + " discard the previous information + call s:Tlist_Discard_FileInfo(i) + + let i = i + 1 + endwhile + + " Reduce the number of files displayed + let s:tlist_file_count = s:tlist_file_count - 1 + + if g:Tlist_Show_One_File + " If the tags for only one file is displayed and if we just + " now removed that file, then invalidate the current file idx + if s:tlist_cur_file_idx == fidx + let s:tlist_cur_file_idx = -1 + endif + endif +endfunction + +" Tlist_Window_Goto_Window +" Goto the taglist window +function! s:Tlist_Window_Goto_Window() + let winnum = bufwinnr(g:TagList_title) + if winnum != -1 + if winnr() != winnum + call s:Tlist_Exe_Cmd_No_Acmds(winnum . 'wincmd w') + endif + endif +endfunction + +" Tlist_Window_Create +" Create a new taglist window. If it is already open, jump to it +function! s:Tlist_Window_Create() + call s:Tlist_Log_Msg('Tlist_Window_Create()') + " If the window is open, jump to it + let winnum = bufwinnr(g:TagList_title) + if winnum != -1 + " Jump to the existing window + if winnr() != winnum + exe winnum . 'wincmd w' + endif + return + endif + + " If used with winmanager don't open windows. Winmanager will handle + " the window/buffer management + if s:tlist_app_name == "winmanager" + return + endif + + " Create a new window. If user prefers a horizontal window, then open + " a horizontally split window. Otherwise open a vertically split + " window + if g:Tlist_Use_Horiz_Window + " Open a horizontally split window + let win_dir = 'botright' + " Horizontal window height + let win_size = g:Tlist_WinHeight + else + if s:tlist_winsize_chgd == -1 + " Open a vertically split window. Increase the window size, if + " needed, to accomodate the new window + if g:Tlist_Inc_Winwidth && + \ &columns < (80 + g:Tlist_WinWidth) + " Save the original window position + let s:tlist_pre_winx = getwinposx() + let s:tlist_pre_winy = getwinposy() + + " one extra column is needed to include the vertical split + let &columns= &columns + g:Tlist_WinWidth + 1 + + let s:tlist_winsize_chgd = 1 + else + let s:tlist_winsize_chgd = 0 + endif + endif + + if g:Tlist_Use_Right_Window + " Open the window at the rightmost place + let win_dir = 'botright vertical' + else + " Open the window at the leftmost place + let win_dir = 'topleft vertical' + endif + let win_size = g:Tlist_WinWidth + endif + + " If the tag listing temporary buffer already exists, then reuse it. + " Otherwise create a new buffer + let bufnum = bufnr(g:TagList_title) + if bufnum == -1 + " Create a new buffer + let wcmd = g:TagList_title + else + " Edit the existing buffer + let wcmd = '+buffer' . bufnum + endif + + " Create the taglist window + exe 'silent! ' . win_dir . ' ' . win_size . 'split ' . wcmd + + " Save the new window position + let s:tlist_winx = getwinposx() + let s:tlist_winy = getwinposy() + + " Initialize the taglist window + call s:Tlist_Window_Init() +endfunction + +" Tlist_Window_Zoom +" Zoom (maximize/minimize) the taglist window +function! s:Tlist_Window_Zoom() + if s:tlist_win_maximized + " Restore the window back to the previous size + if g:Tlist_Use_Horiz_Window + exe 'resize ' . g:Tlist_WinHeight + else + exe 'vert resize ' . g:Tlist_WinWidth + endif + let s:tlist_win_maximized = 0 + else + " Set the window size to the maximum possible without closing other + " windows + if g:Tlist_Use_Horiz_Window + resize + else + vert resize + endif + let s:tlist_win_maximized = 1 + endif +endfunction + +" Tlist_Ballon_Expr +" When the mouse cursor is over a tag in the taglist window, display the +" tag prototype (balloon) +function! Tlist_Ballon_Expr() + " Get the file index + let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(v:beval_lnum) + if fidx == -1 + return '' + endif + + " Get the tag output line for the current tag + let tidx = s:Tlist_Window_Get_Tag_Index(fidx, v:beval_lnum) + if tidx == 0 + return '' + endif + + " Get the tag search pattern and display it + return s:Tlist_Get_Tag_Prototype(fidx, tidx) +endfunction + +" Tlist_Window_Check_Width +" Check the width of the taglist window. For horizontally split windows, the +" 'winfixheight' option is used to fix the height of the window. For +" vertically split windows, Vim doesn't support the 'winfixwidth' option. So +" need to handle window width changes from this function. +function! s:Tlist_Window_Check_Width() + let tlist_winnr = bufwinnr(g:TagList_title) + if tlist_winnr == -1 + return + endif + + let width = winwidth(tlist_winnr) + if width != g:Tlist_WinWidth + call s:Tlist_Log_Msg("Tlist_Window_Check_Width: Changing window " . + \ "width from " . width . " to " . g:Tlist_WinWidth) + let save_winnr = winnr() + if save_winnr != tlist_winnr + call s:Tlist_Exe_Cmd_No_Acmds(tlist_winnr . 'wincmd w') + endif + exe 'vert resize ' . g:Tlist_WinWidth + if save_winnr != tlist_winnr + call s:Tlist_Exe_Cmd_No_Acmds('wincmd p') + endif + endif +endfunction + +" Tlist_Window_Exit_Only_Window +" If the 'Tlist_Exit_OnlyWindow' option is set, then exit Vim if only the +" taglist window is present. +function! s:Tlist_Window_Exit_Only_Window() + " Before quitting Vim, delete the taglist buffer so that + " the '0 mark is correctly set to the previous buffer. + if v:version < 700 + if winbufnr(2) == -1 + bdelete + quit + endif + else + if winbufnr(2) == -1 + if tabpagenr('$') == 1 + " Only one tag page is present + bdelete + quit + else + " More than one tab page is present. Close only the current + " tab page + close + endif + endif + endif +endfunction + +" Tlist_Window_Init +" Set the default options for the taglist window +function! s:Tlist_Window_Init() + call s:Tlist_Log_Msg('Tlist_Window_Init()') + + " The 'readonly' option should not be set for the taglist buffer. + " If Vim is started as "view/gview" or if the ":view" command is + " used, then the 'readonly' option is set for all the buffers. + " Unset it for the taglist buffer + setlocal noreadonly + + " Set the taglist buffer filetype to taglist + setlocal filetype=taglist + + " Define taglist window element highlighting + syntax match TagListComment '^" .*' + syntax match TagListFileName '^[^" ].*$' + syntax match TagListTitle '^ \S.*$' + syntax match TagListTagScope '\s\[.\{-\}\]$' + + " Define the highlighting only if colors are supported + if has('gui_running') || &t_Co > 2 + " Colors to highlight various taglist window elements + " If user defined highlighting group exists, then use them. + " Otherwise, use default highlight groups. + if hlexists('MyTagListTagName') + highlight link TagListTagName MyTagListTagName + else + highlight default link TagListTagName Search + endif + " Colors to highlight comments and titles + if hlexists('MyTagListComment') + highlight link TagListComment MyTagListComment + else + highlight clear TagListComment + highlight default link TagListComment Comment + endif + if hlexists('MyTagListTitle') + highlight link TagListTitle MyTagListTitle + else + highlight clear TagListTitle + highlight default link TagListTitle Title + endif + if hlexists('MyTagListFileName') + highlight link TagListFileName MyTagListFileName + else + highlight clear TagListFileName + highlight default TagListFileName guibg=Grey ctermbg=darkgray + \ guifg=white ctermfg=white + endif + if hlexists('MyTagListTagScope') + highlight link TagListTagScope MyTagListTagScope + else + highlight clear TagListTagScope + highlight default link TagListTagScope Identifier + endif + else + highlight default TagListTagName term=reverse cterm=reverse + endif + + " Folding related settings + setlocal foldenable + setlocal foldminlines=0 + setlocal foldmethod=manual + setlocal foldlevel=9999 + if g:Tlist_Enable_Fold_Column + setlocal foldcolumn=3 + else + setlocal foldcolumn=0 + endif + setlocal foldtext=v:folddashes.getline(v:foldstart) + + if s:tlist_app_name != "winmanager" + " Mark buffer as scratch + silent! setlocal buftype=nofile + if s:tlist_app_name == "none" + silent! setlocal bufhidden=delete + endif + silent! setlocal noswapfile + " Due to a bug in Vim 6.0, the winbufnr() function fails for unlisted + " buffers. So if the taglist buffer is unlisted, multiple taglist + " windows will be opened. This bug is fixed in Vim 6.1 and above + if v:version >= 601 + silent! setlocal nobuflisted + endif + endif + + silent! setlocal nowrap + + " If the 'number' option is set in the source window, it will affect the + " taglist window. So forcefully disable 'number' option for the taglist + " window + silent! setlocal nonumber + + " Use fixed height when horizontally split window is used + if g:Tlist_Use_Horiz_Window + if v:version >= 602 + set winfixheight + endif + endif + if !g:Tlist_Use_Horiz_Window && v:version >= 700 + set winfixwidth + endif + + " Setup balloon evaluation to display tag prototype + if v:version >= 700 && has('balloon_eval') + setlocal balloonexpr=Tlist_Ballon_Expr() + set ballooneval + endif + + " Setup the cpoptions properly for the maps to work + let old_cpoptions = &cpoptions + set cpoptions&vim + + " Create buffer local mappings for jumping to the tags and sorting the list + nnoremap <buffer> <silent> <CR> + \ :call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR> + nnoremap <buffer> <silent> o + \ :call <SID>Tlist_Window_Jump_To_Tag('newwin')<CR> + nnoremap <buffer> <silent> p + \ :call <SID>Tlist_Window_Jump_To_Tag('preview')<CR> + nnoremap <buffer> <silent> P + \ :call <SID>Tlist_Window_Jump_To_Tag('prevwin')<CR> + if v:version >= 700 + nnoremap <buffer> <silent> t + \ :call <SID>Tlist_Window_Jump_To_Tag('checktab')<CR> + nnoremap <buffer> <silent> <C-t> + \ :call <SID>Tlist_Window_Jump_To_Tag('newtab')<CR> + endif + nnoremap <buffer> <silent> <2-LeftMouse> + \ :call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR> + nnoremap <buffer> <silent> s + \ :call <SID>Tlist_Change_Sort('cmd', 'toggle', '')<CR> + nnoremap <buffer> <silent> + :silent! foldopen<CR> + nnoremap <buffer> <silent> - :silent! foldclose<CR> + nnoremap <buffer> <silent> * :silent! %foldopen!<CR> + nnoremap <buffer> <silent> = :silent! %foldclose<CR> + nnoremap <buffer> <silent> <kPlus> :silent! foldopen<CR> + nnoremap <buffer> <silent> <kMinus> :silent! foldclose<CR> + nnoremap <buffer> <silent> <kMultiply> :silent! %foldopen!<CR> + nnoremap <buffer> <silent> <Space> :call <SID>Tlist_Window_Show_Info()<CR> + nnoremap <buffer> <silent> u :call <SID>Tlist_Window_Update_File()<CR> + nnoremap <buffer> <silent> d :call <SID>Tlist_Remove_File(-1, 1)<CR> + nnoremap <buffer> <silent> x :call <SID>Tlist_Window_Zoom()<CR> + nnoremap <buffer> <silent> [[ :call <SID>Tlist_Window_Move_To_File(-1)<CR> + nnoremap <buffer> <silent> <BS> :call <SID>Tlist_Window_Move_To_File(-1)<CR> + nnoremap <buffer> <silent> ]] :call <SID>Tlist_Window_Move_To_File(1)<CR> + nnoremap <buffer> <silent> <Tab> :call <SID>Tlist_Window_Move_To_File(1)<CR> + nnoremap <buffer> <silent> <F1> :call <SID>Tlist_Window_Toggle_Help_Text()<CR> + nnoremap <buffer> <silent> q :close<CR> + + " Insert mode mappings + inoremap <buffer> <silent> <CR> + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR> + " Windows needs return + inoremap <buffer> <silent> <Return> + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR> + inoremap <buffer> <silent> o + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('newwin')<CR> + inoremap <buffer> <silent> p + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('preview')<CR> + inoremap <buffer> <silent> P + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('prevwin')<CR> + if v:version >= 700 + inoremap <buffer> <silent> t + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('checktab')<CR> + inoremap <buffer> <silent> <C-t> + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('newtab')<CR> + endif + inoremap <buffer> <silent> <2-LeftMouse> + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR> + inoremap <buffer> <silent> s + \ <C-o>:call <SID>Tlist_Change_Sort('cmd', 'toggle', '')<CR> + inoremap <buffer> <silent> + <C-o>:silent! foldopen<CR> + inoremap <buffer> <silent> - <C-o>:silent! foldclose<CR> + inoremap <buffer> <silent> * <C-o>:silent! %foldopen!<CR> + inoremap <buffer> <silent> = <C-o>:silent! %foldclose<CR> + inoremap <buffer> <silent> <kPlus> <C-o>:silent! foldopen<CR> + inoremap <buffer> <silent> <kMinus> <C-o>:silent! foldclose<CR> + inoremap <buffer> <silent> <kMultiply> <C-o>:silent! %foldopen!<CR> + inoremap <buffer> <silent> <Space> <C-o>:call + \ <SID>Tlist_Window_Show_Info()<CR> + inoremap <buffer> <silent> u + \ <C-o>:call <SID>Tlist_Window_Update_File()<CR> + inoremap <buffer> <silent> d <C-o>:call <SID>Tlist_Remove_File(-1, 1)<CR> + inoremap <buffer> <silent> x <C-o>:call <SID>Tlist_Window_Zoom()<CR> + inoremap <buffer> <silent> [[ <C-o>:call <SID>Tlist_Window_Move_To_File(-1)<CR> + inoremap <buffer> <silent> <BS> <C-o>:call <SID>Tlist_Window_Move_To_File(-1)<CR> + inoremap <buffer> <silent> ]] <C-o>:call <SID>Tlist_Window_Move_To_File(1)<CR> + inoremap <buffer> <silent> <Tab> <C-o>:call <SID>Tlist_Window_Move_To_File(1)<CR> + inoremap <buffer> <silent> <F1> <C-o>:call <SID>Tlist_Window_Toggle_Help_Text()<CR> + inoremap <buffer> <silent> q <C-o>:close<CR> + + " Map single left mouse click if the user wants this functionality + if g:Tlist_Use_SingleClick == 1 + " Contributed by Bindu Wavell + " attempt to perform single click mapping, it would be much + " nicer if we could nnoremap <buffer> ... however vim does + " not fire the <buffer> <leftmouse> when you use the mouse + " to enter a buffer. + let clickmap = ':if bufname("%") =~ "__Tag_List__" <bar> ' . + \ 'call <SID>Tlist_Window_Jump_To_Tag("useopen") ' . + \ '<bar> endif <CR>' + if maparg('<leftmouse>', 'n') == '' + " no mapping for leftmouse + exe ':nnoremap <silent> <leftmouse> <leftmouse>' . clickmap + else + " we have a mapping + let mapcmd = ':nnoremap <silent> <leftmouse> <leftmouse>' + let mapcmd = mapcmd . substitute(substitute( + \ maparg('<leftmouse>', 'n'), '|', '<bar>', 'g'), + \ '\c^<leftmouse>', '', '') + let mapcmd = mapcmd . clickmap + exe mapcmd + endif + endif + + " Define the taglist autocommands + augroup TagListAutoCmds + autocmd! + " Display the tag prototype for the tag under the cursor. + autocmd CursorHold __Tag_List__ call s:Tlist_Window_Show_Info() + " Highlight the current tag periodically + autocmd CursorHold * silent call s:Tlist_Window_Highlight_Tag( + \ fnamemodify(bufname('%'), ':p'), line('.'), 1, 0) + + " Adjust the Vim window width when taglist window is closed + autocmd BufUnload __Tag_List__ call s:Tlist_Post_Close_Cleanup() + " Close the fold for this buffer when leaving the buffer + if g:Tlist_File_Fold_Auto_Close + autocmd BufEnter * silent + \ call s:Tlist_Window_Open_File_Fold(expand('<abuf>')) + endif + " Exit Vim itself if only the taglist window is present (optional) + if g:Tlist_Exit_OnlyWindow + autocmd BufEnter __Tag_List__ nested + \ call s:Tlist_Window_Exit_Only_Window() + endif + if s:tlist_app_name != "winmanager" && + \ !g:Tlist_Process_File_Always && + \ (!has('gui_running') || !g:Tlist_Show_Menu) + " Auto refresh the taglist window + autocmd BufEnter * call s:Tlist_Refresh() + endif + + if !g:Tlist_Use_Horiz_Window + if v:version < 700 + autocmd WinEnter * call s:Tlist_Window_Check_Width() + endif + endif + if v:version >= 700 + autocmd TabEnter * silent call s:Tlist_Refresh_Folds() + endif + augroup end + + " Restore the previous cpoptions settings + let &cpoptions = old_cpoptions +endfunction + +" Tlist_Window_Refresh +" Display the tags for all the files in the taglist window +function! s:Tlist_Window_Refresh() + call s:Tlist_Log_Msg('Tlist_Window_Refresh()') + " Set report option to a huge value to prevent informational messages + " while deleting the lines + let old_report = &report + set report=99999 + + " Mark the buffer as modifiable + setlocal modifiable + + " Delete the contents of the buffer to the black-hole register + silent! %delete _ + + " As we have cleared the taglist window, mark all the files + " as not visible + let i = 0 + while i < s:tlist_file_count + let s:tlist_{i}_visible = 0 + let i = i + 1 + endwhile + + if g:Tlist_Compact_Format == 0 + " Display help in non-compact mode + call s:Tlist_Window_Display_Help() + endif + + " Mark the buffer as not modifiable + setlocal nomodifiable + + " Restore the report option + let &report = old_report + + " If the tags for only one file should be displayed in the taglist + " window, then no need to add the tags here. The bufenter autocommand + " will add the tags for that file. + if g:Tlist_Show_One_File + return + endif + + " List all the tags for the previously processed files + " Do this only if taglist is configured to display tags for more than + " one file. Otherwise, when Tlist_Show_One_File is configured, + " tags for the wrong file will be displayed. + let i = 0 + while i < s:tlist_file_count + call s:Tlist_Window_Refresh_File(s:tlist_{i}_filename, + \ s:tlist_{i}_filetype) + let i = i + 1 + endwhile + + if g:Tlist_Auto_Update + " Add and list the tags for all buffers in the Vim buffer list + let i = 1 + let last_bufnum = bufnr('$') + while i <= last_bufnum + if buflisted(i) + let fname = fnamemodify(bufname(i), ':p') + let ftype = s:Tlist_Get_Buffer_Filetype(i) + " If the file doesn't support tag listing, skip it + if !s:Tlist_Skip_File(fname, ftype) + call s:Tlist_Window_Refresh_File(fname, ftype) + endif + endif + let i = i + 1 + endwhile + endif + + " If Tlist_File_Fold_Auto_Close option is set, then close all the folds + if g:Tlist_File_Fold_Auto_Close + " Close all the folds + silent! %foldclose + endif + + " Move the cursor to the top of the taglist window + normal! gg +endfunction + +" Tlist_Post_Close_Cleanup() +" Close the taglist window and adjust the Vim window width +function! s:Tlist_Post_Close_Cleanup() + call s:Tlist_Log_Msg('Tlist_Post_Close_Cleanup()') + " Mark all the files as not visible + let i = 0 + while i < s:tlist_file_count + let s:tlist_{i}_visible = 0 + let i = i + 1 + endwhile + + " Remove the taglist autocommands + silent! autocmd! TagListAutoCmds + + " Clear all the highlights + match none + + silent! syntax clear TagListTitle + silent! syntax clear TagListComment + silent! syntax clear TagListTagScope + + " Remove the left mouse click mapping if it was setup initially + if g:Tlist_Use_SingleClick + if hasmapto('<LeftMouse>') + nunmap <LeftMouse> + endif + endif + + if s:tlist_app_name != "winmanager" + if g:Tlist_Use_Horiz_Window || g:Tlist_Inc_Winwidth == 0 || + \ s:tlist_winsize_chgd != 1 || + \ &columns < (80 + g:Tlist_WinWidth) + " No need to adjust window width if using horizontally split taglist + " window or if columns is less than 101 or if the user chose not to + " adjust the window width + else + " If the user didn't manually move the window, then restore the window + " position to the pre-taglist position + if s:tlist_pre_winx != -1 && s:tlist_pre_winy != -1 && + \ getwinposx() == s:tlist_winx && + \ getwinposy() == s:tlist_winy + exe 'winpos ' . s:tlist_pre_winx . ' ' . s:tlist_pre_winy + endif + + " Adjust the Vim window width + let &columns= &columns - (g:Tlist_WinWidth + 1) + endif + endif + + let s:tlist_winsize_chgd = -1 + + " Reset taglist state variables + if s:tlist_app_name == "winmanager" + let s:tlist_app_name = "none" + endif + let s:tlist_window_initialized = 0 +endfunction + +" Tlist_Window_Refresh_File() +" List the tags defined in the specified file in a Vim window +function! s:Tlist_Window_Refresh_File(filename, ftype) + call s:Tlist_Log_Msg('Tlist_Window_Refresh_File (' . a:filename . ')') + " First check whether the file already exists + let fidx = s:Tlist_Get_File_Index(a:filename) + if fidx != -1 + let file_listed = 1 + else + let file_listed = 0 + endif + + if !file_listed + " Check whether this file is removed based on user request + " If it is, then don't display the tags for this file + if s:Tlist_User_Removed_File(a:filename) + return + endif + endif + + if file_listed && s:tlist_{fidx}_visible + " Check whether the file tags are currently valid + if s:tlist_{fidx}_valid + " Goto the first line in the file + exe s:tlist_{fidx}_start + + " If the line is inside a fold, open the fold + if foldclosed('.') != -1 + exe "silent! " . s:tlist_{fidx}_start . "," . + \ s:tlist_{fidx}_end . "foldopen!" + endif + return + endif + + " Discard and remove the tags for this file from display + call s:Tlist_Discard_TagInfo(fidx) + call s:Tlist_Window_Remove_File_From_Display(fidx) + endif + + " Process and generate a list of tags defined in the file + if !file_listed || !s:tlist_{fidx}_valid + let ret_fidx = s:Tlist_Process_File(a:filename, a:ftype) + if ret_fidx == -1 + return + endif + let fidx = ret_fidx + endif + + " Set report option to a huge value to prevent informational messages + " while adding lines to the taglist window + let old_report = &report + set report=99999 + + if g:Tlist_Show_One_File + " Remove the previous file + if s:tlist_cur_file_idx != -1 + call s:Tlist_Window_Remove_File_From_Display(s:tlist_cur_file_idx) + let s:tlist_{s:tlist_cur_file_idx}_visible = 0 + let s:tlist_{s:tlist_cur_file_idx}_start = 0 + let s:tlist_{s:tlist_cur_file_idx}_end = 0 + endif + let s:tlist_cur_file_idx = fidx + endif + + " Mark the buffer as modifiable + setlocal modifiable + + " Add new files to the end of the window. For existing files, add them at + " the same line where they were previously present. If the file is not + " visible, then add it at the end + if s:tlist_{fidx}_start == 0 || !s:tlist_{fidx}_visible + if g:Tlist_Compact_Format + let s:tlist_{fidx}_start = line('$') + else + let s:tlist_{fidx}_start = line('$') + 1 + endif + endif + + let s:tlist_{fidx}_visible = 1 + + " Goto the line where this file should be placed + if g:Tlist_Compact_Format + exe s:tlist_{fidx}_start + else + exe s:tlist_{fidx}_start - 1 + endif + + let txt = fnamemodify(s:tlist_{fidx}_filename, ':t') . ' (' . + \ fnamemodify(s:tlist_{fidx}_filename, ':p:h') . ')' + if g:Tlist_Compact_Format == 0 + silent! put =txt + else + silent! put! =txt + " Move to the next line + exe line('.') + 1 + endif + let file_start = s:tlist_{fidx}_start + + " Add the tag names grouped by tag type to the buffer with a title + let i = 1 + let ttype_cnt = s:tlist_{a:ftype}_count + while i <= ttype_cnt + let ttype = s:tlist_{a:ftype}_{i}_name + " Add the tag type only if there are tags for that type + let fidx_ttype = 's:tlist_' . fidx . '_' . ttype + let ttype_txt = {fidx_ttype} + if ttype_txt != '' + let txt = ' ' . s:tlist_{a:ftype}_{i}_fullname + if g:Tlist_Compact_Format == 0 + let ttype_start_lnum = line('.') + 1 + silent! put =txt + else + let ttype_start_lnum = line('.') + silent! put! =txt + endif + silent! put =ttype_txt + + let {fidx_ttype}_offset = ttype_start_lnum - file_start + + " create a fold for this tag type + let fold_start = ttype_start_lnum + let fold_end = fold_start + {fidx_ttype}_count + exe fold_start . ',' . fold_end . 'fold' + + " Adjust the cursor position + if g:Tlist_Compact_Format == 0 + exe ttype_start_lnum + {fidx_ttype}_count + else + exe ttype_start_lnum + {fidx_ttype}_count + 1 + endif + + if g:Tlist_Compact_Format == 0 + " Separate the tag types by a empty line + silent! put ='' + endif + endif + let i = i + 1 + endwhile + + if s:tlist_{fidx}_tag_count == 0 + if g:Tlist_Compact_Format == 0 + silent! put ='' + endif + endif + + let s:tlist_{fidx}_end = line('.') - 1 + + " Create a fold for the entire file + exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'fold' + exe 'silent! ' . s:tlist_{fidx}_start . ',' . + \ s:tlist_{fidx}_end . 'foldopen!' + + " Goto the starting line for this file, + exe s:tlist_{fidx}_start + + if s:tlist_app_name == "winmanager" + " To handle a bug in the winmanager plugin, add a space at the + " last line + call setline('$', ' ') + endif + + " Mark the buffer as not modifiable + setlocal nomodifiable + + " Restore the report option + let &report = old_report + + " Update the start and end line numbers for all the files following this + " file + let start = s:tlist_{fidx}_start + " include the empty line after the last line + if g:Tlist_Compact_Format + let end = s:tlist_{fidx}_end + else + let end = s:tlist_{fidx}_end + 1 + endif + call s:Tlist_Window_Update_Line_Offsets(fidx + 1, 1, end - start + 1) + + " Now that we have updated the taglist window, update the tags + " menu (if present) + if g:Tlist_Show_Menu + call s:Tlist_Menu_Update_File(1) + endif +endfunction + +" Tlist_Init_File +" Initialize the variables for a new file +function! s:Tlist_Init_File(filename, ftype) + call s:Tlist_Log_Msg('Tlist_Init_File (' . a:filename . ')') + " Add new files at the end of the list + let fidx = s:tlist_file_count + let s:tlist_file_count = s:tlist_file_count + 1 + " Add the new file name to the taglist list of file names + let s:tlist_file_names = s:tlist_file_names . a:filename . "\n" + + " Initialize the file variables + let s:tlist_{fidx}_filename = a:filename + let s:tlist_{fidx}_sort_type = g:Tlist_Sort_Type + let s:tlist_{fidx}_filetype = a:ftype + let s:tlist_{fidx}_mtime = -1 + let s:tlist_{fidx}_start = 0 + let s:tlist_{fidx}_end = 0 + let s:tlist_{fidx}_valid = 0 + let s:tlist_{fidx}_visible = 0 + let s:tlist_{fidx}_tag_count = 0 + let s:tlist_{fidx}_menu_cmd = '' + + " Initialize the tag type variables + let i = 1 + while i <= s:tlist_{a:ftype}_count + let ttype = s:tlist_{a:ftype}_{i}_name + let s:tlist_{fidx}_{ttype} = '' + let s:tlist_{fidx}_{ttype}_offset = 0 + let s:tlist_{fidx}_{ttype}_count = 0 + let i = i + 1 + endwhile + + return fidx +endfunction + +" Tlist_Get_Tag_Type_By_Tag +" Return the tag type for the specified tag index +function! s:Tlist_Get_Tag_Type_By_Tag(fidx, tidx) + let ttype_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_type' + + " Already parsed and have the tag name + if exists(ttype_var) + return {ttype_var} + endif + + let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag + let {ttype_var} = s:Tlist_Extract_Tagtype(tag_line) + + return {ttype_var} +endfunction + +" Tlist_Get_Tag_Prototype +function! s:Tlist_Get_Tag_Prototype(fidx, tidx) + let tproto_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_proto' + + " Already parsed and have the tag prototype + if exists(tproto_var) + return {tproto_var} + endif + + " Parse and extract the tag prototype + let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag + let start = stridx(tag_line, '/^') + 2 + let end = stridx(tag_line, '/;"' . "\t") + if tag_line[end - 1] == '$' + let end = end -1 + endif + let tag_proto = strpart(tag_line, start, end - start) + let {tproto_var} = substitute(tag_proto, '\s*', '', '') + + return {tproto_var} +endfunction + +" Tlist_Get_Tag_SearchPat +function! s:Tlist_Get_Tag_SearchPat(fidx, tidx) + let tpat_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_searchpat' + + " Already parsed and have the tag search pattern + if exists(tpat_var) + return {tpat_var} + endif + + " Parse and extract the tag search pattern + let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag + let start = stridx(tag_line, '/^') + 2 + let end = stridx(tag_line, '/;"' . "\t") + if tag_line[end - 1] == '$' + let end = end -1 + endif + let {tpat_var} = '\V\^' . strpart(tag_line, start, end - start) . + \ (tag_line[end] == '$' ? '\$' : '') + + return {tpat_var} +endfunction + +" Tlist_Get_Tag_Linenum +" Return the tag line number, given the tag index +function! s:Tlist_Get_Tag_Linenum(fidx, tidx) + let tline_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_linenum' + + " Already parsed and have the tag line number + if exists(tline_var) + return {tline_var} + endif + + " Parse and extract the tag line number + let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag + let start = strridx(tag_line, 'line:') + 5 + let end = strridx(tag_line, "\t") + if end < start + let {tline_var} = strpart(tag_line, start) + 0 + else + let {tline_var} = strpart(tag_line, start, end - start) + 0 + endif + + return {tline_var} +endfunction + +" Tlist_Parse_Tagline +" Parse a tag line from the ctags output. Separate the tag output based on the +" tag type and store it in the tag type variable. +" The format of each line in the ctags output is: +" +" tag_name<TAB>file_name<TAB>ex_cmd;"<TAB>extension_fields +" +function! s:Tlist_Parse_Tagline(tag_line) + if a:tag_line == '' + " Skip empty lines + return + endif + + " Extract the tag type + let ttype = s:Tlist_Extract_Tagtype(a:tag_line) + + " Make sure the tag type is a valid and supported one + if ttype == '' || stridx(s:ctags_flags, ttype) == -1 + " Line is not in proper tags format or Tag type is not supported + return + endif + + " Update the total tag count + let s:tidx = s:tidx + 1 + + " The following variables are used to optimize this code. Vim is slow in + " using curly brace names. To reduce the amount of processing needed, the + " curly brace variables are pre-processed here + let fidx_tidx = 's:tlist_' . s:fidx . '_' . s:tidx + let fidx_ttype = 's:tlist_' . s:fidx . '_' . ttype + + " Update the count of this tag type + let ttype_idx = {fidx_ttype}_count + 1 + let {fidx_ttype}_count = ttype_idx + + " Store the ctags output for this tag + let {fidx_tidx}_tag = a:tag_line + + " Store the tag index and the tag type index (back pointers) + let {fidx_ttype}_{ttype_idx} = s:tidx + let {fidx_tidx}_ttype_idx = ttype_idx + + " Extract the tag name + let tag_name = strpart(a:tag_line, 0, stridx(a:tag_line, "\t")) + + " Extract the tag scope/prototype + if g:Tlist_Display_Prototype + let ttxt = ' ' . s:Tlist_Get_Tag_Prototype(s:fidx, s:tidx) + else + let ttxt = ' ' . tag_name + + " Add the tag scope, if it is available and is configured. Tag + " scope is the last field after the 'line:<num>\t' field + if g:Tlist_Display_Tag_Scope + let tag_scope = s:Tlist_Extract_Tag_Scope(a:tag_line) + if tag_scope != '' + let ttxt = ttxt . ' [' . tag_scope . ']' + endif + endif + endif + + " Add this tag to the tag type variable + let {fidx_ttype} = {fidx_ttype} . ttxt . "\n" + + " Save the tag name + let {fidx_tidx}_tag_name = tag_name +endfunction + +" Tlist_Process_File +" Get the list of tags defined in the specified file and store them +" in Vim variables. Returns the file index where the tags are stored. +function! s:Tlist_Process_File(filename, ftype) + call s:Tlist_Log_Msg('Tlist_Process_File (' . a:filename . ', ' . + \ a:ftype . ')') + " Check whether this file is supported + if s:Tlist_Skip_File(a:filename, a:ftype) + return -1 + endif + + " If the tag types for this filetype are not yet created, then create + " them now + let var = 's:tlist_' . a:ftype . '_count' + if !exists(var) + if s:Tlist_FileType_Init(a:ftype) == 0 + return -1 + endif + endif + + " If this file is already processed, then use the cached values + let fidx = s:Tlist_Get_File_Index(a:filename) + if fidx == -1 + " First time, this file is loaded + let fidx = s:Tlist_Init_File(a:filename, a:ftype) + else + " File was previously processed. Discard the tag information + call s:Tlist_Discard_TagInfo(fidx) + endif + + let s:tlist_{fidx}_valid = 1 + + " Exuberant ctags arguments to generate a tag list + let ctags_args = ' -f - --format=2 --excmd=pattern --fields=nks ' + + " Form the ctags argument depending on the sort type + if s:tlist_{fidx}_sort_type == 'name' + let ctags_args = ctags_args . '--sort=yes' + else + let ctags_args = ctags_args . '--sort=no' + endif + + " Add the filetype specific arguments + let ctags_args = ctags_args . ' ' . s:tlist_{a:ftype}_ctags_args + + " Ctags command to produce output with regexp for locating the tags + let ctags_cmd = g:Tlist_Ctags_Cmd . ctags_args + let ctags_cmd = ctags_cmd . ' "' . a:filename . '"' + + if &shellxquote == '"' + " Double-quotes within double-quotes will not work in the + " command-line.If the 'shellxquote' option is set to double-quotes, + " then escape the double-quotes in the ctags command-line. + let ctags_cmd = escape(ctags_cmd, '"') + endif + + " In Windows 95, if not using cygwin, disable the 'shellslash' + " option. Otherwise, this will cause problems when running the + " ctags command. + if has('win95') && !has('win32unix') + let old_shellslash = &shellslash + set noshellslash + endif + + if has('win32') && !has('win32unix') && !has('win95') + \ && (&shell =~ 'cmd.exe') + " Windows does not correctly deal with commands that have more than 1 + " set of double quotes. It will strip them all resulting in: + " 'C:\Program' is not recognized as an internal or external command + " operable program or batch file. To work around this, place the + " command inside a batch file and call the batch file. + " Do this only on Win2K, WinXP and above. + " Contributed by: David Fishburn. + let s:taglist_tempfile = fnamemodify(tempname(), ':h') . + \ '\taglist.cmd' + exe 'redir! > ' . s:taglist_tempfile + silent echo ctags_cmd + redir END + + call s:Tlist_Log_Msg('Cmd inside batch file: ' . ctags_cmd) + let ctags_cmd = '"' . s:taglist_tempfile . '"' + endif + + call s:Tlist_Log_Msg('Cmd: ' . ctags_cmd) + + " Run ctags and get the tag list + let cmd_output = system(ctags_cmd) + + " Restore the value of the 'shellslash' option. + if has('win95') && !has('win32unix') + let &shellslash = old_shellslash + endif + + if exists('s:taglist_tempfile') + " Delete the temporary cmd file created on MS-Windows + call delete(s:taglist_tempfile) + endif + + " Handle errors + if v:shell_error + let msg = "Taglist: Failed to generate tags for " . a:filename + call s:Tlist_Warning_Msg(msg) + if cmd_output != '' + call s:Tlist_Warning_Msg(cmd_output) + endif + return fidx + endif + + " Store the modification time for the file + let s:tlist_{fidx}_mtime = getftime(a:filename) + + " No tags for current file + if cmd_output == '' + call s:Tlist_Log_Msg('No tags defined in ' . a:filename) + return fidx + endif + + call s:Tlist_Log_Msg('Generated tags information for ' . a:filename) + + if v:version > 601 + " The following script local variables are used by the + " Tlist_Parse_Tagline() function. + let s:ctags_flags = s:tlist_{a:ftype}_ctags_flags + let s:fidx = fidx + let s:tidx = 0 + + " Process the ctags output one line at a time. The substitute() + " command is used to parse the tag lines instead of using the + " matchstr()/stridx()/strpart() functions for performance reason + call substitute(cmd_output, "\\([^\n]\\+\\)\n", + \ '\=s:Tlist_Parse_Tagline(submatch(1))', 'g') + + " Save the number of tags for this file + let s:tlist_{fidx}_tag_count = s:tidx + + " The following script local variables are no longer needed + unlet! s:ctags_flags + unlet! s:tidx + unlet! s:fidx + else + " Due to a bug in Vim earlier than version 6.1, + " we cannot use substitute() to parse the ctags output. + " Instead the slow str*() functions are used + let ctags_flags = s:tlist_{a:ftype}_ctags_flags + let tidx = 0 + + while cmd_output != '' + " Extract one line at a time + let idx = stridx(cmd_output, "\n") + let one_line = strpart(cmd_output, 0, idx) + " Remove the line from the tags output + let cmd_output = strpart(cmd_output, idx + 1) + + if one_line == '' + " Line is not in proper tags format + continue + endif + + " Extract the tag type + let ttype = s:Tlist_Extract_Tagtype(one_line) + + " Make sure the tag type is a valid and supported one + if ttype == '' || stridx(ctags_flags, ttype) == -1 + " Line is not in proper tags format or Tag type is not + " supported + continue + endif + + " Update the total tag count + let tidx = tidx + 1 + + " The following variables are used to optimize this code. Vim is + " slow in using curly brace names. To reduce the amount of + " processing needed, the curly brace variables are pre-processed + " here + let fidx_tidx = 's:tlist_' . fidx . '_' . tidx + let fidx_ttype = 's:tlist_' . fidx . '_' . ttype + + " Update the count of this tag type + let ttype_idx = {fidx_ttype}_count + 1 + let {fidx_ttype}_count = ttype_idx + + " Store the ctags output for this tag + let {fidx_tidx}_tag = one_line + + " Store the tag index and the tag type index (back pointers) + let {fidx_ttype}_{ttype_idx} = tidx + let {fidx_tidx}_ttype_idx = ttype_idx + + " Extract the tag name + let tag_name = strpart(one_line, 0, stridx(one_line, "\t")) + + " Extract the tag scope/prototype + if g:Tlist_Display_Prototype + let ttxt = ' ' . s:Tlist_Get_Tag_Prototype(fidx, tidx) + else + let ttxt = ' ' . tag_name + + " Add the tag scope, if it is available and is configured. Tag + " scope is the last field after the 'line:<num>\t' field + if g:Tlist_Display_Tag_Scope + let tag_scope = s:Tlist_Extract_Tag_Scope(one_line) + if tag_scope != '' + let ttxt = ttxt . ' [' . tag_scope . ']' + endif + endif + endif + + " Add this tag to the tag type variable + let {fidx_ttype} = {fidx_ttype} . ttxt . "\n" + + " Save the tag name + let {fidx_tidx}_tag_name = tag_name + endwhile + + " Save the number of tags for this file + let s:tlist_{fidx}_tag_count = tidx + endif + + call s:Tlist_Log_Msg('Processed ' . s:tlist_{fidx}_tag_count . + \ ' tags in ' . a:filename) + + return fidx +endfunction + +" Tlist_Update_File +" Update the tags for a file (if needed) +function! Tlist_Update_File(filename, ftype) + call s:Tlist_Log_Msg('Tlist_Update_File (' . a:filename . ')') + " If the file doesn't support tag listing, skip it + if s:Tlist_Skip_File(a:filename, a:ftype) + return + endif + + " Convert the file name to a full path + let fname = fnamemodify(a:filename, ':p') + + " First check whether the file already exists + let fidx = s:Tlist_Get_File_Index(fname) + + if fidx != -1 && s:tlist_{fidx}_valid + " File exists and the tags are valid + " Check whether the file was modified after the last tags update + " If it is modified, then update the tags + if s:tlist_{fidx}_mtime == getftime(fname) + return + endif + else + " If the tags were removed previously based on a user request, + " as we are going to update the tags (based on the user request), + " remove the filename from the deleted list + call s:Tlist_Update_Remove_List(fname, 0) + endif + + " If the taglist window is opened, update it + let winnum = bufwinnr(g:TagList_title) + if winnum == -1 + " Taglist window is not present. Just update the taglist + " and return + call s:Tlist_Process_File(fname, a:ftype) + else + if g:Tlist_Show_One_File && s:tlist_cur_file_idx != -1 + " If tags for only one file are displayed and we are not + " updating the tags for that file, then no need to + " refresh the taglist window. Otherwise, the taglist + " window should be updated. + if s:tlist_{s:tlist_cur_file_idx}_filename != fname + call s:Tlist_Process_File(fname, a:ftype) + return + endif + endif + + " Save the current window number + let save_winnr = winnr() + + " Goto the taglist window + call s:Tlist_Window_Goto_Window() + + " Save the cursor position + let save_line = line('.') + let save_col = col('.') + + " Update the taglist window + call s:Tlist_Window_Refresh_File(fname, a:ftype) + + " Restore the cursor position + if v:version >= 601 + call cursor(save_line, save_col) + else + exe save_line + exe 'normal! ' . save_col . '|' + endif + + if winnr() != save_winnr + " Go back to the original window + call s:Tlist_Exe_Cmd_No_Acmds(save_winnr . 'wincmd w') + endif + endif + + " Update the taglist menu + if g:Tlist_Show_Menu + call s:Tlist_Menu_Update_File(1) + endif +endfunction + +" Tlist_Window_Close +" Close the taglist window +function! s:Tlist_Window_Close() + call s:Tlist_Log_Msg('Tlist_Window_Close()') + " Make sure the taglist window exists + let winnum = bufwinnr(g:TagList_title) + if winnum == -1 + call s:Tlist_Warning_Msg('Error: Taglist window is not open') + return + endif + + if winnr() == winnum + " Already in the taglist window. Close it and return + if winbufnr(2) != -1 + " If a window other than the taglist window is open, + " then only close the taglist window. + close + endif + else + " Goto the taglist window, close it and then come back to the + " original window + let curbufnr = bufnr('%') + exe winnum . 'wincmd w' + close + " Need to jump back to the original window only if we are not + " already in that window + let winnum = bufwinnr(curbufnr) + if winnr() != winnum + exe winnum . 'wincmd w' + endif + endif +endfunction + +" Tlist_Window_Mark_File_Window +" Mark the current window as the file window to use when jumping to a tag. +" Only if the current window is a non-plugin, non-preview and non-taglist +" window +function! s:Tlist_Window_Mark_File_Window() + if getbufvar('%', '&buftype') == '' && !&previewwindow + let w:tlist_file_window = "yes" + endif +endfunction + +" Tlist_Window_Open +" Open and refresh the taglist window +function! s:Tlist_Window_Open() + call s:Tlist_Log_Msg('Tlist_Window_Open()') + " If the window is open, jump to it + let winnum = bufwinnr(g:TagList_title) + if winnum != -1 + " Jump to the existing window + if winnr() != winnum + exe winnum . 'wincmd w' + endif + return + endif + + if s:tlist_app_name == "winmanager" + " Taglist plugin is no longer part of the winmanager app + let s:tlist_app_name = "none" + endif + + " Get the filename and filetype for the specified buffer + let curbuf_name = fnamemodify(bufname('%'), ':p') + let curbuf_ftype = s:Tlist_Get_Buffer_Filetype('%') + let cur_lnum = line('.') + + " Mark the current window as the desired window to open a file when a tag + " is selected. + call s:Tlist_Window_Mark_File_Window() + + " Open the taglist window + call s:Tlist_Window_Create() + + call s:Tlist_Window_Refresh() + + if g:Tlist_Show_One_File + " Add only the current buffer and file + " + " If the file doesn't support tag listing, skip it + if !s:Tlist_Skip_File(curbuf_name, curbuf_ftype) + call s:Tlist_Window_Refresh_File(curbuf_name, curbuf_ftype) + endif + endif + + if g:Tlist_File_Fold_Auto_Close + " Open the fold for the current file, as all the folds in + " the taglist window are closed + let fidx = s:Tlist_Get_File_Index(curbuf_name) + if fidx != -1 + exe "silent! " . s:tlist_{fidx}_start . "," . + \ s:tlist_{fidx}_end . "foldopen!" + endif + endif + + " Highlight the current tag + call s:Tlist_Window_Highlight_Tag(curbuf_name, cur_lnum, 1, 1) +endfunction + +" Tlist_Window_Toggle() +" Open or close a taglist window +function! s:Tlist_Window_Toggle() + call s:Tlist_Log_Msg('Tlist_Window_Toggle()') + " If taglist window is open then close it. + let winnum = bufwinnr(g:TagList_title) + if winnum != -1 + call s:Tlist_Window_Close() + return + endif + + call s:Tlist_Window_Open() + + " Go back to the original window, if Tlist_GainFocus_On_ToggleOpen is not + " set + if !g:Tlist_GainFocus_On_ToggleOpen + call s:Tlist_Exe_Cmd_No_Acmds('wincmd p') + endif + + " Update the taglist menu + if g:Tlist_Show_Menu + call s:Tlist_Menu_Update_File(0) + endif +endfunction + +" Tlist_Process_Filelist +" Process multiple files. Each filename is separated by "\n" +" Returns the number of processed files +function! s:Tlist_Process_Filelist(file_names) + let flist = a:file_names + + " Enable lazy screen updates + let old_lazyredraw = &lazyredraw + set lazyredraw + + " Keep track of the number of processed files + let fcnt = 0 + + " Process one file at a time + while flist != '' + let nl_idx = stridx(flist, "\n") + let one_file = strpart(flist, 0, nl_idx) + + " Remove the filename from the list + let flist = strpart(flist, nl_idx + 1) + + if one_file == '' + continue + endif + + " Skip directories + if isdirectory(one_file) + continue + endif + + let ftype = s:Tlist_Detect_Filetype(one_file) + + echon "\r " + echon "\rProcessing tags for " . fnamemodify(one_file, ':p:t') + + let fcnt = fcnt + 1 + + call Tlist_Update_File(one_file, ftype) + endwhile + + " Clear the displayed informational messages + echon "\r " + + " Restore the previous state + let &lazyredraw = old_lazyredraw + + return fcnt +endfunction + +" Tlist_Process_Dir +" Process the files in a directory matching the specified pattern +function! s:Tlist_Process_Dir(dir_name, pat) + let flist = glob(a:dir_name . '/' . a:pat) . "\n" + + let fcnt = s:Tlist_Process_Filelist(flist) + + let len = strlen(a:dir_name) + if a:dir_name[len - 1] == '\' || a:dir_name[len - 1] == '/' + let glob_expr = a:dir_name . '*' + else + let glob_expr = a:dir_name . '/*' + endif + let all_files = glob(glob_expr) . "\n" + + while all_files != '' + let nl_idx = stridx(all_files, "\n") + let one_file = strpart(all_files, 0, nl_idx) + + let all_files = strpart(all_files, nl_idx + 1) + if one_file == '' + continue + endif + + " Skip non-directory names + if !isdirectory(one_file) + continue + endif + + echon "\r " + echon "\rProcessing files in directory " . fnamemodify(one_file, ':t') + let fcnt = fcnt + s:Tlist_Process_Dir(one_file, a:pat) + endwhile + + return fcnt +endfunction + +" Tlist_Add_Files_Recursive +" Add files recursively from a directory +function! s:Tlist_Add_Files_Recursive(dir, ...) + let dir_name = fnamemodify(a:dir, ':p') + if !isdirectory(dir_name) + call s:Tlist_Warning_Msg('Error: ' . dir_name . ' is not a directory') + return + endif + + if a:0 == 1 + " User specified file pattern + let pat = a:1 + else + " Default file pattern + let pat = '*' + endif + + echon "\r " + echon "\rProcessing files in directory " . fnamemodify(dir_name, ':t') + let fcnt = s:Tlist_Process_Dir(dir_name, pat) + + echon "\rAdded " . fcnt . " files to the taglist" +endfunction + +" Tlist_Add_Files +" Add the specified list of files to the taglist +function! s:Tlist_Add_Files(...) + let flist = '' + let i = 1 + + " Get all the files matching the file patterns supplied as argument + while i <= a:0 + let flist = flist . glob(a:{i}) . "\n" + let i = i + 1 + endwhile + + if flist == '' + call s:Tlist_Warning_Msg('Error: No matching files are found') + return + endif + + let fcnt = s:Tlist_Process_Filelist(flist) + echon "\rAdded " . fcnt . " files to the taglist" +endfunction + +" Tlist_Extract_Tagtype +" Extract the tag type from the tag text +function! s:Tlist_Extract_Tagtype(tag_line) + " The tag type is after the tag prototype field. The prototype field + " ends with the /;"\t string. We add 4 at the end to skip the characters + " in this special string.. + let start = strridx(a:tag_line, '/;"' . "\t") + 4 + let end = strridx(a:tag_line, 'line:') - 1 + let ttype = strpart(a:tag_line, start, end - start) + + return ttype +endfunction + +" Tlist_Extract_Tag_Scope +" Extract the tag scope from the tag text +function! s:Tlist_Extract_Tag_Scope(tag_line) + let start = strridx(a:tag_line, 'line:') + let end = strridx(a:tag_line, "\t") + if end <= start + return '' + endif + + let tag_scope = strpart(a:tag_line, end + 1) + let tag_scope = strpart(tag_scope, stridx(tag_scope, ':') + 1) + + return tag_scope +endfunction + +" Tlist_Refresh() +" Refresh the taglist +function! s:Tlist_Refresh() + call s:Tlist_Log_Msg('Tlist_Refresh (Skip_Refresh = ' . + \ s:Tlist_Skip_Refresh . ', ' . bufname('%') . ')') + " If we are entering the buffer from one of the taglist functions, then + " no need to refresh the taglist window again. + if s:Tlist_Skip_Refresh + " We still need to update the taglist menu + if g:Tlist_Show_Menu + call s:Tlist_Menu_Update_File(0) + endif + return + endif + + " If part of the winmanager plugin and not configured to process + " tags always and not configured to display the tags menu, then return + if (s:tlist_app_name == 'winmanager') && !g:Tlist_Process_File_Always + \ && !g:Tlist_Show_Menu + return + endif + + " Skip buffers with 'buftype' set to nofile, nowrite, quickfix or help + if &buftype != '' + return + endif + + let filename = fnamemodify(bufname('%'), ':p') + let ftype = s:Tlist_Get_Buffer_Filetype('%') + + " If the file doesn't support tag listing, skip it + if s:Tlist_Skip_File(filename, ftype) + return + endif + + let tlist_win = bufwinnr(g:TagList_title) + + " If the taglist window is not opened and not configured to process + " tags always and not displaying the tags menu, then return + if tlist_win == -1 && !g:Tlist_Process_File_Always && !g:Tlist_Show_Menu + return + endif + + let fidx = s:Tlist_Get_File_Index(filename) + if fidx == -1 + " Check whether this file is removed based on user request + " If it is, then don't display the tags for this file + if s:Tlist_User_Removed_File(filename) + return + endif + + " If the taglist should not be auto updated, then return + if !g:Tlist_Auto_Update + return + endif + endif + + let cur_lnum = line('.') + + if fidx == -1 + " Update the tags for the file + let fidx = s:Tlist_Process_File(filename, ftype) + else + let mtime = getftime(filename) + if s:tlist_{fidx}_mtime != mtime + " Invalidate the tags listed for this file + let s:tlist_{fidx}_valid = 0 + + " Update the taglist and the window + call Tlist_Update_File(filename, ftype) + + " Store the new file modification time + let s:tlist_{fidx}_mtime = mtime + endif + endif + + " Update the taglist window + if tlist_win != -1 + " Disable screen updates + let old_lazyredraw = &lazyredraw + set nolazyredraw + + " Save the current window number + let save_winnr = winnr() + + " Goto the taglist window + call s:Tlist_Window_Goto_Window() + + if !g:Tlist_Auto_Highlight_Tag || !g:Tlist_Highlight_Tag_On_BufEnter + " Save the cursor position + let save_line = line('.') + let save_col = col('.') + endif + + " Update the taglist window + call s:Tlist_Window_Refresh_File(filename, ftype) + + " Open the fold for the file + exe "silent! " . s:tlist_{fidx}_start . "," . + \ s:tlist_{fidx}_end . "foldopen!" + + if g:Tlist_Highlight_Tag_On_BufEnter && g:Tlist_Auto_Highlight_Tag + if g:Tlist_Show_One_File && s:tlist_cur_file_idx != fidx + " If displaying tags for only one file in the taglist + " window and about to display the tags for a new file, + " then center the current tag line for the new file + let center_tag_line = 1 + else + let center_tag_line = 0 + endif + + " Highlight the current tag + call s:Tlist_Window_Highlight_Tag(filename, cur_lnum, 1, center_tag_line) + else + " Restore the cursor position + if v:version >= 601 + call cursor(save_line, save_col) + else + exe save_line + exe 'normal! ' . save_col . '|' + endif + endif + + " Jump back to the original window + if save_winnr != winnr() + call s:Tlist_Exe_Cmd_No_Acmds(save_winnr . 'wincmd w') + endif + + " Restore screen updates + let &lazyredraw = old_lazyredraw + endif + + " Update the taglist menu + if g:Tlist_Show_Menu + call s:Tlist_Menu_Update_File(0) + endif +endfunction + +" Tlist_Change_Sort() +" Change the sort order of the tag listing +" caller == 'cmd', command used in the taglist window +" caller == 'menu', taglist menu +" action == 'toggle', toggle sort from name to order and vice versa +" action == 'set', set the sort order to sort_type +function! s:Tlist_Change_Sort(caller, action, sort_type) + call s:Tlist_Log_Msg('Tlist_Change_Sort (caller = ' . a:caller . + \ ', action = ' . a:action . ', sort_type = ' . a:sort_type . ')') + if a:caller == 'cmd' + let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.')) + if fidx == -1 + return + endif + + " Remove the previous highlighting + match none + elseif a:caller == 'menu' + let fidx = s:Tlist_Get_File_Index(fnamemodify(bufname('%'), ':p')) + if fidx == -1 + return + endif + endif + + if a:action == 'toggle' + let sort_type = s:tlist_{fidx}_sort_type + + " Toggle the sort order from 'name' to 'order' and vice versa + if sort_type == 'name' + let s:tlist_{fidx}_sort_type = 'order' + else + let s:tlist_{fidx}_sort_type = 'name' + endif + else + let s:tlist_{fidx}_sort_type = a:sort_type + endif + + " Invalidate the tags listed for this file + let s:tlist_{fidx}_valid = 0 + + if a:caller == 'cmd' + " Save the current line for later restoration + let curline = '\V\^' . getline('.') . '\$' + + call s:Tlist_Window_Refresh_File(s:tlist_{fidx}_filename, + \ s:tlist_{fidx}_filetype) + + exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'foldopen!' + + " Go back to the cursor line before the tag list is sorted + call search(curline, 'w') + + call s:Tlist_Menu_Update_File(1) + else + call s:Tlist_Menu_Remove_File() + + call s:Tlist_Refresh() + endif +endfunction + +" Tlist_Update_Current_File() +" Update taglist for the current buffer by regenerating the tag list +" Contributed by WEN Guopeng. +function! s:Tlist_Update_Current_File() + call s:Tlist_Log_Msg('Tlist_Update_Current_File()') + if winnr() == bufwinnr(g:TagList_title) + " In the taglist window. Update the current file + call s:Tlist_Window_Update_File() + else + " Not in the taglist window. Update the current buffer + let filename = fnamemodify(bufname('%'), ':p') + let fidx = s:Tlist_Get_File_Index(filename) + if fidx != -1 + let s:tlist_{fidx}_valid = 0 + endif + let ft = s:Tlist_Get_Buffer_Filetype('%') + call Tlist_Update_File(filename, ft) + endif +endfunction + +" Tlist_Window_Update_File() +" Update the tags displayed in the taglist window +function! s:Tlist_Window_Update_File() + call s:Tlist_Log_Msg('Tlist_Window_Update_File()') + let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.')) + if fidx == -1 + return + endif + + " Remove the previous highlighting + match none + + " Save the current line for later restoration + let curline = '\V\^' . getline('.') . '\$' + + let s:tlist_{fidx}_valid = 0 + + " Update the taglist window + call s:Tlist_Window_Refresh_File(s:tlist_{fidx}_filename, + \ s:tlist_{fidx}_filetype) + + exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'foldopen!' + + " Go back to the tag line before the list is updated + call search(curline, 'w') +endfunction + +" Tlist_Window_Get_Tag_Type_By_Linenum() +" Return the tag type index for the specified line in the taglist window +function! s:Tlist_Window_Get_Tag_Type_By_Linenum(fidx, lnum) + let ftype = s:tlist_{a:fidx}_filetype + + " Determine to which tag type the current line number belongs to using the + " tag type start line number and the number of tags in a tag type + let i = 1 + while i <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{i}_name + let start_lnum = + \ s:tlist_{a:fidx}_start + s:tlist_{a:fidx}_{ttype}_offset + let end = start_lnum + s:tlist_{a:fidx}_{ttype}_count + if a:lnum >= start_lnum && a:lnum <= end + break + endif + let i = i + 1 + endwhile + + " Current line doesn't belong to any of the displayed tag types + if i > s:tlist_{ftype}_count + return '' + endif + + return ttype +endfunction + +" Tlist_Window_Get_Tag_Index() +" Return the tag index for the specified line in the taglist window +function! s:Tlist_Window_Get_Tag_Index(fidx, lnum) + let ttype = s:Tlist_Window_Get_Tag_Type_By_Linenum(a:fidx, a:lnum) + + " Current line doesn't belong to any of the displayed tag types + if ttype == '' + return 0 + endif + + " Compute the index into the displayed tags for the tag type + let ttype_lnum = s:tlist_{a:fidx}_start + s:tlist_{a:fidx}_{ttype}_offset + let tidx = a:lnum - ttype_lnum + if tidx == 0 + return 0 + endif + + " Get the corresponding tag line and return it + return s:tlist_{a:fidx}_{ttype}_{tidx} +endfunction + +" Tlist_Window_Highlight_Line +" Highlight the current line +function! s:Tlist_Window_Highlight_Line() + " Clear previously selected name + match none + + " Highlight the current line + if g:Tlist_Display_Prototype == 0 + let pat = '/\%' . line('.') . 'l\s\+\zs.*/' + else + let pat = '/\%' . line('.') . 'l.*/' + endif + + exe 'match TagListTagName ' . pat +endfunction + +" Tlist_Window_Open_File +" Open the specified file in either a new window or an existing window +" and place the cursor at the specified tag pattern +function! s:Tlist_Window_Open_File(win_ctrl, filename, tagpat) + call s:Tlist_Log_Msg('Tlist_Window_Open_File (' . a:filename . ',' . + \ a:win_ctrl . ')') + let prev_Tlist_Skip_Refresh = s:Tlist_Skip_Refresh + let s:Tlist_Skip_Refresh = 1 + + if s:tlist_app_name == "winmanager" + " Let the winmanager edit the file + call WinManagerFileEdit(a:filename, a:win_ctrl == 'newwin') + else + + if a:win_ctrl == 'newtab' + " Create a new tab + exe 'tabnew ' . escape(a:filename, ' ') + " Open the taglist window in the new tab + call s:Tlist_Window_Open() + endif + + if a:win_ctrl == 'checktab' + " Check whether the file is present in any of the tabs. + " If the file is present in the current tab, then use the + " current tab. + if bufwinnr(a:filename) != -1 + let file_present_in_tab = 1 + let i = tabpagenr() + else + let i = 1 + let bnum = bufnr(a:filename) + let file_present_in_tab = 0 + while i <= tabpagenr('$') + if index(tabpagebuflist(i), bnum) != -1 + let file_present_in_tab = 1 + break + endif + let i += 1 + endwhile + endif + + if file_present_in_tab + " Goto the tab containing the file + exe 'tabnext ' . i + else + " Open a new tab + exe 'tabnew ' . escape(a:filename, ' ') + + " Open the taglist window + call s:Tlist_Window_Open() + endif + endif + + let winnum = -1 + if a:win_ctrl == 'prevwin' + " Open the file in the previous window, if it is usable + let cur_win = winnr() + wincmd p + if &buftype == '' && !&previewwindow + exe "edit " . escape(a:filename, ' ') + let winnum = winnr() + else + " Previous window is not usable + exe cur_win . 'wincmd w' + endif + endif + + " Goto the window containing the file. If the window is not there, open a + " new window + if winnum == -1 + let winnum = bufwinnr(a:filename) + endif + + if winnum == -1 + " Locate the previously used window for opening a file + let fwin_num = 0 + let first_usable_win = 0 + + let i = 1 + let bnum = winbufnr(i) + while bnum != -1 + if getwinvar(i, 'tlist_file_window') == 'yes' + let fwin_num = i + break + endif + if first_usable_win == 0 && + \ getbufvar(bnum, '&buftype') == '' && + \ !getwinvar(i, '&previewwindow') + " First non-taglist, non-plugin and non-preview window + let first_usable_win = i + endif + let i = i + 1 + let bnum = winbufnr(i) + endwhile + + " If a previously used window is not found, then use the first + " non-taglist window + if fwin_num == 0 + let fwin_num = first_usable_win + endif + + if fwin_num != 0 + " Jump to the file window + exe fwin_num . "wincmd w" + + " If the user asked to jump to the tag in a new window, then split + " the existing window into two. + if a:win_ctrl == 'newwin' + split + endif + exe "edit " . escape(a:filename, ' ') + else + " Open a new window + if g:Tlist_Use_Horiz_Window + exe 'leftabove split ' . escape(a:filename, ' ') + else + if winbufnr(2) == -1 + " Only the taglist window is present + if g:Tlist_Use_Right_Window + exe 'leftabove vertical split ' . + \ escape(a:filename, ' ') + else + exe 'rightbelow vertical split ' . + \ escape(a:filename, ' ') + endif + + " Go to the taglist window to change the window size to + " the user configured value + call s:Tlist_Exe_Cmd_No_Acmds('wincmd p') + if g:Tlist_Use_Horiz_Window + exe 'resize ' . g:Tlist_WinHeight + else + exe 'vertical resize ' . g:Tlist_WinWidth + endif + " Go back to the file window + call s:Tlist_Exe_Cmd_No_Acmds('wincmd p') + else + " A plugin or help window is also present + wincmd w + exe 'leftabove split ' . escape(a:filename, ' ') + endif + endif + endif + " Mark the window, so that it can be reused. + call s:Tlist_Window_Mark_File_Window() + else + if v:version >= 700 + " If the file is opened in more than one window, then check + " whether the last accessed window has the selected file. + " If it does, then use that window. + let lastwin_bufnum = winbufnr(winnr('#')) + if bufnr(a:filename) == lastwin_bufnum + let winnum = winnr('#') + endif + endif + exe winnum . 'wincmd w' + + " If the user asked to jump to the tag in a new window, then split the + " existing window into two. + if a:win_ctrl == 'newwin' + split + endif + endif + endif + + " Jump to the tag + if a:tagpat != '' + " Add the current cursor position to the jump list, so that user can + " jump back using the ' and ` marks. + mark ' + silent call search(a:tagpat, 'w') + + " Bring the line to the middle of the window + normal! z. + + " If the line is inside a fold, open the fold + if foldclosed('.') != -1 + .foldopen + endif + endif + + " If the user selects to preview the tag then jump back to the + " taglist window + if a:win_ctrl == 'preview' + " Go back to the taglist window + let winnum = bufwinnr(g:TagList_title) + exe winnum . 'wincmd w' + else + " If the user has selected to close the taglist window, when a + " tag is selected, close the taglist window + if g:Tlist_Close_On_Select + call s:Tlist_Window_Goto_Window() + close + + " Go back to the window displaying the selected file + let wnum = bufwinnr(a:filename) + if wnum != -1 && wnum != winnr() + call s:Tlist_Exe_Cmd_No_Acmds(wnum . 'wincmd w') + endif + endif + endif + + let s:Tlist_Skip_Refresh = prev_Tlist_Skip_Refresh +endfunction + +" Tlist_Window_Jump_To_Tag() +" Jump to the location of the current tag +" win_ctrl == useopen - Reuse the existing file window +" win_ctrl == newwin - Open a new window +" win_ctrl == preview - Preview the tag +" win_ctrl == prevwin - Open in previous window +" win_ctrl == newtab - Open in new tab +function! s:Tlist_Window_Jump_To_Tag(win_ctrl) + call s:Tlist_Log_Msg('Tlist_Window_Jump_To_Tag(' . a:win_ctrl . ')') + " Do not process comment lines and empty lines + let curline = getline('.') + if curline =~ '^\s*$' || curline[0] == '"' + return + endif + + " If inside a closed fold, then use the first line of the fold + " and jump to the file. + let lnum = foldclosed('.') + if lnum == -1 + " Jump to the selected tag or file + let lnum = line('.') + else + " Open the closed fold + .foldopen! + endif + + let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(lnum) + if fidx == -1 + return + endif + + " Get the tag output for the current tag + let tidx = s:Tlist_Window_Get_Tag_Index(fidx, lnum) + if tidx != 0 + let tagpat = s:Tlist_Get_Tag_SearchPat(fidx, tidx) + + " Highlight the tagline + call s:Tlist_Window_Highlight_Line() + else + " Selected a line which is not a tag name. Just edit the file + let tagpat = '' + endif + + call s:Tlist_Window_Open_File(a:win_ctrl, s:tlist_{fidx}_filename, tagpat) +endfunction + +" Tlist_Window_Show_Info() +" Display information about the entry under the cursor +function! s:Tlist_Window_Show_Info() + call s:Tlist_Log_Msg('Tlist_Window_Show_Info()') + + " Clear the previously displayed line + echo + + " Do not process comment lines and empty lines + let curline = getline('.') + if curline =~ '^\s*$' || curline[0] == '"' + return + endif + + " If inside a fold, then don't display the prototype + if foldclosed('.') != -1 + return + endif + + let lnum = line('.') + + " Get the file index + let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(lnum) + if fidx == -1 + return + endif + + if lnum == s:tlist_{fidx}_start + " Cursor is on a file name + let fname = s:tlist_{fidx}_filename + if strlen(fname) > 50 + let fname = fnamemodify(fname, ':t') + endif + echo fname . ', Filetype=' . s:tlist_{fidx}_filetype . + \ ', Tag count=' . s:tlist_{fidx}_tag_count + return + endif + + " Get the tag output line for the current tag + let tidx = s:Tlist_Window_Get_Tag_Index(fidx, lnum) + if tidx == 0 + " Cursor is on a tag type + let ttype = s:Tlist_Window_Get_Tag_Type_By_Linenum(fidx, lnum) + if ttype == '' + return + endif + + let ttype_name = '' + + let ftype = s:tlist_{fidx}_filetype + let i = 1 + while i <= s:tlist_{ftype}_count + if ttype == s:tlist_{ftype}_{i}_name + let ttype_name = s:tlist_{ftype}_{i}_fullname + break + endif + let i = i + 1 + endwhile + + echo 'Tag type=' . ttype_name . + \ ', Tag count=' . s:tlist_{fidx}_{ttype}_count + return + endif + + " Get the tag search pattern and display it + echo s:Tlist_Get_Tag_Prototype(fidx, tidx) +endfunction + +" Tlist_Find_Nearest_Tag_Idx +" Find the tag idx nearest to the supplied line number +" Returns -1, if a tag couldn't be found for the specified line number +function! s:Tlist_Find_Nearest_Tag_Idx(fidx, linenum) + let sort_type = s:tlist_{a:fidx}_sort_type + + let left = 1 + let right = s:tlist_{a:fidx}_tag_count + + if sort_type == 'order' + " Tags sorted by order, use a binary search. + " The idea behind this function is taken from the ctags.vim script (by + " Alexey Marinichev) available at the Vim online website. + + " If the current line is the less than the first tag, then no need to + " search + let first_lnum = s:Tlist_Get_Tag_Linenum(a:fidx, 1) + + if a:linenum < first_lnum + return -1 + endif + + while left < right + let middle = (right + left + 1) / 2 + let middle_lnum = s:Tlist_Get_Tag_Linenum(a:fidx, middle) + + if middle_lnum == a:linenum + let left = middle + break + endif + + if middle_lnum > a:linenum + let right = middle - 1 + else + let left = middle + endif + endwhile + else + " Tags sorted by name, use a linear search. (contributed by Dave + " Eggum). + " Look for a tag with a line number less than or equal to the supplied + " line number. If multiple tags are found, then use the tag with the + " line number closest to the supplied line number. IOW, use the tag + " with the highest line number. + let closest_lnum = 0 + let final_left = 0 + while left <= right + let lnum = s:Tlist_Get_Tag_Linenum(a:fidx, left) + + if lnum < a:linenum && lnum > closest_lnum + let closest_lnum = lnum + let final_left = left + elseif lnum == a:linenum + let closest_lnum = lnum + let final_left = left + break + else + let left = left + 1 + endif + endwhile + if closest_lnum == 0 + return -1 + endif + if left >= right + let left = final_left + endif + endif + + return left +endfunction + +" Tlist_Window_Highlight_Tag() +" Highlight the current tag +" cntx == 1, Called by the taglist plugin itself +" cntx == 2, Forced by the user through the TlistHighlightTag command +" center = 1, move the tag line to the center of the taglist window +function! s:Tlist_Window_Highlight_Tag(filename, cur_lnum, cntx, center) + " Highlight the current tag only if the user configured the + " taglist plugin to do so or if the user explictly invoked the + " command to highlight the current tag. + if !g:Tlist_Auto_Highlight_Tag && a:cntx == 1 + return + endif + + if a:filename == '' + return + endif + + " Make sure the taglist window is present + let winnum = bufwinnr(g:TagList_title) + if winnum == -1 + call s:Tlist_Warning_Msg('Error: Taglist window is not open') + return + endif + + let fidx = s:Tlist_Get_File_Index(a:filename) + if fidx == -1 + return + endif + + " If the file is currently not displayed in the taglist window, then retrn + if !s:tlist_{fidx}_visible + return + endif + + " If there are no tags for this file, then no need to proceed further + if s:tlist_{fidx}_tag_count == 0 + return + endif + + " Ignore all autocommands + let old_ei = &eventignore + set eventignore=all + + " Save the original window number + let org_winnr = winnr() + + if org_winnr == winnum + let in_taglist_window = 1 + else + let in_taglist_window = 0 + endif + + " Go to the taglist window + if !in_taglist_window + exe winnum . 'wincmd w' + endif + + " Clear previously selected name + match none + + let tidx = s:Tlist_Find_Nearest_Tag_Idx(fidx, a:cur_lnum) + if tidx == -1 + " Make sure the current tag line is visible in the taglist window. + " Calling the winline() function makes the line visible. Don't know + " of a better way to achieve this. + let lnum = line('.') + + if lnum < s:tlist_{fidx}_start || lnum > s:tlist_{fidx}_end + " Move the cursor to the beginning of the file + exe s:tlist_{fidx}_start + endif + + if foldclosed('.') != -1 + .foldopen + endif + + call winline() + + if !in_taglist_window + exe org_winnr . 'wincmd w' + endif + + " Restore the autocommands + let &eventignore = old_ei + return + endif + + " Extract the tag type + let ttype = s:Tlist_Get_Tag_Type_By_Tag(fidx, tidx) + + " Compute the line number + " Start of file + Start of tag type + offset + let lnum = s:tlist_{fidx}_start + s:tlist_{fidx}_{ttype}_offset + + \ s:tlist_{fidx}_{tidx}_ttype_idx + + " Goto the line containing the tag + exe lnum + + " Open the fold + if foldclosed('.') != -1 + .foldopen + endif + + if a:center + " Move the tag line to the center of the taglist window + normal! z. + else + " Make sure the current tag line is visible in the taglist window. + " Calling the winline() function makes the line visible. Don't know + " of a better way to achieve this. + call winline() + endif + + " Highlight the tag name + call s:Tlist_Window_Highlight_Line() + + " Go back to the original window + if !in_taglist_window + exe org_winnr . 'wincmd w' + endif + + " Restore the autocommands + let &eventignore = old_ei + return +endfunction + +" Tlist_Get_Tag_Prototype_By_Line +" Get the prototype for the tag on or before the specified line number in the +" current buffer +function! Tlist_Get_Tag_Prototype_By_Line(...) + if a:0 == 0 + " Arguments are not supplied. Use the current buffer name + " and line number + let filename = bufname('%') + let linenr = line('.') + elseif a:0 == 2 + " Filename and line number are specified + let filename = a:1 + let linenr = a:2 + if linenr !~ '\d\+' + " Invalid line number + return "" + endif + else + " Sufficient arguments are not supplied + let msg = 'Usage: Tlist_Get_Tag_Prototype_By_Line <filename> ' . + \ '<line_number>' + call s:Tlist_Warning_Msg(msg) + return "" + endif + + " Expand the file to a fully qualified name + let filename = fnamemodify(filename, ':p') + if filename == '' + return "" + endif + + let fidx = s:Tlist_Get_File_Index(filename) + if fidx == -1 + return "" + endif + + " If there are no tags for this file, then no need to proceed further + if s:tlist_{fidx}_tag_count == 0 + return "" + endif + + " Get the tag text using the line number + let tidx = s:Tlist_Find_Nearest_Tag_Idx(fidx, linenr) + if tidx == -1 + return "" + endif + + return s:Tlist_Get_Tag_Prototype(fidx, tidx) +endfunction + +" Tlist_Get_Tagname_By_Line +" Get the tag name on or before the specified line number in the +" current buffer +function! Tlist_Get_Tagname_By_Line(...) + if a:0 == 0 + " Arguments are not supplied. Use the current buffer name + " and line number + let filename = bufname('%') + let linenr = line('.') + elseif a:0 == 2 + " Filename and line number are specified + let filename = a:1 + let linenr = a:2 + if linenr !~ '\d\+' + " Invalid line number + return "" + endif + else + " Sufficient arguments are not supplied + let msg = 'Usage: Tlist_Get_Tagname_By_Line <filename> <line_number>' + call s:Tlist_Warning_Msg(msg) + return "" + endif + + " Make sure the current file has a name + let filename = fnamemodify(filename, ':p') + if filename == '' + return "" + endif + + let fidx = s:Tlist_Get_File_Index(filename) + if fidx == -1 + return "" + endif + + " If there are no tags for this file, then no need to proceed further + if s:tlist_{fidx}_tag_count == 0 + return "" + endif + + " Get the tag name using the line number + let tidx = s:Tlist_Find_Nearest_Tag_Idx(fidx, linenr) + if tidx == -1 + return "" + endif + + return s:tlist_{fidx}_{tidx}_tag_name +endfunction + +" Tlist_Window_Move_To_File +" Move the cursor to the beginning of the current file or the next file +" or the previous file in the taglist window +" dir == -1, move to start of current or previous function +" dir == 1, move to start of next function +function! s:Tlist_Window_Move_To_File(dir) + if foldlevel('.') == 0 + " Cursor is on a non-folded line (it is not in any of the files) + " Move it to a folded line + if a:dir == -1 + normal! zk + else + " While moving down to the start of the next fold, + " no need to do go to the start of the next file. + normal! zj + return + endif + endif + + let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.')) + if fidx == -1 + return + endif + + let cur_lnum = line('.') + + if a:dir == -1 + if cur_lnum > s:tlist_{fidx}_start + " Move to the beginning of the current file + exe s:tlist_{fidx}_start + return + endif + + if fidx != 0 + " Move to the beginning of the previous file + let fidx = fidx - 1 + else + " Cursor is at the first file, wrap around to the last file + let fidx = s:tlist_file_count - 1 + endif + + exe s:tlist_{fidx}_start + return + else + " Move to the beginning of the next file + let fidx = fidx + 1 + + if fidx >= s:tlist_file_count + " Cursor is at the last file, wrap around to the first file + let fidx = 0 + endif + + if s:tlist_{fidx}_start != 0 + exe s:tlist_{fidx}_start + endif + return + endif +endfunction + +" Tlist_Session_Load +" Load a taglist session (information about all the displayed files +" and the tags) from the specified file +function! s:Tlist_Session_Load(...) + if a:0 == 0 || a:1 == '' + call s:Tlist_Warning_Msg('Usage: TlistSessionLoad <filename>') + return + endif + + let sessionfile = a:1 + + if !filereadable(sessionfile) + let msg = 'Taglist: Error - Unable to open file ' . sessionfile + call s:Tlist_Warning_Msg(msg) + return + endif + + " Mark the current window as the file window + call s:Tlist_Window_Mark_File_Window() + + " Source the session file + exe 'source ' . sessionfile + + let new_file_count = g:tlist_file_count + unlet! g:tlist_file_count + + let i = 0 + while i < new_file_count + let ftype = g:tlist_{i}_filetype + unlet! g:tlist_{i}_filetype + + if !exists('s:tlist_' . ftype . '_count') + if s:Tlist_FileType_Init(ftype) == 0 + let i = i + 1 + continue + endif + endif + + let fname = g:tlist_{i}_filename + unlet! g:tlist_{i}_filename + + let fidx = s:Tlist_Get_File_Index(fname) + if fidx != -1 + let s:tlist_{fidx}_visible = 0 + let i = i + 1 + continue + else + " As we are loading the tags from the session file, if this + " file was previously deleted by the user, now we need to + " add it back. So remove the file from the deleted list. + call s:Tlist_Update_Remove_List(fname, 0) + endif + + let fidx = s:Tlist_Init_File(fname, ftype) + + let s:tlist_{fidx}_filename = fname + + let s:tlist_{fidx}_sort_type = g:tlist_{i}_sort_type + unlet! g:tlist_{i}_sort_type + + let s:tlist_{fidx}_filetype = ftype + let s:tlist_{fidx}_mtime = getftime(fname) + + let s:tlist_{fidx}_start = 0 + let s:tlist_{fidx}_end = 0 + + let s:tlist_{fidx}_valid = 1 + + let s:tlist_{fidx}_tag_count = g:tlist_{i}_tag_count + unlet! g:tlist_{i}_tag_count + + let j = 1 + while j <= s:tlist_{fidx}_tag_count + let s:tlist_{fidx}_{j}_tag = g:tlist_{i}_{j}_tag + let s:tlist_{fidx}_{j}_tag_name = g:tlist_{i}_{j}_tag_name + let s:tlist_{fidx}_{j}_ttype_idx = g:tlist_{i}_{j}_ttype_idx + unlet! g:tlist_{i}_{j}_tag + unlet! g:tlist_{i}_{j}_tag_name + unlet! g:tlist_{i}_{j}_ttype_idx + let j = j + 1 + endwhile + + let j = 1 + while j <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{j}_name + + if exists('g:tlist_' . i . '_' . ttype) + let s:tlist_{fidx}_{ttype} = g:tlist_{i}_{ttype} + unlet! g:tlist_{i}_{ttype} + let s:tlist_{fidx}_{ttype}_offset = 0 + let s:tlist_{fidx}_{ttype}_count = g:tlist_{i}_{ttype}_count + unlet! g:tlist_{i}_{ttype}_count + + let k = 1 + while k <= s:tlist_{fidx}_{ttype}_count + let s:tlist_{fidx}_{ttype}_{k} = g:tlist_{i}_{ttype}_{k} + unlet! g:tlist_{i}_{ttype}_{k} + let k = k + 1 + endwhile + else + let s:tlist_{fidx}_{ttype} = '' + let s:tlist_{fidx}_{ttype}_offset = 0 + let s:tlist_{fidx}_{ttype}_count = 0 + endif + + let j = j + 1 + endwhile + + let i = i + 1 + endwhile + + " If the taglist window is open, then update it + let winnum = bufwinnr(g:TagList_title) + if winnum != -1 + let save_winnr = winnr() + + " Goto the taglist window + call s:Tlist_Window_Goto_Window() + + " Refresh the taglist window + call s:Tlist_Window_Refresh() + + " Go back to the original window + if save_winnr != winnr() + call s:Tlist_Exe_Cmd_No_Acmds('wincmd p') + endif + endif +endfunction + +" Tlist_Session_Save +" Save a taglist session (information about all the displayed files +" and the tags) into the specified file +function! s:Tlist_Session_Save(...) + if a:0 == 0 || a:1 == '' + call s:Tlist_Warning_Msg('Usage: TlistSessionSave <filename>') + return + endif + + let sessionfile = a:1 + + if s:tlist_file_count == 0 + " There is nothing to save + call s:Tlist_Warning_Msg('Warning: Taglist is empty. Nothing to save.') + return + endif + + if filereadable(sessionfile) + let ans = input('Do you want to overwrite ' . sessionfile . ' (Y/N)?') + if ans !=? 'y' + return + endif + + echo "\n" + endif + + let old_verbose = &verbose + set verbose&vim + + exe 'redir! > ' . sessionfile + + silent! echo '" Taglist session file. This file is auto-generated.' + silent! echo '" File information' + silent! echo 'let tlist_file_count = ' . s:tlist_file_count + + let i = 0 + + while i < s:tlist_file_count + " Store information about the file + silent! echo 'let tlist_' . i . "_filename = '" . + \ s:tlist_{i}_filename . "'" + silent! echo 'let tlist_' . i . '_sort_type = "' . + \ s:tlist_{i}_sort_type . '"' + silent! echo 'let tlist_' . i . '_filetype = "' . + \ s:tlist_{i}_filetype . '"' + silent! echo 'let tlist_' . i . '_tag_count = ' . + \ s:tlist_{i}_tag_count + " Store information about all the tags + let j = 1 + while j <= s:tlist_{i}_tag_count + let txt = escape(s:tlist_{i}_{j}_tag, '"\\') + silent! echo 'let tlist_' . i . '_' . j . '_tag = "' . txt . '"' + silent! echo 'let tlist_' . i . '_' . j . '_tag_name = "' . + \ s:tlist_{i}_{j}_tag_name . '"' + silent! echo 'let tlist_' . i . '_' . j . '_ttype_idx' . ' = ' . + \ s:tlist_{i}_{j}_ttype_idx + let j = j + 1 + endwhile + + " Store information about all the tags grouped by their type + let ftype = s:tlist_{i}_filetype + let j = 1 + while j <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{j}_name + if s:tlist_{i}_{ttype}_count != 0 + let txt = escape(s:tlist_{i}_{ttype}, '"\') + let txt = substitute(txt, "\n", "\\\\n", 'g') + silent! echo 'let tlist_' . i . '_' . ttype . ' = "' . + \ txt . '"' + silent! echo 'let tlist_' . i . '_' . ttype . '_count = ' . + \ s:tlist_{i}_{ttype}_count + let k = 1 + while k <= s:tlist_{i}_{ttype}_count + silent! echo 'let tlist_' . i . '_' . ttype . '_' . k . + \ ' = ' . s:tlist_{i}_{ttype}_{k} + let k = k + 1 + endwhile + endif + let j = j + 1 + endwhile + + silent! echo + + let i = i + 1 + endwhile + + redir END + + let &verbose = old_verbose +endfunction + +" Tlist_Buffer_Removed +" A buffer is removed from the Vim buffer list. Remove the tags defined +" for that file +function! s:Tlist_Buffer_Removed(filename) + call s:Tlist_Log_Msg('Tlist_Buffer_Removed (' . a:filename . ')') + + " Make sure a valid filename is supplied + if a:filename == '' + return + endif + + " Get tag list index of the specified file + let fidx = s:Tlist_Get_File_Index(a:filename) + if fidx == -1 + " File not present in the taglist + return + endif + + " Remove the file from the list + call s:Tlist_Remove_File(fidx, 0) +endfunction + +" When a buffer is deleted, remove the file from the taglist +autocmd BufDelete * silent call s:Tlist_Buffer_Removed(expand('<afile>:p')) + +" Tlist_Window_Open_File_Fold +" Open the fold for the specified file and close the fold for all the +" other files +function! s:Tlist_Window_Open_File_Fold(acmd_bufnr) + call s:Tlist_Log_Msg('Tlist_Window_Open_File_Fold (' . a:acmd_bufnr . ')') + + " Make sure the taglist window is present + let winnum = bufwinnr(g:TagList_title) + if winnum == -1 + call s:Tlist_Warning_Msg('Taglist: Error - Taglist window is not open') + return + endif + + " Save the original window number + let org_winnr = winnr() + if org_winnr == winnum + let in_taglist_window = 1 + else + let in_taglist_window = 0 + endif + + if in_taglist_window + " When entering the taglist window, no need to update the folds + return + endif + + " Go to the taglist window + if !in_taglist_window + call s:Tlist_Exe_Cmd_No_Acmds(winnum . 'wincmd w') + endif + + " Close all the folds + silent! %foldclose + + " Get tag list index of the specified file + let fname = fnamemodify(bufname(a:acmd_bufnr + 0), ':p') + if filereadable(fname) + let fidx = s:Tlist_Get_File_Index(fname) + if fidx != -1 + " Open the fold for the file + exe "silent! " . s:tlist_{fidx}_start . "," . + \ s:tlist_{fidx}_end . "foldopen" + endif + endif + + " Go back to the original window + if !in_taglist_window + call s:Tlist_Exe_Cmd_No_Acmds(org_winnr . 'wincmd w') + endif +endfunction + +" Tlist_Window_Check_Auto_Open +" Open the taglist window automatically on Vim startup. +" Open the window only when files present in any of the Vim windows support +" tags. +function! s:Tlist_Window_Check_Auto_Open() + let open_window = 0 + + let i = 1 + let buf_num = winbufnr(i) + while buf_num != -1 + let filename = fnamemodify(bufname(buf_num), ':p') + let ft = s:Tlist_Get_Buffer_Filetype(buf_num) + if !s:Tlist_Skip_File(filename, ft) + let open_window = 1 + break + endif + let i = i + 1 + let buf_num = winbufnr(i) + endwhile + + if open_window + call s:Tlist_Window_Toggle() + endif +endfunction + +" Tlist_Refresh_Folds +" Remove and create the folds for all the files displayed in the taglist +" window. Used after entering a tab. If this is not done, then the folds +" are not properly created for taglist windows displayed in multiple tabs. +function! s:Tlist_Refresh_Folds() + let winnum = bufwinnr(g:TagList_title) + if winnum == -1 + return + endif + + let save_wnum = winnr() + exe winnum . 'wincmd w' + + " First remove all the existing folds + normal! zE + + " Create the folds for each in the tag list + let fidx = 0 + while fidx < s:tlist_file_count + let ftype = s:tlist_{fidx}_filetype + + " Create the folds for each tag type in a file + let j = 1 + while j <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{j}_name + if s:tlist_{fidx}_{ttype}_count + let s = s:tlist_{fidx}_start + s:tlist_{fidx}_{ttype}_offset + let e = s + s:tlist_{fidx}_{ttype}_count + exe s . ',' . e . 'fold' + endif + let j = j + 1 + endwhile + + exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'fold' + exe 'silent! ' . s:tlist_{fidx}_start . ',' . + \ s:tlist_{fidx}_end . 'foldopen!' + let fidx = fidx + 1 + endwhile + + exe save_wnum . 'wincmd w' +endfunction + +function! s:Tlist_Menu_Add_Base_Menu() + call s:Tlist_Log_Msg('Adding the base menu') + + " Add the menu + anoremenu <silent> T&ags.Refresh\ menu :call <SID>Tlist_Menu_Refresh()<CR> + anoremenu <silent> T&ags.Sort\ menu\ by.Name + \ :call <SID>Tlist_Change_Sort('menu', 'set', 'name')<CR> + anoremenu <silent> T&ags.Sort\ menu\ by.Order + \ :call <SID>Tlist_Change_Sort('menu', 'set', 'order')<CR> + anoremenu T&ags.-SEP1- : + + if &mousemodel =~ 'popup' + anoremenu <silent> PopUp.T&ags.Refresh\ menu + \ :call <SID>Tlist_Menu_Refresh()<CR> + anoremenu <silent> PopUp.T&ags.Sort\ menu\ by.Name + \ :call <SID>Tlist_Change_Sort('menu', 'set', 'name')<CR> + anoremenu <silent> PopUp.T&ags.Sort\ menu\ by.Order + \ :call <SID>Tlist_Change_Sort('menu', 'set', 'order')<CR> + anoremenu PopUp.T&ags.-SEP1- : + endif +endfunction + +let s:menu_char_prefix = + \ '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + +" Tlist_Menu_Get_Tag_Type_Cmd +" Get the menu command for the specified tag type +" fidx - File type index +" ftype - File Type +" add_ttype_name - To add or not to add the tag type name to the menu entries +" ttype_idx - Tag type index +function! s:Tlist_Menu_Get_Tag_Type_Cmd(fidx, ftype, add_ttype_name, ttype_idx) + " Curly brace variable name optimization + let ftype_ttype_idx = a:ftype . '_' . a:ttype_idx + + let ttype = s:tlist_{ftype_ttype_idx}_name + if a:add_ttype_name + " If the tag type name contains space characters, escape it. This + " will be used to create the menu entries. + let ttype_fullname = escape(s:tlist_{ftype_ttype_idx}_fullname, ' ') + endif + + " Curly brace variable name optimization + let fidx_ttype = a:fidx . '_' . ttype + + " Number of tag entries for this tag type + let tcnt = s:tlist_{fidx_ttype}_count + if tcnt == 0 " No entries for this tag type + return '' + endif + + let mcmd = '' + + " Create the menu items for the tags. + " Depending on the number of tags of this type, split the menu into + " multiple sub-menus, if needed. + if tcnt > g:Tlist_Max_Submenu_Items + let j = 1 + while j <= tcnt + let final_index = j + g:Tlist_Max_Submenu_Items - 1 + if final_index > tcnt + let final_index = tcnt + endif + + " Extract the first and last tag name and form the + " sub-menu name + let tidx = s:tlist_{fidx_ttype}_{j} + let first_tag = s:tlist_{a:fidx}_{tidx}_tag_name + + let tidx = s:tlist_{fidx_ttype}_{final_index} + let last_tag = s:tlist_{a:fidx}_{tidx}_tag_name + + " Truncate the names, if they are greater than the + " max length + let first_tag = strpart(first_tag, 0, g:Tlist_Max_Tag_Length) + let last_tag = strpart(last_tag, 0, g:Tlist_Max_Tag_Length) + + " Form the menu command prefix + let m_prefix = 'anoremenu <silent> T\&ags.' + if a:add_ttype_name + let m_prefix = m_prefix . ttype_fullname . '.' + endif + let m_prefix = m_prefix . first_tag . '\.\.\.' . last_tag . '.' + + " Character prefix used to number the menu items (hotkey) + let m_prefix_idx = 0 + + while j <= final_index + let tidx = s:tlist_{fidx_ttype}_{j} + + let tname = s:tlist_{a:fidx}_{tidx}_tag_name + + let mcmd = mcmd . m_prefix . '\&' . + \ s:menu_char_prefix[m_prefix_idx] . '\.' . + \ tname . ' :call <SID>Tlist_Menu_Jump_To_Tag(' . + \ tidx . ')<CR>|' + + let m_prefix_idx = m_prefix_idx + 1 + let j = j + 1 + endwhile + endwhile + else + " Character prefix used to number the menu items (hotkey) + let m_prefix_idx = 0 + + let m_prefix = 'anoremenu <silent> T\&ags.' + if a:add_ttype_name + let m_prefix = m_prefix . ttype_fullname . '.' + endif + let j = 1 + while j <= tcnt + let tidx = s:tlist_{fidx_ttype}_{j} + + let tname = s:tlist_{a:fidx}_{tidx}_tag_name + + let mcmd = mcmd . m_prefix . '\&' . + \ s:menu_char_prefix[m_prefix_idx] . '\.' . + \ tname . ' :call <SID>Tlist_Menu_Jump_To_Tag(' . tidx + \ . ')<CR>|' + + let m_prefix_idx = m_prefix_idx + 1 + let j = j + 1 + endwhile + endif + + return mcmd +endfunction + +" Update the taglist menu with the tags for the specified file +function! s:Tlist_Menu_File_Refresh(fidx) + call s:Tlist_Log_Msg('Refreshing the tag menu for ' . s:tlist_{a:fidx}_filename) + " The 'B' flag is needed in the 'cpoptions' option + let old_cpoptions = &cpoptions + set cpoptions&vim + + exe s:tlist_{a:fidx}_menu_cmd + + " Update the popup menu (if enabled) + if &mousemodel =~ 'popup' + let cmd = substitute(s:tlist_{a:fidx}_menu_cmd, ' T\\&ags\.', + \ ' PopUp.T\\\&ags.', "g") + exe cmd + endif + + " The taglist menu is not empty now + let s:tlist_menu_empty = 0 + + " Restore the 'cpoptions' settings + let &cpoptions = old_cpoptions +endfunction + +" Tlist_Menu_Update_File +" Add the taglist menu +function! s:Tlist_Menu_Update_File(clear_menu) + if !has('gui_running') + " Not running in GUI mode + return + endif + + call s:Tlist_Log_Msg('Updating the tag menu, clear_menu = ' . a:clear_menu) + + " Remove the tags menu + if a:clear_menu + call s:Tlist_Menu_Remove_File() + + endif + + " Skip buffers with 'buftype' set to nofile, nowrite, quickfix or help + if &buftype != '' + return + endif + + let filename = fnamemodify(bufname('%'), ':p') + let ftype = s:Tlist_Get_Buffer_Filetype('%') + + " If the file doesn't support tag listing, skip it + if s:Tlist_Skip_File(filename, ftype) + return + endif + + let fidx = s:Tlist_Get_File_Index(filename) + if fidx == -1 || !s:tlist_{fidx}_valid + " Check whether this file is removed based on user request + " If it is, then don't display the tags for this file + if s:Tlist_User_Removed_File(filename) + return + endif + + " Process the tags for the file + let fidx = s:Tlist_Process_File(filename, ftype) + if fidx == -1 + return + endif + endif + + let fname = escape(fnamemodify(bufname('%'), ':t'), '.') + if fname != '' + exe 'anoremenu T&ags.' . fname . ' <Nop>' + anoremenu T&ags.-SEP2- : + endif + + if !s:tlist_{fidx}_tag_count + return + endif + + if s:tlist_{fidx}_menu_cmd != '' + " Update the menu with the cached command + call s:Tlist_Menu_File_Refresh(fidx) + + return + endif + + " We are going to add entries to the tags menu, so the menu won't be + " empty + let s:tlist_menu_empty = 0 + + let cmd = '' + + " Determine whether the tag type name needs to be added to the menu + " If more than one tag type is present in the taglisting for a file, + " then the tag type name needs to be present + let add_ttype_name = -1 + let i = 1 + while i <= s:tlist_{ftype}_count && add_ttype_name < 1 + let ttype = s:tlist_{ftype}_{i}_name + if s:tlist_{fidx}_{ttype}_count + let add_ttype_name = add_ttype_name + 1 + endif + let i = i + 1 + endwhile + + " Process the tags by the tag type and get the menu command + let i = 1 + while i <= s:tlist_{ftype}_count + let mcmd = s:Tlist_Menu_Get_Tag_Type_Cmd(fidx, ftype, add_ttype_name, i) + if mcmd != '' + let cmd = cmd . mcmd + endif + + let i = i + 1 + endwhile + + " Cache the menu command for reuse + let s:tlist_{fidx}_menu_cmd = cmd + + " Update the menu + call s:Tlist_Menu_File_Refresh(fidx) +endfunction + +" Tlist_Menu_Remove_File +" Remove the tags displayed in the tags menu +function! s:Tlist_Menu_Remove_File() + if !has('gui_running') || s:tlist_menu_empty + return + endif + + call s:Tlist_Log_Msg('Removing the tags menu for a file') + + " Cleanup the Tags menu + silent! unmenu T&ags + if &mousemodel =~ 'popup' + silent! unmenu PopUp.T&ags + endif + + " Add a dummy menu item to retain teared off menu + noremenu T&ags.Dummy l + + silent! unmenu! T&ags + if &mousemodel =~ 'popup' + silent! unmenu! PopUp.T&ags + endif + + call s:Tlist_Menu_Add_Base_Menu() + + " Remove the dummy menu item + unmenu T&ags.Dummy + + let s:tlist_menu_empty = 1 +endfunction + +" Tlist_Menu_Refresh +" Refresh the taglist menu +function! s:Tlist_Menu_Refresh() + call s:Tlist_Log_Msg('Refreshing the tags menu') + let fidx = s:Tlist_Get_File_Index(fnamemodify(bufname('%'), ':p')) + if fidx != -1 + " Invalidate the cached menu command + let s:tlist_{fidx}_menu_cmd = '' + endif + + " Update the taglist, menu and window + call s:Tlist_Update_Current_File() +endfunction + +" Tlist_Menu_Jump_To_Tag +" Jump to the selected tag +function! s:Tlist_Menu_Jump_To_Tag(tidx) + let fidx = s:Tlist_Get_File_Index(fnamemodify(bufname('%'), ':p')) + if fidx == -1 + return + endif + + let tagpat = s:Tlist_Get_Tag_SearchPat(fidx, a:tidx) + if tagpat == '' + return + endif + + " Add the current cursor position to the jump list, so that user can + " jump back using the ' and ` marks. + mark ' + + silent call search(tagpat, 'w') + + " Bring the line to the middle of the window + normal! z. + + " If the line is inside a fold, open the fold + if foldclosed('.') != -1 + .foldopen + endif +endfunction + +" Tlist_Menu_Init +" Initialize the taglist menu +function! s:Tlist_Menu_Init() + call s:Tlist_Menu_Add_Base_Menu() + + " Automatically add the tags defined in the current file to the menu + augroup TagListMenuCmds + autocmd! + + if !g:Tlist_Process_File_Always + autocmd BufEnter * call s:Tlist_Refresh() + endif + autocmd BufLeave * call s:Tlist_Menu_Remove_File() + augroup end + + call s:Tlist_Menu_Update_File(0) +endfunction + +" Tlist_Vim_Session_Load +" Initialize the taglist window/buffer, which is created when loading +" a Vim session file. +function! s:Tlist_Vim_Session_Load() + call s:Tlist_Log_Msg('Tlist_Vim_Session_Load') + + " Initialize the taglist window + call s:Tlist_Window_Init() + + " Refresh the taglist window + call s:Tlist_Window_Refresh() +endfunction + +" Tlist_Set_App +" Set the name of the external plugin/application to which taglist +" belongs. +" Taglist plugin is part of another plugin like cream or winmanager. +function! Tlist_Set_App(name) + if a:name == "" + return + endif + + let s:tlist_app_name = a:name +endfunction + +" Winmanager integration + +" Initialization required for integration with winmanager +function! TagList_Start() + " If current buffer is not taglist buffer, then don't proceed + if bufname('%') != '__Tag_List__' + return + endif + + call Tlist_Set_App('winmanager') + + " Get the current filename from the winmanager plugin + let bufnum = WinManagerGetLastEditedFile() + if bufnum != -1 + let filename = fnamemodify(bufname(bufnum), ':p') + let ftype = s:Tlist_Get_Buffer_Filetype(bufnum) + endif + + " Initialize the taglist window, if it is not already initialized + if !exists('s:tlist_window_initialized') || !s:tlist_window_initialized + call s:Tlist_Window_Init() + call s:Tlist_Window_Refresh() + let s:tlist_window_initialized = 1 + endif + + " Update the taglist window + if bufnum != -1 + if !s:Tlist_Skip_File(filename, ftype) && g:Tlist_Auto_Update + call s:Tlist_Window_Refresh_File(filename, ftype) + endif + endif +endfunction + +function! TagList_IsValid() + return 0 +endfunction + +function! TagList_WrapUp() + return 0 +endfunction + +" restore 'cpo' +let &cpo = s:cpo_save +unlet s:cpo_save + diff --git a/modules/vim/vim.dot.link/plugin/vcsbzr.vim b/modules/vim/vim.dot.link/plugin/vcsbzr.vim new file mode 100644 index 0000000..b8665b8 --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/vcsbzr.vim @@ -0,0 +1,262 @@ +" vim600: set foldmethod=marker: +" +" BZR extension for VCSCommand. +" +" Maintainer: Bob Hiestand <bob.hiestand@gmail.com> +" License: +" Copyright (c) Bob Hiestand +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to +" deal in the Software without restriction, including without limitation the +" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +" sell copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in +" all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +" IN THE SOFTWARE. +" +" Section: Documentation {{{1 +" +" Options documentation: {{{2 +" +" VCSCommandBZRExec +" This variable specifies the BZR executable. If not set, it defaults to +" 'bzr' executed from the user's executable path. + +" Section: Plugin header {{{1 + +if exists('VCSCommandDisableAll') + finish +endif + +if v:version < 700 + echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None + finish +endif + +runtime plugin/vcscommand.vim + +if !executable(VCSCommandGetOption('VCSCommandBZRExec', 'bzr')) + " BZR is not installed + finish +endif + +let s:save_cpo=&cpo +set cpo&vim + +" Section: Variable initialization {{{1 + +let s:bzrFunctions = {} + +" Section: Utility functions {{{1 + +" Function: s:Executable() {{{2 +" Returns the executable used to invoke bzr suitable for use in a shell +" command. +function! s:Executable() + return shellescape(VCSCommandGetOption('VCSCommandBZRExec', 'bzr')) +endfunction + +" Function: s:DoCommand(cmd, cmdName, statusText) {{{2 +" Wrapper to VCSCommandDoCommand to add the name of the BZR executable to the +" command argument. +function! s:DoCommand(cmd, cmdName, statusText, options) + if VCSCommandGetVCSType(expand('%')) == 'BZR' + let fullCmd = s:Executable() . ' ' . a:cmd + return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options) + else + throw 'BZR VCSCommand plugin called on non-BZR item.' + endif +endfunction + +" Section: VCS function implementations {{{1 + +" Function: s:bzrFunctions.Identify(buffer) {{{2 +function! s:bzrFunctions.Identify(buffer) + let fileName = resolve(bufname(a:buffer)) + let l:save_bzr_log=$BZR_LOG + try + let $BZR_LOG=has("win32") || has("win95") || has("win64") || has("win16") ? "nul" : "/dev/null" + let statusText = s:VCSCommandUtility.system(s:Executable() . ' info -- "' . fileName . '"') + finally + let $BZR_LOG=l:save_bzr_log + endtry + if(v:shell_error) + return 0 + else + return 1 + endif +endfunction + +" Function: s:bzrFunctions.Add() {{{2 +function! s:bzrFunctions.Add(argList) + return s:DoCommand(join(['add'] + a:argList, ' '), 'add', join(a:argList, ' '), {}) +endfunction + +" Function: s:bzrFunctions.Annotate(argList) {{{2 +function! s:bzrFunctions.Annotate(argList) + if len(a:argList) == 0 + if &filetype == 'BZRannotate' + " Perform annotation of the version indicated by the current line. + let caption = matchstr(getline('.'),'\v^\s+\zs\d+') + let options = ' -r' . caption + else + let caption = '' + let options = '' + endif + elseif len(a:argList) == 1 && a:argList[0] !~ '^-' + let caption = a:argList[0] + let options = ' -r' . caption + else + let caption = join(a:argList, ' ') + let options = ' ' . caption + endif + + let resultBuffer = s:DoCommand('blame' . options, 'annotate', caption, {}) + if resultBuffer > 0 + normal 1G2dd + endif + return resultBuffer +endfunction + +" Function: s:bzrFunctions.Commit(argList) {{{2 +function! s:bzrFunctions.Commit(argList) + let resultBuffer = s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {}) + if resultBuffer == 0 + echomsg 'No commit needed.' + endif +endfunction + +" Function: s:bzrFunctions.Delete() {{{2 +function! s:bzrFunctions.Delete(argList) + return s:DoCommand(join(['rm'] + a:argList, ' '), 'rm', join(a:argList, ' '), {}) +endfunction + +" Function: s:bzrFunctions.Diff(argList) {{{2 +function! s:bzrFunctions.Diff(argList) + if len(a:argList) == 0 + let revOptions = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let revOptions = ['-r' . join(a:argList, '..')] + let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')' + else + " Pass-through + let caption = join(a:argList, ' ') + let revOptions = a:argList + endif + + return s:DoCommand(join(['diff'] + revOptions), 'diff', caption, {'allowNonZeroExit': 1}) +endfunction + +" Function: s:bzrFunctions.GetBufferInfo() {{{2 +" Provides version control details for the current file. Current version +" number and current repository version number are required to be returned by +" the vcscommand plugin. +" Returns: List of results: [revision, repository] + +function! s:bzrFunctions.GetBufferInfo() + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + let fileName = resolve(bufname(originalBuffer)) + let statusText = s:VCSCommandUtility.system(s:Executable() . ' status -S -- "' . fileName . '"') + let revision = s:VCSCommandUtility.system(s:Executable() . ' revno -- "' . fileName . '"') + if(v:shell_error) + return [] + endif + + " File not under BZR control. + if statusText =~ '^?' + return ['Unknown'] + endif + + let [flags, repository] = matchlist(statusText, '^\(.\{3}\)\s\+\(\S\+\)')[1:2] + if revision == '' + " Error + return ['Unknown'] + elseif flags =~ '^A' + return ['New', 'New'] + else + return [revision, repository] + endif +endfunction + +" Function: s:bzrFunctions.Info(argList) {{{2 +function! s:bzrFunctions.Info(argList) + return s:DoCommand(join(['version-info'] + a:argList, ' '), 'version-info', join(a:argList, ' '), {}) +endfunction + +" Function: s:bzrFunctions.Lock(argList) {{{2 +function! s:bzrFunctions.Lock(argList) + echomsg 'bzr lock is not necessary' +endfunction + +" Function: s:bzrFunctions.Log() {{{2 +function! s:bzrFunctions.Log(argList) + if len(a:argList) == 0 + let options = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let options = ['-r' . join(a:argList, ':')] + let caption = options[0] + else + " Pass-through + let options = a:argList + let caption = join(a:argList, ' ') + endif + + let resultBuffer = s:DoCommand(join(['log', '-v'] + options), 'log', caption, {}) + return resultBuffer +endfunction + +" Function: s:bzrFunctions.Revert(argList) {{{2 +function! s:bzrFunctions.Revert(argList) + return s:DoCommand('revert', 'revert', '', {}) +endfunction + +" Function: s:bzrFunctions.Review(argList) {{{2 +function! s:bzrFunctions.Review(argList) + if len(a:argList) == 0 + let versiontag = '(current)' + let versionOption = '' + else + let versiontag = a:argList[0] + let versionOption = ' -r ' . versiontag . ' ' + endif + + return s:DoCommand('cat' . versionOption, 'review', versiontag, {}) +endfunction + +" Function: s:bzrFunctions.Status(argList) {{{2 +function! s:bzrFunctions.Status(argList) + let options = ['-S'] + if len(a:argList) != 0 + let options = a:argList + endif + return s:DoCommand(join(['status'] + options, ' '), 'status', join(options, ' '), {}) +endfunction + +" Function: s:bzrFunctions.Unlock(argList) {{{2 +function! s:bzrFunctions.Unlock(argList) + echomsg 'bzr unlock is not necessary' +endfunction +" Function: s:bzrFunctions.Update(argList) {{{2 +function! s:bzrFunctions.Update(argList) + return s:DoCommand('update', 'update', '', {}) +endfunction + +" Annotate setting {{{2 +let s:bzrFunctions.AnnotateSplitRegex = '^[^|]\+ | ' + +" Section: Plugin Registration {{{1 +let s:VCSCommandUtility = VCSCommandRegisterModule('BZR', expand('<sfile>'), s:bzrFunctions, []) + +let &cpo = s:save_cpo diff --git a/modules/vim/vim.dot.link/plugin/vcscommand.vim b/modules/vim/vim.dot.link/plugin/vcscommand.vim new file mode 100644 index 0000000..98214f2 --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/vcscommand.vim @@ -0,0 +1,1427 @@ +" vim600: set foldmethod=marker: +" +" Vim plugin to assist in working with files under control of various Version +" Control Systems, such as CVS, SVN, SVK, and git. +" +" Maintainer: Bob Hiestand <bob.hiestand@gmail.com> +" License: +" Copyright (c) Bob Hiestand +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to +" deal in the Software without restriction, including without limitation the +" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +" sell copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in +" all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +" IN THE SOFTWARE. +" +" Section: Documentation {{{1 +" +" Provides functions to invoke various source control commands on the current +" file (either the current buffer, or, in the case of an directory buffer, the +" directory and all subdirectories associated with the current buffer). The +" output of the commands is captured in a new scratch window. +" +" This plugin needs additional extension plugins, each specific to a source +" control system, to function. Several options include the name of the +" version control system in the option name. Such options use the placeholder +" text '{VCSType}', which would be replaced in actual usage with 'CVS' or +" 'SVN', for instance. +" +" Command documentation {{{2 +" +" VCSAdd Adds the current file to source control. +" +" VCSAnnotate[!] Displays the current file with each line annotated with the +" version in which it was most recently changed. If an +" argument is given, the argument is used as a revision +" number to display. If not given an argument, it uses the +" most recent version of the file on the current branch. +" Additionally, if the current buffer is a VCSAnnotate buffer +" already, the version number on the current line is used. +" +" If '!' is used, the view of the annotated buffer is split +" so that the annotation is in a separate window from the +" content, and each is highlighted separately. +" +" VCSBlame Alias for 'VCSAnnotate'. +" +" VCSCommit[!] Commits changes to the current file to source control. +" +" If called with arguments, the arguments are the log message. +" +" If '!' is used, an empty log message is committed. +" +" If called with no arguments, this is a two-step command. +" The first step opens a buffer to accept a log message. +" When that buffer is written, it is automatically closed and +" the file is committed using the information from that log +" message. The commit can be abandoned if the log message +" buffer is deleted or wiped before being written. +" +" VCSDelete Deletes the current file and removes it from source control. +" +" VCSDiff With no arguments, this displays the differences between +" the current file and its parent version under source +" control in a new scratch buffer. +" +" With one argument, the diff is performed on the +" current file against the specified revision. +" +" With two arguments, the diff is performed between the +" specified revisions of the current file. +" +" This command uses the 'VCSCommand{VCSType}DiffOpt' variable +" to specify diff options. If that variable does not exist, +" a plugin-specific default is used. If you wish to have no +" options, then set it to the empty string. +" +" VCSGotoOriginal Jumps to the source buffer if the current buffer is a VCS +" scratch buffer. If VCSGotoOriginal[!] is used, remove all +" VCS scratch buffers associated with the original file. +" +" VCSInfo Displays extended information about the current file in a +" new scratch buffer. +" +" VCSLock Locks the current file in order to prevent other users from +" concurrently modifying it. The exact semantics of this +" command depend on the underlying VCS. +" +" VCSLog Displays the version history of the current file in a new +" scratch buffer. +" +" VCSRemove Alias for 'VCSDelete'. +" +" VCSRevert Replaces the modified version of the current file with the +" most recent version from the repository. +" +" VCSReview Displays a particular version of the current file in a new +" scratch buffer. If no argument is given, the most recent +" version of the file on the current branch is retrieved. +" +" VCSStatus Displays versioning information about the current file in a +" new scratch buffer. +" +" VCSUnlock Unlocks the current file in order to allow other users from +" concurrently modifying it. The exact semantics of this +" command depend on the underlying VCS. +" +" VCSUpdate Updates the current file with any relevant changes from the +" repository. +" +" VCSVimDiff Uses vimdiff to display differences between versions of the +" current file. +" +" If no revision is specified, the most recent version of the +" file on the current branch is used. With one argument, +" that argument is used as the revision as above. With two +" arguments, the differences between the two revisions is +" displayed using vimdiff. +" +" With either zero or one argument, the original buffer is +" used to perform the vimdiff. When the scratch buffer is +" closed, the original buffer will be returned to normal +" mode. +" +" Once vimdiff mode is started using the above methods, +" additional vimdiff buffers may be added by passing a single +" version argument to the command. There may be up to 4 +" vimdiff buffers total. +" +" Using the 2-argument form of the command resets the vimdiff +" to only those 2 versions. Additionally, invoking the +" command on a different file will close the previous vimdiff +" buffers. +" +" Mapping documentation: {{{2 +" +" By default, a mapping is defined for each command. User-provided mappings +" can be used instead by mapping to <Plug>CommandName, for instance: +" +" nmap ,ca <Plug>VCSAdd +" +" The default mappings are as follow: +" +" <Leader>ca VCSAdd +" <Leader>cn VCSAnnotate +" <Leader>cN VCSAnnotate! +" <Leader>cc VCSCommit +" <Leader>cD VCSDelete +" <Leader>cd VCSDiff +" <Leader>cg VCSGotoOriginal +" <Leader>cG VCSGotoOriginal! +" <Leader>ci VCSInfo +" <Leader>cl VCSLog +" <Leader>cL VCSLock +" <Leader>cr VCSReview +" <Leader>cs VCSStatus +" <Leader>cu VCSUpdate +" <Leader>cU VCSUnlock +" <Leader>cv VCSVimDiff +" +" Options documentation: {{{2 +" +" Several variables are checked by the script to determine behavior as follow: +" +" VCSCommandCommitOnWrite +" This variable, if set to a non-zero value, causes the pending commit to +" take place immediately as soon as the log message buffer is written. If +" set to zero, only the VCSCommit mapping will cause the pending commit to +" occur. If not set, it defaults to 1. +" +" VCSCommandDeleteOnHide +" This variable, if set to a non-zero value, causes the temporary VCS result +" buffers to automatically delete themselves when hidden. +" +" VCSCommand{VCSType}DiffOpt +" This variable, if set, determines the options passed to the diff command +" of the underlying VCS. Each VCS plugin defines a default value. +" +" VCSCommandDiffSplit +" This variable overrides the VCSCommandSplit variable, but only for buffers +" created with VCSVimDiff. +" +" VCSCommandDisableAll +" This variable, if set, prevents the plugin or any extensions from loading +" at all. This is useful when a single runtime distribution is used on +" multiple systems with varying versions. +" +" VCSCommandDisableMappings +" This variable, if set to a non-zero value, prevents the default command +" mappings from being set. +" +" VCSCommandDisableExtensionMappings +" This variable, if set to a non-zero value, prevents the default command +" mappings from being set for commands specific to an individual VCS. +" +" VCSCommandDisableMenu +" This variable, if set to a non-zero value, prevents the default command +" menu from being set. +" +" VCSCommandEdit +" This variable controls whether to split the current window to display a +" scratch buffer ('split'), or to display it in the current buffer ('edit'). +" If not set, it defaults to 'split'. +" +" VCSCommandEnableBufferSetup +" This variable, if set to a non-zero value, activates VCS buffer management +" mode. This mode means that the buffer variable 'VCSRevision' is set if +" the file is VCS-controlled. This is useful for displaying version +" information in the status bar. Additional options may be set by +" individual VCS plugins. +" +" VCSCommandMappings +" This variable, if set, overrides the default mappings used for shortcuts. +" It should be a List of 2-element Lists, each containing a shortcut and +" function name pair. +" +" VCSCommandMapPrefix +" This variable, if set, overrides the default mapping prefix ('<Leader>c'). +" This allows customization of the mapping space used by the vcscommand +" shortcuts. +" +" VCSCommandMenuPriority +" This variable, if set, overrides the default menu priority '' (empty) +" +" VCSCommandMenuRoot +" This variable, if set, overrides the default menu root 'Plugin.VCS' +" +" VCSCommandResultBufferNameExtension +" This variable, if set to a non-blank value, is appended to the name of the +" VCS command output buffers. For example, '.vcs'. Using this option may +" help avoid problems caused by autocommands dependent on file extension. +" +" VCSCommandResultBufferNameFunction +" This variable, if set, specifies a custom function for naming VCS command +" output buffers. This function will be passed the following arguments: +" +" command - name of the VCS command being executed (such as 'Log' or +" 'Diff'). +" +" originalBuffer - buffer number of the source file. +" +" vcsType - type of VCS controlling this file (such as 'CVS' or 'SVN'). +" +" statusText - extra text associated with the VCS action (such as version +" numbers). +" +" VCSCommandSplit +" This variable controls the orientation of the various window splits that +" may occur (such as with VCSVimDiff, when using a VCS command on a VCS +" command buffer, or when the 'VCSCommandEdit' variable is set to 'split'. +" If set to 'horizontal', the resulting windows will be on stacked on top of +" one another. If set to 'vertical', the resulting windows will be +" side-by-side. If not set, it defaults to 'horizontal' for all but +" VCSVimDiff windows. +" +" VCSCommandVCSTypeOverride +" This variable allows the VCS type detection to be overridden on a +" path-by-path basis. The value of this variable is expected to be a List +" of Lists. Each high-level List item is a List containing two elements. +" The first element is a regular expression that will be matched against the +" full file name of a given buffer. If it matches, the second element will +" be used as the VCS type. +" +" Event documentation {{{2 +" For additional customization, VCSCommand.vim uses User event autocommand +" hooks. Each event is in the VCSCommand group, and different patterns +" match the various hooks. +" +" For instance, the following could be added to the vimrc to provide a 'q' +" mapping to quit a VCS scratch buffer: +" +" augroup VCSCommand +" au VCSCommand User VCSBufferCreated silent! nmap <unique> <buffer> q :bwipeout<cr> +" augroup END +" +" The following hooks are available: +" +" VCSBufferCreated This event is fired just after a VCS command +" output buffer is created. It is executed +" within the context of the new buffer. +" +" VCSBufferSetup This event is fired just after VCS buffer setup +" occurs, if enabled. +" +" VCSPluginInit This event is fired when the VCSCommand plugin +" first loads. +" +" VCSPluginFinish This event is fired just after the VCSCommand +" plugin loads. +" +" VCSVimDiffFinish This event is fired just after the VCSVimDiff +" command executes to allow customization of, +" for instance, window placement and focus. +" +" Section: Plugin header {{{1 + +" loaded_VCSCommand is set to 1 when the initialization begins, and 2 when it +" completes. This allows various actions to only be taken by functions after +" system initialization. + +if exists('VCSCommandDisableAll') + finish +endif + +if exists('loaded_VCSCommand') + finish +endif +let loaded_VCSCommand = 1 + +if v:version < 700 + echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None + finish +endif + +let s:save_cpo=&cpo +set cpo&vim + +" Section: Event group setup {{{1 + +augroup VCSCommand +augroup END + +augroup VCSCommandCommit +augroup END + +" Section: Plugin initialization {{{1 +silent do VCSCommand User VCSPluginInit + +" Section: Constants declaration {{{1 + +let g:VCSCOMMAND_IDENTIFY_EXACT = 1 +let g:VCSCOMMAND_IDENTIFY_INEXACT = -1 + +" Section: Script variable initialization {{{1 + +" Hidden functions for use by extensions +let s:VCSCommandUtility = {} + +" plugin-specific information: {vcs -> [script, {command -> function}, {key -> mapping}]} +let s:plugins = {} + +" temporary values of overridden configuration variables +let s:optionOverrides = {} + +" state flag used to vary behavior of certain automated actions +let s:isEditFileRunning = 0 + +" Section: Utility functions {{{1 + +" Function: s:ReportError(mapping) {{{2 +" Displays the given error in a consistent faction. This is intended to be +" invoked from a catch statement. + +function! s:ReportError(error) + echohl WarningMsg|echomsg 'VCSCommand: ' . a:error|echohl None +endfunction + +" Function: s:VCSCommandUtility.system(...) {{{2 +" Replacement for system() function. This version protects the quoting in the +" command line on Windows systems. + +function! s:VCSCommandUtility.system(...) + if (has("win32") || has("win64")) && &sxq !~ '"' + let save_sxq = &sxq + set sxq=\" + endif + try + return call('system', a:000) + finally + if exists("save_sxq") + let &sxq = save_sxq + endif + endtry +endfunction + +" Function: s:VCSCommandUtility.addMenuItem(shortcut, command) {{{2 +" Adds the given menu item. + +function! s:VCSCommandUtility.addMenuItem(shortcut, command) + if s:menuEnabled + exe 'amenu <silent> '.s:menuPriority.' '.s:menuRoot.'.'.a:shortcut.' '.a:command + endif +endfunction + +" Function: s:ClearMenu() {{{2 +" Removes all VCSCommand menu items +function! s:ClearMenu() + if s:menuEnabled + execute 'aunmenu' s:menuRoot + endif +endfunction + +" Function: s:CreateMapping(shortcut, expansion, display) {{{2 +" Creates the given mapping by prepending the contents of +" 'VCSCommandMapPrefix' (by default '<Leader>c') to the given shortcut and +" mapping it to the given plugin function. If a mapping exists for the +" specified shortcut + prefix, emit an error but continue. If a mapping +" exists for the specified function, do nothing. + +function! s:CreateMapping(shortcut, expansion, display) + let lhs = VCSCommandGetOption('VCSCommandMapPrefix', '<Leader>c') . a:shortcut + if !hasmapto(a:expansion) + try + execute 'nmap <silent> <unique>' lhs a:expansion + catch /^Vim(.*):E227:/ + if(&verbose != 0) + echohl WarningMsg|echomsg 'VCSCommand: mapping ''' . lhs . ''' already exists, refusing to overwrite. The mapping for ' . a:display . ' will not be available.'|echohl None + endif + endtry + endif +endfunction + +" Function: s:ExecuteExtensionMapping(mapping) {{{2 +" Invokes the appropriate extension mapping depending on the type of the +" current buffer. + +function! s:ExecuteExtensionMapping(mapping) + let buffer = bufnr('%') + let vcsType = VCSCommandGetVCSType(buffer) + if !has_key(s:plugins, vcsType) + throw 'Unknown VCS type: ' . vcsType + endif + if !has_key(s:plugins[vcsType][2], a:mapping) + throw 'This extended mapping is not defined for ' . vcsType + endif + silent execute 'normal' ':' . s:plugins[vcsType][2][a:mapping] . "\<CR>" +endfunction + +" Function: s:ExecuteVCSCommand(command, argList) {{{2 +" Calls the indicated plugin-specific VCS command on the current buffer. +" Returns: buffer number of resulting output scratch buffer, or -1 if an error +" occurs. + +function! s:ExecuteVCSCommand(command, argList) + try + let buffer = bufnr('%') + + let vcsType = VCSCommandGetVCSType(buffer) + if !has_key(s:plugins, vcsType) + throw 'Unknown VCS type: ' . vcsType + endif + + let originalBuffer = VCSCommandGetOriginalBuffer(buffer) + let bufferName = bufname(originalBuffer) + + " It is already known that the directory is under VCS control. No further + " checks are needed. Otherwise, perform some basic sanity checks to avoid + " VCS-specific error messages from confusing things. + if !isdirectory(bufferName) + if !filereadable(bufferName) + throw 'No such file ' . bufferName + endif + endif + + let functionMap = s:plugins[vcsType][1] + if !has_key(functionMap, a:command) + throw 'Command ''' . a:command . ''' not implemented for ' . vcsType + endif + return functionMap[a:command](a:argList) + catch + call s:ReportError(v:exception) + return -1 + endtry +endfunction + +" Function: s:GenerateResultBufferName(command, originalBuffer, vcsType, statusText) {{{2 +" Default method of generating the name for VCS result buffers. This can be +" overridden with the VCSResultBufferNameFunction variable. + +function! s:GenerateResultBufferName(command, originalBuffer, vcsType, statusText) + let fileName = bufname(a:originalBuffer) + let bufferName = a:vcsType . ' ' . a:command + if strlen(a:statusText) > 0 + let bufferName .= ' ' . a:statusText + endif + let bufferName .= ' ' . fileName + let counter = 0 + let versionedBufferName = bufferName + while bufexists(versionedBufferName) + let counter += 1 + let versionedBufferName = bufferName . ' (' . counter . ')' + endwhile + return versionedBufferName +endfunction + +" Function: s:GenerateResultBufferNameWithExtension(command, originalBuffer, vcsType, statusText) {{{2 +" Method of generating the name for VCS result buffers that uses the original +" file name with the VCS type and command appended as extensions. + +function! s:GenerateResultBufferNameWithExtension(command, originalBuffer, vcsType, statusText) + let fileName = bufname(a:originalBuffer) + let bufferName = a:vcsType . ' ' . a:command + if strlen(a:statusText) > 0 + let bufferName .= ' ' . a:statusText + endif + let bufferName .= ' ' . fileName . VCSCommandGetOption('VCSCommandResultBufferNameExtension', '.vcs') + let counter = 0 + let versionedBufferName = bufferName + while bufexists(versionedBufferName) + let counter += 1 + let versionedBufferName = '(' . counter . ') ' . bufferName + endwhile + return versionedBufferName +endfunction + +" Function: s:EditFile(command, originalBuffer, statusText) {{{2 +" Creates a new buffer of the given name and associates it with the given +" original buffer. + +function! s:EditFile(command, originalBuffer, statusText) + let vcsType = getbufvar(a:originalBuffer, 'VCSCommandVCSType') + + " Protect against useless buffer set-up + let s:isEditFileRunning += 1 + try + let editCommand = VCSCommandGetOption('VCSCommandEdit', 'split') + if editCommand == 'split' + if VCSCommandGetOption('VCSCommandSplit', 'horizontal') == 'horizontal' + rightbelow split + else + vert rightbelow split + endif + endif + + enew + + call s:SetupScratchBuffer(a:command, vcsType, a:originalBuffer, a:statusText) + + finally + let s:isEditFileRunning -= 1 + endtry +endfunction + +" Function: s:SetupScratchBuffer(command, vcsType, originalBuffer, statusText) {{{2 +" Creates convenience buffer variables and the name of a vcscommand result +" buffer. + +function! s:SetupScratchBuffer(command, vcsType, originalBuffer, statusText) + let nameExtension = VCSCommandGetOption('VCSCommandResultBufferNameExtension', '') + if nameExtension == '' + let nameFunction = VCSCommandGetOption('VCSCommandResultBufferNameFunction', 's:GenerateResultBufferName') + else + let nameFunction = VCSCommandGetOption('VCSCommandResultBufferNameFunction', 's:GenerateResultBufferNameWithExtension') + endif + + let name = call(nameFunction, [a:command, a:originalBuffer, a:vcsType, a:statusText]) + + let b:VCSCommandCommand = a:command + let b:VCSCommandOriginalBuffer = a:originalBuffer + let b:VCSCommandSourceFile = bufname(a:originalBuffer) + let b:VCSCommandVCSType = a:vcsType + if a:statusText != '' + let b:VCSCommandStatusText = a:statusText + endif + + setlocal buftype=nofile + setlocal noswapfile + let &filetype = tolower(a:vcsType . a:command) + + if VCSCommandGetOption('VCSCommandDeleteOnHide', 0) + setlocal bufhidden=delete + endif + silent noautocmd file `=name` +endfunction + +" Function: s:SetupBuffer() {{{2 +" Attempts to set the b:VCSCommandBufferInfo variable + +function! s:SetupBuffer() + if (exists('b:VCSCommandBufferSetup') && b:VCSCommandBufferSetup) + " This buffer is already set up. + return + endif + + if !isdirectory(@%) && (strlen(&buftype) > 0 || !filereadable(@%)) + " No special status for special buffers other than directory buffers. + return + endif + + if !VCSCommandGetOption('VCSCommandEnableBufferSetup', 0) || s:isEditFileRunning > 0 + unlet! b:VCSCommandBufferSetup + return + endif + + try + let vcsType = VCSCommandGetVCSType(bufnr('%')) + let b:VCSCommandBufferInfo = s:plugins[vcsType][1].GetBufferInfo() + silent do VCSCommand User VCSBufferSetup + catch /No suitable plugin/ + " This is not a VCS-controlled file. + let b:VCSCommandBufferInfo = [] + endtry + + let b:VCSCommandBufferSetup = 1 +endfunction + +" Function: s:MarkOrigBufferForSetup(buffer) {{{2 +" Resets the buffer setup state of the original buffer for a given VCS scratch +" buffer. +" Returns: The VCS buffer number in a passthrough mode. + +function! s:MarkOrigBufferForSetup(buffer) + checktime + if a:buffer > 0 + let origBuffer = VCSCommandGetOriginalBuffer(a:buffer) + " This should never not work, but I'm paranoid + if origBuffer != a:buffer + call setbufvar(origBuffer, 'VCSCommandBufferSetup', 0) + endif + endif + return a:buffer +endfunction + +" Function: s:OverrideOption(option, [value]) {{{2 +" Provides a temporary override for the given VCS option. If no value is +" passed, the override is disabled. + +function! s:OverrideOption(option, ...) + if a:0 == 0 + call remove(s:optionOverrides[a:option], -1) + else + if !has_key(s:optionOverrides, a:option) + let s:optionOverrides[a:option] = [] + endif + call add(s:optionOverrides[a:option], a:1) + endif +endfunction + +" Function: s:WipeoutCommandBuffers() {{{2 +" Clears all current VCS output buffers of the specified type for a given source. + +function! s:WipeoutCommandBuffers(originalBuffer, VCSCommand) + let buffer = 1 + while buffer <= bufnr('$') + if getbufvar(buffer, 'VCSCommandOriginalBuffer') == a:originalBuffer + if getbufvar(buffer, 'VCSCommandCommand') == a:VCSCommand + execute 'bw' buffer + endif + endif + let buffer = buffer + 1 + endwhile +endfunction + +" Function: s:VimDiffRestore(vimDiffBuff) {{{2 +" Checks whether the given buffer is one whose deletion should trigger +" restoration of an original buffer after it was diffed. If so, it executes +" the appropriate setting command stored with that original buffer. + +function! s:VimDiffRestore(vimDiffBuff) + let s:isEditFileRunning += 1 + try + if exists('t:vcsCommandVimDiffSourceBuffer') + if a:vimDiffBuff == t:vcsCommandVimDiffSourceBuffer + " Original file is being removed. + unlet! t:vcsCommandVimDiffSourceBuffer + unlet! t:vcsCommandVimDiffRestoreCmd + unlet! t:vcsCommandVimDiffScratchList + else + let index = index(t:vcsCommandVimDiffScratchList, a:vimDiffBuff) + if index >= 0 + call remove(t:vcsCommandVimDiffScratchList, index) + if len(t:vcsCommandVimDiffScratchList) == 0 + if exists('t:vcsCommandVimDiffRestoreCmd') + " All scratch buffers are gone, reset the original. + " Only restore if the source buffer is still in Diff mode + + let sourceWinNR = bufwinnr(t:vcsCommandVimDiffSourceBuffer) + if sourceWinNR != -1 + " The buffer is visible in at least one window + let currentWinNR = winnr() + while winbufnr(sourceWinNR) != -1 + if winbufnr(sourceWinNR) == t:vcsCommandVimDiffSourceBuffer + execute sourceWinNR . 'wincmd w' + if getwinvar(0, '&diff') + execute t:vcsCommandVimDiffRestoreCmd + endif + endif + let sourceWinNR = sourceWinNR + 1 + endwhile + execute currentWinNR . 'wincmd w' + else + " The buffer is hidden. It must be visible in order to set the + " diff option. + let currentBufNR = bufnr('') + execute 'hide buffer' t:vcsCommandVimDiffSourceBuffer + if getwinvar(0, '&diff') + execute t:vcsCommandVimDiffRestoreCmd + endif + execute 'hide buffer' currentBufNR + endif + + unlet t:vcsCommandVimDiffRestoreCmd + endif + " All buffers are gone. + unlet t:vcsCommandVimDiffSourceBuffer + unlet t:vcsCommandVimDiffScratchList + endif + endif + endif + endif + finally + let s:isEditFileRunning -= 1 + endtry +endfunction + +" Section: Generic VCS command functions {{{1 + +" Function: s:VCSAnnotate(...) {{{2 +function! s:VCSAnnotate(bang, ...) + try + let line = line('.') + let currentBuffer = bufnr('%') + let originalBuffer = VCSCommandGetOriginalBuffer(currentBuffer) + + let annotateBuffer = s:ExecuteVCSCommand('Annotate', a:000) + if annotateBuffer == -1 + return -1 + endif + if a:bang == '!' && VCSCommandGetOption('VCSCommandDisableSplitAnnotate', 0) == 0 + let vcsType = VCSCommandGetVCSType(annotateBuffer) + let functionMap = s:plugins[vcsType][1] + let splitRegex = '' + if has_key(s:plugins[vcsType][1], 'AnnotateSplitRegex') + let splitRegex = s:plugins[vcsType][1]['AnnotateSplitRegex'] + endif + let splitRegex = VCSCommandGetOption('VCSCommand' . vcsType . 'AnnotateSplitRegex', splitRegex) + if splitRegex == '' + return annotateBuffer + endif + let originalFileType = getbufvar(originalBuffer, '&ft') + let annotateFileType = getbufvar(annotateBuffer, '&ft') + execute "normal 0zR\<c-v>G/" . splitRegex . "/e\<cr>d" + call setbufvar('%', '&filetype', getbufvar(originalBuffer, '&filetype')) + set scrollbind + leftabove vert new + normal 0P + execute "normal" . col('$') . "\<c-w>|" + call s:SetupScratchBuffer('annotate', vcsType, originalBuffer, 'header') + wincmd l + endif + + if currentBuffer == originalBuffer + " Starting from the original source buffer, so the + " current line is relevant. + if a:0 == 0 + " No argument list means that we're annotating + " the current version, so jumping to the same + " line is the expected action. + execute "normal" line . 'G' + if has('folding') + " The execution of the buffer created autocommand + " re-folds the buffer. Display the current line + " unfolded. + normal zv + endif + endif + endif + + return annotateBuffer + catch + call s:ReportError(v:exception) + return -1 + endtry +endfunction + +" Function: s:VCSCommit() {{{2 +function! s:VCSCommit(bang, message) + try + let vcsType = VCSCommandGetVCSType(bufnr('%')) + if !has_key(s:plugins, vcsType) + throw 'Unknown VCS type: ' . vcsType + endif + + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + + " Handle the commit message being specified. If a message is supplied, it + " is used; if bang is supplied, an empty message is used; otherwise, the + " user is provided a buffer from which to edit the commit message. + + if strlen(a:message) > 0 || a:bang == '!' + return s:VCSFinishCommit([a:message], originalBuffer) + endif + + call s:EditFile('commitlog', originalBuffer, '') + setlocal ft=vcscommit + + " Create a commit mapping. + + nnoremap <silent> <buffer> <Plug>VCSCommit :call <SID>VCSFinishCommitWithBuffer()<CR> + + silent 0put ='VCS: ----------------------------------------------------------------------' + silent put ='VCS: Please enter log message. Lines beginning with ''VCS:'' are removed automatically.' + silent put ='VCS: To finish the commit, Type <leader>cc (or your own <Plug>VCSCommit mapping)' + + if VCSCommandGetOption('VCSCommandCommitOnWrite', 1) == 1 + setlocal buftype=acwrite + au VCSCommandCommit BufWriteCmd <buffer> call s:VCSFinishCommitWithBuffer() + silent put ='VCS: or write this buffer' + endif + + silent put ='VCS: ----------------------------------------------------------------------' + $ + setlocal nomodified + silent do VCSCommand User VCSBufferCreated + catch + call s:ReportError(v:exception) + return -1 + endtry +endfunction + +" Function: s:VCSFinishCommitWithBuffer() {{{2 +" Wrapper for s:VCSFinishCommit which is called only from a commit log buffer +" which removes all lines starting with 'VCS:'. + +function! s:VCSFinishCommitWithBuffer() + setlocal nomodified + let currentBuffer = bufnr('%') + let logMessageList = getbufline('%', 1, '$') + call filter(logMessageList, 'v:val !~ ''^\s*VCS:''') + let resultBuffer = s:VCSFinishCommit(logMessageList, b:VCSCommandOriginalBuffer) + if resultBuffer >= 0 + execute 'bw' currentBuffer + endif + return resultBuffer +endfunction + +" Function: s:VCSFinishCommit(logMessageList, originalBuffer) {{{2 +function! s:VCSFinishCommit(logMessageList, originalBuffer) + let messageFileName = tempname() + call writefile(a:logMessageList, messageFileName) + try + let resultBuffer = s:ExecuteVCSCommand('Commit', [messageFileName]) + if resultBuffer < 0 + return resultBuffer + endif + return s:MarkOrigBufferForSetup(resultBuffer) + finally + call delete(messageFileName) + endtry +endfunction + +" Function: s:VCSGotoOriginal(bang) {{{2 +function! s:VCSGotoOriginal(bang) + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + if originalBuffer > 0 + let origWinNR = bufwinnr(originalBuffer) + if origWinNR == -1 + execute 'buffer' originalBuffer + else + execute origWinNR . 'wincmd w' + endif + if a:bang == '!' + let buffnr = 1 + let buffmaxnr = bufnr('$') + while buffnr <= buffmaxnr + if getbufvar(buffnr, 'VCSCommandOriginalBuffer') == originalBuffer + execute 'bw' buffnr + endif + let buffnr = buffnr + 1 + endwhile + endif + endif +endfunction + +function! s:VCSDiff(...) "{{{2 + let resultBuffer = s:ExecuteVCSCommand('Diff', a:000) + if resultBuffer > 0 + let &filetype = 'diff' + elseif resultBuffer == 0 + echomsg 'No differences found' + endif + return resultBuffer +endfunction + +function! s:VCSReview(...) "{{{2 + let resultBuffer = s:ExecuteVCSCommand('Review', a:000) + if resultBuffer > 0 + let &filetype = getbufvar(b:VCSCommandOriginalBuffer, '&filetype') + endif + return resultBuffer +endfunction + +" Function: s:VCSVimDiff(...) {{{2 +function! s:VCSVimDiff(...) + try + let vcsType = VCSCommandGetVCSType(bufnr('%')) + if !has_key(s:plugins, vcsType) + throw 'Unknown VCS type: ' . vcsType + endif + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + let s:isEditFileRunning = s:isEditFileRunning + 1 + try + " If there's already a VimDiff'ed window, restore it. + " There may only be one VCSVimDiff original window at a time. + + if exists('t:vcsCommandVimDiffSourceBuffer') && t:vcsCommandVimDiffSourceBuffer != originalBuffer + " Clear the existing vimdiff setup by removing the result buffers. + call s:WipeoutCommandBuffers(t:vcsCommandVimDiffSourceBuffer, 'vimdiff') + endif + + let orientation = &diffopt =~ 'horizontal' ? 'horizontal' : 'vertical' + let orientation = VCSCommandGetOption('VCSCommandSplit', orientation) + let orientation = VCSCommandGetOption('VCSCommandDiffSplit', orientation) + + " Split and diff + if(a:0 == 2) + " Reset the vimdiff system, as 2 explicit versions were provided. + if exists('t:vcsCommandVimDiffSourceBuffer') + call s:WipeoutCommandBuffers(t:vcsCommandVimDiffSourceBuffer, 'vimdiff') + endif + let resultBuffer = s:VCSReview(a:1) + if resultBuffer < 0 + echomsg 'Can''t open revision ' . a:1 + return resultBuffer + endif + let b:VCSCommandCommand = 'vimdiff' + diffthis + let t:vcsCommandVimDiffScratchList = [resultBuffer] + " If no split method is defined, cheat, and set it to vertical. + try + call s:OverrideOption('VCSCommandSplit', orientation) + let resultBuffer = s:VCSReview(a:2) + finally + call s:OverrideOption('VCSCommandSplit') + endtry + if resultBuffer < 0 + echomsg 'Can''t open revision ' . a:1 + return resultBuffer + endif + let b:VCSCommandCommand = 'vimdiff' + diffthis + let t:vcsCommandVimDiffScratchList += [resultBuffer] + else + " Add new buffer + call s:OverrideOption('VCSCommandEdit', 'split') + try + " Force splitting behavior, otherwise why use vimdiff? + call s:OverrideOption('VCSCommandSplit', orientation) + try + if(a:0 == 0) + let resultBuffer = s:VCSReview() + else + let resultBuffer = s:VCSReview(a:1) + endif + finally + call s:OverrideOption('VCSCommandSplit') + endtry + finally + call s:OverrideOption('VCSCommandEdit') + endtry + if resultBuffer < 0 + echomsg 'Can''t open current revision' + return resultBuffer + endif + let b:VCSCommandCommand = 'vimdiff' + diffthis + + if !exists('t:vcsCommandVimDiffSourceBuffer') + " New instance of vimdiff. + let t:vcsCommandVimDiffScratchList = [resultBuffer] + + " This could have been invoked on a VCS result buffer, not the + " original buffer. + wincmd W + execute 'buffer' originalBuffer + " Store info for later original buffer restore + let t:vcsCommandVimDiffRestoreCmd = + \ 'call setbufvar('.originalBuffer.', ''&diff'', '.getbufvar(originalBuffer, '&diff').')' + \ . '|call setbufvar('.originalBuffer.', ''&foldcolumn'', '.getbufvar(originalBuffer, '&foldcolumn').')' + \ . '|call setbufvar('.originalBuffer.', ''&foldenable'', '.getbufvar(originalBuffer, '&foldenable').')' + \ . '|call setbufvar('.originalBuffer.', ''&foldmethod'', '''.getbufvar(originalBuffer, '&foldmethod').''')' + \ . '|call setbufvar('.originalBuffer.', ''&foldlevel'', '''.getbufvar(originalBuffer, '&foldlevel').''')' + \ . '|call setbufvar('.originalBuffer.', ''&scrollbind'', '.getbufvar(originalBuffer, '&scrollbind').')' + \ . '|call setbufvar('.originalBuffer.', ''&wrap'', '.getbufvar(originalBuffer, '&wrap').')' + \ . '|if &foldmethod==''manual''|execute ''normal zE''|endif' + diffthis + wincmd w + else + " Adding a window to an existing vimdiff + let t:vcsCommandVimDiffScratchList += [resultBuffer] + endif + endif + + let t:vcsCommandVimDiffSourceBuffer = originalBuffer + + " Avoid executing the modeline in the current buffer after the autocommand. + + let currentBuffer = bufnr('%') + let saveModeline = getbufvar(currentBuffer, '&modeline') + try + call setbufvar(currentBuffer, '&modeline', 0) + silent do VCSCommand User VCSVimDiffFinish + finally + call setbufvar(currentBuffer, '&modeline', saveModeline) + endtry + return resultBuffer + finally + let s:isEditFileRunning = s:isEditFileRunning - 1 + endtry + catch + call s:ReportError(v:exception) + return -1 + endtry +endfunction + +" Section: Public functions {{{1 + +" Function: VCSCommandGetVCSType() {{{2 +" Sets the b:VCSCommandVCSType variable in the given buffer to the +" appropriate source control system name. +" +" This uses the Identify extension function to test the buffer. If the +" Identify function returns VCSCOMMAND_IDENTIFY_EXACT, the match is considered +" exact. If the Identify function returns VCSCOMMAND_IDENTIFY_INEXACT, the +" match is considered inexact, and is only applied if no exact match is found. +" Multiple inexact matches is currently considered an error. + +function! VCSCommandGetVCSType(buffer) + let vcsType = getbufvar(a:buffer, 'VCSCommandVCSType') + if strlen(vcsType) > 0 + return vcsType + endif + if exists("g:VCSCommandVCSTypeOverride") + let fullpath = fnamemodify(bufname(a:buffer), ':p') + for [path, vcsType] in g:VCSCommandVCSTypeOverride + if match(fullpath, path) > -1 + call setbufvar(a:buffer, 'VCSCommandVCSType', vcsType) + return vcsType + endif + endfor + endif + let matches = [] + for vcsType in keys(s:plugins) + let identified = s:plugins[vcsType][1].Identify(a:buffer) + if identified + if identified == g:VCSCOMMAND_IDENTIFY_EXACT + let matches = [vcsType] + break + else + let matches += [vcsType] + endif + endif + endfor + if len(matches) == 1 + call setbufvar(a:buffer, 'VCSCommandVCSType', matches[0]) + return matches[0] + elseif len(matches) == 0 + throw 'No suitable plugin' + else + throw 'Too many matching VCS: ' . join(matches) + endif +endfunction + +" Function: VCSCommandChdir(directory) {{{2 +" Changes the current directory, respecting :lcd changes. + +function! VCSCommandChdir(directory) + let command = 'cd' + if exists("*haslocaldir") && haslocaldir() + let command = 'lcd' + endif + execute command escape(a:directory, ' ') +endfunction + +" Function: VCSCommandChangeToCurrentFileDir() {{{2 +" Go to the directory in which the given file is located. + +function! VCSCommandChangeToCurrentFileDir(fileName) + let oldCwd = getcwd() + let newCwd = fnamemodify(resolve(a:fileName), ':p:h') + if strlen(newCwd) > 0 + call VCSCommandChdir(newCwd) + endif + return oldCwd +endfunction + +" Function: VCSCommandGetOriginalBuffer(vcsBuffer) {{{2 +" Attempts to locate the original file to which VCS operations were applied +" for a given buffer. + +function! VCSCommandGetOriginalBuffer(vcsBuffer) + let origBuffer = getbufvar(a:vcsBuffer, 'VCSCommandOriginalBuffer') + if origBuffer + if bufexists(origBuffer) + return origBuffer + else + " Original buffer no longer exists. + throw 'Original buffer for this VCS buffer no longer exists.' + endif + else + " No original buffer + return a:vcsBuffer + endif +endfunction + +" Function: VCSCommandRegisterModule(name, file, commandMap) {{{2 +" Allows VCS modules to register themselves. + +function! VCSCommandRegisterModule(name, path, commandMap, mappingMap) + let s:plugins[a:name] = [a:path, a:commandMap, a:mappingMap] + if !empty(a:mappingMap) + \ && !VCSCommandGetOption('VCSCommandDisableMappings', 0) + \ && !VCSCommandGetOption('VCSCommandDisableExtensionMappings', 0) + for shortcut in keys(a:mappingMap) + let expansion = ":call <SID>ExecuteExtensionMapping('" . shortcut . "')<CR>" + call s:CreateMapping(shortcut, expansion, a:name . " extension mapping " . shortcut) + endfor + endif + return s:VCSCommandUtility +endfunction + +" Function: VCSCommandDoCommand(cmd, cmdName, statusText, [options]) {{{2 +" General skeleton for VCS function execution. The given command is executed +" after appending the current buffer name (or substituting it for +" <VCSCOMMANDFILE>, if such a token is present). The output is captured in a +" new buffer. +" +" The optional 'options' Dictionary may contain the following options: +" allowNonZeroExit: if non-zero, if the underlying VCS command has a +" non-zero exit status, the command is still considered +" successfuly. This defaults to zero. +" Returns: name of the new command buffer containing the command results + +function! VCSCommandDoCommand(cmd, cmdName, statusText, options) + let allowNonZeroExit = 0 + if has_key(a:options, 'allowNonZeroExit') + let allowNonZeroExit = a:options.allowNonZeroExit + endif + + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + if originalBuffer == -1 + throw 'Original buffer no longer exists, aborting.' + endif + + let path = resolve(bufname(originalBuffer)) + + " Work with netrw or other systems where a directory listing is displayed in + " a buffer. + + if isdirectory(path) + let fileName = '.' + else + let fileName = fnamemodify(path, ':t') + endif + + if match(a:cmd, '<VCSCOMMANDFILE>') > 0 + let fullCmd = substitute(a:cmd, '<VCSCOMMANDFILE>', fileName, 'g') + else + let fullCmd = a:cmd . ' -- "' . fileName . '"' + endif + + " Change to the directory of the current buffer. This is done for CVS, but + " is left in for other systems as it does not affect them negatively. + + let oldCwd = VCSCommandChangeToCurrentFileDir(path) + try + let output = s:VCSCommandUtility.system(fullCmd) + finally + call VCSCommandChdir(oldCwd) + endtry + + " HACK: if line endings in the repository have been corrupted, the output + " of the command will be confused. + let output = substitute(output, "\r", '', 'g') + + if v:shell_error && !allowNonZeroExit + if strlen(output) == 0 + throw 'Version control command failed' + else + let output = substitute(output, '\n', ' ', 'g') + throw 'Version control command failed: ' . output + endif + endif + + if strlen(output) == 0 + " Handle case of no output. In this case, it is important to check the + " file status, especially since cvs edit/unedit may change the attributes + " of the file with no visible output. + + checktime + return 0 + endif + + call s:EditFile(a:cmdName, originalBuffer, a:statusText) + + silent 0put=output + + " The last command left a blank line at the end of the buffer. If the + " last line is folded (a side effect of the 'put') then the attempt to + " remove the blank line will kill the last fold. + " + " This could be fixed by explicitly detecting whether the last line is + " within a fold, but I prefer to simply unfold the result buffer altogether. + + if has('folding') + normal zR + endif + + $d + 1 + + " Define the environment and execute user-defined hooks. + + silent do VCSCommand User VCSBufferCreated + return bufnr('%') +endfunction + +" Function: VCSCommandGetOption(name, default) {{{2 +" Grab a user-specified option to override the default provided. Options are +" searched in the window, buffer, then global spaces. + +function! VCSCommandGetOption(name, default) + if has_key(s:optionOverrides, a:name) && len(s:optionOverrides[a:name]) > 0 + return s:optionOverrides[a:name][-1] + elseif exists('w:' . a:name) + return w:{a:name} + elseif exists('b:' . a:name) + return b:{a:name} + elseif exists('g:' . a:name) + return g:{a:name} + else + return a:default + endif +endfunction + +" Function: VCSCommandDisableBufferSetup() {{{2 +" Global function for deactivating the buffer autovariables. + +function! VCSCommandDisableBufferSetup() + let g:VCSCommandEnableBufferSetup = 0 + silent! augroup! VCSCommandPlugin +endfunction + +" Function: VCSCommandEnableBufferSetup() {{{2 +" Global function for activating the buffer autovariables. + +function! VCSCommandEnableBufferSetup() + let g:VCSCommandEnableBufferSetup = 1 + augroup VCSCommandPlugin + au! + au BufEnter * call s:SetupBuffer() + augroup END + + " Only auto-load if the plugin is fully loaded. This gives other plugins a + " chance to run. + if g:loaded_VCSCommand == 2 + call s:SetupBuffer() + endif +endfunction + +" Function: VCSCommandGetStatusLine() {{{2 +" Default (sample) status line entry for VCS-controlled files. This is only +" useful if VCS-managed buffer mode is on (see the VCSCommandEnableBufferSetup +" variable for how to do this). + +function! VCSCommandGetStatusLine() + if exists('b:VCSCommandCommand') + " This is a result buffer. Return nothing because the buffer name + " contains information already. + return '' + endif + + if exists('b:VCSCommandVCSType') + \ && exists('g:VCSCommandEnableBufferSetup') + \ && g:VCSCommandEnableBufferSetup + \ && exists('b:VCSCommandBufferInfo') + return '[' . join(extend([b:VCSCommandVCSType], b:VCSCommandBufferInfo), ' ') . ']' + else + return '' + endif +endfunction + +" Section: Command definitions {{{1 +" Section: Primary commands {{{2 +com! -nargs=* VCSAdd call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Add', [<f-args>])) +com! -nargs=* -bang VCSAnnotate call s:VCSAnnotate(<q-bang>, <f-args>) +com! -nargs=* -bang VCSBlame call s:VCSAnnotate(<q-bang>, <f-args>) +com! -nargs=? -bang VCSCommit call s:VCSCommit(<q-bang>, <q-args>) +com! -nargs=* VCSDelete call s:ExecuteVCSCommand('Delete', [<f-args>]) +com! -nargs=* VCSDiff call s:VCSDiff(<f-args>) +com! -nargs=0 -bang VCSGotoOriginal call s:VCSGotoOriginal(<q-bang>) +com! -nargs=* VCSInfo call s:ExecuteVCSCommand('Info', [<f-args>]) +com! -nargs=* VCSLock call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Lock', [<f-args>])) +com! -nargs=* VCSLog call s:ExecuteVCSCommand('Log', [<f-args>]) +com! -nargs=* VCSRemove call s:ExecuteVCSCommand('Delete', [<f-args>]) +com! -nargs=0 VCSRevert call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Revert', [])) +com! -nargs=? VCSReview call s:VCSReview(<f-args>) +com! -nargs=* VCSStatus call s:ExecuteVCSCommand('Status', [<f-args>]) +com! -nargs=* VCSUnlock call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Unlock', [<f-args>])) +com! -nargs=0 VCSUpdate call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Update', [])) +com! -nargs=* VCSVimDiff call s:VCSVimDiff(<f-args>) + +" Section: VCS buffer management commands {{{2 +com! VCSCommandDisableBufferSetup call VCSCommandDisableBufferSetup() +com! VCSCommandEnableBufferSetup call VCSCommandEnableBufferSetup() + +" Allow reloading VCSCommand.vim +com! VCSReload let savedPlugins = s:plugins|let s:plugins = {}|call s:ClearMenu()|unlet! g:loaded_VCSCommand|runtime plugin/vcscommand.vim|for plugin in values(savedPlugins)|execute 'source' plugin[0]|endfor|unlet savedPlugins + +" Section: Plugin command mappings {{{1 +nnoremap <silent> <Plug>VCSAdd :VCSAdd<CR> +nnoremap <silent> <Plug>VCSAnnotate :VCSAnnotate<CR> +nnoremap <silent> <Plug>VCSCommit :VCSCommit<CR> +nnoremap <silent> <Plug>VCSDelete :VCSDelete<CR> +nnoremap <silent> <Plug>VCSDiff :VCSDiff<CR> +nnoremap <silent> <Plug>VCSGotoOriginal :VCSGotoOriginal<CR> +nnoremap <silent> <Plug>VCSClearAndGotoOriginal :VCSGotoOriginal!<CR> +nnoremap <silent> <Plug>VCSInfo :VCSInfo<CR> +nnoremap <silent> <Plug>VCSLock :VCSLock<CR> +nnoremap <silent> <Plug>VCSLog :VCSLog<CR> +nnoremap <silent> <Plug>VCSRevert :VCSRevert<CR> +nnoremap <silent> <Plug>VCSReview :VCSReview<CR> +nnoremap <silent> <Plug>VCSSplitAnnotate :VCSAnnotate!<CR> +nnoremap <silent> <Plug>VCSStatus :VCSStatus<CR> +nnoremap <silent> <Plug>VCSUnlock :VCSUnlock<CR> +nnoremap <silent> <Plug>VCSUpdate :VCSUpdate<CR> +nnoremap <silent> <Plug>VCSVimDiff :VCSVimDiff<CR> + +" Section: Default mappings {{{1 + +let s:defaultMappings = [ + \['a', 'VCSAdd'], + \['c', 'VCSCommit'], + \['D', 'VCSDelete'], + \['d', 'VCSDiff'], + \['G', 'VCSClearAndGotoOriginal'], + \['g', 'VCSGotoOriginal'], + \['i', 'VCSInfo'], + \['L', 'VCSLock'], + \['l', 'VCSLog'], + \['N', 'VCSSplitAnnotate'], + \['n', 'VCSAnnotate'], + \['q', 'VCSRevert'], + \['r', 'VCSReview'], + \['s', 'VCSStatus'], + \['U', 'VCSUnlock'], + \['u', 'VCSUpdate'], + \['v', 'VCSVimDiff'], + \] + +if !VCSCommandGetOption('VCSCommandDisableMappings', 0) + for [s:shortcut, s:vcsFunction] in VCSCommandGetOption('VCSCommandMappings', s:defaultMappings) + call s:CreateMapping(s:shortcut, '<Plug>' . s:vcsFunction, '''' . s:vcsFunction . '''') + endfor + unlet s:shortcut s:vcsFunction +endif +unlet s:defaultMappings + +" Section: Menu items {{{1 + +let s:menuEnabled = !VCSCommandGetOption('VCSCommandDisableMenu', 0) +let s:menuRoot = VCSCommandGetOption('VCSCommandMenuRoot', '&Plugin.VCS') +let s:menuPriority = VCSCommandGetOption('VCSCommandMenuPriority', '') + +for [s:shortcut, s:command] in [ + \['&Add', '<Plug>VCSAdd'], + \['A&nnotate', '<Plug>VCSAnnotate'], + \['&Commit', '<Plug>VCSCommit'], + \['Delete', '<Plug>VCSDelete'], + \['&Diff', '<Plug>VCSDiff'], + \['&Info', '<Plug>VCSInfo'], + \['&Log', '<Plug>VCSLog'], + \['Revert', '<Plug>VCSRevert'], + \['&Review', '<Plug>VCSReview'], + \['&Status', '<Plug>VCSStatus'], + \['&Update', '<Plug>VCSUpdate'], + \['&VimDiff', '<Plug>VCSVimDiff'] + \] + call s:VCSCommandUtility.addMenuItem(s:shortcut, s:command) +endfor +unlet s:shortcut s:command + +" Section: Autocommands to restore vimdiff state {{{1 +augroup VimDiffRestore + au! + au BufUnload * call s:VimDiffRestore(str2nr(expand('<abuf>'))) +augroup END + +" Section: Optional activation of buffer management {{{1 + +if VCSCommandGetOption('VCSCommandEnableBufferSetup', 0) + call VCSCommandEnableBufferSetup() +endif + +" Section: VIM shutdown hook {{{1 + +" Close all result buffers when VIM exits, to prevent them from being restored +" via viminfo. + +" Function: s:CloseAllResultBuffers() {{{2 +" Closes all vcscommand result buffers. +function! s:CloseAllResultBuffers() + " This avoids using bufdo as that may load buffers already loaded in another + " vim process, resulting in an error. + let buffnr = 1 + let buffmaxnr = bufnr('$') + while buffnr <= buffmaxnr + if getbufvar(buffnr, 'VCSCommandOriginalBuffer') != "" + execute 'bw' buffnr + endif + let buffnr = buffnr + 1 + endwhile +endfunction + +augroup VCSCommandVIMShutdown + au! + au VimLeavePre * call s:CloseAllResultBuffers() +augroup END + +" Section: Plugin completion {{{1 + +let loaded_VCSCommand = 2 + +silent do VCSCommand User VCSPluginFinish + +let &cpo = s:save_cpo diff --git a/modules/vim/vim.dot.link/plugin/vcscvs.vim b/modules/vim/vim.dot.link/plugin/vcscvs.vim new file mode 100644 index 0000000..73d6a7f --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/vcscvs.vim @@ -0,0 +1,449 @@ +" vim600: set foldmethod=marker: +" +" CVS extension for VCSCommand. +" +" Maintainer: Bob Hiestand <bob.hiestand@gmail.com> +" License: +" Copyright (c) Bob Hiestand +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to +" deal in the Software without restriction, including without limitation the +" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +" sell copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in +" all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +" IN THE SOFTWARE. +" +" Section: Documentation {{{1 +" +" Command documentation {{{2 +" +" The following commands only apply to files under CVS source control. +" +" CVSEdit Performs "cvs edit" on the current file. +" +" CVSEditors Performs "cvs editors" on the current file. +" +" CVSUnedit Performs "cvs unedit" on the current file. +" +" CVSWatch Takes an argument which must be one of [on|off|add|remove]. +" Performs "cvs watch" with the given argument on the current +" file. +" +" CVSWatchers Performs "cvs watchers" on the current file. +" +" CVSWatchAdd Alias for "CVSWatch add" +" +" CVSWatchOn Alias for "CVSWatch on" +" +" CVSWatchOff Alias for "CVSWatch off" +" +" CVSWatchRemove Alias for "CVSWatch remove" +" +" Mapping documentation: {{{2 +" +" By default, a mapping is defined for each command. User-provided mappings +" can be used instead by mapping to <Plug>CommandName, for instance: +" +" nnoremap ,ce <Plug>CVSEdit +" +" The default mappings are as follow: +" +" <Leader>ce CVSEdit +" <Leader>cE CVSEditors +" <Leader>ct CVSUnedit +" <Leader>cwv CVSWatchers +" <Leader>cwa CVSWatchAdd +" <Leader>cwn CVSWatchOn +" <Leader>cwf CVSWatchOff +" <Leader>cwr CVSWatchRemove +" +" Options documentation: {{{2 +" +" VCSCommandCVSExec +" This variable specifies the CVS executable. If not set, it defaults to +" 'cvs' executed from the user's executable path. +" +" VCSCommandCVSDiffOpt +" This variable, if set, determines the options passed to the cvs diff +" command. If not set, it defaults to 'u'. + +" Section: Plugin header {{{1 + +if exists('VCSCommandDisableAll') + finish +endif + +if v:version < 700 + echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None + finish +endif + +runtime plugin/vcscommand.vim + +if !executable(VCSCommandGetOption('VCSCommandCVSExec', 'cvs')) + " CVS is not installed + finish +endif + +let s:save_cpo=&cpo +set cpo&vim + +" Section: Variable initialization {{{1 + +let s:cvsFunctions = {} + +" Section: Utility functions {{{1 + +" Function: s:Executable() {{{2 +" Returns the executable used to invoke cvs suitable for use in a shell +" command. +function! s:Executable() + return shellescape(VCSCommandGetOption('VCSCommandCVSExec', 'cvs')) +endfunction + +" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2 +" Wrapper to VCSCommandDoCommand to add the name of the CVS executable to the +" command argument. +function! s:DoCommand(cmd, cmdName, statusText, options) + if VCSCommandGetVCSType(expand('%')) == 'CVS' + let fullCmd = s:Executable() . ' ' . a:cmd + let ret = VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options) + + if ret > 0 + if getline(line('$')) =~ '^cvs \w\+: closing down connection' + $d + 1 + endif + + endif + + return ret + else + throw 'CVS VCSCommand plugin called on non-CVS item.' + endif +endfunction + +" Function: s:GetRevision() {{{2 +" Function for retrieving the current buffer's revision number. +" Returns: Revision number or an empty string if an error occurs. + +function! s:GetRevision() + if !exists('b:VCSCommandBufferInfo') + let b:VCSCommandBufferInfo = s:cvsFunctions.GetBufferInfo() + endif + + if len(b:VCSCommandBufferInfo) > 0 + return b:VCSCommandBufferInfo[0] + else + return '' + endif +endfunction + +" Section: VCS function implementations {{{1 + +" Function: s:cvsFunctions.Identify(buffer) {{{2 +function! s:cvsFunctions.Identify(buffer) + let fileName = resolve(bufname(a:buffer)) + if isdirectory(fileName) + let directoryName = fileName + else + let directoryName = fnamemodify(fileName, ':h') + endif + if strlen(directoryName) > 0 + let CVSRoot = directoryName . '/CVS/Root' + else + let CVSRoot = 'CVS/Root' + endif + if filereadable(CVSRoot) + return 1 + else + return 0 + endif +endfunction + +" Function: s:cvsFunctions.Add(argList) {{{2 +function! s:cvsFunctions.Add(argList) + return s:DoCommand(join(['add'] + a:argList, ' '), 'add', join(a:argList, ' '), {}) +endfunction + +" Function: s:cvsFunctions.Annotate(argList) {{{2 +function! s:cvsFunctions.Annotate(argList) + if len(a:argList) == 0 + if &filetype == 'CVSannotate' + " This is a CVSAnnotate buffer. Perform annotation of the version + " indicated by the current line. + let caption = matchstr(getline('.'),'\v^[0-9.]+') + + if VCSCommandGetOption('VCSCommandCVSAnnotateParent', 0) != 0 + if caption != '1.1' + let revmaj = matchstr(caption,'\v[0-9.]+\ze\.[0-9]+') + let revmin = matchstr(caption,'\v[0-9.]+\.\zs[0-9]+') - 1 + if revmin == 0 + " Jump to ancestor branch + let caption = matchstr(revmaj,'\v[0-9.]+\ze\.[0-9]+') + else + let caption = revmaj . "." . revmin + endif + endif + endif + + let options = ['-r' . caption] + else + " CVS defaults to pulling HEAD, regardless of current branch. + " Therefore, always pass desired revision. + let caption = '' + let options = ['-r' . s:GetRevision()] + endif + elseif len(a:argList) == 1 && a:argList[0] !~ '^-' + let caption = a:argList[0] + let options = ['-r' . caption] + else + let caption = join(a:argList) + let options = a:argList + endif + + let resultBuffer = s:DoCommand(join(['-q', 'annotate'] + options), 'annotate', caption, {}) + if resultBuffer > 0 + " Remove header lines from standard error + silent v/^\d\+\%(\.\d\+\)\+/d + endif + return resultBuffer +endfunction + +" Function: s:cvsFunctions.Commit(argList) {{{2 +function! s:cvsFunctions.Commit(argList) + let resultBuffer = s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {}) + if resultBuffer == 0 + echomsg 'No commit needed.' + endif + return resultBuffer +endfunction + +" Function: s:cvsFunctions.Delete() {{{2 +" By default, use the -f option to remove the file first. If options are +" passed in, use those instead. +function! s:cvsFunctions.Delete(argList) + let options = ['-f'] + let caption = '' + if len(a:argList) > 0 + let options = a:argList + let caption = join(a:argList, ' ') + endif + return s:DoCommand(join(['remove'] + options, ' '), 'delete', caption, {}) +endfunction + +" Function: s:cvsFunctions.Diff(argList) {{{2 +function! s:cvsFunctions.Diff(argList) + if len(a:argList) == 0 + let revOptions = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let revOptions = ['-r' . join(a:argList, ' -r')] + let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')' + else + " Pass-through + let caption = join(a:argList, ' ') + let revOptions = a:argList + endif + + let cvsDiffOpt = VCSCommandGetOption('VCSCommandCVSDiffOpt', 'u') + if cvsDiffOpt == '' + let diffOptions = [] + else + let diffOptions = ['-' . cvsDiffOpt] + endif + + return s:DoCommand(join(['diff'] + diffOptions + revOptions), 'diff', caption, {'allowNonZeroExit': 1}) +endfunction + +" Function: s:cvsFunctions.GetBufferInfo() {{{2 +" Provides version control details for the current file. Current version +" number and current repository version number are required to be returned by +" the vcscommand plugin. This CVS extension adds branch name to the return +" list as well. +" Returns: List of results: [revision, repository, branch] + +function! s:cvsFunctions.GetBufferInfo() + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + let fileName = bufname(originalBuffer) + if isdirectory(fileName) + let tag = '' + if filereadable(fileName . '/CVS/Tag') + let tagFile = readfile(fileName . '/CVS/Tag') + if len(tagFile) == 1 + let tag = substitute(tagFile[0], '^T', '', '') + endif + endif + return [tag] + endif + let realFileName = fnamemodify(resolve(fileName), ':t') + if !filereadable(fileName) + return ['Unknown'] + endif + let oldCwd = VCSCommandChangeToCurrentFileDir(fileName) + try + let statusText=s:VCSCommandUtility.system(s:Executable() . ' status -- "' . realFileName . '"') + if(v:shell_error) + return [] + endif + let revision=substitute(statusText, '^\_.*Working revision:\s*\(\d\+\%(\.\d\+\)\+\|New file!\)\_.*$', '\1', '') + + " We can still be in a CVS-controlled directory without this being a CVS + " file + if match(revision, '^New file!$') >= 0 + let revision='New' + elseif match(revision, '^\d\+\.\d\+\%(\.\d\+\.\d\+\)*$') <0 + return ['Unknown'] + endif + + let branch=substitute(statusText, '^\_.*Sticky Tag:\s\+\(\d\+\%(\.\d\+\)\+\|\a[A-Za-z0-9-_]*\|(none)\).*$', '\1', '') + let repository=substitute(statusText, '^\_.*Repository revision:\s*\(\d\+\%(\.\d\+\)\+\|New file!\|No revision control file\)\_.*$', '\1', '') + let repository=substitute(repository, '^New file!\|No revision control file$', 'New', '') + return [revision, repository, branch] + finally + call VCSCommandChdir(oldCwd) + endtry +endfunction + +" Function: s:cvsFunctions.Log() {{{2 +function! s:cvsFunctions.Log(argList) + if len(a:argList) == 0 + let options = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let options = ['-r' . join(a:argList, ':')] + let caption = options[0] + else + " Pass-through + let options = a:argList + let caption = join(a:argList, ' ') + endif + + return s:DoCommand(join(['log'] + options), 'log', caption, {}) +endfunction + +" Function: s:cvsFunctions.Revert(argList) {{{2 +function! s:cvsFunctions.Revert(argList) + return s:DoCommand('update -C', 'revert', '', {}) +endfunction + +" Function: s:cvsFunctions.Review(argList) {{{2 +function! s:cvsFunctions.Review(argList) + if len(a:argList) == 0 + let versiontag = '(current)' + let versionOption = '' + else + let versiontag = a:argList[0] + let versionOption = ' -r ' . versiontag . ' ' + endif + + return s:DoCommand('-q update -p' . versionOption, 'review', versiontag, {}) +endfunction + +" Function: s:cvsFunctions.Status(argList) {{{2 +function! s:cvsFunctions.Status(argList) + return s:DoCommand(join(['status'] + a:argList, ' '), 'status', join(a:argList, ' '), {}) +endfunction + +" Function: s:cvsFunctions.Update(argList) {{{2 +function! s:cvsFunctions.Update(argList) + return s:DoCommand('update', 'update', '', {}) +endfunction + +" Section: CVS-specific functions {{{1 + +" Function: s:CVSEdit() {{{2 +function! s:CVSEdit() + return s:DoCommand('edit', 'cvsedit', '', {}) +endfunction + +" Function: s:CVSEditors() {{{2 +function! s:CVSEditors() + return s:DoCommand('editors', 'cvseditors', '', {}) +endfunction + +" Function: s:CVSUnedit() {{{2 +function! s:CVSUnedit() + return s:DoCommand('unedit', 'cvsunedit', '', {}) +endfunction + +" Function: s:CVSWatch(onoff) {{{2 +function! s:CVSWatch(onoff) + if a:onoff !~ '^\c\%(on\|off\|add\|remove\)$' + echoerr 'Argument to CVSWatch must be one of [on|off|add|remove]' + return -1 + end + return s:DoCommand('watch ' . tolower(a:onoff), 'cvswatch', '', {}) +endfunction + +" Function: s:CVSWatchers() {{{2 +function! s:CVSWatchers() + return s:DoCommand('watchers', 'cvswatchers', '', {}) +endfunction + +" Annotate setting {{{2 +let s:cvsFunctions.AnnotateSplitRegex = '): ' + +" Section: Command definitions {{{1 +" Section: Primary commands {{{2 +com! CVSEdit call s:CVSEdit() +com! CVSEditors call s:CVSEditors() +com! CVSUnedit call s:CVSUnedit() +com! -nargs=1 CVSWatch call s:CVSWatch(<f-args>) +com! CVSWatchAdd call s:CVSWatch('add') +com! CVSWatchOn call s:CVSWatch('on') +com! CVSWatchOff call s:CVSWatch('off') +com! CVSWatchRemove call s:CVSWatch('remove') +com! CVSWatchers call s:CVSWatchers() + +" Section: Plugin command mappings {{{1 + +let s:cvsExtensionMappings = {} +let mappingInfo = [ + \['CVSEdit', 'CVSEdit', 'e'], + \['CVSEditors', 'CVSEditors', 'E'], + \['CVSUnedit', 'CVSUnedit', 't'], + \['CVSWatchers', 'CVSWatchers', 'wv'], + \['CVSWatchAdd', 'CVSWatch add', 'wa'], + \['CVSWatchOff', 'CVSWatch off', 'wf'], + \['CVSWatchOn', 'CVSWatch on', 'wn'], + \['CVSWatchRemove', 'CVSWatch remove', 'wr'] + \] + +for [pluginName, commandText, shortCut] in mappingInfo + execute 'nnoremap <silent> <Plug>' . pluginName . ' :' . commandText . '<CR>' + if !hasmapto('<Plug>' . pluginName) + let s:cvsExtensionMappings[shortCut] = commandText + endif +endfor + +" Section: Plugin Registration {{{1 +let s:VCSCommandUtility = VCSCommandRegisterModule('CVS', expand('<sfile>'), s:cvsFunctions, s:cvsExtensionMappings) + +" Section: Menu items {{{1 +for [s:shortcut, s:command] in [ + \['CVS.&Edit', '<Plug>CVSEdit'], + \['CVS.Ed&itors', '<Plug>CVSEditors'], + \['CVS.Unedi&t', '<Plug>CVSUnedit'], + \['CVS.&Watchers', '<Plug>CVSWatchers'], + \['CVS.WatchAdd', '<Plug>CVSWatchAdd'], + \['CVS.WatchOn', '<Plug>CVSWatchOn'], + \['CVS.WatchOff', '<Plug>CVSWatchOff'], + \['CVS.WatchRemove', '<Plug>CVSWatchRemove'] + \] + call s:VCSCommandUtility.addMenuItem(s:shortcut, s:command) +endfor +unlet s:shortcut s:command + +let &cpo = s:save_cpo diff --git a/modules/vim/vim.dot.link/plugin/vcsgit.vim b/modules/vim/vim.dot.link/plugin/vcsgit.vim new file mode 100644 index 0000000..4e299d9 --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/vcsgit.vim @@ -0,0 +1,247 @@ +" vim600: set foldmethod=marker: +" +" git extension for VCSCommand. +" +" Maintainer: Bob Hiestand <bob.hiestand@gmail.com> +" License: +" Copyright (c) Bob Hiestand +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to +" deal in the Software without restriction, including without limitation the +" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +" sell copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in +" all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +" IN THE SOFTWARE. +" +" Section: Documentation {{{1 +" +" Options documentation: {{{2 +" +" VCSCommandGitExec +" This variable specifies the git executable. If not set, it defaults to +" 'git' executed from the user's executable path. +" +" VCSCommandGitDiffOpt +" This variable, if set, determines the default options passed to the +" VCSDiff command. If any options (starting with '-') are passed to the +" command, this variable is not used. + +" Section: Plugin header {{{1 + +if exists('VCSCommandDisableAll') + finish +endif + +if v:version < 700 + echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None + finish +endif + +runtime plugin/vcscommand.vim + +if !executable(VCSCommandGetOption('VCSCommandGitExec', 'git')) + " git is not installed + finish +endif + +let s:save_cpo=&cpo +set cpo&vim + +" Section: Variable initialization {{{1 + +let s:gitFunctions = {} + +" Section: Utility functions {{{1 + +" Function: s:Executable() {{{2 +" Returns the executable used to invoke git suitable for use in a shell +" command. +function! s:Executable() + return shellescape(VCSCommandGetOption('VCSCommandGitExec', 'git')) +endfunction + +" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2 +" Wrapper to VCSCommandDoCommand to add the name of the git executable to the +" command argument. +function! s:DoCommand(cmd, cmdName, statusText, options) + if VCSCommandGetVCSType(expand('%')) == 'git' + let fullCmd = s:Executable() . ' ' . a:cmd + return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options) + else + throw 'git VCSCommand plugin called on non-git item.' + endif +endfunction + +" Section: VCS function implementations {{{1 + +" Function: s:gitFunctions.Identify(buffer) {{{2 +" This function only returns an inexact match due to the detection method used +" by git, which simply traverses the directory structure upward. +function! s:gitFunctions.Identify(buffer) + let oldCwd = VCSCommandChangeToCurrentFileDir(resolve(bufname(a:buffer))) + try + call s:VCSCommandUtility.system(s:Executable() . ' rev-parse --is-inside-work-tree') + if(v:shell_error) + return 0 + else + return g:VCSCOMMAND_IDENTIFY_INEXACT + endif + finally + call VCSCommandChdir(oldCwd) + endtry +endfunction + +" Function: s:gitFunctions.Add(argList) {{{2 +function! s:gitFunctions.Add(argList) + return s:DoCommand(join(['add'] + ['-v'] + a:argList, ' '), 'add', join(a:argList, ' '), {}) +endfunction + +" Function: s:gitFunctions.Annotate(argList) {{{2 +function! s:gitFunctions.Annotate(argList) + if len(a:argList) == 0 + if &filetype == 'gitannotate' + " Perform annotation of the version indicated by the current line. + let options = matchstr(getline('.'),'^\x\+') + else + let options = '' + endif + elseif len(a:argList) == 1 && a:argList[0] !~ '^-' + let options = a:argList[0] + else + let options = join(a:argList, ' ') + endif + + return s:DoCommand('blame ' . options, 'annotate', options, {}) +endfunction + +" Function: s:gitFunctions.Commit(argList) {{{2 +function! s:gitFunctions.Commit(argList) + try + return s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {}) + catch /\m^Version control command failed.*nothing\%( added\)\? to commit/ + echomsg 'No commit needed.' + endtry +endfunction + +" Function: s:gitFunctions.Delete() {{{2 +" All options are passed through. +function! s:gitFunctions.Delete(argList) + let options = a:argList + let caption = join(a:argList, ' ') + return s:DoCommand(join(['rm'] + options, ' '), 'delete', caption, {}) +endfunction + +" Function: s:gitFunctions.Diff(argList) {{{2 +" Pass-through call to git-diff. If no options (starting with '-') are found, +" then the options in the 'VCSCommandGitDiffOpt' variable are added. +function! s:gitFunctions.Diff(argList) + let gitDiffOpt = VCSCommandGetOption('VCSCommandGitDiffOpt', '') + if gitDiffOpt == '' + let diffOptions = [] + else + let diffOptions = [gitDiffOpt] + for arg in a:argList + if arg =~ '^-' + let diffOptions = [] + break + endif + endfor + endif + + return s:DoCommand(join(['diff'] + diffOptions + a:argList), 'diff', join(a:argList), {}) +endfunction + +" Function: s:gitFunctions.GetBufferInfo() {{{2 +" Provides version control details for the current file. Current version +" number and current repository version number are required to be returned by +" the vcscommand plugin. This CVS extension adds branch name to the return +" list as well. +" Returns: List of results: [revision, repository, branch] + +function! s:gitFunctions.GetBufferInfo() + let oldCwd = VCSCommandChangeToCurrentFileDir(resolve(bufname('%'))) + try + let branch = substitute(s:VCSCommandUtility.system(s:Executable() . ' symbolic-ref -q HEAD'), '\n$', '', '') + if v:shell_error + let branch = 'DETACHED' + else + let branch = substitute(branch, '^refs/heads/', '', '') + endif + + let info = [branch] + + for method in split(VCSCommandGetOption('VCSCommandGitDescribeArgList', (',tags,all,always')), ',', 1) + if method != '' + let method = ' --' . method + endif + let tag = substitute(s:VCSCommandUtility.system(s:Executable() . ' describe' . method), '\n$', '', '') + if !v:shell_error + call add(info, tag) + break + endif + endfor + + return info + finally + call VCSCommandChdir(oldCwd) + endtry +endfunction + +" Function: s:gitFunctions.Log() {{{2 +function! s:gitFunctions.Log(argList) + return s:DoCommand(join(['log'] + a:argList), 'log', join(a:argList, ' '), {}) +endfunction + +" Function: s:gitFunctions.Revert(argList) {{{2 +function! s:gitFunctions.Revert(argList) + return s:DoCommand('checkout', 'revert', '', {}) +endfunction + +" Function: s:gitFunctions.Review(argList) {{{2 +function! s:gitFunctions.Review(argList) + if len(a:argList) == 0 + let revision = 'HEAD' + else + let revision = a:argList[0] + endif + + let oldCwd = VCSCommandChangeToCurrentFileDir(resolve(bufname(VCSCommandGetOriginalBuffer('%')))) + try + let prefix = s:VCSCommandUtility.system(s:Executable() . ' rev-parse --show-prefix') + finally + call VCSCommandChdir(oldCwd) + endtry + + let prefix = substitute(prefix, '\n$', '', '') + let blob = '"' . revision . ':' . prefix . '<VCSCOMMANDFILE>"' + return s:DoCommand('show ' . blob, 'review', revision, {}) +endfunction + +" Function: s:gitFunctions.Status(argList) {{{2 +function! s:gitFunctions.Status(argList) + return s:DoCommand(join(['status'] + a:argList), 'status', join(a:argList), {'allowNonZeroExit': 1}) +endfunction + +" Function: s:gitFunctions.Update(argList) {{{2 +function! s:gitFunctions.Update(argList) + throw "This command is not implemented for git because file-by-file update doesn't make much sense in that context. If you have an idea for what it should do, please let me know." +endfunction + +" Annotate setting {{{2 +let s:gitFunctions.AnnotateSplitRegex = ') ' + +" Section: Plugin Registration {{{1 +let s:VCSCommandUtility = VCSCommandRegisterModule('git', expand('<sfile>'), s:gitFunctions, []) + +let &cpo = s:save_cpo diff --git a/modules/vim/vim.dot.link/plugin/vcshg.vim b/modules/vim/vim.dot.link/plugin/vcshg.vim new file mode 100644 index 0000000..2c616c0 --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/vcshg.vim @@ -0,0 +1,273 @@ +" vim600: set foldmethod=marker: +" +" Mercurial extension for VCSCommand. +" +" Maintainer: Bob Hiestand <bob.hiestand@gmail.com> +" License: +" Copyright (c) Bob Hiestand +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to +" deal in the Software without restriction, including without limitation the +" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +" sell copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in +" all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +" IN THE SOFTWARE. +" +" Section: Documentation {{{1 +" +" Options documentation: {{{2 +" +" VCSCommandHGExec +" This variable specifies the mercurial executable. If not set, it defaults +" to 'hg' executed from the user's executable path. +" +" VCSCommandHGDiffExt +" This variable, if set, sets the external diff program used by Subversion. +" +" VCSCommandHGDiffOpt +" This variable, if set, determines the options passed to the hg diff +" command (such as 'u', 'w', or 'b'). + +" Section: Plugin header {{{1 + +if exists('VCSCommandDisableAll') + finish +endif + +if v:version < 700 + echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None + finish +endif + +runtime plugin/vcscommand.vim + +if !executable(VCSCommandGetOption('VCSCommandHGExec', 'hg')) + " HG is not installed + finish +endif + +let s:save_cpo=&cpo +set cpo&vim + +" Section: Variable initialization {{{1 + +let s:hgFunctions = {} + +" Section: Utility functions {{{1 + +" Function: s:Executable() {{{2 +" Returns the executable used to invoke hg suitable for use in a shell +" command. +function! s:Executable() + return shellescape(VCSCommandGetOption('VCSCommandHGExec', 'hg')) +endfunction + +" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2 +" Wrapper to VCSCommandDoCommand to add the name of the HG executable to the +" command argument. +function! s:DoCommand(cmd, cmdName, statusText, options) + if VCSCommandGetVCSType(expand('%')) == 'HG' + let fullCmd = s:Executable() . ' ' . a:cmd + return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options) + else + throw 'HG VCSCommand plugin called on non-HG item.' + endif +endfunction + +" Section: VCS function implementations {{{1 + +" Function: s:hgFunctions.Identify(buffer) {{{2 +function! s:hgFunctions.Identify(buffer) + let oldCwd = VCSCommandChangeToCurrentFileDir(resolve(bufname(a:buffer))) + try + call s:VCSCommandUtility.system(s:Executable() . ' root') + if(v:shell_error) + return 0 + else + return g:VCSCOMMAND_IDENTIFY_INEXACT + endif + finally + call VCSCommandChdir(oldCwd) + endtry +endfunction + +" Function: s:hgFunctions.Add() {{{2 +function! s:hgFunctions.Add(argList) + return s:DoCommand(join(['add -v'] + a:argList, ' '), 'add', join(a:argList, ' '), {}) +endfunction + +" Function: s:hgFunctions.Annotate(argList) {{{2 +function! s:hgFunctions.Annotate(argList) + if len(a:argList) == 0 + if &filetype == 'HGannotate' + " Perform annotation of the version indicated by the current line. + let caption = matchstr(getline('.'),'\v^\s+\zs\d+') + let options = ' -r' . caption + else + let caption = '' + let options = ' -un' + endif + elseif len(a:argList) == 1 && a:argList[0] !~ '^-' + let caption = a:argList[0] + let options = ' -un -r' . caption + else + let caption = join(a:argList, ' ') + let options = ' ' . caption + endif + + return s:DoCommand('blame' . options, 'annotate', caption, {}) +endfunction + +" Function: s:hgFunctions.Commit(argList) {{{2 +function! s:hgFunctions.Commit(argList) + try + return s:DoCommand('commit -v -l "' . a:argList[0] . '"', 'commit', '', {}) + catch /Version control command failed.*nothing changed/ + echomsg 'No commit needed.' + endtry +endfunction + +" Function: s:hgFunctions.Delete() {{{2 +function! s:hgFunctions.Delete(argList) + return s:DoCommand(join(['remove'] + a:argList, ' '), 'remove', join(a:argList, ' '), {}) +endfunction + +" Function: s:hgFunctions.Diff(argList) {{{2 +function! s:hgFunctions.Diff(argList) + if len(a:argList) == 0 + let revOptions = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let revOptions = ['-r' . join(a:argList, ':')] + let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')' + else + " Pass-through + let caption = join(a:argList, ' ') + let revOptions = a:argList + endif + + let hgDiffExt = VCSCommandGetOption('VCSCommandHGDiffExt', '') + if hgDiffExt == '' + let diffExt = [] + else + let diffExt = ['--diff-cmd ' . hgDiffExt] + endif + + let hgDiffOpt = VCSCommandGetOption('VCSCommandHGDiffOpt', '') + if hgDiffOpt == '' + let diffOptions = [] + else + let diffOptions = ['-x -' . hgDiffOpt] + endif + + return s:DoCommand(join(['diff'] + diffExt + diffOptions + revOptions), 'diff', caption, {}) +endfunction + +" Function: s:hgFunctions.Info(argList) {{{2 +function! s:hgFunctions.Info(argList) + return s:DoCommand(join(['log --limit 1'] + a:argList, ' '), 'log', join(a:argList, ' '), {}) +endfunction + +" Function: s:hgFunctions.GetBufferInfo() {{{2 +" Provides version control details for the current file. Current version +" number and current repository version number are required to be returned by +" the vcscommand plugin. +" Returns: List of results: [revision, repository, branch] + +function! s:hgFunctions.GetBufferInfo() + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + let fileName = bufname(originalBuffer) + let statusText = s:VCSCommandUtility.system(s:Executable() . ' status -- "' . fileName . '"') + if(v:shell_error) + return [] + endif + + " File not under HG control. + if statusText =~ '^?' + return ['Unknown'] + endif + + let parentsText = s:VCSCommandUtility.system(s:Executable() . ' parents -- "' . fileName . '"') + let revision = matchlist(parentsText, '^changeset:\s\+\(\S\+\)\n')[1] + + let logText = s:VCSCommandUtility.system(s:Executable() . ' log -- "' . fileName . '"') + let repository = matchlist(logText, '^changeset:\s\+\(\S\+\)\n')[1] + + if revision == '' + " Error + return ['Unknown'] + elseif statusText =~ '^A' + return ['New', 'New'] + else + return [revision, repository] + endif +endfunction + +" Function: s:hgFunctions.Log(argList) {{{2 +function! s:hgFunctions.Log(argList) + if len(a:argList) == 0 + let options = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let options = ['-r' . join(a:argList, ':')] + let caption = options[0] + else + " Pass-through + let options = a:argList + let caption = join(a:argList, ' ') + endif + + let resultBuffer = s:DoCommand(join(['log', '-v'] + options), 'log', caption, {}) + return resultBuffer +endfunction + +" Function: s:hgFunctions.Revert(argList) {{{2 +function! s:hgFunctions.Revert(argList) + return s:DoCommand('revert', 'revert', '', {}) +endfunction + +" Function: s:hgFunctions.Review(argList) {{{2 +function! s:hgFunctions.Review(argList) + if len(a:argList) == 0 + let versiontag = '(current)' + let versionOption = '' + else + let versiontag = a:argList[0] + let versionOption = ' -r ' . versiontag . ' ' + endif + + return s:DoCommand('cat' . versionOption, 'review', versiontag, {}) +endfunction + +" Function: s:hgFunctions.Status(argList) {{{2 +function! s:hgFunctions.Status(argList) + let options = ['-A', '-v'] + if len(a:argList) != 0 + let options = a:argList + endif + return s:DoCommand(join(['status'] + options, ' '), 'status', join(options, ' '), {}) +endfunction + +" Function: s:hgFunctions.Update(argList) {{{2 +function! s:hgFunctions.Update(argList) + return s:DoCommand('update', 'update', '', {}) +endfunction + +" Annotate setting {{{2 +let s:hgFunctions.AnnotateSplitRegex = '\d\+: ' + +" Section: Plugin Registration {{{1 +let s:VCSCommandUtility = VCSCommandRegisterModule('HG', expand('<sfile>'), s:hgFunctions, []) + +let &cpo = s:save_cpo diff --git a/modules/vim/vim.dot.link/plugin/vcssvk.vim b/modules/vim/vim.dot.link/plugin/vcssvk.vim new file mode 100644 index 0000000..b7cc6c9 --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/vcssvk.vim @@ -0,0 +1,257 @@ +" vim600: set foldmethod=marker: +" +" SVK extension for VCSCommand. +" +" Maintainer: Bob Hiestand <bob.hiestand@gmail.com> +" License: +" Copyright (c) Bob Hiestand +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to +" deal in the Software without restriction, including without limitation the +" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +" sell copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in +" all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +" IN THE SOFTWARE. +" +" Section: Documentation {{{1 +" +" Options documentation: {{{2 +" +" VCSCommandSVKExec +" This variable specifies the SVK executable. If not set, it defaults to +" 'svk' executed from the user's executable path. + +" Section: Plugin header {{{1 + +if exists('VCSCommandDisableAll') + finish +endif + +if v:version < 700 + echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None + finish +endif + +runtime plugin/vcscommand.vim + +if !executable(VCSCommandGetOption('VCSCommandSVKExec', 'svk')) + " SVK is not installed + finish +endif + +let s:save_cpo=&cpo +set cpo&vim + +" Section: Variable initialization {{{1 + +let s:svkFunctions = {} + +" Section: Utility functions {{{1 + +" Function: s:Executable() {{{2 +" Returns the executable used to invoke SVK suitable for use in a shell +" command. +function! s:Executable() + return shellescape(VCSCommandGetOption('VCSCommandSVKExec', 'svk')) +endfunction + +" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2 +" Wrapper to VCSCommandDoCommand to add the name of the SVK executable to the +" command argument. +function! s:DoCommand(cmd, cmdName, statusText, options) + if VCSCommandGetVCSType(expand('%')) == 'SVK' + let fullCmd = s:Executable() . ' ' . a:cmd + return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options) + else + throw 'SVK VCSCommand plugin called on non-SVK item.' + endif +endfunction + +" Section: VCS function implementations {{{1 + +" Function: s:svkFunctions.Identify(buffer) {{{2 +function! s:svkFunctions.Identify(buffer) + let fileName = resolve(bufname(a:buffer)) + if isdirectory(fileName) + let directoryName = fileName + else + let directoryName = fnamemodify(fileName, ':p:h') + endif + let statusText = s:VCSCommandUtility.system(s:Executable() . ' info -- "' . directoryName . '"', "no") + if(v:shell_error) + return 0 + else + return 1 + endif +endfunction + +" Function: s:svkFunctions.Add() {{{2 +function! s:svkFunctions.Add(argList) + return s:DoCommand(join(['add'] + a:argList, ' '), 'add', join(a:argList, ' '), {}) +endfunction + +" Function: s:svkFunctions.Annotate(argList) {{{2 +function! s:svkFunctions.Annotate(argList) + if len(a:argList) == 0 + if &filetype == 'SVKannotate' + " Perform annotation of the version indicated by the current line. + let caption = matchstr(getline('.'),'\v^\s+\zs\d+') + let options = ' -r' . caption + else + let caption = '' + let options = '' + endif + elseif len(a:argList) == 1 && a:argList[0] !~ '^-' + let caption = a:argList[0] + let options = ' -r' . caption + else + let caption = join(a:argList, ' ') + let options = ' ' . caption + endif + + let resultBuffer = s:DoCommand('blame' . options, 'annotate', caption, {}) + if resultBuffer > 0 + normal 1G2dd + endif + return resultBuffer +endfunction + +" Function: s:svkFunctions.Commit(argList) {{{2 +function! s:svkFunctions.Commit(argList) + let resultBuffer = s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {}) + if resultBuffer == 0 + echomsg 'No commit needed.' + endif +endfunction + +" Function: s:svkFunctions.Delete() {{{2 +function! s:svkFunctions.Delete(argList) + return s:DoCommand(join(['delete'] + a:argList, ' '), 'delete', join(a:argList, ' '), {}) +endfunction + +" Function: s:svkFunctions.Diff(argList) {{{2 +function! s:svkFunctions.Diff(argList) + if len(a:argList) == 0 + let revOptions = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let revOptions = ['-r' . join(a:argList, ':')] + let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')' + else + " Pass-through + let caption = join(a:argList, ' ') + let revOptions = a:argList + endif + + return s:DoCommand(join(['diff'] + revOptions), 'diff', caption, {}) +endfunction + +" Function: s:svkFunctions.GetBufferInfo() {{{2 +" Provides version control details for the current file. Current version +" number and current repository version number are required to be returned by +" the vcscommand plugin. +" Returns: List of results: [revision, repository] + +function! s:svkFunctions.GetBufferInfo() + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + let fileName = resolve(bufname(originalBuffer)) + let statusText = s:VCSCommandUtility.system(s:Executable() . ' status -v -- "' . fileName . '"') + if(v:shell_error) + return [] + endif + + " File not under SVK control. + if statusText =~ '^?' + return ['Unknown'] + endif + + let [flags, revision, repository] = matchlist(statusText, '^\(.\{3}\)\s\+\(\S\+\)\s\+\(\S\+\)\s\+\(\S\+\)\s')[1:3] + if revision == '' + " Error + return ['Unknown'] + elseif flags =~ '^A' + return ['New', 'New'] + else + return [revision, repository] + endif +endfunction + +" Function: s:svkFunctions.Info(argList) {{{2 +function! s:svkFunctions.Info(argList) + return s:DoCommand(join(['info'] + a:argList, ' '), 'info', join(a:argList, ' '), {}) +endfunction + +" Function: s:svkFunctions.Lock(argList) {{{2 +function! s:svkFunctions.Lock(argList) + return s:DoCommand(join(['lock'] + a:argList, ' '), 'lock', join(a:argList, ' '), {}) +endfunction + +" Function: s:svkFunctions.Log() {{{2 +function! s:svkFunctions.Log(argList) + if len(a:argList) == 0 + let options = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let options = ['-r' . join(a:argList, ':')] + let caption = options[0] + else + " Pass-through + let options = a:argList + let caption = join(a:argList, ' ') + endif + + let resultBuffer = s:DoCommand(join(['log', '-v'] + options), 'log', caption, {}) + return resultBuffer +endfunction + +" Function: s:svkFunctions.Revert(argList) {{{2 +function! s:svkFunctions.Revert(argList) + return s:DoCommand('revert', 'revert', '', {}) +endfunction + +" Function: s:svkFunctions.Review(argList) {{{2 +function! s:svkFunctions.Review(argList) + if len(a:argList) == 0 + let versiontag = '(current)' + let versionOption = '' + else + let versiontag = a:argList[0] + let versionOption = ' -r ' . versiontag . ' ' + endif + + return s:DoCommand('cat' . versionOption, 'review', versiontag, {}) +endfunction + +" Function: s:svkFunctions.Status(argList) {{{2 +function! s:svkFunctions.Status(argList) + let options = ['-v'] + if len(a:argList) != 0 + let options = a:argList + endif + return s:DoCommand(join(['status'] + options, ' '), 'status', join(options, ' '), {}) +endfunction + +" Function: s:svkFunctions.Unlock(argList) {{{2 +function! s:svkFunctions.Unlock(argList) + return s:DoCommand(join(['unlock'] + a:argList, ' '), 'unlock', join(a:argList, ' '), {}) +endfunction +" Function: s:svkFunctions.Update(argList) {{{2 +function! s:svkFunctions.Update(argList) + return s:DoCommand('update', 'update', '', {}) +endfunction + +" Section: Plugin Registration {{{1 +let s:VCSCommandUtility = VCSCommandRegisterModule('SVK', expand('<sfile>'), s:svkFunctions, []) + +let &cpo = s:save_cpo diff --git a/modules/vim/vim.dot.link/plugin/vcssvn.vim b/modules/vim/vim.dot.link/plugin/vcssvn.vim new file mode 100644 index 0000000..758f819 --- /dev/null +++ b/modules/vim/vim.dot.link/plugin/vcssvn.vim @@ -0,0 +1,284 @@ +" vim600: set foldmethod=marker: +" +" SVN extension for VCSCommand. +" +" Maintainer: Bob Hiestand <bob.hiestand@gmail.com> +" License: +" Copyright (c) Bob Hiestand +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to +" deal in the Software without restriction, including without limitation the +" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +" sell copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in +" all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +" IN THE SOFTWARE. +" +" Section: Documentation {{{1 +" +" Options documentation: {{{2 +" +" VCSCommandSVNExec +" This variable specifies the SVN executable. If not set, it defaults to +" 'svn' executed from the user's executable path. +" +" VCSCommandSVNDiffExt +" This variable, if set, sets the external diff program used by Subversion. +" +" VCSCommandSVNDiffOpt +" This variable, if set, determines the options passed to the svn diff +" command (such as 'u', 'w', or 'b'). + +" Section: Plugin header {{{1 + +if exists('VCSCommandDisableAll') + finish +endif + +if v:version < 700 + echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None + finish +endif + +runtime plugin/vcscommand.vim + +if !executable(VCSCommandGetOption('VCSCommandSVNExec', 'svn')) + " SVN is not installed + finish +endif + +let s:save_cpo=&cpo +set cpo&vim + +" Section: Variable initialization {{{1 + +let s:svnFunctions = {} + +" Section: Utility functions {{{1 + +" Function: s:Executable() {{{2 +" Returns the executable used to invoke git suitable for use in a shell +" command. +function! s:Executable() + return shellescape(VCSCommandGetOption('VCSCommandSVNExec', 'svn')) +endfunction + +" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2 +" Wrapper to VCSCommandDoCommand to add the name of the SVN executable to the +" command argument. +function! s:DoCommand(cmd, cmdName, statusText, options) + if VCSCommandGetVCSType(expand('%')) == 'SVN' + let fullCmd = s:Executable() . ' ' . a:cmd + return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options) + else + throw 'SVN VCSCommand plugin called on non-SVN item.' + endif +endfunction + +" Section: VCS function implementations {{{1 + +" Function: s:svnFunctions.Identify(buffer) {{{2 +function! s:svnFunctions.Identify(buffer) + let fileName = resolve(bufname(a:buffer)) + if isdirectory(fileName) + let directoryName = fileName + else + let directoryName = fnamemodify(fileName, ':h') + endif + if strlen(directoryName) > 0 + let svnDir = directoryName . '/.svn' + else + let svnDir = '.svn' + endif + if isdirectory(svnDir) + return 1 + else + return 0 + endif +endfunction + +" Function: s:svnFunctions.Add() {{{2 +function! s:svnFunctions.Add(argList) + return s:DoCommand(join(['add'] + a:argList, ' '), 'add', join(a:argList, ' '), {}) +endfunction + +" Function: s:svnFunctions.Annotate(argList) {{{2 +function! s:svnFunctions.Annotate(argList) + if len(a:argList) == 0 + if &filetype == 'SVNannotate' + " Perform annotation of the version indicated by the current line. + let caption = matchstr(getline('.'),'\v^\s+\zs\d+') + let options = ' -r' . caption + else + let caption = '' + let options = '' + endif + elseif len(a:argList) == 1 && a:argList[0] !~ '^-' + let caption = a:argList[0] + let options = ' -r' . caption + else + let caption = join(a:argList, ' ') + let options = ' ' . caption + endif + + return s:DoCommand('blame --non-interactive' . options, 'annotate', caption, {}) +endfunction + +" Function: s:svnFunctions.Commit(argList) {{{2 +function! s:svnFunctions.Commit(argList) + let resultBuffer = s:DoCommand('commit --non-interactive -F "' . a:argList[0] . '"', 'commit', '', {}) + if resultBuffer == 0 + echomsg 'No commit needed.' + endif +endfunction + +" Function: s:svnFunctions.Delete() {{{2 +function! s:svnFunctions.Delete(argList) + return s:DoCommand(join(['delete --non-interactive'] + a:argList, ' '), 'delete', join(a:argList, ' '), {}) +endfunction + +" Function: s:svnFunctions.Diff(argList) {{{2 +function! s:svnFunctions.Diff(argList) + if len(a:argList) == 0 + let revOptions = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let revOptions = ['-r' . join(a:argList, ':')] + let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')' + else + " Pass-through + let caption = join(a:argList, ' ') + let revOptions = a:argList + endif + + let svnDiffExt = VCSCommandGetOption('VCSCommandSVNDiffExt', '') + if svnDiffExt == '' + let diffExt = [] + else + let diffExt = ['--diff-cmd ' . svnDiffExt] + endif + + let svnDiffOpt = VCSCommandGetOption('VCSCommandSVNDiffOpt', '') + if svnDiffOpt == '' + let diffOptions = [] + else + let diffOptions = ['-x -' . svnDiffOpt] + endif + + return s:DoCommand(join(['diff --non-interactive'] + diffExt + diffOptions + revOptions), 'diff', caption, {}) +endfunction + +" Function: s:svnFunctions.GetBufferInfo() {{{2 +" Provides version control details for the current file. Current version +" number and current repository version number are required to be returned by +" the vcscommand plugin. +" Returns: List of results: [revision, repository, branch] + +function! s:svnFunctions.GetBufferInfo() + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + let fileName = bufname(originalBuffer) + let statusText = s:VCSCommandUtility.system(s:Executable() . ' status --non-interactive -vu -- "' . fileName . '"') + if(v:shell_error) + return [] + endif + + " File not under SVN control. + if statusText =~ '^?' + return ['Unknown'] + endif + + let [flags, revision, repository] = matchlist(statusText, '^\(.\{9}\)\s*\(\d\+\)\s\+\(\d\+\)')[1:3] + if revision == '' + " Error + return ['Unknown'] + elseif flags =~ '^A' + return ['New', 'New'] + elseif flags =~ '*' + return [revision, repository, '*'] + else + return [revision, repository] + endif +endfunction + +" Function: s:svnFunctions.Info(argList) {{{2 +function! s:svnFunctions.Info(argList) + return s:DoCommand(join(['info --non-interactive'] + a:argList, ' '), 'info', join(a:argList, ' '), {}) +endfunction + +" Function: s:svnFunctions.Lock(argList) {{{2 +function! s:svnFunctions.Lock(argList) + return s:DoCommand(join(['lock --non-interactive'] + a:argList, ' '), 'lock', join(a:argList, ' '), {}) +endfunction + +" Function: s:svnFunctions.Log(argList) {{{2 +function! s:svnFunctions.Log(argList) + if len(a:argList) == 0 + let options = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let options = ['-r' . join(a:argList, ':')] + let caption = options[0] + else + " Pass-through + let options = a:argList + let caption = join(a:argList, ' ') + endif + + let resultBuffer = s:DoCommand(join(['log --non-interactive', '-v'] + options), 'log', caption, {}) + return resultBuffer +endfunction + +" Function: s:svnFunctions.Revert(argList) {{{2 +function! s:svnFunctions.Revert(argList) + return s:DoCommand('revert', 'revert', '', {}) +endfunction + +" Function: s:svnFunctions.Review(argList) {{{2 +function! s:svnFunctions.Review(argList) + if len(a:argList) == 0 + let versiontag = '(current)' + let versionOption = '' + else + let versiontag = a:argList[0] + let versionOption = ' -r ' . versiontag . ' ' + endif + + return s:DoCommand('cat --non-interactive' . versionOption, 'review', versiontag, {}) +endfunction + +" Function: s:svnFunctions.Status(argList) {{{2 +function! s:svnFunctions.Status(argList) + let options = ['-u', '-v'] + if len(a:argList) != 0 + let options = a:argList + endif + return s:DoCommand(join(['status --non-interactive'] + options, ' '), 'status', join(options, ' '), {}) +endfunction + +" Function: s:svnFunctions.Unlock(argList) {{{2 +function! s:svnFunctions.Unlock(argList) + return s:DoCommand(join(['unlock --non-interactive'] + a:argList, ' '), 'unlock', join(a:argList, ' '), {}) +endfunction + +" Function: s:svnFunctions.Update(argList) {{{2 +function! s:svnFunctions.Update(argList) + return s:DoCommand('update --non-interactive', 'update', '', {}) +endfunction + +" Annotate setting {{{2 +let s:svnFunctions.AnnotateSplitRegex = '\s\+\S\+\s\+\S\+ ' + +" Section: Plugin Registration {{{1 +let s:VCSCommandUtility = VCSCommandRegisterModule('SVN', expand('<sfile>'), s:svnFunctions, []) + +let &cpo = s:save_cpo |