X-Git-Url: https://git.rmz.io/dotfiles.git/blobdiff_plain/a33276da8c7b30f2f52f07fbc05b0d86d0d6d4e2..7d854464d9b1bd386b0eaf25f38ac3b96dd1fc18:/nvim/lua/plugins/ui.lua diff --git a/nvim/lua/plugins/ui.lua b/nvim/lua/plugins/ui.lua index 61fad3b..8e52958 100644 --- a/nvim/lua/plugins/ui.lua +++ b/nvim/lua/plugins/ui.lua @@ -1,50 +1,163 @@ ----@type LazyPluginSpec +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 { - { - "nvim-notify", - }, - { - "bufferline.nvim", - ---@type BufferlineConfig + { 'echasnovski/mini.clue', + -- NOTE: this caused some issue with unterminated mappings, i.e. gc when there + -- is also gcc. It would simply terminate early and not allow for next mapping. + version = '*', opts = { - options = { - always_show_bufferline = true, + triggers = { + { mode = 'n', keys = '' }, + { mode = 'x', keys = '' }, + -- `g` key + { mode = 'n', keys = 'g' }, + { mode = 'x', keys = 'g' }, + -- Marks + { mode = 'n', keys = "'" }, + { mode = 'n', keys = '`' }, + { mode = 'x', keys = "'" }, + { mode = 'x', keys = '`' }, + -- Window commands + { mode = 'n', keys = '' }, + -- `z` key + { mode = 'n', keys = 'z' }, + { mode = 'x', keys = 'z' }, + }, + clues = { + { mode = 'n', keys = 'b', desc = '+Buffers' }, + { mode = 'n', keys = 'c', desc = '+LSP' }, }, }, }, - { - "noice.nvim", - ---@type NoiceConfig + { "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 = { - presets = { - command_palette = false, -- don't position the cmdline and popupmenu together - lsp_doc_border = true, -- add a border to hover docs and signature help - }, - cmdline = { - view = "cmdline", - }, - routes = { - { -- send file written messages to mini - filter = { - event = "msg_show", - kind = "", - find = "%[w%]", + 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, + 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? @@ -52,23 +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 + 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, + opts = function() + -- PERF: we don't need this lualine require madness 🤷 + local lualine_require = require("lualine_require") + lualine_require.require = require + + local icons = rmz.ui.icons + + vim.o.laststatus = vim.g.lualine_laststatus + + local opts = { + options = { + theme = "auto", + globalstatus = vim.o.laststatus == 3, + disabled_filetypes = { statusline = { "dashboard", "alpha", "ministarter", "snacks_dashboard" } }, + }, + 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() }, + }, + }, + extensions = { "neo-tree", "lazy", "fzf" }, + } + + 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, }, { - "indent-blankline.nvim", + "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, }, - { - "mini.indentscope", + { "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, + }, + }, }, - { - "alpha-nvim", + { "indent-blankline.nvim", + enabled = false, -- replaced by snacks.indent }, - { - "nvim-navic", + { "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, }, - { - "nvim-web-devicons", + { "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, }, - { - "nui.nvim", + { "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, }, }