From d5f1c8fcfee4d26d709d5cb87e84ad5c0d0dd1d4 Mon Sep 17 00:00:00 2001 From: Iron-E Date: Sun, 9 Apr 2023 17:35:13 -0400 Subject: [PATCH 1/5] ref: granularize imports --- lua/barbar.lua | 24 +-- lua/barbar/animate.lua | 2 +- lua/barbar/api.lua | 92 ++++----- lua/barbar/bbye.lua | 15 +- lua/barbar/config.lua | 11 +- lua/barbar/events.lua | 47 ++--- lua/barbar/fs.lua | 74 ++++++- lua/barbar/highlight.lua | 212 ++++++++++---------- lua/barbar/icons.lua | 108 +++++----- lua/barbar/jump_mode.lua | 74 +++---- lua/barbar/state.lua | 61 ++---- lua/barbar/ui/layout.lua | 68 +++---- lua/barbar/ui/nodes.lua | 82 ++++---- lua/barbar/ui/render.lua | 151 +++++++------- lua/barbar/utils.lua | 346 +++------------------------------ lua/barbar/utils/highlight.lua | 169 ++++++++++++++++ lua/barbar/utils/list.lua | 43 ++++ lua/barbar/utils/table.lua | 45 +++++ lua/bufferline.lua | 2 +- lua/bufferline/animate.lua | 2 +- lua/bufferline/api.lua | 2 +- lua/bufferline/bbye.lua | 2 +- lua/bufferline/buffer.lua | 2 +- lua/bufferline/config.lua | 2 +- lua/bufferline/events.lua | 2 +- lua/bufferline/highlight.lua | 2 +- lua/bufferline/icons.lua | 2 +- lua/bufferline/jump_mode.lua | 2 +- lua/bufferline/layout.lua | 2 +- lua/bufferline/render.lua | 2 +- lua/bufferline/state.lua | 2 +- lua/bufferline/utils.lua | 26 ++- plugin/barbar.lua | 6 +- 33 files changed, 844 insertions(+), 838 deletions(-) create mode 100644 lua/barbar/utils/highlight.lua create mode 100644 lua/barbar/utils/list.lua create mode 100644 lua/barbar/utils/table.lua diff --git a/lua/barbar.lua b/lua/barbar.lua index ec02e263..dc262463 100644 --- a/lua/barbar.lua +++ b/lua/barbar.lua @@ -6,19 +6,19 @@ local command = vim.api.nvim_command --- @type function local create_user_command = vim.api.nvim_create_user_command --- @type function local set_option = vim.api.nvim_set_option --- @type function -local api = require'barbar.api' -local bbye = require'barbar.bbye' -local events = require'barbar.events' -local notify = require'barbar.utils'.notify -local render = require'barbar.ui.render' -local state = require'barbar.state' -local utils = require'barbar.utils' +local api = require('barbar.api') +local bbye = require('barbar.bbye') +local events = require('barbar.events') +local markdown_inline_code = require('barbar.utils').markdown_inline_code +local notify = require('barbar.utils').notify +local scroll = require('barbar.ui.render').scroll +local state = require('barbar.state') ------------------------------- -- Section: `barbar` module ------------------------------- ---- @class barbar +--- @class Barbar local barbar = {} --- Setup this plugin. @@ -47,7 +47,7 @@ function barbar.setup(options) local index = tonumber(tbl.args) if not index then return notify( - 'Invalid argument to ' .. utils.markdown_inline_code':BufferGoto', + 'Invalid argument to ' .. markdown_inline_code':BufferGoto', vim.log.levels.ERROR ) end @@ -82,7 +82,7 @@ function barbar.setup(options) vim.api.nvim_cmd and function(tbl) vim.cmd.BufferMovePrevious {count = tbl.count} end or function(tbl) command('BufferMovePrevious ' .. tbl.count) end, - {count = true, desc = 'Synonym for ' .. utils.markdown_inline_code':BufferMovePrevious'} + {count = true, desc = 'Synonym for ' .. markdown_inline_code':BufferMovePrevious'} ) create_user_command( @@ -188,13 +188,13 @@ function barbar.setup(options) create_user_command( 'BufferScrollLeft', - function(tbl) render.scroll(-max(1, tbl.count)) end, + function(tbl) scroll(-max(1, tbl.count)) end, {count = true, desc = 'Scroll the bufferline left'} ) create_user_command( 'BufferScrollRight', - function(tbl) render.scroll(max(1, tbl.count)) end, + function(tbl) scroll(max(1, tbl.count)) end, {count = true, desc = 'Scroll the bufferline right'} ) diff --git a/lua/barbar/animate.lua b/lua/barbar/animate.lua index 9ba4603c..43b1418d 100644 --- a/lua/barbar/animate.lua +++ b/lua/barbar/animate.lua @@ -17,7 +17,7 @@ local schedule_wrap = vim.schedule_wrap --- @field timer userdata --- @field type unknown ---- @class barbar.animate +--- @class barbar.Animate local animate = {} --- The amount of time between rendering the next part of the animation. diff --git a/lua/barbar/api.lua b/lua/barbar/api.lua index baf6d046..58d9f4e9 100644 --- a/lua/barbar/api.lua +++ b/lua/barbar/api.lua @@ -16,15 +16,17 @@ local set_current_buf = vim.api.nvim_set_current_buf --- @type function -- TODO: remove `vim.fs and` after 0.8 release local normalize = vim.fs and vim.fs.normalize -local animate = require'barbar.animate' -local bbye = require'barbar.bbye' -local Buffer = require'barbar.buffer' -local config = require'barbar.config' -local JumpMode = require'barbar.jump_mode' -local Layout = require'barbar.ui.layout' -local render = require'barbar.ui.render' -local state = require'barbar.state' -local utils = require'barbar.utils' +local animate = require('barbar.animate') +local bdelete = require('barbar.bbye').bdelete +local buffer = require('barbar.buffer') +local config = require('barbar.config') +local index_of = require('barbar.utils.list').index_of +local is_relative_path = require('barbar.fs').is_relative_path +local jump_mode = require('barbar.jump_mode') +local layout = require('barbar.ui.layout') +local notify = require('barbar.utils').notify +local render = require('barbar.ui.render') +local state = require('barbar.state') local ESC = vim.api.nvim_replace_termcodes('', true, false, true) @@ -32,8 +34,8 @@ local ESC = vim.api.nvim_replace_termcodes('', true, false, true) --- @param fn fun() --- @return nil local function pick_buffer_wrap(fn) - if JumpMode.reinitialize then - JumpMode.initialize_indexes() + if jump_mode.reinitialize then + jump_mode.initialize_indexes() end state.is_picking_buffer = true @@ -49,7 +51,7 @@ end --- @param buffer_number integer --- @return nil local function notify_buffer_not_found(buffer_number) - utils.notify( + notify( 'Current buffer (' .. buffer_number .. ") not found in barbar.nvim's list of buffers: " .. vim.inspect(state.buffers), vim.log.levels.ERROR ) @@ -73,7 +75,7 @@ local function with_pin_order(order_func) end end ---- @class barbar.api +--- @class barbar.Api local api = {} --- Close all open buffers, except the current one. @@ -83,7 +85,7 @@ function api.close_all_but_current() for _, buffer_number in ipairs(state.buffers) do if buffer_number ~= current_bufnr then - bbye.bdelete(false, buffer_number) + bdelete(false, buffer_number) end end @@ -93,10 +95,10 @@ end --- Close all open buffers, except those in visible windows. --- @return nil function api.close_all_but_visible() - local visible = Buffer.activities.Visible + local visible = buffer.activities.Visible for _, buffer_number in ipairs(state.buffers) do - if Buffer.get_activity(buffer_number) < visible then - bbye.bdelete(false, buffer_number) + if buffer.get_activity(buffer_number) < visible then + bdelete(false, buffer_number) end end @@ -108,7 +110,7 @@ end function api.close_all_but_pinned() for _, buffer_number in ipairs(state.buffers) do if not state.is_pinned(buffer_number) then - bbye.bdelete(false, buffer_number) + bdelete(false, buffer_number) end end @@ -122,7 +124,7 @@ function api.close_all_but_current_or_pinned() for _, buffer_number in ipairs(state.buffers) do if not state.is_pinned(buffer_number) and buffer_number ~= current_bufnr then - bbye.bdelete(false, buffer_number) + bdelete(false, buffer_number) end end @@ -132,13 +134,13 @@ end --- Close all buffers which are visually left of the current buffer. --- @return nil function api.close_buffers_left() - local idx = utils.index_of(state.buffers, get_current_buf()) + local idx = index_of(state.buffers, get_current_buf()) if idx == nil or idx == 1 then return end for i = idx - 1, 1, -1 do - bbye.bdelete(false, state.buffers[i]) + bdelete(false, state.buffers[i]) end render.update() @@ -147,13 +149,13 @@ end --- Close all buffers which are visually right of the current buffer. --- @return nil function api.close_buffers_right() - local idx = utils.index_of(state.buffers, get_current_buf()) + local idx = index_of(state.buffers, get_current_buf()) if idx == nil then return end for i = #state.buffers, idx + 1, -1 do - bbye.bdelete(false, state.buffers[i]) + bdelete(false, state.buffers[i]) end render.update() @@ -180,7 +182,7 @@ function api.goto_buffer(index) if buffer_number then set_current_buf(buffer_number) else - utils.notify( + notify( 'E86: buffer at index ' .. index .. ' in list ' .. vim.inspect(state.buffers) .. ' does not exist.', vim.log.levels.ERROR ) @@ -195,15 +197,15 @@ function api.goto_buffer_relative(steps) render.get_updated_buffers() if #state.buffers < 1 then - return utils.notify('E85: There is no listed buffer', vim.log.levels.ERROR) + return notify('E85: There is no listed buffer', vim.log.levels.ERROR) end local current_bufnr = render.set_current_win_listed_buffer() - local idx = utils.index_of(state.buffers, current_bufnr) + local idx = index_of(state.buffers, current_bufnr) if not idx then -- fall back to: 1. the alternate buffer, 2. the first buffer - idx = utils.index_of(state.buffers, bufnr'#') or 1 - utils.notify( + idx = index_of(state.buffers, bufnr'#') or 1 + notify( "Couldn't find buffer #" .. current_bufnr .. ' in the list: ' .. vim.inspect(state.buffers) .. '. Falling back to buffer #' .. state.buffers[idx], vim.log.levels.INFO @@ -222,7 +224,7 @@ local move_animation_data = { --- An incremental animation for `move_buffer_animated`. --- @return nil local function move_buffer_animated_tick(ratio, current_animation) - for _, current_number in ipairs(Layout.buffers) do + for _, current_number in ipairs(layout.buffers) do local current_data = state.get_buffer_data(current_number) if current_animation.running == true then @@ -263,7 +265,7 @@ local function move_buffer(from_idx, to_idx) local previous_positions if animation == true then - previous_positions = Layout.calculate_buffers_position_by_buffer_number() + previous_positions = layout.calculate_buffers_position_by_buffer_number() end table_remove(state.buffers, from_idx) @@ -271,7 +273,7 @@ local function move_buffer(from_idx, to_idx) state.sort_pins_to_left() if animation == true then - local current_index = utils.index_of(Layout.buffers, buffer_number) + local current_index = index_of(layout.buffers, buffer_number) local start_index = min(from_idx, current_index) local end_index = max(from_idx, current_index) @@ -281,9 +283,9 @@ local function move_buffer(from_idx, to_idx) animate.stop(move_animation) end - local next_positions = Layout.calculate_buffers_position_by_buffer_number() + local next_positions = layout.calculate_buffers_position_by_buffer_number() - for _, layout_bufnr in ipairs(Layout.buffers) do + for _, layout_bufnr in ipairs(layout.buffers) do local current_data = state.get_buffer_data(layout_bufnr) local previous_position = previous_positions[layout_bufnr] @@ -319,7 +321,7 @@ function api.move_current_buffer_to(idx) end local current_bufnr = get_current_buf() - local from_idx = utils.index_of(state.buffers, current_bufnr) + local from_idx = index_of(state.buffers, current_bufnr) if from_idx == nil then return notify_buffer_not_found(current_bufnr) @@ -335,7 +337,7 @@ function api.move_current_buffer(steps) render.update() local current_bufnr = get_current_buf() - local idx = utils.index_of(state.buffers, current_bufnr) + local idx = index_of(state.buffers, current_bufnr) if idx == nil then return notify_buffer_not_found(current_bufnr) @@ -361,8 +363,8 @@ function api.order_by_directory() -- TODO: remove this block after 0.8 releases if not normalize then - local a_is_relative = utils.is_relative_path(name_of_a) - if a_is_relative and utils.is_relative_path(name_of_b) then + local a_is_relative = is_relative_path(name_of_a) + if a_is_relative and is_relative_path(name_of_b) then return a_less_than_b end @@ -408,13 +410,13 @@ function api.pick_buffer() pick_buffer_wrap(function() local ok, letter = pcall(function() return char(getchar()) end) if ok and letter ~= '' then - if JumpMode.buffer_by_letter[letter] ~= nil then - set_current_buf(JumpMode.buffer_by_letter[letter]) + if jump_mode.buffer_by_letter[letter] ~= nil then + set_current_buf(jump_mode.buffer_by_letter[letter]) else - utils.notify("Couldn't find buffer", vim.log.levels.WARN) + notify("Couldn't find buffer", vim.log.levels.WARN) end else - utils.notify('Invalid input', vim.log.levels.WARN) + notify('Invalid input', vim.log.levels.WARN) end end) end @@ -426,15 +428,15 @@ function api.pick_buffer_delete() while true do local ok, letter = pcall(function() return char(getchar()) end) if ok and letter ~= '' then - if JumpMode.buffer_by_letter[letter] ~= nil then - bbye.bdelete(false, JumpMode.buffer_by_letter[letter]) + if jump_mode.buffer_by_letter[letter] ~= nil then + bdelete(false, jump_mode.buffer_by_letter[letter]) elseif letter == ESC then break else - utils.notify("Couldn't find buffer with letter '" .. letter .. "'", vim.log.levels.WARN) + notify("Couldn't find buffer with letter '" .. letter .. "'", vim.log.levels.WARN) end else - utils.notify('Invalid input', vim.log.levels.WARN) + notify('Invalid input', vim.log.levels.WARN) end render.update() diff --git a/lua/barbar/bbye.lua b/lua/barbar/bbye.lua index 52c428f5..9f539db1 100644 --- a/lua/barbar/bbye.lua +++ b/lua/barbar/bbye.lua @@ -41,9 +41,10 @@ local set_current_win = vim.api.nvim_set_current_win --- @type function local win_get_buf = vim.api.nvim_win_get_buf --- @type function local win_is_valid = vim.api.nvim_win_is_valid --- @type function -local config = require'barbar.config' -local state = require'barbar.state' -local utils = require'barbar.utils' +local config = require('barbar.config') +local list = require('barbar.utils.list') +local markdown_inline_code = require('barbar.utils').markdown_inline_code +local state = require('barbar.state') ------------------- -- Section: helpers @@ -100,10 +101,10 @@ local function get_focus_on_close(closing_number) end if focus_on_close == 'right' then - state_bufnrs = utils.list_reverse(state.buffers) + state_bufnrs = list.reverse(state.buffers) end - local index = utils.index_of(state_bufnrs, closing_number) + local index = list.index_of(state_bufnrs, closing_number) if index then index = index - 1 @@ -158,7 +159,7 @@ end -- Section: module ------------------ ---- @class bbye +--- @class barbar.Bbye local bbye = {} --- Delete a buffer @@ -249,7 +250,7 @@ function bbye.delete(action, force, buffer, mods) return err(msg) end else - return err('Could not delete buffer ' .. buffer_number .. ' with ' .. utils.markdown_inline_code(action)) + return err('Could not delete buffer ' .. buffer_number .. ' with ' .. markdown_inline_code(action)) end end end diff --git a/lua/barbar/config.lua b/lua/barbar/config.lua index a2a68af1..901364fc 100644 --- a/lua/barbar/config.lua +++ b/lua/barbar/config.lua @@ -2,7 +2,8 @@ local deepcopy = vim.deepcopy local table_concat = table.concat local tbl_deep_extend = vim.tbl_deep_extend -local utils = require'barbar.utils' +local table_set = require('barbar.utils.table').set +local utils = require('barbar.utils') --- The prefix used for `utils.deprecate` local DEPRECATE_PREFIX = '\nThe barbar.nvim option ' @@ -189,7 +190,7 @@ local DEPRECATED_OPTIONS = { --- @field sidebar_filetypes {[string]: nil|barbar.config.options.sidebar_filetype} --- @field tabpages boolean ---- @class barbar.config +--- @class barbar.Config --- @field options barbar.config.options local config = { options = {} } @@ -221,7 +222,7 @@ function config.setup(options) for deprecated_option, new_option in pairs(DEPRECATED_OPTIONS) do local user_setting = options[deprecated_option] if user_setting then - utils.tbl_set(options, new_option, user_setting) + table_set(options, new_option, user_setting) utils.deprecate( DEPRECATE_PREFIX .. utils.markdown_inline_code(deprecated_option), utils.markdown_inline_code(table_concat(new_option, '.')) @@ -234,8 +235,8 @@ function config.setup(options) -- TODO: remove after v2 -- Edge case deprecated option if options.closable == false then - utils.tbl_set(options, { 'icons', 'button' }, false) - utils.tbl_set(options, { 'icons', 'modified', 'button' }, false) + table_set(options, { 'icons', 'button' }, false) + table_set(options, { 'icons', 'modified', 'button' }, false) utils.deprecate( DEPRECATE_PREFIX .. utils.markdown_inline_code'closable', utils.markdown_inline_code'icons.button' .. diff --git a/lua/barbar/events.lua b/lua/barbar/events.lua index 574f1ce3..2c0b6dd6 100644 --- a/lua/barbar/events.lua +++ b/lua/barbar/events.lua @@ -21,14 +21,15 @@ local tbl_isempty = vim.tbl_isempty local win_get_position = vim.api.nvim_win_get_position --- @type function local win_get_width = vim.api.nvim_win_get_width --- @type function -local api = require'barbar.api' -local bbye = require'barbar.bbye' -local config = require'barbar.config' -local highlight = require'barbar.highlight' -local JumpMode = require'barbar.jump_mode' -local render = require'barbar.ui.render' -local state = require'barbar.state' -local utils = require'barbar.utils' +local bdelete = require('barbar.bbye').bdelete +local config = require('barbar.config') +local highlight_reset_cache = require('barbar.utils.highlight').reset_cache +local highlight_setup = require('barbar.highlight').setup +local jump_mode = require('barbar.jump_mode') +local relative = require('barbar.fs').relative +local render = require('barbar.ui.render') +local set_offset = require('barbar.api').set_offset +local state = require('barbar.state') --- The `` used for the close click handler local CLOSE_CLICK_MODS = vim.api.nvim_cmd and { confirm = true } or 'confirm' @@ -36,7 +37,7 @@ local CLOSE_CLICK_MODS = vim.api.nvim_cmd and { confirm = true } or 'confirm' --- Whether barbar is currently set up to render. local enabled = false ---- @class barbar.events +--- @class barbar.Events local events = {} --- Create and reset autocommand groups associated with this plugin. @@ -60,7 +61,7 @@ function events.close_click_handler(buffer) buf_call(buffer, function() command('w') end) exec_autocmds('BufModifiedSet', {buffer = buffer}) else - bbye.bdelete(false, buffer, CLOSE_CLICK_MODS) + bdelete(false, buffer, CLOSE_CLICK_MODS) end end @@ -81,13 +82,13 @@ function events.enable() create_autocmd({'VimLeave'}, { callback = state.save_recently_closed, group = augroup_misc }) create_autocmd({'BufNewFile', 'BufReadPost'}, { - callback = function(tbl) JumpMode.assign_next_letter(tbl.buf) end, + callback = function(tbl) jump_mode.assign_next_letter(tbl.buf) end, group = augroup_misc, }) create_autocmd({'BufDelete', 'BufWipeout'}, { - callback = vim.schedule_wrap(function(tbl) - JumpMode.unassign_letter_for(tbl.buf) + callback = schedule_wrap(function(tbl) + jump_mode.unassign_letter_for(tbl.buf) state.push_recently_closed(tbl.file) render.update() end), @@ -96,8 +97,8 @@ function events.enable() create_autocmd('ColorScheme', { callback = function() - utils.hl.reset_cache() - highlight.setup() + highlight_reset_cache() + highlight_setup() end, group = augroup_misc, }) @@ -133,9 +134,9 @@ function events.enable() ) create_autocmd('User', { - pattern = 'GitSignsUpdate', callback = vim.schedule_wrap(function() render.update() end), group = augroup_render, + pattern = 'GitSignsUpdate', }) if not tbl_isempty(config.options.sidebar_filetypes) then @@ -193,7 +194,7 @@ function events.enable() if width ~= widths[ft] then widths[side][ft] = width widths[other_side][ft] = nil - api.set_offset(total_widths(side), option.text, nil, side) + set_offset(total_widths(side), option.text, nil, side) end end, group = augroup_render, @@ -203,7 +204,7 @@ function events.enable() buffer = tbl.buf, callback = function() widths[side][ft] = nil - api.set_offset(total_widths(side), nil, nil, side) + set_offset(total_widths(side), nil, nil, side) del_autocmd(autocmd) end, group = augroup_render, @@ -271,14 +272,14 @@ function events.enable() for _, bufnr in ipairs(state.buffers) do local name = buf_get_name(bufnr) if use_relative_file_paths then - name = utils.relative(name) + name = relative(name) end -- escape quotes table_insert(buffers, {name = name, pinned = state.is_pinned(bufnr)}) end - vim.g.Bufferline__session_restore = "lua require'barbar.state'.restore_buffers " .. + vim.g.Bufferline__session_restore = "lua require('barbar.state').restore_buffers " .. vim.inspect(buffers, {newline = ' ', indent = ''}) end, group = augroup_misc, @@ -324,7 +325,7 @@ function events.main_click_handler(bufnr, _, btn, _) -- NOTE: in Vimscript this was not `==`, it was a regex compare `=~` if btn == 'm' then - bbye.bdelete(false, bufnr) + bdelete(false, bufnr) else render.set_current_win_listed_buffer() set_current_buf(bufnr) @@ -337,8 +338,8 @@ end --- @return nil function events.on_option_changed(user_config) config.setup(user_config) -- NOTE: must be first `setup` called here - highlight.setup() - JumpMode.set_letters(config.options.letters) + highlight_setup() + jump_mode.set_letters(config.options.letters) -- Don't jump-start barbar if it is disabled if enabled then diff --git a/lua/barbar/fs.lua b/lua/barbar/fs.lua index 021066a6..b3b95695 100644 --- a/lua/barbar/fs.lua +++ b/lua/barbar/fs.lua @@ -2,33 +2,87 @@ -- fs.lua -- +local fnamemodify = vim.fn.fnamemodify --- @type function +--- @class barbar.Fs local fs = {} +--- Get +--- @param path string +--- @param hide_extension? boolean if `true`, exclude the extension of the file in the basename +--- @return string basename +function fs.basename(path, hide_extension) + return fnamemodify(path, hide_extension and ':t:r' or ':t') +end + +--- @param path string +--- @return boolean is_relative `true` if `path` is relative to the CWD +function fs.is_relative_path(path) + return fs.relative(path) == path +end + +--- @param filepath string +--- @param mode? openmode +--- @return string|nil error_message, any content +function fs.read(filepath, mode) + local file, open_err = io.open(filepath, mode or 'r') + + -- Ignore if the file doesn't exist or isn't readable + if open_err ~= nil then + return + elseif file == nil then + return + end + + local content, read_err = file:read('*a') + if read_err ~= nil then + return read_err + end + + do + local success, close_err = file:close() + if close_err ~= nil then + return close_err + elseif success == false then + return 'Error while closing ' .. filepath + end + end + + return nil, content +end + +--- @param path string +--- @return string relative_path +function fs.relative(path) + return fnamemodify(path, ':~:.') +end + --- @param filepath string --- @param content string ---- @param mode string ---- @return string | nil err +--- @param mode? openmode +--- @return string|nil error_message function fs.write(filepath, content, mode) - mode = mode or 'w' - local file, open_err = io.open(filepath, mode) + local file, open_err = io.open(filepath, mode or 'w') + if open_err ~= nil then return open_err elseif file == nil then - return 'Error while opening file' + return 'Error while opening ' .. filepath end - local _, write_err = file:write(content) - if write_err ~= nil then - return write_err + + do + local _, write_err = file:write(content) + if write_err ~= nil then + return write_err + end end local success, close_err = file:close() if close_err ~= nil then return close_err elseif success == false then - return 'Error while closing file' + return 'Error while closing ' .. filepath end - return nil end return fs diff --git a/lua/barbar/highlight.lua b/lua/barbar/highlight.lua index 14320566..6161c84f 100644 --- a/lua/barbar/highlight.lua +++ b/lua/barbar/highlight.lua @@ -1,8 +1,8 @@ -- !::exe [So] -local config = require 'barbar.config' -local hl = require'barbar.utils'.hl -local icons = require 'barbar.icons' +local config = require('barbar.config') +local hl = require('barbar.utils.highlight') +local icons = require('barbar.icons') -- Setup the highlight groups used by the plugin. hl.set_default_link('BufferAlternate', 'BufferDefaultAlternate') @@ -82,109 +82,109 @@ hl.set_default_link('BufferDefaultOffset', 'BufferTabpageFill') hl.set_default_link('BufferDefaultVisibleIcon', 'BufferVisible') hl.set_default_link('BufferDefaultVisibleNumber', 'BufferVisibleIndex') ---- @class barbar.highlight -local highlight = { - --- Setup the highlight groups for this plugin. - --- @return nil - setup = function() - local fg_current = hl.fg_or_default({'Normal'}, '#efefef', 255) - local fg_inactive = hl.fg_or_default({'TabLineFill'}, '#888888', 102) - local fg_target = {gui = 'red'} --- @type barbar.utils.hl.color - fg_target.cterm = fg_target.gui - - local fg_error = hl.fg_or_default({'ErrorMsg'}, '#A80000', 124) - local fg_hint = hl.fg_or_default({'HintMsg'}, '#D5508F', 168) - local fg_info = hl.fg_or_default({'InfoMsg'}, '#FFB7B7', 217) - local fg_warn = hl.fg_or_default({'WarningMsg'}, '#FF8900', 208) - - local fg_added = hl.fg_or_default({'GitSignsAdd'}, '#59ff5a', 82) - local fg_changed = hl.fg_or_default({'GitSignsChange'}, '#599eff', 75) - local fg_deleted = hl.fg_or_default({'GitSignsDelete'}, '#A80000', 124) - - local fg_modified = hl.fg_or_default({'WarningMsg'}, '#E5AB0E', 178) - local fg_special = hl.fg_or_default({'Special'}, '#599eff', 75) - local fg_subtle = hl.fg_or_default({'NonText', 'Comment'}, '#555555', 240) - - local bg_current = hl.bg_or_default({'Normal'}, 'none') - local bg_inactive = hl.bg_or_default({'TabLineFill', 'StatusLine'}, 'none') - - -- Alternate: alternate buffer - -- Current: current buffer - -- Visible: visible but not current buffer - -- Inactive: invisible but not current buffer - -- -Icon: filetype icon - -- -Index: buffer index - -- -Number: buffer number - -- -Mod: when modified - -- -Sign: the separator between buffers - -- -Target: letter in buffer-picking mode - if config.options.highlight_alternate then - local fg_alternate = hl.fg_or_default({'TabLineFill'}, '#ead0a0', 223) - local bg_alternate = hl.bg_or_default({'TabLineSel', 'Normal'}, 'none') - - hl.set('BufferDefaultAlternate', bg_alternate, fg_alternate) - hl.set('BufferDefaultAlternateADDED', bg_alternate, fg_added) - hl.set('BufferDefaultAlternateCHANGED', bg_alternate, fg_changed) - hl.set('BufferDefaultAlternateDELETED', bg_alternate, fg_deleted) - hl.set('BufferDefaultAlternateERROR', bg_alternate, fg_error) - hl.set('BufferDefaultAlternateHINT', bg_alternate, fg_hint) - hl.set('BufferDefaultAlternateIndex', bg_alternate, fg_special) - hl.set('BufferDefaultAlternateINFO', bg_alternate, fg_info) - hl.set('BufferDefaultAlternateMod', bg_alternate, fg_modified) - hl.set('BufferDefaultAlternateSign', bg_alternate, fg_special) - hl.set('BufferDefaultAlternateTarget', bg_alternate, fg_target, nil, {bold = true}) - hl.set('BufferDefaultAlternateWARN', bg_alternate, fg_warn) - end - - hl.set('BufferDefaultCurrent', bg_current, fg_current) - hl.set('BufferDefaultCurrentADDED', bg_current, fg_added) - hl.set('BufferDefaultCurrentCHANGED', bg_current, fg_changed) - hl.set('BufferDefaultCurrentDELETED', bg_current, fg_deleted) - hl.set('BufferDefaultCurrentERROR', bg_current, fg_error) - hl.set('BufferDefaultCurrentHINT', bg_current, fg_hint) - hl.set('BufferDefaultCurrentIndex', bg_current, fg_special) - hl.set('BufferDefaultCurrentINFO', bg_current, fg_info) - hl.set('BufferDefaultCurrentMod', bg_current, fg_modified) - hl.set('BufferDefaultCurrentSign', bg_current, fg_special) - hl.set('BufferDefaultCurrentTarget', bg_current, fg_target, nil, {bold = true}) - hl.set('BufferDefaultCurrentWARN', bg_current, fg_warn) - - hl.set('BufferDefaultInactive', bg_inactive, fg_inactive) - hl.set('BufferDefaultInactiveADDED', bg_inactive, fg_added) - hl.set('BufferDefaultInactiveCHANGED',bg_inactive, fg_changed) - hl.set('BufferDefaultInactiveDELETED',bg_inactive, fg_deleted) - hl.set('BufferDefaultInactiveERROR', bg_inactive, fg_error) - hl.set('BufferDefaultInactiveHINT', bg_inactive, fg_hint) - hl.set('BufferDefaultInactiveIndex', bg_inactive, fg_subtle) - hl.set('BufferDefaultInactiveINFO', bg_inactive, fg_info) - hl.set('BufferDefaultInactiveMod', bg_inactive, fg_modified) - hl.set('BufferDefaultInactiveSign', bg_inactive, fg_subtle) - hl.set('BufferDefaultInactiveTarget', bg_inactive, fg_target, nil, {bold = true}) - hl.set('BufferDefaultInactiveWARN', bg_inactive, fg_warn) - - hl.set('BufferDefaultTabpageFill', bg_inactive, fg_inactive) - hl.set('BufferDefaultTabpages', bg_inactive, fg_special, nil, {bold = true}) - - if config.options.highlight_visible then - local fg_visible = hl.fg_or_default({'TabLineSel'}, '#efefef', 255) - local bg_visible = hl.bg_or_default({'TabLineSel', 'Normal'}, 'none') - - hl.set('BufferDefaultVisible', bg_visible, fg_visible) - hl.set('BufferDefaultVisibleADDED', bg_visible, fg_warn) - hl.set('BufferDefaultVisibleCHANGED', bg_visible, fg_warn) - hl.set('BufferDefaultVisibleDELETED', bg_visible, fg_warn) - hl.set('BufferDefaultVisibleERROR', bg_visible, fg_error) - hl.set('BufferDefaultVisibleHINT', bg_visible, fg_hint) - hl.set('BufferDefaultVisibleIndex', bg_visible, fg_visible) - hl.set('BufferDefaultVisibleINFO', bg_visible, fg_info) - hl.set('BufferDefaultVisibleMod', bg_visible, fg_modified) - hl.set('BufferDefaultVisibleSign', bg_visible, fg_visible) - hl.set('BufferDefaultVisibleTarget', bg_visible, fg_target, nil, {bold = true}) - hl.set('BufferDefaultVisibleWARN', bg_visible, fg_warn) - end - - icons.set_highlights() +--- @class barbar.Highlight +local highlight = {} + +--- Setup the highlight groups for this plugin. +--- @return nil +function highlight.setup() + local fg_current = hl.fg_or_default({'Normal'}, '#efefef', 255) + local fg_inactive = hl.fg_or_default({'TabLineFill'}, '#888888', 102) + local fg_target = {gui = 'red'} --- @type barbar.utils.hl.color + fg_target.cterm = fg_target.gui + + local fg_error = hl.fg_or_default({'ErrorMsg'}, '#A80000', 124) + local fg_hint = hl.fg_or_default({'HintMsg'}, '#D5508F', 168) + local fg_info = hl.fg_or_default({'InfoMsg'}, '#FFB7B7', 217) + local fg_warn = hl.fg_or_default({'WarningMsg'}, '#FF8900', 208) + + local fg_added = hl.fg_or_default({'GitSignsAdd'}, '#59ff5a', 82) + local fg_changed = hl.fg_or_default({'GitSignsChange'}, '#599eff', 75) + local fg_deleted = hl.fg_or_default({'GitSignsDelete'}, '#A80000', 124) + + local fg_modified = hl.fg_or_default({'WarningMsg'}, '#E5AB0E', 178) + local fg_special = hl.fg_or_default({'Special'}, '#599eff', 75) + local fg_subtle = hl.fg_or_default({'NonText', 'Comment'}, '#555555', 240) + + local bg_current = hl.bg_or_default({'Normal'}, 'none') + local bg_inactive = hl.bg_or_default({'TabLineFill', 'StatusLine'}, 'none') + + -- Alternate: alternate buffer + -- Current: current buffer + -- Visible: visible but not current buffer + -- Inactive: invisible but not current buffer + -- -Icon: filetype icon + -- -Index: buffer index + -- -Number: buffer number + -- -Mod: when modified + -- -Sign: the separator between buffers + -- -Target: letter in buffer-picking mode + if config.options.highlight_alternate then + local fg_alternate = hl.fg_or_default({'TabLineFill'}, '#ead0a0', 223) + local bg_alternate = hl.bg_or_default({'TabLineSel', 'Normal'}, 'none') + + hl.set('BufferDefaultAlternate', bg_alternate, fg_alternate) + hl.set('BufferDefaultAlternateADDED', bg_alternate, fg_added) + hl.set('BufferDefaultAlternateCHANGED', bg_alternate, fg_changed) + hl.set('BufferDefaultAlternateDELETED', bg_alternate, fg_deleted) + hl.set('BufferDefaultAlternateERROR', bg_alternate, fg_error) + hl.set('BufferDefaultAlternateHINT', bg_alternate, fg_hint) + hl.set('BufferDefaultAlternateIndex', bg_alternate, fg_special) + hl.set('BufferDefaultAlternateINFO', bg_alternate, fg_info) + hl.set('BufferDefaultAlternateMod', bg_alternate, fg_modified) + hl.set('BufferDefaultAlternateSign', bg_alternate, fg_special) + hl.set('BufferDefaultAlternateTarget', bg_alternate, fg_target, nil, {bold = true}) + hl.set('BufferDefaultAlternateWARN', bg_alternate, fg_warn) end -} + + hl.set('BufferDefaultCurrent', bg_current, fg_current) + hl.set('BufferDefaultCurrentADDED', bg_current, fg_added) + hl.set('BufferDefaultCurrentCHANGED', bg_current, fg_changed) + hl.set('BufferDefaultCurrentDELETED', bg_current, fg_deleted) + hl.set('BufferDefaultCurrentERROR', bg_current, fg_error) + hl.set('BufferDefaultCurrentHINT', bg_current, fg_hint) + hl.set('BufferDefaultCurrentIndex', bg_current, fg_special) + hl.set('BufferDefaultCurrentINFO', bg_current, fg_info) + hl.set('BufferDefaultCurrentMod', bg_current, fg_modified) + hl.set('BufferDefaultCurrentSign', bg_current, fg_special) + hl.set('BufferDefaultCurrentTarget', bg_current, fg_target, nil, {bold = true}) + hl.set('BufferDefaultCurrentWARN', bg_current, fg_warn) + + hl.set('BufferDefaultInactive', bg_inactive, fg_inactive) + hl.set('BufferDefaultInactiveADDED', bg_inactive, fg_added) + hl.set('BufferDefaultInactiveCHANGED',bg_inactive, fg_changed) + hl.set('BufferDefaultInactiveDELETED',bg_inactive, fg_deleted) + hl.set('BufferDefaultInactiveERROR', bg_inactive, fg_error) + hl.set('BufferDefaultInactiveHINT', bg_inactive, fg_hint) + hl.set('BufferDefaultInactiveIndex', bg_inactive, fg_subtle) + hl.set('BufferDefaultInactiveINFO', bg_inactive, fg_info) + hl.set('BufferDefaultInactiveMod', bg_inactive, fg_modified) + hl.set('BufferDefaultInactiveSign', bg_inactive, fg_subtle) + hl.set('BufferDefaultInactiveTarget', bg_inactive, fg_target, nil, {bold = true}) + hl.set('BufferDefaultInactiveWARN', bg_inactive, fg_warn) + + hl.set('BufferDefaultTabpageFill', bg_inactive, fg_inactive) + hl.set('BufferDefaultTabpages', bg_inactive, fg_special, nil, {bold = true}) + + if config.options.highlight_visible then + local fg_visible = hl.fg_or_default({'TabLineSel'}, '#efefef', 255) + local bg_visible = hl.bg_or_default({'TabLineSel', 'Normal'}, 'none') + + hl.set('BufferDefaultVisible', bg_visible, fg_visible) + hl.set('BufferDefaultVisibleADDED', bg_visible, fg_warn) + hl.set('BufferDefaultVisibleCHANGED', bg_visible, fg_warn) + hl.set('BufferDefaultVisibleDELETED', bg_visible, fg_warn) + hl.set('BufferDefaultVisibleERROR', bg_visible, fg_error) + hl.set('BufferDefaultVisibleHINT', bg_visible, fg_hint) + hl.set('BufferDefaultVisibleIndex', bg_visible, fg_visible) + hl.set('BufferDefaultVisibleINFO', bg_visible, fg_info) + hl.set('BufferDefaultVisibleMod', bg_visible, fg_modified) + hl.set('BufferDefaultVisibleSign', bg_visible, fg_visible) + hl.set('BufferDefaultVisibleTarget', bg_visible, fg_target, nil, {bold = true}) + hl.set('BufferDefaultVisibleWARN', bg_visible, fg_warn) + end + + icons.set_highlights() +end return highlight diff --git a/lua/barbar/icons.lua b/lua/barbar/icons.lua index 2eaece27..95cfa7c8 100644 --- a/lua/barbar/icons.lua +++ b/lua/barbar/icons.lua @@ -9,8 +9,8 @@ local buf_get_option = vim.api.nvim_buf_get_option --- @type function local fnamemodify = vim.fn.fnamemodify --- @type function local hlexists = vim.fn.hlexists --- @type function -local utils = require'barbar.utils' -local hl = utils.hl +local hl = require('barbar.utils.highlight') +local utils = require('barbar.utils') --- @type boolean, {get_icon: fun(name: string, ext?: string, opts?: {default: nil|boolean}): string, string} local ok, web = pcall(require, 'nvim-web-devicons') @@ -37,62 +37,60 @@ end --- @type barbar.icons.group[] local hl_groups = {} ---- @class barbar.icons -local icons = { - --- Re-highlight all of the groups which have been set before. Checks for updated highlight groups. - --- @return nil - set_highlights = vim.schedule_wrap(function() - for _, group in ipairs(hl_groups) do - hl_buffer_icon(group.buffer_status, group.icon_hl) - end - end), - - get_icon = ok and - --- @param bufnr integer - --- @param buffer_activity barbar.buffer.activity.name - --- @return string icon, string highlight_group - function(bufnr, buffer_activity) - local basename, extension = '', '' - local filetype = buf_get_option(bufnr, 'filetype') - local icon_char, icon_hl = '', '' - - -- nvim-web-devicon only handles filetype icons, not other types (eg directory) - -- thus we need to do some work here - if filetype == 'netrw' or filetype == 'LuaTree' then - icon_char, icon_hl = '', 'Directory' +--- @class barbar.Icons +local icons = {} + +icons.get_icon = ok and + --- @param bufnr integer + --- @param buffer_activity barbar.buffer.activity.name + --- @return string icon, string highlight_group + function(bufnr, buffer_activity) + local basename, extension = '', '' + local filetype = buf_get_option(bufnr, 'filetype') + local icon_char, icon_hl = '', '' + + -- nvim-web-devicon only handles filetype icons, not other types (eg directory) + -- thus we need to do some work here + if filetype == 'netrw' or filetype == 'LuaTree' then + icon_char, icon_hl = '', 'Directory' + else + if filetype == 'fugitive' or filetype == 'gitcommit' then + basename, extension = 'git', 'git' else - if filetype == 'fugitive' or filetype == 'gitcommit' then - basename, extension = 'git', 'git' - else - basename = fnamemodify(buf_get_name(bufnr), ':t') - extension = fnamemodify(basename, ':e') - end - - icon_char, icon_hl = web.get_icon(basename, extension, { default = true }) + basename = fnamemodify(buf_get_name(bufnr), ':t') + extension = fnamemodify(basename, ':e') end - if icon_hl and hlexists(icon_hl .. buffer_activity) < 1 then - hl_buffer_icon(buffer_activity, icon_hl) - table_insert(hl_groups, {buffer_status = buffer_activity, icon_hl = icon_hl}) - end + icon_char, icon_hl = web.get_icon(basename, extension, { default = true }) + end - return icon_char, icon_hl .. buffer_activity - end or - --- @param buffer_activity barbar.buffer.activity.name - --- @return string icon, string highlight_group - function(_, buffer_activity) - local invalid_option = utils.markdown_inline_code'icons.filetype.enabled' - utils.notify_once( - 'barbar.nvim: ' .. invalid_option .. ' is set to ' .. utils.markdown_inline_code'true' .. - ' but ' .. utils.markdown_inline_code'nvim-dev-icons' .. ' was not found.' .. - '\nbarbar.nvim: icons have been disabled. Set ' .. invalid_option .. ' to ' .. - utils.markdown_inline_code'false' .. ' or ' .. 'install ' .. - utils.markdown_inline_code'nvim-dev-icons' .. 'to disable this message.', - vim.log.levels.WARN - ) - - return '', 'Buffer' .. buffer_activity .. 'Icon' - end, -} + if icon_hl and hlexists(icon_hl .. buffer_activity) < 1 then + hl_buffer_icon(buffer_activity, icon_hl) + table_insert(hl_groups, {buffer_status = buffer_activity, icon_hl = icon_hl}) + end + + return icon_char, icon_hl .. buffer_activity + end or + function(_, buffer_activity) + local invalid_option = utils.markdown_inline_code'icons.filetype.enabled' + utils.notify_once( + 'barbar.nvim: ' .. invalid_option .. ' is set to ' .. utils.markdown_inline_code'true' .. + ' but ' .. utils.markdown_inline_code'nvim-dev-icons' .. ' was not found.' .. + '\nbarbar.nvim: icons have been disabled. Set ' .. invalid_option .. ' to ' .. + utils.markdown_inline_code'false' .. ' or ' .. 'install ' .. + utils.markdown_inline_code'nvim-dev-icons' .. 'to disable this message.', + vim.log.levels.WARN + ) + + return '', 'Buffer' .. buffer_activity .. 'Icon' + end + +--- Re-highlight all of the groups which have been set before. Checks for updated highlight groups. +--- @return nil +icons.set_highlights = vim.schedule_wrap(function() + for _, group in ipairs(hl_groups) do + hl_buffer_icon(group.buffer_status, group.icon_hl) + end +end) return icons diff --git a/lua/barbar/jump_mode.lua b/lua/barbar/jump_mode.lua index 34c4987f..df6e090d 100644 --- a/lua/barbar/jump_mode.lua +++ b/lua/barbar/jump_mode.lua @@ -8,7 +8,7 @@ local split = vim.fn.split --- @type function local strcharpart = vim.fn.strcharpart --- @type function local strwidth = vim.api.nvim_strwidth --- @type function -local config = require'barbar.config' +local config = require('barbar.config') ---------------------------------------- -- Section: Buffer-picking mode state -- @@ -24,36 +24,36 @@ local letters = {} --- @field private letter_by_buffer {[integer]: string} a bi-directional map of buffer integers and their letters. --- @field private letter_status {[integer]: boolean} --- @field reinitialize boolean whether an `initialize_indexes` operation has been queued. -local JumpMode = {} +local jump_mode = {} --- Reset the module to a valid default state --- @return nil -function JumpMode.initialize_indexes() - JumpMode.buffer_by_letter = {} - JumpMode.index_by_letter = {} - JumpMode.letter_by_buffer = {} - JumpMode.letter_status = {} +function jump_mode.initialize_indexes() + jump_mode.buffer_by_letter = {} + jump_mode.index_by_letter = {} + jump_mode.letter_by_buffer = {} + jump_mode.letter_status = {} for index, letter in ipairs(letters) do - JumpMode.index_by_letter[letter] = index - JumpMode.letter_status[index] = false + jump_mode.index_by_letter[letter] = index + jump_mode.letter_status[index] = false end - JumpMode.reinitialize = false + jump_mode.reinitialize = false end --- Set the letters which can be used by jump mode. --- @param chars string --- @return nil -function JumpMode.set_letters(chars) +function jump_mode.set_letters(chars) letters = split(chars, [[\zs]]) - JumpMode.initialize_indexes() + jump_mode.initialize_indexes() end --- @param bufnr integer --- @return nil|string assigned -function JumpMode.assign_next_letter(bufnr) - if JumpMode.letter_by_buffer[bufnr] ~= nil then +function jump_mode.assign_next_letter(bufnr) + if jump_mode.letter_by_buffer[bufnr] ~= nil then return end @@ -64,14 +64,14 @@ function JumpMode.assign_next_letter(bufnr) for i = 1, strwidth(name) do local letter = strcharpart(name, i - 1, 1):lower() - if JumpMode.index_by_letter[letter] ~= nil then - local index = JumpMode.index_by_letter[letter] - local status = JumpMode.letter_status[index] + if jump_mode.index_by_letter[letter] ~= nil then + local index = jump_mode.index_by_letter[letter] + local status = jump_mode.letter_status[index] if status == false then - JumpMode.letter_status[index] = true + jump_mode.letter_status[index] = true -- letter = m.letters[index] - JumpMode.buffer_by_letter[letter] = bufnr - JumpMode.letter_by_buffer[bufnr] = letter + jump_mode.buffer_by_letter[letter] = bufnr + jump_mode.letter_by_buffer[bufnr] = letter return letter end end @@ -79,12 +79,12 @@ function JumpMode.assign_next_letter(bufnr) end -- Otherwise, assign a letter by usable order - for i, status in ipairs(JumpMode.letter_status) do + for i, status in ipairs(jump_mode.letter_status) do if status == false then local letter = letters[i] - JumpMode.letter_status[i] = true - JumpMode.buffer_by_letter[letter] = bufnr - JumpMode.letter_by_buffer[bufnr] = letter + jump_mode.letter_status[i] = true + jump_mode.buffer_by_letter[letter] = bufnr + jump_mode.letter_by_buffer[bufnr] = letter return letter end end @@ -92,35 +92,35 @@ end --- @param bufnr integer --- @return string letter assiegned to `bufnr` -function JumpMode.get_letter(bufnr) - return JumpMode.letter_by_buffer[bufnr] or JumpMode.assign_next_letter(bufnr) +function jump_mode.get_letter(bufnr) + return jump_mode.letter_by_buffer[bufnr] or jump_mode.assign_next_letter(bufnr) end --- @param letter string --- @return nil -function JumpMode.unassign_letter(letter) +function jump_mode.unassign_letter(letter) if letter == '' or letter == nil then return end - local index = JumpMode.index_by_letter[letter] + local index = jump_mode.index_by_letter[letter] - JumpMode.letter_status[index] = false + jump_mode.letter_status[index] = false - if JumpMode.buffer_by_letter[letter] ~= nil then - local bufnr = JumpMode.buffer_by_letter[letter] - JumpMode.buffer_by_letter[letter] = nil - JumpMode.letter_by_buffer[bufnr] = nil + if jump_mode.buffer_by_letter[letter] ~= nil then + local bufnr = jump_mode.buffer_by_letter[letter] + jump_mode.buffer_by_letter[letter] = nil + jump_mode.letter_by_buffer[bufnr] = nil end - JumpMode.reinitialize = true + jump_mode.reinitialize = true end --- Unassign the letter which is assigned to `bufnr.` --- @param bufnr integer --- @return nil -function JumpMode.unassign_letter_for(bufnr) - JumpMode.unassign_letter(JumpMode.letter_by_buffer[bufnr]) +function jump_mode.unassign_letter_for(bufnr) + jump_mode.unassign_letter(jump_mode.letter_by_buffer[bufnr]) end -return JumpMode +return jump_mode diff --git a/lua/barbar/state.lua b/lua/barbar/state.lua index 404e21d2..bc4ae752 100644 --- a/lua/barbar/state.lua +++ b/lua/barbar/state.lua @@ -20,9 +20,10 @@ local tbl_contains = vim.tbl_contains local tbl_filter = vim.tbl_filter local tbl_map = vim.tbl_map -local Buffer = require'barbar.buffer' -local config = require'barbar.config' -local utils = require'barbar.utils' +local buffer = require('barbar.buffer') +local config = require('barbar.config') +local fs = require('barbar.fs') +local utils = require('barbar.utils') local CACHE_PATH = vim.fn.stdpath('cache') .. '/barbar.json' @@ -48,7 +49,7 @@ local CACHE_PATH = vim.fn.stdpath('cache') .. '/barbar.json' --- @field left barbar.state.offset.side --- @field right barbar.state.offset.side ---- @class barbar.state +--- @class barbar.State --- @field buffers integer[] the open buffers, in visual order. --- @field data_by_bufnr {[integer]: barbar.state.data} the buffer data indexed on buffer number --- @field is_picking_buffer boolean whether the user is currently in jump-mode @@ -98,7 +99,7 @@ function state.get_buffer_list() not tbl_contains(exclude_ft, buf_get_option(bufnr, 'filetype')) then local name = buf_get_name(bufnr) - if not tbl_contains(exclude_name, utils.basename(name, hide_extensions)) then + if not tbl_contains(exclude_name, fs.basename(name, hide_extensions)) then table_insert(result, bufnr) end end @@ -196,7 +197,7 @@ function state.update_names() -- Compute names for i, buffer_n in ipairs(state.buffers) do - local name = Buffer.get_name(buffer_n, hide_extensions) + local name = buffer.get_name(buffer_n, hide_extensions) if buffer_index_by_name[name] == nil then buffer_index_by_name[name] = i @@ -205,7 +206,7 @@ function state.update_names() local other_i = buffer_index_by_name[name] local other_n = state.buffers[other_i] local new_name, new_other_name = - Buffer.get_unique_name( + buffer.get_unique_name( buf_get_name(buffer_n), buf_get_name(state.buffers[other_i])) @@ -230,7 +231,7 @@ function state.set_offset(width, text, hl) utils.markdown_inline_code'barbar.api.set_offset' ) - require'barbar.api'.set_offset(width, text, hl) + require('barbar.api').set_offset(width, text, hl) end --- Restore the buffers @@ -269,50 +270,18 @@ end --- Save recently_closed list --- @return nil function state.save_recently_closed() - local file, open_err = io.open(CACHE_PATH, 'w') - if open_err ~= nil then - return utils.notify(open_err, vim.log.levels.ERROR) - elseif file == nil then - return utils.notify('Could not open ' .. CACHE_PATH, vim.log.levels.ERROR) - end - do - local _, write_err = file:write(json_encode({ - recently_closed = state.recently_closed, - })) - if write_err ~= nil then - return utils.notify(write_err, vim.log.levels.ERROR) - end - end - local success, close_err = file:close() - if close_err ~= nil then - return utils.notify(close_err, vim.log.levels.ERROR) - elseif success == false then - return utils.notify('Could not close ' .. CACHE_PATH, vim.log.levels.ERROR) + local err_msg = fs.write(CACHE_PATH, json_encode({ recently_closed = state.recently_closed })) + if err_msg then + utils.notify(err_msg, vim.log.levels.WARN) end end --- Save recently_closed list --- @return nil function state.load_recently_closed() - local file, open_err = io.open(CACHE_PATH, 'r') - - -- Ignore if the file doesn't exist or isn't readable - if open_err ~= nil then - return - elseif file == nil then - return - end - - local content, read_err = file:read('*a') - if read_err ~= nil then - return utils.notify(read_err, vim.log.levels.ERROR) - end - - local success, close_err = file:close() - if close_err ~= nil then - return - elseif success == false then - return + local err_msg, content = fs.read(CACHE_PATH) + if err_msg then + utils.notify(err_msg, vim.log.levels.WARN) end local ok, result = pcall(json_decode, content, {luanil = {array = true, object = true}}) diff --git a/lua/barbar/ui/layout.lua b/lua/barbar/ui/layout.lua index e60ccb98..47ffa664 100644 --- a/lua/barbar/ui/layout.lua +++ b/lua/barbar/ui/layout.lua @@ -12,10 +12,10 @@ local get_option = vim.api.nvim_get_option --- @type function local strwidth = vim.api.nvim_strwidth --- @type function local tabpagenr = vim.fn.tabpagenr --- @type function -local Buffer = require'barbar.buffer' -local config = require'barbar.config' -local icons = require'barbar.icons' -local state = require'barbar.state' +local buffer = require('barbar.buffer') +local config = require('barbar.config') +local get_icon = require('barbar.icons').get_icon +local state = require('barbar.state') --- The number of sides of each buffer in the tabline. local SIDES_OF_BUFFER = 2 @@ -49,23 +49,21 @@ local SPACE_LEN = #' ' --- @class barbar.ui.layout.data.tabpages --- @field width integer the amount of space allocated to the tabpage indicator - ---- @class barbar.ui.layout +--- @class barbar.ui.Layout --- @field buffers integer[] different from `state.buffers` in that the `hide` option is respected. Only updated when calling `calculate_buffers_width`. -local Layout = { buffers = {} } +local layout = { buffers = {} } --- Calculate the current layout of the bufferline. --- @return barbar.ui.layout.data -function Layout.calculate() - +function layout.calculate() local total_width = get_option('columns') local left_width = state.offset.left.width local right_width = state.offset.right.width - local tabpages_width = Layout.calculate_tabpages_width() + local tabpages_width = layout.calculate_tabpages_width() local buffers_width = total_width - state.offset.left.width - state.offset.right.width - tabpages_width - local pinned_count, pinned_sum, unpinned_sum, widths = Layout.calculate_buffers_width() + local pinned_count, pinned_sum, unpinned_sum, widths = layout.calculate_buffers_width() local pinned_width = pinned_sum + (pinned_count * config.options.minimum_padding * SIDES_OF_BUFFER) local unpinned_allocated_width = buffers_width - pinned_width @@ -113,22 +111,20 @@ end --- @param bufnr integer the buffer to calculate the width of --- @param index integer the buffer's numerical index --- @return integer width -function Layout.calculate_buffer_width(bufnr, index) +function layout.calculate_buffer_width(bufnr, index) local buffer_data = state.get_buffer_data(bufnr) if buffer_data.closing then return buffer_data.width or buffer_data.computed_width or 0 end - local buffer_activity = Buffer.activities[Buffer.get_activity(bufnr)] - local buffer_name = buffer_data.name or '[no name]' - - local icons_option = Buffer.get_icons(buffer_activity, buf_get_option(bufnr, 'modified'), buffer_data.pinned) + local buffer_activity = buffer.activities[buffer.get_activity(bufnr)] + local icons_option = buffer.get_icons(buffer_activity, buf_get_option(bufnr, 'modified'), buffer_data.pinned) local width = strwidth(icons_option.separator.left) local filename_enabled = icons_option.filename if filename_enabled then - width = width + strwidth(buffer_name) + width = width + strwidth(buffer_data.name or '[no name]') end if icons_option.buffer_index then @@ -140,7 +136,7 @@ function Layout.calculate_buffer_width(bufnr, index) end if icons_option.filetype.enabled then - local file_icon = icons.get_icon(bufnr, buffer_activity) + local file_icon = get_icon(bufnr, buffer_activity) width = width + strwidth(file_icon) if filename_enabled then @@ -148,11 +144,11 @@ function Layout.calculate_buffer_width(bufnr, index) end end - Buffer.for_each_counted_enabled_diagnostic(bufnr, icons_option.diagnostics, function(count, _, option) + buffer.for_each_counted_enabled_diagnostic(bufnr, icons_option.diagnostics, function(count, _, option) width = width + SPACE_LEN + strwidth(option.icon) + #tostring(count) end) - Buffer.for_each_enabled_git_status(bufnr, icons_option.gitsigns, function(count, _, option) + buffer.for_each_enabled_git_status(bufnr, icons_option.gitsigns, function(count, _, option) width = width + SPACE_LEN + strwidth(option.icon) + #tostring(count) end) @@ -165,25 +161,25 @@ function Layout.calculate_buffer_width(bufnr, index) end --- @return {[integer]: integer} position_by_bufnr -function Layout.calculate_buffers_position_by_buffer_number() - local layout = Layout.calculate() +function layout.calculate_buffers_position_by_buffer_number() + local data = layout.calculate() local positions = {} local pinned_position = 0 - local unpinned_position = layout.buffers.pinned_width + local unpinned_position = data.buffers.pinned_width - for i, buffer_number in ipairs(Layout.buffers) do + for i, buffer_number in ipairs(layout.buffers) do if state.is_pinned(buffer_number) then positions[buffer_number] = pinned_position - pinned_position = pinned_position + Layout.calculate_width( - layout.buffers.base_widths[i], + pinned_position = pinned_position + layout.calculate_width( + data.buffers.base_widths[i], config.options.minimum_padding ) else positions[buffer_number] = unpinned_position - unpinned_position = unpinned_position + Layout.calculate_width( - layout.buffers.base_widths[i], - layout.buffers.padding + unpinned_position = unpinned_position + layout.calculate_width( + data.buffers.base_widths[i], + data.buffers.padding ) end end @@ -193,16 +189,16 @@ end --- Calculate the width of the buffers --- @return integer pinned_count, integer pinned_sum, integer unpinned_sum, integer[] widths -function Layout.calculate_buffers_width() - Layout.buffers = Buffer.hide(state.buffers) +function layout.calculate_buffers_width() + layout.buffers = buffer.hide(state.buffers) local pinned_count = 0 local pinned_sum = 0 local unpinned_sum = 0 local widths = {} - for i, bufnr in ipairs(Layout.buffers) do - local width = Layout.calculate_buffer_width(bufnr, i) + for i, bufnr in ipairs(layout.buffers) do + local width = layout.calculate_buffer_width(bufnr, i) if state.is_pinned(bufnr) then pinned_count = pinned_count + 1 pinned_sum = pinned_sum + width @@ -218,7 +214,7 @@ end --- The number of characters needed to represent the tabpages. --- @return integer width -function Layout.calculate_tabpages_width() +function layout.calculate_tabpages_width() if not config.options.tabpages then return 0 end @@ -235,8 +231,8 @@ end --- @param base_width integer --- @param padding_width integer --- @return integer width -function Layout.calculate_width(base_width, padding_width) +function layout.calculate_width(base_width, padding_width) return base_width + (padding_width * SIDES_OF_BUFFER) end -return Layout +return layout diff --git a/lua/barbar/ui/nodes.lua b/lua/barbar/ui/nodes.lua index ca1ab3af..fb2f0ad5 100644 --- a/lua/barbar/ui/nodes.lua +++ b/lua/barbar/ui/nodes.lua @@ -1,34 +1,34 @@ -- --- nodes.lua +-- node_list.lua -- local table_insert = table.insert local strcharpart = vim.fn.strcharpart --- @type function local strwidth = vim.api.nvim_strwidth --- @type function ---- @class barbar.ui.Nodes ---- @see barbar.ui.node --- Operations on `node`s. -local Nodes = {} +--- @see barbar.ui.node +--- @class barbar.ui.Nodes +local nodes = {} ---- Sums the width of the nodes ---- @param nodes barbar.ui.node[] +--- Sums the width of the node_list +--- @param node_list barbar.ui.node[] --- @return integer -function Nodes.width(nodes) +function nodes.width(node_list) local result = 0 - for _, node in ipairs(nodes) do + for _, node in ipairs(node_list) do result = result + strwidth(node.text) end return result end ---- Concatenates some `nodes` into a valid tabline string. ---- @param nodes barbar.ui.node[] +--- Concatenates some `node_list` into a valid tabline string. +--- @param node_list barbar.ui.node[] --- @return string -function Nodes.to_string(nodes) +function nodes.to_string(node_list) local result = '' - for _, node in ipairs(nodes) do + for _, node in ipairs(node_list) do -- NOTE: We have to escape the text in case it contains '%', which is a special character to the -- tabline. -- To escape '%', we make it '%%'. It just so happens that '%' is also a special character @@ -39,46 +39,46 @@ function Nodes.to_string(nodes) return result end ---- Concatenates some `nodes` into a raw string. ---- @param nodes barbar.ui.node[] +--- Concatenates some `node_list` into a raw string. +--- @param node_list barbar.ui.node[] --- For debugging purposes. --- @return string -function Nodes.to_raw_string(nodes) +function nodes.to_raw_string(node_list) local result = '' - for _, node in ipairs(nodes) do + for _, node in ipairs(node_list) do result = result .. node.text end return result end ---- Insert `other` into `nodes` at the `position`. ---- @param nodes barbar.ui.node[] +--- Insert `other` into `node_list` at the `position`. +--- @param node_list barbar.ui.node[] --- @param position integer --- @return barbar.ui.node[] with_insertions -function Nodes.insert(nodes, position, node) - return Nodes.insert_many(nodes, position, { node }) +function nodes.insert(node_list, position, node) + return nodes.insert_many(node_list, position, { node }) end ---- Insert `others` into `nodes` at the `position`. ---- @param nodes barbar.ui.node[] +--- Insert `others` into `node_list` at the `position`. +--- @param node_list barbar.ui.node[] --- @param position integer --- @param others barbar.ui.node[] --- @return barbar.ui.node[] with_insertions -function Nodes.insert_many(nodes, position, others) +function nodes.insert_many(node_list, position, others) if position < 0 then - local others_width = Nodes.width(others) + local others_width = nodes.width(others) local others_end = position + others_width if others_end < 0 then - return nodes + return node_list end local available_width = others_end position = 0 - others = Nodes.slice_left(others, available_width) + others = nodes.slice_left(others, available_width) end @@ -87,8 +87,8 @@ function Nodes.insert_many(nodes, position, others) local new_nodes = {} local i = 1 - while i <= #nodes do - local node = nodes[i] + while i <= #node_list do + local node = node_list[i] local node_width = strwidth(node.text) -- While we haven't found the position... @@ -109,7 +109,7 @@ function Nodes.insert_many(nodes, position, others) }) end - -- Add new other nodes + -- Add new other node_list local others_width = 0 for _, other in ipairs(others) do local other_width = strwidth(other.text) @@ -119,10 +119,10 @@ function Nodes.insert_many(nodes, position, others) local end_position = position + others_width - -- Then, resume adding previous nodes + -- Then, resume adding previous node_list -- table.insert(new_nodes, 'then') - while i <= #nodes do - local previous_node = nodes[i] + while i <= #node_list do + local previous_node = node_list[i] local previous_node_width = strwidth(previous_node.text) local previous_node_start_position = current_position local previous_node_end_position = current_position + previous_node_width @@ -150,15 +150,15 @@ function Nodes.insert_many(nodes, position, others) return new_nodes end ---- Select from `nodes` while fitting within the provided `width`, discarding all indices larger than the last index that fits. +--- Select from `node_list` while fitting within the provided `width`, discarding all indices larger than the last index that fits. --- @param width integer --- @return barbar.ui.node[] sliced -function Nodes.slice_right(nodes, width) +function nodes.slice_right(node_list, width) local accumulated_width = 0 local new_nodes = {} - for _, node in ipairs(nodes) do + for _, node in ipairs(node_list) do local text_width = strwidth(node.text) accumulated_width = accumulated_width + text_width @@ -174,17 +174,17 @@ function Nodes.slice_right(nodes, width) return new_nodes end ---- Select from `nodes` in reverse while fitting within the provided `width`, discarding all indices less than the last index that fits. ---- @param nodes barbar.ui.node[] +--- Select from `node_list` in reverse while fitting within the provided `width`, discarding all indices less than the last index that fits. +--- @param node_list barbar.ui.node[] --- @param width integer --- @return barbar.ui.node[] sliced -function Nodes.slice_left(nodes, width) +function nodes.slice_left(node_list, width) local accumulated_width = 0 local new_nodes = {} - for i = #nodes, 1, -1 do - local node = nodes[i] --- @type barbar.ui.node (it cannot be `nil`) + for i = #node_list, 1, -1 do + local node = node_list[i] --- @type barbar.ui.node (it cannot be `nil`) local text_width = strwidth(node.text) accumulated_width = accumulated_width + text_width @@ -201,4 +201,4 @@ function Nodes.slice_left(nodes, width) return new_nodes end -return Nodes +return nodes diff --git a/lua/barbar/ui/render.lua b/lua/barbar/ui/render.lua index 8bf58690..536b5e15 100644 --- a/lua/barbar/ui/render.lua +++ b/lua/barbar/ui/render.lua @@ -26,16 +26,17 @@ local tbl_contains = vim.tbl_contains local tbl_filter = vim.tbl_filter local win_get_buf = vim.api.nvim_win_get_buf --- @type function -local animate = require'barbar.animate' -local Buffer = require'barbar.buffer' -local config = require'barbar.config' --- local fs = require'barbar.fs' -- For debugging purposes -local Nodes = require'barbar.ui.nodes' -local icons = require'barbar.icons' -local JumpMode = require'barbar.jump_mode' -local Layout = require'barbar.ui.layout' -local state = require'barbar.state' -local utils = require'barbar.utils' +local animate = require('barbar.animate') +local buffer = require('barbar.buffer') +local config = require('barbar.config') +-- local fs = require('barbar.fs') -- For debugging purposes +local get_icon = require('barbar.icons').get_icon +local get_letter = require('barbar.jump_mode').get_letter +local index_of = require('barbar.utils.list').index_of +local layout = require('barbar.ui.layout') +local nodes = require('barbar.ui.nodes') +local notify = require('barbar.utils').notify +local state = require('barbar.state') --- Last value for tabline --- @type string @@ -65,7 +66,7 @@ local ANIMATION = { --- @field target integer the place where the bufferline is scrolled/wants to scroll to. local scroll = { current = 0, target = 0 } ---- @class barbar.ui.render +--- @class barbar.ui.Render local render = {} --- An incremental animation for `close_buffer_animated`. @@ -127,15 +128,15 @@ end --- Opens a buffer with animation. --- @param bufnr integer ---- @param layout barbar.ui.layout.data +--- @param data barbar.ui.layout.data --- @return nil -local function open_buffer_start_animation(layout, bufnr) +local function open_buffer_start_animation(data, bufnr) local buffer_data = state.get_buffer_data(bufnr) - local index = utils.index_of(Layout.buffers, bufnr) + local index = index_of(layout.buffers, bufnr) - buffer_data.computed_width = Layout.calculate_width( - layout.buffers.base_widths[index] or Layout.calculate_buffer_width(bufnr, #Layout.buffers + 1), - layout.buffers.padding + buffer_data.computed_width = layout.calculate_width( + data.buffers.base_widths[index] or layout.calculate_buffer_width(bufnr, #layout.buffers + 1), + data.buffers.padding ) local target_width = buffer_data.computed_width or 0 @@ -158,7 +159,7 @@ local function open_buffers(new_buffers) -- Open next to the currently opened tab -- Find the new index where the tab will be inserted - local new_index = utils.index_of(state.buffers, state.last_current_buffer) + local new_index = index_of(state.buffers, state.last_current_buffer) if new_index ~= nil then new_index = new_index + 1 else @@ -169,7 +170,7 @@ local function open_buffers(new_buffers) -- Insert the buffers where they go for _, new_buffer in ipairs(new_buffers) do - if utils.index_of(state.buffers, new_buffer) == nil then + if index_of(state.buffers, new_buffer) == nil then local actual_index = new_index local should_insert_at_end = config.options.insert_at_end or @@ -208,10 +209,10 @@ local function open_buffers(new_buffers) -- Update names because they affect the layout state.update_names() - local layout = Layout.calculate() + local data = layout.calculate() for _, buffer_number in ipairs(new_buffers) do - open_buffer_start_animation(layout, buffer_number) + open_buffer_start_animation(data, buffer_number) end end @@ -303,6 +304,7 @@ local function set_scroll_tick(new_scroll, animation) if animation.running == false then scroll_animation = nil end + render.update(nil, false) end @@ -330,7 +332,10 @@ end --- @param s? string --- @return nil function render.set_tabline(s) - s = s or '' + if s == nil then + s = '' + end + if last_tabline ~= s then last_tabline = s set_option('tabline', s) @@ -339,11 +344,11 @@ function render.set_tabline(s) end --- Compute the buffer hl-groups ---- @param layout barbar.ui.layout.data +--- @param data barbar.ui.layout.data --- @param bufnrs integer[] --- @param refocus? boolean --- @return barbar.ui.container[] pinned_groups, barbar.ui.container[] clumps -local function get_bufferline_containers(layout, bufnrs, refocus) +local function get_bufferline_containers(data, bufnrs, refocus) local click_enabled = has('tablineat') and config.options.clickable local accumulated_pinned_width = 0 --- the width of pinned buffers accumulated while iterating @@ -354,26 +359,26 @@ local function get_bufferline_containers(layout, bufnrs, refocus) local pinned_containers = {} --- @type barbar.ui.container[] local pinned_pad_text = (' '):rep(config.options.minimum_padding) - local unpinned_pad_text = (' '):rep(layout.buffers.padding) + local unpinned_pad_text = (' '):rep(data.buffers.padding) for i, bufnr in ipairs(bufnrs) do - local activity = Buffer.get_activity(bufnr) - local activity_name = Buffer.activities[activity] + local activity = buffer.get_activity(bufnr) + local activity_name = buffer.activities[activity] local buffer_data = state.get_buffer_data(bufnr) local modified = buf_get_option(bufnr, 'modified') local pinned = buffer_data.pinned if pinned then buffer_data.computed_position = accumulated_pinned_width - buffer_data.computed_width = Layout.calculate_width(layout.buffers.base_widths[i], config.options.minimum_padding) + buffer_data.computed_width = layout.calculate_width(data.buffers.base_widths[i], config.options.minimum_padding) else - buffer_data.computed_position = accumulated_unpinned_width + layout.buffers.pinned_width - buffer_data.computed_width = Layout.calculate_width(layout.buffers.base_widths[i], layout.buffers.padding) + buffer_data.computed_position = accumulated_unpinned_width + data.buffers.pinned_width + buffer_data.computed_width = layout.calculate_width(data.buffers.base_widths[i], data.buffers.padding) end local container_width = buffer_data.width or buffer_data.computed_width - if activity == Buffer.activities.Current and refocus ~= false then + if activity == buffer.activities.Current and refocus ~= false then current_buffer_index = i local start = accumulated_unpinned_width @@ -381,12 +386,12 @@ local function get_bufferline_containers(layout, bufnrs, refocus) if scroll.target > start then render.set_scroll(start) - elseif scroll.target + layout.buffers.unpinned_allocated_width < end_ then - render.set_scroll(scroll.target + (end_ - (scroll.target + layout.buffers.unpinned_allocated_width))) + elseif scroll.target + data.buffers.unpinned_allocated_width < end_ then + render.set_scroll(scroll.target + (end_ - (scroll.target + data.buffers.unpinned_allocated_width))) end end - local scroll_current = min(scroll.current, layout.buffers.scroll_max) + local scroll_current = min(scroll.current, data.buffers.scroll_max) if pinned then accumulated_pinned_width = accumulated_pinned_width + container_width @@ -396,7 +401,7 @@ local function get_bufferline_containers(layout, bufnrs, refocus) if accumulated_unpinned_width < scroll_current then goto continue -- HACK: there is no `continue` keyword elseif (refocus == false or (refocus ~= false and current_buffer_index ~= nil)) and - accumulated_unpinned_width - scroll_current > layout.buffers.unpinned_allocated_width + accumulated_unpinned_width - scroll_current > data.buffers.unpinned_allocated_width then done = true end @@ -405,7 +410,7 @@ local function get_bufferline_containers(layout, bufnrs, refocus) local buffer_name = buffer_data.name or '[no name]' local buffer_hl = wrap_hl('Buffer' .. activity_name .. (modified and 'Mod' or '')) - local icons_option = Buffer.get_icons(activity_name, modified, pinned) + local icons_option = buffer.get_icons(activity_name, modified, pinned) --- Prefix this value to allow an element to be clicked local clickable = click_enabled and ('%' .. bufnr .. '@barbar#events#main_click_handler@') or '' @@ -452,7 +457,7 @@ local function get_bufferline_containers(layout, bufnrs, refocus) local icon = { hl = clickable, text = '' } if state.is_picking_buffer then - local letter = JumpMode.get_letter(bufnr) + local letter = get_letter(bufnr) -- Replace first character of buf name with jump letter if letter and not icons_option.filetype.enabled then @@ -469,7 +474,7 @@ local function get_bufferline_containers(layout, bufnrs, refocus) jump_letter.text = ' ' end elseif icons_option.filetype.enabled then - local iconChar, iconHl = icons.get_icon(bufnr, activity_name) + local iconChar, iconHl = get_icon(bufnr, activity_name) local hlName = (activity_name == 'Inactive' and not config.options.highlight_inactive_file_icons) and 'BufferInactive' or iconHl @@ -497,14 +502,14 @@ local function get_bufferline_containers(layout, bufnrs, refocus) width = container_width, } - Buffer.for_each_counted_enabled_diagnostic(bufnr, icons_option.diagnostics, function(count, idx, option) + buffer.for_each_counted_enabled_diagnostic(bufnr, icons_option.diagnostics, function(count, idx, option) table_insert(container.nodes, { hl = wrap_hl('Buffer' .. activity_name .. severity[idx]), text = ' ' .. option.icon .. count, }) end) - Buffer.for_each_enabled_git_status(bufnr, icons_option.gitsigns, function(count, idx, option) + buffer.for_each_enabled_git_status(bufnr, icons_option.gitsigns, function(count, idx, option) table_insert(container.nodes, { hl = wrap_hl('Buffer' .. activity_name .. idx:upper()), text = ' ' .. option.icon .. count, @@ -539,8 +544,8 @@ local HL = { --- @param refocus? boolean if `true`, the bufferline will be refocused on the current buffer (default: `true`) --- @return nil|string syntax local function generate_tabline(bufnrs, refocus) - local layout = Layout.calculate() - local pinned_containers, containers = get_bufferline_containers(layout, bufnrs, refocus) + local data = layout.calculate() + local pinned_containers, containers = get_bufferline_containers(data, bufnrs, refocus) -- Create actual tabline string local result = '' @@ -554,25 +559,25 @@ local function generate_tabline(bufnrs, refocus) local content_max_width = state.offset.left.width - 2 offset_nodes = - Nodes.insert_many( + nodes.insert_many( offset_nodes, 1, - Nodes.slice_right(content, content_max_width)) + nodes.slice_right(content, content_max_width)) - result = result .. Nodes.to_string(offset_nodes) + result = result .. nodes.to_string(offset_nodes) end -- Buffer tabs do --- @type barbar.ui.container - local content = { { hl = HL.FILL, text = (' '):rep(layout.buffers.width) } } + local content = { { hl = HL.FILL, text = (' '):rep(data.buffers.width) } } do local current_container = nil for _, container in ipairs(containers) do -- We insert the current buffer after the others so it's always on top - if container.activity ~= Buffer.activities.Current then - content = Nodes.insert_many( + if container.activity ~= buffer.activities.Current then + content = nodes.insert_many( content, container.position - scroll.current, container.nodes) @@ -583,7 +588,7 @@ local function generate_tabline(bufnrs, refocus) if current_container ~= nil then local container = current_container - content = Nodes.insert_many( + content = nodes.insert_many( content, container.position - scroll.current, container.nodes) @@ -593,11 +598,11 @@ local function generate_tabline(bufnrs, refocus) do local inactive_separator = config.options.icons.inactive.separator.left if inactive_separator ~= nil and #containers > 0 and - layout.buffers.unpinned_width + strwidth(inactive_separator) <= layout.buffers.unpinned_allocated_width + data.buffers.unpinned_width + strwidth(inactive_separator) <= data.buffers.unpinned_allocated_width then - content = Nodes.insert( + content = nodes.insert( content, - layout.buffers.used_width, + data.buffers.used_width, { text = inactive_separator, hl = HL.SIGN_INACTIVE }) end end @@ -605,50 +610,48 @@ local function generate_tabline(bufnrs, refocus) if #pinned_containers > 0 then local current_container = nil for _, container in ipairs(pinned_containers) do - if container.activity ~= Buffer.activities.Current then - content = Nodes.insert_many(content, container.position, container.nodes) + if container.activity ~= buffer.activities.Current then + content = nodes.insert_many(content, container.position, container.nodes) else current_container = container end end if current_container ~= nil then local container = current_container - content = Nodes.insert_many(content, container.position, container.nodes) + content = nodes.insert_many(content, container.position, container.nodes) end end - local filler = { { hl = HL.FILL, text = (' '):rep(layout.buffers.width) } } - content = Nodes.insert_many(filler, 0, content) - content = Nodes.slice_right(content, layout.buffers.width) + local filler = { { hl = HL.FILL, text = (' '):rep(data.buffers.width) } } + content = nodes.insert_many(filler, 0, content) + content = nodes.slice_right(content, data.buffers.width) local has_left_scroll = scroll.current > 0 if has_left_scroll then - content = Nodes.insert(content, layout.buffers.pinned_width, + content = nodes.insert(content, data.buffers.pinned_width, { hl = HL.SCROLL_ARROW, text = config.options.icons.scroll.left }) end - local has_right_scroll = layout.buffers.used_width - scroll.current > layout.buffers.width + local has_right_scroll = data.buffers.used_width - scroll.current > data.buffers.width if has_right_scroll then - content = Nodes.insert(content, layout.buffers.width - 1, + content = nodes.insert(content, data.buffers.width - 1, { hl = HL.SCROLL_ARROW, text = config.options.icons.scroll.right }) end -- Render bufferline string - result = result .. Nodes.to_string(content) + result = result .. nodes.to_string(content) - -- Prevent the expansion of the last click node + -- Prevent the expansion of the last click group if config.options.clickable then result = result .. '%0@barbar#events#main_click_handler@' end end -- Tabpages - do - if layout.tabpages.width > 0 then - result = result .. Nodes.to_string({ - { hl = HL.TABPAGES, text = ' ' .. tabpagenr() .. '/' .. tabpagenr('$') .. ' ', }, - }) - end + if data.tabpages.width > 0 then + result = result .. nodes.to_string({ + { hl = HL.TABPAGES, text = ' ' .. tabpagenr() .. '/' .. tabpagenr('$') .. ' ' }, + }) end -- Right offset @@ -660,12 +663,12 @@ local function generate_tabline(bufnrs, refocus) local content_max_width = state.offset.right.width - 2 offset_nodes = - Nodes.insert_many( + nodes.insert_many( offset_nodes, 1, - Nodes.slice_right(content, content_max_width)) + nodes.slice_right(content, content_max_width)) - result = result .. Nodes.to_string(offset_nodes) + result = result .. nodes.to_string(offset_nodes) end -- NOTE: For development or debugging purposes, the following code can be used: @@ -690,7 +693,7 @@ function render.update(update_names, refocus) return end - local buffers = Buffer.hide(render.get_updated_buffers(update_names)) + local buffers = buffer.hide(render.get_updated_buffers(update_names)) -- Auto hide/show if applicable if config.options.auto_hide then @@ -724,7 +727,7 @@ function render.update(update_names, refocus) if not ok then command('BarbarDisable') - utils.notify( + notify( "Barbar detected an error while running. Barbar disabled itself :/ " .. "Include this in your report: " .. tostring(result), diff --git a/lua/barbar/utils.lua b/lua/barbar/utils.lua index fe8cde61..0df00ed5 100644 --- a/lua/barbar/utils.lua +++ b/lua/barbar/utils.lua @@ -2,345 +2,45 @@ -- utils.lua -- -local table_insert = table.insert - -local fnamemodify = vim.fn.fnamemodify --- @type function -local get_hl = vim.api.nvim_get_hl --- @type function -local get_hl_by_name = vim.api.nvim_get_hl_by_name --- @type function -local hlexists = vim.fn.hlexists --- @type function -local list_slice = vim.list_slice local notify = vim.notify local notify_once = vim.notify_once -local set_hl = vim.api.nvim_set_hl --- @type function -local tbl_isempty = vim.tbl_isempty - ---- @type {[string]: table} -local hl_groups_cache = {} ---- Remove `tbl[key]` from `tbl` and return it ---- @param tbl table ---- @param key string ---- @return any -local function tbl_remove_key (tbl, key) - local value = rawget(tbl, key) - rawset(tbl, key, nil) - return value -end +--- @class barbar.Utils +local utils = {} -local get_hl_util = get_hl and - --- @param name string - --- @return table - function(name) return get_hl(0, {link = false, name = name}) end or +utils.deprecate = vim.deprecate and + --- Notify a user that something has been deprecated, and that there is an alternative. --- @param name string - --- @return table - function(name) - if hlexists(name) < 1 then - return {} - end - - local definition = get_hl_by_name(name, true) - for original, new in pairs {background = 'bg', foreground = 'fg', special = 'sp'} do - local attribute_definition = tbl_remove_key(definition, original) - if attribute_definition then - definition[new] = attribute_definition - end - end - - local cterm = get_hl_by_name(name, false) - definition.ctermfg = cterm.foreground - definition.ctermbg = cterm.background - - return definition - end - ---- @param group string the groups to source the color from. ---- @return table -local function get_hl_cached(group) - local hl_cached = hl_groups_cache[group] - if hl_cached then - return hl_cached - end - - local hl = get_hl_util(group) - hl_groups_cache[group] = hl - return hl -end - ---- Generate a color. ---- @param groups string[] the groups to source the color from. ---- @param attribute 'bg'|'ctermbg'|'ctermfg'|'fg'|'sp' where to look for the color. ---- @param default barbar.utils.hl.color.value a color name (`string`), GUI hex (`string`), or cterm color code (`integer`). ---- @return barbar.utils.hl.color.value color -local function get_hl_color_or_default(groups, attribute, default) - for _, group in ipairs(groups) do - local hl_attribute = get_hl_cached(group)[attribute] - if hl_attribute then - return hl_attribute - end + --- @param alternative string + --- @return nil + function(name, alternative) + vim.deprecate(name, alternative, '2.0.0', 'barbar.nvim') + end or + function(name, alternative) + utils.notify_once(name .. ' is deprecated. Use ' .. alternative .. 'instead.', vim.log.levels.WARN) end - return default +--- Return "\``s`\`" +--- @param s string +--- @return string inline_code +function utils.markdown_inline_code(s) + return '`' .. s .. '`' end ---- Return the index of element `n` in `list. ---- @generic T ---- @param list T[] ---- @param t T ---- @return nil|integer index -local function index_of(list, t) - for i, value in ipairs(list) do - if value == t then - return i - end - end - return nil +--- Use `vim.notify` with a `msg` and log `level`. Integrates with `nvim-notify`. +--- @param msg string +--- @param level 0|1|2|3|4|5 +--- @return nil +function utils.notify(msg, level) + notify(msg, level, {title = 'barbar.nvim'}) end --- Use `vim.notify` with a `msg` and log `level`. Integrates with `nvim-notify`. --- @param msg string --- @param level 0|1|2|3|4|5 --- @return nil -local function notify_once_util(msg, level) +function utils.notify_once(msg, level) notify_once(msg, level, {title = 'barbar.nvim'}) end ---- @param path string ---- @return string relative_path -local function relative(path) - return fnamemodify(path, ':~:.') -end - ---- @class barbar.utils -local utils = { - --- @param path string - --- @param hide_extension? boolean if `true`, exclude the extension of the file in the basename - --- @return string basename - basename = function(path, hide_extension) - local modifier = ':t' - - if hide_extension then - modifier = modifier .. ':r' - end - - return fnamemodify(path, modifier) - end, - - deprecate = vim.deprecate and - --- Notify a user that something has been deprecated, and that there is an alternative. - --- @param name string - --- @param alternative string - --- @return nil - function(name, alternative) - vim.deprecate(name, alternative, '2.0.0', 'barbar.nvim') - end or - function(name, alternative) - notify_once_util(name .. ' is deprecated. Use ' .. alternative .. 'instead.', vim.log.levels.WARN) - end, - - --- utilities for working with highlight groups. - --- @class barbar.utils.hl - hl = { - --- @class barbar.utils.hl.attributes see |:h attr-list| - --- @field blend? integer 0–100 - --- @field bold? boolean - --- @field default? boolean - --- @field italic? boolean - --- @field nocombine? boolean - --- @field reverse? boolean - --- @field standout? boolean - --- @field strikethrough? boolean - --- @field undercurl? boolean - --- @field underdashed? boolean - --- @field underdotted? boolean - --- @field underdouble? boolean - --- @field underline? boolean - - --- @alias barbar.utils.hl.color.value integer|string - - --- @class barbar.utils.hl.color - --- @field cterm barbar.utils.hl.color.value - --- @field gui barbar.utils.hl.color.value - - --- Generate a color. - --- @param groups string[] the groups to source the color from. - --- @return nil|barbar.utils.hl.attributes - attributes = function(groups) - for _, group in ipairs(groups) do - local hl = get_hl_cached(group) - if not tbl_isempty(hl) then - return hl - end - end - end, - - --- Generate a background color. - --- @param groups string[] the groups to source the background color from. - --- @param default barbar.utils.hl.color.value the background color to use if no `groups` have a valid background color. - --- @param default_cterm? barbar.utils.hl.color.value the color to use if no `groups` have a valid color and `termguicolors == false`. - --- @return barbar.utils.hl.color color - bg_or_default = function(groups, default, default_cterm) - return { - cterm = get_hl_color_or_default(groups, 'ctermbg', default_cterm or default), - gui = get_hl_color_or_default(groups, 'bg', default), - } - end, - - --- Generate a foreground color. - --- @param groups string[] the groups to source the foreground color from. - --- @param default barbar.utils.hl.color.value the foreground color to use if no `groups` have a valid foreground color. - --- @param default_cterm? barbar.utils.hl.color.value the color to use if no `groups` have a valid color and `termguicolors == false`. - --- @return barbar.utils.hl.color color - fg_or_default = function(groups, default, default_cterm) - return { - cterm = get_hl_color_or_default(groups, 'ctermfg', default_cterm or default), - gui = get_hl_color_or_default(groups, 'fg', default), - } - end, - - --- Reset the `nvim_get_hl` cache - reset_cache = function() hl_groups_cache = {} end, - - --- Set some highlight `group`'s default definition with respect to `&termguicolors` - --- @param group string the name of the highlight group to set - --- @param bg barbar.utils.hl.color - --- @param fg barbar.utils.hl.color - --- @param sp? barbar.utils.hl.color.value - --- @param attributes? barbar.utils.hl.attributes whether the highlight group should be bolded - --- @return nil - set = function(group, bg, fg, sp, attributes) - if not attributes then - attributes = {} - end - - attributes.bg = bg.gui - attributes.ctermbg = bg.cterm - attributes.ctermfg = fg.cterm - attributes.fg = fg.gui - attributes.sp = sp - attributes[vim.type_idx] = nil - - set_hl(0, group, attributes) - end, - - --- Set the default highlight `group_name` as a link to `link_name` - --- @param group_name string the name of the group to by-default be linked to `link_name` - --- @param link_name string the name of the group to by-default link `group_name` to - --- @return nil - set_default_link = function(group_name, link_name) - set_hl(0, group_name, {default = true, link = link_name}) - end, - - --- Generate a foreground color. - --- @param groups string[] the groups to source the foreground color from. - --- @param default string the foreground color to use if no `groups` have a valid foreground color. - --- @return barbar.utils.hl.color.value color - sp_or_default = function(groups, default) - return get_hl_color_or_default(groups, 'sp', default) - end, - }, - - index_of = index_of, - - --- @param path string - --- @return boolean is_relative `true` if `path` is relative to the CWD - is_relative_path = function(path) - return relative(path) == path - end, - - --- Reverse the order of elements in some `list`. - --- @generic T - --- @param list T[] - --- @return T[] reversed - list_reverse = function(list) - local reversed = {} - for i = #list, 1, -1 do - table_insert(reversed, list[i]) - end - return reversed - end, - - --- Run `vim.list_slice` on some `list`, `index`ed from the end of the list. - --- @generic T - --- @param list T[] - --- @param index_from_end number - --- @return T[] sliced - list_slice_from_end = function(list, index_from_end) - return list_slice(list, #list - index_from_end + 1) - end, - - --- Return "\``s`\`" - --- @param s string - --- @return string inline_code - markdown_inline_code = function(s) - return '`' .. s .. '`' - end, - - --- Use `vim.notify` with a `msg` and log `level`. Integrates with `nvim-notify`. - --- @param msg string - --- @param level 0|1|2|3|4|5 - --- @return nil - notify = function(msg, level) - notify(msg, level, {title = 'barbar.nvim'}) - end, - - notify_once = notify_once_util, - - relative = relative, - - --- Set `fallback` as a secondary source of keys when indexing `tbl`. Example: - --- - --- ```lua - --- local fallback = {bar = true} - --- local tbl = {} - --- - --- print(tbl.bar) -- `nil` - --- setfallbacktable(tbl, fallback) - --- print(tbl.bar) -- `true` - --- - --- tbl.bar = false - --- print(tbl.bar, fallback.bar) -- `false`, `true` - --- ``` - --- - --- WARN: this mutates `tbl`! - --- @param tbl? table - --- @param fallback table - --- @return table tbl corresponding to the `tbl` parameter - setfallbacktable = function(tbl, fallback) - return setmetatable(tbl or {}, {__index = fallback}) - end, - - tbl_remove_key = tbl_remove_key, - - --- Set a `value` in a `tbl` multiple `keys` deep. - -- - --- ```lua - --- assert(vim.deep_equal( - --- {a = {b = {c = 'd'}}}, - --- tbl_set({}, {'a', 'b', 'c'}, 'd') - --- )) - --- ``` - --- - --- WARN: this mutates `tbl`! - --- - --- @generic T - --- @param tbl table - --- @param keys table - --- @param value T - --- @return nil - tbl_set = function(tbl, keys, value) - local current = tbl - for i = 1, #keys - 1 do - local key = keys[i] - - if current[key] == nil then - current[key] = {} - end - - current = current[key] - end - - current[keys[#keys]] = value - end, -} - return utils diff --git a/lua/barbar/utils/highlight.lua b/lua/barbar/utils/highlight.lua new file mode 100644 index 00000000..b60795ae --- /dev/null +++ b/lua/barbar/utils/highlight.lua @@ -0,0 +1,169 @@ +local get_hl = vim.api.nvim_get_hl --- @type function +local get_hl_by_name = vim.api.nvim_get_hl_by_name --- @type function +local hlexists = vim.fn.hlexists --- @type function +local set_hl = vim.api.nvim_set_hl --- @type function +local tbl_isempty = vim.tbl_isempty + +local tbl = require('barbar.utils.table') + +--- @type {[string]: nil|table} +local hl_groups_cache = {} + +local get_hl_util = get_hl and + --- @param name string + --- @return table + function(name) return get_hl(0, {link = false, name = name}) end or + function(name) + if hlexists(name) < 1 then + return {} + end + + local definition = get_hl_by_name(name, true) + for original, new in pairs {background = 'bg', foreground = 'fg', special = 'sp'} do + local attribute_definition = tbl.remove_key(definition, original) + if attribute_definition then + definition[new] = attribute_definition + end + end + + local cterm = get_hl_by_name(name, false) + definition.ctermfg = cterm.foreground + definition.ctermbg = cterm.background + + return definition + end + +--- @param group string the groups to source the color from. +--- @return table +local function get_hl_cached(group) + do + local hl_cached = hl_groups_cache[group] + if hl_cached then + return hl_cached + end + end + + local hl = get_hl_util(group) + hl_groups_cache[group] = hl + return hl +end + +--- Generate a color. +--- @param groups string[] the groups to source the color from. +--- @param attribute 'bg'|'ctermbg'|'ctermfg'|'fg'|'sp' where to look for the color. +--- @param default barbar.utils.hl.color.value a color name (`string`), GUI hex (`string`), or cterm color code (`integer`). +--- @return barbar.utils.hl.color.value color +local function get_hl_color_or_default(groups, attribute, default) + for _, group in ipairs(groups) do + local hl_attribute = get_hl_cached(group)[attribute] + if hl_attribute then + return hl_attribute + end + end + + return default +end + +--- @class barbar.utils.hl.attributes see |:h attr-list| +--- @field blend? integer 0–100 +--- @field bold? boolean +--- @field default? boolean +--- @field italic? boolean +--- @field nocombine? boolean +--- @field reverse? boolean +--- @field standout? boolean +--- @field strikethrough? boolean +--- @field undercurl? boolean +--- @field underdashed? boolean +--- @field underdotted? boolean +--- @field underdouble? boolean +--- @field underline? boolean + +--- @alias barbar.utils.hl.color.value integer|string + +--- @class barbar.utils.hl.color +--- @field cterm barbar.utils.hl.color.value +--- @field gui barbar.utils.hl.color.value + +--- utilities for working with highlight groups. +--- @class barbar.utils.Hl +local hl = {} + +--- Generate a color. +--- @param groups string[] the groups to source the color from. +--- @return nil|barbar.utils.hl.attributes +function hl.attributes(groups) + for _, group in ipairs(groups) do + local cached = get_hl_cached(group) + if not tbl_isempty(cached) then + return cached + end + end +end + +--- Generate a background color. +--- @param groups string[] the groups to source the background color from. +--- @param default barbar.utils.hl.color.value the background color to use if no `groups` have a valid background color. +--- @param default_cterm? barbar.utils.hl.color.value the color to use if no `groups` have a valid color and `termguicolors == false`. +--- @return barbar.utils.hl.color color +function hl.bg_or_default(groups, default, default_cterm) + return { + cterm = get_hl_color_or_default(groups, 'ctermbg', default_cterm or default), + gui = get_hl_color_or_default(groups, 'bg', default), + } +end + +--- Generate a foreground color. +--- @param groups string[] the groups to source the foreground color from. +--- @param default barbar.utils.hl.color.value the foreground color to use if no `groups` have a valid foreground color. +--- @param default_cterm? barbar.utils.hl.color.value the color to use if no `groups` have a valid color and `termguicolors == false`. +--- @return barbar.utils.hl.color color +function hl.fg_or_default(groups, default, default_cterm) + return { + cterm = get_hl_color_or_default(groups, 'ctermfg', default_cterm or default), + gui = get_hl_color_or_default(groups, 'fg', default), + } +end + +--- Reset the `nvim_get_hl` cache +function hl.reset_cache() hl_groups_cache = {} end + +--- Set some highlight `group`'s default definition with respect to `&termguicolors` +--- @param group string the name of the highlight group to set +--- @param bg barbar.utils.hl.color +--- @param fg barbar.utils.hl.color +--- @param sp? barbar.utils.hl.color.value +--- @param attributes? barbar.utils.hl.attributes whether the highlight group should be bolded +--- @return nil +function hl.set(group, bg, fg, sp, attributes) + if not attributes then + attributes = {} + end + + attributes.bg = bg.gui + attributes.ctermbg = bg.cterm + attributes.ctermfg = fg.cterm + attributes.fg = fg.gui + attributes.sp = sp + attributes[vim.type_idx] = nil + + set_hl(0, group, attributes) +end + +--- Set the default highlight `group_name` as a link to `link_name` +--- @param group_name string the name of the group to by-default be linked to `link_name` +--- @param link_name string the name of the group to by-default link `group_name` to +--- @return nil +function hl.set_default_link(group_name, link_name) + set_hl(0, group_name, {default = true, link = link_name}) +end + +--- Generate a foreground color. +--- @param groups string[] the groups to source the foreground color from. +--- @param default string the foreground color to use if no `groups` have a valid foreground color. +--- @return barbar.utils.hl.color.value color +function hl.sp_or_default(groups, default) + return get_hl_color_or_default(groups, 'sp', default) +end + +return hl diff --git a/lua/barbar/utils/list.lua b/lua/barbar/utils/list.lua new file mode 100644 index 00000000..19128267 --- /dev/null +++ b/lua/barbar/utils/list.lua @@ -0,0 +1,43 @@ +local table_insert = table.insert + +local list_slice = vim.list_slice + +--- @class barbar.utils.List +local list = {} + +--- Return the index of element `n` in `list. +--- @generic T +--- @param tbl T[] +--- @param t T +--- @return nil|integer index +function list.index_of(tbl, t) + for i, value in ipairs(tbl) do + if value == t then + return i + end + end + return nil +end + +--- reverse the order of elements in some `list`. +--- perf: don't do `ipairs(list_reverse(list))`, just do `for i = #list, 1, -1` instead. +--- @generic t +--- @param tbl t[] +function list.reverse(tbl) + local reversed = {} + for i = #tbl, 1, -1 do + table_insert(reversed, tbl[i]) + end + return reversed +end + +--- Run `vim.list_slice` on some `list`, `index`ed from the end of the list. +--- @generic T +--- @param tbl T[] +--- @param index_from_end number +--- @return T[] sliced +function list.slice_from_end(tbl, index_from_end) + return list_slice(tbl, #tbl - index_from_end + 1) +end + +return list diff --git a/lua/barbar/utils/table.lua b/lua/barbar/utils/table.lua new file mode 100644 index 00000000..a5daab62 --- /dev/null +++ b/lua/barbar/utils/table.lua @@ -0,0 +1,45 @@ +--- @class barbar.utils.Table +local table = {} + +--- Remove `tbl[key]` from `tbl` and return it +--- @param tbl table +--- @param key string +--- @return any +function table.remove_key(tbl, key) + local value = rawget(tbl, key) + rawset(tbl, key, nil) + return value +end + +--- Set a `value` in a `tbl` multiple `keys` deep. +-- +--- ```lua +--- assert(vim.deep_equal( +--- {a = {b = {c = 'd'}}}, +--- tbl_set({}, {'a', 'b', 'c'}, 'd') +--- )) +--- ``` +--- +--- WARN: this mutates `tbl`! +--- +--- @generic T +--- @param tbl table +--- @param keys table +--- @param value T +--- @return nil +function table.set(tbl, keys, value) + local current = tbl + for i = 1, #keys - 1 do + local key = keys[i] + + if current[key] == nil then + current[key] = {} + end + + current = current[key] + end + + current[keys[#keys]] = value +end + +return table diff --git a/lua/bufferline.lua b/lua/bufferline.lua index e3b5e09a..f48d72ae 100644 --- a/lua/bufferline.lua +++ b/lua/bufferline.lua @@ -1 +1 @@ -return require'barbar' +return require('barbar') diff --git a/lua/bufferline/animate.lua b/lua/bufferline/animate.lua index 2efb7fca..65236720 100644 --- a/lua/bufferline/animate.lua +++ b/lua/bufferline/animate.lua @@ -1 +1 @@ -return require'barbar.animate' +return require('barbar.animate') diff --git a/lua/bufferline/api.lua b/lua/bufferline/api.lua index 18a336d0..46b7eed0 100644 --- a/lua/bufferline/api.lua +++ b/lua/bufferline/api.lua @@ -1 +1 @@ -return require'barbar.api' +return require('barbar.api') diff --git a/lua/bufferline/bbye.lua b/lua/bufferline/bbye.lua index 3951c0c3..85b77475 100644 --- a/lua/bufferline/bbye.lua +++ b/lua/bufferline/bbye.lua @@ -1 +1 @@ -return require'barbar.bbye' +return require('barbar.bbye') diff --git a/lua/bufferline/buffer.lua b/lua/bufferline/buffer.lua index 1f647b12..ce4521cd 100644 --- a/lua/bufferline/buffer.lua +++ b/lua/bufferline/buffer.lua @@ -1 +1 @@ -return require'barbar.buffer' +return require('barbar.buffer') diff --git a/lua/bufferline/config.lua b/lua/bufferline/config.lua index 396d0bac..8e4b3deb 100644 --- a/lua/bufferline/config.lua +++ b/lua/bufferline/config.lua @@ -1 +1 @@ -return require'barbar.config' +return require('barbar.config') diff --git a/lua/bufferline/events.lua b/lua/bufferline/events.lua index 9672a499..015486ba 100644 --- a/lua/bufferline/events.lua +++ b/lua/bufferline/events.lua @@ -1 +1 @@ -return require'barbar.events' +return require('barbar.events') diff --git a/lua/bufferline/highlight.lua b/lua/bufferline/highlight.lua index 74077ec3..ac763e9a 100644 --- a/lua/bufferline/highlight.lua +++ b/lua/bufferline/highlight.lua @@ -1 +1 @@ -return require'barbar.highlight' +return require('barbar.highlight') diff --git a/lua/bufferline/icons.lua b/lua/bufferline/icons.lua index 7deca167..570b8dae 100644 --- a/lua/bufferline/icons.lua +++ b/lua/bufferline/icons.lua @@ -1 +1 @@ -return require'barbar.icons' +return require('barbar.icons') diff --git a/lua/bufferline/jump_mode.lua b/lua/bufferline/jump_mode.lua index 5910663e..f0916772 100644 --- a/lua/bufferline/jump_mode.lua +++ b/lua/bufferline/jump_mode.lua @@ -1 +1 @@ -return require'barbar.jump_mode' +return require('barbar.jump_mode') diff --git a/lua/bufferline/layout.lua b/lua/bufferline/layout.lua index 5926ef18..27aa5844 100644 --- a/lua/bufferline/layout.lua +++ b/lua/bufferline/layout.lua @@ -1 +1 @@ -return require'barbar.ui.layout' +return require('barbar.ui.layout') diff --git a/lua/bufferline/render.lua b/lua/bufferline/render.lua index dcdb3a69..58e7629b 100644 --- a/lua/bufferline/render.lua +++ b/lua/bufferline/render.lua @@ -1,4 +1,4 @@ -local render = require('barbar.ui.render') +local render = vim.deepcopy(require('barbar.ui.render')) --- @deprecated use `state.restore_buffers` instead render.restore_buffers = require('barbar.state').restore_buffers diff --git a/lua/bufferline/state.lua b/lua/bufferline/state.lua index 2ccace91..1e5461e3 100644 --- a/lua/bufferline/state.lua +++ b/lua/bufferline/state.lua @@ -1 +1 @@ -return require'barbar.state' +return require('barbar.state') diff --git a/lua/bufferline/utils.lua b/lua/bufferline/utils.lua index 958187b3..c1e6ccd0 100644 --- a/lua/bufferline/utils.lua +++ b/lua/bufferline/utils.lua @@ -1 +1,25 @@ -return require'barbar.utils' +local utils = vim.deepcopy(require('barbar.utils')) + +do + local fs = require('barbar.fs') + utils.basename = fs.basename + utils.is_relative_path = fs.is_relative_path + utils.relative = fs.relative +end + +utils.hl = require('barbar.utils.hl') + +do + local list = require('barbar.utils.list') + utils.index_of = list.index_of + utils.list_reverse = list.reverse + utils.list_slice_from_end = list.slice_from_end +end + +do + local tbl = require('barbar.utils.table') + utils.tbl_remove_key = tbl.remove_key + utils.tbl_set = tbl.set +end + +return utils diff --git a/plugin/barbar.lua b/plugin/barbar.lua index bb74c874..d99b339a 100644 --- a/plugin/barbar.lua +++ b/plugin/barbar.lua @@ -8,12 +8,12 @@ if vim.g.barbar_auto_setup ~= false then local options = vim.g.bufferline if options then - require'barbar.utils'.notify_once( - "`g:bufferline` is deprecated, use `require'barbar'.setup` instead. " .. + require('barbar.utils').notify_once( + "`g:bufferline` is deprecated, use `require('barbar').setup` instead. " .. 'See `:h barbar-setup` for more information.', vim.log.levels.WARN ) end - require'barbar'.setup(options) + require('barbar').setup(options) end From 5fe9aae8b4817087fd53bd4b0ad60d3fd1ae5ea4 Mon Sep 17 00:00:00 2001 From: Iron-E Date: Sun, 9 Apr 2023 23:32:15 -0400 Subject: [PATCH 2/5] fix: sparse `icons.diagnostics` doesn't work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If you do: ```lua require'barbar'.setup { icons = { diagnostics = { [vim.diagnostic.ERROR] = {enabled = true}, [vim.diagnostic.HINT] = {enabled = true}, }, }, } ``` …it will not work. This is because a function in `Buffer` was using `ipairs`, which stops upon reaching the first `nil` numerical index in a `table`. However, upon fixing this, I discovered another bug: `icons` was not being fully merged. We had a snippet of code to "deep extend" `icons.diagnostics`, but since we were only using `vim.tbl_deep_extend` on the other `icons` options, they did not inherit the diagnostics. I extracted this snippet to a function and called it on all `icons.modified`, `icons.current`, etc. --- lua/barbar.lua | 2 +- lua/barbar/buffer.lua | 301 +++++++++++++++++++++--------------------- lua/barbar/config.lua | 132 +++++++++--------- 3 files changed, 217 insertions(+), 218 deletions(-) diff --git a/lua/barbar.lua b/lua/barbar.lua index dc262463..9db667fc 100644 --- a/lua/barbar.lua +++ b/lua/barbar.lua @@ -59,7 +59,7 @@ function barbar.setup(options) local buffers = state.buffers local buffer_indices = {} - for i = 1, #buffers do + for i in ipairs(buffers) do table_insert(buffer_indices, tostring(i)) end diff --git a/lua/barbar/buffer.lua b/lua/barbar/buffer.lua index 4f69e7c0..f0642496 100644 --- a/lua/barbar/buffer.lua +++ b/lua/barbar/buffer.lua @@ -12,24 +12,25 @@ local buf_get_option = vim.api.nvim_buf_get_option --- @type function local buf_is_valid = vim.api.nvim_buf_is_valid --- @type function local bufnr = vim.fn.bufnr --- @type function local bufwinnr = vim.fn.bufwinnr --- @type function -local ERROR = vim.diagnostic.severity.ERROR --- @type integer local get_current_buf = vim.api.nvim_get_current_buf --- @type function local get_diagnostics = vim.diagnostic.get --- @type fun(bufnr: integer): {severity: integer}[] -local HINT = vim.diagnostic.severity.HINT --- @type integer -local INFO = vim.diagnostic.severity.INFO --- @type integer local matchlist = vim.fn.matchlist --- @type function +local severity = vim.diagnostic.severity local split = vim.split local strcharpart = vim.fn.strcharpart --- @type function local strwidth = vim.api.nvim_strwidth --- @type function -local WARN = vim.diagnostic.severity.WARN --- @type integer -local config = require'barbar.config' -local utils = require'barbar.utils' +local basename = require('barbar.fs').basename +local config = require('barbar.config') +local slice_from_end = require('barbar.utils.list').slice_from_end local ELLIPSIS = '…' local ELLIPSIS_LEN = strwidth(ELLIPSIS) - +local ERROR = severity.ERROR local GIT_STATUSES = {'added', 'changed', 'deleted'} +local HINT = severity.HINT +local INFO = severity.INFO +local WARN = severity.WARN --- @alias barbar.buffer.activity 1|2|3|4 @@ -53,9 +54,67 @@ local function terminalname(name) end end +--- @class barbar.Buffer +local buffer = { activities = activities } + +--- @param buffer_number integer +--- @return integer[] # indexed on `vim.diagnostic.severity` +function buffer.count_diagnostics(buffer_number) + local count = {[ERROR] = 0, [HINT] = 0, [INFO] = 0, [WARN] = 0} + + for _, diagnostic in ipairs(get_diagnostics(buffer_number)) do + count[diagnostic.severity] = count[diagnostic.severity] + 1 + end + + return count +end + +--- For each severity in `diagnostics`: if it is enabled, and there are diagnostics associated with it in the `buffer_number` provided, call `f`. +--- @param buffer_number integer the buffer number to count diagnostics in +--- @param diagnostics barbar.config.options.icons.buffer.diagnostics the user configuration for diagnostics +--- @param f fun(count: integer, severity_idx: integer, option: barbar.config.options.icons.diagnostics.severity) the function to run when diagnostics of a specific severity are enabled and present in the `buffer_number` +--- @return nil +function buffer.for_each_counted_enabled_diagnostic(buffer_number, diagnostics, f) + local count + for i in ipairs(severity) do + local option = diagnostics[i] + if option.enabled then + if count == nil then + count = buffer.count_diagnostics(buffer_number) + end + + if count[i] > 0 then + f(count[i], i, option) + end + end + end +end + +--- For each status in `git`: if it is enabled, and there is a git status associated with the buffer (`buffer_number`), call `f`. +--- @param buffer_number integer the buffer number to get git status +--- @param git barbar.config.options.icons.buffer.git the user configuration for git status +--- @param f fun(count: integer, git_status: string, option: barbar.config.options.icons.git.status) the function to run when a specific git status is enabled and present in the `buffer_number` +--- @return nil +function buffer.for_each_enabled_git_status(buffer_number, git, f) + local count + + for _, git_status in ipairs(GIT_STATUSES) do + local git_status_option = git[git_status] + if git_status_option.enabled then + if count == nil then + count = buffer.get_git_status(buffer_number) + end + + if count[git_status] ~= nil and count[git_status] > 0 then + f(count[git_status], git_status, git_status_option) + end + end + end +end + --- @param buffer_number integer --- @return barbar.buffer.activity # whether `bufnr` is inactive, the alternate file, visible, or currently selected (in that order). -local function get_activity(buffer_number) +function buffer.get_activity(buffer_number) if get_current_buf() == buffer_number then return activities.Current elseif config.options.highlight_alternate and bufnr('#') == buffer_number then @@ -67,21 +126,9 @@ local function get_activity(buffer_number) return activities.Inactive end ---- @param buffer_number integer ---- @return integer[] # indexed on `vim.diagnostic.severity` -local function count_diagnostics(buffer_number) - local count = {[ERROR] = 0, [HINT] = 0, [INFO] = 0, [WARN] = 0} - - for _, diagnostic in ipairs(get_diagnostics(buffer_number)) do - count[diagnostic.severity] = count[diagnostic.severity] + 1 - end - - return count -end - --- @param buffer_number integer --- @return integer[] # based on `gitsigns_status_dict` -local function get_git_status(buffer_number) +function buffer.get_git_status(buffer_number) local git_status = { added = 0, changed = 0, deleted = 0 } local ok, gitsigns_status_dict = pcall(vim.api.nvim_buf_get_var, buffer_number, "gitsigns_status_dict") @@ -97,151 +144,97 @@ local function get_git_status(buffer_number) return git_status end ---- @class barbar.buffer -local buffer = { - activities = activities, - - count_diagnostics = count_diagnostics, - - --- For each severity in `diagnostics`: if it is enabled, and there are diagnostics associated with it in the `buffer_number` provided, call `f`. - --- @param buffer_number integer the buffer number to count diagnostics in - --- @param diagnostics barbar.config.options.icons.buffer.diagnostics the user configuration for diagnostics - --- @param f fun(count: integer, severity_idx: integer, option: barbar.config.options.icons.diagnostics.severity) the function to run when diagnostics of a specific severity are enabled and present in the `buffer_number` - --- @return nil - for_each_counted_enabled_diagnostic = function(buffer_number, diagnostics, f) - local count - - for severity_idx, severity_option in ipairs(diagnostics) do - if severity_option.enabled then - if count == nil then - count = count_diagnostics(buffer_number) - end - - if count[severity_idx] > 0 then - f(count[severity_idx], severity_idx, severity_option) - end - end - end - end, - - get_git_status = get_git_status, - - --- For each status in `git`: if it is enabled, and there is a git status associated with the buffer (`buffer_number`), call `f`. - --- @param buffer_number integer the buffer number to get git status - --- @param git barbar.config.options.icons.buffer.git the user configuration for git status - --- @param f fun(count: integer, git_status: string, option: barbar.config.options.icons.git.status) the function to run when a specific git status is enabled and present in the `buffer_number` - --- @return nil - for_each_enabled_git_status = function(buffer_number, git, f) - local count - - for _, git_status in ipairs(GIT_STATUSES) do - local git_status_option = git[git_status] - if git_status_option.enabled then - if count == nil then - count = get_git_status(buffer_number) - end - - if count[git_status] ~= nil and count[git_status] > 0 then - f(count[git_status], git_status, git_status_option) - end - end - end - end, - - get_activity = get_activity, - - --- @param activity barbar.buffer.activity.name - --- @param modified boolean - --- @param pinned boolean - --- @return barbar.config.options.icons.buffer - get_icons = function(activity, modified, pinned) - local icons_option = config.options.icons[activity:lower()] - if pinned then - icons_option = icons_option.pinned - elseif modified then - icons_option = icons_option.modified - end +--- @param activity barbar.buffer.activity.name +--- @param modified boolean +--- @param pinned boolean +--- @return barbar.config.options.icons.buffer +function buffer.get_icons(activity, modified, pinned) + local icons_option = config.options.icons[activity:lower()] + if pinned then + icons_option = icons_option.pinned + elseif modified then + icons_option = icons_option.modified + end - return icons_option - end, + return icons_option +end - --- @param buffer_number integer - --- @param hide_extensions boolean? if `true`, exclude the extension of the file - --- @return string name - get_name = function(buffer_number, hide_extensions) - --- @type string - local name = buf_is_valid(buffer_number) and buf_get_name(buffer_number) or '' +--- @param buffer_number integer +--- @param hide_extensions boolean? if `true`, exclude the extension of the file +--- @return string name +function buffer.get_name(buffer_number, hide_extensions) + --- @type string + local name = buf_is_valid(buffer_number) and buf_get_name(buffer_number) or '' + + local no_name_title = config.options.no_name_title + local maximum_length = config.options.maximum_length + + if name ~= '' then + name = buf_get_option(buffer_number, 'buftype') == 'terminal' and terminalname(name) or basename(name, hide_extensions) + elseif no_name_title ~= nil and no_name_title ~= vim.NIL then + name = no_name_title + end - local no_name_title = config.options.no_name_title - local maximum_length = config.options.maximum_length + if name == '' then + name = '[buffer ' .. buffer_number .. ']' + end - if name ~= '' then - name = buf_get_option(buffer_number, 'buftype') == 'terminal' and terminalname(name) or utils.basename(name, hide_extensions) - elseif no_name_title ~= nil and no_name_title ~= vim.NIL then - name = no_name_title - end + if strwidth(name) > maximum_length then + local ext_index = name:reverse():find('%.') - if name == '' then - name = '[buffer ' .. buffer_number .. ']' + if ext_index ~= nil and (ext_index < maximum_length - ELLIPSIS_LEN) then + local extension = name:sub(-ext_index) + name = strcharpart(name, 0, maximum_length - ELLIPSIS_LEN - #extension) .. ELLIPSIS .. extension + else + name = strcharpart(name, 0, maximum_length - ELLIPSIS_LEN) .. ELLIPSIS end + end - if strwidth(name) > maximum_length then - local ext_index = name:reverse():find('%.') + return name +end - if ext_index ~= nil and (ext_index < maximum_length - ELLIPSIS_LEN) then - local extension = name:sub(-ext_index) - name = strcharpart(name, 0, maximum_length - ELLIPSIS_LEN - #extension) .. ELLIPSIS .. extension - else - name = strcharpart(name, 0, maximum_length - ELLIPSIS_LEN) .. ELLIPSIS - end - end +--- @param first string +--- @param second string +--- @return string, string +function buffer.get_unique_name(first, second) + local first_parts = split(first, separator) + local second_parts = split(second, separator) + + local length = 1 + local first_result = table_concat(slice_from_end(first_parts, length), separator) + local second_result = table_concat(slice_from_end(second_parts, length), separator) + + while first_result == second_result and + length < max(#first_parts, #second_parts) + do + length = length + 1 + first_result = table_concat(slice_from_end(first_parts, min(#first_parts, length)), separator) + second_result = table_concat(slice_from_end(second_parts, min(#second_parts, length)), separator) + end - return name - end, - - --- @param first string - --- @param second string - --- @return string, string - get_unique_name = function(first, second) - local first_parts = split(first, separator) - local second_parts = split(second, separator) - - local length = 1 - local first_result = table_concat(utils.list_slice_from_end(first_parts, length), separator) - local second_result = table_concat(utils.list_slice_from_end(second_parts, length), separator) - - while first_result == second_result and - length < max(#first_parts, #second_parts) - do - length = length + 1 - first_result = table_concat(utils.list_slice_from_end(first_parts, min(#first_parts, length)), separator) - second_result = table_concat(utils.list_slice_from_end(second_parts, min(#second_parts, length)), separator) - end + return first_result, second_result +end - return first_result, second_result - end, - - --- Filter buffer numbers which are not to be shown during the render process. - --- Does **not** mutate `bufnrs`. - --- @param bufnrs integer[] - --- @return integer[] bufnrs the shown buffers - hide = function(bufnrs) - local hide = config.options.hide - if hide.alternate or hide.current or hide.inactive or hide.visible then - local shown = {} - - for _, buffer_number in ipairs(bufnrs) do - local activity = activities[get_activity(buffer_number)] - if not hide[activity:lower()] then - table_insert(shown, buffer_number) - end +--- Filter buffer numbers which are not to be shown during the render process. +--- Does **not** mutate `bufnrs`. +--- @param bufnrs integer[] +--- @return integer[] bufnrs the shown buffers +function buffer.hide(bufnrs) + local hide = config.options.hide + if hide.alternate or hide.current or hide.inactive or hide.visible then + local shown = {} + + for _, buffer_number in ipairs(bufnrs) do + local activity = activities[buffer.get_activity(buffer_number)] + if not hide[activity:lower()] then + table_insert(shown, buffer_number) end - - bufnrs = shown end - return bufnrs - end, -} + bufnrs = shown + end + + return bufnrs +end return buffer diff --git a/lua/barbar/config.lua b/lua/barbar/config.lua index 901364fc..e9d9cc06 100644 --- a/lua/barbar/config.lua +++ b/lua/barbar/config.lua @@ -55,6 +55,28 @@ local DEFAULT_DIAGNOSTIC_ICONS = { [vim.diagnostic.severity.WARN] = { enabled = false, icon = '⚠️ ' }, } +--- Deeply extend `icons` to include the `DEFAULT_DIAGNOSTIC_ICONS` +--- HACK: required because `vim.tbl_deep_extend` does not deep extend lists. +--- @param icons table +--- @see vim.tbl_deep_extend +local function tbl_deep_extend_diagnostic_icons(icons) + for i, default_diagnostic_severity_icons in ipairs(DEFAULT_DIAGNOSTIC_ICONS) do + local diagnostic_severity_icons = icons.diagnostics[i] + if diagnostic_severity_icons == nil then + diagnostic_severity_icons = {} + icons.diagnostics[i] = diagnostic_severity_icons + else + if diagnostic_severity_icons.enabled == nil then + diagnostic_severity_icons.enabled = default_diagnostic_severity_icons.enabled + end + + if diagnostic_severity_icons.icon == nil then + diagnostic_severity_icons.icon = default_diagnostic_severity_icons.icon + end + end + end +end + --- @class barbar.config.options.icons.git.status --- @field enabled boolean --- @field icon string @@ -66,35 +88,35 @@ local DEFAULT_DIAGNOSTIC_ICONS = { --- @class barbar.config.options.icons.buffer.filetype --- @field custom_colors? boolean if present, this color will be used for ALL filetype icons ---- @field enabled? boolean iff `true`, show the `devicons` for the associated buffer's `filetype`. +--- @field enabled boolean iff `true`, show the `devicons` for the associated buffer's `filetype`. --- @class barbar.config.options.icons.buffer.separator ---- @field left? string a buffer's left separator ---- @field right? string a buffer's right separator +--- @field left string a buffer's left separator +--- @field right string a buffer's right separator --- @class barbar.config.options.icons.scroll --- @field left string --- @field right string --- @class barbar.config.options.icons.buffer ---- @field buffer_index? boolean iff `true`, show the index of the associated buffer with respect to the ordering of the buffers in the tabline. ---- @field buffer_number? boolean iff `true`, show the `bufnr` for the associated buffer. ---- @field filename? boolean iff `true`, show the filename ---- @field button? false|string the button which is clicked to close / save a buffer, or indicate that it is pinned. ---- @field diagnostics? barbar.config.options.icons.buffer.diagnostics the diagnostic icons ---- @field gitsigns? barbar.config.options.icons.buffer.git the git status icons ---- @field filetype? barbar.config.options.icons.buffer.filetype filetype icon options ---- @field separator? barbar.config.options.icons.buffer.separator the left-hand separator between buffers in the tabline +--- @field buffer_index boolean iff `true`, show the index of the associated buffer with respect to the ordering of the buffers in the tabline. +--- @field buffer_number boolean iff `true`, show the `bufnr` for the associated buffer. +--- @field filename boolean iff `true`, show the filename +--- @field button false|string the button which is clicked to close / save a buffer, or indicate that it is pinned. +--- @field diagnostics barbar.config.options.icons.buffer.diagnostics the diagnostic icons +--- @field gitsigns barbar.config.options.icons.buffer.git the git status icons +--- @field filetype barbar.config.options.icons.buffer.filetype filetype icon options +--- @field separator barbar.config.options.icons.buffer.separator the left-hand separator between buffers in the tabline --- @class barbar.config.options.icons.state: barbar.config.options.icons.buffer ---- @field modified? barbar.config.options.icons.buffer the icons used for an modified buffer ---- @field pinned? barbar.config.options.icons.buffer the icons used for a pinned buffer +--- @field modified barbar.config.options.icons.buffer the icons used for an modified buffer +--- @field pinned barbar.config.options.icons.buffer the icons used for a pinned buffer --- @class barbar.config.options.icons: barbar.config.options.icons.state ---- @field alternate? barbar.config.options.icons.state the icons used for an alternate buffer ---- @field current? barbar.config.options.icons.state the icons for the current buffer +--- @field alternate barbar.config.options.icons.state the icons used for an alternate buffer +--- @field current barbar.config.options.icons.state the icons for the current buffer --- @field inactive barbar.config.options.icons.state the icons for inactive buffers ---- @field visible? barbar.config.options.icons.state the icons for visible buffers +--- @field visible barbar.config.options.icons.state the icons for visible buffers --- @field scroll barbar.config.options.icons.scroll the scroll arrows local DEFAULT_ICONS = { buffer_index = false, @@ -268,55 +290,39 @@ function config.setup(options) config.options = tbl_deep_extend('keep', options, DEFAULT_OPTIONS, { icons = default_icons }) - -- NOTE: we do this because `vim.tbl_deep_extend` doesn't deep copy lists - for i, default_diagnostic_severity_icons in ipairs(DEFAULT_DIAGNOSTIC_ICONS) do - local diagnostic_severity_icons = config.options.icons.diagnostics[i] or {} - - if diagnostic_severity_icons.enabled == nil then - diagnostic_severity_icons.enabled = default_diagnostic_severity_icons.enabled - end - - if diagnostic_severity_icons.icon == nil then - diagnostic_severity_icons.icon = default_diagnostic_severity_icons.icon + do + local icons = config.options.icons + + --- `config.options.icons` without the recursive structure + --- @type barbar.config.options.icons.buffer + local base_options = { + buffer_index = icons.buffer_index, + buffer_number = icons.buffer_number, + button = icons.button, + diagnostics = icons.diagnostics, + filename = icons.filename, + filetype = icons.filetype, + gitsigns = icons.gitsigns, + separator = icons.separator, + } + + local modified_icons = icons.modified or {} + local pinned_icons = icons.pinned or {} + + -- resolve all of the icons for the activities + for _, activity in ipairs { 'alternate', 'current', 'inactive', 'visible' } do + local activity_icons = tbl_deep_extend('keep', config.options.icons[activity] or {}, base_options) + tbl_deep_extend_diagnostic_icons(activity_icons) + + activity_icons.pinned = tbl_deep_extend('keep', activity_icons.pinned or {}, pinned_icons, activity_icons) + tbl_deep_extend_diagnostic_icons(activity_icons.pinned) + + activity_icons.modified = tbl_deep_extend('keep', activity_icons.modified or {}, modified_icons, activity_icons) + tbl_deep_extend_diagnostic_icons(activity_icons.modified) + + icons[activity] = activity_icons end end - - local icons = config.options.icons - - --- `config.options.icons` without the recursive structure - --- @type barbar.config.options.icons.buffer - local base_options = { - buffer_index = icons.buffer_index, - buffer_number = icons.buffer_number, - filename = icons.filename, - button = icons.button, - diagnostics = icons.diagnostics, - gitsigns = icons.gitsigns, - filetype = icons.filetype, - separator = icons.separator, - } - - local modified_icons = icons.modified or {} - local pinned_icons = icons.pinned or {} - - -- resolve all of the icons for the activities - for _, activity in ipairs { 'alternate', 'current', 'inactive', 'visible' } do - local activity_options = tbl_deep_extend('keep', config.options.icons[activity] or {}, base_options) - config.options.icons[activity] = activity_options - config.options.icons[activity].modified = tbl_deep_extend( - 'keep', - config.options.icons[activity].modified or {}, - modified_icons, - activity_options - ) - - config.options.icons[activity].pinned = tbl_deep_extend( - 'keep', - config.options.icons[activity].pinned or {}, - pinned_icons, - activity_options - ) - end end return config From 43b82bdf60a9071e7e8c90511efa728f6fe3de79 Mon Sep 17 00:00:00 2001 From: Iron-E Date: Mon, 10 Apr 2023 19:53:30 -0400 Subject: [PATCH 3/5] =?UTF-8?q?ref:=20`'left'|'right'`=20=E2=86=92=20`side?= =?UTF-8?q?`=20alias?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We reference distinct sides of the barbar often enough that it probably deserves its own alias. --- lua/barbar/api.lua | 2 +- lua/barbar/config.lua | 4 ++-- lua/barbar/events.lua | 4 ++-- lua/barbar/types.lua | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 lua/barbar/types.lua diff --git a/lua/barbar/api.lua b/lua/barbar/api.lua index 58d9f4e9..6191dda2 100644 --- a/lua/barbar/api.lua +++ b/lua/barbar/api.lua @@ -448,7 +448,7 @@ end --- @param width integer the amount to offset --- @param text? string text to put in the offset --- @param hl? string ---- @param side? 'left'|'right' +--- @param side? side --- @return nil function api.set_offset(width, text, hl, side) if side == nil then diff --git a/lua/barbar/config.lua b/lua/barbar/config.lua index e9d9cc06..23d291fb 100644 --- a/lua/barbar/config.lua +++ b/lua/barbar/config.lua @@ -106,7 +106,7 @@ end --- @field diagnostics barbar.config.options.icons.buffer.diagnostics the diagnostic icons --- @field gitsigns barbar.config.options.icons.buffer.git the git status icons --- @field filetype barbar.config.options.icons.buffer.filetype filetype icon options ---- @field separator barbar.config.options.icons.buffer.separator the left-hand separator between buffers in the tabline +--- @field separator barbar.config.options.icons.buffer.separator the separators between buffers in the tabline --- @class barbar.config.options.icons.state: barbar.config.options.icons.buffer --- @field modified barbar.config.options.icons.buffer the icons used for an modified buffer @@ -195,7 +195,7 @@ local DEPRECATED_OPTIONS = { --- @field clickable boolean --- @field exclude_ft string[] --- @field exclude_name string[] ---- @field focus_on_close 'left'|'right' +--- @field focus_on_close side --- @field hide barbar.config.options.hide --- @field highlight_alternate boolean --- @field highlight_inactive_file_icons boolean diff --git a/lua/barbar/events.lua b/lua/barbar/events.lua index 2c0b6dd6..8af0f055 100644 --- a/lua/barbar/events.lua +++ b/lua/barbar/events.lua @@ -157,7 +157,7 @@ function events.enable() right = {}, --- @type {[string]: nil|integer} } - --- @param side 'left'|'right' + --- @param side side --- @return integer total_width local function total_widths(side) local offset = 0 @@ -175,7 +175,7 @@ function events.enable() create_autocmd('FileType', { callback = function(tbl) local bufwinid --- @type nil|integer - local side --- @type 'left'|'right' + local side --- @type side local autocmd = create_autocmd({'BufWinEnter', 'WinScrolled'}, { callback = function() if bufwinid == nil then diff --git a/lua/barbar/types.lua b/lua/barbar/types.lua new file mode 100644 index 00000000..c3534fcd --- /dev/null +++ b/lua/barbar/types.lua @@ -0,0 +1,3 @@ +--- @meta + +--- @alias side 'left'|'right' From 31b4aa072bbe15cfe2b58c26d787025743b9c1ca Mon Sep 17 00:00:00 2001 From: Iron-E Date: Tue, 11 Apr 2023 17:15:38 -0400 Subject: [PATCH 4/5] perf(render): reuse `current_buffer_index` for scroll locking buffer We already had a `current_buffer_index` variable in `get_bufferline_containers`, so we can use that instead of storing all the activities of each buffer in their containers and looping over the buffers in each container looking for the current buffer, Apologies for the style changes; I was debugging something else, and had a really hard time reading this particular section. While reading through to understand the changes I discovered this performance improvement. --- lua/barbar/ui/render.lua | 95 +++++++++++++++++----------------------- lua/barbar/ui/types.lua | 1 - 2 files changed, 40 insertions(+), 56 deletions(-) diff --git a/lua/barbar/ui/render.lua b/lua/barbar/ui/render.lua index 536b5e15..db7a6624 100644 --- a/lua/barbar/ui/render.lua +++ b/lua/barbar/ui/render.lua @@ -5,6 +5,7 @@ local max = math.max local min = math.min local table_insert = table.insert +local table_remove = table.remove local buf_get_option = vim.api.nvim_buf_get_option --- @type function local buf_is_valid = vim.api.nvim_buf_is_valid --- @type function @@ -347,13 +348,13 @@ end --- @param data barbar.ui.layout.data --- @param bufnrs integer[] --- @param refocus? boolean ---- @return barbar.ui.container[] pinned_groups, barbar.ui.container[] clumps +--- @return barbar.ui.container[] pinend, barbar.ui.container[] unpinned, nil|{[1]: integer, pinned: boolean} current_buffer_index local function get_bufferline_containers(data, bufnrs, refocus) local click_enabled = has('tablineat') and config.options.clickable local accumulated_pinned_width = 0 --- the width of pinned buffers accumulated while iterating local accumulated_unpinned_width = 0 --- the width of buffers accumulated while iterating - local current_buffer_index = nil --- @type nil|integer + local current_buffer_index = nil --- @type nil|{[1]: integer, pinned: boolean} local done = false --- if all of the visible buffers have been clumped local containers = {} --- @type barbar.ui.container[] local pinned_containers = {} --- @type barbar.ui.container[] @@ -379,7 +380,7 @@ local function get_bufferline_containers(data, bufnrs, refocus) local container_width = buffer_data.width or buffer_data.computed_width if activity == buffer.activities.Current and refocus ~= false then - current_buffer_index = i + current_buffer_index = {i, pinned = pinned} local start = accumulated_unpinned_width local end_ = accumulated_unpinned_width + container_width @@ -494,7 +495,6 @@ local function get_bufferline_containers(data, bufnrs, refocus) local padding = { hl = buffer_hl, text = pinned and pinned_pad_text or unpinned_pad_text } local container = { --- @type barbar.ui.container - activity = activity, nodes = { left_separator, padding, buffer_index, buffer_number, icon, jump_letter, name }, --- @diagnostic disable-next-line:assign-type-mismatch it is assigned just earlier position = buffer_data.position or buffer_data.computed_position, @@ -529,7 +529,7 @@ local function get_bufferline_containers(data, bufnrs, refocus) ::continue:: end - return pinned_containers, containers + return pinned_containers, containers, current_buffer_index end local HL = { @@ -545,7 +545,7 @@ local HL = { --- @return nil|string syntax local function generate_tabline(bufnrs, refocus) local data = layout.calculate() - local pinned_containers, containers = get_bufferline_containers(data, bufnrs, refocus) + local pinned, unpinned, current_buffer_index = get_bufferline_containers(data, bufnrs, refocus) -- Create actual tabline string local result = '' @@ -558,12 +558,7 @@ local function generate_tabline(bufnrs, refocus) local content = { { hl = hl, text = state.offset.left.text } } local content_max_width = state.offset.left.width - 2 - offset_nodes = - nodes.insert_many( - offset_nodes, - 1, - nodes.slice_right(content, content_max_width)) - + offset_nodes = nodes.insert_many(offset_nodes, 1, nodes.slice_right(content, content_max_width)) result = result .. nodes.to_string(offset_nodes) end @@ -573,52 +568,41 @@ local function generate_tabline(bufnrs, refocus) local content = { { hl = HL.FILL, text = (' '):rep(data.buffers.width) } } do - local current_container = nil - for _, container in ipairs(containers) do - -- We insert the current buffer after the others so it's always on top - if container.activity ~= buffer.activities.Current then - content = nodes.insert_many( - content, - container.position - scroll.current, - container.nodes) - else - current_container = container - end + local current_container + if current_buffer_index ~= nil and current_buffer_index.pinned == false then + current_container = table_remove(unpinned, current_buffer_index[1]) + end + + for _, container in ipairs(unpinned) do + content = nodes.insert_many(content, container.position - scroll.current, container.nodes) end if current_container ~= nil then - local container = current_container - content = nodes.insert_many( - content, - container.position - scroll.current, - container.nodes) + content = nodes.insert_many(content, current_container.position - scroll.current, current_container.nodes) end end do local inactive_separator = config.options.icons.inactive.separator.left - if inactive_separator ~= nil and #containers > 0 and + if inactive_separator ~= nil and #unpinned > 0 and data.buffers.unpinned_width + strwidth(inactive_separator) <= data.buffers.unpinned_allocated_width then - content = nodes.insert( - content, - data.buffers.used_width, - { text = inactive_separator, hl = HL.SIGN_INACTIVE }) + content = nodes.insert(content, data.buffers.used_width, { text = inactive_separator, hl = HL.SIGN_INACTIVE }) end end - if #pinned_containers > 0 then - local current_container = nil - for _, container in ipairs(pinned_containers) do - if container.activity ~= buffer.activities.Current then - content = nodes.insert_many(content, container.position, container.nodes) - else - current_container = container - end + if #pinned > 0 then + local current_container + if current_buffer_index ~= nil and current_buffer_index.pinned == true then + current_container = table_remove(pinned, current_buffer_index[1]) end + + for _, container in ipairs(pinned) do + content = nodes.insert_many(content, container.position - scroll.current, container.nodes) + end + if current_container ~= nil then - local container = current_container - content = nodes.insert_many(content, container.position, container.nodes) + content = nodes.insert_many(content, current_container.position - scroll.current, current_container.nodes) end end @@ -628,14 +612,18 @@ local function generate_tabline(bufnrs, refocus) local has_left_scroll = scroll.current > 0 if has_left_scroll then - content = nodes.insert(content, data.buffers.pinned_width, - { hl = HL.SCROLL_ARROW, text = config.options.icons.scroll.left }) + content = nodes.insert(content, data.buffers.pinned_width, { + hl = HL.SCROLL_ARROW, + text = config.options.icons.scroll.left, + }) end local has_right_scroll = data.buffers.used_width - scroll.current > data.buffers.width if has_right_scroll then - content = nodes.insert(content, data.buffers.width - 1, - { hl = HL.SCROLL_ARROW, text = config.options.icons.scroll.right }) + content = nodes.insert(content, data.buffers.width - 1, { + hl = HL.SCROLL_ARROW, + text = config.options.icons.scroll.right, + }) end -- Render bufferline string @@ -649,9 +637,10 @@ local function generate_tabline(bufnrs, refocus) -- Tabpages if data.tabpages.width > 0 then - result = result .. nodes.to_string({ - { hl = HL.TABPAGES, text = ' ' .. tabpagenr() .. '/' .. tabpagenr('$') .. ' ' }, - }) + result = result .. nodes.to_string({{ + hl = HL.TABPAGES, + text = ' ' .. tabpagenr() .. '/' .. tabpagenr('$') .. ' ', + }}) end -- Right offset @@ -662,11 +651,7 @@ local function generate_tabline(bufnrs, refocus) local content = { { hl = hl, text = state.offset.right.text } } local content_max_width = state.offset.right.width - 2 - offset_nodes = - nodes.insert_many( - offset_nodes, - 1, - nodes.slice_right(content, content_max_width)) + offset_nodes = nodes.insert_many(offset_nodes, 1, nodes.slice_right(content, content_max_width)) result = result .. nodes.to_string(offset_nodes) end diff --git a/lua/barbar/ui/types.lua b/lua/barbar/ui/types.lua index bf88e3a3..bdaa51d9 100644 --- a/lua/barbar/ui/types.lua +++ b/lua/barbar/ui/types.lua @@ -5,7 +5,6 @@ --- @field text string the content being rendered --- @class barbar.ui.container ---- @field activity barbar.buffer.activity --- @field nodes barbar.ui.node[] --- @field position integer --- @field width integer From c3431aaba8f35887fa40012496297e216a138928 Mon Sep 17 00:00:00 2001 From: Iron-E Date: Fri, 28 Apr 2023 19:29:35 -0400 Subject: [PATCH 5/5] perf(config): generate defaults inside `setup` Removes unecessary `deepcopy` --- lua/barbar/config.lua | 86 +++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/lua/barbar/config.lua b/lua/barbar/config.lua index 23d291fb..9df6cd9d 100644 --- a/lua/barbar/config.lua +++ b/lua/barbar/config.lua @@ -1,4 +1,3 @@ -local deepcopy = vim.deepcopy local table_concat = table.concat local tbl_deep_extend = vim.tbl_deep_extend @@ -8,30 +7,6 @@ local utils = require('barbar.utils') --- The prefix used for `utils.deprecate` local DEPRECATE_PREFIX = '\nThe barbar.nvim option ' ---- @type barbar.config.options -local DEFAULT_OPTIONS = { - animation = true, - auto_hide = false, - clickable = true, - exclude_ft = {}, - exclude_name = {}, - focus_on_close = 'left', - hide = {}, - highlight_alternate = false, - highlight_inactive_file_icons = false, - highlight_visible = true, - insert_at_end = false, - insert_at_start = false, - letters = 'asdfjkl;ghnmxcvbziowerutyqpASDFJKLGHNMXCVBZIOWERUTYQP', - maximum_length = 30, - maximum_padding = 4, - minimum_padding = 1, - no_name_title = nil, - semantic_letters = true, - sidebar_filetypes = {}, - tabpages = true, -} - --- @class barbar.config.options.hide --- @field alternate? boolean --- @field current? boolean @@ -118,24 +93,6 @@ end --- @field inactive barbar.config.options.icons.state the icons for inactive buffers --- @field visible barbar.config.options.icons.state the icons for visible buffers --- @field scroll barbar.config.options.icons.scroll the scroll arrows -local DEFAULT_ICONS = { - buffer_index = false, - buffer_number = false, - button = '', - diagnostics = {}, - gitsigns = { - added = { enabled = false, icon = '+' }, - changed = { enabled = false, icon = '~' }, - deleted = { enabled = false, icon = '-' }, - }, - filename = true, - filetype = { enabled = true }, - inactive = { separator = { left = '▎', right = '' } }, - modified = { button = '●' }, - pinned = { button = false, filename = false }, - separator = { left = '▎', right = '' }, - scroll = { left = '❮', right = '❯' } -} --- @alias barbar.config.options.icons.preset boolean|"both"|"buffer_number_with_icon"|"buffer_numbers"|"numbers" @@ -279,7 +236,24 @@ function config.setup(options) end end - local default_icons = deepcopy(DEFAULT_ICONS) + local default_icons = { + buffer_index = false, + buffer_number = false, + button = '', + diagnostics = {}, + gitsigns = { + added = { enabled = false, icon = '+' }, + changed = { enabled = false, icon = '~' }, + deleted = { enabled = false, icon = '-' }, + }, + filename = true, + filetype = { enabled = true }, + inactive = { separator = { left = '▎', right = '' } }, + modified = { button = '●' }, + pinned = { button = false, filename = false }, + separator = { left = '▎', right = '' }, + scroll = { left = '❮', right = '❯' } + } do local pinned_icons = options.icons and options.icons.pinned @@ -288,7 +262,29 @@ function config.setup(options) end end - config.options = tbl_deep_extend('keep', options, DEFAULT_OPTIONS, { icons = default_icons }) + config.options = tbl_deep_extend('keep', options, { + animation = true, + auto_hide = false, + clickable = true, + exclude_ft = {}, + exclude_name = {}, + focus_on_close = 'left', + hide = {}, + icons = default_icons, + highlight_alternate = false, + highlight_inactive_file_icons = false, + highlight_visible = true, + insert_at_end = false, + insert_at_start = false, + letters = 'asdfjkl;ghnmxcvbziowerutyqpASDFJKLGHNMXCVBZIOWERUTYQP', + maximum_length = 30, + maximum_padding = 4, + minimum_padding = 1, + no_name_title = nil, + semantic_letters = true, + sidebar_filetypes = {}, + tabpages = true, + }) do local icons = config.options.icons