Clipboard plugin.
[profile.git] / .vim / plugin / bufexplorer.vim
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: Friday, 22 October 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.
17 "
18 "               You may use the default keymappings of
19 "
20 "                 <Leader>be  - Opens BE.
21 "                 <Leader>bs  - Opens horizontally window BE.
22 "                 <Leader>bv  - Opens vertically window BE.
23 "
24 "               Or you can use
25 "
26 "                 ":BufExplorer"                - Opens BE.
27 "                 ":BufExplorerHorizontalSplit" - Opens horizontally window BE.
28 "                 ":BufExplorerVerticalSplit"   - Opens vertically window BE.
29 "
30 "               For more help see supplied documentation.
31 "      History: See supplied documentation.
32 "==============================================================================
33
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}}}
39
40 " Version number
41 let g:bufexplorer_version = "7.2.8"
42
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
48
49 " Public Interface {{{1
50 if maparg("<Leader>be") =~ 'BufExplorer'
51   nunmap <Leader>be
52 endif
53
54 if maparg("<Leader>bs") =~ 'BufExplorerHorizontalSplit'
55   nunmap <Leader>bs
56 endif
57
58 if maparg("<Leader>bv") =~ 'BufExplorerVerticalSplit'
59   nunmap <Leader>bv
60 endif
61
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>
65
66 " Create commands {{{1
67 command! BufExplorer :call StartBufExplorer(has ("gui") ? "drop" : "hide edit")
68 command! BufExplorerHorizontalSplit :call BufExplorerHorizontalSplit()
69 command! BufExplorerVerticalSplit :call BufExplorerVerticalSplit()
70
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
79
80     return 1
81   endif
82
83   return 0
84 endfunction
85
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('$'))
91
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
98
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
105
106 " BESetup {{{1
107 function! s:BESetup()
108   call s:BEReset()
109
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()
117
118     autocmd BufWipeOut * call s:BEDeactivateBuffer(1)
119     autocmd BufDelete * call s:BEDeactivateBuffer(0)
120
121     autocmd BufWinEnter \[BufExplorer\] call s:BEInitialize()
122     autocmd BufWinLeave \[BufExplorer\] call s:BECleanup()
123     autocmd SessionLoadPost * call s:BEReset()
124   augroup END
125
126   " Remove the VimEnter event as it is no longer needed
127   augroup SelectBufVimEnter
128     autocmd!
129   augroup END
130 endfunction
131
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
139
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.
152
153     " There are some checks which can be performed 
154     " before deciding to refresh the buffer list.
155     let bufnr = expand('<abuf>') + 0
156
157     if s:BEIgnoreBuffer(bufnr) == 1
158       return 
159     else
160       let s:refreshBufferList = 1
161     endif
162   endif
163
164   call s:BEActivateBuffer()
165 endfunction
166
167 " ActivateBuffer {{{1
168 function! s:BEActivateBuffer()
169   let b = bufnr("%")
170   let l = get(s:tabSpace, tabpagenr(), [])
171
172   if s:BEIgnoreBuffer(b) == 1
173     return
174   endif
175
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
187
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
203
204   call s:BEMRUPush(b)
205
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\>'."'")
215
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
223
224 " BEDeactivateBuffer {{{1
225 function! s:BEDeactivateBuffer(remove)
226   let _bufnr = str2nr(expand("<abuf>"))
227
228   call s:BEMRUPop(_bufnr)
229
230   if a:remove
231     call s:BEDeleteBufferListing(_bufnr)
232   else
233     let s:refreshBufferList = 1
234   endif
235 endfunction
236
237 " BEMRUPop {{{1
238 function! s:BEMRUPop(buf)
239   call filter(s:MRUList, 'v:val != '.a:buf)
240 endfunction
241
242 " BEMRUPush {{{1
243 function! s:BEMRUPush(buf)
244   if s:BEIgnoreBuffer(a:buf) == 1
245     return
246   endif
247
248   " Remove the buffer number from the list if it already exists.
249   call s:BEMRUPop(a:buf)
250
251   " Add the buffer number to the head of the list.
252   call insert(s:MRUList,a:buf)
253 endfunction
254
255 " BEInitialize {{{1
256 function! s:BEInitialize()
257   let s:_insertmode = &insertmode
258   set noinsertmode
259
260   let s:_showcmd = &showcmd
261   set noshowcmd
262
263   let s:_cpo = &cpo
264   set cpo&vim
265
266   let s:_report = &report
267   let &report = 10000
268
269   let s:_list = &list
270   set nolist
271
272   setlocal nonumber
273   setlocal foldcolumn=0
274   setlocal nofoldenable
275   setlocal cursorline
276   setlocal nospell
277   setlocal nobuflisted
278
279   let s:running = 1
280 endfunction
281
282 " BEIgnoreBuffer 
283 function! s:BEIgnoreBuffer(buf)
284   " Check to see if this buffer should be ignore by BufExplorer.
285
286   " Skip temporary buffers with buftype set.
287   if empty(getbufvar(a:buf, "&buftype") == 0)
288     return 1
289   endif
290
291   " Skip unlisted buffers.
292   if buflisted(a:buf) == 0
293     return 1
294   endif
295
296   " Skip buffers with no name.
297   if empty(bufname(a:buf)) == 1
298     return 1
299   endif
300
301   " Do not add the BufExplorer window to the list.
302   if fnamemodify(bufname(a:buf), ":t") == s:name
303     call s:BEError("The buffer name was [".s:name."] so it was skipped.")
304     return 1
305   endif
306
307   if index(s:MRU_Exclude_List, bufname(a:buf)) >= 0
308     return 1
309   end
310
311   return 0 
312 endfunction
313
314 " BECleanup {{{1
315 function! s:BECleanup()
316   if exists("s:_insertmode")|let &insertmode = s:_insertmode|endif
317   if exists("s:_showcmd")   |let &showcmd    = s:_showcmd   |endif
318   if exists("s:_cpo")       |let &cpo        = s:_cpo       |endif
319   if exists("s:_report")    |let &report     = s:_report    |endif
320   if exists("s:_list")      |let &list       = s:_list      |endif
321   let s:running   = 0
322   let s:splitMode = ""
323
324   delmarks!
325 endfunction
326
327 " BufExplorerHorizontalSplit {{{1
328 function! BufExplorerHorizontalSplit()
329   let s:splitMode = "sp"
330   exec "BufExplorer"
331 endfunction
332
333 " BufExplorerVerticalSplit {{{1
334 function! BufExplorerVerticalSplit()
335   let s:splitMode = "vsp"
336   exec "BufExplorer"
337 endfunction
338
339 " StartBufExplorer {{{1
340 function! StartBufExplorer(open)
341     let name = s:name
342
343     if !has("win32")
344         " On non-Windows boxes, escape the name so that is shows up correctly.
345         let name = escape(name, "[]")
346         call s:BEError("Escaped")
347     endif
348
349     " Make sure there is only one explorer open at a time.
350     if s:running == 1
351         call s:BEError("WHAT THE 1")
352         " Go to the open buffer.
353         if has("gui")
354             call s:BEError("WHAT THE 2")
355             call s:BEError(name)
356             exec "drop" name
357         endif
358
359         return
360     endif
361
362     " Add zero to ensure the variable is treated as a Number.
363     let s:originBuffer = bufnr("%") + 0
364
365     " Create or rebuild the raw buffer list if necessary.
366     if !exists('s:raw_buffer_listing') || 
367             \ empty(s:raw_buffer_listing) ||
368             \ s:refreshBufferList == 1
369         silent let s:raw_buffer_listing = s:BEGetBufferInfo(0)
370     endif
371
372     let copy = copy(s:raw_buffer_listing)
373
374     if (g:bufExplorerShowUnlisted == 0)
375         call filter(copy, 'v:val.attributes !~ "u"')
376     endif
377
378   " We may have to split the current window.
379   if (s:splitMode != "")
380     " Save off the original settings.
381     let [_splitbelow, _splitright] = [&splitbelow, &splitright]
382
383     " Set the setting to ours.
384     let [&splitbelow, &splitright] = [g:bufExplorerSplitBelow, g:bufExplorerSplitRight]
385
386     " Do it.
387     exe 'keepalt '.s:splitMode
388
389     " Restore the original settings.
390     let [&splitbelow, &splitright] = [_splitbelow, _splitright]
391   endif
392
393   if !exists("b:displayMode") || b:displayMode != "winmanager"
394     " Do not use keepalt when opening bufexplorer to allow the buffer that we are
395     " leaving to become the new alternate buffer
396     exec "silent keepjumps ".a:open." ".name
397   endif
398
399   call s:BEDisplayBufferList()
400 endfunction
401
402 " BEDisplayBufferList {{{1
403 function! s:BEDisplayBufferList()
404   " Do not set bufhidden since it wipes out 
405   " the data if we switch away from the buffer 
406   " using CTRL-^
407   setlocal buftype=nofile
408   setlocal modifiable
409   setlocal noswapfile
410   setlocal nowrap
411
412   " Delete all previous lines to the black hole register
413   call cursor(1,1)
414   exec 'silent! normal! "_dG'
415
416   call s:BESetupSyntax()
417   call s:BEMapKeys()
418   call setline(1, s:BECreateHelp())
419   call s:BEBuildBufferList()
420   call cursor(s:firstBufferLine, 1)
421
422   if !g:bufExplorerResize
423     normal! zz
424   endif
425
426   setlocal nomodifiable
427 endfunction
428
429 " BEMapKeys {{{1
430 function! s:BEMapKeys()
431   if exists("b:displayMode") && b:displayMode == "winmanager"
432     nnoremap <buffer> <silent> <tab> :call <SID>BESelectBuffer("tab")<cr>
433   endif
434
435   nnoremap <buffer> <silent> <F1>          :call <SID>BEToggleHelp()<cr>
436   nnoremap <buffer> <silent> <2-leftmouse> :call <SID>BESelectBuffer()<cr>
437   nnoremap <buffer> <silent> <cr>          :call <SID>BESelectBuffer()<cr>
438   nnoremap <buffer> <silent> o             :call <SID>BESelectBuffer()<cr>
439   nnoremap <buffer> <silent> t             :call <SID>BESelectBuffer("tab")<cr>
440   nnoremap <buffer> <silent> <s-cr>        :call <SID>BESelectBuffer("tab")<cr>
441
442   nnoremap <buffer> <silent> d             :call <SID>BERemoveBuffer("delete", "n")<cr>
443   xnoremap <buffer> <silent> d             :call <SID>BERemoveBuffer("delete", "v")<cr>
444   nnoremap <buffer> <silent> D             :call <SID>BERemoveBuffer("wipe", "n")<cr>
445   xnoremap <buffer> <silent> D             :call <SID>BERemoveBuffer("wipe", "v")<cr>
446
447   nnoremap <buffer> <silent> m             :call <SID>BEMRUListShow()<cr>
448   nnoremap <buffer> <silent> p             :call <SID>BEToggleSplitOutPathName()<cr>
449   nnoremap <buffer> <silent> q             :call <SID>BEClose("quit")<cr>
450   nnoremap <buffer> <silent> r             :call <SID>BESortReverse()<cr>
451   nnoremap <buffer> <silent> R             :call <SID>BEToggleShowRelativePath()<cr>
452   nnoremap <buffer> <silent> s             :call <SID>BESortSelect()<cr>
453   nnoremap <buffer> <silent> S             :call <SID>BEReverseSortSelect()<cr>
454   nnoremap <buffer> <silent> u             :call <SID>BEToggleShowUnlisted()<cr>
455   nnoremap <buffer> <silent> f             :call <SID>BEToggleFindActive()<cr>
456   nnoremap <buffer> <silent> T             :call <SID>BEToggleShowTabBuffer()<cr>
457   nnoremap <buffer> <silent> B             :call <SID>BEToggleOnlyOneTab()<cr>
458
459   for k in ["G", "n", "N", "L", "M", "H"]
460     exec "nnoremap <buffer> <silent>" k ":keepjumps normal!" k."<cr>"
461   endfor
462 endfunction
463
464 " BESetupSyntax {{{1
465 function! s:BESetupSyntax()
466   if has("syntax")
467     syn match bufExplorerHelp         "^\".*" contains=bufExplorerSortBy,bufExplorerMapping,bufExplorerTitle,bufExplorerSortType,bufExplorerToggleSplit,bufExplorerToggleOpen
468     syn match bufExplorerOpenIn       "Open in \w\+ window" contained
469     syn match bufExplorerSplit        "\w\+ split" contained
470     syn match bufExplorerSortBy       "Sorted by .*" contained contains=bufExplorerOpenIn,bufExplorerSplit
471     syn match bufExplorerMapping      "\" \zs.\+\ze :" contained
472     syn match bufExplorerTitle        "Buffer Explorer.*" contained
473     syn match bufExplorerSortType     "'\w\{-}'" contained
474     syn match bufExplorerBufNbr       /^\s*\d\+/
475     syn match bufExplorerToggleSplit  "toggle split type" contained
476     syn match bufExplorerToggleOpen   "toggle open mode" contained
477     syn match bufExplorerModBuf       /^\s*\d\+.\{4}+.*/
478     syn match bufExplorerLockedBuf    /^\s*\d\+.\{3}[\-=].*/
479     syn match bufExplorerHidBuf       /^\s*\d\+.\{2}h.*/
480     syn match bufExplorerActBuf       /^\s*\d\+.\{2}a.*/
481     syn match bufExplorerCurBuf       /^\s*\d\+.%.*/
482     syn match bufExplorerAltBuf       /^\s*\d\+.#.*/
483     syn match bufExplorerUnlBuf       /^\s*\d\+u.*/
484
485     hi def link bufExplorerBufNbr Number
486     hi def link bufExplorerMapping NonText
487     hi def link bufExplorerHelp Special
488     hi def link bufExplorerOpenIn Identifier
489     hi def link bufExplorerSortBy String
490     hi def link bufExplorerSplit NonText
491     hi def link bufExplorerTitle NonText
492     hi def link bufExplorerSortType bufExplorerSortBy
493     hi def link bufExplorerToggleSplit bufExplorerSplit
494     hi def link bufExplorerToggleOpen bufExplorerOpenIn
495
496     hi def link bufExplorerActBuf Identifier
497     hi def link bufExplorerAltBuf String
498     hi def link bufExplorerCurBuf Type
499     hi def link bufExplorerHidBuf Constant
500     hi def link bufExplorerLockedBuf Special
501     hi def link bufExplorerModBuf Exception
502     hi def link bufExplorerUnlBuf Comment
503   endif
504 endfunction
505
506 " BEToggleHelp {{{1
507 function! s:BEToggleHelp()
508   let g:bufExplorerDetailedHelp = !g:bufExplorerDetailedHelp
509
510   setlocal modifiable
511
512   " Save position.
513   normal! ma
514
515   " Remove old header.
516   if (s:firstBufferLine > 1)
517     exec "keepjumps 1,".(s:firstBufferLine - 1) "d _"
518   endif
519
520   call append(0, s:BECreateHelp())
521
522   silent! normal! g`a
523   delmarks a
524
525   setlocal nomodifiable
526
527   if exists("b:displayMode") && b:displayMode == "winmanager"
528     call WinManagerForceReSize("BufExplorer")
529   endif
530 endfunction
531
532 " BEGetHelpStatus {{{1
533 function! s:BEGetHelpStatus()
534   let ret = '" Sorted by '.((g:bufExplorerReverseSort == 1) ? "reverse " : "").g:bufExplorerSortBy
535   let ret .= ' | '.((g:bufExplorerFindActive == 0) ? "Don't " : "")."Locate buffer"
536   let ret .= ((g:bufExplorerShowUnlisted == 0) ? "" : " | Show unlisted")
537   let ret .= ((g:bufExplorerShowTabBuffer == 0) ? "" : " | Show buffers/tab")
538   let ret .= ((g:bufExplorerOnlyOneTab == 1) ? "" : " | One tab / buffer")
539   let ret .= ' | '.((g:bufExplorerShowRelativePath == 0) ? "Absolute" : "Relative")
540   let ret .= ' '.((g:bufExplorerSplitOutPathName == 0) ? "Full" : "Split")." path"
541
542   return ret
543 endfunction
544
545 " BECreateHelp {{{1
546 function! s:BECreateHelp()
547   if g:bufExplorerDefaultHelp == 0 && g:bufExplorerDetailedHelp == 0
548     let s:firstBufferLine = 1
549     return []
550   endif
551
552   let header = []
553
554   if g:bufExplorerDetailedHelp == 1
555     call add(header, '" Buffer Explorer ('.g:bufexplorer_version.')')
556     call add(header, '" --------------------------')
557     call add(header, '" <F1> : toggle this help')
558     call add(header, '" <enter> or o or Mouse-Double-Click : open buffer under cursor')
559     call add(header, '" <shift-enter> or t : open buffer in another tab')
560     call add(header, '" d : delete buffer')
561     call add(header, '" D : wipe buffer')
562     call add(header, '" f : toggle find active buffer')
563     call add(header, '" p : toggle spliting of file and path name')
564     call add(header, '" q : quit')
565     call add(header, '" r : reverse sort')
566     call add(header, '" R : toggle showing relative or full paths')
567     call add(header, '" s : cycle thru "sort by" fields '.string(s:sort_by).'')
568     call add(header, '" S : reverse cycle thru "sort by" fields')
569     call add(header, '" T : toggle if to show only buffers for this tab or not')
570     call add(header, '" u : toggle showing unlisted buffers')
571   else
572     call add(header, '" Press <F1> for Help')
573   endif
574
575   if (!exists("b:displayMode") || b:displayMode != "winmanager") || (b:displayMode == "winmanager" && g:bufExplorerDetailedHelp == 1)
576     call add(header, s:BEGetHelpStatus())
577     call add(header, '"=')
578   endif
579
580   let s:firstBufferLine = len(header) + 1
581
582   return header
583 endfunction
584
585 " BEGetBufferInfo {{{1
586 function! s:BEGetBufferInfo(bufnr)
587   redir => bufoutput
588   buffers!
589   redir END
590
591   if (a:bufnr > 0)
592     " Since we are only interested in this specified buffer 
593     " remove the other buffers listed
594     let bufoutput = substitute(bufoutput."\n", '^.*\n\(\s*'.a:bufnr.'\>.\{-}\)\n.*', '\1', '')
595   endif
596
597   let [all, allwidths, listedwidths] = [[], {}, {}]
598
599   for n in keys(s:types)
600     let allwidths[n] = []
601     let listedwidths[n] = []
602   endfor
603
604   for buf in split(bufoutput, '\n')
605     let bits = split(buf, '"')
606     let b = {"attributes": bits[0], "line": substitute(bits[2], '\s*', '', '')}
607
608     for [key, val] in items(s:types)
609       let b[key] = fnamemodify(bits[1], val)
610     endfor
611
612     if getftype(b.fullname) == "dir" && g:bufExplorerShowDirectories == 1
613       let b.shortname = "<DIRECTORY>"
614     endif
615
616     call add(all, b)
617
618     for n in keys(s:types)
619       call add(allwidths[n], len(b[n]))
620
621       if b.attributes !~ "u"
622         call add(listedwidths[n], len(b[n]))
623       endif
624     endfor
625   endfor
626
627   let [s:allpads, s:listedpads] = [{}, {}]
628
629   for n in keys(s:types)
630     let s:allpads[n] = repeat(' ', max(allwidths[n]))
631     let s:listedpads[n] = repeat(' ', max(listedwidths[n]))
632   endfor
633
634   let s:refreshBufferList = 1
635
636   return all
637 endfunction
638
639 " BEBuildBufferList {{{1
640 function! s:BEBuildBufferList()
641     let lines = []
642
643     " Loop through every buffer.
644     for buf in s:raw_buffer_listing
645         " Skip unlisted buffers if we are not to show them.
646         if (!g:bufExplorerShowUnlisted && buf.attributes =~ "u")
647             continue
648         endif
649
650         if (g:bufExplorerShowTabBuffer)
651             let show_buffer = 0
652
653             for bufnr in s:tabSpace[tabpagenr()]
654                 if (buf.attributes =~ '^\s*'.bufnr.'\>')
655                     " Only buffers shown on the current tabpagenr
656                     let show_buffer = 1
657                     break
658                 endif
659             endfor
660
661             if show_buffer == 0 
662                 continue
663             endif
664         endif
665
666         let line = buf.attributes." "
667
668         if g:bufExplorerSplitOutPathName
669             let type = (g:bufExplorerShowRelativePath) ? "relativepath" : "path"
670             let path = buf[type]
671             let pad  = (g:bufExplorerShowUnlisted) ? s:allpads.shortname : s:listedpads.shortname
672             let line .= buf.shortname." ".strpart(pad.path, len(buf.shortname))
673         else
674             let type = (g:bufExplorerShowRelativePath) ? "relativename" : "fullname"
675             let path = buf[type]
676             let line .= path
677         endif
678
679         let pads = (g:bufExplorerShowUnlisted) ? s:allpads : s:listedpads
680
681         if !empty(pads[type])
682             let line .= strpart(pads[type], len(path))." "
683         endif
684
685         let line .= buf.line
686
687         call add(lines, line)
688     endfor
689
690     call setline(s:firstBufferLine, lines)
691
692     call s:BESortListing()
693 endfunction
694
695 " BESelectBuffer {{{1
696 function! s:BESelectBuffer(...)
697   " Sometimes messages are not cleared when we get here so it looks like an error has
698   " occurred when it really has not.
699   echo ""
700
701   " Are we on a line with a file name?
702   if line('.') < s:firstBufferLine
703     exec "normal! \<cr>"
704     return
705   endif
706
707   let _bufNbr = str2nr(getline('.'))
708
709   " Check and see if we are running BE via WinManager.
710   if exists("b:displayMode") && b:displayMode == "winmanager"
711     let bufname = expand("#"._bufNbr.":p")
712
713     if (a:0 == 1) && (a:1 == "tab")
714       call WinManagerFileEdit(bufname, 1)
715     else
716       call WinManagerFileEdit(bufname, 0)
717     endif
718  
719     return
720   endif
721
722   if bufexists(_bufNbr)
723     if bufnr("#") == _bufNbr && !exists("g:bufExplorerChgWin")
724       return s:BEClose("")
725     endif
726
727     " Are we suppose to open the selected buffer in a tab?
728     if (a:0 == 1) && (a:1 == "tab")
729       " Yes, we are to open the selected buffer in a tab.
730
731       " Restore [BufExplorer] buffer.
732       exec "keepjumps silent buffer!".s:originBuffer
733
734       " Get the tab number where this buffer is located at.
735       let tabNbr = s:BEGetTabNbr(_bufNbr)
736
737       " Was the tab found?
738       if tabNbr == 0
739         " _bufNbr is not opened in any tabs. Open a new tab with the selected buffer in it.
740         exec "999tab split +buffer" . _bufNbr
741       else
742         " The _bufNbr is already opened in tab, go to that tab.
743         exec tabNbr . "tabnext"
744
745         " Focus window.
746         exec s:BEGetWinNbr(tabNbr, _bufNbr) . "wincmd w"
747       endif
748     else
749         "No, the use did not ask to open the selected buffer in a tab.
750
751         " Are we suppose to move to the tab where this active buffer is?
752         if exists("g:bufExplorerChgWin")
753                 exe g:bufExplorerChgWin."wincmd w"
754             elseif bufloaded(_bufNbr) && g:bufExplorerFindActive
755             " Close the BE window.
756             call s:BEClose("")
757
758         " Get the tab number where this buffer is located at.
759         let tabNbr = s:BEGetTabNbr(_bufNbr)
760
761         " Was the tab found?
762         if tabNbr != 0
763           " The buffer is located in a tab. Go to that tab number.
764           exec tabNbr . "tabnext"
765         else
766           " Nope, the buffer is not in a tab, simple switch to that buffer.
767           let bufname = expand("#"._bufNbr.":p")
768           exec bufname ? "drop ".escape(bufname, " ") : "buffer "._bufNbr
769         endif
770       endif
771
772       " Switch to the buffer.
773       exec "keepalt keepjumps silent b!" _bufNbr
774     endif
775
776     " Make the buffer 'listed' again.
777     call setbufvar(_bufNbr, "&buflisted", "1")
778  
779         " call any associated function references
780         " g:bufExplorerFuncRef may be an individual function reference
781         "                or it may be a list containing function references.
782         " It will ignore anything that's not a function reference.
783         " See  :help FuncRef  for more on function references.
784         if exists("g:BufExplorerFuncRef")
785           if type(g:BufExplorerFuncRef) == 2
786             keepj call g:BufExplorerFuncRef()
787           elseif type(g:BufExplorerFuncRef) == 3
788             for FncRef in g:BufExplorerFuncRef
789               if type(FncRef) == 2
790                     keepj call FncRef()
791               endif
792             endfor
793           endif
794     endif
795   else
796     call s:BEError("Sorry, that buffer no longer exists, please select another")
797     call s:BEDeleteBuffer(_bufNbr, "wipe")
798   endif
799 endfunction
800
801 " BEDeleteBufferListing {{{1
802 function! s:BEDeleteBufferListing(buf)
803     if exists('s:raw_buffer_listing') && !empty(s:raw_buffer_listing)
804         " Delete the buffer from the raw buffer list.
805         " Careful use of ' and " so we do not have to escape all the \'s
806         " Regex: ^\s*\(10\|20\)\>
807         "        ^ - Starting at the beginning of the string
808         "        \s* - optional whitespace
809         "        \(10\|20\) - either a 10 or a 20
810         "        \> - end of word (so it can't make 100 or 201)
811         call filter(s:raw_buffer_listing, "v:val.attributes !~ '".'^\s*\('.substitute(a:buf, ' ', '\\|', 'g').'\)\>'."'")
812     endif
813 endfunction
814
815 " BERemoveBuffer {{{1
816 function! s:BERemoveBuffer(type, mode) range
817     " Are we on a line with a file name?
818     if line('.') < s:firstBufferLine
819         return
820     endif
821
822     " These commands are to temporarily suspend the activity of winmanager.
823     if exists("b:displayMode") && b:displayMode == "winmanager"
824         call WinManagerSuspendAUs()
825     endif
826
827     let _bufNbrs = ''
828
829     for lineNum in range(a:firstline, a:lastline)
830         let line = getline(lineNum)
831
832         if line =~ '^\s*\(\d\+\)'
833             " Regex: ^\s*\(10\|20\)\>
834             "        ^ - Starting at the beginning of the string
835             "        \s* - optional whitespace
836             "        \zs - start the match here
837             "        \d\+ - any digits
838             "        \> - end of word (so it can't make 100 or 201)
839             let bufNbr = matchstr(line, '^\s*\zs\d\+\>')
840
841             " Add 0 to bufNbr to ensure Vim treats it as a Number
842             " for use with the getbufvar() function
843             if bufNbr !~ '^\d\+$' || getbufvar(bufNbr+0, '&modified') != 0
844                 call s:BEError("Sorry, no write since last change for buffer ".bufNbr.", unable to delete")
845             else
846                 let _bufNbrs = _bufNbrs . (_bufNbrs==''?'':' '). bufNbr 
847             endif
848         endif
849     endfor
850
851     " Okay, everything is good, delete or wipe the buffers.
852     call s:BEDeleteBuffer(_bufNbrs, a:type)
853
854     " Reactivate winmanager autocommand activity.
855     if exists("b:displayMode") && b:displayMode == "winmanager"
856         call WinManagerForceReSize("BufExplorer")
857         call WinManagerResumeAUs()
858     endif
859 endfunction
860
861 " BEDeleteBuffer {{{1
862 function! s:BEDeleteBuffer(bufNbr, mode)
863     " This routine assumes that the buffer to be removed is on the current line.
864     try
865         if a:mode == "wipe"
866             exe "bwipe" a:bufNbr
867         else
868             exe "bdelete" a:bufNbr
869         endif
870
871         setlocal modifiable
872
873         " Remove each of the lines beginning with the buffer numbers we are removing
874         " Regex: ^\s*\(10\|20\)\>
875         "        ^ - Starting at the beginning of the string
876         "        \s* - optional whitespace
877         "        \(10\|20\) - either a 10 or a 20
878         "        \> - end of word (so it can't make 100 or 201)
879         exec 'silent! g/^\s*\('.substitute(a:bufNbr, ' ', '\\|', 'g').'\)\>/d_'
880
881         setlocal nomodifiable
882
883         call s:BEDeleteBufferListing(a:bufNbr)
884     catch
885         call s:BEError(v:exception)
886     endtry
887 endfunction
888
889 " BEClose {{{1
890 function! s:BEClose(mode)
891     " Get only the listed buffers.
892     let listed = filter(copy(s:MRUList), "buflisted(v:val)")
893
894     " If we needed to split the main window, close the split one.
895 "  if (s:splitMode)
896 "  if (s:splitMode != "")
897     if (s:splitMode != "" && a:mode == "quit")
898         exec "wincmd c"
899     endif
900
901     if len(listed) == 0
902         exe "enew"
903     else
904         for b in reverse(listed[0:1])
905             exec "keepjumps silent b ".b
906         endfor
907     endif
908 endfunction
909
910 " BEToggleSplitOutPathName {{{1
911 function! s:BEToggleSplitOutPathName()
912     let g:bufExplorerSplitOutPathName = !g:bufExplorerSplitOutPathName
913     call s:BERebuildBufferList()
914     call s:BEUpdateHelpStatus()
915 endfunction
916
917 " BEToggleShowRelativePath {{{1
918 function! s:BEToggleShowRelativePath()
919     let g:bufExplorerShowRelativePath = !g:bufExplorerShowRelativePath
920     call s:BERebuildBufferList()
921     call s:BEUpdateHelpStatus()
922 endfunction
923
924 " BEToggleShowUnlisted {{{1
925 function! s:BEToggleShowUnlisted()
926     let g:bufExplorerShowUnlisted = !g:bufExplorerShowUnlisted
927     let num_bufs = s:BERebuildBufferList(g:bufExplorerShowUnlisted == 0)
928     call s:BEUpdateHelpStatus()
929 endfunction
930
931 " BEToggleFindActive {{{1
932 function! s:BEToggleFindActive()
933     let g:bufExplorerFindActive = !g:bufExplorerFindActive
934     call s:BEUpdateHelpStatus()
935 endfunction
936
937 " BEToggleShowTabBuffer {{{1
938 function! s:BEToggleShowTabBuffer()
939     let g:bufExplorerShowTabBuffer = !g:bufExplorerShowTabBuffer
940     call s:BEDisplayBufferList()
941 endfunction
942
943 " BEToggleOnlyOneTab {{{1
944 function! s:BEToggleOnlyOneTab()
945     let g:bufExplorerOnlyOneTab = !g:bufExplorerOnlyOneTab
946     call s:BEDisplayBufferList()
947 endfunction
948
949 " BERebuildBufferList {{{1
950 function! s:BERebuildBufferList(...)
951     setlocal modifiable
952
953     let curPos = getpos('.')
954
955     if a:0
956         " Clear the list first.
957         exec "keepjumps ".s:firstBufferLine.',$d "_'
958     endif
959
960     let num_bufs = s:BEBuildBufferList()
961
962     call setpos('.', curPos)
963
964     setlocal nomodifiable
965
966     return num_bufs
967 endfunction
968
969 " BEUpdateHelpStatus {{{1
970 function! s:BEUpdateHelpStatus()
971     setlocal modifiable
972
973     let text = s:BEGetHelpStatus()
974     call setline(s:firstBufferLine - 2, text)
975
976     setlocal nomodifiable
977 endfunction
978
979 " BEMRUCmp {{{1
980 function! s:BEMRUCmp(line1, line2)
981     return index(s:MRUList, str2nr(a:line1)) - index(s:MRUList, str2nr(a:line2))
982 endfunction
983
984 " BESortReverse {{{1
985 function! s:BESortReverse()
986     let g:bufExplorerReverseSort = !g:bufExplorerReverseSort
987     call s:BEReSortListing()
988 endfunction
989
990 " BESortSelect {{{1
991 function! s:BESortSelect()
992     let g:bufExplorerSortBy = get(s:sort_by, index(s:sort_by, g:bufExplorerSortBy) + 1, s:sort_by[0])
993     call s:BEReSortListing()
994 endfunction
995
996 " BEReverseSortSelect {{{1
997 function! s:BEReverseSortSelect()
998     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])
999     call s:BEReSortListing()
1000 endfunction
1001
1002 " BEReSortListing {{{1
1003 function! s:BEReSortListing()
1004     setlocal modifiable
1005
1006     let curPos = getpos('.')
1007
1008     call s:BESortListing()
1009     call s:BEUpdateHelpStatus()
1010
1011     call setpos('.', curPos)
1012
1013     setlocal nomodifiable
1014 endfunction
1015
1016 " BESortListing {{{1
1017 function! s:BESortListing()
1018   let sort = s:firstBufferLine.",$sort".((g:bufExplorerReverseSort == 1) ? "!": "")
1019
1020   if g:bufExplorerSortBy == "number"
1021     " Easiest case.
1022     exec sort 'n'
1023   elseif g:bufExplorerSortBy == "name"
1024     if g:bufExplorerSplitOutPathName
1025       exec sort 'ir /\d.\{7}\zs\f\+\ze/'
1026     else
1027       exec sort 'ir /\zs[^\/\\]\+\ze\s*line/'
1028     endif
1029   elseif g:bufExplorerSortBy == "fullpath"
1030     if g:bufExplorerSplitOutPathName
1031       " Sort twice - first on the file name then on the path.
1032       exec sort 'ir /\d.\{7}\zs\f\+\ze/'
1033     endif
1034
1035     exec sort 'ir /\zs\f\+\ze\s\+line/'
1036   elseif g:bufExplorerSortBy == "extension"
1037     exec sort 'ir /\.\zs\w\+\ze\s/'
1038   elseif g:bufExplorerSortBy == "mru"
1039     let l = getline(s:firstBufferLine, "$")
1040
1041     call sort(l, "<SID>BEMRUCmp")
1042
1043     if g:bufExplorerReverseSort
1044       call reverse(l)
1045     endif
1046
1047     call setline(s:firstBufferLine, l)
1048   endif
1049 endfunction
1050
1051 " BEMRUListShow {{{1
1052 function! s:BEMRUListShow()
1053     echomsg "MRUList=".string(s:MRUList)
1054 endfunction
1055
1056 " BEError {{{1
1057 function! s:BEError(msg)
1058     echohl ErrorMsg | echo a:msg | echohl none
1059 endfunction
1060
1061 " BEWarning {{{1
1062 function! s:BEWarning(msg)
1063     echohl WarningMsg | echo a:msg | echohl none
1064 endfunction
1065
1066 " GetTabNbr {{{1
1067 function! s:BEGetTabNbr(bufNbr)
1068     " Searching buffer bufno, in tabs.
1069     for i in range(tabpagenr("$"))
1070         if index(tabpagebuflist(i + 1), a:bufNbr) != -1
1071             return i + 1
1072         endif
1073     endfor
1074
1075     return 0
1076 endfunction
1077
1078 " GetWinNbr" {{{1
1079 function! s:BEGetWinNbr(tabNbr, bufNbr)
1080     " window number in tabpage.
1081     return index(tabpagebuflist(a:tabNbr), a:bufNbr) + 1
1082 endfunction
1083
1084 " Winmanager Integration {{{1
1085 let g:BufExplorer_title = "\[Buf\ List\]"
1086 call s:BESet("g:bufExplorerResize", 1)
1087 call s:BESet("g:bufExplorerMaxHeight", 25) " Handles dynamic resizing of the window.
1088
1089 " Function to start display. Set the mode to 'winmanager' for this buffer.
1090 " This is to figure out how this plugin was called. In a standalone fashion
1091 " or by winmanager.
1092 function! BufExplorer_Start()
1093     let b:displayMode = "winmanager"
1094     call StartBufExplorer("e")
1095 endfunction
1096
1097 " Returns whether the display is okay or not.
1098 function! BufExplorer_IsValid()
1099     return 0
1100 endfunction
1101
1102 " Handles dynamic refreshing of the window.
1103 function! BufExplorer_Refresh()
1104     let b:displayMode = "winmanager"
1105     call StartBufExplorer("e")
1106 endfunction
1107
1108 function! BufExplorer_ReSize()
1109   if !g:bufExplorerResize
1110     return
1111   endif
1112
1113   let nlines = min([line("$"), g:bufExplorerMaxHeight])
1114
1115   exe nlines." wincmd _"
1116
1117   " The following lines restore the layout so that the last file line is also
1118   " the last window line. Sometimes, when a line is deleted, although the
1119   " window size is exactly equal to the number of lines in the file, some of
1120   " the lines are pushed up and we see some lagging '~'s.
1121   let pres = getpos(".")
1122
1123   exe $
1124
1125   let _scr = &scrolloff
1126   let &scrolloff = 0
1127
1128   normal! z-
1129
1130   let &scrolloff = _scr
1131
1132   call setpos(".", pres)
1133 endfunction
1134
1135 " Default values {{{1
1136 call s:BESet("g:bufExplorerDefaultHelp", 1)           " Show default help?
1137 call s:BESet("g:bufExplorerDetailedHelp", 0)          " Show detailed help?
1138 call s:BESet("g:bufExplorerFindActive", 1)            " When selecting an active buffer, take you to the window where it is active?
1139 call s:BESet("g:bufExplorerReverseSort", 0)           " sort reverse?
1140 call s:BESet("g:bufExplorerShowDirectories", 1)       " (Dir's are added by commands like ':e .')
1141 call s:BESet("g:bufExplorerShowRelativePath", 0)      " Show listings with relative or absolute paths?
1142 call s:BESet("g:bufExplorerShowUnlisted", 0)          " Show unlisted buffers?
1143 call s:BESet("g:bufExplorerSortBy", "mru")            " Sorting methods are in s:sort_by:
1144 call s:BESet("g:bufExplorerSplitOutPathName", 1)      " Split out path and file name?
1145 call s:BESet("g:bufExplorerSplitRight", &splitright)  " Should vertical splits be on the right or left of current window?
1146 call s:BESet("g:bufExplorerSplitBelow", &splitbelow)  " Should horizontal splits be below or above current window?
1147 call s:BESet("g:bufExplorerShowTabBuffer", 0)         " Show only buffer(s) for this tab?
1148 call s:BESet("g:bufExplorerOnlyOneTab", 1)            " If ShowTabBuffer = 1, only store the most recent tab for this buffer.
1149
1150 " Global variables {{{1
1151 call s:BEReset()
1152 let s:running = 0
1153 let s:sort_by = ["number", "name", "fullpath", "mru", "extension"]
1154 let s:types = {"fullname": ':p', "path": ':p:h', "relativename": ':~:.', "relativepath": ':~:.:h', "shortname": ':t'}
1155 let s:originBuffer = 0
1156 let s:splitMode = ""
1157 let s:name = '[BufExplorer]'
1158 let s:refreshBufferList = 1
1159 let s:MRU_Exclude_List = ["[BufExplorer]","__MRU_Files__"]
1160 "1}}}
1161
1162 " vim:ft=vim foldmethod=marker sw=2