Improve buffer MRU tracking algorithm#121
Conversation
|
I added one more commit to remove the obsolete documentation for the |
|
I just noticed that the final commit (which removed the documentation for |
|
@jlanzarotta I just realized I've been overlooking an aspect of the But now I see that setting With BufExplorer 7.6.0, perform the following commands: :edit file9.txt
:edit file1.txt
:tabedit file9.txt
:edit file2.txt
:tabedit file9.txt
:edit file3.txtLaunch BufExplorer with Quit BufExplorer and switch to tab 2: Launch BufExplorer with Now restart Vim and change BufExplorer's mode via: let g:bufExplorerOnlyOneTab = 0Repeating the above demo, tab 3 still contains This is the additional mode I'd overlooked. The user can elect to see all buffers ever encountered in a tab (via As this seems like useful behavior, I'll rework this pull request to retain I'm assuming this means I should also restore the |
These can be used to view or modify script-local variables and invoke
script-local functions from outside the plugin.
Examples:
- Display the MRU data structure `s:bufMru`:
:echo BufExplorer_eval('s:bufMru')
- Use `s:MRUGetItems()` to display buffer numbers from this structure in
MRU-order:
:echo BufExplorer_eval('s:MRUGetItems(s:bufMru,0)')
- Re-initialize `s:bufMru`:
:call BufExplorer_execute('let s:bufMru = s:MRUNew(0)')
Commit 3c0b11f (2017-09-18) removed the `B` key that permitted toggling `g:bufExplorerOnlyOneTab`. Restore this command to bring back this documented functionality.
`s:ToggleOnlyOneTab()` lacked the optional parameter to `s:RebuildBufferList()` required to remove excess lines when the number of displayed buffers is expected to decrease. Rather than add this logic, remove the optional argument from `s:RebuildBufferList()` and adjust `s:BuildBufferList()` to remove any excess lines in the event that the line count is too large. This unburdens callers of `s:RebuildBufferList()` from the responsibility of determining how any changes they've made will impact the number of lines in the buffer list.
This provides a way to track the identity of a tab as tabs are created and deleted (which can cause tab numbers to change).
MRU tracking is now done dynamically as buffers come and go, so there is no need to track buffers at plugin startup or session-load time. - Issue jlanzarotta#8 "Bufexplorer MRU sort doesn't work properly if using restore_session.vim" was caused by resetting the MRU list via `s:Reset()` after a session is loaded. Removing the `SessionLoadPost` autocmd that invokes this function resolves this issue. - Issue jlanzarotta#87 "Bufexplorer interferes with the startinsert! command" was caused, as correctly diagnosed by the original poster, by the use of `:normal!` in the `s:CatalogBuffers()` function: ```vim silent execute 'normal! ' . tab . 'gt' ``` `s:CatalogBuffers()` is no longer called at startup, resolving this issue.
Buffer-tracking auto-commands need not be delayed until the `VimEnter` event.
This cleans up excess accumulation of deleted tabs and buffers in the MRU data structures. Tab deletions are expensive to track via `TabClosed` events because those events fire after the tab has already been deleted, so cleanup of dead tabs is handled via garbage collection. Buffer deletions are tracked via `BufDelete` events, so garbage won't build up in typical operation; but autocommands may be suppressed (e.g., via `:noautocmd bdelete`), necessitating garbage collection for buffers as well. To prevent excess garbage build-up caused by many tab deletions between invocations of BufExplorer, tab garbage collection is invoked on `TabClosed` events, for Vim versions where that event is supported.
|
Thanks for the update. I will review and merge in the next day or so. |
Yes, I believe the g:bufExplorerOnlyOneTab code needs to be restored so functionality is not lost. |
In most cases, BufExplorer's own buffer won't be part of the list of buffers because the buffer list is enumerated before creating the `[BufExplorer]` buffer. However, there is an edge case that occurs when the current buffer is an empty unnamed buffer at BufExplorer launch. When Vim starts, an empty unnamed buffer is active, e.g.: ``` vim :ls 1 %a "[No Name]" line 1 ``` Normally, editing a file via `:edit somefile.txt` would allocate a new buffer number; but if the current buffer is empty and unnamed, Vim reuses this buffer instead of allocating a new buffer, e.g.: ``` :edit somefile.txt :ls 1 %a "somefile.txt" line 1 ``` Notice that buffer 1 has been reused as `somefile.txt`. This same thing happens when launching BufExplorer with an empty unnamed buffer active. With BufExplorer 7.6.0, launch Vim and immediately invoke BufExplorer via `\be`; this results in: ``` " Press <F1> for Help " Sorted by mru | Locate buffer | One tab/buffer | Absolute Split path | Show terminal "= 1 %a [BufExplorer] ~/testing line 1 ``` At BufExplorer startup, the buffer list is scanned to acquire buffer numbers and attributes. For performance reasons, the other buffer metadata is gathered at display time. Because the `[BufExplorer]` buffer has taken over buffer 1 by that point, it shows up in the buffer list with that name. In prior BufExplorer versions, the problem presented in a different fashion. Using BufExplorer 7.5.0 or older, launch Vim and enable BufExplorer display of unnamed buffers via: ``` :let g:bufExplorerShowNoName = 1 ``` Now launch BufExplorer via `\be`: ``` " Press <F1> for Help " Sorted by mru | Locate buffer | One tab/buffer | Absolute Split path | Show terminal "= 1 %a [No Name] ~/testing line 1 ``` Buffer 1 appears to be unnamed; but in actuality, it's really the BufExplorer buffer (which is unlisted): ``` :ls! 1u%a- "[BufExplorer]" line 4 ``` The fix is to record the number of BufExplorer's buffer at startup and prevent its display.
UpdateI've updated the
For clarity, I've updated the pull request text with the changes and have included some additional information, consolidated below. I've also included a fix for a historical corner case that would allow BufExplorer's buffer to be incorrectly displayed. OverviewThis pull request provides a new MRU buffer tracking algorithm to:
DetailsFor demonstration purposes, create a temporary directory for testing and create a few test files:
MRU tracking on-demandMRU tracking is now done only on-demand as buffers come and go, so there is no need to track buffers at plugin startup or session-load time. This resolves two issues:
|
|
Fantastic! Let me check the changes out again and give them a test. |
Overview
This pull request provides a new MRU buffer tracking algorithm to:
Fix issue Bufexplorer MRU sort doesn't work properly if using restore_session.vim #8 "Bufexplorer MRU sort doesn't work properly if using restore_session.vim".
Fix issue Bufexplorer interferes with the startinsert! command #87 "Bufexplorer interferes with the startinsert! command".
Treat unlisted buffers as least-recently-used instead of most-recently-used in the MRU sort order.
Remove the now-unneeded status indicator "One tab/buffer" in the BufExplorer status line; also remove the associated undocumented variable
g:bufExplorerOnlyOneTab.Replace the undocumented
mkey (intended for debugging MRU logic) with theBufExplorer_eval()andBufExplorer_execute()general debugging features.Improve speed of MRU tracking.
Details
For demonstration purposes, create a temporary directory for testing and create a few test files:
500files.outand9files.outhave simulated compiler output suitable for use with:cgetfile; for example,9files.outis:MRU tracking is now done only on-demand as buffers come and go, so there is no need to track buffers at plugin startup or session-load time. This resolves two issues:
Issue Bufexplorer MRU sort doesn't work properly if using restore_session.vim #8 "Bufexplorer MRU sort doesn't work properly if using restore_session.vim" was caused by resetting the MRU list via
s:Reset()after a session is loaded. Removing theSessionLoadPostautocmd that invokes this function resolves this issue.Issue Bufexplorer interferes with the startinsert! command #87 "Bufexplorer interferes with the startinsert! command" was caused, as correctly diagnosed by the original poster, by the use of
:normal!in thes:CatalogBuffers()function:s:CatalogBuffers()is no longer called at startup, resolving this issue.Unlisted buffers (as created by
:grepor:cgetfile, for example) are now treated as least-recently-used instead of most-recently-used in the MRU sort order. For example, launch Vim and run these commands:Then launch BufExplorer and press
uto view unlisted buffers.Using BufExplorer 7.6.0, this results in:
Note that the unlisted buffers 3-9 (which have never been opened) are found between buffers 1 and 2 (which were used most recently).
Using the
mrubranch, unlisted buffers are shown last:The undocumented variable
g:bufExplorerOnlyOneTabhas been removed. Commit 3c0b11f (2017-09-18) removed theBkey that permitted toggling this variable (via thes:ToggleOnlyOneTab()function). The variable controlled whether MRU data for a buffer would be gathered globally or on a per-tab basis (and defaulting to per-tab collection). In the new MRU algorithm, this control is not needed because MRU information is gathered and kept independently of whether buffers will be subsequently displayed globally or on a per-tab basis. As a result, the now-unneeded status indicator "One tab/buffer" has been removed, saving space in the BufExplorer status line.The undocumented
mkey (intended for debugging MRU logic) has been removed and replace with more generalBufExplorer_eval()andBufExplorer_execute()debugging features. These can be used to view or modify script-local variables and invoke script-local functions from outside the plugin. For example:To display the MRU data structure
s:bufMru:To use
s:MRUGetItems()to display buffer numbers from this structure in MRU-order:To re-initialize
s:bufMru:The new MRU algorithm gathers buffer MRU data using a doubly linked list to avoid linear searching through arrays, and it defers display-related computations until BufExplorer has been invoked, improving MRU data collection speed. To demonstrate, launch Vim via:
First, visit all files in the argument list to ensure they've all been opened. The repeat the operation while profiling and view
profile.log:Using BufExplorer 7.6.0, this results in:
Using the
mrubranch, this results in: