Sanchayan Maity
cd50d8f956
We want file path being shown in winbar to be relative to the current directory or in our case frequently the git root. For whatever reason, only the first opened leftmost split would show the file path relative to current git root while any files opened later would show the full path starting from `HOME`/~ directory. So something around how '%f%m%r' actually works or how it is being used to set winbar has been a problem.
313 lines
7.9 KiB
Lua
313 lines
7.9 KiB
Lua
local fn = vim.fn
|
||
local api = vim.api
|
||
|
||
local lsp_status = ""
|
||
local gstatus = { ahead = 0, behind = 0 }
|
||
|
||
local update_status = 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()
|
||
|
||
if vim.lsp.status then
|
||
lsp_status = vim.lsp.status()
|
||
end
|
||
end
|
||
|
||
if _G.Gstatus_timer == nil then
|
||
_G.Gstatus_timer = vim.uv.new_timer()
|
||
_G.Gstatus_timer:start(0, 1000, vim.schedule_wrap(update_status))
|
||
else
|
||
_G.Gstatus_timer:stop()
|
||
end
|
||
|
||
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)
|
||
highlight("LspProgress" , colors.orange , colors.bg)
|
||
|
||
M.trunc_width = setmetatable({
|
||
filename = 140,
|
||
git_status = 60,
|
||
lsp_status = 80,
|
||
}, {
|
||
__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 is_git = fn.FugitiveHead() ~= ""
|
||
local signs = vim.b.minidiff_summary or { add = 0, change = 0, delete = 0 }
|
||
|
||
return is_git
|
||
and string.format(
|
||
" %s+%s %s~%s %s-%s",
|
||
"%#DiffAdded#",
|
||
signs.add or 0,
|
||
"%#DiffChanged#",
|
||
signs.change or 0,
|
||
"%#DiffRemoved#",
|
||
signs.delete 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()
|
||
local is_git = fn.FugitiveHead() ~= ""
|
||
|
||
return
|
||
is_git
|
||
and
|
||
string.format(" %s%s%d%s%d", '%#GitStatus#', ' ', gstatus.behind, ' ', gstatus.ahead)
|
||
or ""
|
||
end
|
||
|
||
M.get_lsp_status = function(self)
|
||
local trunc_width = self.trunc_width.lsp_status
|
||
if self:is_truncated(trunc_width) then
|
||
return ""
|
||
end
|
||
|
||
if vim.lsp.status then
|
||
return string.format(" %s%s", '%#LspProgress#', string.sub(lsp_status, 1, trunc_width))
|
||
end
|
||
|
||
return ""
|
||
end
|
||
|
||
M.set_active = function(self)
|
||
return table.concat {
|
||
self:get_filename(),
|
||
self:get_git_status(),
|
||
self:git_ahead_behind_status(),
|
||
"%=",
|
||
self:get_lsp_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
|
||
|
||
-- `:.` Reduces file name to be relative to current directory, if possible.
|
||
local winbar_file = vim.fn.expand('%:.')
|
||
|
||
vim.opt_local.winbar = string.format(" %s%s", "%#WinbarFile#", winbar_file)
|
||
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"
|
||
})
|