Skip to content

Fix buffer sorting and display issues#119

Merged
jlanzarotta merged 17 commits intojlanzarotta:masterfrom
drmikehenry:sort-display
Feb 18, 2025
Merged

Fix buffer sorting and display issues#119
jlanzarotta merged 17 commits intojlanzarotta:masterfrom
drmikehenry:sort-display

Conversation

@drmikehenry
Copy link
Copy Markdown
Contributor

Overview

This pull request addresses a few issues surrounding buffer sort order and buffer name displaying. The initial motivation for these changes was to ensure that files and directories below a common directory would sort near each other when using the fullpath sort order. With fullpath, the ordering should depend only on each buffer's full path name; historically, however, sorting has been a best-effort function of the displayed strings in the BufExplorer window, leading to some anomalies.

Issues addressed by these changes

  • Sorting by fullname now depends only on the buffer's absolute path using a new sorting mechanism. Other sort modes have been converted to use this mechanism as well.

  • Display of directory buffers is now suppressed when g:bufExplorerShowDirectories == 0 as described in the BufExplorer documentation.

  • Path calculations for the elements in s:types have been normalized. fullpath now has simplify() applied to normalize paths like dir/../file. Trailing path separators for directories have been removed. The shortname <DIRECTORY> for directories has been eliminated; instead, the directory's basename is used as is done for files (except for root directories, where the trailing path separator cannot be removed; a shortname of . is used for this case). A new element, homepath, has been added to s.types; this is fullpath shortened for paths in $HOME and without the trailing path separator. Other displayable paths (path, relativepath, relativename) are similarly shortened for paths in $HOME.

  • Textual substitution of $HOME -> ~ in paths has been eliminated. This substitution could occur anywhere in the path, not just at the start, causing undesirable anomalies. Shortening for paths in $HOME is now done via homename and associated variables from s:types.

  • If the devicons plugin is installed, a buffer's buf.isdir status has been added as a parameter passed to that plugin, allowing the plugin to supply a directory icon for buffer directories.

  • Avoid calculating buffer details until they are needed for display. Users who don't want to see unlisted buffers shouldn't have to pay for the expensive work of calculating buffer details for unlisted buffers, only to have that information ignored. This resolves bufexplorer is slow to open with many unlisted buffers #20 ("bufexplorer is slow to open with many unlisted buffers").

Below are some examples demonstrating the changes

Where "Before" and "After" examples are shown below, "Before" applies to BufExplorer 7.5.0 and "After" applies to this pull request.

fullpath sorting

Start from the bufexplorer/ source directory and invoke Vim as:

vim README.md doc/bufexplorer.txt ~/.bashrc doc/ . .. /rootfile / ~ /etc/hosts /etc

