neo-tree.nvim icon indicating copy to clipboard operation
neo-tree.nvim copied to clipboard

FEATURE: Show source selector tabs in the tabline

Open pwnalone opened this issue 11 months ago • 12 comments

Did you check the docs?

  • [X] I have read all the docs.

Is your feature request related to a problem? Please describe.

Neotree currently provides the ability to show the source selector tabs in the status line or winbar, but it would be nice if it were possible to show them in the tabline. I use bufferline.nvim and I would prefer that the source selector tabs from Neotree were at the same level as my other buffer tabs (i.e. where the red box is in the screenshot, below).

image

This idea was previous proposed here in the discussion under #425, but it was never implemented.

Describe the solution you'd like.

I'm new to Lua so I'm not sure how this would best be implemented, or if it would even be possible to implement here versus in another plugin like bufferline. The three scenarios that I am able to envision are:

  1. This feature is implemented here.
  2. This feature is implemented in other plugins (e.g. bufferline).
  3. This feature is implemented as an extension that can be used from other plugins like bufferline.

For example, in bufferline's configuration, you can reserve space for a sidebar (e.g. file explorer) so that the buffer tabs do not display over the sidebar. Additionally, you can configure the text that will be displayed above this sidebar.

return {
  'akinsho/bufferline.nvim',
  version = '*',
  dependencies = { 'nvim-tree/nvim-web-devicons' },
  event = { 'VimEnter' },
  init = function()
    vim.opt.termguicolors = true
  end,
  opts = {
    options = {
      offsets = {
        {
          filetype = 'neo-tree',
          text = '<The source selector tabs could go here>',
          text_align = 'center',
          highlight = 'Directory',
        },
      },
    },
  },
}

It seems that the above text key can be a function, and any string that gets returned will be displayed in the tabline above the sidebar. This may provide an easy way to implement this feature, or it may already be possible using some of Neotree's internal functions.

Describe alternatives you've considered.

The alternative that I have considered is to just use the status line or winbar, but my preference, of course, would be to use the tabline. I understand that this feature may not be the preference of most, but I just thought I would create this issue to express my own interest in it, and to see if others would be interested as well.

Additional Context

No response

pwnalone avatar Feb 29 '24 06:02 pwnalone

I definitely love the idea, but long story short, it is very difficult.

The content of the selector can be calculated with require("neo-tree.ui.selector").get().

This is used like this as you can see here. https://github.com/nvim-neo-tree/neo-tree.nvim/blob/459c60317cc1d251f6eb3b6f010d015d5d24b806/lua/neo-tree/ui/selector.lua#L402-L413

If you run this in neovim (:lua require("neo-tree.ui.selector").get()<CR>), you'll see that the string already contains highlighting and other stuffs. However, iirc those tabline plugins are too clever that they calculate the text-width of the text() function and try to adjust the length and so on.

If the plugin had such field as raw where it showed exactly what was passed, it would work, but it is a very difficult task to make separate plugins cooperate. (you know like there are gazillion tabline plugins lol)

For example, tabline needs one extra space to compensate the WinSeparator compared to winbar, but on which side (left or right)? Should that be handled on neo-tree side? or in the bufferline plugin?

IMO this is the reason why it is too difficult to officially support this functionality, but if you have any ideas how it can be implemented I'd love to hear your opinion! Don't get me wrong, I like the idea but I just can't come up with a reliable way to achieve this.


If you have the time and knowledge, all of the building blocks are in this single file and it is relatively well documented.

I'm the main author of this code (with a lot of help of others ofc!) so ask me if you have any questions.

pysan3 avatar Feb 29 '24 06:02 pysan3

Hmm lovely...

image

