Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

One-file statusline plugin#36955

shushtain started this conversation inShow and tell
Discussion options

One-file statusline plugin

Neovim 0.11.5

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.

Idea

We 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 likeLspProgress may fire 100 times a second, while we could wait a full second to see if the LSP is ready.

  • Redraw statusline passively every 1-2 seconds.
  • Prevent bottlenecks with a queue of updates.

Concept

We 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.1000 is basically what defines passive FPS (1000/1000=1fps). We will force instant redraw on the most crucial changes: mode, location, etc.

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,})

Example

This 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 additionalStatusLine... highlight groups defined, sincemy theme provides them for me.

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,})
You must be logged in to vote

Replies: 0 comments

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Labels
None yet
1 participant
@shushtain

[8]ページ先頭

©2009-2025 Movatter.jp