Open-Source Wikis

/

Neovim

/

Systems

/

Windows and screen rendering

neovim/neovim

Windows and screen rendering

Purpose

This subsystem turns the contents of a buffer into a 2-D grid of cells, dispatches that grid as redraw events to attached UIs, and manages the window tree (splits, floats, tabs) inside which buffers are displayed. It is the largest cluster of files in src/nvim/ by line count, and the most performance-sensitive.

Directory layout

src/nvim/
├── window.c (~236k bytes)        Splits, layout, focus, win_T mutators
├── window.h
├── winfloat.c, winfloat.h        Floating window helpers
├── drawscreen.c (~96k bytes)     update_screen, win_update, redraw flags
├── drawscreen.h
├── drawline.c (~123k bytes)      win_line — render one line of one window
├── drawline.h
├── grid.c, grid_defs.h           The internal cell grid
├── ui.c, ui.h, ui_defs.h         Server-side UI multiplexer
├── ui_compositor.c               Composite floats for non-multigrid UIs
├── ui_client.c                   Drives the TUI process
├── popupmenu.c                   <C-x> completion popup, wildmenu
├── statusline.c                  Statusline, winbar, tabline rendering
├── highlight.c, highlight_group.c, highlight_defs.h  Highlight groups + namespaces
├── decoration.c, decoration_provider.c  On-demand decorations from plugins
└── extmark.c                     Backed by marktree; see Marks and extmarks

Key abstractions

Type / function File Description
win_T buffer_defs.h A window: cursor, viewport (w_topline/w_botline), local options, status line.
tabpage_T buffer_defs.h A tab page. Owns a tree of win_Ts in a frame_T.
frame_T buffer_defs.h An internal node in the window split tree.
ScreenGrid grid_defs.h The cell grid. One per window in multigrid mode, one global otherwise.
update_screen() drawscreen.c Top-level redraw. Called when must_redraw is non-zero.
win_update() drawscreen.c Redraw one window's portion of the grid.
win_line() drawline.c Render one line into the grid. The hot path.
ui_call_* ui.c Dispatch a UI event to all attached UIs.
decoration_redraw_* decoration.c Apply extmark + provider decorations to the grid.

How it works

graph TD
    Edit[Edit operation<br/>change.c] --> Flag[redraw_*<br/>flags set]
    Flag --> Loop[update_screen<br/>called by mode check]
    Loop --> WUpd[win_update per window]
    WUpd --> WLine[win_line per visible line]
    WLine --> Decor[decoration_redraw_line]
    Decor --> Mark[marktree query<br/>per-line extmarks]
    Decor --> Prov[decoration providers]
    Decor --> Synt[syntax highlighter]
    WLine --> Grid[ScreenGrid<br/>cell array]
    Grid --> UICall[ui_call_grid_line]
    UICall --> UI[ui.c<br/>dispatch to attached UIs]
    UI --> TUI[TUI / remote UI / ui_attach]

The flow is lazy:

  1. Anything that mutates buffer content sets a redraw flag (UPD_VALID, UPD_CLEAR, UPD_INVERTED, ...).
  2. update_screen() is called from each mode's check callback at a safe time.
  3. update_screen() walks every visible window. For each one, win_update() decides whether the window can be incrementally redrawn (most cases) or needs a full redraw (mode change, viewport scroll, etc.).
  4. win_update() calls win_line() for each visible line. win_line() queries decoration providers, extmarks, and the syntax engine for everything that affects the line's appearance, then writes cells to the ScreenGrid.
  5. As cells are written, ui_call_grid_line posts events to every attached UI.

The reason this is fast on a typical edit: only the lines that changed are passed through win_line(). The reason it is slow on a viewport scroll: every line of the new viewport is.

Window layout

window.c (236 KB, ~7,500 lines) implements the window tree. A tab page contains a top-level frame_T, which is either a leaf (a win_T) or an internal node (a vertical or horizontal split with children). Splits are h-trees of frames; the layout is computed by walking the tree depth-first.

User-facing operations are mapped to tree mutations:

  • :split / :vsplit → split the current frame.
  • <C-w>w, <C-w>h, etc. → traverse and refocus.
  • :close → remove a leaf, possibly collapsing parent frames.
  • :resize, <C-w>= → adjust frame sizes; equalize.

Floating windows (winfloat.c) are not in the frame tree; they are a flat list per tab page with absolute coordinates and z-ordering.

