Brainstorm for a more flexible buffer list

Hello

Current situation

Kakoune code has a BufferManager which is basically a wrapper class around a Vector of buffers: buffer_manager.hh

To walk through this vector, the editor offers 2 basic commands buffer-previous and buffer-next. Internally, they both let cycle_buffer function do the heavy lifting.

There’s also the ga command to go back and forth between 2 buffers.

Problem

The user has very little control about the order in which the buffers are stored in this vector. Over the years, a few issues were raised about this topic:

To summarize, the main problem from a user perspective is that it’s often not very intuitive/predictable which buffer will be displayed when buffer-next is called.

Other editors

A lot of graphical editors, display buffers in “tabs” (like “Firefox tabs”). Using the mouse, these tabs can be freely drag’n’drop, to reorder the buffers. As Kakoune is keyboard centric we have to find another paradigm.

A few ideas

Here are some proposals. They can be complementary or not. Keep in mind that they are just broad ideas, no specific details is set in stone.

  1. a new sort-buffers command. This command can take arguments specifying how to reorder the buffer_manager vector. For example:
  • sort-buffers alphabetical to reorder them by bufname.

  • sort-buffers path to reorder them by filepath.

  • sort-buffers modified to reorder them by last modification date

  • sort-buffers filetype to reorder them filetype

  • Coupled with a -reverse switch to decide the direction of the sorting. This sort-buffers command is great for bulk reordering, but does not offer granular control.

  1. a new move-buffer command. Something like move-buffer qux.txt -1. Example, it will have an effect like [foo.txt, bar.txt, qux.txt][foo.txt, qux.txt, bar.txt]

  2. an interactive *buffers* buffer. By using the new list buffers command, a debug *buffers* is created. It contains the list of current buffers, with names, filetype, dates flags… By editing and then saving this special buffer, the buffer_manager vector is adapted accordingly.

My opinion

I think I prefer option 3 (the interactive *buffers*) for several reasons. Firstly, it only introduces 1 new command which is list <arg>. This command can be useful for other purposes, for example list registers (but that’s another topic entirely).
Secondly, the user is free to use regular Kakoune commands, |sort or whatever tool he knows to deal with this buffer list.

@andreyorst built something quite similar in the plug.kak plugin for the plug-list command.
@occivink also built an interactive buffer in the kakoune-find plugin.


What’s your take on this broad inflexible buffer-list subject?

3 Likes

There’s already a way to list buffers: %val{buflist}. move-buffer is simple, and sufficient to implement the rest in the Kakoune configuration language.

I’ll admit I can never really predict what is next and what is prevous, and I almost never get it right. The only piece of this that I use is that I expect delete-buffer to return to the previous thing I was editing, and it usually does. I’m not sure where I might use it.

On topic of move-buffer command.

Emacs also features bury-buffer to move buffer to the last position in the list, which is quite handy thing.

I also propose move-buffer to have fomrat move-buffer WHERE WHAT so WHAT could be omitted, e.g. move-buffer last would move current buffer to the last position, and move-buffer last foo.txt would do that for foo.txt buffer.

I also thought about creating a buf-list plugin/command that would open interactive split with list of buffers, that will allow opening, closing, saving, updating, and possibly redirecting if move-buffer would become a thing. Since Kakoune doesn’t have tabs, like Vim or Emacs, this would be handy.

How about an arrange-buffers command, which takes zero or more buffer names, and re-arranges the buffer list so the given buffer names are the first in the list. For example, if Kakoune’s current buffer list looks like:

  • foo.txt
  • bar.txt
  • qux.txt
  • *debug*

…and then the user runs:

arrange-buffers bar.txt *debug*

…then the resulting buffer list would be:

  • bar.txt
  • *debug*
  • foo.txt
  • qux.txt

…with the named buffers at the beginning in the given order, and all the other buffers afterward in their original order.

Invoking arrange-buffers with a name that is not a currently-open buffer would be an error.

With this command, it should be possible to implement all the other commands mentioned so far with some shell-scripting — sort-buffers, move-buffer, even a magic *buffers* scratch buffer.

Of course, if we wanted to be even more minmalist, we could make the list of open buffers an option, so we could set it with the regular set-option command rather than a special arrange-buffers command. On the other hand, it would be weird to have a str-list option that you couldn’t use set -add with, or remove things from.

1 Like

The idea of magical buffers like #3 both makes me very excited, and horrified, in nearly equal parts.

I sincerely don’t know how I feel. :slight_smile:

I like both Delapouite’s suggestion #2 and Screwtape’s suggestion.

I’m also fond of Delapouite’s suggestion #3, but I think this is better left as a userscript. I actually already use a special buffer for switching around between buffers.
It’s a little crude but it’s my main way of changing the current buffer. I would definitely like to reorder the buffers by moving the lines around (and also deleting them by removing lines, but that’s already possible).

Thanks all for you input!

I think Screwtape you’re definitely on the right track, because as you mention, arrange-buffers is the only building block needed to allow scripting the other approaches.

I’ll try to come up with a prototype implementation if my C++fu is strong enough.

I would go for Screwtape’s. It’s minimal and expressive in the sense that all the other ideas you had can be implemented on top of it.

I wrote a small script a long ago, that opens a buffer with a buffer list, let me re-order them and when I type :w it maps the ten first buffers on <a-1…0>. It keeps track of these ten buffers in an opt so they do appear in the same order in subsequent calls. (I mention this here, because it’s the same philosophy as Screwtape’s solution and is enough for my buffers needs).

I didn’t want to learn about fifo at the time so it’s a quite rough, writting temp files in /tmp…

I’d like buffers have register concept, so it’s possible to group buffers logically.
For example move some related buffers to register a, then display only those with :buffer --reg=a.

Merged in https://github.com/mawww/kakoune/commit/09375edf546dc2845960adb718db28bdcb220df1

Thanks all!

I ended up publishing my buffer-switcher plugin which makes use of this. As improvements I’m thinking of showing some buffer flags, such as modified/scratch/readonly. Suggestions are welcome too.

1 Like