1
0
Fork 0
mirror of synced 2024-06-29 03:51:09 -04:00
ultimate-vim/vim_plugins_src/winmanager/plugin/winmanager.vim

1317 lines
39 KiB
VimL

"=============================================================================
" 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