diff options
Diffstat (limited to 'home/.vim/plugin/winmanager.vim')
-rw-r--r-- | home/.vim/plugin/winmanager.vim | 1316 |
1 files changed, 1316 insertions, 0 deletions
diff --git a/home/.vim/plugin/winmanager.vim b/home/.vim/plugin/winmanager.vim new file mode 100644 index 0000000..2b04044 --- /dev/null +++ b/home/.vim/plugin/winmanager.vim @@ -0,0 +1,1316 @@ +"============================================================================= +" File: winmanager.vim +" Author: Srinath Avadhanula (srinath@eecs.berkeley.edu) +" Last Change: Wed Apr 03 05:00 PM 2002 PST +" Help: winmanager.vim is a plugin which implements a classical windows +" type IDE in Vim-6.0. When you open up a new file, simply type +" in :WMToggle. This will start up the file explorer. +" +" +" NOTE: Starting from winmanager-2.x you can add new plugins to winmanager +" and also customize the window layout in your .vimrc +" +" See ":help winmanager" for additional details. +" ============================================================================ + +" quit if the user doesnt want us or if we are already loaded. +if exists("loaded_winmanager") + finish +end +let loaded_winmanager = 1 + +" width of the explorer windows +if !exists("g:winManagerWidth") + let g:winManagerWidth = 25 +end + +" whether to close winmanager if only explorer windows are visible. +if !exists("g:persistentBehaviour") + let g:persistentBehaviour = 1 +end + +" default window layout +if !exists("g:winManagerWindowLayout") + let g:winManagerWindowLayout = "FileExplorer,TagsExplorer|BufExplorer" +end + +" use default explorer plugin which ships with vim. +if !exists("g:defaultExplorer") + let g:defaultExplorer = 1 +end + +" commands +" toggling between the windows manager open or closed. this can also be used +" to start win manager. +if !exists(':WMToggle') + command -nargs=0 WMToggle :silent call <SID>ToggleWindowsManager() +end + +" WManager and WMclose still exist for backward compatibility, but their use +" is deprecated because WMToggle has the functionality of both of them. +if !exists(':WManager') + command -nargs=0 WManager :silent call <SID>StartWindowsManager() +end +if !exists(':WMClose') + command -nargs=0 WMClose :silent call <SID>CloseWindowsManager() +end +" command to go to either the first explorer window visible +if !exists(':FirstExplorerWindow') + command -nargs=0 FirstExplorerWindow :silent call <SID>GotoExplorerWindow('1') +end +" command to go to either the last explorer window visible +if !exists(':BottomExplorerWindow') + command -nargs=0 BottomExplorerWindow :silent call <SID>GotoExplorerWindow('$') +end + +" this command is used internally by winmanager. shouldnt be of concern to the +" user. +if !exists(':WinManagerGotoNextInGroup') + command -nargs=1 WinManagerGotoNextInGroup :silent call <SID>GotoNextExplorerInGroup(<args>) +end +if !exists(':WinManagerGotoPrevInGroup') + command -nargs=1 WinManagerGotoPrevInGroup :silent call <SID>GotoNextExplorerInGroup(<args>,-1) +end + +" nifty command for debugging. SVarValueWinManager 'MRUList' will echo the +" value of 's:MRUList' for instance. to be used for debugging winmanager. +" shouldn't be of interest to the user. +if !exists(':SVarValueWinManager') + command -nargs=* SVarValueWinManager :call <SID>ShowVariableValue(<args>) +end + +" characters that must be escaped for filenames +if has("dos16") || has("dos32") || has("win16") || has("win32") || has("os2") + let s:escfilename = ' %#' +else + let s:escfilename = ' \%#' +endif + +" a quick way to "uncomment" all the debug print statements. +let g:debugWinManager = 1 +let g:numRefs = 0 + +" initialization. +let s:numExplorerGroups = 0 +let s:numExplorers = 0 + +" Line continuation used here +let s:cpo_save = &cpo +set cpo&vim + +"--- +" this function creates a variable +" s:explorerGroup_i +" for the i^th time it is called. This variable will be of the form +" s:explorerGroup_i = ",member1,member2,member3," +" +" this provides a way to "group" various explorers into common groups, so that +" one of them will be visible at a time. +" +function! <SID>RegisterExplorerGroup() + " g:winManagerWindowLayout is of the form + " 'FileExplorer,TagsExplorer|BufExplorer' + + " begin extracting groups from the layout variable. + let groupNum = 1 + while 1 + " if no more groups then break. + let curGroup = s:Strntok(g:winManagerWindowLayout, '|', groupNum) + if curGroup == '' + break + end + + " otherwise extract the explorers belonging to this group and the + " explorer ID's etc. also protect against the same explorer being put + " in 2 groups. + let grplist = ',' + let numlist = ',' + let curgn = s:numExplorerGroups + 1 + + let i = 1 + while 1 + let name = s:Strntok(curGroup, ',', i) + if name == '' + break + end + " refuse to register an explorer twice, or if the explorer's title + " doesnt exist. + if exists('s:'.name.'_numberID') || !exists('g:'.name.'_title') + if !exists('g:'.name.'_title') + if has('gui_running') + call confirm(name." is registered as a plugin, but I cannot seem to find it anywhere.\n" + \.'Make sure you have downloaded the relevant plugin or change the g:winManagerWindowLayout variable', + \"&ok", 1, 'Warning') + else + echohl Error + echomsg name." is registered as a plugin, but I cannot seem to find it anywhere." + \.'Please make sure you have downloaded the relevant plugin' + echohl None + endif + endif + let i = i + 1 + continue + end + + let s:numExplorers = s:numExplorers + 1 + let num = s:numExplorers + + exe 'let s:explorerName_'.num.' = name' + + let grplist = grplist.name."," + let numlist = numlist.''.num.',' + + " create variables of the form ExplorerName_<group/member/number>ID + " which contains which group the explorer belongs to and its member + " number within that group and also its number + " this will create a variable of the form + exe 'let s:'.name.'_groupID = "'.curgn.'"' + exe 'let s:'.name.'_memberID = "'.i.'"' + exe 'let s:'.name.'_numberID = "'.num.'"' + + let i = i + 1 + endwhile + if grplist == ',' + call PrintError('no explorers registered in this run') + return + end + + let s:numExplorerGroups = s:numExplorerGroups + 1 + + exe 'let s:explorerGroup_'.curgn.' = grplist' + exe 'let s:explorerGroupNums_'.curgn.' = numlist' + exe 'let s:numMembers_'.curgn.' = a:0' + + let groupNum = groupNum + 1 + endwhile + +endfunction + + +"--- +" initializes the window manager. sets the initial layout. as of now, the +" layout of the explorer windows (i.e, which plugin appears above or below the +" other) depends on the order in which the plugins are sourced. +" TODO: make this easily user customizable later. +" Done! See comments about registration. +" +" this function opens each "registered" plugin in its appropriate position. it +" also starts off the autocommand which makes dynamic updating of buffers +" possible. +" +function! <SID>StartWindowsManager() + " for the first few versions of winmanager, if no registration is done, + " assume the following default configuration of the windows: + " (FileExplorer, TagsExplorer) + " (BufExplorer) + " This allows for an "easy" distribution. i.e, the installation will not + " break if the user is careless with his .vimrc + let oldRep=&report + let save_sc = &sc + set report=10000 nosc + if s:numExplorers == 0 + call s:RegisterExplorerGroup() + end + let nothingShown = 1 + let s:commandRunning = 1 + + if !exists("s:MRUList") + call s:InitializeMRUList() + end + let currentWindowNumber = winnr() + + if !exists("s:gotExplorerTitles") + let s:gotExplorerTitles = 1 + let i = 1 + while i <= s:numExplorers + exe 'let name = s:explorerName_'.i + exe 'let s:explorerTitle_'.i.' = g:'.name.'_title' + let i = i + 1 + endwhile + endif + + " focus on the first visible explorer window. + let gotvisible = 0 + let i = 1 + while i <= s:numExplorerGroups + " check if the ith explorer is visible. + let windownum = s:IsExplorerGroupVisible(i) + if windownum != -1 + call s:GotoWindow(windownum) + let gotvisible = 1 + " cen is the "current explorer number". used while restoring the + " layout. + let cen = i + let nothingShown = 0 + break + end + let i = i + 1 + endwhile + + " split the current window or vsplit a new window for the explorers if + " none of the explorers is visible. + if !gotvisible + if exists('s:lastMemberDisplayed_1') + let lastmem = s:lastMemberDisplayed_1 + else + let lastmem = 1 + end + let somethingDisplayed = s:EditNextVisibleExplorer(1, lastmem-1, 1, 'vsplit') + " if nothing was displayed this time, there is a possiblity it could + " happen later during one of the refresh cycles. remember this for + " then. + call PrintError('something displayed on '.lastmem.' of 1 :'.somethingDisplayed) + if !somethingDisplayed + let s:tryGroupAgain_1 = 1 + q + else + let s:tryGroupAgain_1 = 0 + let nothingShown = 0 + let currentWindowNumber = currentWindowNumber + 1 + end + let cen = 1 + " for now assume that the explorer windows always stay on the left. + " TODO: make this optional later + wincmd H + " set up the correct width + exe g:winManagerWidth.'wincmd |' + end + + " now we are on one of the explorers. time to redo the original layout. + let _split = &splitbelow + let i = 1 + while i <= s:numExplorerGroups + " for each group, see if any member of it is visible. + let windownum = s:IsExplorerGroupVisible(i) + + " if this explorer group is not visible, then open the first plugin + " belonging to this group + if windownum == -1 + + " if this explorer group is "before" the cen, then split above, else + " below. except for the first time when this could possibly be + " true, it always evaluates to the else. + if i < cen + set nosplitbelow + else + set splitbelow + end + " find the last plugin belonging to this "group" which was + " displayed. + if exists('s:lastMemberDisplayed_'.i) + exe 'let lastmem = s:lastMemberDisplayed_'.i + else + let lastmem = 1 + end + " try to display either that plugin or the one after it. + let somethingDisplayed = s:EditNextVisibleExplorer(i, lastmem-1,1,"split") + if !somethingDisplayed + exe 'let s:tryGroupAgain_'.i.' = 1' + q + else + exe 'let s:tryGroupAgain_'.i.' = 0' + " if this is the first explorer shown, need to push it to the + " right. + if nothingShown + wincmd H + " set up the correct width + exe g:winManagerWidth.'wincmd |' + end + let nothingShown = 0 + let currentWindowNumber = currentWindowNumber + 1 + end + let cen = i + + " the group is visible, go to it so we can split the one after that + " from it. + else + call s:GotoWindow(windownum) + endif + + let i = i + 1 + " cen: current explorer (group) number which was visited. + let cen = i + endwhile + call PrintError('done with start while loop') + + " now make the run for resizing. + let i = 1 + while i <= s:numExplorerGroups + " find if its visible and the explorer of this group which is + " currently displayed. + let windownum = s:IsExplorerGroupVisible(i) + " need to check because some of the explorer groups might not have + " been displayed, if all their members were unable to display + " anything. + if windownum == -1 + let i = i + 1 + continue + end + let numexp = s:WhichMemberVisible(i) + + " visible, goto that window. + call s:GotoWindow(windownum) + exe 'let name = s:explorerName_'.numexp + " if this is not occupying the entire height of the window, then call + " its ReSize() function (if it exists). + if exists('*'.name.'_ReSize') && !s:IsOnlyVertical() + exe 'call '.name.'_ReSize()' + end + let i = i + 1 + endwhile + call PrintError('done with refresh while loop') + + let &splitbelow = _split + + augroup WinManagerRefresh + au! + au BufEnter * call <SID>RefreshWinManager() + au BufDelete * call <SID>RefreshWinManager("BufDelete") + augroup END + + call s:GotoWindow(currentWindowNumber) + " RepairAltRegister needs to be called here as well, because + " 1. when winmanager is re-started, we need to restore the @# register to + " what it was. + " 2. if winmanager is started for the first time, then we need to ensure + " that @# is at least not one of the explorer windows. + if buflisted(bufnr('%')) + call s:RepairAltRegister() + end + let s:commandRunning = 0 + let &report=oldRep + let &sc = save_sc + if nothingShown + echomsg "[ no valid explorers available. winmanager will start when next possible ]" + end +endfunction + +"--- +" if this window occupies the entire height of the screen, return 1, else +" return 0. i.e return 1 if there is no window above or below this window. +" +function! <SID>IsOnlyVertical() + let curwin = winnr() + wincmd k + if curwin != winnr() + wincmd j + return 0 + else + wincmd j + if winnr() != curwin + wincmd k + return 0 + end + end + return 1 +endfunction + +"--- +" this function first takes focus to the last listed file being edited and +" then depending on the users action and modified, etc opens the file bufName +" either on it or splits a new window etc. +" +function! WinManagerFileEdit(bufName, split) + " this function is usually _not_ triggered from an autocommand, so the + " movement commands in this function will trigger RefreshWinManager(). + " make that do nothing with this flag. + let s:commandRunning = 1 + let oldRep=&report + let save_sc = &sc + set report=10000 nosc + + " if the file is already visible somewhere just go there. + " a:bufName is a fully qualified filename of the form + " e:/path/to/file + " now bufnr('e:/path/to/file') != -1 even in the case where a file called + " e:/path/to/file/other/name is opened. (this is bufnr()'s behavior). + " therefore make an additional check so were protected against false + " matches. + if bufwinnr(bufnr(a:bufName)) != -1 && + \ a:bufName == expand('#'.bufnr(a:bufName).':p') + + call s:GotoWindow(bufwinnr(a:bufName)) + " however, we still have to repair the @# register + call s:RepairAltRegister() + + " otherwise goto the last listed buffer being edited. + else + + " if we had already opened this file, then use the #n notation instead + " of opening by file name. this preserves cursor position. + if bufnr(a:bufName) != -1 && + \ a:bufName == expand('#'.bufnr(a:bufName).':p') + let bufcall = '#'.bufnr(a:bufName) + else + let bufcall = a:bufName + end + + let lastBufferNumber = s:MRUGet(1) + " if the last accessed buffer is visible, then goto it. + if bufwinnr(lastBufferNumber) != -1 + " the fact that we go to the last listed buffer and then open this + " buffer automatically protects the @# register. + call s:GotoWindow(bufwinnr(bufnr(lastBufferNumber))) + " now split it or not depending on stuff. + if (&modified && !&hidden) || a:split + exe 'silent! split '.bufcall + else + exe 'silent! e '.bufcall + end + else + " the last accessed buffer is not visible. this most probably + " means that the explorer buffers are the only windows visible. + " this means that the layout has to be redone by v-splitting a new + " window for this file. + " first open the alternate file just to retain @# if its still + " listed. + if buflisted(lastBufferNumber) + exe 'silent! vsplit #'.lastBufferNumber + exe 'silent! e '.bufcall + " the last accessed buffer has dissapeared. just edit this file. + else + exe 'silent! vsplit '.bufcall + end + " now push this to the very right + wincmd L + " calculate the width of this window and reset it. + exe &columns-g:winManagerWidth.' wincmd |' + end + end + + let s:commandRunning = 0 + + " call Refresh incase this fileopen made some displays invalid. + call s:RefreshWinManager() + let &report=oldRep + let &sc = save_sc +endfunction + + +"--- +" function to repair the @# register. +" +" quickly edit the alternate buffer previously being edited in the +" FileExplorer area so that the % and # registers are not screwed with. +" This function must be called while focus is on a listed buffer which needs +" to be made @%. +" +function! <SID>RepairAltRegister() + " setting hidden while going back and forth is very wise because sometimes + " this function is used from within an autocommand. in such cases, + " switching back and forth between buffers makes the syntax highlighting + " dissapear. + let _hidden = &l:bufhidden + setlocal bufhidden=hide + let oldRep=&report + let save_sc = &sc + set report=10000 nosc + + let currentBufferNumber = bufnr('%') + let currentBufferName = expand('%:p') + let alternateBufferNumber = s:MRUGet(2) + + " if the required alternatebuffer exists, then first edit it to preserve @# + if alternateBufferNumber != bufnr("#") + \ && alternateBufferNumber != -1 + \ && buflisted(alternateBufferNumber) + exec 'silent! b! '.alternateBufferNumber + elseif alternateBufferNumber == -1 + " if the alternate buffer doesnt exist, do some randomness so that the @# + " register is at least not some explorer buffer number. ideally, at this + " stage, something would have been done to ensure that @# = -1, however, + " for now, edit a temporary file. + exe "e ".tempname() + setlocal nobuflisted + setlocal nomodifiable + setlocal bufhidden=delete + setlocal buftype=nofile + let tmpBufNum = bufnr('%') + exe 'silent! b! '.currentBufferNumber + exe 'silent! bwipeout '.tmpBufNum + let &l:bufhidden = _hidden + return + end + + " now edit the current file (to preserve @% :-) ) + " it seems that using ":b !" is _very_ important to preserve syntax + " highlighting. if ":e #" or ":b " is used, then syntax highlighting is + " lost and the ugly hack thing keeps getting called everytime. + " still dont know exactly why this is. it has something to do with + " abandoned buffers being kept and also nested autocommands, but its not + " very clear to me what it is. + exec('silent! b! '.currentBufferNumber) + + " a totally ugly hack to restore syntax highlighting... i have NO idea why + " this has to be here... somehow mixing opening files with autocommands + " has always been very very problematic. + " NOTE: the problem seems to have gone away now... see above comment. + if has("syntax") && exists("g:syntax_on") && !has("syntax_items") + call PrintError('needing to reset syntax!') + do syntax + else + call PrintError('fugly hack not needed!') + end + " end fugly hack. + + let &l:bufhidden = _hidden + let &report=oldRep + let &sc = save_sc +endfunction + +"--- +" the main function. this is responsible for updating plugins dynamically. +" this function is triggered on the BufEnter and BufDelete events. every time +" it is called, it makes a pass through all visible plugins and if their +" display is not valid, it calls their Start() function. +" +" if this function is called with no arguments, it is assumed to be triggered +" from a BufEnter even or due to a forcible refresh. If it is called with one +" argument called "BufDelete", then it is assumed that it is triggered from +" the BufDelete event. +" +function! <SID>RefreshWinManager(...) + " refreshes the window layout and the displayes of windows which trigger + " on autocommands. + + " make a note of whether this refresh was triggered by the BufDelete event + " or not. + let _split = &splitbelow + if a:0 > 0 && a:1 == "BufDelete" + let BufDelete = 1 + else + let BufDelete = 0 + end + " do the push pop thing irrespective of whether we do the rest of the + " stuff or not. + if BufDelete + call s:MRUPop() + else + call s:MRUPush() + end + " if this autocommand was triggered because of internal movements/commands + " due to other winmanager commands, then quit. + if exists("s:commandRunning") && s:commandRunning + return + end + " check if only explorer windows are visible and if so quit if we dont + " want persistent behavior. + if !g:persistentBehaviour && s:OnlyExplorerWindowsOpen() + qa + end + + " this magic statement is curing the syntax losing problem. WHY? + let s:commandRunning = 1 + let g:numRefs = g:numRefs + 1 + + " remember this window number because we will return to it after + " refreshing the buffer listing. + let currentWindowNumber = winnr() + let curBufListed = buflisted(bufnr('%')) + let cfn = s:Path(expand("%:p")) + + " now cycle through all the visible explorers and and for each "invalid"ly + " displayed explorer call its corresponding refresh and resize functions. + let i = 1 + while i <= s:numExplorerGroups && curBufListed + " find if its visible and the explorer of this group which is + " currently displayed. + let windownum = s:IsExplorerGroupVisible(i) + " if this explorer is visible, then call its _IsValid() function, etc. + if windownum == -1 + let i = i + 1 + continue + end + let numexp = s:WhichMemberVisible(i) + " visible, goto that window. + call s:GotoWindow(windownum) + exe 'let name = s:explorerName_'.numexp + + exe 'let explorerName = s:explorerName_'.numexp + exe 'let isvalid = '.explorerName.'_IsValid()' + " ... and if it isnt then update it. + if !isvalid + call <SID>GotoWindow(windownum) + exe 'call '.explorerName.'_Start()' + if exists('*'.explorerName.'_ReSize') && !s:IsOnlyVertical() + exe 'call '.explorerName.'_ReSize()' + end + end + let i = i + 1 + endwhile + + " this while loop handles the case where a group of explorers are was not + " valid at some point and therefore didnt occupy a window, but became + " valid after some point and therefore need to obtain a seperate window. + let i = 1 + while i <= s:numExplorerGroups && curBufListed + exe 'let retry = s:tryGroupAgain_'.i + " only do this if we need to retry opening this buffer. we should not + " keep opening a group which the user has closed using a ":quit" + " command. + if retry + call PrintError('retrying group '.i) + " find the 'nearest' group which is open. + let nearestGroup = 'inf' + let nearestWindow = 'inf' + " TODO: possible bug: what if there are more than a million + " plugins being used? :-) + let nearestGroupDist = 1000000 + let j = 1 + while j <= s:numExplorerGroups + + let windownum = s:IsExplorerGroupVisible(j) + if windownum != -1 + let dist = ( (j-i) < 0 ? (i-j) : (j-i) ) + if dist < nearestGroupDist + let nearestGroupDist = dist + let nearestGroup = j + let nearestWindow = windownum + end + end + let j = j + 1 + endwhile + + call PrintError('nearestWindow = '.nearestWindow) + " if nearestWindow is 'inf', it means no other explorer plugins + " are open. which means that this thing needs to go the very + " right. + if nearestWindow == 'inf' + let ecmd = 'vsplit' + else + let ecmd = 'split' + if nearestGroup > i + setlocal nosplitbelow + else + setlocal splitbelow + end + end + let somethingDisplayed = s:EditNextVisibleExplorer(i, 0, 1, ecmd) + " if nothing was displayed this time, there is a possiblity it could + " happen later during one of the refresh cycles. remember this for + " then. + if !somethingDisplayed + exe 'let s:tryGroupAgain_'.i.' = 1' + q + else + exe 'let s:tryGroupAgain_'.i.' = 0' + let currentWindowNumber = currentWindowNumber + 1 + exe 'let name = s:explorerName_'.somethingDisplayed + if exists('*'.name.'_ReSize') && !s:IsOnlyVertical() + exe 'call '.name.'_ReSize()' + end + if nearestWindow == 'inf' + wincmd H + " set up the correct width + " set width only if we are creating a new window... + exe g:winManagerWidth.'wincmd |' + end + call PrintError('doing the funky open thing') + end + end + let i = i + 1 + endwhile + + call s:ResizeAllExplorers() + + " refreshing done, now return back to where we were originally. + call <SID>GotoWindow(currentWindowNumber) + + " however, we still have to "repair" the actual @% and @# registers, in + " case we are returning to a listed buffer. also should do this only for + " a BufEnter event. For a BufDelete event, the do this only if the current + " buffer is not the buffer being deleted. + call PrintError('refresh: abuf = '.expand('<abuf>')) + if buflisted(bufnr("%")) && !isdirectory(bufname("%")) && + \ ( !BufDelete || ( bufnr('%') != expand('<abuf>') ) ) + call <SID>RepairAltRegister() + end + + let s:commandRunning = 0 + let &splitbelow = _split +endfunction + +function! <SID>ResizeAllExplorers() + let i = 1 + while i <= s:numExplorers + let explorerWinNum = s:IsExplorerVisible(i) + if explorerWinNum != -1 + exe 'let explorerName = s:explorerName_'.i + if exists('*'.explorerName.'_ReSize') && !s:IsOnlyVertical() + " if a resize() function exists for this explorer and there + " is some window above and/or below this window, then call its + " resize function. this allows for dynamic resizing. + call s:GotoWindow(explorerWinNum) + exe 'call '.explorerName.'_ReSize()' + call PrintError('calling resize for '.explorerName) + end + end + let i = i + 1 + endwhile +endfunction + +"--- +" Make sure a path has proper form. +" this function forces every path to take the following form +" dir1/dir2/file OR +" dir1/dir2/dir/ +" i.e, it replaces \ with / and stuff. +" +function! <SID>Path(p) + let _p = a:p + if a:p =~ '//$' + return "" + end + if isdirectory(_p) + let origdir= getcwd() + exe "chdir" _p + let _p = getcwd() + exe "chdir" origdir + end + if has("dos16") || has("dos32") || has("win16") || has("win32") || has("os2") + let _p = substitute(_p,'\\','/','g') + endif + if _p !~ '/$' && isdirectory(_p) + let _p = _p.'/' + endif + return _p +endfunction + +" goto the reqdWinNum^th window. returns 0 on failure otherwise 1. +function! <SID>GotoWindow(reqdWinNum) + let startWinNum = winnr() + if startWinNum == a:reqdWinNum + return 1 + end + if winbufnr(a:reqdWinNum) == -1 + return 0 + else + exe a:reqdWinNum.' wincmd w' + return 1 + end + +endfunction + +" returns the window number of the ith explorer if its visible, else -1 +function! <SID>IsExplorerVisible(i) + if exists('s:explorerBufNum_'.a:i) + exe 'let explorerBufNum = s:explorerBufNum_'.a:i + else + let explorerBufNum = -1 + end + return bufwinnr(explorerBufNum) +endfunction + +" returns the window number of the first explorer of the ith explorer group if +" its visible, else -1 +" +" if called with 2 arguments with the second being 'member', then returns the +" member number which is visible instead of its window number +" +function! <SID>IsExplorerGroupVisible(i, ...) + " numList : the list of explorer numbers belonging to this group + exe 'let numList = s:explorerGroupNums_'.a:i + " ncl : next comma location + " pcl : previous comma location + let pcl = 0 + let ncl = match(numList, ',', pcl + 1) + while ncl != -1 + exe 'let num = '.strpart(numList, pcl + 1, ncl - pcl - 1) + if s:IsExplorerVisible(num) != -1 + if a:0 == 1 && a:1 == 'mem' + return num + else + return s:IsExplorerVisible(num) + end + end + let pcl = ncl + let ncl = match(numList, ',', pcl + 1) + endwhile + return -1 +endfunction + +" returns the member number of the first explorer of the ith explorer group if +" its visible, else -1 +function! <SID>WhichMemberVisible(i) + return s:IsExplorerGroupVisible(a:i, 'mem') +endfunction + +" a handy little function for debugging. +function! PrintError(eline) + if !g:debugWinManager + return + end + if !exists("g:myerror") + let g:myerror = "" + end + let g:myerror = g:myerror . "\n" . a:eline +endfunction + +"--- +" find the memn^th member's explorer number of the groupn^th explorer group +" i.e, if s:explorerGroup_2 = ",3,4,5," +" then FindExplorerInGroup(2,3) = 5 +" +" returs -1 if its not possible. +" +function! <SID>FindExplorerInGroup(groupn, memn) + " numList : the list of explorer numbers belonging to this group + exe 'let numList = s:explorerGroupNums_'.a:groupn + + let num = s:Strntok2(numList, ',', a:memn) + if num == '' + return -1 + end + exe 'return '.num +endfunction + +"--- +" goto the next explorer in the group which this one belongs to. +" if called with 2 arguments, goto the previous explorer. +" +function! <SID>GotoNextExplorerInGroup(name, ...) + let s:commandRunning = 1 + " go forward or back? + if a:0 > 1 + let dir = -1 + else + let dir = 1 + end + + " first extract the ID variable from the name + exe 'let grpn = s:'.a:name.'_groupID' + exe 'let memn = s:'.a:name.'_memberID' + exe 'let numn = s:'.a:name.'_numberID' + + " find the number of members of this group. + exe 'let nummems = s:numMembers_'.grpn + if nummems == 1 + return 0 + end + + if exists('*'.a:name.'_WrapUp') + exe 'call '.a:name.'_WrapUp()' + end + + let curbufnum = bufnr('%') + let somethingDisplayed = s:EditNextVisibleExplorer(grpn, memn, dir, 'e') + if !somethingDisplayed && curbufnum != bufnr('%') + " now start the next explorer using its title + exe 'let title = s:explorerTitle_'.numn + exe 'silent! e '.title + setlocal nobuflisted + setlocal bufhidden=delete + setlocal buftype=nofile + setlocal noswapfile + + " call the Start() function for the next explorer ... + exe 'call '.a:name.'_Start()' + exe 'nnoremap <buffer> <C-n> :WinManagerGotoNextInGroup "'.a:name.'"<cr>' + exe 'nnoremap <buffer> <C-p> :WinManagerGotoPrevInGroup "'.a:name.'"<cr>' + setlocal nomodifiable + call WinManagerForceReSize(a:name) + end + + let s:commandRunning = 0 +endfunction + +" edit the first possible explorer after memn belonging to groun. use editcmd +" to form the new window. +function! <SID>EditNextVisibleExplorer(grpn, memn, dir, editcmd) + + call PrintError('EditNext: grpn = '.a:grpn.', memn = '.a:memn.', dir = '.a:dir.' editcmd = '.a:editcmd) + " then try to find the number of the next member. + let startmn = (a:memn ? a:memn : 1) + let nextmn = a:memn + a:dir + let editcmd = a:editcmd + + let somethingDisplayed = 0 + + let once = 0 + " enter this loop at least once + while nextmn != startmn || !once + " cycle through the next explorers in this group finding out the next + " explorer which says its able to display anything at all. + let once = 1 + + let nextEN = s:FindExplorerInGroup(a:grpn, nextmn) + " if the next member doesnt exist wrap around. + if nextEN == -1 + if a:dir == 1 + let nextEN = s:FindExplorerInGroup(a:grpn, 1) + let nextmn = 1 + continue + else + let nextEN = s:FindExplorerInGroup(a:grpn, nummems) + let nextmn = nummems + continue + end + end + + " if we have come back to the same explorer with every other group + " member not able to display anything, then return. + call PrintError('nextmn = '.nextmn.' a:memn = '.a:memn) + + exe 'let name = s:explorerName_'.nextEN + " if the _IsPossible() function doesn't exist, assume its always + " possible to display stuff. + let isposs = 1 + if exists('*'.name.'_IsPossible') + exe 'let isposs = '.name.'_IsPossible()' + end + if isposs + " now start the next explorer using its title + exe 'let title = s:explorerTitle_'.nextEN + exe 'let name = s:explorerName_'.nextEN + exe 'silent! '.editcmd.' '.title + " use vsplitting etc only the first time things are opened. + if editcmd != 'e' + let editcmd = 'e' + end + " these are a few setting which most well-made explorers + " already set, but just to be on the safe side. + setlocal nobuflisted + setlocal bufhidden=delete + setlocal buftype=nofile + setlocal noswapfile + + " call the Start() function for the next explorer ... + exe 'call '.name.'_Start()' + setlocal nomodifiable + " and remember its buffer number for later. + exe 'let s:explorerBufNum_'.nextEN.' = bufnr("%")' + " also remember that this was the last explorer of this group which was + " displayed. + exe 'let s:lastMemberDisplayed_'.a:grpn.' = nextmn' + + " if this explorer has actually not put anything in the buffer + " then quit and forget. + if line('$') > 0 && getline('$') != '' + let somethingDisplayed = nextEN + break + end + end + " goto the next explorer of the group. + let nextmn = nextmn + a:dir + endwhile + + if somethingDisplayed + " and then add this mapping to switch to the next/previous + " explorer in this group + exe 'nnoremap <buffer> <C-n> :WinManagerGotoNextInGroup "'.name.'"<cr>' + exe 'nnoremap <buffer> <C-p> :WinManagerGotoPrevInGroup "'.name.'"<cr>' + end + return somethingDisplayed +endfunction + + +" goes to either the first explorer window or the last explorer window +" visible. +function! <SID>GotoExplorerWindow(which) + let s:commandRunning = 1 + " first go to either the top left or the bottom right window. + if a:which == '1' + " goto to the top left and move in the bottom/right direction. + wincmd t + let winmovecmd = 'wincmd w' + else + wincmd b + let winmovecmd = 'wincmd W' + end + " remember the window we started from. + let startWin = winnr() + let firstTime = 1 + " then begin cycling through all the windows either going in the + " bottom/right direction or the top/left direction. + while 1 + " if we are on an explorer window quit. + if s:IsExplorerBuffer(bufnr('%')) + let s:commandRunning = 0 + return + end + " if we have cycled through one complete time without hitting pay + " dirt, quit. + if winnr() == startWin && !firstTime + " TODO: this will screw the @% and @# register. + break + end + let firstTime = 0 + exe winmovecmd + endwhile + let s:commandRunning = 0 +endfunction + +" returns the explorer number if an explorer plugin exists with the specified +" buffer number +function! <SID>IsExplorerBuffer(num) + let i = 1 + while i <= s:numExplorers + if exists('s:explorerBufNum_'.i) + exe 'let bufnum = s:explorerBufNum_'.i + if bufnum == a:num + return i + end + end + let i = i + 1 + endwhile + return 0 +endfunction + +" toggle showing the explorer plugins. +function! <SID>ToggleWindowsManager() + if IsWinManagerVisible() + call s:CloseWindowsManager() + else + call s:StartWindowsManager() + end +endfunction + +" exported function. returns the buffer number of the last file being edited +" in the file editing area. +function! WinManagerGetLastEditedFile(...) + if a:0 == 0 + return s:MRUGet(1) + else + let ret = s:MRUGet(a:1) + if ret == '' + return matchstr(s:MRUList, ',\zs[0-9]\+\ze,$') + else + return ret + end +endfunction + + +" exported function. returns 1 if any of the explorer windows are open, +" otherwise returns 0. +function! IsWinManagerVisible() + let i = 1 + while i <= s:numExplorers + if s:IsExplorerVisible(i) != -1 + return 1 + end + let i = i + 1 + endwhile + return 0 +endfunction + + +" close all the visible explorer windows. +function! <SID>CloseWindowsManager() + let s:commandRunning = 1 + + let i = 1 + while i <= bufnr('$') + let explNum = s:IsExplorerBuffer(i) + if explNum > 0 && bufwinnr(i) != -1 + exe 'bd '.i + end + let i = i + 1 + endwhile + + let s:commandRunning = 0 +endfunction + +" provides a way to examine script local variables from outside the script. +" very handy for debugging. +function! <SID>ShowVariableValue(...) + let i = 1 + while i <= a:0 + exe 'let arg = a:'.i + if exists('s:'.arg) || + \ exists('*s:'.arg) + exe 'let val = s:'.arg + echomsg 's:'.arg.' = '.val + end + let i = i + 1 + endwhile +endfunction + +" the following functions are hooks provided by winmanager to external plugins +" as a way to get winmanager to stop getting triggered on AUs. This is useful +" when an explorer plugin triggers a BufEnter or BufDelete *internally*. For +" example, bufexplorer.vim's "delete buffer" function triggers a BufDelete +" function. +" +function! WinManagerSuspendAUs() + let s:commandRunning = 1 +endfunction +function! WinManagerResumeAUs() + let s:commandRunning = 0 +endfunction + +" Another hook provided by winmanager. Normally winmanager will call the +" plugins resize function every time the BufEnter or BufDelete event is +" triggered. However, sometimes a plugin might change the number of lines +" *internally*. In this case, the plugin could make a call to this function +" which will make a safety check and then call its resize function. +" +function! WinManagerForceReSize(explName) + if !exists('s:'.a:explName.'_numberID') || !exists('*'.a:explName.'_ReSize') + call PrintError('resize quitting because resize function not found or explorer not registered') + return + end + exe 'let explNum = s:'.a:explName.'_numberID' + let s:commandRunning = 1 + let windowNum = s:IsExplorerVisible(explNum) + if windowNum == -1 + call PrintError('resize quitting because window not visible') + return + end + call s:GotoWindow(windowNum) + if s:IsOnlyVertical() + call PrintError('resize quitting because its illegal') + return + end + exe 'call '.a:explName.'_ReSize()' + let s:commandRunning = 0 +endfunction + +" returns 1 if the only visible windows are explorer windows. +function! <SID>OnlyExplorerWindowsOpen() + let i = 1 + " loop over all open windows + while 1 + " if we have checked all open windows and not returned yet, then it + " means only explorers are visible. + if winbufnr(i) == -1 + return 1 + end + " if this is a non-explorer window then return 0 + if !s:IsExplorerBuffer(winbufnr(i)) + return 0 + end + let i = i + 1 + endwhile +endfunction + +" MRUPush +function! <SID>MRUPush() + if buflisted(bufnr("%")) && !isdirectory(bufname("%")) + let _bufNbr = bufnr('%') + let _list = substitute(s:MRUList, ','._bufNbr.',', ',', '') + let s:MRUList = ','._bufNbr._list + unlet _bufNbr _list + end +endfunction + +" MRUPop +function! <SID>MRUPop() + let _bufNbr = expand('<abuf>') + let s:MRUList = substitute(s:MRUList, ''._bufNbr.',', '', '') + unlet _bufNbr +endfunction + +" MRUGet +function! <SID>MRUGet(slot) + let ret = s:Strntok2(s:MRUList, ',', a:slot) + if ret == '' + return -1 + end + exe 'return '.ret +endfunction + +" Strntok: +" extract the n^th token from s seperated by tok. +" example: Strntok('1,23,3', ',', 2) = 23 +fun! <SID>Strntok(s, tok, n) + return matchstr( a:s.a:tok[0], '\v(\zs([^'.a:tok.']*)\ze['.a:tok.']){'.a:n.'}') +endfun + +" Strntok2 +" same as Strntok except that s is delimited by the tok character at the +" beginning and end. +" example: Strntok2(',1,23,3,', ',', 2) = 23 +fun! <SID>Strntok2(s, tok, n) + return matchstr( a:s, '\v((['.a:tok.']\zs[^'.a:tok.']*)\ze){'.a:n.'}') +endfun + +" InitializeMRUList +" +" initialize the MRU list. initially this will be just the buffers in the +" order of their buffer numbers with the @% and @# leading. The MRU list +" consists of a string of the following form: ",1,2,3,4," +" NOTE: there are commas at the beginning and the end. this is to make +" identifying the position of buffers in the list easier even if they occur in +" the beginning or end and in situations where one buffer number is part of +" another. i.e the string "9" is part of the string "19" +" +function! <SID>InitializeMRUList() + let nBufs = bufnr('$') + let _i = 1 + + " put the % and the # numbers at the beginning if they are listed. + let s:MRUList = '' + if buflisted(bufnr("%")) + let s:MRUList = ','.bufnr("%") + end + if buflisted(bufnr("#")) + let s:MRUList = s:MRUList.','.bufnr("#") + end + let s:MRUList = s:MRUList.',' + + " then proceed with the rest of the buffers + while _i <= nBufs + " dont keep unlisted buffers in the MRU list. + if buflisted(_i) && bufnr("%") != _i && bufnr("#") != _i + let s:MRUList = s:MRUList._i.',' + end + let _i = _i + 1 + endwhile + " Doing this makes bufexplorer.vim display the first two listed buffers as + " @% and @# which they actually are when winmanager starts up after doing + " something like: + " vim *.vim + " :WMtoggle + let g:MRUList = s:MRUList +endfunction + +if !g:defaultExplorer + let loaded_explorer = 1 + "--- + " Set up the autocommand to allow directories to be edited + " + augroup fileExplorer + au! + au VimEnter * call s:EditDir("VimEnter") + au BufEnter * call s:EditDir("BufEnter") + augroup end +end + +" handles editing a directory via winmanager. +function! <SID>EditDir(event) + " return immediately if this isn't a directory. + let name = expand("%") + if name == "" + let name = expand("%:p") + endif + if !isdirectory(name) + return + endif + + " if it is, then call the modified explorer.vim's Explore command. + if a:event != "VimEnter" + if exists(":Explore") + ExploreInCurrentWindow + end + end + " if we have entered vim while editing a directory, then remove the + " directory buffer, and start the window layout. + " Note that we only start up winmanager in a VimEnter event because we + " want commands such as ":e /some/dir/" within vim to have the same effect + " as with the standard explorer.vim plugin which ships with vim. + " + " NOTE: if the user has chosen a layout where the FileExplorer is not at + " the top-left, this will be unintuitive. + if a:event == "VimEnter" + bwipeout + + call s:StartWindowsManager() + call s:MRUPush() + call s:GotoExplorerWindow('1') + end +endfunction + +" restore 'cpo' +let &cpo = s:cpo_save +unlet s:cpo_save +" vim:ts=4:noet:sw=4 |