#42182·neovim

Wrapped inline virtual text with 'linebreak' miscounts line height: cursor drawn too high, stale rows on scroll

Author: gaardhusCreated Sep 30, 2026Updated Oct 1, 2026
Labelsdisplaymarks

Problem

With wrap and linebreak set, a long virt_text_pos = "inline" extmark that wraps over several screen rows makes Neovim miscount the screen rows of its line. Drawing and the row count disagree about where the wrapped rows break. This has two visible effects:

  1. For text after the extmark on the same line, the cursor is drawn several rows too high, inside the virtual text.
  2. When scrolling, rows below the line are left stale: the next line's number and first rows go missing, and text is drawn in the wrong place until a full redraw (<C-l>).

nvim_win_text_height() reports the wrong height for the line too. With nolinebreak all of these are correct.

Reproduces on v0.12.5 and v0.13.0-dev-1761+gd5e7c7e55c.

Steps to reproduce

nvim --clean -u repro.lua in a terminal 90 columns wide and 30 rows high:

lua
vim.o.wrap = true
vim.o.linebreak = true
vim.o.number = true
local line = "The harbour town sits at the mouth of a slow river, where fishing boats come in each morning with the tide and the market opens before sunrise. Narrow streets climb the hill behind the quay, lined with bakeries, net menders and a small museum about the old lighthouse. In winter the ferry runs twice a week and most visitors stay away. Travellers say the best view of the bay is from the chapel at the top of the hill. Local guides recommend arriving by train and walking the coastal path from the next village, which takes about three hours and passes several sheltered beaches."
local lines = { line }
for _ = 1, 4 do
  vim.list_extend(lines, { line, "" })
end
vim.api.nvim_buf_set_lines(0, 0, -1, false, lines)
local col = line:find("stay away.", 1, true) + #"stay away." - 1
vim.api.nvim_buf_set_extmark(0, vim.api.nvim_create_namespace("repro"), 0, col, {
  virt_text = { { "In summer the ferry leaves every hour, the cafes put tables out along the harbour wall, and the population of the town roughly doubles as holiday homes fill up. Booking a room several weeks ahead is wise, especially around the regatta in early August.", "DiffAdd" } },
  virt_text_pos = "inline",
})
vim.fn.cursor(1, line:find("Travellers", 1, true))
  1. On startup, the cursor should be on the T of "Travellers" in line 1.
  2. :lua print(vim.api.nvim_win_text_height(0, { start_row = 0, end_row = 0 }).all)
  3. Press G, then gg.

Expected behavior

  1. The cursor is drawn on the T of "Travellers". Instead it is drawn at the start of a row of the inline virtual text: three rows too high on v0.12.5 and two on nightly. The offset lasts for every position after the extmark, up to the end of the buffer line.
  2. The height is the 10 rows line 1 is drawn over. Instead it prints 7 on v0.12.5 and 8 on nightly.
  3. The screen is the same as it was on startup. Instead, line 2's number and first rows are missing: the rest of line 2 is drawn directly under line 1, without a number. <C-l> redraws it correctly.

:set nolinebreak fixes all three.

Nvim version (nvim -v)

NVIM v0.12.5 (also v0.13.0-dev-1761+gd5e7c7e55c)

Vim (not Nvim) behaves the same?

yes, Vim 9.2.1046

Operating system/version

Linux 7.2.6-arch2-1

Terminal name/version

alacritty 0.17.0 (94e7c887)

$TERM environment variable

tmux-256color

Installation

Arch User Repository (AUR)