Then launch BufExplorer via \be.

  • Absolute Full path view (uses homename):

    Before:

    " Press <F1> for Help
    " Sorted by fullpath | Locate buffer | One tab/buffer | Absolute Full path | Show terminal
    "=
      8       /                                                   line 0
     11       /etc/                                               line 0
     10       /etc/hosts                                          line 0
      7       /rootfile                                           line 0
      9       ~/                                                  line 0
      3       ~/.bashrc                                           line 0
      5       ~/projects/bufexplorer/                             line 0
      6       ~/projects/bufexplorer/../                          line 0
      4       ~/projects/bufexplorer/doc/                         line 0
      2       ~/projects/bufexplorer/doc/bufexplorer.txt          line 0
      1 %a    ~/projects/bufexplorer/README.md                    line 16
    

    After:

    " Press <F1> for Help
    " Sorted by fullpath | Locate buffer | One tab/buffer | Absolute Full path | Show terminal
    "=
      8       /                                          line 0
     11       /etc                                       line 0
     10       /etc/hosts                                 line 0
      9       ~                                          line 0
      3       ~/.bashrc                                  line 0
      6       ~/projects                                 line 0
      5       ~/projects/bufexplorer                     line 0
      4       ~/projects/bufexplorer/doc                 line 0
      2       ~/projects/bufexplorer/doc/bufexplorer.txt line 0
      1 %a    ~/projects/bufexplorer/README.md           line 16
      7       /rootfile                                  line 0
    
  • Relative Full path view (uses relativename):

    Before:

    " Press <F1> for Help
    " Sorted by fullpath | Locate buffer | One tab/buffer | Relative Full path | Show terminal
    "=
      6       ..                     line 0
      8       /                      line 0
     11       /etc                   line 0
     10       /etc/hosts             line 0
      7       /rootfile              line 0
      4       doc                    line 0
      2       doc/bufexplorer.txt    line 0
      1 %a    README.md              line 16
      9       ~                      line 0
      3       ~/.bashrc              line 0
      5       ~/projects/bufexplorer line 0
    

    After:

    " Press <F1> for Help
    " Sorted by fullpath | Locate buffer | One tab/buffer | Relative Full path | Show terminal
    "=
      8       /                   line 0
     11       /etc                line 0
     10       /etc/hosts          line 0
      9       ~                   line 0
      3       ~/.bashrc           line 0
      6       ~/projects          line 0
      5       .                   line 0
      4       doc                 line 0
      2       doc/bufexplorer.txt line 0
      1 %a    README.md           line 16
      7       /rootfile           line 0
    
  • Absolute Split path view (uses shortname + path):

    Before:

    " Press <F1> for Help
    " Sorted by fullpath | Locate buffer | One tab/buffer | Absolute Split path | Show terminal
    "=
      8       <DIRECTORY>     /                                   line 0
      7       rootfile        /                                   line 0
     11       <DIRECTORY>     /etc                                line 0
     10       hosts           /etc                                line 0
      9       <DIRECTORY>     ~                                   line 0
      3       .bashrc         ~                                   line 0
      5       <DIRECTORY>     ~/projects/bufexplorer              line 0
      1 %a    README.md       ~/projects/bufexplorer              line 16
      6       <DIRECTORY>     ~/projects/bufexplorer/..           line 0
      4       <DIRECTORY>     ~/projects/bufexplorer/doc          line 0
      2       bufexplorer.txt ~/projects/bufexplorer/doc          line 0
    

    After:

    " Press <F1> for Help
    " Sorted by fullpath | Locate buffer | One tab/buffer | Absolute Split path | Show terminal
    "=
      8       .               /                          line 0
     11       etc             /                          line 0
     10       hosts           /etc                       line 0
      9       mike            /home                      line 0
      3       .bashrc         ~                          line 0
      6       projects        ~                          line 0
      5       bufexplorer     ~/projects                 line 0
      4       doc             ~/projects/bufexplorer     line 0
      2       bufexplorer.txt ~/projects/bufexplorer/doc line 0
      1 %a    README.md       ~/projects/bufexplorer     line 16
      7       rootfile        /                          line 0
    
  • Relative Split path view (uses shortname + relativepath):

    Before:

    " Press <F1> for Help
    " Sorted by fullpath | Locate buffer | One tab/buffer | Relative Split path | Show terminal
    "=
      4       <DIRECTORY>     .          line 0
      6       <DIRECTORY>     .          line 0
      9       <DIRECTORY>     .          line 0
      1 %a    README.md       .          line 16
      8       <DIRECTORY>     /          line 0
     11       <DIRECTORY>     /          line 0
      7       rootfile        /          line 0
     10       hosts           /etc       line 0
      2       bufexplorer.txt doc        line 0
      3       .bashrc         ~          line 0
      5       <DIRECTORY>     ~/projects line 0
    

    After:

    " Press <F1> for Help
    " Sorted by fullpath | Locate buffer | One tab/buffer | Relative Split path | Show terminal
    "=
      8       .               /          line 0
     11       etc             /          line 0
     10       hosts           /etc       line 0
      9       mike            /home      line 0
      3       .bashrc         ~          line 0
      6       projects        ~          line 0
      5       bufexplorer     ~/projects line 0
      4       doc             .          line 0
      2       bufexplorer.txt doc        line 0
      1 %a    README.md       .          line 16
      7       rootfile        /          line 0
    

Substitution of ~ with $HOME

To demonstrate the problem of substituting ~ with $HOME, create a test directory and launch Vim as follows:

mkdir -p tmp$HOME
vim tmp$HOME/file.txt

Then invoke BufExplorer via \be. Notice that the file's path was previously incorrectly shortened to tmp~:

  • Before:

    " Press <F1> for Help
    " Sorted by mru | Locate buffer | One tab/buffer | Relative Split path | Show terminal
    "=
      1 %a    file.txt tmp~          line 1
    
  • After:

    " Press <F1> for Help
    " Sorted by mru | Locate buffer | One tab/buffer | Relative Split path | Show terminal
    "=
      1 %a    file.txt tmp/home/mike line 1
    

Also, because of the last-minute nature of the textual substitution of ~ with $HOME, minimum column width calculation could have overestimated how many columns were necessary. This can be seen in the previous Absolute Full path example above, e.g.:

Before:

  2       ~/projects/bufexplorer/doc/bufexplorer.txt          line 0

After:

  2       ~/projects/bufexplorer/doc/bufexplorer.txt line 0

Speed when ignoring unlisted buffers

To demonstrate speed improvements in the presence of many unlisted buffers, create a test directory with many small files:

mkdir -p tmp
for i in tmp/file{1..5000}.txt; do echo text > $i; done

