Pentadactyl fixes.
[profile.git] / .vim / plugin / bufexplorer.vim
1 "=============================================================================
2 "    Copyright: Copyright (C) 2001-2008 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: Wednesday, 19 Nov 2008
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 BufExplorer
21 "                 <Leader>bs  - Opens horizontally split window BufExplorer
22 "                 <Leader>bv  - Opens vertically split window BufExplorer
23 "
24 "               Or you can use
25 "
26 "                 ":BufExplorer" - Opens BufExplorer
27 "                 ":HSBufExplorer" - Opens horizontally window BufExplorer
28 "                 ":VSBufExplorer" - Opens vertically split window BufExplorer
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.2"
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 nmap <silent> <unique> <Leader>be :BufExplorer<CR>
51 nmap <silent> <unique> <Leader>bs :HSBufExplorer<CR>
52 nmap <silent> <unique> <Leader>bv :VSBufExplorer<CR>
53
54 " Create commands {{{1
55 command BufExplorer :call StartBufExplorer(has ("gui") ? "drop" : "hide edit")
56 command HSBufExplorer :call HorizontalSplitBufExplorer()
57 command VSBufExplorer :call VerticalSplitBufExplorer()
58
59 " Set {{{1
60 function s:Set(var, default)
61   if !exists(a:var)
62     if type(a:default)
63       exec "let" a:var "=" string(a:default)
64     else
65       exec "let" a:var "=" a:default
66     endif
67
68     return 1
69   endif
70
71   return 0
72 endfunction
73
74 " Default values {{{1
75 call s:Set("g:bufExplorerDefaultHelp", 1)           " Show default help?
76 call s:Set("g:bufExplorerDetailedHelp", 0)          " Show detailed help?
77 call s:Set("g:bufExplorerFindActive", 1)            " When selecting an active buffer, take you to the window where it is active?
78 call s:Set("g:bufExplorerReverseSort", 0)           " sort reverse?
79 call s:Set("g:bufExplorerShowDirectories", 1)       " (Dir's are added by commands like ':e .')
80 call s:Set("g:bufExplorerShowRelativePath", 0)      " Show listings with relative or absolute paths?
81 call s:Set("g:bufExplorerShowUnlisted", 0)          " Show unlisted buffers?
82 call s:Set("g:bufExplorerSortBy", "mru")            " Sorting methods are in s:sort_by:
83 call s:Set("g:bufExplorerSplitOutPathName", 1)      " Split out path and file name?
84 call s:Set("g:bufExplorerSplitRight", &splitright)  " Should vertical splits be on the right or left of current window?
85 call s:Set("g:bufExplorerSplitBelow", &splitbelow)  " Should horizontal splits be below or above current window?
86
87 " Global variables {{{1
88 let s:MRUList = []
89 let s:running = 0
90 let s:sort_by = ["number", "name", "fullpath", "mru", "extension"]
91 let s:tabSpace = []
92 let s:types = {"fullname": ':p', "path": ':p:h', "relativename": ':~:.', "relativepath": ':~:.:h', "shortname": ':t'}
93 let s:originBuffer = 0
94 let s:splitMode = ""
95
96 " Setup the autocommands that handle the MRUList and other stuff. {{{1
97 autocmd VimEnter * call s:Setup()
98
99 " Setup {{{1
100 function s:Setup()
101                                 " Build initial MRUList.
102   let s:MRUList = range(1, bufnr('$'))
103   let s:tabSpace = []
104                                 " Now that the MRUList is created, add the other autocmds.
105   autocmd BufEnter,BufNew * call s:ActivateBuffer()
106   autocmd BufWipeOut * call s:DeactivateBuffer(1)
107   autocmd BufDelete * call s:DeactivateBuffer(0)
108
109   autocmd BufWinEnter \[BufExplorer\] call s:Initialize()
110   autocmd BufWinLeave \[BufExplorer\] call s:Cleanup()
111 endfunction
112
113 " ActivateBuffer {{{1
114 function s:ActivateBuffer()
115   let b = bufnr("%")
116   let l = get(s:tabSpace, tabpagenr(), [])
117
118   if empty(l) || index(l, b) == -1
119     call add(l, b)
120     let s:tabSpace[tabpagenr()] = l
121   endif
122
123   call s:MRUPush(b)
124 endfunction
125
126 " DeactivateBuffer {{{1
127 function s:DeactivateBuffer(remove)
128   "echom "afile:" expand("<afile>")
129   "echom "bufnr, afile:" bufnr(expand("<afile>"))
130   "echom "buffers:" string(tabpagebuflist())
131   "echom "MRU before:" string(s:MRUList)
132
133   let _bufnr = bufnr(expand("<afile>"))
134   let _buftype = getbufvar(_bufnr, "&buftype")
135
136   if empty(_buftype) || _buftype == "nofile" || !buflisted(_bufnr) || empty(bufname(_bufnr)) || fnamemodify(bufname(_bufnr), ":t") == "[BufExplorer]"
137     return
138   end
139
140   if a:remove
141     call s:MRUPop(bufnr(expand("<afile>")))
142   end
143 endfunction
144
145 " MRUPop {{{1
146 function s:MRUPop(buf)
147   call filter(s:MRUList, 'v:val != '.a:buf)
148 endfunction
149
150 " MRUPush {{{1
151 function s:MRUPush(buf)
152                                 " Skip temporary buffer with buftype set. Don't add the BufExplorer window to the
153                                 " list.
154   if !empty(getbufvar(a:buf, "&buftype")) ||
155       \ !buflisted(a:buf) || empty(bufname(a:buf)) ||
156       \ fnamemodify(bufname(a:buf), ":t") == "[BufExplorer]"
157     return
158   end
159                                 " Remove the buffer number from the list if it already exists.
160   call s:MRUPop(a:buf)
161                                 " Add the buffer number to the head of the list.
162   call insert(s:MRUList,a:buf)
163 endfunction
164
165 " Initialize {{{1
166 function s:Initialize()
167   let s:_insertmode = &insertmode
168   set noinsertmode
169
170   let s:_showcmd = &showcmd
171   set noshowcmd
172
173   let s:_cpo = &cpo
174   set cpo&vim
175
176   let s:_report = &report
177   let &report = 10000
178
179   let s:_list = &list
180   set nolist
181
182   setlocal nonumber
183   setlocal foldcolumn=0
184   setlocal nofoldenable
185   setlocal cursorline
186   setlocal nospell
187
188   set nobuflisted
189
190   let s:running = 1
191 endfunction
192
193 " Cleanup {{{1
194 function s:Cleanup()
195   let &insertmode = s:_insertmode
196   let &showcmd = s:_showcmd
197   let &cpo = s:_cpo
198   let &report = s:_report
199   let &list = s:_list
200   let s:running = 0
201   let s:splitMode = ""
202
203   delmarks!
204 endfunction
205
206 " HorizontalSplitBufExplorer {{{1
207 function HorizontalSplitBufExplorer()
208   let s:splitMode = "sp"
209   exec "BufExplorer"
210 endfunction
211
212 " VerticalSplitBufExplorer {{{1
213 function VerticalSplitBufExplorer()
214   let s:splitMode = "vsp"
215   exec "BufExplorer"
216 endfunction
217
218 " StartBufExplorer {{{1
219 function StartBufExplorer(open)
220   let name = '[BufExplorer]'
221
222   if !has("win32")
223                                 " On non-Windows boxes, escape the name so that is shows up correctly.
224     let name = escape(name, "[]")
225   endif
226                                 " Make sure there is only one explorer open at a time.
227   if s:running == 1
228                                 " Go to the open buffer.
229     if has("gui")
230       exec "drop" name
231     endif
232
233     return
234   endif
235
236   let s:originBuffer = bufnr("%")
237   silent let s:raw_buffer_listing = s:GetBufferInfo()
238
239   let copy = copy(s:raw_buffer_listing)
240
241   if (g:bufExplorerShowUnlisted == 0)
242     call filter(copy, 'v:val.attributes !~ "u"')
243   endif
244
245   if (!empty(copy))
246     call filter(copy, 'v:val.shortname !~ "\\\[No Name\\\]"')
247   endif
248
249   if len(copy) <= 1
250     echo "\r"
251     call s:Warning("Sorry, there are no more buffers to explore")
252
253     return
254   endif
255                                 " We may have to split the current window.
256   if (s:splitMode != "")
257                                 " Save off the original settings.
258     let [_splitbelow, _splitright] = [&splitbelow, &splitright]
259                                 " Set the setting to ours.
260     let [&splitbelow, &splitright] = [g:bufExplorerSplitBelow, g:bufExplorerSplitRight]
261                                 " Do it.
262     exe s:splitMode
263                                 " Restore the original settings.
264     let [&splitbelow, &splitright] = [_splitbelow, _splitright]
265   endif
266
267   if !exists("b:displayMode") || b:displayMode != "winmanager"
268                                 " Do not use keepalt when opening bufexplorer to allow the buffer that we are
269                                 " leaving to become the new alternate buffer
270     exec "silent keepjumps ".a:open." ".name
271   endif
272
273   call s:DisplayBufferList()
274 endfunction
275
276 " DisplayBufferList {{{1
277 function s:DisplayBufferList()
278   setlocal bufhidden=delete
279   setlocal buftype=nofile
280   setlocal modifiable
281   setlocal noswapfile
282   setlocal nowrap
283
284   call s:SetupSyntax()
285   call s:MapKeys()
286   call setline(1, s:CreateHelp())
287   call s:BuildBufferList()
288   call cursor(s:firstBufferLine, 1)
289
290   if !g:bufExplorerResize
291     normal! zz
292   endif
293
294   setlocal nomodifiable
295 endfunction
296
297 " MapKeys {{{1
298 function s:MapKeys()
299   if exists("b:displayMode") && b:displayMode == "winmanager"
300     nnoremap <buffer> <silent> <tab> :call <SID>SelectBuffer("tab")<cr>
301   endif
302
303   nnoremap <buffer> <silent> <F1>          :call <SID>ToggleHelp()<cr>
304   nnoremap <buffer> <silent> <2-leftmouse> :call <SID>SelectBuffer()<cr>
305   nnoremap <buffer> <silent> <cr>          :call <SID>SelectBuffer()<cr>
306   nnoremap <buffer> <silent> t             :call <SID>SelectBuffer("tab")<cr>
307   nnoremap <buffer> <silent> <s-cr>        :call <SID>SelectBuffer("tab")<cr>
308   nnoremap <buffer> <silent> d             :call <SID>RemoveBuffer("wipe")<cr>
309   nnoremap <buffer> <silent> D             :call <SID>RemoveBuffer("delete")<cr>
310   nnoremap <buffer> <silent> m             :call <SID>MRUListShow()<cr>
311   nnoremap <buffer> <silent> p             :call <SID>ToggleSplitOutPathName()<cr>
312   nnoremap <buffer> <silent> q             :call <SID>Close()<cr>
313   nnoremap <buffer> <silent> r             :call <SID>SortReverse()<cr>
314   nnoremap <buffer> <silent> R             :call <SID>ToggleShowRelativePath()<cr>
315   nnoremap <buffer> <silent> s             :call <SID>SortSelect()<cr>
316   nnoremap <buffer> <silent> u             :call <SID>ToggleShowUnlisted()<cr>
317   nnoremap <buffer> <silent> f             :call <SID>ToggleFindActive()<cr>
318
319   for k in ["G", "n", "N", "L", "M", "H"]
320     exec "nnoremap <buffer> <silent>" k ":keepjumps normal!" k."<cr>"
321   endfor
322 endfunction
323
324 " SetupSyntax {{{1
325 function s:SetupSyntax()
326   if has("syntax")
327     syn match bufExplorerHelp     "^\".*" contains=bufExplorerSortBy,bufExplorerMapping,bufExplorerTitle,bufExplorerSortType,bufExplorerToggleSplit,bufExplorerToggleOpen
328     syn match bufExplorerOpenIn   "Open in \w\+ window" contained
329     syn match bufExplorerSplit    "\w\+ split" contained
330     syn match bufExplorerSortBy   "Sorted by .*" contained contains=bufExplorerOpenIn,bufExplorerSplit
331     syn match bufExplorerMapping  "\" \zs.\+\ze :" contained
332     syn match bufExplorerTitle    "Buffer Explorer.*" contained
333     syn match bufExplorerSortType "'\w\{-}'" contained
334     syn match bufExplorerBufNbr   /^\s*\d\+/
335     syn match bufExplorerToggleSplit  "toggle split type" contained
336     syn match bufExplorerToggleOpen   "toggle open mode" contained
337
338     syn match bufExplorerModBuf    /^\s*\d\+.\{4}+.*/
339     syn match bufExplorerLockedBuf /^\s*\d\+.\{3}[\-=].*/
340     syn match bufExplorerHidBuf    /^\s*\d\+.\{2}h.*/
341     syn match bufExplorerActBuf    /^\s*\d\+.\{2}a.*/
342     syn match bufExplorerCurBuf    /^\s*\d\+.%.*/
343     syn match bufExplorerAltBuf    /^\s*\d\+.#.*/
344     syn match bufExplorerUnlBuf    /^\s*\d\+u.*/
345
346     hi def link bufExplorerBufNbr Number
347     hi def link bufExplorerMapping NonText
348     hi def link bufExplorerHelp Special
349     hi def link bufExplorerOpenIn Identifier
350     hi def link bufExplorerSortBy String
351     hi def link bufExplorerSplit NonText
352     hi def link bufExplorerTitle NonText
353     hi def link bufExplorerSortType bufExplorerSortBy
354     hi def link bufExplorerToggleSplit bufExplorerSplit
355     hi def link bufExplorerToggleOpen bufExplorerOpenIn
356
357     hi def link bufExplorerActBuf Identifier
358     hi def link bufExplorerAltBuf String
359     hi def link bufExplorerCurBuf Type
360     hi def link bufExplorerHidBuf Constant
361     hi def link bufExplorerLockedBuf Special
362     hi def link bufExplorerModBuf Exception
363     hi def link bufExplorerUnlBuf Comment
364   endif
365 endfunction
366
367 " ToggleHelp {{{1
368 function s:ToggleHelp()
369   let g:bufExplorerDetailedHelp = !g:bufExplorerDetailedHelp
370
371   setlocal modifiable
372                                 " Save position.
373   normal! ma
374                                 " Remove old header.
375   if (s:firstBufferLine > 1)
376     exec "keepjumps 1,".(s:firstBufferLine - 1) "d _"
377   endif
378
379   call append(0, s:CreateHelp())
380
381   silent! normal! g`a
382   delmarks a
383
384   setlocal nomodifiable
385
386   if exists("b:displayMode") && b:displayMode == "winmanager"
387     call WinManagerForceReSize("BufExplorer")
388   end
389 endfunction
390
391 " GetHelpStatus {{{1
392 function s:GetHelpStatus()
393   let ret = '" Sorted by '.((g:bufExplorerReverseSort == 1) ? "reverse " : "").g:bufExplorerSortBy
394   let ret .= ' | '.((g:bufExplorerFindActive == 0) ? "Don't " : "")."Locate buffer"
395   let ret .= ((g:bufExplorerShowUnlisted == 0) ? "" : " | Show unlisted")
396   let ret .= ' | '.((g:bufExplorerShowRelativePath == 0) ? "Absolute" : "Relative")
397   let ret .= ' '.((g:bufExplorerSplitOutPathName == 0) ? "Full" : "Split")." path"
398
399   return ret
400 endfunction
401
402 " CreateHelp {{{1
403 function s:CreateHelp()
404   if g:bufExplorerDefaultHelp == 0 && g:bufExplorerDetailedHelp == 0
405     let s:firstBufferLine = 1
406     return []
407   endif
408
409   let header = []
410
411   if g:bufExplorerDetailedHelp == 1
412     call add(header, '" Buffer Explorer ('.g:bufexplorer_version.')')
413     call add(header, '" --------------------------')
414     call add(header, '" <F1> : toggle this help')
415     call add(header, '" <enter> or Mouse-Double-Click : open buffer under cursor')
416     call add(header, '" <shift-enter> or t : open buffer in another tab')
417     call add(header, '" d : wipe buffer')
418     call add(header, '" D : delete buffer')
419     call add(header, '" p : toggle spliting of file and path name')
420     call add(header, '" q : quit')
421     call add(header, '" r : reverse sort')
422     call add(header, '" R : toggle showing relative or full paths')
423     call add(header, '" u : toggle showing unlisted buffers')
424     call add(header, '" s : select sort field '.string(s:sort_by).'')
425     call add(header, '" f : toggle find active buffer')
426   else
427     call add(header, '" Press <F1> for Help')
428   endif
429
430   call add(header, s:GetHelpStatus())
431   call add(header, '"=')
432
433   let s:firstBufferLine = len(header) + 1
434
435   return header
436 endfunction
437
438 " GetBufferInfo {{{1
439 function s:GetBufferInfo()
440   redir => bufoutput
441   buffers!
442   redir END
443
444   let [all, allwidths, listedwidths] = [[], {}, {}]
445
446   for n in keys(s:types)
447     let allwidths[n] = []
448     let listedwidths[n] = []
449   endfor
450
451   for buf in split(bufoutput, '\n')
452     let bits = split(buf, '"')
453     let b = {"attributes": bits[0], "line": substitute(bits[2], '\s*', '', '')}
454
455     for [key, val] in items(s:types)
456       let b[key] = fnamemodify(bits[1], val)
457     endfor
458
459     if getftype(b.fullname) == "dir" && g:bufExplorerShowDirectories == 1
460       let b.shortname = "<DIRECTORY>"
461     end
462
463     call add(all, b)
464
465     for n in keys(s:types)
466       call add(allwidths[n], len(b[n]))
467
468       if b.attributes !~ "u"
469         call add(listedwidths[n], len(b[n]))
470       endif
471     endfor
472   endfor
473
474   let [s:allpads, s:listedpads] = [{}, {}]
475
476   for n in keys(s:types)
477     let s:allpads[n] = repeat(' ', max(allwidths[n]))
478     let s:listedpads[n] = repeat(' ', max(listedwidths[n]))
479   endfor
480
481   return all
482 endfunction
483
484 " BuildBufferList {{{1
485 function s:BuildBufferList()
486   let lines = []
487                                 " Loop through every buffer.
488   for buf in s:raw_buffer_listing
489     if (!g:bufExplorerShowUnlisted && buf.attributes =~ "u")
490                                 " Skip unlisted buffers if we are not to show them.
491       continue
492     endif
493
494     let line = buf.attributes." "
495
496     if g:bufExplorerSplitOutPathName
497       let type = (g:bufExplorerShowRelativePath) ? "relativepath" : "path"
498       let path = buf[type]
499       let pad  = (g:bufExplorerShowUnlisted) ? s:allpads.shortname : s:listedpads.shortname
500       let line .= buf.shortname." ".strpart(pad.path, len(buf.shortname))
501     else
502       let type = (g:bufExplorerShowRelativePath) ? "relativename" : "fullname"
503       let path = buf[type]
504       let line .= path
505     endif
506
507     let pads = (g:bufExplorerShowUnlisted) ? s:allpads : s:listedpads
508
509     if !empty(pads[type])
510       let line .= strpart(pads[type], len(path))." "
511     endif
512
513     let line .= buf.line
514
515     call add(lines, line)
516   endfor
517
518   call setline(s:firstBufferLine, lines)
519
520   call s:SortListing()
521 endfunction
522
523 " SelectBuffer {{{1
524 function s:SelectBuffer(...)
525                                 " Sometimes messages are not cleared when we get here so it looks like an error has
526                                 " occurred when it really has not.
527   echo ""
528                                 " Are we on a line with a file name?
529   if line('.') < s:firstBufferLine
530     exec "normal! \<cr>"
531     return
532   endif
533
534   let _bufNbr = str2nr(getline('.'))
535
536   if exists("b:displayMode") && b:displayMode == "winmanager"
537     let bufname = expand("#"._bufNbr.":p")
538
539     if (a:0 == 1) && (a:1 == "tab")
540       call WinManagerFileEdit(bufname, 1)
541     else
542       call WinManagerFileEdit(bufname, 0)
543     endif
544  
545     return
546   end
547
548   if bufexists(_bufNbr)
549     if bufnr("#") == _bufNbr
550       return s:Close()
551     endif
552
553     if (a:0 == 1) && (a:1 == "tab")
554                                 " Restore [BufExplorer] buffer.
555       exec "keepjumps silent buffer!".s:originBuffer
556
557       let tabNbr = s:GetTabNbr(_bufNbr)
558
559       if tabNbr == 0
560                                 " _bufNbr is not opened in any tabs. Open a new tab with the selected buffer in it.
561         exec "999tab split +buffer" . _bufNbr
562       else
563                                 " The _bufNbr is already opened in tab(s), go to that tab.
564         exec tabNbr . "tabnext"
565                                 " Focus window.
566         exec s:GetWinNbr(tabNbr, _bufNbr) . "wincmd w"
567       endif
568     else
569       if bufloaded(_bufNbr) && g:bufExplorerFindActive
570         call s:Close()
571
572         let tabNbr = s:GetTabNbr(_bufNbr)
573
574         if tabNbr != 0
575                                 " The buffer is located in a tab. Go to that tab number.
576           exec tabNbr . "tabnext"
577         else
578           let bufname = expand("#"._bufNbr.":p")
579           exec bufname ? "drop ".escape(bufname, " ") : "buffer "._bufNbr
580         endif
581       endif
582                                 " Switch to the buffer.
583       exec "keepalt keepjumps silent b!" _bufNbr
584     endif
585                                 " Make the buffer 'listed' again.
586     call setbufvar(_bufNbr, "&buflisted", "1")
587   else
588     call s:Error("Sorry, that buffer no longer exists, please select another")
589     call s:DeleteBuffer(_bufNbr, "wipe")
590   endif
591 endfunction
592
593 " RemoveBuffer {{{1
594 function s:RemoveBuffer(mode)
595                                 " Are we on a line with a file name?
596   if line('.') < s:firstBufferLine
597     return
598   endif
599                                 " Do not allow this buffer to be deleted if it is the last one.
600   if len(s:MRUList) == 1
601     call s:Error("Sorry, you are not allowed to delete the last buffer")
602     return
603   endif
604                                 " These commands are to temporarily suspend the activity of winmanager.
605   if exists("b:displayMode") && b:displayMode == "winmanager"
606     call WinManagerSuspendAUs()
607   end
608
609   let _bufNbr = str2nr(getline('.'))
610
611   if getbufvar(_bufNbr, '&modified') == 1
612     call s:Error("Sorry, no write since last change for buffer "._bufNbr.", unable to delete")
613     return
614   else
615                                 " Okay, everything is good, delete or wipe the buffer.
616     call s:DeleteBuffer(_bufNbr, a:mode)
617   endif
618                                 " Reactivate winmanager autocommand activity.
619   if exists("b:displayMode") && b:displayMode == "winmanager"
620     call WinManagerForceReSize("BufExplorer")
621     call WinManagerResumeAUs()
622   end
623 endfunction
624
625 " DeleteBuffer {{{1
626 function s:DeleteBuffer(buf, mode)
627                                 " This routine assumes that the buffer to be removed is on the current line.
628   try
629     if a:mode == "wipe"
630       exe "silent bw" a:buf
631     else
632       exe "silent bd" a:buf
633     end
634
635     setlocal modifiable
636     normal! "_dd
637     setlocal nomodifiable
638                                 " Delete the buffer from the raw buffer list.
639     call filter(s:raw_buffer_listing, 'v:val.attributes !~ " '.a:buf.' "')
640   catch
641     call s:Error(v:exception)
642   endtry
643 endfunction
644
645 " Close {{{1
646 function s:Close()
647                                 " Get only the listed buffers.
648   let listed = filter(copy(s:MRUList), "buflisted(v:val)")
649                                 " If we needed to split the main window, close the split one.
650   if (s:splitMode != "")
651     exec "wincmd c"
652   end
653
654   for b in reverse(listed[0:1])
655     exec "keepjumps silent b ".b
656   endfor
657 endfunction
658
659 " ToggleSplitOutPathName {{{1
660 function s:ToggleSplitOutPathName()
661   let g:bufExplorerSplitOutPathName = !g:bufExplorerSplitOutPathName
662   call s:RebuildBufferList()
663   call s:UpdateHelpStatus()
664 endfunction
665
666 " ToggleShowRelativePath {{{1
667 function s:ToggleShowRelativePath()
668   let g:bufExplorerShowRelativePath = !g:bufExplorerShowRelativePath
669   call s:RebuildBufferList()
670   call s:UpdateHelpStatus()
671 endfunction
672
673 " ToggleShowUnlisted {{{1
674 function s:ToggleShowUnlisted()
675   let g:bufExplorerShowUnlisted = !g:bufExplorerShowUnlisted
676   let num_bufs = s:RebuildBufferList(g:bufExplorerShowUnlisted == 0)
677   call s:UpdateHelpStatus()
678 endfunction
679
680 " ToggleFindActive {{{1
681 function s:ToggleFindActive()
682   let g:bufExplorerFindActive = !g:bufExplorerFindActive
683   call s:UpdateHelpStatus()
684 endfunction
685
686 " RebuildBufferList {{{1
687 function s:RebuildBufferList(...)
688   setlocal modifiable
689
690   let curPos = getpos('.')
691
692   if a:0
693     " Clear the list first.
694     exec "keepjumps ".s:firstBufferLine.',$d "_'
695   endif
696
697   let num_bufs = s:BuildBufferList()
698
699   call setpos('.', curPos)
700
701   setlocal nomodifiable
702
703   return num_bufs
704 endfunction
705
706 " UpdateHelpStatus {{{1
707 function s:UpdateHelpStatus()
708   setlocal modifiable
709
710   let text = s:GetHelpStatus()
711   call setline(s:firstBufferLine - 2, text)
712
713   setlocal nomodifiable
714 endfunction
715
716 " MRUCmp {{{1
717 function s:MRUCmp(line1, line2)
718   return index(s:MRUList, str2nr(a:line1)) - index(s:MRUList, str2nr(a:line2))
719 endfunction
720
721 " SortReverse {{{1
722 function s:SortReverse()
723   let g:bufExplorerReverseSort = !g:bufExplorerReverseSort
724
725   call s:ReSortListing()
726 endfunction
727
728 " SortSelect {{{1
729 function s:SortSelect()
730   let g:bufExplorerSortBy = get(s:sort_by, index(s:sort_by, g:bufExplorerSortBy) + 1, s:sort_by[0])
731
732   call s:ReSortListing()
733 endfunction
734
735 " ReSortListing {{{1
736 function s:ReSortListing()
737   setlocal modifiable
738
739   let curPos = getpos('.')
740
741   call s:SortListing()
742   call s:UpdateHelpStatus()
743
744   call setpos('.', curPos)
745
746   setlocal nomodifiable
747 endfunction
748
749 " SortListing {{{1
750 function s:SortListing()
751   let sort = s:firstBufferLine.",$sort".((g:bufExplorerReverseSort == 1) ? "!": "")
752
753   if g:bufExplorerSortBy == "number"
754                                 " Easiest case.
755     exec sort 'n'
756   elseif g:bufExplorerSortBy == "name"
757     if g:bufExplorerSplitOutPathName
758       exec sort 'ir /\d.\{7}\zs\f\+\ze/'
759     else
760       exec sort 'ir /\zs[^\/\\]\+\ze\s*line/'
761     endif
762   elseif g:bufExplorerSortBy == "fullpath"
763     if g:bufExplorerSplitOutPathName
764                                 " Sort twice - first on the file name then on the path.
765       exec sort 'ir /\d.\{7}\zs\f\+\ze/'
766     endif
767
768     exec sort 'ir /\zs\f\+\ze\s\+line/'
769   elseif g:bufExplorerSortBy == "extension"
770     exec sort 'ir /\.\zs\w\+\ze\s/'
771   elseif g:bufExplorerSortBy == "mru"
772     let l = getline(s:firstBufferLine, "$")
773
774     call sort(l, "<SID>MRUCmp")
775
776     if g:bufExplorerReverseSort
777       call reverse(l)
778     endif
779
780     call setline(s:firstBufferLine, l)
781   endif
782 endfunction
783
784 " MRUListShow {{{1
785 function s:MRUListShow()
786   echomsg "MRUList=".string(s:MRUList)
787 endfunction
788
789 " Error {{{1
790 function s:Error(msg)
791   echohl ErrorMsg | echo a:msg | echohl none
792 endfunction
793
794 " Warning {{{1
795 function s:Warning(msg)
796   echohl WarningMsg | echo a:msg | echohl none
797 endfunction
798
799 " GetTabNbr {{{1
800 function s:GetTabNbr(bufNbr)
801   " Searching buffer bufno, in tabs.
802   for i in range(tabpagenr("$"))
803     if index(tabpagebuflist(i + 1), a:bufNbr) != -1
804       return i + 1
805     endif
806   endfor
807
808   return 0
809 endfunction
810
811 " GetWinNbr" {{{1
812 function s:GetWinNbr(tabNbr, bufNbr)
813   " window number in tabpage.
814   return index(tabpagebuflist(a:tabNbr), a:bufNbr) + 1
815 endfunction
816
817 " Winmanager Integration {{{1
818 let g:BufExplorer_title = "\[Buf\ List\]"
819 call s:Set("g:bufExplorerResize", 1)
820 call s:Set("g:bufExplorerMaxHeight", 25) " Handles dynamic resizing of the window.
821
822 " Function to start display. Set the mode to 'winmanager' for this buffer.
823 " This is to figure out how this plugin was called. In a standalone fashion
824 " or by winmanager.
825 function BufExplorer_Start()
826   let b:displayMode = "winmanager"
827   call StartBufExplorer("e")
828 endfunction
829
830 " Returns whether the display is okay or not.
831 function BufExplorer_IsValid()
832   return 0
833 endfunction
834
835 " Handles dynamic refreshing of the window.
836 function BufExplorer_Refresh()
837   let b:displayMode = "winmanager"
838   call StartBufExplorer("e")
839 endfunction
840
841 function BufExplorer_ReSize()
842   if !g:bufExplorerResize
843     return
844   end
845
846   let nlines = min([line("$"), g:bufExplorerMaxHeight])
847
848   exe nlines." wincmd _"
849
850   " The following lines restore the layout so that the last file line is also
851   " the last window line. Sometimes, when a line is deleted, although the
852   " window size is exactly equal to the number of lines in the file, some of
853   " the lines are pushed up and we see some lagging '~'s.
854   let pres = getpos(".")
855
856   exe $
857
858   let _scr = &scrolloff
859   let &scrolloff = 0
860
861   normal! z-
862
863   let &scrolloff = _scr
864
865   call setpos(".", pres)
866 endfunction
867 "1}}}
868
869 " vim:ft=vim foldmethod=marker sw=2