bufexplorer.vim 60 KB

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