Preserve semantic highlights during mid-edit via decoration provider #22
Labels
No labels
autorelease: pending
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
upstream/digest
upstream/pr
wontfix
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
barrettruth/canola.nvim#22
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
When editing file or directory names in a canola buffer (e.g. renaming with
cw), semantic highlights —OilDir,OilFile,OilExecutable,OilLink,OilHidden, permission column bits, mtime, size — disappear mid-edit and only return after:wtriggers a full re-render. This is because highlights are currently applied as static extmarks vianvim_buf_set_extmark()duringutil.set_highlights(). When the user edits a line, those extmarks shift or are invalidated by the buffer change.stevearc noted this as a "technical limitation" of extmarks (stevearc/oil.nvim#254). That is accurate for the current static approach, but
nvim_set_decoration_provideroffers a different architecture that sidesteps the problem.Upstream context
Proposed solution:
nvim_set_decoration_providerReplace static extmarks with a decoration provider — a Neovim API used by gitsigns.nvim and the built-in treesitter highlighter. The provider registers callbacks that fire during every redraw cycle:
on_win(ns, winid, bufnr, toprow, botrow)— called once per visible window range each redrawon_line(ns, winid, bufnr, row)— called per visible line within that rangeHighlights become ephemeral: recalculated each frame, only for the lines currently visible. The provider never writes to buffer content and never stores extmarks persistently. Because the highlights are recomputed on every redraw rather than stored, editing a line does not invalidate them — the next frame simply recomputes them from the current parse result.
Flow per visible line
parser.parse_line)cache.get_entry_by_id)nvim_buf_set_extmarkwithephemeral = trueHighlight cache
A per-buffer table (
bufnr → lnum → { hl_group, col_start, col_end }[]) stores the highlight metadata produced during the last full render. The decoration provider reads this cache for column highlights (icon hl, permission bits, mtime, size) that require column layout knowledge to compute accurately. For type-based highlights (OilDir,OilFile, etc.) the provider can recompute directly from the cache entry without the layout table.Affected highlight groups
OilDir,OilFile,OilExecutable,OilLink,OilOrphan,OilHiddenOilExecutableHidden,OilLinkTarget,OilLinkTargetErrorOilMtime,OilSize, and any other column-specific groupshighlight_filenamecallbacksInteraction with virtual text columns (#142)
Virtual text columns (issue #142) also need decoration-provider infrastructure to render without modifying buffer content. The same provider registration serves both: the
on_linecallback handles semantic highlights for existing columns and virtual text rendering for virtual columns. Implementing this issue first establishes the provider pattern that #142 builds on.Performance
The provider processes only visible lines per frame, not the entire buffer. For a directory with thousands of entries, only the ~50 visible lines are touched per redraw. Cache lookup by lnum is O(1). This is comparable to or faster than the current approach, which applies extmarks for every entry in the buffer on each render, including off-screen entries.
The main cost is the ID prefix parse on every redraw for every visible line. This can be optimized by caching the last-seen line text and skipping the parse if the line is unchanged since the previous frame.
Key files
lua/oil/util.lua—set_highlights()(line 366),render_table()(line 319)lua/oil/view.lua—format_entry_cols()(line 892),render_buffer()(line 749)lua/oil/mutator/parser.lua—parse_line()for ID prefix extractionsyntax/oil.vim— concealment of the ID prefix (unchanged by this work)