Repository: nvim-lualine/lualine.nvim (lua)

Issue:
Feature request: Option to display buffer number instead of buffer index
<!--
  Before reporting: search existing issues and
  ensure you are running neovim >= 0.5 and the
  latest version of lualine.
-->

### Self Checks

<!-- Check these boxes by placeing an X in the middle -->
- [x] I'm using the latest lualine.
- [x] I didn't find the issue in exsisting issues or prs.

### How to reproduce the problem
Execute the following commands:
```vimscript
:edit first
:edit second
:bdelete first
:ls
```
Outputs:
```
2 %a   "second"                       line 1
```

### Expected behaviour
The lualine buffer section is consistent with the output of `:ls` (i.e. the lualine buffer number is `2`)

### Actual behaviour
The lualine buffer section is _not_ consistent with the output of `:ls` (i.e. the lualine number is `1`)

### Minimal config to reproduce the issue

```vimscript
call plug#begin("/home/mrcjk/tmp/.local/share/nvim/plugged")
Plug 'nvim-lualine/lualine.nvim'
call plug#end()
lua << END
require'lualine'.setup {
  tabline = {
    lualine_b = {{'buffers', mode = 2}}
  }
}
END
```

Pull request:
This is a rough draft to address #668.
Still needs documentation (and maybe a test spec), which I will add if the feature request is approved.

Patch:
diff --git a/README.md b/README.md
index ead8f1ece..d7f3516ea 100644
--- a/README.md
+++ b/README.md
@@ -442,7 +442,9 @@ sections = {
 
       mode = 0, -- 0: Shows buffer name
                 -- 1: Shows buffer index
-                -- 2: Shows buffer name + buffer index
+                -- 2: Shows buffer name + buffer index 
+                -- 3: Shows buffer number
+                -- 4: Shows buffer name + buffer number
 
       max_length = vim.o.columns * 2 / 3, -- Maximum width of buffers component,
                                           -- it can also be a function that returns
@@ -460,6 +462,12 @@ sections = {
         active = 'lualine_{section}_normal',     -- Color for active buffer.
         inactive = 'lualine_{section}_inactive', -- Color for inactive buffer.
       },
+
+      symbols = {
+        modified = ' ●'       -- Text to show when the buffer is modified
+        alternate_file = '#', -- Text to show to indify the alternate file
+        directory =  '',     -- Text to show when the buffer is a directory
+      },
     }
   }
 }
diff --git a/lua/lualine/components/buffers/buffer.lua b/lua/lualine/components/buffers/buffer.lua
index 3f7268c24..613a140d9 100644
--- a/lua/lualine/components/buffers/buffer.lua
+++ b/lua/lualine/components/buffers/buffer.lua
@@ -20,14 +20,18 @@ function Buffer:is_current()
   return vim.api.nvim_get_current_buf() == self.bufnr
 end
 
+function Buffer:is_alternate()
+  return vim.fn.bufnr('#') == self.bufnr and not self:is_current()
+end
+
 ---setup icons, modified status for buffer
 function Buffer:get_props()
   self.file = modules.utils.stl_escape(vim.api.nvim_buf_get_name(self.bufnr))
   self.buftype = vim.api.nvim_buf_get_option(self.bufnr, 'buftype')
   self.filetype = vim.api.nvim_buf_get_option(self.bufnr, 'filetype')
   local modified = self.options.show_modified_status and vim.api.nvim_buf_get_option(self.bufnr, 'modified')
-  local modified_icon = self.options.icons_enabled and ' ●' or ' +'
-  self.modified_icon = modified and modified_icon or ''
+  self.modified_icon = modified and self.options.symbols.modified or ''
+  self.alternate_file_icon = self:is_alternate() and self.options.symbols.alternate_file or ''
   self.icon = ''
   if self.options.icons_enabled then
     local dev
@@ -43,7 +47,7 @@ function Buffer:get_props()
     elseif self.buftype == 'terminal' then
       dev, _ = require('nvim-web-devicons').get_icon('zsh')
     elseif vim.fn.isdirectory(self.file) == 1 then
