X-Git-Url: https://git.rmz.io/dotfiles.git/blobdiff_plain/eca3163fa25f5d6a4d1aa77d447473141d1a5b79..449e5a08ca88c158f6954eebfbf62b088fc333b6:/nvim/lua/plugins/ui.lua diff --git a/nvim/lua/plugins/ui.lua b/nvim/lua/plugins/ui.lua index 5271ad6..8e52958 100644 --- a/nvim/lua/plugins/ui.lua +++ b/nvim/lua/plugins/ui.lua @@ -1,3 +1,89 @@ +local M = {} + +---@param component any -- actually a lualine.component +---@param text string +---@param hl_group? string +---@return string +function M.format(component, text, hl_group) + text = text:gsub("%%", "%%%%") + if not hl_group or hl_group == "" then + return text + end + --- hl_cache is added here, not modified + ---@type table + component.hl_cache = component.hl_cache or {} + local lualine_hl_group = component.hl_cache[hl_group] + if not lualine_hl_group then + local utils = require("lualine.utils.utils") + ---@type string[] + local gui = vim.tbl_filter(function(x) return x end, { + utils.extract_highlight_colors(hl_group, "bold") and "bold", + utils.extract_highlight_colors(hl_group, "italic") and "italic", + }) + + lualine_hl_group = component:create_hl({ + fg = utils.extract_highlight_colors(hl_group, "fg"), + gui = #gui > 0 and table.concat(gui, ",") or nil, + }, "LV_" .. hl_group) --[[@as string]] + component.hl_cache[hl_group] = lualine_hl_group + end + return component:format_hl(lualine_hl_group) .. text .. component:get_default_hl() +end + +function M.pretty_path() + return function(self) + local opts = { + directory_hl = "", + filename_hl = "Bold", + length = 3, + modified_hl = "MatchParen", -- hl group when file is modified + modified_sign = "", + readonly_icon = " 󰌾 ", + } + + local path = vim.fn.expand("%:p") --[[@as string]] + + if path == "" then + return "" + end + + path = require("lazy.core.util").norm(path) + local cwd = vim.uv.cwd() + + if path:find(cwd, 1, true) == 1 then + path = path:sub(#cwd + 2) + end + + local sep = package.config:sub(1, 1) + local parts = vim.split(path, "[\\/]") + + if opts.length == 0 then + parts = parts + elseif #parts > opts.length then + parts = { parts[1], "…", unpack(parts, #parts - opts.length + 2, #parts) } + end + + if vim.bo.modified then + parts[#parts] = parts[#parts] .. opts.modified_sign + parts[#parts] = M.format(self, parts[#parts], opts.modified_hl) + else + parts[#parts] = M.format(self, parts[#parts], opts.filename_hl) + end + + local dir = "" + if #parts > 1 then + dir = table.concat({ unpack(parts, 1, #parts - 1) }, sep) + dir = M.format(self, dir .. sep, opts.directory_hl) + end + + local readonly = "" + if vim.bo.readonly then + readonly = M.format(self, opts.readonly_icon, opts.modified_hl) + end + return dir .. parts[#parts] .. readonly + end +end + ---@type LazySpec return { { 'echasnovski/mini.clue', @@ -28,63 +114,50 @@ return { }, }, }, - { - "nvim-notify", - }, - { - "bufferline.nvim", + { "akinsho/bufferline.nvim", + event = "VeryLazy", + keys = { + { "bp", "BufferLineTogglePin", desc = "Toggle Pin" }, + { "bP", "BufferLineGroupClose ungrouped", desc = "Delete Non-Pinned Buffers" }, + { "br", "BufferLineCloseRight", desc = "Delete Buffers to the Right" }, + { "bl", "BufferLineCloseLeft", desc = "Delete Buffers to the Left" }, + { "", "BufferLineCyclePrev", desc = "Prev Buffer" }, + { "", "BufferLineCycleNext", desc = "Next Buffer" }, + { "[b", "BufferLineCyclePrev", desc = "Prev Buffer" }, + { "]b", "BufferLineCycleNext", desc = "Next Buffer" }, + { "[B", "BufferLineMovePrev", desc = "Move buffer prev" }, + { "]B", "BufferLineMoveNext", desc = "Move buffer next" }, + }, ---@type BufferlineConfig opts = { options = { + close_command = function(n) Snacks.bufdelete(n) end, + right_mouse_command = function(n) Snacks.bufdelete(n) end, + diagnostics = "nvim_lsp", always_show_bufferline = false, - }, - }, - }, - { - "stevearc/dressing.nvim", - lazy = true, - opts = { - input = { - start_in_insert = false, - insert_only = false, - }, - }, - }, - { - "noice.nvim", - enabled = false, - ---@type NoiceConfig - opts = { - presets = { - command_palette = false, -- don't position the cmdline and popupmenu together - lsp_doc_border = true, - long_message_to_split = true, - }, - cmdline = { - view = "cmdline", - }, - routes = { - { -- send file written messages to mini - filter = { - event = "msg_show", - kind = "", - find = "%[w%]", + diagnostics_indicator = function(_, _, diag) + local icons = rmz.ui.icons.diagnostics + local ret = (diag.error and icons.Error .. diag.error .. " " or "") + .. (diag.warning and icons.Warn .. diag.warning or "") + return vim.trim(ret) + end, + offsets = { + { + filetype = "man", + text = "Man page", + highlight = "Directory", + text_align = "left", }, - opts = { skip = true }, }, + ---@param opts bufferline.IconFetcherOpts + get_element_icon = function(opts) + return rmz.ui.icons.ft[opts.filetype] + end, }, }, - config = function(_, opts) - -- ensure [w] is written to msg_show so we can match it - vim.opt.shortmess:append("w") - vim.opt.shortmess:remove("W") - - require("noice").setup(opts) - end, }, - { - "lualine.nvim", - --TODO: Things that were in vim but are missing + { "nvim-lualine/lualine.nvim", + -- TODO: Things that were in vim but are missing -- - git line add/mod/del ar next to branch name rather on right -- - one status line per splits -- - maybe a single one is OK too? @@ -92,98 +165,193 @@ return { -- - unix/dos eof markers -- - really I only want to know if it's not unix -- - filetype in text form. It's quite important to glance this quickly - }, - { - "indent-blankline.nvim", - }, - { - "echasnovski/mini.indentscope", - version = false, -- wait till new 0.7.0 release to put it back on semver - event = "LazyFile", - opts = function (_, opts) - return { - draw = { - animation = require("mini.indentscope").gen_animation.linear({ duration = 10 }) - } - } + event = "VeryLazy", + init = function() + vim.g.lualine_laststatus = vim.o.laststatus + if vim.fn.argc(-1) > 0 then + -- set an empty statusline till lualine loads + vim.o.statusline = " " + else + -- hide the statusline on the starter page + vim.o.laststatus = 0 + end end, - }, - { - "nvimdev/dashboard-nvim", - -- TODO: create cool dashboard header - -- generate logo from text here: - -- https://patorjk.com/software/taag/#p=testall&v=0&f=Computer&t=NEOVIM - -- Can also use use `chafa` and aplha.term to generate text from cli, see - -- https://github.com/goolord/alpha-nvim/discussions/16#discussioncomment-4915947 - -- https://github.com/goolord/alpha-nvim/discussions/16#discussioncomment-5065731 - -- To add orgmode agenda button in alpha - -- dashboard.button("a", "󰕪 " .. " View Agenda", ":lua require('orgmode').action('agenda.agenda')"), opts = function() - local logo = [[ - ██╗ █████╗ ███████╗██╗ ██╗██╗ ██╗██╗███╗ ███╗ Z - ██║ ██╔══██╗╚══███╔╝╚██╗ ██╔╝██║ ██║██║████╗ ████║ Z - ██║ ███████║ ███╔╝ ╚████╔╝ ██║ ██║██║██╔████╔██║ z - ██║ ██╔══██║ ███╔╝ ╚██╔╝ ╚██╗ ██╔╝██║██║╚██╔╝██║ z - ███████╗██║ ██║███████╗ ██║ ╚████╔╝ ██║██║ ╚═╝ ██║ - ╚══════╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ - ]] + -- PERF: we don't need this lualine require madness 🤷 + local lualine_require = require("lualine_require") + lualine_require.require = require - logo = string.rep("\n", 8) .. logo .. "\n\n" + local icons = rmz.ui.icons + + vim.o.laststatus = vim.g.lualine_laststatus local opts = { - theme = "doom", - hide = { - -- this is taken care of by lualine - -- enabling this messes up the actual laststatus setting after loading a file - statusline = false, + options = { + theme = "auto", + globalstatus = vim.o.laststatus == 3, + disabled_filetypes = { statusline = { "dashboard", "alpha", "ministarter", "snacks_dashboard" } }, }, - config = { - header = vim.split(logo, "\n"), - -- stylua: ignore - center = { - { action = [[lua require('orgmode').action('agenda.agenda')]], desc = " View Agenda", icon = "󰕪 ", key = "a" }, - { action = "Telescope find_files", desc = " Find file", icon = " ", key = "f" }, - { action = "ene | startinsert", desc = " New file", icon = " ", key = "n" }, - { action = "Telescope oldfiles", desc = " Recent files", icon = " ", key = "r" }, - { action = [[lua require("lazyvim.util").telescope.config_files()()]], desc = " Config", icon = " ", key = "c" }, - { action = 'lua require("persistence").load()', desc = " Restore Session", icon = " ", key = "s" }, - { action = "LazyExtras", desc = " Lazy Extras", icon = " ", key = "x" }, - { action = "Lazy", desc = " Lazy", icon = "󰒲 ", key = "l" }, + sections = { + lualine_a = { "mode" }, + lualine_b = { "branch" }, + + lualine_c = { + { + "diagnostics", + symbols = { + error = icons.diagnostics.Error, + warn = icons.diagnostics.Warn, + info = icons.diagnostics.Info, + hint = icons.diagnostics.Hint, + }, + }, + { "filetype", icon_only = true, separator = "", padding = { left = 1, right = 0 } }, + { M.pretty_path() }, + }, + lualine_x = { + Snacks.profiler.status(), + -- stylua: ignore + { + function() return " " .. require("dap").status() end, + cond = function() return package.loaded["dap"] and require("dap").status() ~= "" end, + color = function() return { fg = Snacks.util.color("Debug") } end, + }, + -- stylua: ignore + { + require("lazy.status").updates, + cond = require("lazy.status").has_updates, + color = function() return { fg = Snacks.util.color("Special") } end, + }, + { + "diff", + symbols = { + added = icons.git.added, + modified = icons.git.modified, + removed = icons.git.removed, + }, + source = function() + local gitsigns = vim.b.gitsigns_status_dict + if gitsigns then + return { + added = gitsigns.added, + modified = gitsigns.changed, + removed = gitsigns.removed, + } + end + end, + }, + }, + lualine_y = { + { "progress", separator = " ", padding = { left = 1, right = 0 } }, + { "location", padding = { left = 0, right = 1 } }, + }, + lualine_z = { + function() + return " " .. os.date("%R") + end, + }, + }, + inactive_sections = { + lualine_c = { + { "filetype", icon_only = true, separator = "", padding = { left = 1, right = 0 } }, + { M.pretty_path() }, }, - footer = function() - local stats = require("lazy").stats() - local ms = (math.floor(stats.startuptime * 100 + 0.5) / 100) - return { "⚡ Neovim loaded " .. stats.loaded .. "/" .. stats.count .. " plugins in " .. ms .. "ms" } - end, }, + extensions = { "neo-tree", "lazy", "fzf" }, } - for _, button in ipairs(opts.config.center) do - button.desc = button.desc .. string.rep(" ", 43 - #button.desc) - button.key_format = " %s" - end - - -- close Lazy and re-open when the dashboard is ready - if vim.o.filetype == "lazy" then - vim.cmd.close() - vim.api.nvim_create_autocmd("User", { - pattern = "DashboardLoaded", - callback = function() - require("lazy").show() - end, - }) - end + local trouble = require("trouble") + local symbols = trouble.statusline({ + mode = "symbols", + groups = {}, + title = false, + filter = { range = true }, + format = "{kind_icon}{symbol.name:Normal}", + hl_group = "lualine_c_normal", + }) + table.insert(opts.sections.lualine_c, { + symbols and symbols.get, + cond = function() + return vim.b.trouble_lualine ~= false and symbols.has() + end, + }) return opts - end + end, }, { - "SmiteshP/nvim-navic", + "echasnovski/mini.icons", + lazy = true, + opts = { + file = { + [".keep"] = { glyph = "󰊢", hl = "MiniIconsGrey" }, + ["devcontainer.json"] = { glyph = "", hl = "MiniIconsAzure" }, + }, + filetype = { + dotenv = { glyph = "", hl = "MiniIconsYellow" }, + }, + }, + init = function() + -- TODO: try without this + -- see :h MiniIcons.mock_nvim_web_devicons() + package.preload["nvim-web-devicons"] = function() + require("mini.icons").mock_nvim_web_devicons() + return package.loaded["nvim-web-devicons"] + end + end, }, - { - "nvim-web-devicons", + { "MunifTanjim/nui.nvim", lazy = true }, + { "stevearc/dressing.nvim", + enabled = false, -- replaced by snacks.input + lazy = true, + opts = { + input = { + start_in_insert = false, + insert_only = false, + }, + }, }, - { - "nui.nvim", + { "indent-blankline.nvim", + enabled = false, -- replaced by snacks.indent + }, + { "echasnovski/mini.indentscope", + enabled = false, -- replaced by snacks.indent + version = false, -- wait till new 0.7.0 release to put it back on semver + event = { "BufReadPost", "BufNewFile", "BufWritePre" }, + opts = function (_, opts) + return { + draw = { + animation = require("mini.indentscope").gen_animation.linear({ duration = 10 }) + } + } + end, + }, + { "SmiteshP/nvim-navic", + lazy = true, + init = function() + vim.g.navic_silence = true + rmz.lsp.on_attach(function(client, buffer) + if client.supports_method("textDocument/documentSymbol") then + require("nvim-navic").attach(client, buffer) + end + end) + end, + opts = function() + return { + separator = " ", + highlight = true, + depth_limit = 5, + icons = rmz.ui.icons.kinds, + lazy_update_context = true, + } + end, + }, + { "nvim-lualine/lualine.nvim", + optional = true, + opts = function(_, opts) + if not vim.g.trouble_lualine then + table.insert(opts.sections.lualine_c, { "navic", color_correction = "dynamic" }) + end + end, }, }