bufferline.lua 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. local M = {}
  2. local function is_ft(b, ft)
  3. return vim.bo[b].filetype == ft
  4. end
  5. local function diagnostics_indicator(num, _, diagnostics, _)
  6. local result = {}
  7. local symbols = {
  8. error = lvim.icons.diagnostics.Error,
  9. warning = lvim.icons.diagnostics.Warning,
  10. info = lvim.icons.diagnostics.Information,
  11. }
  12. if not lvim.use_icons then
  13. return "(" .. num .. ")"
  14. end
  15. for name, count in pairs(diagnostics) do
  16. if symbols[name] and count > 0 then
  17. table.insert(result, symbols[name] .. " " .. count)
  18. end
  19. end
  20. result = table.concat(result, " ")
  21. return #result > 0 and result or ""
  22. end
  23. local function custom_filter(buf, buf_nums)
  24. local logs = vim.tbl_filter(function(b)
  25. return is_ft(b, "log")
  26. end, buf_nums or {})
  27. if vim.tbl_isempty(logs) then
  28. return true
  29. end
  30. local tab_num = vim.fn.tabpagenr()
  31. local last_tab = vim.fn.tabpagenr "$"
  32. local is_log = is_ft(buf, "log")
  33. if last_tab == 1 then
  34. return true
  35. end
  36. -- only show log buffers in secondary tabs
  37. return (tab_num == last_tab and is_log) or (tab_num ~= last_tab and not is_log)
  38. end
  39. M.config = function()
  40. lvim.builtin.bufferline = {
  41. active = true,
  42. on_config_done = nil,
  43. keymap = {
  44. normal_mode = {},
  45. },
  46. highlights = {
  47. background = {
  48. italic = true,
  49. },
  50. buffer_selected = {
  51. bold = true,
  52. },
  53. },
  54. options = {
  55. themable = true, -- whether or not bufferline highlights can be overridden externally
  56. -- style_preset = preset,
  57. get_element_icon = nil,
  58. show_duplicate_prefix = true,
  59. duplicates_across_groups = true,
  60. auto_toggle_bufferline = true,
  61. move_wraps_at_ends = false,
  62. groups = { items = {}, options = { toggle_hidden_on_enter = true } },
  63. mode = "buffers", -- set to "tabs" to only show tabpages instead
  64. numbers = "none", -- can be "none" | "ordinal" | "buffer_id" | "both" | function
  65. close_command = function(bufnr) -- can be a string | function, see "Mouse actions"
  66. M.buf_kill("bd", bufnr, false)
  67. end,
  68. right_mouse_command = "vert sbuffer %d", -- can be a string | function, see "Mouse actions"
  69. left_mouse_command = "buffer %d", -- can be a string | function, see "Mouse actions"
  70. middle_mouse_command = nil, -- can be a string | function, see "Mouse actions"
  71. indicator = {
  72. icon = lvim.icons.ui.BoldLineLeft, -- this should be omitted if indicator style is not 'icon'
  73. style = "icon", -- can also be 'underline'|'none',
  74. },
  75. buffer_close_icon = lvim.icons.ui.Close,
  76. modified_icon = lvim.icons.ui.Circle,
  77. close_icon = lvim.icons.ui.BoldClose,
  78. left_trunc_marker = lvim.icons.ui.ArrowCircleLeft,
  79. right_trunc_marker = lvim.icons.ui.ArrowCircleRight,
  80. --- name_formatter can be used to change the buffer's label in the bufferline.
  81. --- Please note some names can/will break the
  82. --- bufferline so use this at your discretion knowing that it has
  83. --- some limitations that will *NOT* be fixed.
  84. name_formatter = function(buf) -- buf contains a "name", "path" and "bufnr"
  85. -- remove extension from markdown files for example
  86. if buf.name:match "%.md" then
  87. return vim.fn.fnamemodify(buf.name, ":t:r")
  88. end
  89. end,
  90. max_name_length = 18,
  91. max_prefix_length = 15, -- prefix used when a buffer is de-duplicated
  92. truncate_names = true, -- whether or not tab names should be truncated
  93. tab_size = 18,
  94. diagnostics = "nvim_lsp",
  95. diagnostics_update_in_insert = false,
  96. diagnostics_indicator = diagnostics_indicator,
  97. -- NOTE: this will be called a lot so don't do any heavy processing here
  98. custom_filter = custom_filter,
  99. offsets = {
  100. {
  101. filetype = "undotree",
  102. text = "Undotree",
  103. highlight = "PanelHeading",
  104. padding = 1,
  105. },
  106. {
  107. filetype = "NvimTree",
  108. text = "Explorer",
  109. highlight = "PanelHeading",
  110. padding = 1,
  111. },
  112. {
  113. filetype = "DiffviewFiles",
  114. text = "Diff View",
  115. highlight = "PanelHeading",
  116. padding = 1,
  117. },
  118. {
  119. filetype = "flutterToolsOutline",
  120. text = "Flutter Outline",
  121. highlight = "PanelHeading",
  122. },
  123. {
  124. filetype = "lazy",
  125. text = "Lazy",
  126. highlight = "PanelHeading",
  127. padding = 1,
  128. },
  129. },
  130. color_icons = true, -- whether or not to add the filetype icon highlights
  131. show_buffer_icons = lvim.use_icons, -- disable filetype icons for buffers
  132. show_buffer_close_icons = lvim.use_icons,
  133. show_close_icon = false,
  134. show_tab_indicators = true,
  135. persist_buffer_sort = true, -- whether or not custom sorted buffers should persist
  136. -- can also be a table containing 2 custom separators
  137. -- [focused and unfocused]. eg: { '|', '|' }
  138. separator_style = "thin",
  139. enforce_regular_tabs = false,
  140. always_show_bufferline = false,
  141. hover = {
  142. enabled = false, -- requires nvim 0.8+
  143. delay = 200,
  144. reveal = { "close" },
  145. },
  146. sort_by = "id",
  147. debug = { logging = false },
  148. },
  149. }
  150. end
  151. M.setup = function()
  152. require("lvim.keymappings").load(lvim.builtin.bufferline.keymap)
  153. local status_ok, bufferline = pcall(require, "bufferline")
  154. if not status_ok then
  155. return
  156. end
  157. -- can't be set in settings.lua because default tabline would flash before bufferline is loaded
  158. vim.opt.showtabline = 2
  159. bufferline.setup {
  160. options = lvim.builtin.bufferline.options,
  161. highlights = lvim.builtin.bufferline.highlights,
  162. }
  163. if lvim.builtin.bufferline.on_config_done then
  164. lvim.builtin.bufferline.on_config_done()
  165. end
  166. end
  167. --stylua: ignore
  168. -- Common kill function for bdelete and bwipeout
  169. -- credits: based on bbye and nvim-bufdel
  170. ---@param kill_command? string defaults to "bd"
  171. ---@param bufnr? number defaults to the current buffer
  172. ---@param force? boolean defaults to false
  173. function M.buf_kill(kill_command, bufnr, force)
  174. kill_command = kill_command or "bd"
  175. local bo = vim.bo
  176. local api = vim.api
  177. local fmt = string.format
  178. local fn = vim.fn
  179. if bufnr == 0 or bufnr == nil then
  180. bufnr = api.nvim_get_current_buf()
  181. end
  182. local bufname = api.nvim_buf_get_name(bufnr)
  183. if not force then
  184. local choice
  185. if bo[bufnr].modified then
  186. choice = fn.confirm(fmt([[Save changes to "%s"?]], bufname), "&Yes\n&No\n&Cancel")
  187. if choice == 1 then
  188. vim.api.nvim_buf_call(bufnr, function()
  189. vim.cmd("w")
  190. end)
  191. elseif choice == 2 then
  192. force = true
  193. else return
  194. end
  195. elseif api.nvim_get_option_value("buftype", { buf = 0 }) == "terminal" then
  196. choice = fn.confirm(fmt([[Close "%s"?]], bufname), "&Yes\n&No\n&Cancel")
  197. if choice == 1 then
  198. force = true
  199. else
  200. return
  201. end
  202. end
  203. end
  204. -- Get list of windows IDs with the buffer to close
  205. local windows = vim.tbl_filter(function(win)
  206. return api.nvim_win_get_buf(win) == bufnr
  207. end, api.nvim_list_wins())
  208. if force then
  209. kill_command = kill_command .. "!"
  210. end
  211. -- Get list of active buffers
  212. local buffers = vim.tbl_filter(function(buf)
  213. return api.nvim_buf_is_valid(buf) and bo[buf].buflisted
  214. end, api.nvim_list_bufs())
  215. -- If there is only one buffer (which has to be the current one), vim will
  216. -- create a new buffer on :bd.
  217. -- For more than one buffer, pick the previous buffer (wrapping around if necessary)
  218. if #buffers > 1 and #windows > 0 then
  219. for i, v in ipairs(buffers) do
  220. if v == bufnr then
  221. local prev_buf_idx = i == 1 and #buffers or (i - 1)
  222. local prev_buffer = buffers[prev_buf_idx]
  223. for _, win in ipairs(windows) do
  224. api.nvim_win_set_buf(win, prev_buffer)
  225. end
  226. end
  227. end
  228. end
  229. -- Check if buffer still exists, to ensure the target buffer wasn't killed
  230. -- due to options like bufhidden=wipe.
  231. if api.nvim_buf_is_valid(bufnr) and bo[bufnr].buflisted then
  232. vim.cmd(string.format("%s %d", kill_command, bufnr))
  233. end
  234. end
  235. return M