bufexplorer.vim 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777
  1. "============================================================================
  2. " Copyright: Copyright (c) 2001-2025, Jeff Lanzarotta
  3. " All rights reserved.
  4. "
  5. " Redistribution and use in source and binary forms, with or
  6. " without modification, are permitted provided that the
  7. " following conditions are met:
  8. "
  9. " * Redistributions of source code must retain the above
  10. " copyright notice, this list of conditions and the following
  11. " disclaimer.
  12. "
  13. " * Redistributions in binary form must reproduce the above
  14. " copyright notice, this list of conditions and the following
  15. " disclaimer in the documentation and/or other materials
  16. " provided with the distribution.
  17. "
  18. " * Neither the name of the {organization} nor the names of its
  19. " contributors may be used to endorse or promote products
  20. " derived from this software without specific prior written
  21. " permission.
  22. "
  23. " THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  24. " CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  25. " INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  26. " MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  27. " DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  28. " CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  29. " SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  30. " NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  31. " LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32. " HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  33. " CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  34. " OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  35. " EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36. " Name Of File: bufexplorer.vim
  37. " Description: Buffer Explorer Vim Plugin
  38. " Maintainer: Jeff Lanzarotta (my name at gmail dot com)
  39. " Last Changed: Thursday, 20 March 2025
  40. " Version: See g:bufexplorer_version for version number.
  41. " Usage: This file should reside in the plugin directory and be
  42. " automatically sourced.
  43. "
  44. " You may use the default keymappings of
  45. "
  46. " <Leader>be - Opens BufExplorer
  47. " <Leader>bt - Toggles BufExplorer open or closed
  48. " <Leader>bs - Opens horizontally split window BufExplorer
  49. " <Leader>bv - Opens vertically split window BufExplorer
  50. "
  51. " Or you can override the defaults and define your own mapping
  52. " in your vimrc file, for example:
  53. "
  54. " nnoremap <silent> <F11> :BufExplorer<CR>
  55. " nnoremap <silent> <s-F11> :ToggleBufExplorer<CR>
  56. " nnoremap <silent> <m-F11> :BufExplorerHorizontalSplit<CR>
  57. " nnoremap <silent> <c-F11> :BufExplorerVerticalSplit<CR>
  58. "
  59. " Or you can use
  60. "
  61. " ":BufExplorer" - Opens BufExplorer
  62. " ":ToggleBufExplorer" - Opens/Closes BufExplorer
  63. " ":BufExplorerHorizontalSplit" - Opens horizontally window BufExplorer
  64. " ":BufExplorerVerticalSplit" - Opens vertically split window BufExplorer
  65. "
  66. " For more help see supplied documentation.
  67. " History: See supplied documentation.
  68. "=============================================================================
  69. " Exit quickly if already running or when 'compatible' is set. {{{1
  70. if exists("g:bufexplorer_version") || &cp
  71. finish
  72. endif
  73. "1}}}
  74. " Version number.
  75. let g:bufexplorer_version = "7.9.0"
  76. " Plugin Code {{{1
  77. " Check for Vim version {{{2
  78. if !exists("g:bufExplorerVersionWarn")
  79. let g:bufExplorerVersionWarn = 1
  80. endif
  81. " Make sure we are using the correct version of Vim. If not, do not load the
  82. " plugin.
  83. if v:version < 704
  84. if g:bufExplorerVersionWarn
  85. echohl WarningMsg
  86. echo "Sorry, bufexplorer ".g:bufexplorer_version." required Vim 7.4 or greater."
  87. echohl None
  88. endif
  89. finish
  90. endif
  91. " Command actions {{{2
  92. let s:actions = [
  93. \ 'current',
  94. \ 'close',
  95. \ ]
  96. " Command-line completion function for `s:actions`.
  97. function! s:ActionArgs(ArgLead, CmdLine, CursorPos)
  98. return join(s:actions, "\n")
  99. endfunction
  100. " Create commands {{{2
  101. command! -nargs=? -complete=custom,<SID>ActionArgs
  102. \ BufExplorer :call BufExplorer(<f-args>)
  103. command! ToggleBufExplorer :call ToggleBufExplorer()
  104. command! BufExplorerHorizontalSplit :call BufExplorerHorizontalSplit()
  105. command! BufExplorerVerticalSplit :call BufExplorerVerticalSplit()
  106. " Set {{{2
  107. function! s:Set(var, default)
  108. if !exists(a:var)
  109. if type(a:default)
  110. execute "let" a:var "=" string(a:default)
  111. else
  112. execute "let" a:var "=" a:default
  113. endif
  114. return 1
  115. endif
  116. return 0
  117. endfunction
  118. " Script variables {{{2
  119. let s:MRU_Exclude_List = ["[BufExplorer]","__MRU_Files__","[Buf\ List]"]
  120. let s:name = '[BufExplorer]'
  121. " Buffer number of the BufExplorer window.
  122. let s:bufExplorerBuffer = 0
  123. let s:running = 0
  124. let s:sort_by = ["number", "name", "fullpath", "mru", "extension"]
  125. let s:splitMode = ""
  126. let s:didSplit = 0
  127. let s:types = ["fullname", "homename", "path", "relativename", "relativepath", "shortname"]
  128. " Setup the autocommands that handle stuff. {{{2
  129. augroup BufExplorer
  130. autocmd!
  131. autocmd WinEnter * call s:DoWinEnter()
  132. autocmd BufEnter * call s:DoBufEnter()
  133. autocmd BufDelete * call s:DoBufDelete()
  134. if exists('##TabClosed')
  135. autocmd TabClosed * call s:DoTabClosed()
  136. endif
  137. autocmd BufWinEnter \[BufExplorer\] call s:Initialize()
  138. autocmd BufWinLeave \[BufExplorer\] call s:Cleanup()
  139. augroup END
  140. " AssignTabId {{{2
  141. " Assign a `tabId` to the given tab.
  142. function! s:AssignTabId(tabNbr)
  143. " Create a unique `tabId` based on the current time and an incrementing
  144. " counter value that helps ensure uniqueness.
  145. let tabId = reltimestr(reltime()) . ':' . s:tabIdCounter
  146. call settabvar(a:tabNbr, 'bufexp_tabId', tabId)
  147. let s:tabIdCounter = (s:tabIdCounter + 1) % 1000000000
  148. return tabId
  149. endfunction
  150. let s:tabIdCounter = 0
  151. " GetTabId {{{2
  152. " Retrieve the `tabId` for the given tab (or '' if the tab has no `tabId`).
  153. function! s:GetTabId(tabNbr)
  154. return gettabvar(a:tabNbr, 'bufexp_tabId', '')
  155. endfunction
  156. " MRU data structure {{{2
  157. " An MRU data structure is a dictionary that holds a circular doubly linked list
  158. " of `item` values. The dictionary contains three keys:
  159. " 'head': a sentinel `item` representing the head of the list.
  160. " 'next': a dictionary mapping an `item` to the next `item` in the list.
  161. " 'prev': a dictionary mapping an `item` to the previous `item` in the list.
  162. " E.g., an MRU holding buffer numbers will use `0` (an invalid buffer number) as
  163. " `head`. With the buffer numbers `1`, `2`, and `3`, an example MRU would be:
  164. "
  165. " +--<---------<---------<---------<---------<+
  166. " `next` | |
  167. " +--> +---+ --> +---+ --> +---+ --> +---+ -->+
  168. " `head` | 0 | | 1 | | 2 | | 3 |
  169. " +<-- +---+ <-- +---+ <-- +---+ <-- +---+ <--+
  170. " `prev` | |
  171. " +->-------->--------->--------->--------->--+
  172. "
  173. " `head` allows the chosen sentinel item to differ in value and type; for
  174. " example, `head` could be the string '.', allowing an MRU of strings (such as
  175. " for `TabId` values).
  176. "
  177. " Note that dictionary keys are always strings. Integers may be used, but they
  178. " are converted to strings when used (and `keys(theDictionary)` will be a
  179. " list of strings, not of integers).
  180. " MRUNew {{{2
  181. function! s:MRUNew(head)
  182. let [next, prev] = [{}, {}]
  183. let next[a:head] = a:head
  184. let prev[a:head] = a:head
  185. return { 'head': a:head, 'next': next, 'prev': prev }
  186. endfunction
  187. " MRULen {{{2
  188. function! s:MRULen(mru)
  189. " Do not include the always-present `mru.head` item.
  190. return len(a:mru.next) - 1
  191. endfunction
  192. " MRURemoveMustExist {{{2
  193. " `item` must exist in `mru`.
  194. function! s:MRURemoveMustExist(mru, item)
  195. let [next, prev] = [a:mru.next, a:mru.prev]
  196. let prevItem = prev[a:item]
  197. let nextItem = next[a:item]
  198. let next[prevItem] = nextItem
  199. let prev[nextItem] = prevItem
  200. unlet next[a:item]
  201. unlet prev[a:item]
  202. endfunction
  203. " MRURemove {{{2
  204. " `item` need not exist in `mru`.
  205. function! s:MRURemove(mru, item)
  206. if has_key(a:mru.next, a:item)
  207. call s:MRURemoveMustExist(a:mru, a:item)
  208. endif
  209. endfunction
  210. " MRUAdd {{{2
  211. function! s:MRUAdd(mru, item)
  212. let [next, prev] = [a:mru.next, a:mru.prev]
  213. let prevItem = a:mru.head
  214. let nextItem = next[prevItem]
  215. if a:item != nextItem
  216. call s:MRURemove(a:mru, a:item)
  217. let next[a:item] = nextItem
  218. let prev[a:item] = prevItem
  219. let next[prevItem] = a:item
  220. let prev[nextItem] = a:item
  221. endif
  222. endfunction
  223. " MRUGetItems {{{2
  224. " Return list of up to `maxItems` items in MRU order.
  225. " `maxItems == 0` => unlimited.
  226. function! s:MRUGetItems(mru, maxItems)
  227. let [head, next] = [a:mru.head, a:mru.next]
  228. let items = []
  229. let item = next[head]
  230. while item != head
  231. if a:maxItems > 0 && len(items) >= a:maxItems
  232. break
  233. endif
  234. call add(items, item)
  235. let item = next[item]
  236. endwhile
  237. return items
  238. endfunction
  239. " MRUGetOrdering {{{2
  240. " Return dictionary mapping up to `maxItems` from `item` to MRU order.
  241. " `maxItems == 0` => unlimited.
  242. function! s:MRUGetOrdering(mru, maxItems)
  243. let [head, next] = [a:mru.head, a:mru.next]
  244. let items = {}
  245. let order = 0
  246. let item = next[head]
  247. while item != head
  248. if a:maxItems > 0 && order >= a:maxItems
  249. break
  250. endif
  251. let items[item] = order
  252. let order = order + 1
  253. let item = next[item]
  254. endwhile
  255. return items
  256. endfunction
  257. " MRU trackers {{{2
  258. " `.head` value for tab MRU:
  259. let s:tabIdHead = '.'
  260. " Track MRU buffers globally (independent of tabs).
  261. let s:bufMru = s:MRUNew(0)
  262. " Track MRU buffers for each tab, indexed by `tabId`.
  263. " `s:bufMruByTab[tabId] -> MRU structure`.
  264. let s:bufMruByTab = {}
  265. " Track MRU tabs for each buffer, indexed by `bufNbr`.
  266. " `s:tabMruByBuf[burNbr] -> MRU structure`.
  267. let s:tabMruByBuf = {}
  268. " MRURemoveBuf {{{2
  269. function! s:MRURemoveBuf(bufNbr)
  270. call s:MRURemove(s:bufMru, a:bufNbr)
  271. if has_key(s:tabMruByBuf, a:bufNbr)
  272. let mru = s:tabMruByBuf[a:bufNbr]
  273. let [head, next] = [mru.head, mru.next]
  274. let tabId = next[head]
  275. while tabId != head
  276. call s:MRURemoveMustExist(s:bufMruByTab[tabId], a:bufNbr)
  277. let tabId = next[tabId]
  278. endwhile
  279. unlet s:tabMruByBuf[a:bufNbr]
  280. endif
  281. endfunction
  282. " MRURemoveTab {{{2
  283. function! s:MRURemoveTab(tabId)
  284. if has_key(s:bufMruByTab, a:tabId)
  285. let mru = s:bufMruByTab[a:tabId]
  286. let [head, next] = [mru.head, mru.next]
  287. let bufNbr = next[head]
  288. while bufNbr != head
  289. call s:MRURemoveMustExist(s:tabMruByBuf[bufNbr], a:tabId)
  290. let bufNbr = next[bufNbr]
  291. endwhile
  292. unlet s:bufMruByTab[a:tabId]
  293. endif
  294. endfunction
  295. " MRUAddBufTab {{{2
  296. function! s:MRUAddBufTab(bufNbr, tabId)
  297. if s:ShouldIgnore(a:bufNbr)
  298. return
  299. endif
  300. call s:MRUAdd(s:bufMru, a:bufNbr)
  301. if !has_key(s:bufMruByTab, a:tabId)
  302. let s:bufMruByTab[a:tabId] = s:MRUNew(0)
  303. endif
  304. let bufMru = s:bufMruByTab[a:tabId]
  305. call s:MRUAdd(bufMru, a:bufNbr)
  306. if !has_key(s:tabMruByBuf, a:bufNbr)
  307. let s:tabMruByBuf[a:bufNbr] = s:MRUNew(s:tabIdHead)
  308. endif
  309. let tabMru = s:tabMruByBuf[a:bufNbr]
  310. call s:MRUAdd(tabMru, a:tabId)
  311. endfunction
  312. " MRUTabForBuf {{{2
  313. " Return `tabId` most recently used by `bufNbr`.
  314. " If no `tabId` is found for `bufNbr`, return `s:tabIdHead`.
  315. function! s:MRUTabForBuf(bufNbr)
  316. let tabMru = get(s:tabMruByBuf, a:bufNbr, s:alwaysEmptyTabMru)
  317. return tabMru.next[tabMru.head]
  318. endfunction
  319. " An always-empty MRU for tabs as a default when looking up
  320. " `s:tabMruByBuf[bufNbr]` for an unknown `bufNbr`.
  321. let s:alwaysEmptyTabMru = s:MRUNew(s:tabIdHead)
  322. " MRUTabHasSeenBuf {{{2
  323. " Return true if `tabId` has ever seen `bufNbr`.
  324. function! s:MRUTabHasSeenBuf(tabId, bufNbr)
  325. let mru = get(s:bufMruByTab, a:tabId, s:alwaysEmptyBufMru)
  326. return has_key(mru.next, a:bufNbr)
  327. endfunction
  328. " MRUTabShouldShowBuf {{{2
  329. " Return true if `tabId` should show `bufNbr`.
  330. " This is a function of current display modes.
  331. function! s:MRUTabShouldShowBuf(tabId, bufNbr)
  332. if !g:bufExplorerShowTabBuffer
  333. " We are showing buffers from all tabs.
  334. return 1
  335. elseif g:bufExplorerOnlyOneTab
  336. " We are showing buffers that were most recently seen in this tab.
  337. return s:MRUTabForBuf(a:bufNbr) == a:tabId
  338. else
  339. " We are showing buffers that have ever been seen in this tab.
  340. return s:MRUTabHasSeenBuf(a:tabId, a:bufNbr)
  341. endif
  342. endfunction
  343. " MRUListedBuffersForTab {{{2
  344. " Return list of up to `maxBuffers` listed buffers in MRU order for the tab.
  345. " `maxBuffers == 0` => unlimited.
  346. function! s:MRUListedBuffersForTab(tabId, maxBuffers)
  347. let bufNbrs = []
  348. let mru = get(s:bufMruByTab, a:tabId, s:alwaysEmptyBufMru)
  349. let [head, next] = [mru.head, mru.next]
  350. let bufNbr = next[head]
  351. while bufNbr != head
  352. if a:maxBuffers > 0 && len(bufNbrs) >= a:maxBuffers
  353. break
  354. endif
  355. if buflisted(bufNbr) && s:MRUTabShouldShowBuf(a:tabId, bufNbr)
  356. call add(bufNbrs, bufNbr)
  357. endif
  358. let bufNbr = next[bufNbr]
  359. endwhile
  360. return bufNbrs
  361. endfunction
  362. " An always-empty MRU for buffers as a default when looking up
  363. " `s:bufMruByTab[tabId]` for an unknown `tabId`.
  364. let s:alwaysEmptyBufMru = s:MRUNew(0)
  365. " MRUOrderForBuf {{{2
  366. " Return the position of `bufNbr` in the current MRU ordering.
  367. " This is a function of the current display mode. When showing buffers from all
  368. " tabs, it's the global MRU order; otherwise, it the MRU order for the tab at
  369. " BufExplorer launch. The latter includes all buffers seen in this tab, which
  370. " is sufficient whether `g:bufExplorerOnlyOneTab` is true or false.
  371. function! s:MRUOrderForBuf(bufNbr)
  372. if !exists('s:mruOrder')
  373. if g:bufExplorerShowTabBuffer
  374. let mru = get(s:bufMruByTab, s:tabIdAtLaunch, s:alwaysEmptyBufMru)
  375. else
  376. let mru = s:bufMru
  377. endif
  378. let s:mruOrder = s:MRUGetOrdering(mru, 0)
  379. endif
  380. return get(s:mruOrder, a:bufNbr, len(s:mruOrder))
  381. endfunction
  382. " MRUEnsureTabId {{{2
  383. function! s:MRUEnsureTabId(tabNbr)
  384. let tabId = s:GetTabId(a:tabNbr)
  385. if tabId == ''
  386. let tabId = s:AssignTabId(a:tabNbr)
  387. for bufNbr in tabpagebuflist(a:tabNbr)
  388. call s:MRUAddBufTab(bufNbr, tabId)
  389. endfor
  390. endif
  391. return tabId
  392. endfunction
  393. " MRUGarbageCollectBufs {{{2
  394. " Requires `s:raw_buffer_listing`.
  395. function! s:MRUGarbageCollectBufs()
  396. for bufNbr in values(s:bufMru.next)
  397. if bufNbr != 0 && !has_key(s:raw_buffer_listing, bufNbr)
  398. call s:MRURemoveBuf(bufNbr)
  399. endif
  400. endfor
  401. endfunction
  402. " MRUGarbageCollectTabs {{{2
  403. function! s:MRUGarbageCollectTabs()
  404. let numTabs = tabpagenr('$')
  405. let liveTabIds = {}
  406. for tabNbr in range(1, numTabs)
  407. let tabId = s:GetTabId(tabNbr)
  408. if tabId != ''
  409. let liveTabIds[tabId] = 1
  410. endif
  411. endfor
  412. for tabId in keys(s:bufMruByTab)
  413. if tabId != s:tabIdHead && !has_key(liveTabIds, tabId)
  414. call s:MRURemoveTab(tabId)
  415. endif
  416. endfor
  417. endfunction
  418. " DoWinEnter {{{2
  419. function! s:DoWinEnter()
  420. let bufNbr = str2nr(expand("<abuf>"))
  421. let tabNbr = tabpagenr()
  422. let tabId = s:GetTabId(tabNbr)
  423. " Ignore `WinEnter` for a newly created tab; this event comes when creating
  424. " a new tab, and the buffer at that moment is one that is about to be
  425. " replaced by the buffer to which we are switching; this latter buffer will
  426. " be handled by the forthcoming `BufEnter` event.
  427. if tabId != ''
  428. call s:MRUAddBufTab(bufNbr, tabId)
  429. endif
  430. endfunction
  431. " DoBufEnter {{{2
  432. function! s:DoBufEnter()
  433. let bufNbr = str2nr(expand("<abuf>"))
  434. let tabNbr = tabpagenr()
  435. let tabId = s:MRUEnsureTabId(tabNbr)
  436. call s:MRUAddBufTab(bufNbr, tabId)
  437. endfunction
  438. " DoBufDelete {{{2
  439. function! s:DoBufDelete()
  440. let bufNbr = str2nr(expand("<abuf>"))
  441. call s:MRURemoveBuf(bufNbr)
  442. endfunction
  443. " DoTabClosed {{{2
  444. function! s:DoTabClosed()
  445. call s:MRUGarbageCollectTabs()
  446. endfunction
  447. " ShouldIgnore {{{2
  448. function! s:ShouldIgnore(buf)
  449. " Ignore temporary buffers with buftype set.
  450. if empty(getbufvar(a:buf, "&buftype")) == 0
  451. return 1
  452. endif
  453. " Ignore buffers with no name.
  454. if empty(bufname(a:buf)) == 1
  455. return 1
  456. endif
  457. " Ignore the BufExplorer buffer.
  458. if fnamemodify(bufname(a:buf), ":t") == s:name
  459. return 1
  460. endif
  461. " Ignore any buffers in the exclude list.
  462. if index(s:MRU_Exclude_List, bufname(a:buf)) >= 0
  463. return 1
  464. endif
  465. " Else return 0 to indicate that the buffer was not ignored.
  466. return 0
  467. endfunction
  468. " Initialize {{{2
  469. function! s:Initialize()
  470. call s:SetLocalSettings()
  471. let s:running = 1
  472. endfunction
  473. " Cleanup {{{2
  474. function! s:Cleanup()
  475. if exists("s:_insertmode")
  476. let &insertmode = s:_insertmode
  477. endif
  478. if exists("s:_showcmd")
  479. let &showcmd = s:_showcmd
  480. endif
  481. if exists("s:_cpo")
  482. let &cpo = s:_cpo
  483. endif
  484. if exists("s:_report")
  485. let &report = s:_report
  486. endif
  487. let s:running = 0
  488. let s:splitMode = ""
  489. let s:didSplit = 0
  490. delmarks!
  491. endfunction
  492. " SetLocalSettings {{{2
  493. function! s:SetLocalSettings()
  494. let s:_insertmode = &insertmode
  495. set noinsertmode
  496. let s:_showcmd = &showcmd
  497. set noshowcmd
  498. let s:_cpo = &cpo
  499. set cpo&vim
  500. let s:_report = &report
  501. let &report = 10000
  502. setlocal nonumber
  503. setlocal foldcolumn=0
  504. setlocal nofoldenable
  505. setlocal cursorline
  506. setlocal nospell
  507. setlocal nobuflisted
  508. setlocal filetype=bufexplorer
  509. endfunction
  510. " BufExplorerHorizontalSplit {{{2
  511. function! BufExplorerHorizontalSplit()
  512. let s:splitMode = "sp"
  513. execute "BufExplorer"
  514. let s:splitMode = ""
  515. endfunction
  516. " BufExplorerVerticalSplit {{{2
  517. function! BufExplorerVerticalSplit()
  518. let s:splitMode = "vsp"
  519. execute "BufExplorer"
  520. let s:splitMode = ""
  521. endfunction
  522. " ToggleBufExplorer {{{2
  523. function! ToggleBufExplorer()
  524. if exists("s:running") && s:running == 1 && bufname(winbufnr(0)) == s:name
  525. call s:Close()
  526. else
  527. call BufExplorer()
  528. endif
  529. endfunction
  530. " BufExplorer {{{2
  531. " Args: `([action])`
  532. " Optional `action` argument must be taken from `s:actions`. If not present,
  533. " `action` defaults to `g:bufExplorerDefaultAction`.
  534. function! BufExplorer(...)
  535. if a:0 >= 1
  536. let action = a:1
  537. else
  538. let action = g:bufExplorerDefaultAction
  539. endif
  540. if a:0 >= 2
  541. echoerr 'Too many arguments'
  542. return
  543. endif
  544. if index(s:actions, action) < 0
  545. echoerr 'Invalid action ' . action
  546. return
  547. endif
  548. if action == 'close'
  549. call s:Close()
  550. return
  551. endif
  552. let [tabNbr, winNbr] = s:FindBufExplorer()
  553. if tabNbr > 0
  554. execute 'keepjumps ' . tabNbr . 'tabnext'
  555. execute 'keepjumps ' . winNbr . 'wincmd w'
  556. return
  557. endif
  558. let name = s:name
  559. if !has("win32")
  560. " On non-Windows boxes, escape the name so that is shows up correctly.
  561. let name = escape(name, "[]")
  562. endif
  563. let s:tabIdAtLaunch = s:MRUEnsureTabId(tabpagenr())
  564. let s:windowAtLaunch = winnr()
  565. " Forget any cached MRU ordering from previous invocations.
  566. unlet! s:mruOrder
  567. let s:raw_buffer_listing = s:GetBufferInfo(0)
  568. call s:MRUGarbageCollectBufs()
  569. call s:MRUGarbageCollectTabs()
  570. " We may have to split the current window.
  571. if s:splitMode != ""
  572. " Save off the original settings.
  573. let [_splitbelow, _splitright] = [&splitbelow, &splitright]
  574. " Set the setting to ours.
  575. let [&splitbelow, &splitright] = [g:bufExplorerSplitBelow, g:bufExplorerSplitRight]
  576. let _size = (s:splitMode == "sp") ? g:bufExplorerSplitHorzSize : g:bufExplorerSplitVertSize
  577. " Split the window either horizontally or vertically.
  578. if _size <= 0
  579. execute 'keepalt ' . s:splitMode
  580. else
  581. execute 'keepalt ' . _size . s:splitMode
  582. endif
  583. " Restore the original settings.
  584. let [&splitbelow, &splitright] = [_splitbelow, _splitright]
  585. " Remember that a split was triggered
  586. let s:didSplit = 1
  587. endif
  588. if !exists("b:displayMode") || b:displayMode != "winmanager"
  589. " Do not use keepalt when opening bufexplorer to allow the buffer that
  590. " we are leaving to become the new alternate buffer
  591. execute "silent keepjumps hide edit".name
  592. endif
  593. " Record BufExplorer's buffer number.
  594. let s:bufExplorerBuffer = bufnr('%')
  595. call s:DisplayBufferList()
  596. " Position the cursor in the newly displayed list on the line representing
  597. " the active buffer. The active buffer is the line with the '%' character
  598. " in it.
  599. execute search("%")
  600. if exists('#User#BufExplorer_Started')
  601. " Notify that BufExplorer has started. This is an opportunity to make
  602. " custom buffer-local mappings and the like.
  603. doautocmd User BufExplorer_Started
  604. endif
  605. endfunction
  606. " Tracks `tabId` at BufExplorer launch.
  607. let s:tabIdAtLaunch = ''
  608. " Tracks window number at BufExplorer launch.
  609. let s:windowAtLaunch = 0
  610. " DisplayBufferList {{{2
  611. function! s:DisplayBufferList()
  612. setlocal buftype=nofile
  613. setlocal modifiable
  614. setlocal noreadonly
  615. setlocal noswapfile
  616. setlocal nowrap
  617. setlocal bufhidden=wipe
  618. call s:MapKeys()
  619. " Wipe out any existing lines in case BufExplorer buffer exists and the
  620. " user had changed any global settings that might reduce the number of
  621. " lines needed in the buffer.
  622. silent keepjumps 1,$d _
  623. call setline(1, s:CreateHelp())
  624. call s:BuildBufferList()
  625. call cursor(s:firstBufferLine, 1)
  626. if !g:bufExplorerResize
  627. normal! zz
  628. endif
  629. setlocal nomodifiable
  630. endfunction
  631. " RedisplayBufferList {{{2
  632. function! s:RedisplayBufferList()
  633. call s:RebuildBufferList()
  634. call s:UpdateHelpStatus()
  635. endfunction
  636. " MapKeys {{{2
  637. function! s:MapKeys()
  638. nnoremap <silent> <buffer> <Plug>(BufExplorer_BufferDelete) :call <SID>RemoveBuffer("delete")<CR>
  639. nnoremap <silent> <buffer> <Plug>(BufExplorer_BufferDeleteForced) :call <SID>RemoveBuffer("force_delete")<CR>
  640. nnoremap <silent> <buffer> <Plug>(BufExplorer_BufferWipe) :call <SID>RemoveBuffer("wipe")<CR>
  641. nnoremap <silent> <buffer> <Plug>(BufExplorer_BufferWipeForced) :call <SID>RemoveBuffer("force_wipe")<CR>
  642. nnoremap <silent> <buffer> <Plug>(BufExplorer_Close) :call <SID>Close()<CR>
  643. nnoremap <silent> <buffer> <Plug>(BufExplorer_OpenBuffer) :call <SID>SelectBuffer()<CR>
  644. nnoremap <silent> <buffer> <Plug>(BufExplorer_OpenBufferAsk) :call <SID>SelectBuffer("ask")<CR>
  645. nnoremap <silent> <buffer> <Plug>(BufExplorer_OpenBufferOriginalWindow) :call <SID>SelectBuffer("original_window")<CR>
  646. nnoremap <silent> <buffer> <Plug>(BufExplorer_OpenBufferSplitAbove) :call <SID>SelectBuffer("split", "st")<CR>
  647. nnoremap <silent> <buffer> <Plug>(BufExplorer_OpenBufferSplitBelow) :call <SID>SelectBuffer("split", "sb")<CR>
  648. nnoremap <silent> <buffer> <Plug>(BufExplorer_OpenBufferSplitLeft) :call <SID>SelectBuffer("split", "vl")<CR>
  649. nnoremap <silent> <buffer> <Plug>(BufExplorer_OpenBufferSplitRight) :call <SID>SelectBuffer("split", "vr")<CR>
  650. nnoremap <silent> <buffer> <Plug>(BufExplorer_OpenBufferTab) :call <SID>SelectBuffer("tab")<CR>
  651. nnoremap <silent> <buffer> <Plug>(BufExplorer_SortByNext) :call <SID>SortSelect()<CR>
  652. nnoremap <silent> <buffer> <Plug>(BufExplorer_SortByPrev) :call <SID>ReverseSortSelect()<CR>
  653. nnoremap <silent> <buffer> <Plug>(BufExplorer_ToggleFindActive) :call <SID>ToggleFindActive()<CR>
  654. nnoremap <silent> <buffer> <Plug>(BufExplorer_ToggleHelp) :call <SID>ToggleHelp()<CR>
  655. nnoremap <silent> <buffer> <Plug>(BufExplorer_ToggleOnlyOneTab) :call <SID>ToggleOnlyOneTab()<CR>
  656. nnoremap <silent> <buffer> <Plug>(BufExplorer_ToggleReverseSort) :call <SID>SortReverse()<CR>
  657. nnoremap <silent> <buffer> <Plug>(BufExplorer_ToggleShowRelativePath) :call <SID>ToggleShowRelativePath()<CR>
  658. nnoremap <silent> <buffer> <Plug>(BufExplorer_ToggleShowTabBuffer) :call <SID>ToggleShowTabBuffer()<CR>
  659. nnoremap <silent> <buffer> <Plug>(BufExplorer_ToggleShowTerminal) :call <SID>ToggleShowTerminal()<CR>
  660. nnoremap <silent> <buffer> <Plug>(BufExplorer_ToggleShowUnlisted) :call <SID>ToggleShowUnlisted()<CR>
  661. nnoremap <silent> <buffer> <Plug>(BufExplorer_ToggleSplitOutPathName) :call <SID>ToggleSplitOutPathName()<CR>
  662. if exists("b:displayMode") && b:displayMode == "winmanager"
  663. nnoremap <buffer> <silent> <tab> :call <SID>SelectBuffer()<CR>
  664. endif
  665. nmap <nowait> <buffer> <2-leftmouse> <Plug>(BufExplorer_OpenBuffer)
  666. nmap <nowait> <buffer> <CR> <Plug>(BufExplorer_OpenBuffer)
  667. nmap <nowait> <buffer> <F1> <Plug>(BufExplorer_ToggleHelp)
  668. nmap <nowait> <buffer> <s-cr> <Plug>(BufExplorer_OpenBufferTab)
  669. nmap <nowait> <buffer> a <Plug>(BufExplorer_ToggleFindActive)
  670. nmap <nowait> <buffer> b <Plug>(BufExplorer_OpenBufferAsk)
  671. nmap <nowait> <buffer> B <Plug>(BufExplorer_ToggleOnlyOneTab)
  672. nmap <nowait> <buffer> d <Plug>(BufExplorer_BufferDelete)
  673. nmap <nowait> <buffer> D <Plug>(BufExplorer_BufferWipe)
  674. nmap <nowait> <buffer> f <Plug>(BufExplorer_OpenBufferSplitBelow)
  675. nmap <nowait> <buffer> F <Plug>(BufExplorer_OpenBufferSplitAbove)
  676. nmap <nowait> <buffer> o <Plug>(BufExplorer_OpenBuffer)
  677. nmap <nowait> <buffer> O <Plug>(BufExplorer_OpenBufferOriginalWindow)
  678. nmap <nowait> <buffer> p <Plug>(BufExplorer_ToggleSplitOutPathName)
  679. nmap <nowait> <buffer> q <Plug>(BufExplorer_Close)
  680. nmap <nowait> <buffer> r <Plug>(BufExplorer_ToggleReverseSort)
  681. nmap <nowait> <buffer> R <Plug>(BufExplorer_ToggleShowRelativePath)
  682. nmap <nowait> <buffer> s <Plug>(BufExplorer_SortByNext)
  683. nmap <nowait> <buffer> S <Plug>(BufExplorer_SortByPrev)
  684. nmap <nowait> <buffer> t <Plug>(BufExplorer_OpenBufferTab)
  685. nmap <nowait> <buffer> T <Plug>(BufExplorer_ToggleShowTabBuffer)
  686. nmap <nowait> <buffer> u <Plug>(BufExplorer_ToggleShowUnlisted)
  687. nmap <nowait> <buffer> v <Plug>(BufExplorer_OpenBufferSplitRight)
  688. nmap <nowait> <buffer> V <Plug>(BufExplorer_OpenBufferSplitLeft)
  689. nmap <nowait> <buffer> X <Plug>(BufExplorer_ToggleShowTerminal)
  690. for k in ["G", "n", "N", "L", "M", "H"]
  691. execute "nnoremap <buffer> <silent>" k ":keepjumps normal!" k."<CR>"
  692. endfor
  693. endfunction
  694. " ToggleHelp {{{2
  695. function! s:ToggleHelp()
  696. let g:bufExplorerDetailedHelp = !g:bufExplorerDetailedHelp
  697. setlocal modifiable
  698. " Save position.
  699. normal! ma
  700. " Remove old header.
  701. if s:firstBufferLine > 1
  702. execute "keepjumps 1,".(s:firstBufferLine - 1) "d _"
  703. endif
  704. call append(0, s:CreateHelp())
  705. silent! normal! g`a
  706. delmarks a
  707. setlocal nomodifiable
  708. if exists("b:displayMode") && b:displayMode == "winmanager"
  709. call WinManagerForceReSize("BufExplorer")
  710. endif
  711. endfunction
  712. " GetHelpStatus {{{2
  713. function! s:GetHelpStatus()
  714. let ret = '" Sorted by '.((g:bufExplorerReverseSort == 1) ? "reverse " : "").g:bufExplorerSortBy
  715. let ret .= ' | '.((g:bufExplorerFindActive == 0) ? "Don't " : "")."Locate buffer"
  716. let ret .= ((g:bufExplorerShowUnlisted == 0) ? "" : " | Show unlisted")
  717. let ret .= ((g:bufExplorerShowTabBuffer == 0) ? "" : " | Show buffers/tab")
  718. let ret .= ((g:bufExplorerOnlyOneTab == 0) ? "" : " | One tab/buffer")
  719. let ret .= ' | '.((g:bufExplorerShowRelativePath == 0) ? "Absolute" : "Relative")
  720. let ret .= ' '.((g:bufExplorerSplitOutPathName == 0) ? "Full" : "Split")." path"
  721. let ret .= ((g:bufExplorerShowTerminal == 0) ? "" : " | Show terminal")
  722. return ret
  723. endfunction
  724. " CreateHelp {{{2
  725. function! s:CreateHelp()
  726. if g:bufExplorerDefaultHelp == 0 && g:bufExplorerDetailedHelp == 0
  727. let s:firstBufferLine = 1
  728. return []
  729. endif
  730. let header = []
  731. if g:bufExplorerDetailedHelp == 1
  732. call add(header, '" Buffer Explorer ('.g:bufexplorer_version.')')
  733. call add(header, '" --------------------------')
  734. call add(header, '" <F1> : toggle this help')
  735. call add(header, '" <enter> or o or Mouse-Double-Click : open buffer under cursor')
  736. call add(header, '" <shift-enter> or t : open buffer in another tab')
  737. call add(header, '" a : toggle find active buffer')
  738. call add(header, '" b : Fast buffer switching with b<any bufnum>')
  739. call add(header, '" B : toggle showing buffers only on their MRU tabs')
  740. call add(header, '" d : delete buffer')
  741. call add(header, '" D : wipe buffer')
  742. call add(header, '" F : open buffer in another window above the current')
  743. call add(header, '" f : open buffer in another window below the current')
  744. call add(header, '" O : open buffer in original window')
  745. call add(header, '" p : toggle splitting of file and path name')
  746. call add(header, '" q : quit')
  747. call add(header, '" r : reverse sort')
  748. call add(header, '" R : toggle showing relative or full paths')
  749. call add(header, '" s : cycle thru "sort by" fields '.string(s:sort_by).'')
  750. call add(header, '" S : reverse cycle thru "sort by" fields')
  751. call add(header, '" T : toggle showing all buffers/only buffers used on this tab')
  752. call add(header, '" u : toggle showing unlisted buffers')
  753. call add(header, '" V : open buffer in another window on the left of the current')
  754. call add(header, '" v : open buffer in another window on the right of the current')
  755. call add(header, '" X : toggle showing terminal buffers')
  756. else
  757. call add(header, '" Press <F1> for Help')
  758. endif
  759. if (!exists("b:displayMode") || b:displayMode != "winmanager") || (b:displayMode == "winmanager" && g:bufExplorerDetailedHelp == 1)
  760. call add(header, s:GetHelpStatus())
  761. call add(header, '"=')
  762. endif
  763. let s:firstBufferLine = len(header) + 1
  764. return header
  765. endfunction
  766. " CalculateBufferDetails {{{2
  767. " Calculate `buf`-related details.
  768. " Only these fields of `buf` must be defined on entry:
  769. " - `._bufnr`
  770. " - `.attributes`
  771. " - `.line`
  772. function! s:CalculateBufferDetails(buf)
  773. let buf = a:buf
  774. let name = bufname(buf._bufnr)
  775. let buf["hasNoName"] = empty(name)
  776. if buf.hasNoName
  777. let name = "[No Name]"
  778. endif
  779. let buf.isterminal = getbufvar(buf._bufnr, '&buftype') == 'terminal'
  780. if buf.isterminal
  781. " Neovim uses paths with `term://` prefix, where the provided path
  782. " is the current working directory when the terminal was launched, e.g.:
  783. " - Unix:
  784. " term://~/tmp/sort//1464953:/bin/bash
  785. " - Windows:
  786. " term://C:\apps\nvim-win64\bin//6408:C:\Windows\system32\cmd.exe
  787. " Vim uses paths starting with `!`, with no provided path, e.g.:
  788. " - Unix:
  789. " !/bin/bash
  790. " - Windows:
  791. " !C:\Windows\system32\cmd.exe
  792. " Use the terminal's current working directory as the `path`.
  793. " For `shortname`, use `!PID:shellName`, prefixed with `!` as Vim does,
  794. " and without the shell's path for brevity, e.g.:
  795. " `/bin/bash` -> `!bash`
  796. " `1464953:/bin/bash` -> `!1464953:bash`
  797. " `C:\Windows\system32\cmd.exe` -> `!cmd.exe`
  798. " `6408:C:\Windows\system32\cmd.exe` -> `!6408:cmd.exe`
  799. " Neovim-style name format:
  800. " term://(cwd)//(pid):(shellPath)
  801. " e.g.:
  802. " term://~/tmp/sort//1464953:/bin/bash
  803. " `cwd` is the directory at terminal launch.
  804. let termNameParts = matchlist(name, '\v\c^term://(.*)//(\d+):(.*)$')
  805. if len(termNameParts) > 0
  806. let [cwd, pidStr, shellPath] = termNameParts[1:3]
  807. let pid = str2nr(pidStr)
  808. let shellName = fnamemodify(shellPath, ':t')
  809. else
  810. " Default to Vim's current working directory.
  811. let cwd = '.'
  812. let shellName = fnamemodify(name, ':t')
  813. let pid = -1
  814. if exists('*term_getjob') && exists('*job_info')
  815. let job = term_getjob(buf._bufnr)
  816. if job != v:null
  817. let pid = job_info(job).process
  818. endif
  819. endif
  820. endif
  821. if pid < 0
  822. let shortname = '!' . shellName
  823. else
  824. let shortname = '!' . pid . ':' . shellName
  825. " On some systems having a `/proc` filesystem (e.g., Linux, *BSD,
  826. " Solaris), each process has a `cwd` symlink for the current working
  827. " directory. `resolve()` will return the actual current working
  828. " directory if possible; otherwise, it will return the symlink path
  829. " unchanged.
  830. let cwd_symlink = '/proc/' . pid . '/cwd'
  831. let resolved_cwd = resolve(cwd_symlink)
  832. if resolved_cwd != cwd_symlink
  833. let cwd = resolved_cwd
  834. endif
  835. endif
  836. let slashed_path = fnamemodify(cwd, ':p')
  837. let buf.fullname = slashed_path . shortname
  838. let buf.shortname = shortname
  839. let homepath = fnamemodify(slashed_path, ':~:h')
  840. let buf.path = homepath
  841. let buf.homename = fnamemodify(buf.fullname, ':~')
  842. let buf.relativepath = fnamemodify(slashed_path, ':~:.:h')
  843. let buf.relativename = fnamemodify(buf.fullname, ':~:.')
  844. return
  845. endif
  846. let buf.fullname = simplify(fnamemodify(name, ':p'))
  847. let buf.isdir = getftype(buf.fullname) == "dir"
  848. if buf.isdir
  849. " `buf.fullname` ends with a path separator; this will be
  850. " removed via the first `:h` applied to `buf.fullname` (except
  851. " for the root directory, where the path separator will remain).
  852. let parent = fnamemodify(buf.fullname, ':h:h')
  853. let buf.shortname = fnamemodify(buf.fullname, ':h:t')
  854. " Special case for root directory: fnamemodify('/', ':h:t') == ''
  855. if buf.shortname == ''
  856. let buf.shortname = '.'
  857. endif
  858. " Must perform shortening (`:~`, `:.`) before `:h`.
  859. let buf.homename = fnamemodify(buf.fullname, ':~:h')
  860. let buf.relativename = fnamemodify(buf.fullname, ':~:.:h')
  861. else
  862. let parent = fnamemodify(buf.fullname, ':h')
  863. let buf.shortname = fnamemodify(buf.fullname, ':t')
  864. let buf.homename = fnamemodify(buf.fullname, ':~')
  865. let buf.relativename = fnamemodify(buf.fullname, ':~:.')
  866. endif
  867. " `:p` on `parent` adds back the path separator which permits more
  868. " effective shortening (`:~`, `:.`), but `:h` is required afterward
  869. " to trim this separator.
  870. let buf.path = fnamemodify(parent, ':p:~:h')
  871. let buf.relativepath = fnamemodify(parent, ':p:~:.:h')
  872. endfunction
  873. " GetBufferInfo {{{2
  874. " Return dictionary `{ bufNbr : buf }`.
  875. " - If `onlyBufNbr > 0`, dictionary will contain at most that buffer.
  876. " On return, only these fields are set for each `buf`:
  877. " - `._bufnr`
  878. " - `.attributes`
  879. " - `.line`
  880. " Other fields will be populated by `s:CalculateBufferDetails()`.
  881. function! s:GetBufferInfo(onlyBufNbr)
  882. redir => bufoutput
  883. " Below, `:silent buffers` allows capturing the output via `:redir` but
  884. " prevents display to the user.
  885. if a:onlyBufNbr > 0 && buflisted(a:onlyBufNbr)
  886. " We care only about the listed buffer `a:onlyBufNbr`, so no need to
  887. " enumerate unlisted buffers.
  888. silent buffers
  889. else
  890. " Use `!` to show all buffers including the unlisted ones.
  891. silent buffers!
  892. endif
  893. redir END
  894. if a:onlyBufNbr > 0
  895. " Since we are only interested in this specified buffer remove the
  896. " other buffers listed.
  897. " Use a very-magic pattern starting with a newline and a run of zero or
  898. " more spaces/tabs:
  899. let onlyLinePattern = '\v\n\s*'
  900. " Continue with the buffer number followed by a non-digit character
  901. " (which will be a buffer attribute character such as `u` or ` `).
  902. let onlyLinePattern .= a:onlyBufNbr . '\D'
  903. " Finish with a run of zero or more non-newline characters plus newline:
  904. let onlyLinePattern .= '[^\n]*\n'
  905. let bufoutput = matchstr("\n" . bufoutput . "\n", onlyLinePattern)
  906. endif
  907. let all = {}
  908. " Loop over each line in the buffer.
  909. for line in split(bufoutput, '\n')
  910. let bits = split(line, '"')
  911. " Use first and last components after the split on '"', in case a
  912. " filename with an embedded '"' is present.
  913. let buf = {"attributes": bits[0], "line": substitute(bits[-1], '\s*', '', '')}
  914. let buf._bufnr = str2nr(buf.attributes)
  915. let all[buf._bufnr] = buf
  916. endfor
  917. return all
  918. endfunction
  919. " BuildBufferList {{{2
  920. function! s:BuildBufferList()
  921. let table = []
  922. " Loop through every buffer.
  923. for buf in values(s:raw_buffer_listing)
  924. " `buf.attributes` must exist, but we defer the expensive work of
  925. " calculating other buffer details (e.g., `buf.fullname`) until we know
  926. " the user wants to view this buffer.
  927. " Skip BufExplorer's buffer.
  928. if buf._bufnr == s:bufExplorerBuffer
  929. continue
  930. endif
  931. " Skip unlisted buffers if we are not to show them.
  932. if !g:bufExplorerShowUnlisted && buf.attributes =~ "u"
  933. " Skip unlisted buffers if we are not to show them.
  934. continue
  935. endif
  936. " Ensure buffer details are computed for this buffer.
  937. if !has_key(buf, 'fullname')
  938. call s:CalculateBufferDetails(buf)
  939. endif
  940. " Skip 'No Name' buffers if we are not to show them.
  941. if g:bufExplorerShowNoName == 0 && buf.hasNoName
  942. continue
  943. endif
  944. " Should we show this buffer in this tab?
  945. if !s:MRUTabShouldShowBuf(s:tabIdAtLaunch, buf._bufnr)
  946. continue
  947. endif
  948. " Skip terminal buffers if we are not to show them.
  949. if !g:bufExplorerShowTerminal && buf.isterminal
  950. continue
  951. endif
  952. " Skip directory buffers if we are not to show them.
  953. if !g:bufExplorerShowDirectories && buf.isdir
  954. continue
  955. endif
  956. let row = [buf.attributes]
  957. if exists("g:loaded_webdevicons")
  958. let row += [WebDevIconsGetFileTypeSymbol(buf.fullname, buf.isdir)]
  959. endif
  960. " Are we to split the path and file name?
  961. if g:bufExplorerSplitOutPathName
  962. let type = (g:bufExplorerShowRelativePath) ? "relativepath" : "path"
  963. let row += [buf.shortname, buf[type]]
  964. else
  965. let type = (g:bufExplorerShowRelativePath) ? "relativename" : "homename"
  966. let row += [buf[type]]
  967. endif
  968. let row += [buf.line]
  969. call add(table, row)
  970. endfor
  971. let lines = s:MakeLines(table)
  972. call setline(s:firstBufferLine, lines)
  973. let firstMissingLine = s:firstBufferLine + len(lines)
  974. if line('$') >= firstMissingLine
  975. " Clear excess lines starting with `firstMissingLine`.
  976. execute "silent keepjumps ".firstMissingLine.',$d _'
  977. endif
  978. call s:SortListing()
  979. endfunction
  980. " MakeLines {{{2
  981. function! s:MakeLines(table)
  982. if len(a:table) == 0
  983. return []
  984. endif
  985. let lines = []
  986. " To avoid trailing whitespace, do not pad the final column.
  987. let numColumnsToPad = len(a:table[0]) - 1
  988. let maxWidths = repeat([0], numColumnsToPad)
  989. for row in a:table
  990. let i = 0
  991. while i < numColumnsToPad
  992. let maxWidths[i] = max([maxWidths[i], s:StringWidth(row[i])])
  993. let i = i + 1
  994. endwhile
  995. endfor
  996. let pads = []
  997. for w in maxWidths
  998. call add(pads, repeat(' ', w))
  999. endfor
  1000. for row in a:table
  1001. let i = 0
  1002. while i < numColumnsToPad
  1003. let row[i] .= strpart(pads[i], s:StringWidth(row[i]))
  1004. let i = i + 1
  1005. endwhile
  1006. call add(lines, join(row, ' '))
  1007. endfor
  1008. return lines
  1009. endfunction
  1010. " SelectBuffer {{{2
  1011. " Valid arguments:
  1012. " `()` Open in current window.
  1013. " `("ask")` Prompt for buffer, then open in current window.
  1014. " `("original_window")` Open in original window.
  1015. " `("split", "st")` Open in horizontal split above current window.
  1016. " `("split", "sb")` Open in horizontal split below current window.
  1017. " `("split", "vl")` Open in vertical split left of current window.
  1018. " `("split", "vr")` Open in vertical split right of current window.
  1019. " `("tab")` Open in a new tab.
  1020. function! s:SelectBuffer(...)
  1021. " Sometimes messages are not cleared when we get here so it looks like an
  1022. " error has occurred when it really has not.
  1023. "echo ""
  1024. let _bufNbr = -1
  1025. if (a:0 == 1) && (a:1 == "ask")
  1026. " Ask the user for input.
  1027. call inputsave()
  1028. let cmd = input("Enter buffer number to switch to: ")
  1029. call inputrestore()
  1030. " Clear the message area from the previous prompt.
  1031. redraw | echo
  1032. if strlen(cmd) > 0
  1033. let _bufNbr = str2nr(cmd)
  1034. else
  1035. call s:Error("Invalid buffer number, try again.")
  1036. return
  1037. endif
  1038. else
  1039. " Are we on a line with a file name?
  1040. if line('.') < s:firstBufferLine
  1041. execute "normal! \<CR>"
  1042. return
  1043. endif
  1044. let _bufNbr = str2nr(getline('.'))
  1045. " Check and see if we are running BufferExplorer via WinManager.
  1046. if exists("b:displayMode") && b:displayMode == "winmanager"
  1047. let _bufName = expand("#"._bufNbr.":p")
  1048. if (a:0 == 1) && (a:1 == "tab")
  1049. call WinManagerFileEdit(_bufName, 1)
  1050. else
  1051. call WinManagerFileEdit(_bufName, 0)
  1052. endif
  1053. return
  1054. endif
  1055. endif
  1056. if bufexists(_bufNbr)
  1057. " Get the tab number where this buffer is located in.
  1058. let tabNbr = s:GetTabNbr(_bufNbr)
  1059. if exists("g:bufExplorerChgWin") && g:bufExplorerChgWin <=winnr("$")
  1060. execute g:bufExplorerChgWin."wincmd w"
  1061. execute "keepjumps keepalt silent b!" _bufNbr
  1062. " Are we supposed to open the selected buffer in a tab?
  1063. elseif (a:0 == 1) && (a:1 == "tab")
  1064. call s:Close()
  1065. " Open a new tab with the selected buffer in it.
  1066. if v:version > 704 || ( v:version == 704 && has('patch2237') )
  1067. " new syntax for last tab as of 7.4.2237
  1068. execute "$tab split +buffer" . _bufNbr
  1069. else
  1070. execute "999tab split +buffer" . _bufNbr
  1071. endif
  1072. " Are we supposed to open the selected buffer in a split?
  1073. elseif (a:0 == 2) && (a:1 == "split")
  1074. call s:Close()
  1075. if (a:2 == "vl")
  1076. execute "vert topleft sb "._bufNbr
  1077. elseif (a:2 == "vr")
  1078. execute "vert belowright sb "._bufNbr
  1079. elseif (a:2 == "st")
  1080. execute "topleft sb "._bufNbr
  1081. else " = sb
  1082. execute "belowright sb "._bufNbr
  1083. endif
  1084. " Are we supposed to open the selected buffer in the original window?
  1085. elseif (a:0 == 1) && (a:1 == "original_window")
  1086. call s:Close()
  1087. execute s:windowAtLaunch . "wincmd w"
  1088. execute "keepjumps keepalt silent b!" _bufNbr
  1089. else
  1090. " Request to open in current (BufExplorer) window.
  1091. if g:bufExplorerFindActive && tabNbr > 0
  1092. " Close BufExplorer window and switch to existing tab/window.
  1093. call s:Close()
  1094. execute tabNbr . "tabnext"
  1095. execute bufwinnr(_bufNbr) . "wincmd w"
  1096. else
  1097. " Use BufExplorer window for the buffer.
  1098. execute "keepjumps keepalt silent b!" _bufNbr
  1099. endif
  1100. endif
  1101. " Make the buffer 'listed' again.
  1102. call setbufvar(_bufNbr, "&buflisted", "1")
  1103. " Call any associated function references. g:bufExplorerFuncRef may be
  1104. " an individual function reference or it may be a list containing
  1105. " function references. It will ignore anything that's not a function
  1106. " reference.
  1107. "
  1108. " See :help FuncRef for more on function references.
  1109. if exists("g:BufExplorerFuncRef")
  1110. if type(g:BufExplorerFuncRef) == 2
  1111. keepj call g:BufExplorerFuncRef()
  1112. elseif type(g:BufExplorerFuncRef) == 3
  1113. for FncRef in g:BufExplorerFuncRef
  1114. if type(FncRef) == 2
  1115. keepj call FncRef()
  1116. endif
  1117. endfor
  1118. endif
  1119. endif
  1120. else
  1121. call s:Error("Sorry, that buffer no longer exists, please select another")
  1122. call s:DeleteBuffer(_bufNbr, "wipe")
  1123. endif
  1124. endfunction
  1125. " RemoveBuffer {{{2
  1126. " Valid `mode` values:
  1127. " - "delete"
  1128. " - "force_delete"
  1129. " - "wipe"
  1130. " - "force_wipe"
  1131. function! s:RemoveBuffer(mode)
  1132. " Are we on a line with a file name?
  1133. if line('.') < s:firstBufferLine
  1134. return
  1135. endif
  1136. let mode = a:mode
  1137. let forced = mode =~# '^force_'
  1138. " These commands are to temporarily suspend the activity of winmanager.
  1139. if exists("b:displayMode") && b:displayMode == "winmanager"
  1140. call WinManagerSuspendAUs()
  1141. end
  1142. let bufNbr = str2nr(getline('.'))
  1143. let buf = s:raw_buffer_listing[bufNbr]
  1144. if !forced && (buf.isterminal || getbufvar(bufNbr, '&modified'))
  1145. if buf.isterminal
  1146. let msg = "Buffer " . bufNbr . " is a terminal"
  1147. else
  1148. let msg = "No write since last change for buffer " . bufNbr
  1149. endif
  1150. " Calling confirm() requires Vim built with dialog option.
  1151. if !has("dialog_con") && !has("dialog_gui")
  1152. call s:Error(msg . "; cannot remove without 'force'")
  1153. return
  1154. endif
  1155. let answer = confirm(msg . "; Remove anyway?", "&Yes\n&No", 2)
  1156. if answer == 1
  1157. let mode = 'force_' . mode
  1158. else
  1159. return
  1160. endif
  1161. endif
  1162. " Okay, everything is good, delete or wipe the buffer.
  1163. call s:DeleteBuffer(bufNbr, mode)
  1164. " Reactivate winmanager autocommand activity.
  1165. if exists("b:displayMode") && b:displayMode == "winmanager"
  1166. call WinManagerForceReSize("BufExplorer")
  1167. call WinManagerResumeAUs()
  1168. end
  1169. endfunction
  1170. " DeleteBuffer {{{2
  1171. " Valid `mode` values:
  1172. " - "delete"
  1173. " - "force_delete"
  1174. " - "wipe"
  1175. " - "force_wipe"
  1176. function! s:DeleteBuffer(bufNbr, mode)
  1177. " This routine assumes that the buffer to be removed is on the current line.
  1178. if a:mode =~# 'delete$' && bufexists(a:bufNbr) && !buflisted(a:bufNbr)
  1179. call s:Error('Buffer ' . a:bufNbr
  1180. \ . ' is unlisted; must `wipe` to remove')
  1181. return
  1182. endif
  1183. try
  1184. " Wipe/Delete buffer from Vim.
  1185. if a:mode == "wipe"
  1186. execute "silent bwipe" a:bufNbr
  1187. elseif a:mode == "force_wipe"
  1188. execute "silent bwipe!" a:bufNbr
  1189. elseif a:mode == "force_delete"
  1190. execute "silent bdelete!" a:bufNbr
  1191. else
  1192. execute "silent bdelete" a:bufNbr
  1193. endif
  1194. catch
  1195. call s:Error(v:exception)
  1196. endtry
  1197. if bufexists(a:bufNbr)
  1198. " Buffer is still present. We may have failed to wipe it, or it may
  1199. " have changed attributes (as `:bd` only makes a buffer unlisted).
  1200. " Regather information on this buffer, update the buffer list, and
  1201. " redisplay.
  1202. let info = s:GetBufferInfo(a:bufNbr)
  1203. let s:raw_buffer_listing[a:bufNbr] = info[a:bufNbr]
  1204. call s:RedisplayBufferList()
  1205. else
  1206. " Delete the buffer from the list on screen.
  1207. setlocal modifiable
  1208. normal! "_dd
  1209. setlocal nomodifiable
  1210. " Delete the buffer from the raw buffer list.
  1211. unlet s:raw_buffer_listing[a:bufNbr]
  1212. endif
  1213. endfunction
  1214. " Close {{{2
  1215. function! s:Close()
  1216. let [tabNbr, winNbr] = s:FindBufExplorer()
  1217. if tabNbr == 0
  1218. return
  1219. endif
  1220. let [curTabNbr, curWinNbr] = [tabpagenr(), winnr()]
  1221. if [tabNbr, winNbr] != [curTabNbr, curWinNbr]
  1222. " User has switched away from the original BufExplorer window.
  1223. " It's unclear how to do better than simply wiping out the
  1224. " BufExplorer buffer.
  1225. execute 'bwipeout ' . s:bufExplorerBuffer
  1226. return
  1227. endif
  1228. " Get only the listed buffers associated with the current tab (up to 2).
  1229. let listed = s:MRUListedBuffersForTab(s:tabIdAtLaunch, 2)
  1230. " If we needed to split the main window, close the split one.
  1231. if s:didSplit
  1232. execute "wincmd c"
  1233. endif
  1234. " Check to see if there are anymore buffers listed.
  1235. if len(listed) == 0
  1236. " Since there are no buffers left to switch to, open a new empty
  1237. " buffers.
  1238. execute "enew"
  1239. else
  1240. " Since there are buffers left to switch to, switch to the previous and
  1241. " then the current.
  1242. for b in reverse(listed[0:1])
  1243. execute "keepjumps silent b ".b
  1244. endfor
  1245. endif
  1246. " Clear any messages.
  1247. echo
  1248. endfunction
  1249. " FindBufExplorer {{{2
  1250. " Return `[tabNbr, winNbr]`; both numbers will be zero if not found.
  1251. function! s:FindBufExplorer()
  1252. let result = [0, 0]
  1253. if s:running
  1254. let numTabs = tabpagenr('$')
  1255. for tabNbr in range(1, numTabs)
  1256. let winNbr = index(tabpagebuflist(tabNbr), s:bufExplorerBuffer) + 1
  1257. if winNbr > 0
  1258. let result = [tabNbr, winNbr]
  1259. break
  1260. endif
  1261. endfor
  1262. endif
  1263. return result
  1264. endfunction
  1265. " ToggleShowTerminal {{{2
  1266. function! s:ToggleShowTerminal()
  1267. let g:bufExplorerShowTerminal = !g:bufExplorerShowTerminal
  1268. call s:RedisplayBufferList()
  1269. endfunction
  1270. " ToggleSplitOutPathName {{{2
  1271. function! s:ToggleSplitOutPathName()
  1272. let g:bufExplorerSplitOutPathName = !g:bufExplorerSplitOutPathName
  1273. call s:RedisplayBufferList()
  1274. endfunction
  1275. " ToggleShowRelativePath {{{2
  1276. function! s:ToggleShowRelativePath()
  1277. let g:bufExplorerShowRelativePath = !g:bufExplorerShowRelativePath
  1278. call s:RedisplayBufferList()
  1279. endfunction
  1280. " ToggleShowTabBuffer {{{2
  1281. function! s:ToggleShowTabBuffer()
  1282. " Forget any cached MRU ordering, as it depends on
  1283. " `g:bufExplorerShowTabBuffer`.
  1284. unlet! s:mruOrder
  1285. let g:bufExplorerShowTabBuffer = !g:bufExplorerShowTabBuffer
  1286. call s:RedisplayBufferList()
  1287. endfunction
  1288. " ToggleOnlyOneTab {{{2
  1289. function! s:ToggleOnlyOneTab()
  1290. let g:bufExplorerOnlyOneTab = !g:bufExplorerOnlyOneTab
  1291. call s:RedisplayBufferList()
  1292. endfunction
  1293. " ToggleShowUnlisted {{{2
  1294. function! s:ToggleShowUnlisted()
  1295. let g:bufExplorerShowUnlisted = !g:bufExplorerShowUnlisted
  1296. call s:RedisplayBufferList()
  1297. endfunction
  1298. " ToggleFindActive {{{2
  1299. function! s:ToggleFindActive()
  1300. let g:bufExplorerFindActive = !g:bufExplorerFindActive
  1301. call s:UpdateHelpStatus()
  1302. endfunction
  1303. " RebuildBufferList {{{2
  1304. function! s:RebuildBufferList()
  1305. setlocal modifiable
  1306. let curPos = getpos('.')
  1307. let num_bufs = s:BuildBufferList()
  1308. call setpos('.', curPos)
  1309. setlocal nomodifiable
  1310. return num_bufs
  1311. endfunction
  1312. " UpdateHelpStatus {{{2
  1313. function! s:UpdateHelpStatus()
  1314. setlocal modifiable
  1315. let text = s:GetHelpStatus()
  1316. call setline(s:firstBufferLine - 2, text)
  1317. setlocal nomodifiable
  1318. endfunction
  1319. " Key_number {{{2
  1320. function! s:Key_number(line)
  1321. let _bufnr = str2nr(a:line)
  1322. let key = [printf('%9d', _bufnr)]
  1323. return key
  1324. endfunction
  1325. " Key_name {{{2
  1326. function! s:Key_name(line)
  1327. let _bufnr = str2nr(a:line)
  1328. let buf = s:raw_buffer_listing[_bufnr]
  1329. let key = [buf.shortname, buf.fullname]
  1330. return key
  1331. endfunction
  1332. " Key_fullpath {{{2
  1333. function! s:Key_fullpath(line)
  1334. let _bufnr = str2nr(a:line)
  1335. let buf = s:raw_buffer_listing[_bufnr]
  1336. let key = [buf.fullname]
  1337. return key
  1338. endfunction
  1339. " Key_extension {{{2
  1340. function! s:Key_extension(line)
  1341. let _bufnr = str2nr(a:line)
  1342. let buf = s:raw_buffer_listing[_bufnr]
  1343. let extension = fnamemodify(buf.shortname, ':e')
  1344. let key = [extension, buf.shortname, buf.fullname]
  1345. return key
  1346. endfunction
  1347. " Key_mru {{{2
  1348. function! s:Key_mru(line)
  1349. let _bufnr = str2nr(a:line)
  1350. let buf = s:raw_buffer_listing[_bufnr]
  1351. let pos = s:MRUOrderForBuf(_bufnr)
  1352. return [printf('%9d', pos), buf.fullname]
  1353. endfunction
  1354. " SortByKeyFunc {{{2
  1355. function! s:SortByKeyFunc(keyFunc)
  1356. let keyedLines = []
  1357. for line in getline(s:firstBufferLine, "$")
  1358. let key = eval(a:keyFunc . '(line)')
  1359. call add(keyedLines, join(key + [line], "\1"))
  1360. endfor
  1361. " Ignore case when sorting by passing `1`:
  1362. call sort(keyedLines, 1)
  1363. if g:bufExplorerReverseSort
  1364. call reverse(keyedLines)
  1365. endif
  1366. let lines = []
  1367. for keyedLine in keyedLines
  1368. call add(lines, split(keyedLine, "\1")[-1])
  1369. endfor
  1370. call setline(s:firstBufferLine, lines)
  1371. endfunction
  1372. " SortReverse {{{2
  1373. function! s:SortReverse()
  1374. let g:bufExplorerReverseSort = !g:bufExplorerReverseSort
  1375. call s:ReSortListing()
  1376. endfunction
  1377. " SortSelect {{{2
  1378. function! s:SortSelect()
  1379. let g:bufExplorerSortBy = get(s:sort_by, index(s:sort_by, g:bufExplorerSortBy) + 1, s:sort_by[0])
  1380. call s:ReSortListing()
  1381. endfunction
  1382. " ReverseSortSelect {{{2
  1383. function! s:ReverseSortSelect()
  1384. let g:bufExplorerSortBy = get(s:sort_by, index(s:sort_by, g:bufExplorerSortBy) - 1, s:sort_by[-1])
  1385. call s:ReSortListing()
  1386. endfunction
  1387. " ReSortListing {{{2
  1388. function! s:ReSortListing()
  1389. setlocal modifiable
  1390. let curPos = getpos('.')
  1391. call s:SortListing()
  1392. call s:UpdateHelpStatus()
  1393. call setpos('.', curPos)
  1394. setlocal nomodifiable
  1395. endfunction
  1396. " SortListing {{{2
  1397. function! s:SortListing()
  1398. call s:SortByKeyFunc("<SID>Key_" . g:bufExplorerSortBy)
  1399. endfunction
  1400. " Error {{{2
  1401. " Display a message using ErrorMsg highlight group.
  1402. function! s:Error(msg)
  1403. echohl ErrorMsg
  1404. echomsg a:msg
  1405. echohl None
  1406. endfunction
  1407. " Warning {{{2
  1408. " Display a message using WarningMsg highlight group.
  1409. function! s:Warning(msg)
  1410. echohl WarningMsg
  1411. echomsg a:msg
  1412. echohl None
  1413. endfunction
  1414. " GetTabNbr {{{2
  1415. function! s:GetTabNbr(bufNbr)
  1416. " Prefer current tab.
  1417. if bufwinnr(a:bufNbr) > 0
  1418. return tabpagenr()
  1419. endif
  1420. " Searching buffer bufno, in tabs.
  1421. for i in range(tabpagenr("$"))
  1422. if index(tabpagebuflist(i + 1), a:bufNbr) != -1
  1423. return i + 1
  1424. endif
  1425. endfor
  1426. return 0
  1427. endfunction
  1428. " GetWinNbr" {{{2
  1429. function! s:GetWinNbr(tabNbr, bufNbr)
  1430. " window number in tabpage.
  1431. let tablist = tabpagebuflist(a:tabNbr)
  1432. " Number: 0
  1433. " String: 1
  1434. " Funcref: 2
  1435. " List: 3
  1436. " Dictionary: 4
  1437. " Float: 5
  1438. if type(tablist) == 3
  1439. return index(tabpagebuflist(a:tabNbr), a:bufNbr) + 1
  1440. else
  1441. return 1
  1442. endif
  1443. endfunction
  1444. " StringWidth" {{{2
  1445. if exists('*strwidth')
  1446. function s:StringWidth(s)
  1447. return strwidth(a:s)
  1448. endfunction
  1449. else
  1450. function s:StringWidth(s)
  1451. return len(a:s)
  1452. endfunction
  1453. endif
  1454. " Winmanager Integration {{{2
  1455. let g:BufExplorer_title = "\[Buf\ List\]"
  1456. call s:Set("g:bufExplorerResize", 1)
  1457. call s:Set("g:bufExplorerMaxHeight", 25) " Handles dynamic resizing of the window.
  1458. " Evaluate a Vimscript expression in the context of this file.
  1459. " This enables debugging of script-local variables and functions from outside
  1460. " the plugin, e.g.:
  1461. " :echo BufExplorer_eval('s:bufMru')
  1462. function! BufExplorer_eval(expr)
  1463. return eval(a:expr)
  1464. endfunction
  1465. " Execute a Vimscript statement in the context of this file.
  1466. " This enables setting script-local variables from outside the plugin, e.g.:
  1467. " :call BufExplorer_execute('let s:bufMru = s:MRUNew(0)')
  1468. function! BufExplorer_execute(statement)
  1469. execute a:statement
  1470. endfunction
  1471. " function! to start display. Set the mode to 'winmanager' for this buffer.
  1472. " This is to figure out how this plugin was called. In a standalone fashion
  1473. " or by winmanager.
  1474. function! BufExplorer_Start()
  1475. let b:displayMode = "winmanager"
  1476. call s:SetLocalSettings()
  1477. call BufExplorer()
  1478. endfunction
  1479. " Returns whether the display is okay or not.
  1480. function! BufExplorer_IsValid()
  1481. return 0
  1482. endfunction
  1483. " Handles dynamic refreshing of the window.
  1484. function! BufExplorer_Refresh()
  1485. let b:displayMode = "winmanager"
  1486. call s:SetLocalSettings()
  1487. call BufExplorer()
  1488. endfunction
  1489. function! BufExplorer_ReSize()
  1490. if !g:bufExplorerResize
  1491. return
  1492. end
  1493. let nlines = min([line("$"), g:bufExplorerMaxHeight])
  1494. execute nlines." wincmd _"
  1495. " The following lines restore the layout so that the last file line is also
  1496. " the last window line. Sometimes, when a line is deleted, although the
  1497. " window size is exactly equal to the number of lines in the file, some of
  1498. " the lines are pushed up and we see some lagging '~'s.
  1499. let pres = getpos(".")
  1500. normal! $
  1501. let _scr = &scrolloff
  1502. let &scrolloff = 0
  1503. normal! z-
  1504. let &scrolloff = _scr
  1505. call setpos(".", pres)
  1506. endfunction
  1507. " Default values {{{2
  1508. call s:Set("g:bufExplorerDisableDefaultKeyMapping", 0) " Do not disable default key mappings.
  1509. call s:Set("g:bufExplorerDefaultAction", 'current') " Default action for `:BufExplorer` with no args.
  1510. call s:Set("g:bufExplorerDefaultHelp", 1) " Show default help?
  1511. call s:Set("g:bufExplorerDetailedHelp", 0) " Show detailed help?
  1512. call s:Set("g:bufExplorerFindActive", 1) " When selecting an active buffer, take you to the window where it is active?
  1513. call s:Set("g:bufExplorerOnlyOneTab", 1) " Show buffer only on MRU tab? (Applies when `g:bufExplorerShowTabBuffer` is true.)
  1514. call s:Set("g:bufExplorerReverseSort", 0) " Sort in reverse order by default?
  1515. call s:Set("g:bufExplorerShowDirectories", 1) " (Dir's are added by commands like ':e .')
  1516. call s:Set("g:bufExplorerShowRelativePath", 0) " Show listings with relative or absolute paths?
  1517. call s:Set("g:bufExplorerShowTabBuffer", 0) " Show only buffer(s) for this tab?
  1518. call s:Set("g:bufExplorerShowUnlisted", 0) " Show unlisted buffers?
  1519. call s:Set("g:bufExplorerShowNoName", 0) " Show 'No Name' buffers?
  1520. call s:Set("g:bufExplorerSortBy", "mru") " Sorting methods are in s:sort_by:
  1521. call s:Set("g:bufExplorerSplitBelow", &splitbelow) " Should horizontal splits be below or above current window?
  1522. call s:Set("g:bufExplorerSplitOutPathName", 1) " Split out path and file name?
  1523. call s:Set("g:bufExplorerSplitRight", &splitright) " Should vertical splits be on the right or left of current window?
  1524. call s:Set("g:bufExplorerSplitVertSize", 0) " Height for a vertical split. If <=0, default Vim size is used.
  1525. call s:Set("g:bufExplorerSplitHorzSize", 0) " Height for a horizontal split. If <=0, default Vim size is used.
  1526. call s:Set("g:bufExplorerShowTerminal", 1) " Show terminal buffers?
  1527. " Default key mapping {{{2
  1528. if !hasmapto('BufExplorer') && g:bufExplorerDisableDefaultKeyMapping == 0
  1529. nnoremap <script> <silent> <unique> <Leader>be :BufExplorer<CR>
  1530. endif
  1531. if !hasmapto('ToggleBufExplorer') && g:bufExplorerDisableDefaultKeyMapping == 0
  1532. nnoremap <script> <silent> <unique> <Leader>bt :ToggleBufExplorer<CR>
  1533. endif
  1534. if !hasmapto('BufExplorerHorizontalSplit') && g:bufExplorerDisableDefaultKeyMapping == 0
  1535. nnoremap <script> <silent> <unique> <Leader>bs :BufExplorerHorizontalSplit<CR>
  1536. endif
  1537. if !hasmapto('BufExplorerVerticalSplit') && g:bufExplorerDisableDefaultKeyMapping == 0
  1538. nnoremap <script> <silent> <unique> <Leader>bv :BufExplorerVerticalSplit<CR>
  1539. endif
  1540. " vim:ft=vim foldmethod=marker sw=4