From 332a0b0a90074e8b2eb494f47e2153714c27927b Mon Sep 17 00:00:00 2001 From: DriesOlbrechts <32861260+DriesOlbrechts@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:54:20 +0200 Subject: [PATCH] Allow custom component order and custom functions (#2) * feat: allow custom component order and custom functions * feat: segmented components * feat: handle hidden edge seperators --- lua/slimline/autocommands.lua | 2 +- lua/slimline/components.lua | 67 ++++++++++--------- lua/slimline/default_config.lua | 50 ++++++++++---- lua/slimline/highlights.lua | 6 +- lua/slimline/init.lua | 114 ++++++++++++++++++++++++++------ 5 files changed, 171 insertions(+), 68 deletions(-) diff --git a/lua/slimline/autocommands.lua b/lua/slimline/autocommands.lua index cf89a56..8a4194b 100644 --- a/lua/slimline/autocommands.lua +++ b/lua/slimline/autocommands.lua @@ -19,6 +19,6 @@ vim.api.nvim_create_autocmd('Colorscheme', { callback = function() local hl = require('slimline.highlights') hl.hl_cache = {} - hl.create() + hl.create(vim.g.slimline_config) end, }) diff --git a/lua/slimline/components.lua b/lua/slimline/components.lua index 98ac297..df1ad57 100644 --- a/lua/slimline/components.lua +++ b/lua/slimline/components.lua @@ -1,4 +1,3 @@ -local config = vim.g.slimline_config local highlights = require('slimline.highlights') local M = {} @@ -50,9 +49,9 @@ function M.get_mode() end --- Function to get the highlight of a given mode ---- @param mode string --- @return string -function M.get_mode_hl(mode) +function M.get_mode_hl() + local mode = M.get_mode() if mode == 'NORMAL' then return highlights.hls.mode.normal.text elseif mode:find('PENDING') then @@ -68,26 +67,26 @@ function M.get_mode_hl(mode) end --- Mode component ---- @param mode string +--- @param config table +--- @param sep {left: string, right: string} --- @return string -function M.mode(mode) +function M.mode(config, sep) + local mode = M.get_mode() local render = mode if config.verbose_mode == false then render = string.sub(mode, 1, 1) end local content = ' ' .. render .. ' ' - local sep_left = config.sep.left - if config.sep.hide.first then - sep_left = '' - end - return highlights.highlight_content(content, M.get_mode_hl(mode), sep_left, config.sep.right) + return highlights.highlight_content(content, M.get_mode_hl(), sep.left, sep.right) end --- Git component showing branch --- as well as changed, added and removed lines --- Using gitsigns for it +--- @param config table +--- @param sep {left: string, right: string} --- @return string -function M.git() +function M.git(config, sep) local status = vim.b.gitsigns_status_dict if not status then return '' @@ -102,12 +101,17 @@ function M.git() local modifications = added or removed or changed local branch = string.format(' %s %s ', config.icons.git.branch, status.head) - branch = highlights.highlight_content(branch, highlights.hls.primary.text, config.sep.left) + branch = highlights.highlight_content(branch, highlights.hls.primary.text, sep.left) local branch_hl_right_sep = highlights.hls.primary.sep if modifications then branch_hl_right_sep = highlights.hls.primary.sep_transition end - branch = branch .. highlights.highlight_content(config.sep.right, branch_hl_right_sep) + -- if there are modifications the main part of the git components should have a right side + -- seperator + if modifications then + sep.right = config.sep.right + end + branch = branch .. highlights.highlight_content(sep.right, branch_hl_right_sep) local mods = '' if modifications then @@ -120,17 +124,19 @@ function M.git() if removed then mods = mods .. string.format(' -%s', status.removed) end - mods = highlights.highlight_content(mods .. ' ', highlights.hls.secondary.text, nil, config.sep.right) + mods = highlights.highlight_content(mods .. ' ', highlights.hls.secondary.text, nil, sep.right) end return branch .. mods end --- Path component --- Displays directory and file seperately +--- @param config table +--- @param sep {left: string, right: string} --- @return string -function M.path() +function M.path(config, sep) local file = - highlights.highlight_content(' ' .. vim.fn.expand('%:t') .. ' %m%r', highlights.hls.primary.text, config.sep.left) + highlights.highlight_content(' ' .. vim.fn.expand('%:t') .. ' %m%r', highlights.hls.primary.text, sep.left) file = file .. highlights.highlight_content(config.sep.right, highlights.hls.primary.sep_transition) local path = vim.fs.normalize(vim.fn.expand('%:.:h')) @@ -141,7 +147,7 @@ function M.path() ' ' .. config.icons.folder .. path .. ' ', highlights.hls.secondary.text, nil, - config.sep.right + sep.right ) return file .. path @@ -149,8 +155,10 @@ end local last_diagnostic_component = '' --- Diagnostics component +--- @param config table +--- @param sep {left: string, right: string} --- @return string -function M.diagnostics() +function M.diagnostics(config, sep) -- Lazy uses diagnostic icons, but those aren"t errors per se. if vim.bo.filetype == 'lazy' then return '' @@ -190,14 +198,16 @@ function M.diagnostics() last_diagnostic_component = highlights.highlight_content( ' ' .. last_diagnostic_component .. ' ', highlights.hls.primary.text, - config.sep.left, - config.sep.right + sep.left, + sep.right ) return last_diagnostic_component end --- Filetype and attached LSPs component -function M.filetype_lsp() +--- @param config table +--- @param sep {left: string, right: string} +function M.filetype_lsp(config, sep) local filetype = vim.bo.filetype if filetype == '' then filetype = '[No Name]' @@ -207,8 +217,7 @@ function M.filetype_lsp() if status then icon = ' ' .. MiniIcons.get('filetype', filetype) end - filetype = - highlights.highlight_content(icon .. ' ' .. filetype .. ' ', highlights.hls.primary.text, nil, config.sep.right) + filetype = highlights.highlight_content(icon .. ' ' .. filetype .. ' ', highlights.hls.primary.text, nil, sep.right) local attached_clients = vim.lsp.get_clients { bufnr = 0 } local it = vim.iter(attached_clients) @@ -224,7 +233,7 @@ function M.filetype_lsp() filetype_hl_sep_left = highlights.hls.primary.sep_transition end filetype = highlights.highlight_content(config.sep.left, filetype_hl_sep_left) .. filetype - lsp_clients = highlights.highlight_content(' ' .. lsp_clients .. ' ', highlights.hls.secondary.text, config.sep.left) + lsp_clients = highlights.highlight_content(' ' .. lsp_clients .. ' ', highlights.hls.secondary.text, sep.left) local result = filetype if #attached_clients > 0 then @@ -234,8 +243,10 @@ function M.filetype_lsp() end --- File progress component +--- @param config table +--- @param sep {left: string, right: string} --- @return string -function M.progress(mode) +function M.progress(config, sep) local cur = vim.fn.line('.') local total = vim.fn.line('$') local content @@ -247,11 +258,7 @@ function M.progress(mode) content = string.format('%2d%%%%', math.floor(cur / total * 100)) end content = string.format(' %s %s / %s ', config.icons.lines, content, total) - local sep_right = config.sep.right - if config.sep.hide.last then - sep_right = '' - end - return highlights.highlight_content(content, M.get_mode_hl(mode), config.sep.left, sep_right) + return highlights.highlight_content(content, M.get_mode_hl(), sep.left, sep.right) end return M diff --git a/lua/slimline/default_config.lua b/lua/slimline/default_config.lua index d542ebf..1fdad51 100644 --- a/lua/slimline/default_config.lua +++ b/lua/slimline/default_config.lua @@ -1,21 +1,40 @@ -local M = { - bold = false, - verbose_mode = false, - style = 'bg', - spaces = { +local function create_M() + local M = {} + + M.bold = false + M.verbose_mode = false + M.style = 'bg' + + M.spaces = { components = ' ', left = ' ', right = ' ', - }, - sep = { + } + + M.sep = { hide = { first = false, last = false, }, left = '', right = '', - }, - hl = { + } + + M.components = { + left = { + 'mode', + 'path', + 'git', + }, + center = {}, + right = { + 'diagnostics', + 'filetype_lsp', + 'progress', + }, + } + + M.hl = { modes = { normal = 'Type', insert = 'Function', @@ -26,8 +45,9 @@ local M = { base = 'Comment', primary = 'Normal', secondary = 'Comment', - }, - icons = { + } + + M.icons = { diagnostics = { ERROR = ' ', WARN = ' ', @@ -39,7 +59,11 @@ local M = { }, folder = ' ', lines = ' ', - }, -} + } + + return M +end + +local M = create_M() return M diff --git a/lua/slimline/highlights.lua b/lua/slimline/highlights.lua index 57be83d..17cc2ca 100644 --- a/lua/slimline/highlights.lua +++ b/lua/slimline/highlights.lua @@ -1,5 +1,3 @@ -local config = vim.g.slimline_config - local M = {} M.hls = { @@ -36,8 +34,8 @@ M.hls = { }, }, } - -function M.create() +---@param config table +function M.create(config) M.hls.base = M.get_or_create('', config.hl.base) local as_background = true diff --git a/lua/slimline/init.lua b/lua/slimline/init.lua index 6e50e44..77ee882 100644 --- a/lua/slimline/init.lua +++ b/lua/slimline/init.lua @@ -1,28 +1,94 @@ local M = {} +local components = require('slimline.components') +local components_length = 0 +local position = 0 + +--- Returns correct component from config +---@return function +---@param component_name string | function +---@param config table +local function resolve_component(component_name, config) + position = position + 1 + if type(component_name) == 'function' then + return component_name + elseif type(component_name) == 'string' then + if components[component_name] then + local sep = { + left = config.sep.left, + right = config.sep.right, + } + if config.sep.hide.first and position == 1 then + sep.left = '' + end + if config.sep.hide.last and position == components_length then + sep.right = '' + end + return function(...) + return components[component_name](config, sep, ...) + end + else + return function() + return component_name + end + end + end + return function() + return '' + end +end + +-- Concats resolved components with given spacing +---comment +---@param comps table +---@param spacing string +---@return string +function M.concat_components(comps, spacing) + local result = '' + local first = true + for _, c_fn in ipairs(comps) do + local space = spacing + if first then + space = '' + first = false + end + if type(c_fn) == 'function' then + result = result .. space .. c_fn() + elseif type(c_fn) == 'string' then + result = result .. space .. c_fn + end + end + return result +end --- Renders the statusline. ---@return string function M.render() local config = vim.g.slimline_config - local sep = config.spaces.components - local components = require('slimline.components') - local mode = components.get_mode() - local stl = table.concat { - '%#Slimline#' .. config.spaces.left, - components.mode(mode), - sep, - components.path(), - sep, - components.git(), - '%=', - components.diagnostics(), - sep, - components.filetype_lsp(), - sep, - components.progress(mode), - config.spaces.right, - } - return stl + if not config or not (config.components.left or config.components.right or config.components.center) then + return '' + end + + local result = '%#Slimline#' .. config.spaces.left + result = result .. M.concat_components(config.components.left, config.spaces.components) + result = result .. '%=' + result = result .. M.concat_components(config.components.center, config.spaces.components) + result = result .. '%=' + result = result .. M.concat_components(config.components.right, config.spaces.components) + result = result .. config.spaces.right + + return result +end + +-- Resolving components into a table +---@param comps table +---@param opts table +---@return table +local function resolve_components(comps, opts) + local result = {} + for _, component in ipairs(comps) do + table.insert(result, resolve_component(component, opts)) + end + return result end ---@param opts table @@ -37,9 +103,17 @@ function M.setup(opts) opts.sep.left = '' opts.sep.right = '' end + + components_length = #opts.components.left + #opts.components.center + #opts.components.right + + -- Resolve component references + opts.components.left = resolve_components(opts.components.left, opts) + opts.components.center = resolve_components(opts.components.center, opts) + opts.components.right = resolve_components(opts.components.right, opts) + vim.g.slimline_config = opts local hl = require('slimline.highlights') - hl.create() + hl.create(opts) vim.o.statusline = "%!v:lua.require'slimline'.render()" end