local fn = vim.fn local api = vim.api local gstatus = { ahead = 0, behind = 0 } local update_gstatus = function() local Job = require 'plenary.job' Job:new({ command = 'git', args = { 'rev-list', '--left-right', '--count', 'HEAD...@{upstream}' }, on_exit = function(exit_job, _) local res = exit_job:result()[1] if type(res) ~= 'string' then gstatus = { ahead = 0, behind = 0 }; return end local ok, ahead, behind = pcall(string.match, res, "(%d+)%s*(%d+)") if not ok then ahead, behind = 0, 0 end gstatus = { ahead = ahead, behind = behind } end, }):start() end if _G.Gstatus_timer == nil then _G.Gstatus_timer = vim.loop.new_timer() else _G.Gstatus_timer:stop() end _G.Gstatus_timer:start(0, 2000, vim.schedule_wrap(update_gstatus)) local M = {} local colors = { line_bg = '#21242b', bg = '#000000', fg = '#dobfa1', yellow = '#fabd2f', cyan = '#008080', darkblue = '#081633', green = '#afd700', orange = '#FF8800', purple = '#5d4d7a', magenta = '#c678dd', blue = '#51afef', red = '#ec5f67' } local function highlight(group, fg, bg) api.nvim_set_hl(0, group, { fg = fg, bg = bg }) end highlight("Window" , colors.red , colors.bg) highlight("Mode" , colors.green , colors.bg) highlight("Filename" , colors.blue , colors.bg) highlight("Fileformat" , colors.purple , colors.bg) highlight("GitBranch" , colors.orange , colors.bg) highlight("GitStatus" , colors.magenta, colors.bg) highlight("DiffAdded" , colors.green , colors.bg) highlight("DiffChanged" , colors.orange , colors.bg) highlight("DiffRemoved" , colors.red , colors.bg) highlight("DiagnosticsE", colors.red , colors.bg) highlight("DiagnosticsW", colors.yellow , colors.bg) highlight("DiagnosticsI", colors.cyan , colors.bg) highlight("DiagnosticsH", colors.orange , colors.bg) highlight("Location" , colors.yellow , colors.bg) highlight("Progress" , colors.magenta, colors.bg) highlight("WinbarFile" , colors.magenta, colors.bg) M.trunc_width = setmetatable({ git_status = 60, filename = 140, }, { __index = function() return 80 end, }) M.is_truncated = function(_, width) return api.nvim_win_get_width(0) < width end M.get_window_number = function() return string.format(" %s%d", "%#Window#", api.nvim_win_get_number(0)) end M.modes = setmetatable({ ["n" ] = "N" , ["no"] = "N·P", ["v" ] = "V" , ["V" ] = "V·L", [""] = "V·B", -- this is not ^V, but it's , they're different ["s" ] = "S" , ["S" ] = "S·L", [""] = "S·B", -- same with this one, it's not ^S but it's  ["i" ] = "I" , ["ic"] = "I" , ["R" ] = "R" , ["Rv"] = "V·R", ["c" ] = "C" , ["cv"] = "V·E", ["ce"] = "E" , ["r" ] = "P" , ["rm"] = "RM" , ["r?"] = "C" , ["!" ] = "S" , ["t" ] = "T" , }, { __index = function() return "U" end, }) M.get_current_mode = function(self) local current_mode = api.nvim_get_mode().mode return string.format(" %s%s", "%#Mode#", self.modes[current_mode]) end M.get_git_branch = function(self) if self:is_truncated(self.trunc_width.git_status) then return "" end return string.format(" %s %s", "%#GitBranch#", fn.FugitiveHead()) end M.get_git_status = function(self) local signs = vim.b.gitsigns_status_dict or { head = "", added = 0, changed = 0, removed = 0 } local is_head_empty = signs.head ~= "" return is_head_empty and string.format( " %s+%s %s~%s %s-%s", "%#DiffAdded#", signs.added or 0, "%#DiffChanged#", signs.changed or 0, "%#DiffRemoved#", signs.removed or 0 ) or "" end M.get_filename = function() local filetype = vim.bo.filetype local special_ft = { 'diff', 'fugitive', 'git', 'qf' } for _, v in pairs(special_ft) do if v == filetype then return string.format(" %s%s", "%#Filename#", filetype) end end return string.format(" %s%s", "%#Filename#", '%t%m%r') end M.get_fileformat = function() return string.format("%s%s", "%#Fileformat#", vim.o.fileformat):lower() end M.diagnostic_status = function() local diagnostic = '' local num_errors = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.ERROR }) if num_errors > 0 then diagnostic = string.format("%s%s%d", diagnostic, ' %#DiagnosticsE#E:', num_errors) end local num_warnings = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.WARN }) if num_warnings > 0 then diagnostic = string.format("%s%s%d", diagnostic, ' %#DiagnosticsW#W:', num_warnings) end local num_infos = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.INFO }) if num_infos > 0 then diagnostic = string.format("%s%s%d", diagnostic, ' %#DiagnosticsI#I:', num_infos) end local num_hints = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.HINT }) if num_hints > 0 then diagnostic = string.format("%s%s%d", diagnostic, ' %#DiagnosticsH#H:', num_hints) end return diagnostic end M.get_line_col = function() return string.format(" %s%s", "%#Location#", "%l:%c") end M.progress = function() return string.format(" %s%s", "%#Progress#", "%3p%% ") end M.git_ahead_behind_status = function() return vim.b.gitsigns_status_dict and string.format(" %s%s%d%s%d", '%#GitStatus#', '  ', gstatus.behind, '  ', gstatus.ahead) or "" end M.set_active = function(self) return table.concat { self:get_filename(), self:get_git_status(), self:git_ahead_behind_status(), "%=", self:diagnostic_status(), self:get_line_col(), self:progress(), } end M.set_inactive = function(self) return table.concat { "%#Window#", -- Putting this in a function like the others seems to not work. And takes -- the window number of the active window. Same for filename. " %{winnr()}", self:get_git_branch(), "%=", self:get_fileformat(), self:get_line_col(), self:progress(), } end _G.Statusline = setmetatable(M, { __call = function(self, mode) return self["set_" .. mode](self) end, }) local status_id = api.nvim_create_augroup("Statusline", {clear = true}) local hide_status_id = api.nvim_create_augroup("HideStatusline", {clear = true}) api.nvim_create_autocmd({"WinEnter", "BufEnter"}, { group = status_id, pattern = "*", command = "setlocal statusline=%!v:lua.Statusline('active')" }) api.nvim_create_autocmd({"WinLeave" , "BufLeave"}, { group = status_id, pattern = "*", command = "setlocal statusline=%!v:lua.Statusline('inactive')" }) api.nvim_create_autocmd({"BufWinEnter", "BufFilePost"}, { group = status_id, callback = function() -- Skip floating windows -- Idea taken from https://github.com/itchyny/lightline.vim/pull/552 if api.nvim_win_get_config(0).relative ~= '' then return end local winbar_filetype_exclude = { "diff", "fugitive", "git", "help", "packer", "qf" } if vim.tbl_contains(winbar_filetype_exclude, vim.bo.filetype) then vim.opt_local.winbar = nil return end vim.opt_local.winbar = string.format(" %s%s", "%#WinbarFile#", '%f%m%r') end, }) api.nvim_create_autocmd({"TermOpen", "TermEnter"}, { group = hide_status_id, pattern = "*", command = "set laststatus=0 noshowmode noruler" }) api.nvim_create_autocmd({"TermLeave"}, { group = hide_status_id, pattern = "*", command = "set laststatus=2 showmode ruler" })