From 90fb8b00ccaee9c370621dee3b7d2a379e89db1d Mon Sep 17 00:00:00 2001 From: Tran Vo Son Tung Date: Sat, 4 Nov 2023 11:45:26 +0700 Subject: [PATCH] feat: big update for highlighting --- README.md | 115 +++++----- lua/sttusline/component.lua | 10 +- lua/sttusline/components/diagnostics.lua | 47 ++-- lua/sttusline/components/encoding.lua | 2 +- lua/sttusline/components/filename.lua | 31 +-- lua/sttusline/components/git-branch.lua | 5 +- lua/sttusline/components/git-diff.lua | 47 ++-- lua/sttusline/components/indent.lua | 2 +- lua/sttusline/components/lsps-formatters.lua | 19 +- lua/sttusline/components/mode.lua | 35 ++- .../components/pos-cursor-progress.lua | 2 +- lua/sttusline/components/pos-cursor.lua | 1 - lua/sttusline/config.lua | 8 +- lua/sttusline/runner.lua | 216 +++++++++++++----- lua/sttusline/utils/init.lua | 124 ++++++---- lua/sttusline/utils/new-component.lua | 56 +++-- 16 files changed, 418 insertions(+), 302 deletions(-) diff --git a/README.md b/README.md index a7e3f9e..abc793c 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,9 @@ please open an issue or pull request. I'm very happy to hear from you. create your own component. I'm very happy to see your component. So if you have any idea to create a new component, please open an issue or pull request. -🛠️ At present, the highlight feature of this plugin is very simple. So I hope you can contribute to this plugin to make it better. +🛠️ At present, I feel that use table to create new component is easy to control +than creating by calling get and set function. So I recommend you to use branch +[table_version](https://github.com/sontungexpt/sttusline/tree/table_version) instead of this branch ## Preview @@ -53,7 +55,10 @@ any idea to create a new component, please open an issue or pull request. event = { "BufEnter" }, config = function(_, opts) require("sttusline").setup { - -- 0 | 1 | 2 | 3 + -- statusline_color = "#000000", + statusline_color = "StatusLine", + + -- | 1 | 2 | 3 -- recommended: 3 laststatus = 3, disabled = { @@ -99,7 +104,6 @@ or copy the template to your component module ```lua -- Change NewComponent to your component name -local utils = require("sttusline.utils") local NewComponent = require("sttusline.set_component").new() -- The component will be update when the event is triggered @@ -125,26 +129,51 @@ NewComponent.set_config {} NewComponent.set_padding(1) -- or NewComponent.set_padding{ left = 1, right = 1 } --- The colors of the component +-- The colors of the component. Rely on the return value of the update function, you have 3 ways to set the colors +-- If the return value is string +-- NewComponent.set_colors { fg = colors.set_black, bg = colors.set_white } +-- If the return value is table of string +-- NewComponent.set_colors { { fg = "#009900", bg = "#ffffff" }, { fg = "#000000", bg = "#ffffff" }} +-- -- so if the return value is { "string1", "string2" } +-- -- then the string1 will be highlight with { fg = "#009900", bg = "#ffffff" } +-- -- and the string2 will be highlight with { fg = "#000000", bg = "#ffffff" } +-- +-- -- if you don't want to add highlight for the string1 now +-- -- because it will auto update new colors when the returning value in update function is a table that contains the color options, +-- -- you can add a empty table in the first element +-- -- { +-- colors = { +-- {}, +-- { fg = "#000000", bg = "#ffffff" } +-- }, +-- -- } +-- +-- NOTE: The colors options can be the colors name or the colors options +-- -- colors = { +-- -- { fg = "#009900", bg = "#ffffff" }, +-- -- "DiagnosticsSignError", +-- -- }, +-- -- So if the return value is { "string1", "string2" } +-- -- then the string1 will be highlight with { fg = "#009900", bg = "#ffffff" } +-- -- and the string2 will be highlight with the colors options of the DiagnosticsSignError highlight +-- -- Or you can set the fg(bg) follow the colors options of the DiagnosticsSignError highlight +-- -- { +-- -- colors = { +-- -- { fg = "DiagnosticsSignError", bg = "#ffffff" }, +-- -- "DiagnosticsSignError", +-- -- }, +-- -- } + NewComponent.set_colors {} -- { fg = colors.set_black, bg = colors.set_white } --- The function will return the value of the component to display on the statusline --- Must return a string +-- The function will return the value of the component to display on the statusline(required). +-- Must return a string or a table of string or a table of { "string", { fg = "color", bg = "color" } } +-- NewComponent.set_update(function() return { "string1", "string2" } end) +-- NewComponent.set_update(function() return { { "string1", {fg = "#000000", bg ="#fdfdfd"} }, "string3", "string4" } end) NewComponent.set_update(function() return "" end) --- NOTE: --- If you don't use NewComponent.set_colors{} and you want to customize the highlight of your component --- You should use utils.add_highlight_name(value, highlight_name) to add the highlight name to the value --- After that you can set the highlight of your component by using vim.api.nvim_set_hl(0, highlight_name, opts) (You should add this to NewComponent.set_onhighlight) --- The function utils.add_highlight_name(value,highlight_name) will return the value --- So you can use it like this: --- NewComponent.set_update(function() return utils.add_highlight_name("Hello", "HelloHighlight") end) --- NewComponent.set_onhighlight(function() vim.api.nvim_set_hl(0, "HelloHighlight", { fg = "#ffffff", bg = "#000000" }) end) -- The function will call when the component is highlight --- You should use it to set the highlight of the component --- Example: --- NewComponent.set_onhighlight(function() vim.api.nvim_set_hl(0, "ComponentHighlight", { fg = colors.set_black, bg = colors.set_white }) end) NewComponent.set_onhighlight(function() end) -- The function will return the condition to display the component when the component is update @@ -221,18 +250,7 @@ We provide you some default component: ```lua require("sttusline").setup { - -- 0 | 1 | 2 | 3 - -- recommended: 3 - laststatus = 3, - disabled = { - filetypes = { - -- "NvimTree", - -- "lazy", - }, - buftypes = { - -- "terminal", - }, - }, + -- ... components = { -- "mode", -- "filename", @@ -370,15 +388,15 @@ Some config I provide to override default component ["x"] = { "CONFIRM", "STTUSLINE_CONFIRM_MODE" }, }, mode_colors = { - ["STTUSLINE_NORMAL_MODE"] = { fg = colors.blue, bg = colors.bg }, - ["STTUSLINE_INSERT_MODE"] = { fg = colors.green, bg = colors.bg }, - ["STTUSLINE_VISUAL_MODE"] = { fg = colors.purple, bg = colors.bg }, - ["STTUSLINE_NTERMINAL_MODE"] = { fg = colors.gray, bg = colors.bg }, - ["STTUSLINE_TERMINAL_MODE"] = { fg = colors.cyan, bg = colors.bg }, - ["STTUSLINE_REPLACE_MODE"] = { fg = colors.red, bg = colors.bg }, - ["STTUSLINE_SELECT_MODE"] = { fg = colors.magenta, bg = colors.bg }, - ["STTUSLINE_COMMAND_MODE"] = { fg = colors.yellow, bg = colors.bg }, - ["STTUSLINE_CONFIRM_MODE"] = { fg = colors.yellow, bg = colors.bg }, + ["STTUSLINE_NORMAL_MODE"] = { fg = colors.blue }, + ["STTUSLINE_INSERT_MODE"] = { fg = colors.green }, + ["STTUSLINE_VISUAL_MODE"] = { fg = colors.purple }, + ["STTUSLINE_NTERMINAL_MODE"] = { fg = colors.gray }, + ["STTUSLINE_TERMINAL_MODE"] = { fg = colors.cyan }, + ["STTUSLINE_REPLACE_MODE"] = { fg = colors.red }, + ["STTUSLINE_SELECT_MODE"] = { fg = colors.magenta }, + ["STTUSLINE_COMMAND_MODE"] = { fg = colors.yellow }, + ["STTUSLINE_CONFIRM_MODE"] = { fg = colors.yellow }, }, }, auto_hide_on_vim_resized = true, @@ -395,12 +413,6 @@ Some config I provide to override default component HINT = "󰌵", WARN = "", }, - diagnostics_color = { - ERROR = "DiagnosticError", - WARN = "DiagnosticWarn", - HINT = "DiagnosticHint", - INFO = "DiagnosticInfo", - }, order = { "ERROR", "WARN", "INFO", "HINT" }, } ``` @@ -425,7 +437,7 @@ encoding.set_config { ```lua local filename = require("sttusline.components.filename") filename.set_config { - color = { fg = colors.orange, bg = colors.bg }, + color = { fg = colors.orange }, } ``` @@ -450,11 +462,6 @@ encoding.set_config { changed = "", removed = "", }, - colors = { - added = "DiagnosticInfo", - changed = "DiagnosticWarn", - removed = "DiagnosticError", - }, order = { "added", "changed", "removed" }, } ``` @@ -464,7 +471,7 @@ encoding.set_config { ```lua local indent = require("sttusline.components.indent") -indent.set_colors { fg = colors.cyan, bg = colors.bg } +indent.set_colors { fg = colors.cyan } ``` - lsps-formatters @@ -472,7 +479,7 @@ indent.set_colors { fg = colors.cyan, bg = colors.bg } ```lua local lsps_formatters = require("sttusline.components.lsps-formatters") -lsps_formatters.set_colors { fg = colors.magenta, bg = colors.bg } +lsps_formatters.set_colors { fg = colors.magenta } ``` - copilot @@ -480,7 +487,7 @@ lsps_formatters.set_colors { fg = colors.magenta, bg = colors.bg } ```lua local copilot = require("sttusline.components.copilot") -copilot.set_colors { fg = colors.yellow, bg = colors.bg } +copilot.set_colors { fg = colors.yellow } copilot.set_config { icons = { normal = "", @@ -502,7 +509,7 @@ pos_cursor.set_colors { fg = colors.fg } ```lua local pos_cursor_progress = require("sttusline.components.pos-cursor-progress") -pos_cursor_rogress.set_colors { fg = colors.orange, bg = colors.bg } +pos_cursor_rogress.set_colors { fg = colors.orange } ``` ## Contributing diff --git a/lua/sttusline/component.lua b/lua/sttusline/component.lua index 75b4a63..7b7593c 100644 --- a/lua/sttusline/component.lua +++ b/lua/sttusline/component.lua @@ -79,11 +79,11 @@ function Component.new() get_colors = function() return component_data.colors end, set_colors = function(colors) - if type(colors) == "table" then - component_data.colors = { - fg = type(colors.fg) == "string" and colors.fg or nil, - bg = type(colors.bg) == "string" and colors.bg or nil, - } + if + require("sttusline.utils").is_highlight_option(colors) + or require("sttusline.utils").is_highlight_name(colors) + then + component_data.colors = colors end end, diff --git a/lua/sttusline/components/diagnostics.lua b/lua/sttusline/components/diagnostics.lua index 097b0cf..e48e447 100644 --- a/lua/sttusline/components/diagnostics.lua +++ b/lua/sttusline/components/diagnostics.lua @@ -1,10 +1,13 @@ -local HIGHLIGHT_PREFIX = "STTUSLINE_DIAGNOSTICS_" - local colors = require("sttusline.utils.color") -local utils = require("sttusline.utils") local diag = vim.diagnostic local Diagnostics = require("sttusline.component").new() +Diagnostics.set_colors { + { fg = colors.tokyo_diagnostics_error }, + { fg = colors.tokyo_diagnostics_warn }, + { fg = colors.tokyo_diagnostics_hint }, + { fg = colors.tokyo_diagnostics_info }, +} Diagnostics.set_config { icons = { @@ -13,12 +16,6 @@ Diagnostics.set_config { HINT = "󰌵", WARN = "", }, - diagnostics_color = { - ERROR = { fg = colors.tokyo_diagnostics_error, bg = colors.bg }, - WARN = { fg = colors.tokyo_diagnostics_warn, bg = colors.bg }, - HINT = { fg = colors.tokyo_diagnostics_hint, bg = colors.bg }, - INFO = { fg = colors.tokyo_diagnostics_info, bg = colors.bg }, - }, order = { "ERROR", "WARN", "INFO", "HINT" }, } @@ -38,39 +35,25 @@ Diagnostics.set_update(function() local config = Diagnostics.get_config() local icons = config.icons - local diagnostics_color = config.diagnostics_color local order = config.order + local should_add_spacing = false for _, key in ipairs(order) do local count = #diag.get(0, { severity = diag.severity[key] }) if count > 0 then - local color = diagnostics_color[key] - if color then - if utils.is_color(color) or type(color) == "table" then - table.insert( - result, - utils.add_highlight_name(icons[key] .. " " .. count, HIGHLIGHT_PREFIX .. key) - ) - else - table.insert(result, utils.add_highlight_name(icons[key] .. " " .. count, color)) - end + if should_add_spacing then + table.insert(result, " " .. icons[key] .. " " .. count) + else + should_add_spacing = true + table.insert(result, icons[key] .. " " .. count) end + else + table.insert(result, "") end end - return #result > 0 and table.concat(result, " ") or "" -end) - -Diagnostics.set_onhighlight(function() - local diagnostics_color = Diagnostics.get_config().diagnostics_color - for key, color in pairs(diagnostics_color) do - if utils.is_color(color) then - vim.api.nvim_set_hl(0, HIGHLIGHT_PREFIX .. key, { fg = color, bg = colors.bg }) - elseif type(color) == "table" then - vim.api.nvim_set_hl(0, HIGHLIGHT_PREFIX .. key, color) - end - end + return result end) return Diagnostics diff --git a/lua/sttusline/components/encoding.lua b/lua/sttusline/components/encoding.lua index 9c9be09..1d5ca0c 100644 --- a/lua/sttusline/components/encoding.lua +++ b/lua/sttusline/components/encoding.lua @@ -1,7 +1,7 @@ local colors = require("sttusline.utils.color") local Encoding = require("sttusline.component").new() -Encoding.set_colors { fg = colors.yellow, bg = colors.bg } +Encoding.set_colors { fg = colors.yellow } Encoding.set_config { ["utf-8"] = "󰉿", diff --git a/lua/sttusline/components/filename.lua b/lua/sttusline/components/filename.lua index 5637cac..1fdbca7 100644 --- a/lua/sttusline/components/filename.lua +++ b/lua/sttusline/components/filename.lua @@ -1,20 +1,14 @@ -local FILENAME_HIGHLIGHT = "STTUSLINE_FILE_NAME" -local ICON_HIGHLIGHT = "STTUSLINE_FILE_ICON" - local fn = vim.fn local get_option = vim.api.nvim_buf_get_option -local hl = vim.api.nvim_set_hl - local colors = require("sttusline.utils.color") -local utils = require("sttusline.utils") local Filename = require("sttusline.component").new() -Filename.set_config { - color = { fg = colors.orange, bg = colors.bg }, -} - Filename.set_event { "BufEnter", "WinEnter" } +Filename.set_colors { + {}, + { fg = colors.orange }, +} Filename.set_update(function() local has_devicons, devicons = pcall(require, "nvim-web-devicons") @@ -44,22 +38,17 @@ Filename.set_update(function() elseif filetype == "lazy" then icon, color_icon = "󰏔", colors.red filename = "Lazy" + elseif filetype == "checkhealth" then + icon, color_icon = "", colors.red + filename = "CheckHealth" + elseif filetype == "plantuml" then + icon, color_icon = "", colors.green elseif filetype == "dashboard" then icon, color_icon = "", colors.red end end - hl(0, ICON_HIGHLIGHT, { fg = color_icon, bg = colors.bg }) - - if icon then - return utils.add_highlight_name(icon, ICON_HIGHLIGHT) - .. " " - .. utils.add_highlight_name(filename, FILENAME_HIGHLIGHT) - else - return utils.add_highlight_name(filename, FILENAME_HIGHLIGHT) - end + return { icon and { icon, { fg = color_icon } } or "", " " .. filename } end) -Filename.set_onhighlight(function() hl(0, FILENAME_HIGHLIGHT, Filename.get_config().color) end) - return Filename diff --git a/lua/sttusline/components/git-branch.lua b/lua/sttusline/components/git-branch.lua index 8fcf582..212db11 100644 --- a/lua/sttusline/components/git-branch.lua +++ b/lua/sttusline/components/git-branch.lua @@ -2,7 +2,7 @@ local colors = require("sttusline.utils.color") local GitBranch = require("sttusline.component").new() -GitBranch.set_colors { fg = colors.pink, bg = colors.bg } +GitBranch.set_colors { fg = colors.pink } GitBranch.set_config { icon = "", } @@ -16,7 +16,8 @@ local get_branch = function() if head_file then local content = head_file:read("*all") head_file:close() - return content:match("ref: refs/heads/(.-)%s*$") + -- branch name or commit hash + return content:match("ref: refs/heads/(.-)%s*$") or content:sub(1, 7) or "" end return "" end diff --git a/lua/sttusline/components/git-diff.lua b/lua/sttusline/components/git-diff.lua index c782f71..4678e13 100644 --- a/lua/sttusline/components/git-diff.lua +++ b/lua/sttusline/components/git-diff.lua @@ -1,12 +1,14 @@ local colors = require("sttusline.utils.color") -local utils = require("sttusline.utils") - -local ADD_HIGHLIGHT_PREFIX = "STTUSLINE_GIT_DIFF_" local GitDiff = require("sttusline.component").new() GitDiff.set_event { "BufWritePost", "VimResized", "BufEnter" } GitDiff.set_user_event { "GitSignsUpdate" } +GitDiff.set_colors { + { fg = colors.tokyo_diagnostics_hint }, + { fg = colors.tokyo_diagnostics_info }, + { fg = colors.tokyo_diagnostics_error }, +} GitDiff.set_config { icons = { @@ -14,11 +16,6 @@ GitDiff.set_config { changed = "", removed = "", }, - colors = { - added = { fg = colors.tokyo_diagnostics_hint, bg = colors.bg }, - changed = { fg = colors.tokyo_diagnostics_hint, bg = colors.bg }, - removed = { fg = colors.tokyo_diagnostics_error, bg = colors.bg }, - }, order = { "added", "changed", "removed" }, } @@ -28,40 +25,24 @@ GitDiff.set_update(function() local config = GitDiff.get_config() local order = config.order local icons = config.icons - local diff_colors = config.colors + local should_add_spacing = false local result = {} for _, v in ipairs(order) do if git_status[v] and git_status[v] > 0 then - local color = diff_colors[v] - - if color then - if utils.is_color(color) or type(color) == "table" then - table.insert( - result, - utils.add_highlight_name(icons[v] .. " " .. git_status[v], ADD_HIGHLIGHT_PREFIX .. v) - ) - else - table.insert(result, utils.add_highlight_name(icons[v] .. " " .. git_status[v], color)) - end + if should_add_spacing then + table.insert(result, " " .. icons[v] .. " " .. git_status[v]) + else + should_add_spacing = true + table.insert(result, icons[v] .. " " .. git_status[v]) end + else + table.insert(result, "") end end - - return #result > 0 and table.concat(result, " ") or "" + return result end) GitDiff.set_condition(function() return vim.b.gitsigns_status_dict ~= nil and vim.o.columns > 70 end) -GitDiff.set_onhighlight(function() - local conf_colors = GitDiff.get_config().colors - for key, color in pairs(conf_colors) do - if utils.is_color(color) then - vim.api.nvim_set_hl(0, ADD_HIGHLIGHT_PREFIX .. key, { fg = color, bg = colors.bg }) - elseif type(color) == "table" then - vim.api.nvim_set_hl(0, ADD_HIGHLIGHT_PREFIX .. key, color) - end - end -end) - return GitDiff diff --git a/lua/sttusline/components/indent.lua b/lua/sttusline/components/indent.lua index 69878ae..e63b7b5 100644 --- a/lua/sttusline/components/indent.lua +++ b/lua/sttusline/components/indent.lua @@ -1,7 +1,7 @@ local colors = require("sttusline.utils.color") local Indent = require("sttusline.component").new() -Indent.set_colors { fg = colors.cyan, bg = colors.bg } +Indent.set_colors { fg = colors.cyan } Indent.set_event("BufEnter") diff --git a/lua/sttusline/components/lsps-formatters.lua b/lua/sttusline/components/lsps-formatters.lua index 52b6f74..021ccbc 100644 --- a/lua/sttusline/components/lsps-formatters.lua +++ b/lua/sttusline/components/lsps-formatters.lua @@ -2,26 +2,28 @@ local colors = require("sttusline.utils.color") local Lsps_Formatters = require("sttusline.component"):new() -Lsps_Formatters.set_colors { fg = colors.magenta, bg = colors.bg } +Lsps_Formatters.set_colors { fg = colors.magenta } Lsps_Formatters.set_event { "LspAttach", "LspDetach", "BufWritePost", "BufEnter", "VimResized" } -- for null-ls and conform Lsps_Formatters.set_user_event {} -- disable user events Lsps_Formatters.set_condition(function() return vim.o.columns > 70 end) Lsps_Formatters.set_update(function() local buf_clients = vim.lsp.buf_get_clients() - if not buf_clients or #buf_clients == 0 then return "NO LSP  " end - local server_names = {} + local has_null_ls = false + local ignore_lsp_servers = { + ["null-ls"] = true, + ["copilot"] = true, + } for _, client in pairs(buf_clients) do local client_name = client.name - if client_name ~= "null-ls" and client_name ~= "copilot" then - table.insert(server_names, client_name) - end + if not ignore_lsp_servers[client_name] then table.insert(server_names, client_name) end end if package.loaded["null-ls"] then - local has_null_ls, null_ls = pcall(require, "null-ls") + local null_ls = nil + has_null_ls, null_ls = pcall(require, "null-ls") if has_null_ls then local buf_ft = vim.api.nvim_buf_get_option(0, "filetype") @@ -76,10 +78,11 @@ Lsps_Formatters.set_update(function() server_names, vim.tbl_map(function(formatter) return formatter.name end, conform.list_formatters(0)) ) + if has_null_ls then server_names = vim.fn.uniq(server_names) end end end - return table.concat(vim.fn.uniq(server_names), ", ") + return #server_names > 0 and table.concat(server_names, ", ") or "NO LSP, FORMATTER  " end) return Lsps_Formatters diff --git a/lua/sttusline/components/mode.lua b/lua/sttusline/components/mode.lua index e3ee615..3c56432 100644 --- a/lua/sttusline/components/mode.lua +++ b/lua/sttusline/components/mode.lua @@ -1,4 +1,3 @@ -local utils = require("sttusline.utils") local colors = require("sttusline.utils.color") local Mode = require("sttusline.component").new() @@ -51,15 +50,15 @@ Mode.set_config { ["x"] = { "CONFIRM", "STTUSLINE_CONFIRM_MODE" }, }, mode_colors = { - ["STTUSLINE_NORMAL_MODE"] = { fg = colors.blue, bg = colors.bg }, - ["STTUSLINE_INSERT_MODE"] = { fg = colors.green, bg = colors.bg }, - ["STTUSLINE_VISUAL_MODE"] = { fg = colors.purple, bg = colors.bg }, - ["STTUSLINE_NTERMINAL_MODE"] = { fg = colors.gray, bg = colors.bg }, - ["STTUSLINE_TERMINAL_MODE"] = { fg = colors.cyan, bg = colors.bg }, - ["STTUSLINE_REPLACE_MODE"] = { fg = colors.red, bg = colors.bg }, - ["STTUSLINE_SELECT_MODE"] = { fg = colors.magenta, bg = colors.bg }, - ["STTUSLINE_COMMAND_MODE"] = { fg = colors.yellow, bg = colors.bg }, - ["STTUSLINE_CONFIRM_MODE"] = { fg = colors.yellow, bg = colors.bg }, + ["STTUSLINE_NORMAL_MODE"] = { fg = colors.blue }, + ["STTUSLINE_INSERT_MODE"] = { fg = colors.green }, + ["STTUSLINE_VISUAL_MODE"] = { fg = colors.purple }, + ["STTUSLINE_NTERMINAL_MODE"] = { fg = colors.gray }, + ["STTUSLINE_TERMINAL_MODE"] = { fg = colors.cyan }, + ["STTUSLINE_REPLACE_MODE"] = { fg = colors.red }, + ["STTUSLINE_SELECT_MODE"] = { fg = colors.magenta }, + ["STTUSLINE_COMMAND_MODE"] = { fg = colors.yellow }, + ["STTUSLINE_CONFIRM_MODE"] = { fg = colors.yellow }, }, auto_hide_on_vim_resized = true, } @@ -69,24 +68,16 @@ Mode.set_padding(0) Mode.set_condition(function() if Mode.get_config().auto_hide_on_vim_resized then - if vim.o.columns > 70 then - vim.opt.showmode = false - return true - else - vim.opt.showmode = true - return false - end + vim.opt.showmode = not (vim.o.columns > 70) + ---@diagnostic disable-next-line: undefined-field + return not vim.opt.showmode:get() end end) Mode.set_update(function() local mode_code = vim.api.nvim_get_mode().mode local mode = Mode.get_config().modes[mode_code] - if mode then - local hl_name = mode[2] - vim.api.nvim_set_hl(0, hl_name, Mode.get_config().mode_colors[hl_name]) - return utils.add_highlight_name(" " .. mode[1] .. " ", hl_name) - end + if mode then return { { mode[1], Mode.get_config().mode_colors[mode[2]] } } end return " " .. mode_code .. " " end) diff --git a/lua/sttusline/components/pos-cursor-progress.lua b/lua/sttusline/components/pos-cursor-progress.lua index 6476f06..df4c919 100644 --- a/lua/sttusline/components/pos-cursor-progress.lua +++ b/lua/sttusline/components/pos-cursor-progress.lua @@ -6,7 +6,7 @@ local PosCursorProgress = require("sttusline.component").new() PosCursorProgress.set_event { "CursorMoved", "CursorMovedI" } PosCursorProgress.set_padding(0) -PosCursorProgress.set_colors { fg = colors.orange, bg = colors.bg } +PosCursorProgress.set_colors { fg = colors.orange } local chars = { "_", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" } diff --git a/lua/sttusline/components/pos-cursor.lua b/lua/sttusline/components/pos-cursor.lua index ecc8fcd..535d5d3 100644 --- a/lua/sttusline/components/pos-cursor.lua +++ b/lua/sttusline/components/pos-cursor.lua @@ -8,7 +8,6 @@ PosCursor.set_colors { fg = colors.fg } PosCursor.set_update(function() local pos = vim.api.nvim_win_get_cursor(0) - return pos[1] .. ":" .. pos[2] end) diff --git a/lua/sttusline/config.lua b/lua/sttusline/config.lua index 871f2e4..0f03676 100644 --- a/lua/sttusline/config.lua +++ b/lua/sttusline/config.lua @@ -4,6 +4,7 @@ local M = {} local configs = { -- 0 | 1 | 2 | 3 -- recommended: 3 + statusline_color = "StatusLine", laststatus = 3, disabled = { filetypes = { @@ -37,7 +38,12 @@ M.setup = function(user_opts) return user_opts end -M.init_config = function(opts) vim.opt.laststatus = opts.laststatus end +M.init_config = function(opts) + vim.opt.laststatus = opts.laststatus + if opts.statusline_color then + require("sttusline.utils").set_hl("StatusLine", { bg = opts.statusline_color }) + end +end --- Format opts.components to be a table of strings and tables --- @tparam table opts : user opts diff --git a/lua/sttusline/runner.lua b/lua/sttusline/runner.lua index 32674fe..7de2f98 100644 --- a/lua/sttusline/runner.lua +++ b/lua/sttusline/runner.lua @@ -8,10 +8,14 @@ local augroup = api.nvim_create_augroup local HIGHLIGHT_COMPONENT_PREFIX = "STTUSLINE_COMPONENT_" local AUTOCMD_GROUP_COMPONENT = "STTUSLINE_COMPONENT_EVENTS" local AUTOCMD_GROUP_CORE = "STTUSLINE_DISABLE" +local COMPONENT_PARENT_MODULE = "sttusline.components" -- module local utils = require("sttusline.utils") -local notify = require("sttusline.utils.notify") +local is_highlight_option = utils.is_highlight_option +local is_highlight_name = utils.is_highlight_name +local set_hl = utils.set_hl + local component_autocmd_group = nil local core_autocmd_group = nil @@ -51,6 +55,26 @@ M.update_statusline = function() opt.statusline = str_statusline end +M.foreach_component = function(opts, callback, empty_comp_callback) + for index, component in ipairs(opts.components) do + if type(component) == "string" then + if component == "%=" then + if type(empty_comp_callback) == "function" then empty_comp_callback(component, index) end + else + local status_ok, real_comp = pcall(require, COMPONENT_PARENT_MODULE .. "." .. component) + if status_ok then + opts.components[index] = real_comp + callback(real_comp, index) + else + require("sttusline.utils.notify").error("Failed to load component: " .. component) + end + end + else + callback(component, index) + end + end +end + M.get_component_autocmd_group = function() if component_autocmd_group == nil then component_autocmd_group = augroup(AUTOCMD_GROUP_COMPONENT, { clear = true }) @@ -109,8 +133,8 @@ end M.restore_statusline = function(opts) if statusline_hidden then statusline_hidden = false - M.reinit_event() - M.start_timer() + M.reinit_event(opts) + M.start_timer(opts) M.update_all_components(opts) M.update_statusline() end @@ -121,61 +145,57 @@ M.remove_event = function() component_autocmd_group = nil end -M.reinit_event = function() - for event, _ in pairs(event_components.default) do - M.create_default_autocmd(event) - end - for event, _ in pairs(event_components.user) do - M.create_user_autocmd(event) - end +M.reinit_event = function(opts) + M.create_default_autocmd(opts, vim.tbl_keys(event_components.default)) + M.create_user_autocmd(opts, vim.tbl_keys(event_components.user)) end - --- Init timer, autocmds, and highlight for statusline M.init = function(opts) - utils.foreach_component(opts, function(component, index) + M.foreach_component(opts, function(component, index) if component.get_lazy() == false then - statusline[index] = M.update_component_value(component, index) + M.update_component_value(opts, component, index) else statusline[index] = "" end component.load() - M.init_component_autocmds(component, index) - M.init_timer(component, index) - M.set_component_highlight(component, index) + M.init_component_autocmds(opts, component, index) + M.cache_timming_component(component, index) + M.set_component_highlight(opts, component, index) end, function(empty_zone_comp, index) statusline[index] = empty_zone_comp end) + M.init_component_timer(opts) end -M.init_component_autocmds = function(component, index) - M.create_autocmd(component.get_event(), component, index) - M.create_autocmd(component.get_user_event(), component, index, true) +M.init_component_autocmds = function(opts, component, index) + M.create_autocmd(opts, component.get_event(), component, index) + M.create_autocmd(opts, component.get_user_event(), component, index, true) end -M.create_default_autocmd = function(event) +M.create_default_autocmd = function(opts, event) autocmd(event, { pattern = "*", group = M.get_component_autocmd_group(), - callback = function(e) M.run(e.event) end, + callback = function(e) M.run(opts, e.event) end, }) end -M.create_user_autocmd = function(event) +M.create_user_autocmd = function(opts, event) autocmd("User", { pattern = event, group = M.get_component_autocmd_group(), - callback = function(e) M.run(e.match, true) end, + callback = function(e) M.run(opts, e.match, true) end, }) end -M.create_autocmd = function(events, component, index, is_user_event) +M.create_autocmd = function(opts, events, component, index, is_user_event) local key = is_user_event and "user" or "default" for _, event in ipairs(events) do if event_components[key][event] == nil then event_components[key][event] = { { component, index } } if is_user_event then - M.create_user_autocmd(event) + M.create_user_autocmd(opts, event) else - M.create_default_autocmd(event) + M.create_default_autocmd(opts, event) end else local next_index = #event_components[key][event] + 1 @@ -184,69 +204,149 @@ M.create_autocmd = function(events, component, index, is_user_event) end end -M.stop_timer = function() - if timer then timer:stop() end +M.start_timer = function(opts) + if timer == nil then timer = vim.loop.new_timer() end + timer:start( + 1000, + 1000, + vim.schedule_wrap(function() + if not statusline_hidden then M.run(opts) end + end) + ) end -M.start_timer = function() - if timer == nil then timer = vim.loop.new_timer() end - timer:start(1000, 1000, vim.schedule_wrap(M.run)) +M.cache_timming_component = function(component, index) + if component.get_timing() then table.insert(timer_components, { component, index }) end end -M.init_timer = function(component, index) - if component.get_timing() then - timer_components[#timer_components + 1] = { component, index } - if timer == nil then M.start_timer() end - end +M.init_component_timer = function(opts) + if #timer_components > 0 then M.start_timer(opts) end end -M.set_component_highlight = function(component, index) - if next(component.get_colors()) then - api.nvim_set_hl(0, HIGHLIGHT_COMPONENT_PREFIX .. index, component.get_colors()) - end +M.stop_timer = function() + if timer then timer:stop() end +end +M.set_component_highlight = function(opts, component, index) + local colors = component.get_colors() + if type(colors) == "table" then + local is_list = false + for k, color in ipairs(colors) do + is_list = true + set_hl(HIGHLIGHT_COMPONENT_PREFIX .. index .. "_" .. k, color, opts.statusline_color) + end + if not is_list then set_hl(HIGHLIGHT_COMPONENT_PREFIX .. index, colors, opts.statusline_color) end + end component.get_onhighlight()() end -M.set_all_component_highlight = function(opts) utils.foreach_component(opts, M.set_component_highlight) end +M.set_all_component_highlight = function(opts) + M.foreach_component( + opts, + function(component, index) M.set_component_highlight(opts, component, index) end + ) +end -M.update_component_value = function(component, index) +M.update_component_value = function(opts, component, index) local should_display = component.get_condition()() if type(should_display) == "boolean" and not should_display then statusline[index] = "" return end - local value = component.get_update()() - if type(value) == "string" then - value = utils.add_padding(value, component.get_padding()) - if next(component.get_colors()) then - statusline[index] = utils.add_highlight_name(value, HIGHLIGHT_COMPONENT_PREFIX .. index) + local updating_value = component.get_update()() + -- updating_value must be string or table + -- if updating_value is table, then it must be a list of string or list of + -- two elements table, the first element is string, the second element is the + -- colors option of component + -- example: + -- { "filetype_icon", "filename" } + -- { {"filetype_icon", { fg="", bg="" }}, "filename" } + -- { {"filetype_icon"} } + + local colors = component.get_colors() + if type(updating_value) == "string" then + updating_value = utils.add_padding(updating_value, component.get_padding()) + if is_highlight_option(colors) then + -- if assign colors to component, then add highlight name to component + statusline[index] = utils.add_highlight_name(updating_value, HIGHLIGHT_COMPONENT_PREFIX .. index) + elseif is_highlight_name(colors) then + -- if assign the highlight name to component, then add that highlight name to component + statusline[index] = utils.add_highlight_name(updating_value, colors) else - statusline[index] = value + -- if not assign colors to component, then not need to add highlight name + statusline[index] = updating_value end + elseif type(updating_value) == "table" then + updating_value = utils.add_padding(updating_value, component.get_padding()) + for k, v in ipairs(updating_value) do + if type(v) == "string" then + -- "filename" + if type(colors) == "table" then + if is_highlight_option(colors[k]) then + updating_value[k] = + utils.add_highlight_name(v, HIGHLIGHT_COMPONENT_PREFIX .. index .. "_" .. k) + elseif is_highlight_name(colors[k]) then + updating_value[k] = utils.add_highlight_name(v, colors[k]) + end + else + updating_value[k] = v + end + elseif type(v) == "table" and type(v[1]) == "string" then + if is_highlight_option(v[2]) then + -- { "filename", { fg="", bg="" }} + colors[k] = v[2] + component.set_colors(colors) + updating_value[k] = + utils.add_highlight_name(v[1], HIGHLIGHT_COMPONENT_PREFIX .. index .. "_" .. k) + set_hl(HIGHLIGHT_COMPONENT_PREFIX .. index .. "_" .. k, v[2], opts.statusline_color) + component.get_onhighlight()() + elseif is_highlight_name(v[2]) then + -- { "filename", "HIGHLIGHT_NAME" } + updating_value[k] = utils.add_highlight_name(v[1], v[2]) + else + -- {"filename"} + updating_value[k] = v[1] + end + else + statusline[index] = "" + require("sttusline.utils.notify").error( + "opts.component[" + .. index + .. "].update() must return string or table of string or table of {string, table}" + ) + return + end + end + statusline[index] = table.concat(updating_value, "") else statusline[index] = "" - notify.error("opts.component[" .. index .. "].update() must return string") + require("sttusline.utils.notify").error( + "opts.component[" + .. index + .. "].update() must return string or table of string or table of {string, table}" + ) end end -M.update_all_components = function(opts) utils.foreach_component(opts, M.update_component_value) end +M.update_all_components = function(opts) + M.foreach_component( + opts, + function(component, index) M.update_component_value(opts, component, index) end + ) +end -M.update_on_trigger = function(table) +M.update_on_trigger = function(opts, table) for _, values in ipairs(table) do - M.update_component_value(values[1], values[2]) + M.update_component_value(opts, values[1], values[2]) end end -M.run = function(event_name, is_user_event) +M.run = function(opts, event_name, is_user_event) local event_table = is_user_event and event_components.user or event_components.default vim.schedule(function() - vim.schedule(function() - M.update_on_trigger(event_name and event_table[event_name] or timer_components) - if not statusline_hidden then M.update_statusline() end - end, 0) - M.update_statusline() + M.update_on_trigger(opts, event_name and event_table[event_name] or timer_components) + if not statusline_hidden then M.update_statusline() end end, 0) end diff --git a/lua/sttusline/utils/init.lua b/lua/sttusline/utils/init.lua index 2664363..6ea1e42 100644 --- a/lua/sttusline/utils/init.lua +++ b/lua/sttusline/utils/init.lua @@ -1,77 +1,107 @@ +local api = vim.api local Component = require("sttusline.component") -local COMPONENT_PARENT_MODULE = "sttusline.components" - local M = {} M.add_padding = function(str, value) if #str == 0 then return str end + value = value or 1 if type(value) == "number" then - if value <= 0 then return str end - local padding = (" "):rep(value) + if value < 1 then return str end + local padding = (" "):rep(math.floor(value)) + + if type(str) == "string" then + return padding .. str .. padding + else -- table + local first_element = str[1] + local last_element = str[#str] - local startpos = str:find([[#([^#%%]+)%%%*]]) - if not startpos then return padding .. str .. padding end + if type(first_element) == "string" then + str[1] = padding .. first_element + elseif type(first_element) == "table" and type(first_element[1]) == "string" then + first_element[1] = padding .. first_element[1] + end - return str:sub(1, startpos) .. padding .. str:sub(startpos + 1, #str - 2) .. padding .. "%*" + if type(last_element) == "string" then + str[#str] = last_element .. padding + elseif type(last_element) == "table" and type(last_element[1]) == "string" then + last_element[1] = last_element[1] .. padding + end + return str + end + return str elseif type(value) == "table" then - local left_padding = type(value.left) == "number" and value.left >= 0 and (" "):rep(value.left) + local left_padding = type(value.left) == "number" + and value.left >= 0 + and (" "):rep(math.floor(value.left)) or " " - local right_padding = type(value.right) == "number" and value.left >= 0 and (" "):rep(value.right) + local right_padding = type(value.right) == "number" + and value.right >= 0 + and (" "):rep(math.floor(value.right)) or " " - local startpos = str:find([[#([^#%%]+)%%%*]]) - if not startpos then return left_padding .. str .. right_padding end - return str:sub(1, startpos) - .. left_padding - .. str:sub(startpos + 1, #str - 2) - .. right_padding - .. "%*" - end -end + if type(str) == "string" then + return left_padding .. str .. right_padding + elseif type(str) == "table" then + local first_element = str[1] + local last_element = str[#str] -M.add_highlight_name = function(str, highlight_name) - vim.validate { str = { str, "string" }, highlight_name = { highlight_name, "string" } } - return "%#" .. highlight_name .. "#" .. str .. "%*" -end + if type(first_element) == "string" then + str[1] = left_padding .. first_element + elseif type(first_element) == "table" and type(first_element[1]) == "string" then + first_element[1] = left_padding .. first_element[1] + end -M.foreach_component = function(opts, callback, empty_zone_component_callback) - for index, component in ipairs(opts.components) do - if type(component) == "string" then - if component == "%=" then - if type(empty_zone_component_callback) == "function" then - empty_zone_component_callback(component, index) - end - else - local status_ok, real_comp = pcall(require, COMPONENT_PARENT_MODULE .. "." .. component) - if status_ok then - opts.components[index] = real_comp - callback(real_comp, index) - else - require("sttusline.utils.notify").error("Failed to load component: " .. component) - end + if type(last_element) == "string" then + str[#str] = last_element .. right_padding + elseif type(last_element) == "table" and type(last_element[1]) == "string" then + last_element[1] = last_element[1] .. right_padding end - else - callback(component, index) + + return str end + return str end end +M.add_highlight_name = function(str, highlight_name) + return #str > 0 and "%#" .. highlight_name .. "#" .. str .. "%*" or "" +end + M.is_color = function(color) return type(color) == "string" and color:match("^#%x%x%x%x%x%x$") end M.is_component = function(obj) return type(obj) == "table" and getmetatable(obj) == Component end M.is_disabled = function(opts) - local filetype = vim.api.nvim_buf_get_option(0, "filetype") - local buftype = vim.api.nvim_buf_get_option(0, "buftype") - if - vim.tbl_contains(opts.disabled.filetypes or {}, filetype) - or vim.tbl_contains(opts.disabled.buftypes or {}, buftype) - then - return true + return vim.tbl_contains(opts.disabled.filetypes or {}, api.nvim_buf_get_option(0, "filetype")) + or vim.tbl_contains(opts.disabled.buftypes or {}, api.nvim_buf_get_option(0, "buftype")) +end + +M.get_hl_name_color = function(hl_name) + local ok, colors = pcall(api.nvim_get_hl_by_name, hl_name, true) + return ok and colors or {} +end + +M.set_hl = function(group, opts, global_background) + if M.is_highlight_option(opts) then + if opts.fg and not M.is_color(opts.fg) then opts.fg = M.get_hl_name_color(opts.fg).foreground end + + if opts.bg then + opts.bg = M.is_color(opts.bg) and opts.bg or M.get_hl_name_color(opts.bg).background + elseif global_background then + opts.bg = M.is_color(global_background) and global_background + or M.get_hl_name_color(global_background).background + else + -- fallback to StatusLine background + opts.bg = M.get_hl_name_color("StatusLine").background + end + pcall(api.nvim_set_hl, 0, group, opts) end - return false end +M.is_highlight_option = function(hl_opts) return type(hl_opts) == "table" and next(hl_opts) ~= nil end + +M.is_highlight_name = function(hl_name) return type(hl_name) == "string" and #hl_name > 0 end + return M diff --git a/lua/sttusline/utils/new-component.lua b/lua/sttusline/utils/new-component.lua index 87a9372..2934960 100644 --- a/lua/sttusline/utils/new-component.lua +++ b/lua/sttusline/utils/new-component.lua @@ -3,7 +3,6 @@ local fn = vim.fn local NEW_COMPONENT_TEMPLATE = [[ -- Change NewComponent to your component name -local utils = require("sttusline.utils") local NewComponent = require("sttusline.set_component").new() -- The component will be update when the event is triggered @@ -29,26 +28,53 @@ NewComponent.set_config {} NewComponent.set_padding(1) -- or NewComponent.set_padding{ left = 1, right = 1 } --- The colors of the component +-- The colors of the component. Rely on the return value of the update function, you have 3 ways to set the colors +-- If the return value is string +-- NewComponent.set_colors { fg = colors.set_black, bg = colors.set_white } +-- If the return value is table of string +-- NewComponent.set_colors { { fg = "#009900", bg = "#ffffff" }, { fg = "#000000", bg = "#ffffff" }} +-- -- so if the return value is { "string1", "string2" } +-- -- then the string1 will be highlight with { fg = "#009900", bg = "#ffffff" } +-- -- and the string2 will be highlight with { fg = "#000000", bg = "#ffffff" } +-- +-- -- if you don't want to add highlight for the string1 now +-- -- because it will auto update new colors when the returning value in update function is a table that contains the color options, +-- -- you can add a empty table in the first element +-- -- { +-- colors = { +-- {}, +-- { fg = "#000000", bg = "#ffffff" } +-- }, +-- -- } +-- +-- NOTE: The colors options can be the colors name or the colors options +-- colors = { +-- { fg = "#009900", bg = "#ffffff" }, +-- "DiagnosticsSignError", +-- }, +-- +-- -- So if the return value is { "string1", "string2" } +-- -- then the string1 will be highlight with { fg = "#009900", bg = "#ffffff" } +-- -- and the string2 will be highlight with the colors options of the DiagnosticsSignError highlight +-- +-- -- Or you can set the fg(bg) follow the colors options of the DiagnosticsSignError highlight +-- { +-- colors = { +-- { fg = "DiagnosticsSignError", bg = "#ffffff" }, +-- "DiagnosticsSignError", +-- }, +-- } + NewComponent.set_colors {} -- { fg = colors.set_black, bg = colors.set_white } --- The function will return the value of the component to display on the statusline --- Must return a string +-- The function will return the value of the component to display on the statusline(required). +-- Must return a string or a table of string or a table of { "string", { fg = "color", bg = "color" } } +-- NewComponent.set_update(function() return { "string1", "string2" } end) +-- NewComponent.set_update(function() return { { "string1", {fg = "#000000", bg ="#fdfdfd"} }, "string3", "string4" } end) NewComponent.set_update(function() return "" end) --- NOTE: --- If you don't use NewComponent.set_colors{} and you want to customize the highlight of your component --- You should use utils.add_highlight_name(value, highlight_name) to add the highlight name to the value --- After that you can set the highlight of your component by using vim.api.nvim_set_hl(0, highlight_name, opts) (You should add this to NewComponent.set_onhighlight) --- The function utils.add_highlight_name(value,highlight_name) will return the value --- So you can use it like this: --- NewComponent.set_update(function() return utils.add_highlight_name("Hello", "HelloHighlight") end) --- NewComponent.set_onhighlight(function() vim.api.nvim_set_hl(0, "HelloHighlight", { fg = "#ffffff", bg = "#000000" }) end) -- The function will call when the component is highlight --- You should use it to set the highlight of the component --- Example: --- NewComponent.set_onhighlight(function() vim.api.nvim_set_hl(0, "ComponentHighlight", { fg = colors.set_black, bg = colors.set_white }) end) NewComponent.set_onhighlight(function() end) -- The function will return the condition to display the component when the component is update