"============================================================================= " Copyright: Copyright (c) 2001-2014, Jeff Lanzarotta " All rights reserved. " " Redistribution and use in source and binary forms, with or " without modification, are permitted provided that the " following conditions are met: " " * Redistributions of source code must retain the above " copyright notice, this list of conditions and the following " disclaimer. " " * Redistributions in binary form must reproduce the above " copyright notice, this list of conditions and the following " disclaimer in the documentation and/or other materials " provided with the distribution. " " * Neither the name of the {organization} nor the names of its " contributors may be used to endorse or promote products " derived from this software without specific prior written " permission. " " THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND " CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, " INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF " MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE " DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR " CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, " SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT " NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; " LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) " HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN " CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR " OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, " EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. " Name Of File: bufexplorer.vim " Description: Buffer Explorer Vim Plugin " Maintainer: Jeff Lanzarotta (delux256-vim at yahoo dot com) " Last Changed: Monday, 03 November 2014 " 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 " " be - Opens BufExplorer " bs - Opens horizontally split window BufExplorer " bv - Opens vertically split window BufExplorer " " Or you can override the defaults and define your own mapping " in your vimrc file, for example: " " nnoremap :BufExplorer " nnoremap :BufExplorerHorizontalSplit " nnoremap :BufExplorerVerticalSplit " " 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. "============================================================================= " 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.4.6" " 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() command! BufExplorerHorizontalSplit :call BufExplorerHorizontalSplit() command! BufExplorerVerticalSplit :call BufExplorerVerticalSplit() " Set {{{2 function! s:Set(var, default) if !exists(a:var) if type(a:default) execute "let" a:var "=" string(a:default) else execute "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("")) 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 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 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 let s:running = 0 let s:splitMode = "" delmarks! endfunction " BufExplorerHorizontalSplit {{{2 function! BufExplorerHorizontalSplit() let s:splitMode = "sp" execute "BufExplorer" endfunction " BufExplorerVerticalSplit {{{2 function! BufExplorerVerticalSplit() let s:splitMode = "vsp" execute "BufExplorer" endfunction " BufExplorer {{{2 function! BufExplorer() 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") execute "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) " 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] let _size = (s:splitMode == "sp") ? g:bufExplorerSplitHorzSize : g:bufExplorerSplitVertSize " Split the window either horizontally or vertically. if _size <= 0 execute 'keepalt ' . s:splitMode else execute 'keepalt ' . _size . s:splitMode endif " 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 execute "silent keepjumps hide edit".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() " Wipe out any existing lines in case BufExplorer buffer exists and the " user had changed any global settings that might reduce the number of " lines needed in the buffer. silent keepjumps 1,$d _ 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 :call SelectBuffer() endif nnoremap