local fn = vim.fn local git_status_cache = {} local on_exit_fetch = function(result) if result.code == 0 then git_status_cache.fetch_success = true end end local function handle_numeric_result(cache_key) return function(result) if result.code == 0 then git_status_cache[cache_key] = tonumber(result.stdout:match("(%d+)")) or 0 end end end local async_cmd = function(cmd_str, on_exit) local cmd = vim.tbl_filter(function(element) return element ~= "" end, vim.split(cmd_str, " ")) vim.system(cmd, { text = true }, on_exit) end local async_git_status_update = function() -- Fetch the latest changes from the remote repository (replace 'origin' if needed) async_cmd("git fetch origin", on_exit_fetch) if not git_status_cache.fetch_success then return end -- Get the number of commits behind -- the @{upstream} notation is inspired by post: https://www.reddit.com/r/neovim/comments/t48x5i/git_branch_aheadbehind_info_status_line_component/ -- note that here we should use double dots instead of triple dots local behind_cmd_str = "git rev-list --count HEAD..@{upstream}" async_cmd(behind_cmd_str, handle_numeric_result("behind_count")) -- Get the number of commits ahead local ahead_cmd_str = "git rev-list --count @{upstream}..HEAD" async_cmd(ahead_cmd_str, handle_numeric_result("ahead_count")) end local function get_git_ahead_behind_info() async_git_status_update() local status = git_status_cache if not status then return "" end local msg = "" if type(status.ahead_count) == "number" and status.ahead_count > 0 then local ahead_str = string.format("↑[%d] ", status.ahead_count) msg = msg .. ahead_str end if type(status.behind_count) == "number" and status.behind_count > 0 then local behind_str = string.format("↓[%d] ", status.behind_count) msg = msg .. behind_str end return msg end local function spell() if vim.o.spell then return string.format("[SPELL]") end return "" end --- show indicator for Chinese IME local function ime_state() if vim.g.is_mac then -- ref: https://github.com/vim-airline/vim-airline/blob/master/autoload/airline/extensions/xkblayout.vim#L11 local layout = fn.libcall(vim.g.XkbSwitchLib, "Xkb_Switch_getXkbLayout", "") -- We can use `xkbswitch -g` on the command line to get current mode. -- mode for macOS builtin pinyin IME: com.apple.inputmethod.SCIM.ITABC -- mode for Rime: im.rime.inputmethod.Squirrel.Rime local res = fn.match(layout, [[\v(Squirrel\.Rime|SCIM.ITABC)]]) if res ~= -1 then return "[CN]" end end return "" end local function trailing_space() if not vim.o.modifiable then return "" end local line_num = nil for i = 1, fn.line("$") do local linetext = fn.getline(i) -- To prevent invalid escape error, we wrap the regex string with `[[]]`. local idx = fn.match(linetext, [[\v\s+$]]) if idx ~= -1 then line_num = i break end end local msg = "" if line_num ~= nil then msg = string.format("[%d]trailing", line_num) end return msg end local function mixed_indent() if not vim.o.modifiable then return "" end local space_pat = [[\v^ +]] local tab_pat = [[\v^\t+]] local space_indent = fn.search(space_pat, "nwc") local tab_indent = fn.search(tab_pat, "nwc") local mixed = (space_indent > 0 and tab_indent > 0) local mixed_same_line if not mixed then mixed_same_line = fn.search([[\v^(\t+ | +\t)]], "nwc") mixed = mixed_same_line > 0 end if not mixed then return "" end if mixed_same_line ~= nil and mixed_same_line > 0 then return "MI:" .. mixed_same_line end local space_indent_cnt = fn.searchcount({ pattern = space_pat, max_count = 1e3 }).total local tab_indent_cnt = fn.searchcount({ pattern = tab_pat, max_count = 1e3 }).total if space_indent_cnt > tab_indent_cnt then return "MI:" .. tab_indent else return "MI:" .. space_indent end end local diff = function() local git_status = vim.b.gitsigns_status_dict if git_status == nil then return end local modify_num = git_status.changed local remove_num = git_status.removed local add_num = git_status.added local info = { added = add_num, modified = modify_num, removed = remove_num } -- vim.print(info) return info end local virtual_env = function() -- only show virtual env for Python if vim.bo.filetype ~= "python" then return "" end local conda_env = os.getenv("CONDA_DEFAULT_ENV") local venv_path = os.getenv("VIRTUAL_ENV") if venv_path == nil then if conda_env == nil then return "" else return string.format(" %s (conda)", conda_env) end else local venv_name = vim.fn.fnamemodify(venv_path, ":t") return string.format(" %s (venv)", venv_name) end end local get_active_lsp = function() local msg = "đŸšĢ" local buf_ft = vim.api.nvim_get_option_value("filetype", {}) local clients = vim.lsp.get_clients { bufnr = 0 } if next(clients) == nil then return msg end for _, client in ipairs(clients) do ---@diagnostic disable-next-line: undefined-field local filetypes = client.config.filetypes if filetypes and vim.fn.index(filetypes, buf_ft) ~= -1 then return client.name end end return msg end require("lualine").setup { options = { icons_enabled = true, theme = "auto", component_separators = { left = "⏐", right = "⏐" }, section_separators = "", disabled_filetypes = {}, always_divide_middle = true, refresh = { statusline = 1000, }, }, sections = { lualine_a = { { "filename", symbols = { readonly = "[🔒]", }, }, }, lualine_b = { { "branch", fmt = function(name, _) -- truncate branch name in case the name is too long return string.sub(name, 1, 20) end, color = { gui = "italic,bold" }, }, { get_git_ahead_behind_info, color = { fg = "#E0C479" }, }, { "diff", source = diff, }, { virtual_env, color = { fg = "black", bg = "#F1CA81" }, }, }, lualine_c = { { "%S", color = { gui = "bold", fg = "cyan" }, }, { spell, color = { fg = "black", bg = "#a7c080" }, }, }, lualine_x = { { get_active_lsp, icon = "📡", }, { "diagnostics", sources = { "nvim_diagnostic" }, symbols = { error = "🆇 ", warn = "âš ī¸ ", info = "â„šī¸ ", hint = " " }, }, { trailing_space, color = "WarningMsg", }, { mixed_indent, color = "WarningMsg", }, }, lualine_y = { { "encoding", fmt = string.upper, }, { "fileformat", symbols = { unix = "unix", dos = "win", mac = "mac", }, }, "filetype", { ime_state, color = { fg = "black", bg = "#f46868" }, }, }, lualine_z = { "location", "progress", }, }, inactive_sections = { lualine_a = {}, lualine_b = {}, lualine_c = { "filename" }, lualine_x = { "location" }, lualine_y = {}, lualine_z = {}, }, tabline = {}, extensions = { "quickfix", "fugitive", "nvim-tree" }, }