Drawscreen and drawline

drawscreen.c is the top-level coordinator. It owns the must_redraw global and the per-window w_redr_type field. The combinations of these two control whether any redraw happens at all and how aggressive it is.

drawline.c is the hot path. win_line() is ~123 KB of conditional rendering — fold lines, conceal regions, virtual text, line numbers, sign column, fold column, status column, syntax highlights, extmarks, search highlights, listchar handling, double-width characters, line break, breakindent, showbreak, virtual edit, and so on. It is one of the few files where reading the comments first is faster than reading the code.

Grids

ScreenGrid is the in-memory cell buffer. Each cell is (unsigned attribute, char[]bytes, int char_width). The "attribute" is an index into a per-grid attribute table; the table maps to highlight definitions.

In single-grid mode, there is one global grid covering the whole window area. In multigrid mode (negotiated with the UI on attach), each window has its own grid, and the UI is responsible for compositing them. Float windows always use multigrid; the ui_compositor.c exists to flatten multigrid output back to single-grid for UIs that haven't opted in.

Decoration providers

decoration_provider.c lets a plugin register callbacks (on_buf, on_win, on_line, on_end) that the renderer invokes during win_update. The plugin computes highlights/virtual text/signs lazily — only for visible lines. This is what powers tree-sitter highlighting (runtime/lua/vim/treesitter/highlighter.lua), inlay hints, semantic tokens, and most "expensive" decorations.

The contract is strict: callbacks must be api-fast, must not modify the buffer, and must return quickly. Misbehavior triggers obvious slowdowns; the editor logs decoration provider errors but keeps the provider registered.

Highlights

highlight.c and highlight_group.c (~120k bytes combined) implement:

  • Named highlight groups (:hi Comment ...).
  • Namespaces (vim.api.nvim_create_namespace returns an int that scopes highlights to a plugin).
  • The nvim_set_hl() API and its cterm/gui translation.
  • Color resolution for the TUI (RGB → 256-color → 16-color).

Statusline / tabline / winbar

statusline.c (~70k bytes) renders any string that follows Vim's 'statusline' format: %f, %y, %{...}, %#HighlightGroup#, etc. The same code handles the global tabline ('tabline'), per-window winbar ('winbar'), and the status column ('statuscolumn'). The format string is interpreted into a sequence of "items" that are then assembled into the line at draw time.

Integration points

  • Buffer changes call redraw_curbuf_later(...) / redraw_buf_later(...) / redraw_later(...) in drawscreen.c. Anything that should make the screen update must call one of these.
  • Mode transitions call redrawcmd(), update_screen(UPD_NOT_VALID), or similar.
  • Extmarks with hl_group, virt_text, sign_text, conceal, etc., are queried by win_line() via decoration.c.
  • UIs attach via nvim_ui_attach (src/nvim/api/ui.c); the editor publishes the same redraw stream to all of them.

Entry points for modification

  • Add a new way to decorate lines. The right path is almost always extmarks + virt_text or a decoration provider, not a new field in win_line. The decoration API in runtime/lua/vim/api/extmark.c is the place to look.
  • Tweak the redraw scheduling. Most opportunities are in drawscreen.c#update_screen — e.g., checking new redraw_flag bits.
  • Window layout changes. window.c is intricate but well-commented. Adding a new layout primitive (a third orientation, group splits, etc.) means touching frame_T and the layout walkers.
  • Floats with new behavior. winfloat.c is shorter than window.c and easier to extend. New positioning options usually go on FloatConfig.

Key source files

File Purpose
src/nvim/window.c Splits, focus, layout
src/nvim/winfloat.c Floating windows
src/nvim/drawscreen.c Top-level redraw orchestration
src/nvim/drawline.c The per-line renderer
src/nvim/grid.c The ScreenGrid cell buffer
src/nvim/ui.c Server-side multiplexer to attached UIs
src/nvim/ui_compositor.c Float compositing for non-multigrid UIs
src/nvim/decoration.c, decoration_provider.c Plugin-driven decorations
src/nvim/highlight.c, highlight_group.c Highlight groups
src/nvim/popupmenu.c Completion popup, wildmenu
src/nvim/statusline.c Statusline / tabline / winbar / statuscolumn
src/nvim/api/win_config.c nvim_open_win and friends

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Windows and screen rendering – Neovim wiki | Factory