]> git.rmz.io Git - dotfiles.git/blobdiff - nvim/lua/plugins/ui.lua
nvim: lazy load mini.clue
[dotfiles.git] / nvim / lua / plugins / ui.lua
index 3da2dd1a2e5db3e37a2b83bf5862c76949f73fbe..56dcba9e549f01bbd52d2556c8f7c7ee71fec2dc 100644 (file)
----@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<string, string>
+  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. <L>gc when there 
+    -- is also <L>gcc. It would simply terminate early and not allow for next mapping.
+    version = '*',
+    lazy = true,  -- NOTE: load this explicitely when needed, it blocks querying for key definitons via :Verbose map
     opts = {
-      options = {
-        always_show_bufferline = false,
+      triggers = {
+        { mode = 'n', keys = '<Leader>' },
+        { mode = 'x', keys = '<Leader>' },
+        -- `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 = '<C-w>' },
+        -- `z` key
+        { mode = 'n', keys = 'z' },
+        { mode = 'x', keys = 'z' },
       },
-    },
-  },
-  {
-    "stevearc/dressing.nvim",
-    lazy = true,
-    opts = {
-      input = {
-        insert_only = false,
+      clues = {
+        { mode = 'n', keys = '<Leader>b', desc = '+Buffers' },
+        { mode = 'n', keys = '<Leader>c', desc = '+LSP' },
       },
     },
   },
-  {
-    "noice.nvim",
-    enabled = false,
-    ---@type NoiceConfig
+  { "akinsho/bufferline.nvim",
+    event = "VeryLazy",
+    keys = {
+      { "<leader>bp", "<Cmd>BufferLineTogglePin<CR>", desc = "Toggle Pin" },
+      { "<leader>bP", "<Cmd>BufferLineGroupClose ungrouped<CR>", desc = "Delete Non-Pinned Buffers" },
+      { "<leader>br", "<Cmd>BufferLineCloseRight<CR>", desc = "Delete Buffers to the Right" },
+      { "<leader>bl", "<Cmd>BufferLineCloseLeft<CR>", desc = "Delete Buffers to the Left" },
+      { "<S-h>", "<cmd>BufferLineCyclePrev<cr>", desc = "Prev Buffer" },
+      { "<S-l>", "<cmd>BufferLineCycleNext<cr>", desc = "Next Buffer" },
+      { "[b", "<cmd>BufferLineCyclePrev<cr>", desc = "Prev Buffer" },
+      { "]b", "<cmd>BufferLineCycleNext<cr>", desc = "Next Buffer" },
+      { "[B", "<cmd>BufferLineMovePrev<cr>", desc = "Move buffer prev" },
+      { "]B", "<cmd>BufferLineMoveNext<cr>", desc = "Move buffer next" },
+    },
+    ---@type BufferlineConfig
     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%]",
+      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?
@@ -63,94 +166,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",
-  },
-  {
-    "mini.indentscope",
-    opts = {
-      draw = {
-        animation = require("mini.indentscope").gen_animation.linear({ duration = 10 }),
-      },
-    },
-  },
-  {
-    "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')<CR>"),
+    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()
-      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
+
+      local icons = rmz.ui.icons
 
-      logo = string.rep("\n", 8) .. logo .. "\n\n"
+      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,
           },
-          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,
         },
+        inactive_sections = {
+          lualine_c = {
+            { "filetype", icon_only = true, separator = "", padding = { left = 1, right = 0 } },
+            { M.pretty_path() },
+          },
+        },
+        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,
   },
   {
-    "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,
   },
 }