-      dev, _ = '', nil
+      dev, _ = self.options.symbols.directory, nil
     else
       dev, _ = require('nvim-web-devicons').get_icon(self.file, vim.fn.expand('#' .. self.bufnr .. ':e'))
     end
@@ -159,14 +163,24 @@ end
 
 function Buffer:apply_mode(name)
   if self.options.mode == 0 then
-    return string.format('%s%s%s', self.icon, name, self.modified_icon)
+    return string.format('%s%s%s%s', self.alternate_file_icon, self.icon, name, self.modified_icon)
   end
 
   if self.options.mode == 1 then
-    return string.format('%s %s%s', self.buf_index or '', self.icon, self.modified_icon)
+    return string.format('%s%s %s%s', self.alternate_file_icon, self.buf_index or '', self.icon, self.modified_icon)
+  end
+
+  if self.options.mode == 2 then
+    return string.format('%s%s %s%s%s', self.alternate_file_icon, self.buf_index or '', self.icon, name, self.modified_icon)
   end
 
-  return string.format('%s %s%s%s', self.buf_index or '', self.icon, name, self.modified_icon)
+  if self.options.mode == 3 then
+    return string.format('%s%s %s%s', self.alternate_file_icon, self.bufnr or '', self.icon, self.modified_icon)
+  end
+
+  -- if self.options.mode == 4 then
+  return string.format('%s%s %s%s%s', self.alternate_file_icon, self.bufnr or '', self.icon, name, self.modified_icon)
+
 end
 
 return Buffer
diff --git a/lua/lualine/components/buffers/init.lua b/lua/lualine/components/buffers/init.lua
index 0276123a5..f427c9e97 100644
--- a/lua/lualine/components/buffers/init.lua
+++ b/lua/lualine/components/buffers/init.lua
@@ -22,6 +22,11 @@ local default_options = {
     active = nil,
     inactive = nil,
   },
+  symbols = {
+    modified = ' ●',
+    alternate_file = '#',
+    directory =  '',
+  }
 }
 
 -- This function is duplicated in tabs


Test patch:
diff --git a/tests/spec/lualine_spec.lua b/tests/spec/lualine_spec.lua
index dc0d8dfda..430fb8869 100644
--- a/tests/spec/lualine_spec.lua
+++ b/tests/spec/lualine_spec.lua
@@ -558,12 +558,12 @@ describe('Lualine', function()
             4: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984" }
             5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
         }
-        |{1: a.txt }
+        |{1: #a.txt }
         {2:}
         {3: b.txt }
         {4:}
         {1: [No Name] }
-        {5:                                                                                             }|
+        {MATCH:{5:%s+}|}
         ]===])
 
         vim.cmd('tabprev')
@@ -577,8 +577,8 @@ describe('Lualine', function()
         |{1: a.txt }
         {2:}
         {3: b.txt }
-        {3: [No Name] }
-        {4:                                                                                             }|
+        {3: #[No Name] }
+        {MATCH:{4:%s+}|}
         ]===])
 
         vim.cmd('tabprev')
@@ -591,11 +591,11 @@ describe('Lualine', function()
             5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
         }
         |{1: a.txt }
-        {1: b.txt }
+        {1: #b.txt }
         {2:}
         {3: [No Name] }
         {4:}
-        {5:                                                                                            }|
+        {MATCH:{5:%s+}|}
         ]===])
       end)
 
@@ -633,7 +633,7 @@ describe('Lualine', function()
             4: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984" }
             5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
         }
-        {MATCH:|{1: %d+  }}
+        {MATCH:|{1: #%d+  }}
         {2:}
         {MATCH:{3: %d+  }}
         {4:}
@@ -652,7 +652,7 @@ describe('Lualine', function()
             4: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984" }
             5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
         }
-        {MATCH:|{1: %d+ a.txt }}
+        {MATCH:|{1: #%d+ a.txt }}
         {2:}
         {MATCH:{3: %d+ b.txt }}
         {4:}
@@ -684,7 +684,7 @@ describe('Lualine', function()
             2: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
             3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
         }
-        |{1: [No Name] + }
+        |{1: [No Name] ● }
         {2:}
         {3:                                                                                                          }|
         ]===])
@@ -751,6 +751,112 @@ describe('Lualine', function()
         {3:                                                                                                              }|
         ]===])
       end)
