Rainbow
[my-vim-dotfolder.git] / plugin / bufexplorer.vim
blob3514a7dd5bda824c0427e80ce7b8f3df98aa117c
1 "==============================================================================
2 "    Copyright: Copyright (C) 2001-2010 Jeff Lanzarotta
3 "               Permission is hereby granted to use and distribute this code,
4 "               with or without modifications, provided that this copyright
5 "               notice is copied with it. Like anything else that's free,
6 "               bufexplorer.vim is provided *as is* and comes with no
7 "               warranty of any kind, either expressed or implied. In no
8 "               event will the copyright holder be liable for any damages
9 "               resulting from the use of this software.
10 " Name Of File: bufexplorer.vim
11 "  Description: Buffer Explorer Vim Plugin
12 "   Maintainer: Jeff Lanzarotta (delux256-vim at yahoo dot com)
13 " Last Changed: Tuesday, 16 Feb 2010
14 "      Version: See g:bufexplorer_version for version number.
15 "        Usage: This file should reside in the plugin directory and be
16 "               automatically sourced.
18 "               You may use the default keymappings of
20 "                 <Leader>be  - Opens BE.
21 "                 <Leader>bs  - Opens horizontally window BE.
22 "                 <Leader>bv  - Opens vertically window BE.
24 "               Or you can use
26 "                 ":BufExplorer"                - Opens BE.
27 "                 ":BufExplorerHorizontalSplit" - Opens horizontally window BE.
28 "                 ":BufExplorerVerticalSplit"   - Opens vertically window BE.
30 "               For more help see supplied documentation.
31 "      History: See supplied documentation.
32 "==============================================================================
34 " Exit quickly if already running or when 'compatible' is set. {{{1
35 if exists("g:bufexplorer_version") || &cp
36   finish
37 endif
38 "1}}}
40 " Version number
41 let g:bufexplorer_version = "7.2.7"
43 " Check for Vim version 700 or greater {{{1
44 if v:version < 700
45   echo "Sorry, bufexplorer ".g:bufexplorer_version."\nONLY runs with Vim 7.0 and greater."
46   finish
47 endif
49 " Public Interface {{{1
50 if maparg("<Leader>be") =~ 'BufExplorer'
51   nunmap <Leader>be
52 endif
54 if maparg("<Leader>bs") =~ 'BufExplorerHorizontalSplit'
55   nunmap <Leader>bs
56 endif
58 if maparg("<Leader>bv") =~ 'BufExplorerVerticalSplit'
59   nunmap <Leader>bv
60 endif
62 nmap <script> <silent> <unique> <Leader>be :BufExplorer<CR>
63 nmap <script> <silent> <unique> <Leader>bs :BufExplorerHorizontalSplit<CR>
64 nmap <script> <silent> <unique> <Leader>bv :BufExplorerVerticalSplit<CR>
66 " Create commands {{{1
67 command! BufExplorer :call StartBufExplorer(has ("gui") ? "drop" : "hide edit")
68 command! BufExplorerHorizontalSplit :call BufExplorerHorizontalSplit()
69 command! BufExplorerVerticalSplit :call BufExplorerVerticalSplit()
71 " BESet {{{1
72 function! s:BESet(var, default)
73   if !exists(a:var)
74     if type(a:default)
75       exec "let" a:var "=" string(a:default)
76     else
77       exec "let" a:var "=" a:default
78     endif
80     return 1
81   endif
83   return 0
84 endfunction
86 " BEReset {{{1
87 function! s:BEReset()
88   " Build initial MRUList. This makes sure all the files specified on the
89   " command line are picked up correctly.
90   let s:MRUList = range(1, bufnr('$'))
92   " Initialize one tab space array, ignore zero-based tabpagenr
93   " since all tabpagenr's start at 1.
94   " -1 signifies this is the first time we are referencing this
95   " tabpagenr.
96   let s:tabSpace = [ [-1], [-1] ]
97 endfunction
99 " Setup the autocommands that handle the MRUList and other stuff. {{{1
100 " This is only done once when Vim starts up.
101 augroup BufExplorerVimEnter
102   autocmd!
103   autocmd VimEnter * call s:BESetup()
104 augroup END
106 " BESetup {{{1
107 function! s:BESetup()
108   call s:BEReset()
110   " Now that the MRUList is created, add the other autocmds.
111   augroup BufExplorer
112     " Deleting autocommands in case the script is reloaded
113     autocmd!
114     autocmd TabEnter * call s:BETabEnter()
115     autocmd BufNew * call s:BEAddBuffer()
116     autocmd BufEnter * call s:BEActivateBuffer()
118     autocmd BufWipeOut * call s:BEDeactivateBuffer(1)
119     autocmd BufDelete * call s:BEDeactivateBuffer(0)
121     autocmd BufWinEnter \[BufExplorer\] call s:BEInitialize()
122     autocmd BufWinLeave \[BufExplorer\] call s:BECleanup()
123     autocmd SessionLoadPost * call s:BEReset()
124   augroup END
126   " Remove the VimEnter event as it is no longer needed
127   augroup SelectBufVimEnter
128     autocmd!
129   augroup END
130 endfunction
132 " BETabEnter {{{1
133 function! s:BETabEnter()
134   " Make s:tabSpace 1-based
135   if empty(s:tabSpace) || len(s:tabSpace) < (tabpagenr() + 1)
136     call add(s:tabSpace, [-1])
137   endif
138 endfunction
140 " BEAddBuffer {{{1
141 function! s:BEAddBuffer()
142   if !exists('s:raw_buffer_listing') || empty(s:raw_buffer_listing)
143     silent let s:raw_buffer_listing = s:BEGetBufferInfo(0)
144   else
145     " We cannot use :buffers! or :ls! to gather information 
146     " about this buffer since it was only just added.
147     " Any changes to the buffer (setlocal buftype, ...) 
148     " happens after this event fires.
149     "
150     " So we will indicate the :buffers! command must be re-run.
151     " This should help with the performance of the plugin.
153     " There are some checks which can be performed 
154     " before deciding to refresh the buffer list.
155     let bufnr = expand('<abuf>') + 0
157     if s:BEIgnoreBuffer(bufnr) == 1
158       return 
159     else
160       let s:refreshBufferList = 1
161     endif
162   endif
164   call s:BEActivateBuffer()
165 endfunction
167 " ActivateBuffer {{{1
168 function! s:BEActivateBuffer()
169   let b = bufnr("%")
170   let l = get(s:tabSpace, tabpagenr(), [])
172   if s:BEIgnoreBuffer(b) == 1
173     return
174   endif
176   if !empty(l) && l[0] == '-1'
177     " The first time we add a tab Vim uses the current 
178     " buffer as it's starting page, even though we are about
179     " to edit a new page (BufEnter triggers after), so
180     " remove the -1 entry indicating we have covered this case.
181     let l = []
182     let s:tabSpace[tabpagenr()] = l
183   elseif empty(l) || index(l, b) == -1
184     " Add new buffer to this tab buffer list
185     let l = add(l, b)
186     let s:tabSpace[tabpagenr()] = l
188     if g:bufExplorerOnlyOneTab == 1
189       " If a buffer can only be available in 1 tab page
190       " ensure this buffer is not present in any other tabs
191       let tabidx = 1
192       while tabidx < len(s:tabSpace)
193         if tabidx != tabpagenr()
194           let bufidx = index(s:tabSpace[tabidx], b)
195           if bufidx != -1
196             call remove(s:tabSpace[tabidx], bufidx)
197           endif
198         endif
199         let tabidx = tabidx + 1
200       endwhile
201     endif
202   endif
204   call s:BEMRUPush(b)
206   if exists('s:raw_buffer_listing') && !empty(s:raw_buffer_listing)
207     " Check if the buffer exists, but was deleted previously
208     " Careful use of ' and " so we do not have to escape all the \'s
209     " Regex: ^\s*bu\>
210     "        ^ - Starting at the beginning of the string
211     "        \s* - optional whitespace
212     "        b - Vim's buffer number
213     "        u\> - the buffer must be unlisted
214     let shortlist = filter(copy(s:raw_buffer_listing), "v:val.attributes =~ '".'^\s*'.b.'u\>'."'")
216     if !empty(shortlist)
217       " If it is unlisted (ie deleted), but now we editing it again 
218       " rebuild the buffer list.
219       let s:refreshBufferList = 1
220     endif
221   endif
222 endfunction
224 " BEDeactivateBuffer {{{1
225 function! s:BEDeactivateBuffer(remove)
226   let _bufnr = str2nr(expand("<abuf>"))
228   call s:BEMRUPop(_bufnr)
230   if a:remove
231     call s:BEDeleteBufferListing(_bufnr)
232   else
233     let s:refreshBufferList = 1
234   endif
235 endfunction
237 " BEMRUPop {{{1
238 function! s:BEMRUPop(buf)
239   call filter(s:MRUList, 'v:val != '.a:buf)
240 endfunction
242 " BEMRUPush {{{1
243 function! s:BEMRUPush(buf)
244   if s:BEIgnoreBuffer(a:buf) == 1
245     return
246   endif
248   " Remove the buffer number from the list if it already exists.
249   call s:BEMRUPop(a:buf)
251   " Add the buffer number to the head of the list.
252   call insert(s:MRUList,a:buf)
253 endfunction
255 " BEInitialize {{{1
256 function! s:BEInitialize()
257   let s:_insertmode = &insertmode
258   set noinsertmode
260   let s:_showcmd = &showcmd
261   set noshowcmd
263   let s:_cpo = &cpo
264   set cpo&vim
266   let s:_report = &report
267   let &report = 10000
269   let s:_list = &list
270   set nolist
272   setlocal nonumber
273   setlocal foldcolumn=0
274   setlocal nofoldenable
275   setlocal cursorline
276   setlocal nospell
277   setlocal nobuflisted
279   let s:running = 1
280 endfunction
282 " BEIgnoreBuffer 
283 function! s:BEIgnoreBuffer(buf)
284   " Check to see if this buffer should be ignore by BufExplorer.
286   " Skip temporary buffers with buftype set.
287   if empty(getbufvar(a:buf, "&buftype") == 0)
288     return 1
289   endif
291   " Skip unlisted buffers.
292   if buflisted(a:buf) == 0
293     return 1
294   endif
296   " Skip buffers with no name.
297   if empty(bufname(a:buf)) == 1
298     return 1
299   endif
301   " Do not add the BufExplorer window to the list.
302   if fnamemodify(bufname(a:buf), ":t") == s:name
303     return 1
304   endif
306   if index(s:MRU_Exclude_List, bufname(a:buf)) >= 0
307     return 1
308   end
310   return 0 
311 endfunction
313 " BECleanup {{{1
314 function! s:BECleanup()
315   let &insertmode = s:_insertmode
316   let &showcmd = s:_showcmd
317   let &cpo = s:_cpo
318   let &report = s:_report
319   let &list = s:_list
320   let s:running = 0
321   let s:splitMode = ""
323   delmarks!
324 endfunction
326 " BufExplorerHorizontalSplit {{{1
327 function! BufExplorerHorizontalSplit()
328   let s:splitMode = "sp"
329   exec "BufExplorer"
330 endfunction
332 " BufExplorerVerticalSplit {{{1
333 function! BufExplorerVerticalSplit()
334   let s:splitMode = "vsp"
335   exec "BufExplorer"
336 endfunction
338 " StartBufExplorer {{{1
339 function! StartBufExplorer(open)
340   let name = s:name
342   if !has("win32")
343     " On non-Windows boxes, escape the name so that is shows up correctly.
344     let name = escape(name, "[]")
345   endif
347   " Make sure there is only one explorer open at a time.
348   if s:running == 1
349     " Go to the open buffer.
350     if has("gui")
351       exec "drop" name
352     endif
354     return
355   endif
357   " Add zero to ensure the variable is treated as a Number.
358   let s:originBuffer = bufnr("%") + 0
360   " Create or rebuild the raw buffer list if necessary.
361   if !exists('s:raw_buffer_listing') || 
362         \ empty(s:raw_buffer_listing) ||
363         \ s:refreshBufferList == 1
364     silent let s:raw_buffer_listing = s:BEGetBufferInfo(0)
365   endif
367   let copy = copy(s:raw_buffer_listing)
369   if (g:bufExplorerShowUnlisted == 0)
370     call filter(copy, 'v:val.attributes !~ "u"')
371   endif
373   " We may have to split the current window.
374   if (s:splitMode != "")
375     " Save off the original settings.
376     let [_splitbelow, _splitright] = [&splitbelow, &splitright]
378     " Set the setting to ours.
379     let [&splitbelow, &splitright] = [g:bufExplorerSplitBelow, g:bufExplorerSplitRight]
381     " Do it.
382     exe 'keepalt '.s:splitMode
384     " Restore the original settings.
385     let [&splitbelow, &splitright] = [_splitbelow, _splitright]
386   endif
388   if !exists("b:displayMode") || b:displayMode != "winmanager"
389     " Do not use keepalt when opening bufexplorer to allow the buffer that we are
390     " leaving to become the new alternate buffer
391     exec "silent keepjumps ".a:open." ".name
392   endif
394   call s:BEDisplayBufferList()
395 endfunction
397 " BEDisplayBufferList {{{1
398 function! s:BEDisplayBufferList()
399   " Do not set bufhidden since it wipes out 
400   " the data if we switch away from the buffer 
401   " using CTRL-^
402   setlocal buftype=nofile
403   setlocal modifiable
404   setlocal noswapfile
405   setlocal nowrap
407   " Delete all previous lines to the black hole register
408   call cursor(1,1)
409   exec 'silent! normal! "_dG'
411   call s:BESetupSyntax()
412   call s:BEMapKeys()
413   call setline(1, s:BECreateHelp())
414   call s:BEBuildBufferList()
415   call cursor(s:firstBufferLine, 1)
417   if !g:bufExplorerResize
418     normal! zz
419   endif
421   setlocal nomodifiable
422 endfunction
424 " BEMapKeys {{{1
425 function! s:BEMapKeys()
426   if exists("b:displayMode") && b:displayMode == "winmanager"
427     nnoremap <buffer> <silent> <tab> :call <SID>BESelectBuffer("tab")<cr>
428   endif
430   nnoremap <buffer> <silent> <F1>          :call <SID>BEToggleHelp()<cr>
431   nnoremap <buffer> <silent> <2-leftmouse> :call <SID>BESelectBuffer()<cr>
432   nnoremap <buffer> <silent> <cr>          :call <SID>BESelectBuffer()<cr>
433   nnoremap <buffer> <silent> o             :call <SID>BESelectBuffer()<cr>
434   nnoremap <buffer> <silent> t             :call <SID>BESelectBuffer("tab")<cr>
435   nnoremap <buffer> <silent> <s-cr>        :call <SID>BESelectBuffer("tab")<cr>
437   nnoremap <buffer> <silent> d             :call <SID>BERemoveBuffer("delete", "n")<cr>
438   xnoremap <buffer> <silent> d             :call <SID>BERemoveBuffer("delete", "v")<cr>
439   nnoremap <buffer> <silent> D             :call <SID>BERemoveBuffer("wipe", "n")<cr>
440   xnoremap <buffer> <silent> D             :call <SID>BERemoveBuffer("wipe", "v")<cr>
442   nnoremap <buffer> <silent> m             :call <SID>BEMRUListShow()<cr>
443   nnoremap <buffer> <silent> p             :call <SID>BEToggleSplitOutPathName()<cr>
444   nnoremap <buffer> <silent> q             :call <SID>BEClose("quit")<cr>
445   nnoremap <buffer> <silent> r             :call <SID>BESortReverse()<cr>
446   nnoremap <buffer> <silent> R             :call <SID>BEToggleShowRelativePath()<cr>
447   nnoremap <buffer> <silent> s             :call <SID>BESortSelect()<cr>
448   nnoremap <buffer> <silent> S             :call <SID>BEReverseSortSelect()<cr>
449   nnoremap <buffer> <silent> u             :call <SID>BEToggleShowUnlisted()<cr>
450   nnoremap <buffer> <silent> f             :call <SID>BEToggleFindActive()<cr>
451   nnoremap <buffer> <silent> T             :call <SID>BEToggleShowTabBuffer()<cr>
452   nnoremap <buffer> <silent> B             :call <SID>BEToggleOnlyOneTab()<cr>
454   for k in ["G", "n", "N", "L", "M", "H"]
455     exec "nnoremap <buffer> <silent>" k ":keepjumps normal!" k."<cr>"
456   endfor
457 endfunction
459 " BESetupSyntax {{{1
460 function! s:BESetupSyntax()
461   if has("syntax")
462     syn match bufExplorerHelp         "^\".*" contains=bufExplorerSortBy,bufExplorerMapping,bufExplorerTitle,bufExplorerSortType,bufExplorerToggleSplit,bufExplorerToggleOpen
463     syn match bufExplorerOpenIn       "Open in \w\+ window" contained
464     syn match bufExplorerSplit        "\w\+ split" contained
465     syn match bufExplorerSortBy       "Sorted by .*" contained contains=bufExplorerOpenIn,bufExplorerSplit
466     syn match bufExplorerMapping      "\" \zs.\+\ze :" contained
467     syn match bufExplorerTitle        "Buffer Explorer.*" contained
468     syn match bufExplorerSortType     "'\w\{-}'" contained
469     syn match bufExplorerBufNbr       /^\s*\d\+/
470     syn match bufExplorerToggleSplit  "toggle split type" contained
471     syn match bufExplorerToggleOpen   "toggle open mode" contained
472     syn match bufExplorerModBuf       /^\s*\d\+.\{4}+.*/
473     syn match bufExplorerLockedBuf    /^\s*\d\+.\{3}[\-=].*/
474     syn match bufExplorerHidBuf       /^\s*\d\+.\{2}h.*/
475     syn match bufExplorerActBuf       /^\s*\d\+.\{2}a.*/
476     syn match bufExplorerCurBuf       /^\s*\d\+.%.*/
477     syn match bufExplorerAltBuf       /^\s*\d\+.#.*/
478     syn match bufExplorerUnlBuf       /^\s*\d\+u.*/
480     hi def link bufExplorerBufNbr Number
481     hi def link bufExplorerMapping NonText
482     hi def link bufExplorerHelp Special
483     hi def link bufExplorerOpenIn Identifier
484     hi def link bufExplorerSortBy String
485     hi def link bufExplorerSplit NonText
486     hi def link bufExplorerTitle NonText
487     hi def link bufExplorerSortType bufExplorerSortBy
488     hi def link bufExplorerToggleSplit bufExplorerSplit
489     hi def link bufExplorerToggleOpen bufExplorerOpenIn
491     hi def link bufExplorerActBuf Identifier
492     hi def link bufExplorerAltBuf String
493     hi def link bufExplorerCurBuf Type
494     hi def link bufExplorerHidBuf Constant
495     hi def link bufExplorerLockedBuf Special
496     hi def link bufExplorerModBuf Exception
497     hi def link bufExplorerUnlBuf Comment
498   endif
499 endfunction
501 " BEToggleHelp {{{1
502 function! s:BEToggleHelp()
503   let g:bufExplorerDetailedHelp = !g:bufExplorerDetailedHelp
505   setlocal modifiable
507   " Save position.
508   normal! ma
510   " Remove old header.
511   if (s:firstBufferLine > 1)
512     exec "keepjumps 1,".(s:firstBufferLine - 1) "d _"
513   endif
515   call append(0, s:BECreateHelp())
517   silent! normal! g`a
518   delmarks a
520   setlocal nomodifiable
522   if exists("b:displayMode") && b:displayMode == "winmanager"
523     call WinManagerForceReSize("BufExplorer")
524   endif
525 endfunction
527 " BEGetHelpStatus {{{1
528 function! s:BEGetHelpStatus()
529   let ret = '" Sorted by '.((g:bufExplorerReverseSort == 1) ? "reverse " : "").g:bufExplorerSortBy
530   let ret .= ' | '.((g:bufExplorerFindActive == 0) ? "Don't " : "")."Locate buffer"
531   let ret .= ((g:bufExplorerShowUnlisted == 0) ? "" : " | Show unlisted")
532   let ret .= ((g:bufExplorerShowTabBuffer == 0) ? "" : " | Show buffers/tab")
533   let ret .= ((g:bufExplorerOnlyOneTab == 1) ? "" : " | One tab / buffer")
534   let ret .= ' | '.((g:bufExplorerShowRelativePath == 0) ? "Absolute" : "Relative")
535   let ret .= ' '.((g:bufExplorerSplitOutPathName == 0) ? "Full" : "Split")." path"
537   return ret
538 endfunction
540 " BECreateHelp {{{1
541 function! s:BECreateHelp()
542   if g:bufExplorerDefaultHelp == 0 && g:bufExplorerDetailedHelp == 0
543     let s:firstBufferLine = 1
544     return []
545   endif
547   let header = []
549   if g:bufExplorerDetailedHelp == 1
550     call add(header, '" Buffer Explorer ('.g:bufexplorer_version.')')
551     call add(header, '" --------------------------')
552     call add(header, '" <F1> : toggle this help')
553     call add(header, '" <enter> or o or Mouse-Double-Click : open buffer under cursor')
554     call add(header, '" <shift-enter> or t : open buffer in another tab')
555     call add(header, '" d : delete buffer')
556     call add(header, '" D : wipe buffer')
557     call add(header, '" f : toggle find active buffer')
558     call add(header, '" p : toggle spliting of file and path name')
559     call add(header, '" q : quit')
560     call add(header, '" r : reverse sort')
561     call add(header, '" R : toggle showing relative or full paths')
562     call add(header, '" s : cycle thru "sort by" fields '.string(s:sort_by).'')
563     call add(header, '" S : reverse cycle thru "sort by" fields')
564     call add(header, '" T : toggle if to show only buffers for this tab or not')
565     call add(header, '" u : toggle showing unlisted buffers')
566   else
567     call add(header, '" Press <F1> for Help')
568   endif
570   if (!exists("b:displayMode") || b:displayMode != "winmanager") || (b:displayMode == "winmanager" && g:bufExplorerDetailedHelp == 1)
571     call add(header, s:BEGetHelpStatus())
572     call add(header, '"=')
573   endif
575   let s:firstBufferLine = len(header) + 1
577   return header
578 endfunction
580 " BEGetBufferInfo {{{1
581 function! s:BEGetBufferInfo(bufnr)
582   redir => bufoutput
583   buffers!
584   redir END
586   if (a:bufnr > 0)
587     " Since we are only interested in this specified buffer 
588     " remove the other buffers listed
589     let bufoutput = substitute(bufoutput."\n", '^.*\n\(\s*'.a:bufnr.'\>.\{-}\)\n.*', '\1', '')
590   endif
592   let [all, allwidths, listedwidths] = [[], {}, {}]
594   for n in keys(s:types)
595     let allwidths[n] = []
596     let listedwidths[n] = []
597   endfor
599   for buf in split(bufoutput, '\n')
600     let bits = split(buf, '"')
601     let b = {"attributes": bits[0], "line": substitute(bits[2], '\s*', '', '')}
603     for [key, val] in items(s:types)
604       let b[key] = fnamemodify(bits[1], val)
605     endfor
607     if getftype(b.fullname) == "dir" && g:bufExplorerShowDirectories == 1
608       let b.shortname = "<DIRECTORY>"
609     endif
611     call add(all, b)
613     for n in keys(s:types)
614       call add(allwidths[n], len(b[n]))
616       if b.attributes !~ "u"
617         call add(listedwidths[n], len(b[n]))
618       endif
619     endfor
620   endfor
622   let [s:allpads, s:listedpads] = [{}, {}]
624   for n in keys(s:types)
625     let s:allpads[n] = repeat(' ', max(allwidths[n]))
626     let s:listedpads[n] = repeat(' ', max(listedwidths[n]))
627   endfor
629   let s:refreshBufferList = 1
631   return all
632 endfunction
634 " BEBuildBufferList {{{1
635 function! s:BEBuildBufferList()
636   let lines = []
638   " Loop through every buffer.
639   for buf in s:raw_buffer_listing
640     if (!g:bufExplorerShowUnlisted && buf.attributes =~ "u")
641       " Skip unlisted buffers if we are not to show them.
642       continue
643     endif
645     if (g:bufExplorerShowTabBuffer)
646       let show_buffer = 0
648       for bufnr in s:tabSpace[tabpagenr()]
649         if (buf.attributes =~ '^\s*'.bufnr.'\>')
650           " Only buffers shown on the current tabpagenr
651           let show_buffer = 1
652           break
653         endif
654       endfor
656       if show_buffer == 0 
657         continue
658       endif
659     endif
661     let line = buf.attributes." "
663     if g:bufExplorerSplitOutPathName
664       let type = (g:bufExplorerShowRelativePath) ? "relativepath" : "path"
665       let path = buf[type]
666       let pad  = (g:bufExplorerShowUnlisted) ? s:allpads.shortname : s:listedpads.shortname
667       let line .= buf.shortname." ".strpart(pad.path, len(buf.shortname))
668     else
669       let type = (g:bufExplorerShowRelativePath) ? "relativename" : "fullname"
670       let path = buf[type]
671       let line .= path
672     endif
674     let pads = (g:bufExplorerShowUnlisted) ? s:allpads : s:listedpads
676     if !empty(pads[type])
677       let line .= strpart(pads[type], len(path))." "
678     endif
680     let line .= buf.line
682     call add(lines, line)
683   endfor
685   call setline(s:firstBufferLine, lines)
687   call s:BESortListing()
688 endfunction
690 " BESelectBuffer {{{1
691 function! s:BESelectBuffer(...)
692   " Sometimes messages are not cleared when we get here so it looks like an error has
693   " occurred when it really has not.
694   echo ""
696   " Are we on a line with a file name?
697   if line('.') < s:firstBufferLine
698     exec "normal! \<cr>"
699     return
700   endif
702   let _bufNbr = str2nr(getline('.'))
704   " Check and see if we are running BE via WinManager.
705   if exists("b:displayMode") && b:displayMode == "winmanager"
706     let bufname = expand("#"._bufNbr.":p")
708     if (a:0 == 1) && (a:1 == "tab")
709       call WinManagerFileEdit(bufname, 1)
710     else
711       call WinManagerFileEdit(bufname, 0)
712     endif
714     return
715   endif
717   if bufexists(_bufNbr)
718     if bufnr("#") == _bufNbr
719       return s:BEClose("")
720     endif
722     " Are we suppose to open the selected buffer in a tab?
723     if (a:0 == 1) && (a:1 == "tab")
724       " Yes, we are to open the selected buffer in a tab.
726       " Restore [BufExplorer] buffer.
727       exec "keepjumps silent buffer!".s:originBuffer
729       " Get the tab number where this buffer is located at.
730       let tabNbr = s:BEGetTabNbr(_bufNbr)
732       " Was the tab found?
733       if tabNbr == 0
734         " _bufNbr is not opened in any tabs. Open a new tab with the selected buffer in it.
735         exec "999tab split +buffer" . _bufNbr
736       else
737         " The _bufNbr is already opened in tab, go to that tab.
738         exec tabNbr . "tabnext"
740         " Focus window.
741         exec s:BEGetWinNbr(tabNbr, _bufNbr) . "wincmd w"
742       endif
743     else
744       "No, the use did not ask to open the selected buffer in a tab.
746       " Are we suppose to move to the tab where this active buffer is?
747       if bufloaded(_bufNbr) && g:bufExplorerFindActive
748         " Close the BE window.
749         call s:BEClose("")
751         " Get the tab number where this buffer is located at.
752         let tabNbr = s:BEGetTabNbr(_bufNbr)
754         " Was the tab found?
755         if tabNbr != 0
756           " The buffer is located in a tab. Go to that tab number.
757           exec tabNbr . "tabnext"
758         else
759           " Nope, the buffer is not in a tab, simple switch to that buffer.
760           let bufname = expand("#"._bufNbr.":p")
761           exec bufname ? "drop ".escape(bufname, " ") : "buffer "._bufNbr
762         endif
763       endif
765       " Switch to the buffer.
766       exec "keepalt keepjumps silent b!" _bufNbr
767     endif
769     " Make the buffer 'listed' again.
770     call setbufvar(_bufNbr, "&buflisted", "1")
771   else
772     call s:BEError("Sorry, that buffer no longer exists, please select another")
773     call s:BEDeleteBuffer(_bufNbr, "wipe")
774   endif
775 endfunction
777 " BEDeleteBufferListing {{{1
778 function! s:BEDeleteBufferListing(buf)
779   if exists('s:raw_buffer_listing') && !empty(s:raw_buffer_listing)
780     " Delete the buffer from the raw buffer list.
781     " Careful use of ' and " so we do not have to escape all the \'s
782     " Regex: ^\s*\(10\|20\)\>
783     "        ^ - Starting at the beginning of the string
784     "        \s* - optional whitespace
785     "        \(10\|20\) - either a 10 or a 20
786     "        \> - end of word (so it can't make 100 or 201)
787     call filter(s:raw_buffer_listing, "v:val.attributes !~ '".'^\s*\('.substitute(a:buf, ' ', '\\|', 'g').'\)\>'."'")
788   endif
789 endfunction
791 " BERemoveBuffer {{{1
792 function! s:BERemoveBuffer(type, mode) range
793   " Are we on a line with a file name?
794   if line('.') < s:firstBufferLine
795     return
796   endif
798   " These commands are to temporarily suspend the activity of winmanager.
799   if exists("b:displayMode") && b:displayMode == "winmanager"
800     call WinManagerSuspendAUs()
801   endif
803   let _bufNbrs = ''
805   for lineNum in range(a:firstline, a:lastline)
806     let line = getline(lineNum)
808     if line =~ '^\s*\(\d\+\)'
809       " Regex: ^\s*\(10\|20\)\>
810       "        ^ - Starting at the beginning of the string
811       "        \s* - optional whitespace
812       "        \zs - start the match here
813       "        \d\+ - any digits
814       "        \> - end of word (so it can't make 100 or 201)
815       let bufNbr = matchstr(line, '^\s*\zs\d\+\>')
817       " Add 0 to bufNbr to ensure Vim treats it as a Number
818       " for use with the getbufvar() function
819       if bufNbr !~ '^\d\+$' || getbufvar(bufNbr+0, '&modified') != 0
820         call s:BEError("Sorry, no write since last change for buffer ".bufNbr.", unable to delete")
821       else
822         let _bufNbrs = _bufNbrs . (_bufNbrs==''?'':' '). bufNbr 
823       endif
824     endif
825   endfor
827   " Okay, everything is good, delete or wipe the buffers.
828   call s:BEDeleteBuffer(_bufNbrs, a:type)
830   " Reactivate winmanager autocommand activity.
831   if exists("b:displayMode") && b:displayMode == "winmanager"
832     call WinManagerForceReSize("BufExplorer")
833     call WinManagerResumeAUs()
834   endif
835 endfunction
837 " BEDeleteBuffer {{{1
838 function! s:BEDeleteBuffer(bufNbr, mode)
839   " This routine assumes that the buffer to be removed is on the current line.
840   try
841     if a:mode == "wipe"
842       exe "bwipe" a:bufNbr
843     else
844       exe "bdelete" a:bufNbr
845     endif
847     setlocal modifiable
849     " Remove each of the lines beginning with the buffer numbers we are removing
850     " Regex: ^\s*\(10\|20\)\>
851     "        ^ - Starting at the beginning of the string
852     "        \s* - optional whitespace
853     "        \(10\|20\) - either a 10 or a 20
854     "        \> - end of word (so it can't make 100 or 201)
855     exec 'silent! g/^\s*\('.substitute(a:bufNbr, ' ', '\\|', 'g').'\)\>/d_'
857     setlocal nomodifiable
859     call s:BEDeleteBufferListing(a:bufNbr)
860   catch
861     call s:BEError(v:exception)
862   endtry
863 endfunction
865 " BEClose {{{1
866 function! s:BEClose(mode)
867   " Get only the listed buffers.
868   let listed = filter(copy(s:MRUList), "buflisted(v:val)")
870   " If we needed to split the main window, close the split one.
871 "  if (s:splitMode)
872   if (s:splitMode != "" && a:mode == "quit")
873     exec "wincmd c"
874   endif
876   if len(listed) == 0
877     exe "enew"
878   else
879     for b in reverse(listed[0:1])
880       exec "keepjumps silent b ".b
881     endfor
882   endif
883 endfunction
885 " BEToggleSplitOutPathName {{{1
886 function! s:BEToggleSplitOutPathName()
887   let g:bufExplorerSplitOutPathName = !g:bufExplorerSplitOutPathName
888   call s:BERebuildBufferList()
889   call s:BEUpdateHelpStatus()
890 endfunction
892 " BEToggleShowRelativePath {{{1
893 function! s:BEToggleShowRelativePath()
894   let g:bufExplorerShowRelativePath = !g:bufExplorerShowRelativePath
895   call s:BERebuildBufferList()
896   call s:BEUpdateHelpStatus()
897 endfunction
899 " BEToggleShowUnlisted {{{1
900 function! s:BEToggleShowUnlisted()
901   let g:bufExplorerShowUnlisted = !g:bufExplorerShowUnlisted
902   let num_bufs = s:BERebuildBufferList(g:bufExplorerShowUnlisted == 0)
903   call s:BEUpdateHelpStatus()
904 endfunction
906 " BEToggleFindActive {{{1
907 function! s:BEToggleFindActive()
908   let g:bufExplorerFindActive = !g:bufExplorerFindActive
909   call s:BEUpdateHelpStatus()
910 endfunction
912 " BEToggleShowTabBuffer {{{1
913 function! s:BEToggleShowTabBuffer()
914   let g:bufExplorerShowTabBuffer = !g:bufExplorerShowTabBuffer
915   call s:BEDisplayBufferList()
916 endfunction
918 " BEToggleOnlyOneTab {{{1
919 function! s:BEToggleOnlyOneTab()
920   let g:bufExplorerOnlyOneTab = !g:bufExplorerOnlyOneTab
921   call s:BEDisplayBufferList()
922 endfunction
924 " BERebuildBufferList {{{1
925 function! s:BERebuildBufferList(...)
926   setlocal modifiable
928   let curPos = getpos('.')
930   if a:0
931     " Clear the list first.
932     exec "keepjumps ".s:firstBufferLine.',$d "_'
933   endif
935   let num_bufs = s:BEBuildBufferList()
937   call setpos('.', curPos)
939   setlocal nomodifiable
941   return num_bufs
942 endfunction
944 " BEUpdateHelpStatus {{{1
945 function! s:BEUpdateHelpStatus()
946   setlocal modifiable
948   let text = s:BEGetHelpStatus()
949   call setline(s:firstBufferLine - 2, text)
951   setlocal nomodifiable
952 endfunction
954 " BEMRUCmp {{{1
955 function! s:BEMRUCmp(line1, line2)
956   return index(s:MRUList, str2nr(a:line1)) - index(s:MRUList, str2nr(a:line2))
957 endfunction
959 " BESortReverse {{{1
960 function! s:BESortReverse()
961   let g:bufExplorerReverseSort = !g:bufExplorerReverseSort
963   call s:BEReSortListing()
964 endfunction
966 " BESortSelect {{{1
967 function! s:BESortSelect()
968   let g:bufExplorerSortBy = get(s:sort_by, index(s:sort_by, g:bufExplorerSortBy) + 1, s:sort_by[0])
970   call s:BEReSortListing()
971 endfunction
973 " BEReverseSortSelect {{{1
974 function! s:BEReverseSortSelect()
975   let g:bufExplorerSortBy = get(s:sort_by, (index(s:sort_by, g:bufExplorerSortBy) + len(s:sort_by) - 1) % len(s:sort_by), s:sort_by[0])
977   call s:BEReSortListing()
978 endfunction
980 " BEReSortListing {{{1
981 function! s:BEReSortListing()
982   setlocal modifiable
984   let curPos = getpos('.')
986   call s:BESortListing()
987   call s:BEUpdateHelpStatus()
989   call setpos('.', curPos)
991   setlocal nomodifiable
992 endfunction
994 " BESortListing {{{1
995 function! s:BESortListing()
996   let sort = s:firstBufferLine.",$sort".((g:bufExplorerReverseSort == 1) ? "!": "")
998   if g:bufExplorerSortBy == "number"
999     " Easiest case.
1000     exec sort 'n'
1001   elseif g:bufExplorerSortBy == "name"
1002     if g:bufExplorerSplitOutPathName
1003       exec sort 'ir /\d.\{7}\zs\f\+\ze/'
1004     else
1005       exec sort 'ir /\zs[^\/\\]\+\ze\s*line/'
1006     endif
1007   elseif g:bufExplorerSortBy == "fullpath"
1008     if g:bufExplorerSplitOutPathName
1009       " Sort twice - first on the file name then on the path.
1010       exec sort 'ir /\d.\{7}\zs\f\+\ze/'
1011     endif
1013     exec sort 'ir /\zs\f\+\ze\s\+line/'
1014   elseif g:bufExplorerSortBy == "extension"
1015     exec sort 'ir /\.\zs\w\+\ze\s/'
1016   elseif g:bufExplorerSortBy == "mru"
1017     let l = getline(s:firstBufferLine, "$")
1019     call sort(l, "<SID>BEMRUCmp")
1021     if g:bufExplorerReverseSort
1022       call reverse(l)
1023     endif
1025     call setline(s:firstBufferLine, l)
1026   endif
1027 endfunction
1029 " BEMRUListShow {{{1
1030 function! s:BEMRUListShow()
1031   echomsg "MRUList=".string(s:MRUList)
1032 endfunction
1034 " BEError {{{1
1035 function! s:BEError(msg)
1036   echohl ErrorMsg | echo a:msg | echohl none
1037 endfunction
1039 " BEWarning {{{1
1040 function! s:BEWarning(msg)
1041   echohl WarningMsg | echo a:msg | echohl none
1042 endfunction
1044 " GetTabNbr {{{1
1045 function! s:BEGetTabNbr(bufNbr)
1046   " Searching buffer bufno, in tabs.
1047   for i in range(tabpagenr("$"))
1048     if index(tabpagebuflist(i + 1), a:bufNbr) != -1
1049       return i + 1
1050     endif
1051   endfor
1053   return 0
1054 endfunction
1056 " GetWinNbr" {{{1
1057 function! s:BEGetWinNbr(tabNbr, bufNbr)
1058   " window number in tabpage.
1059   return index(tabpagebuflist(a:tabNbr), a:bufNbr) + 1
1060 endfunction
1062 " Winmanager Integration {{{1
1063 let g:BufExplorer_title = "\[Buf\ List\]"
1064 call s:BESet("g:bufExplorerResize", 1)
1065 call s:BESet("g:bufExplorerMaxHeight", 25) " Handles dynamic resizing of the window.
1067 " Function to start display. Set the mode to 'winmanager' for this buffer.
1068 " This is to figure out how this plugin was called. In a standalone fashion
1069 " or by winmanager.
1070 function! BufExplorer_Start()
1071   let b:displayMode = "winmanager"
1072   call StartBufExplorer("e")
1073 endfunction
1075 " Returns whether the display is okay or not.
1076 function! BufExplorer_IsValid()
1077   return 0
1078 endfunction
1080 " Handles dynamic refreshing of the window.
1081 function! BufExplorer_Refresh()
1082   let b:displayMode = "winmanager"
1083   call StartBufExplorer("e")
1084 endfunction
1086 function! BufExplorer_ReSize()
1087   if !g:bufExplorerResize
1088     return
1089   endif
1091   let nlines = min([line("$"), g:bufExplorerMaxHeight])
1093   exe nlines." wincmd _"
1095   " The following lines restore the layout so that the last file line is also
1096   " the last window line. Sometimes, when a line is deleted, although the
1097   " window size is exactly equal to the number of lines in the file, some of
1098   " the lines are pushed up and we see some lagging '~'s.
1099   let pres = getpos(".")
1101   exe $
1103   let _scr = &scrolloff
1104   let &scrolloff = 0
1106   normal! z-
1108   let &scrolloff = _scr
1110   call setpos(".", pres)
1111 endfunction
1113 " Default values {{{1
1114 call s:BESet("g:bufExplorerDefaultHelp", 1)           " Show default help?
1115 call s:BESet("g:bufExplorerDetailedHelp", 0)          " Show detailed help?
1116 call s:BESet("g:bufExplorerFindActive", 1)            " When selecting an active buffer, take you to the window where it is active?
1117 call s:BESet("g:bufExplorerReverseSort", 0)           " sort reverse?
1118 call s:BESet("g:bufExplorerShowDirectories", 1)       " (Dir's are added by commands like ':e .')
1119 call s:BESet("g:bufExplorerShowRelativePath", 0)      " Show listings with relative or absolute paths?
1120 call s:BESet("g:bufExplorerShowUnlisted", 0)          " Show unlisted buffers?
1121 call s:BESet("g:bufExplorerSortBy", "mru")            " Sorting methods are in s:sort_by:
1122 call s:BESet("g:bufExplorerSplitOutPathName", 1)      " Split out path and file name?
1123 call s:BESet("g:bufExplorerSplitRight", &splitright)  " Should vertical splits be on the right or left of current window?
1124 call s:BESet("g:bufExplorerSplitBelow", &splitbelow)  " Should horizontal splits be below or above current window?
1125 call s:BESet("g:bufExplorerShowTabBuffer", 0)         " Show only buffer(s) for this tab?
1126 call s:BESet("g:bufExplorerOnlyOneTab", 1)            " If ShowTabBuffer = 1, only store the most recent tab for this buffer.
1128 " Global variables {{{1
1129 call s:BEReset()
1130 let s:running = 0
1131 let s:sort_by = ["number", "name", "fullpath", "mru", "extension"]
1132 let s:types = {"fullname": ':p', "path": ':p:h', "relativename": ':~:.', "relativepath": ':~:.:h', "shortname": ':t'}
1133 let s:originBuffer = 0
1134 let s:splitMode = ""
1135 let s:name = '[BufExplorer]'
1136 let s:refreshBufferList = 1
1137 let s:MRU_Exclude_List = ["[BufExplorer]","__MRU_Files__"]
1138 "1}}}
1140 " vim:ft=vim foldmethod=marker sw=2