Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

### Fixed

- The diff drew its cursor line over the text even with `cursorlineopt=number`, and the panel, history and PR checks lists lost their cursor row under that setting. The diff now follows `cursorlineopt`, and the lists always highlight the row

## [0.1.43] — 2026-09-08

### Added

- The PR overview can now be commented on without leaving it. `ga` posts a comment on the pull request, and `gp`/`gq` answer what's under the cursor: into that code thread, or as a new PR comment where GitHub offers no threading. `gq` opens with the comment quoted, and in visual mode quotes just the selection
Expand Down
1 change: 1 addition & 0 deletions lua/differ/history/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ function History:_setup_window()
set_wo(win, "foldcolumn", "0")
set_wo(win, "wrap", false)
set_wo(win, "cursorline", true)
set_wo(win, "cursorlineopt", "line")
if self.position == "left" or self.position == "right" then
set_wo(win, "winfixwidth", true)
else
Expand Down
1 change: 1 addition & 0 deletions lua/differ/panel/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,7 @@ function Panel:_setup_window()
set_wo(win, "foldcolumn", "0")
set_wo(win, "wrap", false)
set_wo(win, "cursorline", true)
set_wo(win, "cursorlineopt", "line")
if self.progress then
-- a `%!` expression so the file-position meter tracks the cursor on each redraw
set_wo(win, "winbar", '%!v:lua.require("differ.ui.winbar").panel()')
Expand Down
1 change: 1 addition & 0 deletions lua/differ/pr/checks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ local function render(checks)
title_pos = "center",
})
set_wo(win, "cursorline", true)
set_wo(win, "cursorlineopt", "line")

local function close()
if vim.api.nvim_win_is_valid(win) then
Expand Down
26 changes: 22 additions & 4 deletions lua/differ/view.lua
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,26 @@ function View:_paint_staged()
end
end

-- 'cursorlineopt' values that highlight the line text; screenline still gets the
-- whole line, an extmark can't target one screen row
local CURSORLINE_TEXT = { line = true, screenline = true, both = true }

-- reads the window's effective values, so a :setlocal inside the diff wins over the global
---@param win integer
---@return boolean
local function highlights_cursor_text(win)
if not vim.api.nvim_get_option_value("cursorline", { scope = "local", win = win }) then
return false
end
local opt = vim.api.nvim_get_option_value("cursorlineopt", { scope = "local", win = win })
for value in opt:gmatch("[^,]+") do
if CURSORLINE_TEXT[value] then
return true
end
end
return false
end

-- repaint our own cursor line above the diff backgrounds, since a no-foreground
-- CursorLine is low-priority and gets buried by them. mirrors the diff line bg
-- (a char-level fill with hl_eol so it spans the whole row past EOL) but at a higher
Expand All @@ -391,9 +411,7 @@ function View:_paint_cursorline()
if not (win and vim.api.nvim_win_is_valid(win)) then
return
end
-- read the window's effective value, so a :setlocal cursorline inside the diff
-- still paints while a global nocursorline leaves the rows clean
if not vim.api.nvim_get_option_value("cursorline", { scope = "local", win = win }) then
if not highlights_cursor_text(win) then
return
end
local row = vim.api.nvim_win_get_cursor(win)[1] - 1
Expand Down Expand Up @@ -632,7 +650,7 @@ function View:_setup_window(winid, bufnr)
-- can't also be buffer-scoped: skip the fire unless this view holds focus
vim.api.nvim_create_autocmd("OptionSet", {
group = group,
pattern = "cursorline",
pattern = { "cursorline", "cursorlineopt" },
callback = function()
if vim.api.nvim_get_current_buf() == bufnr then
self:_paint_cursorline()
Expand Down
13 changes: 13 additions & 0 deletions test/nvim/panel_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,19 @@ describe("panel navigation", function()
assert.is_truthy(vim.tbl_contains(texts, "-3"))
p:close()
end)

it("highlights the cursor row even when cursorlineopt is number only", function()
local had = vim.o.cursorlineopt
vim.o.cursorlineopt = "number"
local p = panel({ fe("a.lua") })
p:open()
local opt =
vim.api.nvim_get_option_value("cursorlineopt", { scope = "local", win = p.winid })
assert.are.equal("line", opt)
assert.are.equal("number", vim.go.cursorlineopt)
p:close()
vim.o.cursorlineopt = had
end)
end)

-- the panel window's position relative to the origin window it was split from
Expand Down
62 changes: 59 additions & 3 deletions test/nvim/view_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1032,13 +1032,14 @@ describe("view cursor-line overlay", function()
local cursor_ns = vim.api.nvim_create_namespace("differ.cursorline")

-- the overlay follows the window's cursorline, and nvim ships it off
local had_cursorline
local had_cursorline, had_cursorlineopt
before_each(function()
had_cursorline = vim.o.cursorline
had_cursorline, had_cursorlineopt = vim.o.cursorline, vim.o.cursorlineopt
vim.o.cursorline = true
vim.o.cursorlineopt = "both"
end)
after_each(function()
vim.o.cursorline = had_cursorline
vim.o.cursorline, vim.o.cursorlineopt = had_cursorline, had_cursorlineopt
end)

local CURSORLINE = {
Expand Down Expand Up @@ -1201,6 +1202,61 @@ describe("view cursor-line overlay", function()
assert.are.same({}, overlay_rows(buf))
v:close()
end)

it("paints no overlay when cursorlineopt only highlights the number", function()
vim.cmd("silent! only")
vim.o.cursorlineopt = "number"
local v = View.new(model("a\nb\nc\n", "a\nB\nc\n"), {
layout = "stacked",
context = math.huge,
deep_diff = { enabled = true },
})
v:open()
local buf, win = v.columns[1].bufnr, v.columns[1].winid
assert.are.same({}, overlay_rows(buf))

vim.api.nvim_win_set_cursor(win, { 1, 0 })
vim.api.nvim_exec_autocmds("CursorMoved", { buffer = buf })
assert.are.same({}, overlay_rows(buf))
v:close()
end)

it("paints the overlay for every cursorlineopt that highlights the line", function()
vim.cmd("silent! only")
local v = View.new(model("a\nb\nc\n", "a\nB\nc\n"), {
layout = "stacked",
context = math.huge,
deep_diff = { enabled = true },
})
v:open()
local buf, win = v.columns[1].bufnr, v.columns[1].winid
local cur = vim.api.nvim_win_get_cursor(win)[1] - 1
for _, opt in ipairs({ "line", "screenline", "line,number", "number,screenline" }) do
vim.api.nvim_set_option_value("cursorlineopt", opt, { scope = "local", win = win })
assert.are.same({ cur }, overlay_rows(buf), opt)
end
v:close()
end)

it("repaints when cursorlineopt changes on the diff window mid-session", function()
vim.cmd("silent! only")
local v = View.new(model("a\nb\nc\n", "a\nB\nc\n"), {
layout = "stacked",
context = math.huge,
deep_diff = { enabled = true },
})
v:open()
local buf, win = v.columns[1].bufnr, v.columns[1].winid
local cur = vim.api.nvim_win_get_cursor(win)[1] - 1
assert.are.same({ cur }, overlay_rows(buf))

vim.api.nvim_set_option_value("cursorlineopt", "number", { scope = "local", win = win })
assert.are.same({}, overlay_rows(buf))

vim.api.nvim_set_option_value("cursorlineopt", "both", { scope = "local", win = win })
assert.are.same({ cur }, overlay_rows(buf))
v:close()
end)
end)

describe("view close guard", function()
Expand Down
Loading