+
+      it('can show buffer numbers instead of indices (without file names)', function()
+        local conf = vim.deepcopy(tab_conf)
+        conf.tabline.lualine_a = { { 'buffers', mode = 3, max_length = 1e3, icons_enabled = false } }
+        require('lualine').setup(conf)
+        require('lualine').statusline()
+        vim.cmd('e a.txt')
+        vim.cmd('silent! bd #') -- NeoVim 0.5 does not create an unnamed buffer. This ensures consistent results between NeoVim versions.
+        vim.cmd('e b.txt')
+        local bufnr_a = vim.fn.bufnr('a.txt')
+        local bufnr_b = vim.fn.bufnr('b.txt')
+        tabline:expect([===[
+        highlights = {
+            1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" }
+            2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836" }
+            3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" }
+            4: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
+            5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
+        }
+        |{1: #]===] .. bufnr_a .. [===[  }
+        {2:}
+        {3: ]===] .. bufnr_b .. [===[  }
+        {4:}
+        {MATCH:{5:%s+}|}
+        ]===])
+      end)
+
+      it('can show buffer numbers instead of indices (with file names)', function()
+        local conf = vim.deepcopy(tab_conf)
+        conf.tabline.lualine_a = { { 'buffers', mode = 4, max_length = 1e3, icons_enabled = false } }
+        vim.cmd('e a.txt')
+        vim.cmd('silent! bd #') -- NeoVim 0.5 does not create an unnamed buffer. This ensures consistent results between NeoVim versions.
+        vim.cmd('e b.txt')
+        local bufnr_a = vim.fn.bufnr('a.txt')
+        local bufnr_b = vim.fn.bufnr('b.txt')
+        require('lualine').setup(conf)
+        require('lualine').statusline()
+        tabline:expect([===[
+        highlights = {
+            1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" }
+            2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836" }
+            3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" }
+            4: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
+            5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
+        }
+        |{1: #]===] .. bufnr_a .. [===[ a.txt }
+        {2:}
+        {3: ]===] .. bufnr_b .. [===[ b.txt }
+        {4:}
+        {MATCH:{5:%s+}|}
+        ]===])
+      end)
+
+      it('displays alternate buffer correctly when switching buffers', function()
+        local conf = vim.deepcopy(tab_conf)
+        conf.tabline.lualine_a = { { 'buffers', mode = 3, max_length = 1e3, icons_enabled = false } }
+        require('lualine').setup(conf)
+        require('lualine').statusline()
+        vim.cmd('e a.txt')
+        vim.cmd('silent! bd #') -- NeoVim 0.5 does not create an unnamed buffer. This ensures consistent results between NeoVim versions.
+        vim.cmd('e b.txt')
+        local bufnr_a = vim.fn.bufnr('a.txt')
+        local bufnr_b = vim.fn.bufnr('b.txt')
+        tabline:expect([===[
+        highlights = {
+            1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" }
+            2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836" }
+            3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" }
+            4: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
+            5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
+        }
+        |{1: #]===] .. bufnr_a .. [===[  }
+        {2:}
+        {3: ]===] .. bufnr_b .. [===[  }
+        {4:}
+        {MATCH:{5:%s+}|}
+        ]===])
+        vim.cmd('e a.txt')
+        tabline:expect([===[
+        highlights = {
+            1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" }
+            2: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984" }
+            3: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" }
+            4: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
+        }
+        |{1: ]===] .. bufnr_a .. [===[  }
+        {2:}
+        {3: #]===] .. bufnr_b .. [===[  }
+        {MATCH:{4:%s+}|}
+        ]===])
+        vim.cmd('bprev')
+        tabline:expect([===[
+        highlights = {
+            1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" }
+            2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836" }
+            3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828" }
+            4: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
+            5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
+        }
+        |{1: #]===] .. bufnr_a .. [===[  }
+        {2:}
+        {3: ]===] .. bufnr_b .. [===[  }
+        {4:}
+        {MATCH:{5:%s+}|}
+        ]===])
+      end)
     end)
 
     describe('windows component', function()