diff --git a/nix/neovim-overlay.nix b/nix/neovim-overlay.nix index 383164c..65ea407 100644 --- a/nix/neovim-overlay.nix +++ b/nix/neovim-overlay.nix @@ -43,6 +43,7 @@ with final.pkgs.lib; let mini-icons alpha-nvim bufferline-nvim + lualine-nvim ]; extraPackages = with pkgs; [ diff --git a/nvim/lua/icons.lua b/nvim/lua/icons.lua index 6376e54..b177183 100644 --- a/nvim/lua/icons.lua +++ b/nvim/lua/icons.lua @@ -10,4 +10,9 @@ return { mru = '', project = '', }, + git = { + added = ' ', + modified = ' ', + removed = ' ', + }, } diff --git a/nvim/lua/lib/lazyvim/lualine.lua b/nvim/lua/lib/lazyvim/lualine.lua new file mode 100644 index 0000000..417edfb --- /dev/null +++ b/nvim/lua/lib/lazyvim/lualine.lua @@ -0,0 +1,155 @@ +-- https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/util/lualine.lua + +local lazy_util = require('lib.lazy.util') +local lazyvim_root = require('lib.lazyvim.root') +local lazyvim_ui = require('lib.lazyvim.ui') + +---@class lib.lazyvim.lualine +local M = {} + +---@param component any +---@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 + + ---@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 + +---@param opts? {relative: "cwd"|"root", modified_hl: string?, directory_hl: string?, filename_hl: string?, modified_sign: string?, readonly_icon: string?, length: number?} +function M.pretty_path(opts) + opts = vim.tbl_extend('force', { + relative = 'cwd', + modified_hl = 'MatchParen', + directory_hl = '', + filename_hl = 'Bold', + modified_sign = '', + readonly_icon = ' 󰌾 ', + length = 3, + }, opts or {}) + + return function(self) + local path = vim.fn.expand('%:p') --[[@as string]] + + if path == '' then + return '' + end + + path = lazy_util.norm(path) + local root = lazyvim_root.get({ normalize = true }) + local cwd = lazyvim_root.cwd() + + if opts.relative == 'cwd' and path:find(cwd, 1, true) == 1 then + path = path:sub(#cwd + 2) + elseif path:find(root, 1, true) == 1 then + path = path:sub(#root + 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 opts.modified_hl and vim.bo.modified then + parts[#parts] = parts[#parts] .. opts.modified_sign + parts[#parts] = M.format(self, parts[#parts], opts.modified_h1) + 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 + +---@param opts? {cwd:false, subdirectory:true, parent:true, other:true, icon?:string} +function M.root_dir(opts) + opts = vim.tbl_extend('force', { + cwd = false, + subdirectory = true, + parent = true, + other = true, + icon = '󱉭 ', + color = function() + return lazyvim_ui.fg('Special') + end, + }, opts or {}) + + local function get() + local cwd = lazyvim_root.cwd() + local root = lazyvim_root.get({ normalize = true }) + local name = vim.fs.basename(root) + + if root == cwd then + return opts.cwd and name + elseif root:find(cwd, 1, true) == 1 then + -- Root is subdirectory or cwd. + return opts.subdirectory and name + elseif cwd:find(root, 1, true) == 1 then + -- Root is parent directory of cwd. + return opts.parent and name + else + -- Root and cwd are not related. + return opts.other and name + end + end + + return { + function() + return (opts.icon and opts.icon .. ' ') .. get() + end, + cond = function() + return type(get()) == 'string' + end, + color = opts.color, + } +end + +return M diff --git a/nvim/lua/lib/lazyvim/root.lua b/nvim/lua/lib/lazyvim/root.lua index 6348c60..1bcc69e 100644 --- a/nvim/lua/lib/lazyvim/root.lua +++ b/nvim/lua/lib/lazyvim/root.lua @@ -190,4 +190,8 @@ function M.git() return ret end +function M.cwd() + return M.realpath(vim.uv.cwd()) or '' +end + return M diff --git a/nvim/lua/lib/lazyvim/ui.lua b/nvim/lua/lib/lazyvim/ui.lua index ae5d3e0..85089e7 100644 --- a/nvim/lua/lib/lazyvim/ui.lua +++ b/nvim/lua/lib/lazyvim/ui.lua @@ -22,4 +22,11 @@ function M.foldexpr() return vim.b[buf].ts_folds and vim.treesitter.foldexpr() or '0' end +---@return {fg?:string}? +function M.fg(name) + local hl = vim.api.nvim_get_hl(0, { name = name, link = false }) + local fg = hl and hl.fg or hl.foreground + return fg and { fg = string.format('#%06x', fg) } or nil +end + return M diff --git a/nvim/lua/plugins/ui/init.lua b/nvim/lua/plugins/ui/init.lua index 2f0e08e..15c8874 100644 --- a/nvim/lua/plugins/ui/init.lua +++ b/nvim/lua/plugins/ui/init.lua @@ -3,5 +3,6 @@ local req = require('lib.marleyvim').localRequire('plugins.ui') return { req('alpha-nvim'), req('bufferline-nvim'), + req('lualine-nvim'), req('mini-icons'), } diff --git a/nvim/lua/plugins/ui/lualine-nvim.lua b/nvim/lua/plugins/ui/lualine-nvim.lua new file mode 100644 index 0000000..f3d2343 --- /dev/null +++ b/nvim/lua/plugins/ui/lualine-nvim.lua @@ -0,0 +1,126 @@ +return { + 'lualine.nvim', + event = 'DeferredUIEnter', + beforeAll = function() + vim.g.lualine_laststatus = vim.o.laststatus + + if vim.fn.argc(-1) > 0 then + -- Set an empty statusline until lualine loads. + vim.o.statusline = ' ' + else + -- Hide the statusline on the starter page. + vim.o.laststatus = 0 + end + end, + before = function() + require('lz.n').trigger_load('mini.icons') + + if vim.g.colors_name == 'rose-pine' then + require('lz.n').trigger_load('rose-pine') + end + end, + after = function() + local lazyvim_lualine = require('lib.lazyvim.lualine') + local lazyvim_ui = require('lib.lazyvim.ui') + local icons = require('icons') + + vim.o.laststatus = vim.g.lualine_laststatus + + require('lualine').setup({ + 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 = { + lazyvim_lualine.root_dir(), + { + '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 }, + }, + { lazyvim_lualine.pretty_path() }, + }, + + lualine_x = { + { + function() + return require('noice').api.status.command.get() + end, + cond = function() + return package.loaded['noice'] + and require('noice').api.status.command.has() + end, + color = function() + return lazyvim_ui.fg('Statement') + end, + }, + + { + function() + return ' ' .. require('dap').status() + end, + cond = function() + return package.loaded['dap'] and require('dap').status ~= '' + end, + color = function() + return lazyvim_ui.fg('debug') + 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, + }, + }, + extensions = { 'neo-tree' }, + }, + }) + end, +}