rebelot/heirline.nvim

How to setup tabline separators like in lightline

Closed this issue · 3 comments

Hi, I am migrating from lightline and trying to figure out to add these types of separators:

image

I tried using surround, but it seems to be more well-suited for surrounding characters like ( ). I would like to use the single-character separators for the bufferline.

image

Do you have any recommendations on how it might be implemented with heirline?

so, it is possible, but requires a bit of effort to pull it off nicely.. here it goes:

-- initialise the handle for the buflist. This will be an array of bufnumbers.
-- define a couple of utility functions to check if the component is at the end of the buflist,
-- of if it is right before the active buffer
local bufcache = {}
local function is_last(bufnr)
    return bufcache[#bufcache] == bufnr
end
local function is_before_active(bufnr)
    for i, b in ipairs(bufcache) do
        if b == bufnr then
            return vim.api.nvim_get_current_buf() == bufcache[i + 1]
        end
    end
end

local TablineBufferBlock = {
    {
        condition = function(self)
            return self.is_active
        end,
        hl = { bg = "white" },
        {
            provider = "", -- right full chevron (green on white)
            hl = { fg = "green", bg = "white" },
        },
        { TablinePicker, TablineFileNameBlock, TablineCloseButton },
        {
            provider = "", -- right full chevron (white on green/none)
            hl = function(self)
                if is_last(self.bufnr) then
                    return { fg = "white", bg = "none" }
                else
                    return { fg = "white", bg = "green" }
                end
            end,
        },
    },
    {
        condition = function(self)
            return not self.is_active
        end,
        hl = { bg = "green" },
        { TablinePicker, TablineFileNameBlock, TablineCloseButton },
        {
            init = function(self)
                self.provider = "" -- reset the provider to an empty string
                if is_last(self.bufnr) then
                    self.provider = "" -- full right chevron (green on none)
                    self.hl = { fg = "green", bg = "none" }

                elseif not is_before_active(self.bufnr) then
                    self.provider = "" -- thin right chevron (white on green)
                    self.hl = { fg = "white", bg = "green" }
                end
            end,
        },
    },
}

local BufferLine = utils.make_buflist(
    TablineBufferBlock,
    { provider = "", hl = { fg = "gray" } },
    { provider = "", hl = { fg = "gray" } },
    nil,
    bufcache
)

The difficulty here lies in the fact that both separators and their highlights depend on the current buffer and their relative/absolute position in the buflist. This is much easier to do in the statusline/winbar where fields are usually static

HTH

Thank you for the excellent answer. One minor tweak I made was to add a condition so that it only adds a chevron before the current buffer if that buffer is not the first one in the list.

Another question I have, is it possible to support this but for tabpages also like in Lightline? In Lightline there is a configuration that shows the filename of the current buffer of the current window per tabpage.

I tried this so far:

local TabpageFileFlags = {
  {
    condition = function(self)
      return vim.api.nvim_buf_get_option(self.bufnr, "modified")
    end,
    provider = "+ ",
  },
  {
    condition = function(self)
      return not vim.api.nvim_buf_get_option(self.bufnr, "modifiable")
          or vim.api.nvim_buf_get_option(self.bufnr, "readonly")
    end,
    provider = function(self)
      if vim.api.nvim_buf_get_option(self.bufnr, "buftype") == "terminal" then
        return ""
      else
        return ""
      end
    end,
    hl = { fg = "orange" },
  },
}

local TabpageName = {
  provider = function(self)
    return vim.fn.fnamemodify(vim.api.nvim_buf_get_name(self.bufnr), ":t")
  end
}

local function is_last_tab(tabnr)
  local tabpages = vim.api.nvim_list_tabpages()
  return tabpages[#tabpages] == tabnr
end

local function is_before_active_tab(tabnr)
  local tabpages = vim.api.nvim_list_tabpages()
  for i, t in ipairs(tabpages) do
    if t == tabnr then
      return vim.api.nvim_get_current_buf() == tabpages[i + 1]
    end
  end
end

local Tabpage = {
  init = function(self)
    local winnr = vim.api.nvim_tabpage_get_win(self.tabnr)
    self.bufnr = vim.api.nvim_win_get_buf(winnr)
  end,
  {
    condition = function(self)
      return self.is_active
    end,
    hl = { bg = "white" },
    {
      provider = "", -- right full chevron (green on white)
      hl = { fg = "green", bg = "white" },
    },
    { TabpageName, TabpageFileFlags },
    {
      provider = "", -- right full chevron (white on green/none)
      hl = function(self)
        if is_last_tab(self.tabnr) then
          return { fg = "white", bg = "none" }
        else
          return { fg = "white", bg = "green" }
        end
      end,
    },
  },
  {
    condition = function(self)
      return not self.is_active
    end,
    hl = { bg = "green" },
    { TabpageName, TabpageFileFlags },
    {
      init = function(self)
        self.provider = "" -- reset the provider to an empty string
        if is_last_tab(self.bufnr) then
          self.provider = "" -- full right chevron (green on none)
          self.hl = { fg = "green", bg = "none" }

        elseif not is_before_active_tab(self.bufnr) then
          self.provider = "" -- thin right chevron (white on green)
          self.hl = { fg = "white", bg = "green" }
        end
      end,
    },
  },
}

local Tabline = require("heirline.utils").make_tablist(Tabpage)

A problem I run into is that once I start closing tabs the self.tabnr becomes invalid. Once that happens any calls to calculate the winnr and bufnr throws an exception. Do you have any ideas or recommendations?

you should use but_func argument to make_buflist for this. You can pass a function that returns bufnrs of the current tab. Be aware that there is a difference between tab number and tab id (as for winnr and winid).