" Mini Buffer Explorer " " HINT: Type zR if you don't know how to use folds " " Script Info and Documentation {{{ "============================================================================= " Copyright: Copyright (C) 2002 & 2003 Bindu Wavell " Copyright (C) 2010 Oliver Uvman " Copyright (C) 2010 Danielle Church " Copyright (C) 2010 Stephan Sokolow " Copyright (C) 2010 & 2011 Federico Holgado " 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, " minibufexpl.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. " " Name Of File: minibufexpl.vim " Description: Mini Buffer Explorer Vim Plugin " Documentation: See minibufexpl.txt " "============================================================================= " }}} " " Startup Check " " Has this plugin already been loaded? {{{ " if exists('loaded_minibufexplorer') finish endif let loaded_minibufexplorer = 1 " }}} " " Mappings and Commands " " MBE commands {{{ " if !exists(':MiniBufExplorer') command! MiniBufExplorer echoe 'MiniBufExplorer is depreciated, please use MBEOpen instead.' endif if !exists(':CMiniBufExplorer') command! CMiniBufExplorer echoe 'CMiniBufExplorer is depreciated, please use MBEClose instead.' endif if !exists(':TMiniBufExplorer') command! TMiniBufExplorer echoe 'TMiniBufExplorer is depreciated, please use MBEToggle instead.' endif if !exists(':MBEFocus') command! MBEFocus call FocusExplorer() endif if !exists(':MBEFocusAll') command! MBEFocusAll tabdo call FocusExplorer() endif if !exists(':MBEOpen') command! -bang MBEOpen let t:skipEligibleBuffersCheck = 1 | if '' == '!' | call StopExplorer(0) | endif | call StartExplorer(bufnr("%")) endif if !exists(':MBEOpenAll') command! -bang MBEOpenAll tabdo let t:skipEligibleBuffersCheck = 1 | if '' == '!' | call StopExplorer(0) | endif | call StartExplorer(bufnr("%")) | let s:TabsMBEState = 1 endif if !exists(':MBEClose') command! -bang MBEClose let t:skipEligibleBuffersCheck = 0 | call StopExplorer('' == '!') endif if !exists(':MBECloseAll') command! -bang MBECloseAll tabdo let t:skipEligibleBuffersCheck = 0 | call StopExplorer('' == '!') | let s:TabsMBEState = 0 endif if !exists(':MBEToggle') command! -bang MBEToggle call ToggleExplorer(0,''=='!') endif if !exists(':MBEToggleAll') command! -bang MBEToggleAll call ToggleExplorer(1,''=='!') endif if !exists(':MBEToggleMRU') command! -bang MBEToggleMRU call ToggleMRU() endif if !exists(':MBEToggleMRUAll') command! -bang MBEToggleMRUAll tabdo call ToggleMRU() endif if !exists(':MBEbn') command! MBEbn call CycleBuffer(1) endif if !exists(':MBEbp') command! MBEbp call CycleBuffer(0) endif if !exists(':MBEbf') command! MBEbf call CycleBuffer(1,1) endif if !exists(':MBEbb') command! MBEbb call CycleBuffer(0,1) endif if !exists(':MBEbd') command! -bang -nargs=* MBEbd call DeleteBuffer(0,''=='!',) endif if !exists(':MBEbw') command! -bang -nargs=* MBEbw call DeleteBuffer(1,''=='!',) endif if !exists(':MBEbun') command! -bang -nargs=* MBEbun call DeleteBuffer(2,''=='!',) endif " }}} " " Global Configuration Variables - Depreciated " " {{{ if exists('g:miniBufExplSplitBelow') let g:miniBufExplBRSplit = g:miniBufExplSplitBelow endif if exists('g:miniBufExplMaxHeight') let g:miniBufExplMaxSize = g:miniBufExplMaxHeight endif if exists('g:miniBufExplMinHeight') let g:miniBufExplMinSize = g:miniBufExplMinHeight endif if exists('g:miniBufExplorerMoreThanOne') let g:miniBufExplBuffersNeeded = g:miniBufExplorerMoreThanOne endif if exists('g:miniBufExplorerAutoStart') let g:miniBufExplAutoStart = g:miniBufExplorerAutoStart endif if exists('g:miniBufExplorerDebugMode') let g:miniBufExplDebugMode = g:miniBufExplorerDebugMode endif if exists('g:miniBufExplorerDebugLevel') let g:miniBufExplDebugLevel = g:miniBufExplorerDebugLevel endif if exists('g:miniBufExplorerDebugOutput') let g:miniBufExplDebugOutput = g:miniBufExplorerDebugOutput endif if exists('g:miniBufExplorerHideWhenDiff') let g:miniBufExplHideWhenDiff = g:miniBufExplorerHideWhenDiff endif " }}} " " Global Configuration Variables " " Start MBE automatically ? {{{ " if !exists('g:miniBufExplAutoStart') let g:miniBufExplAutoStart = 1 endif " }}} " Debug Mode {{{ " " 0 = debug to a window " 1 = use vim's echo facility " 2 = write to a file named MiniBufExplorer.DBG " in the directory where vim was started " THIS IS VERY SLOW " 3 = Write into g:miniBufExplDebugOutput " global variable [This is the default] if !exists('g:miniBufExplDebugMode') let g:miniBufExplDebugMode = 3 endif " }}} " Debug Level {{{ " " 0 = no logging " 1=5 = errors ; 1 is the most important " 5-9 = info ; 5 is the most important " 10 = Entry/Exit if !exists('g:miniBufExplDebugLevel') let g:miniBufExplDebugLevel = 1 endif " }}} " Stop auto starting MBE in diff mode? {{{ if !exists('g:miniBufExplHideWhenDiff') let g:miniBufExplHideWhenDiff = 0 endif " }}} " MoreThanOne? {{{ " Display Mini Buf Explorer when there are 'More Than One' eligible buffers " if !exists('g:miniBufExplBuffersNeeded') let g:miniBufExplBuffersNeeded = 2 endif " }}} " Set the updatetime? {{{ if !exists('g:miniBufExplSetUT') let g:miniBufExplSetUT = 1 endif " }}} " Horizontal or Vertical explorer? {{{ " For folks that like vertical explorers, I'm caving in and providing for " veritcal splits. If this is set to 0 then the current horizontal " splitting logic will be run. If however you want a vertical split, " assign the width (in characters) you wish to assign to the MBE window. " if !exists('g:miniBufExplVSplit') let g:miniBufExplVSplit = 0 endif " }}} " Split below/above/left/right? {{{ " When opening a new -MiniBufExplorer- window, split the new windows below or " above the current window? 1 = below, 0 = above. " if !exists('g:miniBufExplBRSplit') let g:miniBufExplBRSplit = g:miniBufExplVSplit ? &splitright : &splitbelow endif " }}} " Split to edge? {{{ " When opening a new -MiniBufExplorer- window, split the new windows to the " full edge? 1 = yes, 0 = no. " if !exists('g:miniBufExplSplitToEdge') let g:miniBufExplSplitToEdge = 1 endif " }}} " MaxSize {{{ " Same as MaxHeight but also works for vertical splits if specified with a " vertical split then vertical resizing will be performed. If left at 0 " then the number of columns in g:miniBufExplVSplit will be used as a " static window width. if !exists('g:miniBufExplMaxSize') let g:miniBufExplMaxSize = 0 endif " }}} " MinSize {{{ " Same as MinHeight but also works for vertical splits. For vertical splits, " this is ignored unless g:miniBufExplMax(Size|Height) are specified. if !exists('g:miniBufExplMinSize') let g:miniBufExplMinSize = 1 endif " }}} " TabWrap? {{{ " By default line wrap is used (possibly breaking a tab name between two " lines.) Turning this option on (setting it to 1) can take more screen " space, but will make sure that each tab is on one and only one line. " if !exists('g:miniBufExplTabWrap') let g:miniBufExplTabWrap = 0 endif " }}} " ShowBufNumber? {{{ " By default buffers' numbers are shown in MiniBufExplorer. You can turn it off " by setting this option to 0. " if !exists('g:miniBufExplShowBufNumbers') let g:miniBufExplShowBufNumbers = 1 endif " }}} " Single/Double Click? {{{ " flag that can be set to 1 in a users .vimrc to allow " single click switching of tabs. By default we use " double click for tab selection. " if !exists('g:miniBufExplUseSingleClick') let g:miniBufExplUseSingleClick = 0 endif " }}} " Close on Select? {{{ " Flag that can be set to 1 in a users .vimrc to hide " the explorer when a user selects a buffer. " if !exists('g:miniBufExplCloseOnSelect') let g:miniBufExplCloseOnSelect = 0 endif " }}} " Status Line Text for MBE window {{{ " if !exists('g:miniBufExplStatusLineText') let g:miniBufExplStatusLineText = "-MiniBufExplorer-" endif " }}} " How to sort the buffers in MBE window {{{ " if !exists('g:miniBufExplSortBy') let g:miniBufExplSortBy = "number" endif " }}} " Should buffer be cycled arround if hits the begining or the end while {{{ " using MBE's buffer movement commands. " if !exists('g:miniBufExplCycleArround') let g:miniBufExplCycleArround = 0 endif " }}} " " Variables used internally " " Script/Global variables {{{ " In debug mode 3 this variable will hold the debug output let g:miniBufExplDebugOutput = '' " check to see what platform we are in if (has('unix')) let s:PathSeparator = '/' else let s:PathSeparator = '\' endif " Variable used to count debug output lines let s:debugIndex = 0 " Variable used to pass maxTabWidth info between functions let s:maxTabWidth = 0 " Variable used as a mutex to indicate the current state of MBEToggleAll let s:TabsMBEState = 0 " List for all eligible buffers let s:BufList = [] " List for tracking order of the buffer entering let s:MRUList = [] " Whether MRU List should be updated. let s:MRUEnable = 1 " Dictionary used to keep track of the modification state of buffers let s:bufStateDict = {} " Global used to store the buffer list so that we don't update the MBE " unless the list has changed. let s:miniBufExplBufList = '' " Variable used as a mutex so that AutoUpdates would not get nested. let s:miniBufExplInAutoUpdate = 0 " Dictionary used to keep track of the names we have seen. let s:bufNameDict = {} " Dictionary used to map buffer numbers to names when the buffer " names are not unique. let s:bufUniqNameDict = {} " Dictionary used to hold the path parts for each buffer let s:bufPathDict = {} " Dictionary used to hold the path signature index for each buffer let s:bufPathSignDict = {} " We start out with this off for startup, but once vim is running we " turn this on. This prevent any BufEnter event from being triggered " before VimEnter event. let t:miniBufExplAutoUpdate = 0 " If MBE was opened manually, then we should skip eligible buffers checking, " open MBE window no matter what value 'g:miniBufExplBuffersNeeded' is set. let t:skipEligibleBuffersCheck = 0 " The order of buffer listing in MBE window is tab dependently. let t:miniBufExplSortBy = g:miniBufExplSortBy " }}} " " Auto Commands " " Setup an autocommand group and some autocommands {{{ " that keep our explorer updated automatically. " " Set a lower value to 'updatetime' for the CursorHold/CursorHoldI event, so " that the MBE could be updated in time. It can not be set too low, otherwise " might breaks many things, 1500ms should be a reasonable value. " We only change it if we are allowed to and it has not been changed yet. if g:miniBufExplSetUT && &ut == 4000 set updatetime=1500 endif augroup MiniBufExpl autocmd! autocmd VimEnter * nested call VimEnterHandler() autocmd TabEnter * nested call TabEnterHandler() autocmd BufAdd * call BufAddHandler() autocmd BufEnter * nested call BufEnterHandler() autocmd BufDelete * call BufDeleteHandler() autocmd CursorHold,CursorHoldI,BufWritePost * \ call DEBUG('Entering UpdateBufferStateDict AutoCmd', 10) | \ call UpdateBufferStateDict(bufnr("%"),0) | \ call DEBUG('Leaving UpdateBufferStateDict AutoCmd', 10) if exists('##QuitPre') autocmd QuitPre * \ if NextNormalWindow() == -1 | call StopExplorer(0) | endif else autocmd BufEnter * nested call QuitIfLastOpen() endif autocmd FileType minibufexpl call RenderSyntax() augroup END function! VimEnterHandler() call DEBUG('Entering VimEnter Handler', 10) " Build initial MRUList. " This makes sure all the files specified on the command " line are picked up correctly. let s:BufList = range(1, bufnr('$')) let s:MRUList = range(1, bufnr('$')) for l:i in s:BufList if IsBufferIgnored(l:i) call ListPop(s:BufList,l:i) endif endfor for l:i in s:MRUList if IsBufferIgnored(l:i) call ListPop(s:MRUList,l:i) endif endfor if g:miniBufExplHideWhenDiff!=1 || !&diff let t:miniBufExplAutoUpdate = 1 endif if g:miniBufExplAutoStart && t:miniBufExplAutoUpdate == 1 \ && (t:skipEligibleBuffersCheck == 1 || HasEligibleBuffers() == 1) call StartExplorer(bufnr("%")) " Let the MBE open in the new tab let s:TabsMBEState = 1 endif call DEBUG('Leaving VimEnter Handler', 10) endfunction function! TabEnterHandler() call DEBUG('Entering TabEnter Handler', 10) if !exists('t:miniBufExplSortBy') let t:miniBufExplSortBy = g:miniBufExplSortBy endif if !exists('t:miniBufExplAutoUpdate') let t:miniBufExplAutoUpdate = s:TabsMBEState endif if !exists('t:skipEligibleBuffersCheck') let t:skipEligibleBuffersCheck = 0 endif if g:miniBufExplAutoStart && t:miniBufExplAutoUpdate == 1 \ && (t:skipEligibleBuffersCheck == 1 || HasEligibleBuffers() == 1) call StartExplorer(bufnr("%")) endif call DEBUG('Leaving TabEnter Handler', 10) endfunction function! BufAddHandler() call DEBUG('Entering BufAdd Handler', 10) call ListAdd(s:BufList,str2nr(expand(""))) call ListAdd(s:MRUList,str2nr(expand(""))) call UpdateAllBufferDicts(expand(""),0) call AutoUpdate(bufnr("%"),0) call DEBUG('Leaving BufAdd Handler', 10) endfunction function! BufEnterHandler() abort call DEBUG('Entering BufEnter Handler', 10) for l:i in s:BufList if IsBufferIgnored(l:i) call ListPop(s:BufList,l:i) endif endfor for l:i in s:MRUList if IsBufferIgnored(l:i) call ListPop(s:MRUList,l:i) endif endfor call AutoUpdate(bufnr("%"),0) call DEBUG('Leaving BufEnter Handler', 10) endfunction function! BufDeleteHandler() call DEBUG('Entering BufDelete Handler', 10) call ListPop(s:BufList,str2nr(expand(""))) call ListPop(s:MRUList,str2nr(expand(""))) call UpdateAllBufferDicts(expand(""),1) " Handle ':bd' command correctly if (bufname('%') == '-MiniBufExplorer-' && NextNormalWindow() == -1 && len(s:BufList) > 0) if(tabpagenr('$') == 1) setlocal modifiable resize exec 'noautocmd sb'.s:BufList[0] call StopExplorer(0) call StartExplorer(bufnr("%")) else close endif endif call AutoUpdate(bufnr("%"),1) call DEBUG('Leaving BufDelete Handler', 10) endfunction " }}} " " Functions " " RenderSyntax {{{ " function! RenderSyntax() if has("syntax") syn clear syn match MBENormal '\[[^\]]*\]' syn match MBEChanged '\[[^\]]*\]+' syn match MBEVisibleNormal '\[[^\]]*\]\*' syn match MBEVisibleChanged '\[[^\]]*\]\*+' syn match MBEVisibleActiveNormal '\[[^\]]*\]\*!' syn match MBEVisibleActiveChanged '\[[^\]]*\]\*+!' "MiniBufExpl Color Examples " hi MBENormal guifg=#808080 guibg=fg " hi MBEChanged guifg=#CD5907 guibg=fg " hi MBEVisibleNormal guifg=#5DC2D6 guibg=fg " hi MBEVisibleChanged guifg=#F1266F guibg=fg " hi MBEVisibleActiveNormal guifg=#A6DB29 guibg=fg " hi MBEVisibleActiveChanged guifg=#F1266F guibg=fg if !exists("g:did_minibufexplorer_syntax_inits") let g:did_minibufexplorer_syntax_inits = 1 hi def link MBENormal Comment hi def link MBEChanged String hi def link MBEVisibleNormal Special hi def link MBEVisibleChanged Special hi def link MBEVisibleActiveNormal Underlined hi def link MBEVisibleActiveChanged Error endif let b:current_syntax = "minibufexpl" endif endfunction " }}} " StartExplorer - Sets up our explorer and causes it to be displayed {{{ " function! StartExplorer(curBufNum) call DEBUG('Entering StartExplorer('.a:curBufNum.')',10) call DEBUG('Current state: '.winnr().' : '.bufnr('%').' : '.bufname('%'),10) call BuildAllBufferDicts() let t:miniBufExplAutoUpdate = 1 let l:winNum = FindWindow('-MiniBufExplorer-', 1) if l:winNum != -1 call DEBUG('There is already a MBE window, aborting...',1) call DEBUG('Leaving StartExplorer()',10) return endif call CreateWindow('-MiniBufExplorer-', g:miniBufExplVSplit, g:miniBufExplBRSplit, g:miniBufExplSplitToEdge, 1, 1) let l:winNum = FindWindow('-MiniBufExplorer-', 1) if l:winNum == -1 call DEBUG('Failed to create the MBE window, aborting...',1) call DEBUG('Leaving StartExplorer()',10) return endif " Save current window number and switch to previous " window before entering MBE window so that the later " `wincmd p` command will get into this window, then " we can preserve a one level window movement history. let l:currWin = winnr() call s:SwitchWindow('p',1) " Switch into MBE allowing autocmd to run will " make the syntax highlight in MBE window working call s:SwitchWindow('w',0,l:winNum) " Make sure we are in our window if bufname('%') != '-MiniBufExplorer-' call DEBUG('StartExplorer called in invalid window',1) call DEBUG('Leaving StartExplorer()',10) return endif " Set filetype for MBE buffer set filetype=minibufexpl " !!! We may want to make the following optional -- Bindu " New windows don't cause all windows to be resized to equal sizes set noequalalways " !!! We may want to make the following optional -- Bindu " We don't want the mouse to change focus without a click set nomousefocus if g:miniBufExplVSplit == 0 setlocal wrap else setlocal nowrap exec 'setlocal winwidth='.g:miniBufExplMinSize endif " If folks turn numbering and columns on by default we will turn " them off for the MBE window setlocal foldcolumn=0 setlocal nonumber if exists("&norelativenumber") " relativenumber was introduced in Vim 7.3 - this provides compatibility " for older versions of Vim setlocal norelativenumber endif "don't highlight matching parentheses, etc. setlocal matchpairs= "Depending on what type of split, make sure the MBE buffer is not "automatically rezised by CTRL + W =, etc... setlocal winfixheight setlocal winfixwidth " Set the text of the statusline for the MBE buffer. See help:stl for " many options exec 'setlocal statusline='.g:miniBufExplStatusLineText " No spell check setlocal nospell " Restore colorcolumn for VIM >= 7.3 if exists("+colorcolumn") setlocal colorcolumn& end " If you press return, o or e in the -MiniBufExplorer- then try " to open the selected buffer in the previous window. nnoremap o :call MBESelectBuffer(0): nnoremap e :call MBESelectBuffer(0): nnoremap :call MBESelectBuffer(0): " If you press s in the -MiniBufExplorer- then try " to open the selected buffer in a split in the previous window. nnoremap s :call MBESelectBuffer(1): " If you press j in the -MiniBufExplorer- then try " to open the selected buffer in a vertical split in the previous window. nnoremap v :call MBESelectBuffer(2): " If you press d in the -MiniBufExplorer- then try to " delete the selected buffer. nnoremap d :call MBEDeleteBuffer(): " The following allow us to use regular movement keys to " scroll in a wrapped single line buffer nnoremap k gk nnoremap j gj nnoremap gk nnoremap gj " The following allows for quicker moving between buffer " names in the [MBE] window it also saves the last-pattern " and restores it. if !g:miniBufExplShowBufNumbers nnoremap l :call search('\[[^\]]*\]'): nnoremap h :call search('\[[^\]]*\]','b'): nnoremap :call search('\[[^\]]*\]'): nnoremap :call search('\[[^\]]*\]','b'): else nnoremap l :call search('\[[0-9]*:[^\]]*\]'): nnoremap h :call search('\[[0-9]*:[^\]]*\]','b'): nnoremap :call search('\[[0-9]*:[^\]]*\]'): nnoremap :call search('\[[0-9]*:[^\]]*\]','b'): endif " Attempt to perform single click mapping " It would be much nicer if we could 'nnoremap ...', however " vim does not fire the ' ' when you use the mouse " to enter a buffer. if g:miniBufExplUseSingleClick == 1 let l:mapcmd = ':nnoremap ' let l:clickcmd = ':if bufname("%") == "-MiniBufExplorer-" call MBESelectBuffer(0) endif ' " no mapping for leftmouse if maparg('', 'n') == '' exec l:mapcmd . l:clickcmd " we have a mapping else let l:mapcmd = l:mapcmd . substitute(substitute(maparg('', 'n'), '|', '', 'g'), '\c^', '', '') let l:mapcmd = l:mapcmd . l:clickcmd exec l:mapcmd endif " If you DoubleClick in the MBE window then try to open the selected " buffer in the previous window. else nnoremap <2-LEFTMOUSE> :call MBESelectBuffer(0): endif call BuildBufferList(a:curBufNum) call DisplayBuffers(a:curBufNum) " Switch away from MBE allowing autocmd to run which will " trigger PowerLine's BufLeave event handler call s:SwitchWindow('p',0) " Now we are in the previous window, let's enter " the window current window, this will preserve " one-level backwards window movement history. call s:SwitchWindow('w',1,l:currWin) call DEBUG('Leaving StartExplorer()',10) endfunction " }}} " StopExplorer - Looks for our explorer and closes the window if it is open {{{ " function! StopExplorer(force) call DEBUG('Entering StopExplorer()',10) if a:force || HasEligibleBuffers() let t:miniBufExplAutoUpdate = 0 endif let l:winNum = FindWindow('-MiniBufExplorer-', 1) if l:winNum == -1 call DEBUG('There is no MBE window, aborting...',1) call DEBUG('Leaving StopExplorer()',10) return endif call s:SwitchWindow('w',1,l:winNum) silent! close call s:SwitchWindow('p',1) " Work around a redraw bug in gVim (Confirmed present in 7.3.50) if has('gui_gtk') && has('gui_running') redraw! endif call DEBUG('Leaving StopExplorer()',10) endfunction " }}} " FocusExplorer {{{ " function! FocusExplorer() call DEBUG('Entering FocusExplorer()',10) let t:miniBufExplAutoUpdate = 1 let l:winNum = FindWindow('-MiniBufExplorer-', 1) if l:winNum == -1 call DEBUG('There is no MBE window, aborting...',1) call DEBUG('Leaving FocusExplorer()',10) return endif call s:SwitchWindow('w',0,l:winNum) call DEBUG('Leaving FocusExplorer()',10) endfunction " }}} " ToggleMRU - Switch the order of buffer listing in MBE window {{{ " between its default and most recently used. " function! ToggleMRU() call DEBUG('Entering ToggleMRU()',10) if t:miniBufExplSortBy == 'number' let t:miniBufExplSortBy = 'mru' else let t:miniBufExplSortBy = 'number' endif let l:winnr = FindWindow('-MiniBufExplorer-', 1) if (l:winnr == -1) call DEBUG('MiniBufExplorer was not running, starting...', 9) call StartExplorer(bufnr('%')) else call DEBUG('Updating MiniBufExplorer...', 9) call UpdateExplorer(bufnr('%')) endif call DEBUG('Leaving ToggleMRU()',10) endfunction " }}} " ToggleExplorer - Looks for our explorer and opens/closes the window {{{ " " a:tabs, 0 no, 1 yes " toggle MBE in all tabs " a:force, 0 no, 1 yes " reopen MBE when it is already open " close MBE with auto-updating disabled " function! ToggleExplorer(tabs,force) call DEBUG('Entering ToggleExplorer()',10) if a:tabs if s:TabsMBEState tabdo let t:skipEligibleBuffersCheck = 0 | call StopExplorer(a:force) else tabdo let t:skipEligibleBuffersCheck = 1 | if a:force | call StopExplorer(0) | endif | call StartExplorer(bufnr("%")) endif let s:TabsMBEState = !s:TabsMBEState else let l:winNum = FindWindow('-MiniBufExplorer-', 1) if l:winNum != -1 let t:skipEligibleBuffersCheck = 0 call StopExplorer(a:force) else let t:skipEligibleBuffersCheck = 1 call StartExplorer(bufnr("%")) endif endif call DEBUG('Leaving ToggleExplorer()',10) endfunction " }}} " UpdateExplorer {{{ " function! UpdateExplorer(curBufNum) call DEBUG('Entering UpdateExplorer('.a:curBufNum.')',10) call DEBUG('Current state: '.winnr().' : '.bufnr('%').' : '.bufname('%'),10) if !BuildBufferList(a:curBufNum) call DEBUG('Buffer List have not changed, aborting...',10) call DEBUG('Leaving UpdateExplorer()',10) return endif let l:winNum = FindWindow('-MiniBufExplorer-', 1) if l:winNum == -1 call DEBUG('Found no MBE window, aborting...',1) call DEBUG('Leaving UpdateExplorer()',10) return endif if l:winNum != winnr() let l:winChanged = 1 " Save current window number and switch to previous " window before entering MBE window so that the later " `wincmd p` command will get into this window, then " we can preserve a one level window movement history. let l:currWin = winnr() call s:SwitchWindow('p',1) " Switch into MBE allowing autocmd to run will " make the syntax highlight in MBE window working call s:SwitchWindow('w',0,l:winNum) endif call DisplayBuffers(a:curBufNum) if exists('l:winChanged') " Switch away from MBE allowing autocmd to run which will " trigger PowerLine's BufLeave event handler call s:SwitchWindow('p',0) " Now we are in the previous window, let's enter " the window current window, this will preserve " one-level backwards window movement history. call s:SwitchWindow('w',1,l:currWin) endif call DEBUG('Leaving UpdateExplorer()',10) endfunction " }}} " FindWindow - Return the window number of a named buffer {{{ " If none is found then returns -1. " function! FindWindow(bufName, doDebug) if a:doDebug call DEBUG('Entering FindWindow('.a:bufName.','.a:doDebug.')',10) endif " Try to find an existing window that contains " our buffer. let l:winnr = bufwinnr(a:bufName) if l:winnr != -1 if a:doDebug call DEBUG('Found window '.l:winnr.' with buffer ('.winbufnr(l:winnr).' : '.bufname(winbufnr(l:winnr)).')',9) endif else if a:doDebug call DEBUG('Can not find window with buffer ('.a:bufName.')',9) endif endif if a:doDebug call DEBUG('Leaving FindWindow()',10) endif return l:winnr endfunction " }}} " CreateWindow {{{ " " vSplit, 0 no, 1 yes " split vertically or horizontally " brSplit, 0 no, 1 yes " split the window below/right to current window " forceEdge, 0 no, 1 yes " split the window at the edege of the editor " isPluginWindow, 0 no, 1 yes " if it is a plugin window " doDebug, 0 no, 1 yes " show debugging message or not " function! CreateWindow(bufName, vSplit, brSplit, forceEdge, isPluginWindow, doDebug) if a:doDebug call DEBUG('Entering CreateWindow('.a:bufName.','.a:vSplit.','.a:brSplit.','.a:forceEdge.','.a:isPluginWindow.','.a:doDebug.')',10) endif " Window number will change after creating a new window, " we need to save both current and previous window number " so that we can canculate theire correct window number " after the new window creating. let l:currWin = winnr() call s:SwitchWindow('p',1) let l:prevWin = winnr() call s:SwitchWindow('p',1) " Save the user's split setting. let l:saveSplitBelow = &splitbelow let l:saveSplitRight = &splitright " Set to our new values. let &splitbelow = a:brSplit let &splitright = a:brSplit let l:bufNum = bufnr(a:bufName) if l:bufNum == -1 let l:spCmd = 'sp' else let l:spCmd = 'sb' endif if a:forceEdge == 1 let l:edge = a:vSplit ? &splitright : &splitbelow if l:edge if a:vSplit == 0 silent exec 'noautocmd bo '.l:spCmd.' '.a:bufName else silent exec 'noautocmd bo vert '.l:spCmd.' '.a:bufName endif else if a:vSplit == 0 silent exec 'noautocmd to '.l:spCmd.' '.a:bufName else silent exec 'noautocmd to vert '.l:spCmd.' '.a:bufName endif endif else if a:vSplit == 0 silent exec 'noautocmd '.l:spCmd.' '.a:bufName else silent exec 'noautocmd vert '.l:spCmd.' '.a:bufName endif endif " Restore the user's split setting. let &splitbelow = l:saveSplitBelow let &splitright = l:saveSplitRight " Turn off the swapfile, set the buftype and bufhidden option, so that it " won't get written and will be deleted when it gets hidden. if a:isPluginWindow setlocal noswapfile setlocal nobuflisted setlocal buftype=nofile setlocal bufhidden=delete endif " Canculate the correct window number, for those whose window " number before the creating is greater than or equal to the " number of the newly created window, their window number should " increase by one. let l:prevWin = l:prevWin >= winnr() ? l:prevWin + 1 : l:prevWin let l:currWin = l:currWin >= winnr() ? l:currWin + 1 : l:currWin " This will preserve one-level backwards window movement history. call s:SwitchWindow('w',1,l:prevWin) call s:SwitchWindow('w',1,l:currWin) if a:doDebug call DEBUG('Leaving CreateWindow()',10) endif endfunction " }}} " FindCreateWindow - Attempts to find a window for a named buffer. {{{ " " If it is found then moves there. Otherwise creates a new window and " configures it and moves there. " " vSplit, 0 no, 1 yes " split vertically or horizontally " brSplit, 0 no, 1 yes " split the window below/right to current window " forceEdge, 0 no, 1 yes " split the window at the edege of the editor " isPluginWindow, 0 no, 1 yes " if it is a plugin window " doDebug, 0 no, 1 yes " show debugging message or not " function! FindCreateWindow(bufName, vSplit, brSplit, forceEdge, isPluginWindow, doDebug) if a:doDebug call DEBUG('Entering FindCreateWindow('.a:bufName.','.a:vSplit.','.a:brSplit.','.a:forceEdge.','.a:isPluginWindow.','.a:doDebug.')',10) endif " Try to find an existing explorer window let l:winNum = FindWindow(a:bufName, a:doDebug) " If found goto the existing window, otherwise " split open a new window. if l:winNum == -1 if a:doDebug call DEBUG('Creating a new window with buffer ('.a:bufName.')',9) endif call CreateWindow(a:bufName, a:vSplit, a:brSplit, a:forceEdge, a:isPluginWindow, a:doDebug) " Try to find an existing explorer window let l:winNum = FindWindow(a:bufName, 0) if l:winNum != -1 if a:doDebug call DEBUG('Created window '.l:winNum.' with buffer ('.a:bufName.')',9) endif else if a:doDebug call DEBUG('Failed to create window with buffer ('.a:bufName.').',1) endif endif endif if a:doDebug call DEBUG('Leaving FindCreateWindow()',10) endif return l:winNum endfunction " }}} " DisplayBuffers - Wrapper for getting MBE window shown {{{ " " Makes sure we are in our explorer, then erases the current buffer and turns " it into a mini buffer explorer window. " function! DisplayBuffers(curBufNum) call DEBUG('Entering DisplayExplorer('.a:curBufNum.')',10) " Make sure we are in our window if bufname('%') != '-MiniBufExplorer-' call DEBUG('DisplayBuffers called in invalid window',1) return endif call ShowBuffers() call ResizeWindow() " Place cursor at current buffer in MBE if !IsBufferIgnored(a:curBufNum) if !g:miniBufExplShowBufNumbers call search('\V['.s:bufUniqNameDict[a:curBufNum].']', 'w') else call search('\V['.a:curBufNum.':'.s:bufUniqNameDict[a:curBufNum].']', 'w') endif endif call DEBUG('Leaving DisplayExplorer()',10) endfunction " }}} " Resize Window - Set width/height of MBE window {{{ " " Makes sure we are in our explorer, then sets the height/width for our explorer " window so that we can fit all of our information without taking extra lines. " function! ResizeWindow() call DEBUG('Entering ResizeWindow()',10) " Make sure we are in our window if bufname('%') != '-MiniBufExplorer-' call DEBUG('ResizeWindow called in invalid window',1) call DEBUG('Leaving ResizeWindow()',10) return endif " Prevent a report of our actions from showing up. let l:save_rep = &report let l:save_sc = &showcmd let &report = 10000 set noshowcmd let l:width = winwidth('.') " Horizontal Resize if g:miniBufExplVSplit == 0 if g:miniBufExplTabWrap == 0 let l:length = strlen(getline('.')) let l:height = 0 if (l:width == 0) let l:height = winheight('.') else let l:height = (l:length / l:width) " handle truncation from div if (l:length % l:width) != 0 let l:height = l:height + 1 endif endif else " We need to be able to modify the buffer setlocal modifiable exec "setlocal textwidth=".l:width normal gg normal gq} normal G let l:height = line('.') normal gg " Prevent the buffer from being modified. setlocal nomodifiable endif " enforce max window height if g:miniBufExplMaxSize != 0 if g:miniBufExplMaxSize < l:height let l:height = g:miniBufExplMaxSize endif endif " enfore min window height if l:height < g:miniBufExplMinSize || l:height == 0 let l:height = g:miniBufExplMinSize endif call DEBUG('ResizeWindow to '.l:height.' lines',9) if &winminheight > l:height let l:saved_winminheight = &winminheight let &winminheight = 1 exec 'resize '.l:height let &winminheight = l:saved_winminheight else exec 'resize '.l:height endif let saved_ead = &ead let &ead = 'ver' set equalalways let &ead = saved_ead set noequalalways " Vertical Resize else if g:miniBufExplMaxSize != 0 let l:newWidth = s:maxTabWidth if l:newWidth > g:miniBufExplMaxSize let l:newWidth = g:miniBufExplMaxSize endif if l:newWidth < g:miniBufExplMinSize let l:newWidth = g:miniBufExplMinSize endif else let l:newWidth = g:miniBufExplVSplit endif if l:width != l:newWidth call DEBUG('ResizeWindow to '.l:newWidth.' columns',9) exec 'vertical resize '.l:newWidth endif let saved_ead = &ead let &ead = 'hor' set equalalways let &ead = saved_ead set noequalalways endif normal! zz let &report = l:save_rep let &showcmd = l:save_sc call DEBUG('Leaving ResizeWindow()',10) endfunction " }}} " ShowBuffers - Clear current buffer and put the MBE text into it {{{ " " Makes sure we are in our explorer, then adds a list of all modifiable " buffers to the current buffer. Special marks are added for buffers that " are in one or more windows (*) and buffers that have been modified (+) " function! ShowBuffers() call DEBUG('Entering ShowExplorer()',10) " Make sure we are in our window if bufname('%') != '-MiniBufExplorer-' call DEBUG('ShowBuffers called in invalid window',1) call DEBUG('Leaving ShowBuffers()',10) return endif let l:save_rep = &report let l:save_sc = &showcmd let &report = 10000 set noshowcmd " We need to be able to modify the buffer setlocal modifiable " Delete all lines in buffer. silent 1,$d _ " Goto the end of the buffer put the buffer list " and then delete the extra trailing blank line $ put! =s:miniBufExplBufList silent $ d _ " Prevent the buffer from being modified. setlocal nomodifiable let &report = l:save_rep let &showcmd = l:save_sc call DEBUG('Leaving ShowBuffers()',10) endfunction " }}} " CycleBuffer - Cycle Through Buffers {{{ " " Move to next or previous buffer in the current window. If there " are no more modifiable buffers then stay on the current buffer. " can be called with no parameters in which case the buffers are " cycled forward. Otherwise a single argument is accepted, if " it's 0 then the buffers are cycled backwards, otherwise they " are cycled forward. " function! CycleBuffer(forward,...) if IsBufferIgnored(bufnr('%')) == 1 return endif let curBufNum = bufnr('%') if exists('a:1') && a:1 == 1 call DEBUG('MRUList is '.string(s:MRUList),1) let curBufIndex = index(s:MRUList, l:curBufNum) call DEBUG('curBufIndex is '.l:curBufIndex,1) let forBufIndex = l:curBufIndex - 1 < 0 ? len(s:MRUList) - 1 : l:curBufIndex - 1 call DEBUG('forBufIndex is '.l:forBufIndex,1) let bacBufIndex = l:curBufIndex + 1 >= len(s:MRUList) ? 0 : l:curBufIndex + 1 call DEBUG('bacBufIndex is '.l:bacBufIndex,1) if a:forward if !g:miniBufExplCycleArround && l:curBufIndex < l:forBufIndex echo "You have reached the most recent buffer!" return endif let l:moveCmd = 'b! '.s:MRUList[l:forBufIndex] else if !g:miniBufExplCycleArround && l:curBufIndex > l:bacBufIndex echo "You have reached the least recent buffer!" return endif let l:moveCmd = 'b! '.s:MRUList[l:bacBufIndex] endif let s:MRUEnable = 0 else call DEBUG('BufList is '.string(s:BufList),1) let curBufIndex = index(s:BufList, l:curBufNum) call DEBUG('curBufIndex is '.l:curBufIndex,1) let forBufIndex = l:curBufIndex + 1 >= len(s:BufList) ? 0 : l:curBufIndex + 1 call DEBUG('forBufIndex is '.l:forBufIndex,1) let bacBufIndex = l:curBufIndex - 1 < 0 ? len(s:BufList) - 1 : l:curBufIndex - 1 call DEBUG('bacBufIndex is '.l:bacBufIndex,1) if a:forward if !g:miniBufExplCycleArround && l:curBufIndex > l:forBufIndex echo "You have reached the last buffer!" return endif let l:moveCmd = 'b! '.s:BufList[l:forBufIndex] else if !g:miniBufExplCycleArround && l:curBufIndex < l:bacBufIndex echo "You have reached the first buffer!" return endif let l:moveCmd = 'b! '.s:BufList[l:bacBufIndex] endif let s:MRUEnable = 1 endif call DEBUG('===============move cmd is '.l:moveCmd,1) " Change buffer (keeping track of before and after buffers) exec l:moveCmd let s:MRUEnable = 1 endfunction " }}} " DeleteBuffer {{{ " " Delete/Unload/Wipeout a buffer but preserve the window it was in " " a:action 0 bd, 1 bw, 2 bun " delete/wipeout/unload a buffer " a:bufNum " number of the buffer to be deleted " function! DeleteBuffer(action,bang,...) call DEBUG('Entering DeleteBuffer('.a:action.','.a:bang.')',10) if a:0 == 0 call DEBUG('No buffer is given, use current buffer',5) let l:bufNums = [ bufnr('%') ] else call DEBUG('Given buffers are '.string(a:000),5) let l:bufNums = map(copy(a:000),'v:val =~ ''\d\+'' ? bufnr(v:val + 0) : bufnr(v:val)') endif call DEBUG('Buffers to be deleted are '.string(l:bufNums),5) for l:bufNum in l:bufNums if IsBufferIgnored(l:bufNum) call DEBUG('Buffer '.l:bufNum.'is not a normal buffer, skip it',5) continue endif let l:bufName = bufname(l:bufNum) call DEBUG('Buffer to be deleted is <'.l:bufName.'>['.l:bufNum.']',5) " Don't want auto updates while we are processing a delete " request. let l:saveAutoUpdate = t:miniBufExplAutoUpdate let t:miniBufExplAutoUpdate = 0 " Get the currently active buffer, so we can update the MBE " correctly. If that is the buffer to be deleted, then get " the window for that buffer, so we can find which buffer " is in that window after the detaching. let l:actBuf = GetActiveBuffer() if l:actBuf == l:bufNum let l:actBufWin = bufwinnr(l:actBuf) endif " Detach the buffer from all the windows that holding it " in every tab page. tabdo call DetachBuffer(l:bufNum) " Find which buffer is in the active window now if l:actBuf == l:bufNum let l:actBuf = winbufnr(l:actBufWin) endif " Delete the buffer selected. call DEBUG('About to delete buffer: '.l:bufNum,5) if a:action == 2 let l:cmd = 'bun' elseif a:action == 1 let l:cmd = 'bw' else let l:cmd = 'bd' endif if a:bang let l:cmd = l:cmd.'!' endif let l:cmd = 'silent! '.l:cmd.' '.l:bufNum call DEBUG('About to execute the command "'.l:cmd.'"',5) exec l:cmd let t:miniBufExplAutoUpdate = l:saveAutoUpdate call UpdateExplorer(l:actBuf) endfor call DEBUG('Leaving DeleteBuffer()',10) endfunction " }}} " DetachBuffer {{{ " " Detach a buffer from all the windows in which it is displayed. " function! DetachBuffer(bufNum) call DEBUG('Entering DetachBuffer('.a:bufNum.')',10) call DEBUG('We are currently in tab page '.tabpagenr(),10) let l:bufNum = a:bufNum + 0 let l:winNum = bufwinnr(l:bufNum) " while we have windows that contain our buffer while l:winNum != -1 call DEBUG('Buffer '.l:bufNum.' is being displayed in window: '.l:winNum,5) " move to window that contains our selected buffer call s:SwitchWindow('w',1,l:winNum) call DEBUG('We are now in window: '.winnr(),5) call DEBUG('Window contains buffer: '.bufnr('%').' which should be: '.l:bufNum,5) let l:origBuf = bufnr('%') call CycleBuffer(0,1) let l:currBuf = bufnr('%') call DEBUG('Window now contains buffer: '.bufnr('%').' which should not be: '.l:bufNum,5) if l:origBuf == l:currBuf " we wrapped so we are going to have to delete a buffer " that is in an open window. let l:winNum = -1 else " see if we have anymore windows with our selected buffer let l:winNum = bufwinnr(l:bufNum) endif endwhile call DEBUG('Leaving DetachBuffer()',10) endfunction " }}} " IsBufferIgnored - check to see if buffer should be ignored {{{ " " Returns 0 if this buffer should be displayed in the list, 1 otherwise. " function! IsBufferIgnored(buf) call DEBUG('Entering IsBufferIgnored('.a:buf.')',10) " Skip unlisted buffers. if buflisted(a:buf) == 0 || index(s:BufList,a:buf) == -1 call DEBUG('Buffer '.a:buf.' is unlisted, ignoring...',5) call DEBUG('Leaving IsBufferIgnored()',10) return 1 endif " Skip non normal buffers. if getbufvar(a:buf, "&buftype") != '' call DEBUG('Buffer '.a:buf.' is not normal, ignoring...',5) call DEBUG('Leaving IsBufferIgnored()',10) return 1 endif call DEBUG('Leaving IsBufferIgnored()',10) return 0 endfunction " }}} " BuildBufferList - Build the text for the MBE window {{{ " " Creates the buffer list string and returns 1 if it is different than " last time this was called and 0 otherwise. " function! BuildBufferList(curBufNum) call DEBUG('Entering BuildBufferList('.a:curBufNum.')',10) let l:CurBufNum = a:curBufNum " Get the number of the last buffer. let l:NBuffers = bufnr('$') let l:tabList = [] let l:maxTabWidth = 0 " Loop through every buffer less than the total number of buffers. for l:i in s:BufList let l:BufName = expand( "#" . l:i . ":p:t") " Establish the tab's content, including the differentiating root " dir if neccessary let l:tab = '[' if g:miniBufExplShowBufNumbers == 1 let l:tab .= l:i.':' endif let l:tab .= s:bufUniqNameDict[l:i] let l:tab .= ']' " If the buffer is open in a window mark it if bufwinnr(l:i) != -1 let l:tab .= '*' endif " If the buffer is modified then mark it if(getbufvar(l:i, '&modified') == 1) let l:tab .= '+' endif " If the buffer matches the)current buffer name, then mark it call DEBUG('l:i is '.l:i.' and l:CurBufNum is '.l:CurBufNum,10) if(l:i == l:CurBufNum) let l:tab .= '!' endif let l:maxTabWidth = strlen(l:tab) > l:maxTabWidth ? strlen(l:tab) : l:maxTabWidth call add(l:tabList, l:tab) endfor if t:miniBufExplSortBy == "name" call sort(l:tabList, "NameCmp") elseif t:miniBufExplSortBy == "mru" call sort(l:tabList, "MRUCmp") endif let l:miniBufExplBufList = '' for l:tab in l:tabList let l:miniBufExplBufList = l:miniBufExplBufList.l:tab " If horizontal and tab wrap is turned on we need to add spaces if g:miniBufExplVSplit == 0 if g:miniBufExplTabWrap != 0 let l:miniBufExplBufList = l:miniBufExplBufList.' ' endif " If not horizontal we need a newline else let l:miniBufExplBufList = l:miniBufExplBufList . "\n" endif endfor if (s:miniBufExplBufList != l:miniBufExplBufList) let s:maxTabWidth = l:maxTabWidth let s:miniBufExplBufList = l:miniBufExplBufList call DEBUG('Leaving BuildBufferList()',10) return 1 else call DEBUG('Leaving BuildBufferList()',10) return 0 endif endfunction " }}} " CreateBufferUniqName {{{ " " Construct a unique buffer name using the parts from the signature index of " the path. " function! CreateBufferUniqName(bufNum) call DEBUG('Entering CreateBufferUniqName('.a:bufNum.')',10) let l:bufNum = 0 + a:bufNum let l:bufName = expand( "#" . l:bufNum . ":p:t") " Remove []'s & ()'s, these chars are preserved for buffer name locating let l:bufName = substitute(l:bufName, '[][()]', '', 'g') " Create a unique name for unamed buffer if empty(l:bufName) call DEBUG('Leaving CreateBufferUniqName()',10) return '--NO NAME--'.localtime() endif let l:bufPathPrefix = "" if(!has_key(s:bufPathSignDict, l:bufNum)) call DEBUG(l:bufNum . ' is not in s:bufPathSignDict, aborting...',5) call DEBUG('Leaving CreateBufferUniqName()',10) return l:bufName endif let l:signs = s:bufPathSignDict[l:bufNum] if(empty(l:signs)) call DEBUG('Signs for ' . l:bufNum . ' is empty, aborting...',5) call DEBUG('Leaving CreateBufferUniqName()',10) return l:bufName endif for l:sign in l:signs call DEBUG('l:sign is ' . l:sign,5) if empty(get(s:bufPathDict[l:bufNum],l:sign)) continue endif let l:bufPathSignPart = get(s:bufPathDict[l:bufNum],l:sign).'/' " If the index is not right after the previous one and it is also not the " last one, then put a '-' before it if exists('l:last') && l:last + 1 != l:sign let l:bufPathSignPart = '-/'.l:bufPathSignPart endif let l:bufPathPrefix = l:bufPathPrefix.l:bufPathSignPart let l:last = l:sign endfor " If the last signature index is not the last index of the path, then put " a '-' after it if l:sign < len(s:bufPathDict[l:bufNum]) - 1 let l:bufPathPrefix = l:bufPathPrefix.'-/' endif call DEBUG('Uniq name for ' . l:bufNum . ' is ' . l:bufPathPrefix.l:bufName,5) call DEBUG('Leaving CreateBufferUniqName()',10) return l:bufPathPrefix.l:bufName endfunction " }}} " UpdateBufferNameDict {{{ " function! UpdateBufferNameDict(bufNum,deleted) call DEBUG('Entering UpdateBufferNameDict('.a:bufNum.','.a:deleted.')',10) let l:bufNum = 0 + a:bufNum let l:bufName = expand( "#" . l:bufNum . ":p:t") " Identify buffers with no name if empty(l:bufName) let l:bufName = '--NO NAME--' endif " Remove a deleted buffer from the buffer name dictionary if a:deleted if has_key(s:bufNameDict, l:bufName) call DEBUG('Found entry for deleted buffer '.l:bufNum,5) let l:bufnrs = s:bufNameDict[l:bufName] call filter(l:bufnrs, 'v:val != '.l:bufNum) let s:bufNameDict[l:bufName] = l:bufnrs call DEBUG('Delete entry for deleted buffer '.l:bufNum,5) endif call DEBUG('Leaving UpdateBufferNameDict()',10) return endif if(!has_key(s:bufNameDict, l:bufName)) call DEBUG('Adding empty list for ' . l:bufName,5) let s:bufNameDict[l:bufName] = [] endif if index(s:bufNameDict[l:bufName],l:bufNum) == -1 call add(s:bufNameDict[l:bufName], l:bufNum) endif call DEBUG('Leaving UpdateBufferNameDict()',10) endfunction " }}} " UpdateBufferPathDict {{{ " function! UpdateBufferPathDict(bufNum,deleted) call DEBUG('Entering UpdateBufferPathDict('.a:bufNum.','.a:deleted.')',10) let l:bufNum = 0 + a:bufNum let l:bufPath = expand( "#" . l:bufNum . ":p:h") let l:bufName = expand( "#" . l:bufNum . ":p:t") " Identify buffers with no name if empty(l:bufName) let l:bufName = '--NO NAME--' endif " Remove a deleted buffer from the buffer path dictionary if a:deleted if has_key(s:bufNameDict, l:bufName) call DEBUG('Found entry for deleted buffer '.l:bufNum,5) let l:bufnrs = s:bufNameDict[l:bufName] call filter(s:bufPathDict, 'v:key != '.l:bufNum) call DEBUG('Delete entry for deleted buffer '.l:bufNum,5) endif call DEBUG('Leaving UpdateBufferPathDict()',10) return endif let s:bufPathDict[l:bufNum] = split(l:bufPath,s:PathSeparator,0) call DEBUG('Leaving UpdateBufferPathDict()',10) endfunction " }}} " BuildBufferPathSignDict {{{ " " Compare the parts from the same index of all the buffer's paths, if there " are differences, it means this index is a signature index for all the " buffer's paths, mark it. At this point, the buffers are splited into several " subsets. Then, doing the same check for each subset on the next index. We " should finally get a set of signature locations which will uniquely identify " the path. We could then construct a string with these locaitons using as " less characters as possible. " function! BuildBufferPathSignDict(bufnrs, ...) if a:0 == 0 let index = 0 else let index = a:1 endif call DEBUG('Entering BuildBufferPathSignDict('.string(a:bufnrs).','.index.')',10) let bufnrs = a:bufnrs " Temporary dictionary to see if there is any different part let partDict = {} " Marker to see if there are more avaliable parts let moreParts = 0 " Group the buffers by this part of the buffer's path for bufnr in bufnrs " Make sure each buffer has an entry in 's:bufPathSignDict' " If index is zero, we force re-initialize the entry if index == 0 || !has_key(s:bufPathSignDict, bufnr) let s:bufPathSignDict[bufnr] = [] endif " If some buffers' path does not have this index, we skip it if len(s:bufPathDict[bufnr]) < index continue endif " Mark that there are still available paths let moreParts = 1 " Get requested part of the path let part = get(s:bufPathDict[bufnr],index) if empty(part) let part = '--EMPTY--' endif " Group the buffers using dictionary by this part if(!has_key(partDict, part)) let partDict[part] = [] endif call add(partDict[part],bufnr) endfor " All the paths have been walked to the end if !moreParts call DEBUG('Leaving BuildBufferPathSignDict() '.index,10) return endif " We only need the buffer subsets from now on let subsets = values(partDict) " If the buffers have been splited into more than one subset, or all the " remaining buffers are still in the same subset but some buffers' path " have hit the end, then mark this index as signature index. if len(partDict) > 1 || ( len(partDict) == 1 && len(subsets[0]) < len(bufnrs) ) " Store the signature index in the 's:bufPathSignDict' variable for bufnr in bufnrs call add(s:bufPathSignDict[bufnr],index) endfor " For all buffer subsets, increase the index by one, run again. for subset in subsets " If we only have one buffer left in the subset, it means there are " already enough signature index sufficient to identify the buffer if len(subset) <= 1 continue endif call BuildBufferPathSignDict(subset, index + 1) endfor " If all the buffers are in the same subset, then this index is not a " signature index, increase the index by one, run again. else call BuildBufferPathSignDict(bufnrs, index + 1) endif call DEBUG('Leaving BuildBufferPathSignDict() '.index,10) endfunction " }}} " UpdateBufferPathSignDict {{{ " function! UpdateBufferPathSignDict(bufNum,deleted) call DEBUG('Entering UpdateBufferPathSignDict('.a:bufNum.','.a:deleted.')',10) let l:bufNum = 0 + a:bufNum " Remove a deleted buffer from the buffer path signature dictionary if a:deleted if has_key(s:bufPathSignDict, l:bufNum) call DEBUG('Found entry for deleted buffer '.l:bufNum,5) call filter(s:bufPathSignDict, 'v:key != '.l:bufNum) call DEBUG('Delete entry for deleted buffer '.l:bufNum,5) endif call DEBUG('Leaving UpdateBufferPathSignDict()',10) return endif call DEBUG('Leaving UpdateBufferPathSignDict()',10) endfunction " }}} " BuildBufferFinalDict {{{ " function! BuildBufferFinalDict(arg,deleted) call DEBUG('Entering BuildBufferFinalDict('.string(a:arg).','.a:deleted.')',10) if type(a:arg) == 3 let l:bufnrs = a:arg else let l:bufNum = 0 + a:arg let l:bufName = expand( "#" . l:bufNum . ":p:t") " Identify buffers with no name if empty(l:bufName) let l:bufName = '--NO NAME--' endif if(!has_key(s:bufNameDict, l:bufName)) call DEBUG(l:bufName . ' is not in s:bufNameDict, aborting...',5) call DEBUG('Leaving BuildBufferFinalDict()',10) return endif let l:bufnrs = s:bufNameDict[l:bufName] " Remove a deleted buffer from the buffer unique name dictionary if a:deleted call UpdateBufferPathSignDict(l:bufNum, a:deleted) call UpdateBufferUniqNameDict(l:bufNum, a:deleted) endif endif call BuildBufferPathSignDict(l:bufnrs) call BuildBufferUniqNameDict(l:bufnrs) call DEBUG('Leaving BuildBufferFinalDict()',10) endfunction " }}} " BuildBufferUniqNameDict {{{ " function! BuildBufferUniqNameDict(bufnrs) call DEBUG('Entering BuildBufferUniqNameDict('.string(a:bufnrs).')',10) let l:bufnrs = a:bufnrs for bufnr in l:bufnrs call UpdateBufferUniqNameDict(bufnr,0) endfor call DEBUG('Leaving BuildBufferUniqNameDict()',10) endfunction " }}} " UpdateBufferUniqNameDict {{{ " function! UpdateBufferUniqNameDict(bufNum,deleted) call DEBUG('Entering UpdateBufferUniqNameDict('.a:bufNum.','.a:deleted.')',10) let l:bufNum = 0 + a:bufNum " Remove a deleted buffer from the buffer path dictionary if a:deleted if has_key(s:bufUniqNameDict, l:bufNum) call DEBUG('Found entry for deleted buffer '.l:bufNum,5) call filter(s:bufUniqNameDict, 'v:key != '.l:bufNum) call DEBUG('Delete entry for deleted buffer '.l:bufNum,5) endif call DEBUG('Leaving UpdateBufferUniqNameDict()',10) return endif call DEBUG('Creating buffer name for ' . l:bufNum,5) let l:bufUniqName = CreateBufferUniqName(l:bufNum) call DEBUG('Setting ' . l:bufNum . ' to ' . l:bufUniqName,5) let s:bufUniqNameDict[l:bufNum] = l:bufUniqName call DEBUG('Leaving UpdateBufferUniqNameDict()',10) endfunction " }}} " BuildAllBufferDicts {{{ " function! BuildAllBufferDicts() call DEBUG('Entering BuildAllBuffersDicts()',10) " Get the number of the last buffer. let l:NBuffers = bufnr('$') " Loop through every buffer less than the total number of buffers. let l:i = 0 while(l:i <= l:NBuffers) if IsBufferIgnored(l:i) let l:i = l:i + 1 continue endif call UpdateBufferNameDict(l:i,0) call UpdateBufferPathDict(l:i,0) call UpdateBufferStateDict(l:i,0) let l:i = l:i + 1 endwhile for bufnrs in values(s:bufNameDict) call BuildBufferFinalDict(bufnrs,0) endfor call DEBUG('Leaving BuildAllBuffersDicts()',10) endfunction " }}} " UpdateAllBufferDicts {{{ " function! UpdateAllBufferDicts(bufNum,deleted) call DEBUG('Entering UpdateAllBuffersDicts('.a:bufNum.','.a:deleted.')',10) call UpdateBufferNameDict(a:bufNum,a:deleted) call UpdateBufferPathDict(a:bufNum,a:deleted) call UpdateBufferStateDict(a:bufNum,a:deleted) call BuildBufferFinalDict(a:bufNum,a:deleted) call DEBUG('Leaving UpdateAllBuffersDicts()',10) endfunction " }}} " UpdateBufferStateDict {{{ function! UpdateBufferStateDict(bufNum,deleted) call DEBUG('Entering UpdateBufferStateDict('.a:bufNum.','.a:deleted.')',10) let l:bufNum = 0 + a:bufNum if a:deleted && has_key(s:bufStateDict, l:bufNum) call filter(s:bufStateDict, 'v:key != '.l:bufNum) call DEBUG('Leaving UpdateBufferStateDict()',10) return endif if has_key(s:bufStateDict, l:bufNum) if s:bufStateDict[l:bufNum] != getbufvar(a:bufNum, '&modified') let s:bufStateDict[l:bufNum] = getbufvar(a:bufNum, '&modified') call AutoUpdate(bufnr(a:bufNum),0) endif else let s:bufStateDict[l:bufNum] = getbufvar(a:bufNum, '&modified') endif call DEBUG('Leaving UpdateBufferStateDict()',10) endfunction " }}} " NameCmp - compares tabs based on filename {{{ " function! NameCmp(tab1, tab2) let l:name1 = matchstr(a:tab1, ":.*") let l:name2 = matchstr(a:tab2, ":.*") if l:name1 < l:name2 return -1 elseif l:name1 > l:name2 return 1 else return 0 endif endfunction " }}} " MRUCmp - compares tabs based on MRU order {{{ " function! MRUCmp(tab1, tab2) let l:buf1 = str2nr(matchstr(a:tab1, '[0-9]\+')) let l:buf2 = str2nr(matchstr(a:tab2, '[0-9]\+')) return index(s:MRUList, l:buf1) - index(s:MRUList, l:buf2) endfunction " }}} " HasEligibleBuffers - Are there enough MBE eligible buffers to open the MBE window? {{{ " " Returns 1 if there are any buffers that can be displayed in a " mini buffer explorer. Otherwise returns 0. " function! HasEligibleBuffers() call DEBUG('Entering HasEligibleBuffers()',10) let l:found = len(s:BufList) let l:needed = g:miniBufExplBuffersNeeded call DEBUG('Eligible buffers are '.string(s:BufList),6) call DEBUG('Found '.l:found.' eligible buffers of '.l:needed.' needed',6) call DEBUG('Leaving HasEligibleBuffers()',10) return (l:found >= l:needed) endfunction " }}} " Auto Update - Function called by auto commands for auto updating the MBE {{{ " " IF auto update is turned on AND " we are in a real buffer AND " we have enough eligible buffers THEN " Update our explorer and get back to the current window " " If we get a buffer number for a buffer that " is being deleted, we need to make sure and " remove the buffer from the list of eligible " buffers in case we are down to one eligible " buffer, in which case we will want to close " the MBE window. " function! AutoUpdate(curBufNum,force) call DEBUG('Entering AutoUpdate('.a:curBufNum.')',10) call DEBUG('Current state: '.winnr().' : '.bufnr('%').' : '.bufname('%'),10) if (s:miniBufExplInAutoUpdate == 1) call DEBUG('AutoUpdate recursion stopped',9) call DEBUG('Leaving AutoUpdate()',10) return else let s:miniBufExplInAutoUpdate = 1 endif " Skip windows holding ignored buffer if !a:force && IsBufferIgnored(a:curBufNum) == 1 call DEBUG('Leaving AutoUpdate()',10) let s:miniBufExplInAutoUpdate = 0 return endif if s:MRUEnable == 1 call ListPush(s:MRUList,a:curBufNum) endif " Only allow updates when the AutoUpdate flag is set " this allows us to stop updates on startup. if exists('t:miniBufExplAutoUpdate') && t:miniBufExplAutoUpdate == 1 " if we don't have a window then create one let l:winnr = FindWindow('-MiniBufExplorer-', 1) if (exists('t:skipEligibleBuffersCheck') && t:skipEligibleBuffersCheck == 1) || HasEligibleBuffers() == 1 if (l:winnr == -1) if g:miniBufExplAutoStart == 1 call DEBUG('MiniBufExplorer was not running, starting...', 9) call StartExplorer(a:curBufNum) else call DEBUG('MiniBufExplorer was not running, aborting...', 9) call DEBUG('Leaving AutoUpdate()',10) let s:miniBufExplInAutoUpdate = 0 return endif else call DEBUG('Updating MiniBufExplorer...', 9) call UpdateExplorer(a:curBufNum) endif else if (l:winnr == -1) call DEBUG('MiniBufExplorer was not running, aborting...', 9) call DEBUG('Leaving AutoUpdate()',10) let s:miniBufExplInAutoUpdate = 0 return else call DEBUG('Failed in eligible check', 9) call StopExplorer(0) " we do not want to turn auto-updating off let t:miniBufExplAutoUpdate = 1 endif endif else call DEBUG('AutoUpdates are turned off, terminating',9) endif call DEBUG('Leaving AutoUpdate()',10) let s:miniBufExplInAutoUpdate = 0 endfunction " }}} " QuitIfLastOpen {{{ " function! QuitIfLastOpen() abort " Quit MBE if no more mormal window left if (bufname('%') == '-MiniBufExplorer-') && (NextNormalWindow() == -1) call DEBUG('MBE is the last open window, quit it', 9) if tabpagenr('$') == 1 " Before quitting Vim, delete the MBE buffer so that " the '0 mark is correctly set to the previous buffer. " Also disable autocmd on this command to avoid unnecessary " autocmd nesting. if winnr('$') == 1 noautocmd bdelete endif quit else close endif endif endfunction " }}} " GetActiveBuffer {{{ " function! GetActiveBuffer() call DEBUG('Entering GetActiveBuffer()',10) let l:bufNum = substitute(s:miniBufExplBufList,'\[\([0-9]*\):[^\]]*\][^\!]*!', '\1', '') + 0 call DEBUG('Currently active buffer is '.l:bufNum,10) call DEBUG('Leaving GetActiveBuffer()',10) return l:bufNum endfunction " }}} " GetSelectedBuffer - From the MBE window, return the bufnum for buf under cursor {{{ " " If we are in our explorer window then return the buffer number " for the buffer under the cursor. " function! GetSelectedBuffer() call DEBUG('Entering GetSelectedBuffer()',10) " Make sure we are in our window if bufname('%') != '-MiniBufExplorer-' call DEBUG('GetSelectedBuffer called in invalid window',1) call DEBUG('Leaving GetSelectedBuffer()',10) return -1 endif let l:save_rep = &report let l:save_sc = &showcmd let &report = 10000 set noshowcmd let l:save_reg = @" let @" = "" normal ""yi[ if @" != "" if !g:miniBufExplShowBufNumbers " This is a bit ugly, but it works, unless we come up with a " better way to get the key for a dictionary by its value. let l:bufUniqNameDictKeys = keys(s:bufUniqNameDict) let l:bufUniqNameDictValues = values(s:bufUniqNameDict) let l:retv = l:bufUniqNameDictKeys[match(l:bufUniqNameDictValues,substitute(@",'[0-9]*:\(.*\)', '\1', ''))] else let l:retv = substitute(@",'\([0-9]*\):.*', '\1', '') + 0 endif let @" = l:save_reg call DEBUG('Leaving GetSelectedBuffer()',10) return l:retv else let @" = l:save_reg call DEBUG('Leaving GetSelectedBuffer()',10) return -1 endif let &report = l:save_rep let &showcmd = l:save_sc endfunction " }}} " MBESelectBuffer - From the MBE window, open buffer under the cursor {{{ " " If we are in our explorer, then we attempt to open the buffer under the " cursor in the previous window. " " Split indicates whether to open with split, 0 no split, 1 split horizontally " function! MBESelectBuffer(split) call DEBUG('Entering MBESelectBuffer()',10) " Make sure we are in our window if bufname('%') != '-MiniBufExplorer-' call DEBUG('MBESelectBuffer called in invalid window',1) call DEBUG('Leaving MBESelectBuffer()',10) return endif let l:bufnr = GetSelectedBuffer() if(l:bufnr != -1) " If the buffer exists. let l:saveAutoUpdate = t:miniBufExplAutoUpdate let t:miniBufExplAutoUpdate = 0 call s:SwitchWindow('p',1) if IsBufferIgnored(bufnr('%')) let l:winNum = NextNormalWindow() if l:winNum != -1 call s:SwitchWindow('w',1,l:winNum) else call DEBUG('No elegible window avaliable',1) call DEBUG('Leaving MBESelectBuffer()',10) return endif endif if a:split == 0 exec 'b! '.l:bufnr elseif a:split == 1 exec 'sb! '.l:bufnr elseif a:split == 2 exec 'vertical sb! '.l:bufnr endif let t:miniBufExplAutoUpdate = l:saveAutoUpdate call AutoUpdate(bufnr("%"),0) endif if g:miniBufExplCloseOnSelect == 1 call StopExplorer(0) endif call DEBUG('Leaving MBESelectBuffer()',10) endfunction " }}} " MBEDeleteBuffer - From the MBE window, delete selected buffer from list {{{ " " After making sure that we are in our explorer, This will delete the buffer " under the cursor. If the buffer under the cursor is being displayed in a " window, this routine will attempt to get different buffers into the " windows that will be affected so that windows don't get removed. " function! MBEDeleteBuffer() call DEBUG('Entering MBEDeleteBuffer()',10) " Make sure we are in our window if bufname('%') != '-MiniBufExplorer-' call DEBUG('MBEDeleteBuffer called in invalid window',1) call DEBUG('Leaving MBEDeleteBuffer()',10) return endif let l:selBuf = GetSelectedBuffer() if l:selBuf != -1 call DeleteBuffer(0,0,l:selBuf) endif call DEBUG('Leaving MBEDeleteBuffer()',10) endfunction " }}} " NextNormalWindow {{{ " function! NextNormalWindow() call DEBUG('Entering NextNormalWindow()',10) let l:winSum = winnr('$') call DEBUG('Total number of open windows are '.l:winSum,9) let l:i = 1 while(l:i <= l:winSum) call DEBUG('window: '.l:i.', buffer: ('.winbufnr(l:i).':'.bufname(winbufnr(l:i)).')',9) if !IsBufferIgnored(winbufnr(l:i)) && l:i != winnr() call DEBUG('Found window '.l:i,8) call DEBUG('Leaving NextNormalWindow()',10) return l:i endif let l:i = l:i + 1 endwhile call DEBUG('Found no window',8) call DEBUG('Leaving NextNormalWindow()',10) return -1 endfunction " }}} " ListAdd {{{ " function! ListAdd(list,val) call DEBUG('Entering ListAdd('.string(a:list).','.a:val.')',10) call add(a:list, a:val) call DEBUG('Leaving ListAdd()',10) endfunction " }}} " ListPop {{{ " function! ListPop(list,val) call DEBUG('Entering ListPop('.string(a:list).','.a:val.')',10) call filter(a:list, 'v:val != '.a:val) call DEBUG('Leaving ListPop()',10) endfunction " }}} " ListPush {{{ " function! ListPush(list,val) call DEBUG('Entering ListPush('.string(a:list).','.a:val.')',10) " Remove the buffer number from the list if it already exists. call ListPop(a:list,a:val) " Add the buffer number to the head of the list. call insert(a:list,a:val) call DEBUG('Leaving ListPush()',10) endfunction " }}} " DEBUG - Display debug output when debugging is turned on {{{ " " Thanks to Charles E. Campbell, Jr. PhD " for Decho.vim which was the inspiration for this enhanced debugging " capability. " let g:miniBufExplFuncCallDepth = 0 function! DEBUG(msg, level) if g:miniBufExplDebugLevel >= a:level if a:level == 10 && a:msg =~ '^Entering' let g:miniBufExplFuncCallDepth += 1 endif if a:msg =~ '^Entering' let l:msg = repeat('│ ',g:miniBufExplFuncCallDepth - 1).'┌ '.a:msg elseif a:msg =~ '^Leaving' let l:msg = repeat('│ ',g:miniBufExplFuncCallDepth - 1).'└ '.a:msg else let l:msg = repeat('│ ',g:miniBufExplFuncCallDepth).a:msg endif " Prevent a report of our actions from showing up. let l:save_rep = &report let l:save_sc = &showcmd let &report = 10000 set noshowcmd " Debug output to a buffer if g:miniBufExplDebugMode == 0 if bufname('%') == 'MiniBufExplorer.DBG' return endif " Get into the debug window or create it if needed let l:winNum = FindCreateWindow('MiniBufExplorer.DBG', 0, 1, 1, 1, 0) if l:winNum == -1 let g:miniBufExplDebugMode == 3 call DEBUG('Failed to get the MBE debugging window, reset debugging mode to 3.',1) call DEBUG('Forwarding message...',1) call DEBUG(a:msg,1) call DEBUG('Forwarding message end.',1) return endif " Save the current window number so we can come back here let l:currWin = winnr() call s:SwitchWindow('p',1) " Change to debug window call s:SwitchWindow('w',1,l:winNum) " Make sure we really got to our window, if not we " will display a confirm dialog and turn debugging " off so that we won't break things even more. if bufname('%') != 'MiniBufExplorer.DBG' call confirm('Error in window debugging code. Dissabling MiniBufExplorer debugging.', 'OK') let g:miniBufExplDebugLevel = 0 return endif set modified " Write Message to DBG buffer let res=append("$",s:debugIndex.':'.a:level.':'.a:msg) set nomodified norm G " Return to original window call s:SwitchWindow('p',1) call s:SwitchWindow('w',1,l:currWin) " Debug output using VIM's echo facility elseif g:miniBufExplDebugMode == 1 echo s:debugIndex.':'.a:level.':'.a:msg " Debug output to a file -- VERY SLOW!!! " should be OK on UNIX and Win32 (not the 95/98 variants) elseif g:miniBufExplDebugMode == 2 if has('system') || has('fork') if has('win32') && !has('win95') let l:result = system("cmd /c 'echo ".s:debugIndex.':'.a:level.':'.a:msg." >> MiniBufExplorer.DBG'") endif if has('unix') let l:result = system("echo '".s:debugIndex.':'.a:level.':'.a:msg." >> MiniBufExplorer.DBG'") endif else call confirm('Error in file writing version of the debugging code, vim not compiled with system or fork. Dissabling MiniBufExplorer debugging.', 'OK') let g:miniBufExplDebugLevel = 0 endif elseif g:miniBufExplDebugMode == 3 let g:miniBufExplDebugOutput = g:miniBufExplDebugOutput."\n".s:debugIndex."\t".':'.a:level."\t".':'.l:msg endif let s:debugIndex = s:debugIndex + 1 if a:level == 10 && a:msg =~ '^Leaving' let g:miniBufExplFuncCallDepth -= 1 endif let &report = l:save_rep let &showcmd = l:save_sc endif endfunc " }}} " SwitchWindow {{{ " function! s:SwitchWindow(action, ...) call DEBUG('Entering SwitchWindow('.a:action.','.string(a:000).')',10) if a:action !~ '[hjkltbwWpP]' call DEBUG('invalid window action : '.a:action,10) call DEBUG('Leaving SwitchWindow()',10) return endif if exists('a:1') && a:1 == 1 let l:aucmd = 'noautocmd ' else let l:aucmd = '' endif if exists('a:2') let l:winnr = a:2 else let l:winnr = '' endif call DEBUG('previous window is: '.winnr(),10) let l:wincmd = l:aucmd.l:winnr.'wincmd '.a:action call DEBUG('window switching command is: '.l:wincmd,10) exec l:wincmd call DEBUG('current window is: '.winnr(),10) call DEBUG('Leaving SwitchWindow()',10) endfunction " }}} " vim:ft=vim:fdm=marker:ff=unix:nowrap:tabstop=2:shiftwidth=2:softtabstop=2:smarttab:shiftround:expandtab