diff --git a/lua/bufferline/offset.lua b/lua/bufferline/offset.lua
index 0315107..42aa3fb 100644
--- a/lua/bufferline/offset.lua
+++ b/lua/bufferline/offset.lua
@@ -28,7 +28,7 @@ local supported_win_types = {
 ---@param is_left boolean?
 ---@return string
 local function get_section_text(size, highlight, offset, is_left)
-  local text = offset.text
+  local text = offset.raw or offset.text
 
   if type(text) == "function" then text = text() end
   text = text or padding:rep(size - 2)
@@ -36,7 +36,9 @@ local function get_section_text(size, highlight, offset, is_left)
   local text_size, left, right = api.nvim_strwidth(text), 0, 0
   local alignment = offset.text_align or "center"
 
-  if text_size + 2 >= size then
+  if offset.raw then
+    -- skip (left = 0, right = 0)
+  elseif text_size + 2 >= size then
     text, left, right = utils.truncate_name(text, size - 2), 1, 1
   else
     local remainder = size - text_size

But I'm not motivated enough to send them a PR.

You can do whatever you want with my code. Maybe if you send them this link, they might be kind enough to add the functionality.

Uploaded the code just in case: https://github.com/pysan3/bufferline.nvim/tree/bufferline-offset-raw-text

pysan3 avatar Feb 29 '24 07:02 pysan3

Thanks for the quick reply and for the solution! I checked out your fork and it is exactly what I was looking for, but when testing it out in preparation for making a PR, I did notice one issue that would probably prevent it from being accepted into bufferline.

When you open neotree and then shift focus away from it without closing the sidebar, the source selector tabs disappear and the buffer tabs are no longer offset properly.

image

I have a feeling that this might be a simple fix, so I will take a look and see if I can get it working with my limited Lua knowledge.

pwnalone avatar Feb 29 '24 08:02 pwnalone

Yikes... You are right, and there's no good way to fix it actually.

.get() assumes that the nvim_api_current_win() is a neo-tree window.

So you need some specific implementation to cache the result, and keep displaying it when you're out of neo-tree, or you can add an event_handler for neo-tree to udpate this global variable on "neo_tree_buffer_enter". Nah it's getting more and more complex lol.

_G.__cached_neo_tree_selector = nil
_G.__get_neo_tree_selector = function()
  local str = require("neo-tree.ui.selector").get()
  if str then
    _G.__cached_neo_tree_selector = str
  end
  return _G.__cached_neo_tree_selector
end

-- setup({
  opts = {
    options = {
      offsets = {
        {
          filetype = "neo-tree",
          raw = " %{%v:lua._G.__get_neo_tree_selector()%} ",
          highlight = { sep = { link = "WinSeparator" } },
          separator = "┃",
        },
      },

pysan3 avatar Feb 29 '24 08:02 pysan3

OK final working solution, which is very hacky. I do not maintain this code and I don't know when it'll break.

cc @pwnalone

-- neo-tree
-- require("neo-tree").setup({
    event_handlers = {
      {
        event = "after_render",
        handler = function(state)
          if state.current_position == "left" or state.current_position == "right" then
            vim.api.nvim_win_call(state.winid, function()
              local str = require("neo-tree.ui.selector").get()
              if str then
                _G.__cached_neo_tree_selector = str
              end
            end)
          end
        end,
      },
    }
-- end

-- bufferline
_G.__cached_neo_tree_selector = nil
_G.__get_selector = function()
  return _G.__cached_neo_tree_selector
end

-- require("bufferline.nvim").setup({
    options = {
      offsets = {
        {
          filetype = "neo-tree",
          raw = " %{%v:lua.__get_selector()%} ",
          highlight = { sep = { link = "WinSeparator" } },
          separator = "┃",
        },
      },
-- end

pysan3 avatar Feb 29 '24 08:02 pysan3

@pysan3

Thank you so much for looking into this and coming up with a working solution!

Do you think this would be something that I could convince the maintainers of bufferline to include in their code base? Or is it too specific to neotree and wouldn't provide any benefit for other file explorers like nvim-tree?

pwnalone avatar Feb 29 '24 20:02 pwnalone

The change needed for bufferline is the raw parameter which is not limited to trees but should open more possibilities to any plugin.

I don't think nvim tree has any winbar functionality (in my old memory) so they won't benefit from this?

My concerns are...

  • Shall we add it to the docs or keep it like an undocumented secret feature.
  • Shall we also add this raw keyword to not only offsets (sidebars) but also to normal buffers?
  • Shall we also allow raw() like text can take functions as well?
    • Which will make implementation a bit more complicated and will be too much for an undocumented feature imo
    • My snippet will benefit from this as now I must make a dumb wrapper to fetch the value.

pysan3 avatar Mar 02 '24 00:03 pysan3

@pwnalone As there's nothing to be done in neo-tree, can we close this issue?

@cseickel Do you think this snippet is worth being added to the wiki? https://github.com/nvim-neo-tree/neo-tree.nvim/issues/1368#issuecomment-1970678429

pysan3 avatar Mar 03 '24 07:03 pysan3

@cseickel Do you think this snippet is worth being added to the wiki?

The answer to that question is always yes.

cseickel avatar Mar 03 '24 15:03 cseickel

Could you do it for me as I don't know how to interact with the wiki...

pysan3 avatar Mar 03 '24 17:03 pysan3

Could you do it for me as I don't know how to interact with the wiki...

Sorry,the point of the wiki is for community self help and sharing, I'm not going to get in the business of adding other people's stuff for them. I think you are more than capable of finding the edit button.

cseickel avatar Mar 03 '24 19:03 cseickel

@pwnalone Did you make a PR/FR upstream?

Anyways please close this issue if my solution is working / you are satisfied.

pysan3 avatar Mar 09 '24 11:03 pysan3

Hi, @pysan3, sorry for the late reply; I had an urgent matter to attend to. I have not had a chance to make a PR yet, but I will close this issue in the meantime, since there is nothing left that needs to be done on neo-tree's side of things.

Thank you again for your help in this and for providing a working solution!

pwnalone avatar Mar 10 '24 20:03 pwnalone

Thanks for your reply @pwnalone .

Please ping me when you make the regarding PR/FR.

pysan3 avatar Mar 11 '24 01:03 pysan3

Hi @pwnalone, just wanted to chime in and say thanks for the patch / fork! Works like a charm.

so-rose avatar Mar 17 '24 15:03 so-rose

@so-rose Thanks, but @pysan3 is the one who made the patch/fork. I just created the feature request, which was actually first proposed by @cseickel in #425, so I can't really take any of the credit.

pwnalone avatar Mar 18 '24 20:03 pwnalone