bufexplorer.vim 60 KB

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