bufferline.nvim icon indicating copy to clipboard operation
bufferline.nvim copied to clipboard

[Feature request] Sort by MRU

Open mgutz opened this issue 4 years ago • 10 comments

Perhaps there's a way to do this but my neovim lua fu isn't as good as yours. Is there a way to navigate through the most recently used buffers? I often times will live_grep in telescope, open a few buffers, then I want to quickly get back to the most recently used buffers.

BTW, great plugin! I switched from another popular plugin and not going back.

mgutz avatar Jul 18 '21 03:07 mgutz

@mgutz nope, there currently isn't an easy way to do this. You can have a look in the docs for a way to sort buffers based on the last modified timestamp which is close-ish to this but if the idea is to do it based on what was last visited then no no exact solution. It's not a trivial problem to solve since nvim doesn't really expose a list or field for what buffers where last opened, it would require building in a mechanism for tracking buffer visits but in a way where the list wasn't constantly shifting in a disorienting way.

I started over in #97 but have abandoned it for now since there were a few bugs and I don't really have time to look into it for the time being. Anyone is free to try to take that over or open a new PR for it though 🤷🏿‍♂️

akinsho avatar Jul 18 '21 19:07 akinsho

@mgutz so it turns out that nvim does have a field you can use to see what was last used, buried deep inside a helper method called getbufinfo which has a last used field. I found that this is what telescope uses for the buffer picker so I've raised a pr to use that over in #151 can you try that out and see what you think.

Just to be clear it might not be perfect I don't know what mechanism vim is using but as you can tell from the pr it's about 5 lines of code to add, doing it manually will involve a much bigger change so as long as it is "good enough" that's what I'll use. A solution like telescope-frecency or something is a huge chunk of work so don't think I'll aim for something like that for now.

akinsho avatar Jul 19 '21 09:07 akinsho

I have a naive MRU algorithm working but it's a bit wonky on the UI side. MRU, by its nature changes changes the order of buffers on-the-fly. So, when I cycle to the previous buffer, a buffer might have increased in weight giving it a higher priority than what is visually displayed (as it should). The issue is weight is on BufLeave event based on how long it's been viewed. If a user has viewed a file for an arbitrary duration period, then it becomes the MRU. If a user is just switching through tabs quickly, those tabs have not been viewed long enough to warrant a reordering of the MRU stack.

I think the lastused field is in seconds. I use reltime for nanosecond granularity (on linux?) and track each buffer's access. I need less than seconds granularity for switching between buffers. It's like a debounce timeout. The lastused field may be good for some users but I want something more robust. I'm thinking of giving higher weights to modified files, perhaps auto closing files that have reach negative weight based on time decay.

mgutz avatar Jul 19 '21 13:07 mgutz

@mgutz that sounds interesting and along the lines I imagined a more complex solution would require. You can raise what you have as a draft PR and we can go from there. You mentioned some wonky UI? what issues are you having.

Regarding things like weighting based on modified state or not I think these can be more advanced optional/configurable features not necessary for the initial implementation, for example if a buffer was modified not saved but ends up ranking higher than buffers I was just in that seems like a point of preference since I can imagine not all users would want that behaviour by default.

Regarding autoclosing buffers I have pretty strong feelings about this plugin not manipulating buffers automatically ever. Happy to have some sort of hook a user can use to then implement an autoclose function but I definitely don't think a primarily UI plugin should be just closing user buffers, especially if a user forgets this was on and things just start disappearing.

akinsho avatar Jul 19 '21 14:07 akinsho

I keep track of my buffers by MRU with the help of another plugin: mildred/vim-bufmru (it is quite old but works very well)

Then to apply the MRU order to bufferline, in the setup:

    sort_by = function(buffer_a, buffer_b)
      for _, buffer in ipairs(vim.fn.BufMRUList()) do
        if buffer == buffer_a.id then
          return true
        elseif buffer == buffer_b.id then
          return false
        end
      end
      return false
    end

Personally, I like the MRU list to be refreshed when I move my cursor after I cycled through my buffers. If vim-bufmru is installed, you can do that with:

  autocmd CursorMoved * call bufmru#save_change("CursorMoved", 0.1)

I also advise to map :BufMRUPrev<CR> and :BufMRUNext<CR> to your liking in order to cycle through your buffers by MRU.

ecogest avatar Mar 17 '22 11:03 ecogest

I wanted this sort by mru feature so I spent some time pushing a patch up on my forked repo. I am not sure how to do a pull request but it seems as though I did something right because it is showing up on this issue/feature request.

@akinsho let me know what you think.

bluexnine avatar Sep 02 '22 22:09 bluexnine

@mgutz You mentioned you had a "naive mru algorithm". Can you show me your algorithm or describe your implementation? If i get a chance I want to add the weighted time view sort by recording buffer view time by tracking buffer's active time in a set range of time. I believe this is what you were eluding to.

You also mentioned trying to give priority to modified buffers. I think this is a great feature but @akinsho, correct me if wrong but this would be a bigger change/feature. Would be called "sort by multi sorted list". For example, sorting by modified, then sorting by weighted view time, then sorting by mru, ... etc. FYI, sorting by modified files might be achievable through getbufinfo().changedtick.

I agree with @akinsho that closing a buffer is overstepping.

bluexnine avatar Sep 08 '22 17:09 bluexnine

Perhaps there's a way to do this but my neovim lua fu isn't as good as yours. Is there a way to navigate through the most recently used buffers? I often times will live_grep in telescope, open a few buffers, then I want to quickly get back to the most recently used buffers.

BTW, great plugin! I switched from another popular plugin and not going back.

Telescope buffers picker has a sort_mru option now.

shans10 avatar Oct 19 '22 05:10 shans10

I was looking for this feature as well and ended up implementing it on my own config.

It's similar to your naive approach @mgutz, it sorts based on the last time opened in ms, debounced so that you can navigate a bit before ordering occurs. Here's a gist

I used node because on MacOS date %N doesn't work, I'll be looking for something friendlier.

I like your idea of a weighted MRU based on time spent on a file and/or number of modifications, I think I'll try that next to see how it feels in a real work context. Nevertheless I think there's still value in a dumb last accessed MRU.

I'm thinking of creating a PR with this feature, but I'd like to know if there's interest before I do so.

bhugoVilela avatar Sep 26 '23 10:09 bhugoVilela

@bhugoVilela I guess the value would depend on trying it out and seeing how it actually works in practice. Hard to say without something to try. Regarding implementation depending on node would be a no go but you can definitely get the date via vim.{loop|uv} there are some vim.fn functions etc. in either case definitely node isn't necessary for this.

akinsho avatar Sep 26 '23 12:09 akinsho