Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork6.5k
One-file statusline plugin#36955
-
One-file statusline plugin
There are endless ways to customize a statusline once you know what you are doing. I will share my personal setup at the end, but the main goal is to explore ways to make the update loop more efficient. IdeaWe can't just update everything on every tracked event. First of all, we don't need to update branch on mode change, etc. Secondly, events like
ConceptWe need a lightweight queue to see if update is needed but not required instantly. localqueue= {}---Enqueue a specific module or all of themlocalfunctionenqueue(module)ifmodulethenqueue[module]=trueelseformod,_inpairs(state)doqueue[mod]=trueendendend We need a state object for the results of module updates, and a handler that will hold update functions (could be done with separate functions just as well). localstate= {mode="N",branch="",filename="",location="",---...}localhandler= {}functionhandler.mode()localmode=vim.fn.mode()mode=mode:upper()mode=mode:gsub("\22","V")returnmodeend---... We need a function that will update module states without redrawing the statusline. localfunctionupdate()localupdated=falseformodule,_inpairs(queue)dolocalcallback=handler[module]---@diagnosticdisable-next-line:unnecessary-ififcallbackthenstate[module]=callback()elsevim.notify_once("Statusline module"..module.." missing a callback")endqueue[module]=nilupdated=trueendreturnupdatedend We need a function that redraws statusline. localfunctionredraw()---Statusline is hidden -> returnifvim.o.laststatus==0thenreturnend---Update modules. If no updates -> returnifnotupdate()thenreturnend---Concatenate modules and send statuslinelocalall_modules= {"TODO!"}statusline=table.concat(all_modules,sep)vim.wo.statusline=statuslineend We need to create and start the main loop. localfunctionrun()---Here we enqueue modules not tracked elsewhereenqueue("stats")redraw()vim.defer_fn(run,1000)endrun() And we need some autocmds to enqueue modules. ---Grouping autocmds is a good practicelocalgroup=vim.api.nvim_create_augroup("uStatusline", {clear=false })---This one causes complete updatevim.api.nvim_create_autocmd({"BufEnter","BufWritePost",}, {group=group,callback=function()enqueue()redraw()end,})---This one updates diagnostics passively, since triggered often---but redrawing with 1 sec delay is just finevim.api.nvim_create_autocmd("LspProgress", {group=group,callback=function()enqueue("diagnostics")end,}) ExampleThis is a highly personal setup, but it shows it all working together. I believe, this could be plug-and-play, except you will need several additional Full code---This is mainly to change to `StatusLineVisual`---and other highlight groupslocalmodes= { ["N"]="", ["I"]="Insert", ["S"]="Insert", ["V"]="Visual", ["C"]="Command", ["O"]="Command", ["R"]="Replace", ["T"]="Terminal",}localqueue= {}localstate= {mode="N",branch="",filename="",filestatus="",filetype="",selection="",diagnostics="",stats="",location="",}localfunctionenqueue(module)ifmodulethenqueue[module]=trueelseformod,_inpairs(state)doqueue[mod]=trueendendendlocalhandler= {}---"\22" is <C-v> (blockwise visual mode)functionhandler.mode()localmode=vim.fn.mode()mode=mode:upper()mode=mode:gsub("\22","V")returnmodeend---If there are changes, display "~main~"functionhandler.branch()localgit=vim.b.gitsigns_status_dictor {}localbranch=git.heador""ifbranch~=""and (git.addedor0)+ (git.changedor0)+ (git.removedor0)>0thenreturn"~"..branch.."~"endreturnbranchend---"⋯" replaces standard "[No Name]"functionhandler.filename()localfilename=vim.fn.bufname()filename=filename==""and"⋯"orvim.fn.fnamemodify(filename,":~:.")returnfilenameend---Could as well use standard "[+]" and "[RO]"functionhandler.filestatus()localreadonly=vim.bo.readonlyand"⌀"or""localmodified=vim.bo.modifiedand"🞷"or""returnreadonly..modifiedend---`blink.cmp` is a completion plugin, so I don't need to see---its filetypes in pmenus and suchfunctionhandler.filetype()localft=vim.bo.filetypeifft:find("blink%-cmp")thenreturnstate.filetypeendreturnftend---We could've used standard statusline expression for this,---but we wouldn't track widechars and such properly,---as the standard one shows bytes, not chars.functionhandler.location()localpos=vim.fn.getcursorcharpos()return ("%2d:%-2d"):format(pos[2],pos[3])end---Shows how many row-columns are in selection.functionhandler.selection()localmode=vim.fn.mode()ifmode~="v"andmode~="V"andmode~="\22"thenreturn""endlocalsel,cur=vim.fn.getpos("v"),vim.fn.getpos(".")localsrow,scol,erow,ecol=sel[2],sel[3],cur[2],cur[3]---@diagnosticdisable-next-line:need-check-nillocalcnum=math.abs(scol-ecol)+1---@diagnosticdisable-next-line:need-check-nillocallnum=math.abs(srow-erow)+1localrange=mode=="\22"and (lnum..":"..cnum)or ((mode=="V"orlnum>1)andlnumorcnum)return"<"..range..">"end---This is controversial. I want very noticeable diagnostic signs---and don't care about the number of errorsfunctionhandler.diagnostics()ifvim.lsp.status()~=""thenvim.defer_fn(function()enqueue("diagnostics")end,1000)return""endlocalhl,sym="DiagnosticSign","█"localndiags=vim.diagnostic.count(0)localerrors= (ndiags[1]or0)>0localwarns= (ndiags[2]or0)>0localinfos= (ndiags[3]or0)>0localhints= (ndiags[4]or0)>0errors=errorsand ("%#"..hl.."Error#"..sym)or""warns=warnsand ("%#"..hl.."Warn#"..sym)or""infos=infosand ("%#"..hl.."Info#"..sym)or""hints=hintsand ("%#"..hl.."Hint#"..sym)or""returnerrors..warns..infos..hintsend---These are custom indicatorsfunctionhandler.stats()localstats= {keymap=vim.o.keymap~=""and"⌥"or"",format=vim.g.u_manual_formattingand"⨂"or"", }stats=vim.tbl_filter(function(v)returnv~=""end,stats)returnvim.fn.join(stats,"")endlocalfunctionupdate()localupdated=falseformodule,_inpairs(queue)do---If module is "diagnostics", wait for Normal mode to updateifmodule~="diagnostics"orvim.fn.mode()=="n"thenlocalcallback=handler[module]---@diagnosticdisable-next-line:unnecessary-ififcallbackthenstate[module]=callback()elsevim.notify_once("Statusline module"..module.." missing a callback")endqueue[module]=nilupdated=trueendendreturnupdatedendlocalfunctionredraw()ifvim.o.laststatus==0thenreturnendifnotupdate()thenreturnendlocaltheme="%#StatusLine".. (modes[state.mode]or"Error").."#"localsep=theme..""localfile=state.filename---@diagnosticdisable-next-line:unnecessary-ififstate.filestatus~=""thenfile=file..""..state.filestatusendlocalmode=state.modelocalbranch=state.branchlocalselection=state.selectionlocalfiletype=state.filetypelocallocation=state.locationlocaldiagnostics=state.diagnosticslocalstats=state.stats---This defines where to trim the textlocalis_short=vim.go.columns<60ifbranch==""oris_shortthenfile="%<"..fileelsebranch="%<"..branchendlocalfull= {theme,mode,branch,file,"%=",selection,diagnostics,stats,filetype,location,"%*", }localmini= {theme,file,"%=",diagnostics, }localstatusline=is_shortandminiorfullstatusline=vim.tbl_filter(function(v)returnvandv~=""end,statusline)statusline=table.concat(statusline,sep)vim.wo.statusline=statuslineendlocalfunctionrun()enqueue("stats")redraw()vim.defer_fn(run,1000)endrun()localgroup=vim.api.nvim_create_augroup("uStatusline", {clear=false })---Complete redraw is best herevim.api.nvim_create_autocmd({"BufEnter","BufWritePost",}, {group=group,callback=function()enqueue()redraw()end,})---I need a small delay on first load to get the branchvim.api.nvim_create_autocmd("BufEnter", {group=group,once=true,callback=function()vim.defer_fn(function()enqueue("branch")end,100)end,})vim.api.nvim_create_autocmd("CursorMoved", {group=group,callback=function()enqueue("filestatus")enqueue("selection")enqueue("location")redraw()end,})vim.api.nvim_create_autocmd("CursorMovedI", {group=group,callback=function()enqueue("filestatus")enqueue("location")redraw()end,})vim.api.nvim_create_autocmd("DiagnosticChanged", {group=group,callback=function()enqueue("diagnostics")end,})vim.api.nvim_create_autocmd("LspProgress", {group=group,callback=function()enqueue("diagnostics")end,})vim.api.nvim_create_autocmd("ModeChanged", {group=group,callback=function()enqueue("mode")enqueue("filetype")enqueue("selection")redraw()end,})vim.api.nvim_create_autocmd("FileChangedShellPost", {group=group,callback=function()enqueue("branch")enqueue("filename")enqueue("filetype")redraw()end,})vim.api.nvim_create_autocmd("FileType", {group=group,callback=function()enqueue("filetype")redraw()end,})vim.api.nvim_create_autocmd("TermLeave", {group=group,callback=function()enqueue("mode")redraw()end,}) |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1