Invoke Vim and search for text in these files:

vim
:grep text tmp/file*.txt

Use Vim's profiling feature to measure timing:

:profile start profile.log | profile func * | profile file *

Then invoke BufExplorer via \be. By default, unlisted buffers are not displayed.

Now stop profiling and exit Vim:

:profile pause | noautocmd qall

profile.log will have timing measurements. Excerpts below focus on the total time of the BufExplorer() function.

  • Before:

    FUNCTIONS SORTED ON TOTAL TIME
    count     total (s)      self (s)  function
        1   0.604716401   0.002036093  BufExplorer()
    
  • After:

    FUNCTIONS SORTED ON TOTAL TIME
    count     total (s)      self (s)  function
        1   0.174561318   0.001017237  BufExplorer()
    

Total BufExplorer time went down from 605 ms to 175 ms when ignoring unlisted buffers.

To test with unlisted buffers active, relaunch Vim, launch BufExplorer with \be, press u to display unlisted buffers, then quit BufExplorer via q.

Now repeat the :grep measurements as above:

:grep text tmp/file*.txt
:profile start profile.log | profile func * | profile file *
\be
:profile pause | noautocmd qall
  • Before:

    FUNCTIONS SORTED ON TOTAL TIME
    count     total (s)      self (s)  function
        1   0.824951205   0.002819013  BufExplorer()
    
  • After:

    FUNCTIONS SORTED ON TOTAL TIME
    count     total (s)      self (s)  function
        1   0.776613797   0.001670610  BufExplorer()
    

This allows quick lookup of buffer details given the buffer number.
The `term://` prefix on the buffer name is Neovim-specific, but the
check of `&buftype` works for both Vim and Neovim.
- Use `simplify()` to normalize paths like `dir/../file`.
- Remove trailing path separator for directories.
- Eliminate the shortname `<DIRECTORY>` for directories; instead, use
  the directory's basename as is done for files.  Handle the special
  case of root directory paths (where the trailing path separator cannot
  be removed); use a shortname of `.` for this case.
- Add `homepath` to `s.types`; this is `fullpath` shortened for paths in
  `$HOME` and without the trailing path separator.  Other displayable
  paths (`path`, `relativepath`, `relativename`) are similarly shortened
  for paths in `$HOME`.
- Undesirable substitution could occur anywhere in the path (not just at
  the start).  Shortening for paths in `$HOME` is now done via
  `homename` and associated variables from `s:types`.
This eliminates anomalies caused by sorting based on the displayed text.

- The sort order no longer changes when toggling `Absolute`/`Relative`
  and `Split`/`Full` display options.

- For `Relative Split path` view, files below the current working
  directory now remain grouped together.  For example, launching Vim via
  this invocation:

      vim README.md LICENSE doc/bufexplorer.txt plugin/bufexplorer.vim \
        ~/.vimrc /etc/passwd

  Previously would lead to this order when sorting by `fullpath`, where
  the files of `bufexplorer/` are not kept together:

      2  h    LICENSE         .      line 1
      1 %a    README.md       .      line 1
      6 #h=   passwd          /etc   line 1
      3  h    bufexplorer.txt doc    line 1
      4  h    bufexplorer.vim plugin line 1
      5  h    .vimrc          ~      line 1

  They are now sorted by the overall `fullpath`:

      6       passwd          /etc   line 0
      5       .vimrc          ~      line 0
      3       bufexplorer.txt doc    line 0
      2       LICENSE         .      line 0
      4       bufexplorer.vim plugin line 0
      1 %a    README.md       .      line 16
This allows a directory icon to be chosen.
Users who don't want to see unlisted buffers shouldn't have to pay for
the expensive work of calculating buffer details for unlisted buffers,
only to have that information ignored.  Calculate an unlisted buffer's
details only after we know the user wants to view them.
@drmikehenry
Copy link
Copy Markdown
Contributor Author

This pull request also includes a change to the method of detecting a terminal window. Instead of checking for the prefix term:// (which is Neovim-specific), test for &buftype == 'terminal' (which works for both Vim and Neovim). This allows Vim terminal buffers to be suppressed via :let g:bufExplorerShowTerminal = 0.

@jlanzarotta
Copy link
Copy Markdown
Owner

Very nice changes along with fantastic explanation. Let me pull the code and give it a try.

Copy link
Copy Markdown
Owner

@jlanzarotta jlanzarotta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

@jlanzarotta jlanzarotta merged commit 8170aaa into jlanzarotta:master Feb 18, 2025
@drmikehenry drmikehenry deleted the sort-display branch February 22, 2025 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bufexplorer is slow to open with many unlisted buffers

3 participants