Skip to content

Commit 513c1ba

Browse files
committed
fix(ui/output): preserve fold open state
Preserve which folds were open when updating folds
1 parent e3ed379 commit 513c1ba

2 files changed

Lines changed: 42 additions & 58 deletions

File tree

lua/opencode/ui/output_window.lua

Lines changed: 41 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,12 @@ end
7373
---@param windows OpencodeWindowState?
7474
function M.mounted(windows)
7575
windows = windows or state.windows
76-
return windows and windows.output_buf and windows.output_win and vim.api.nvim_win_is_valid(windows.output_win)
77-
end
78-
79-
---Check if the output buffer is valid (even if window is hidden)
80-
---@param windows? OpencodeWindowState
81-
---@return boolean
82-
function M.buffer_valid(windows)
83-
windows = windows or state.windows
84-
return windows and windows.output_buf and vim.api.nvim_buf_is_valid(windows.output_buf)
76+
return windows
77+
and windows.output_buf
78+
and windows.output_win
79+
and vim.api.nvim_win_is_valid(windows.output_win)
80+
and vim.api.nvim_buf_is_valid(windows.output_buf)
81+
and vim.api.nvim_win_get_buf(windows.output_win) == windows.output_buf
8582
end
8683

8784
---Check if the cursor in the output window is at (or was at) the bottom of
@@ -320,74 +317,61 @@ end
320317
_G.opencode_fold_expr = M.fold_expr
321318
_G.opencode_fold_text = M.fold_text
322319

320+
function M.get_open_fold_starts(win, buf)
321+
if not win or not buf then
322+
return {}
323+
end
324+
325+
local ok, prev_folds = pcall(vim.api.nvim_buf_get_var, buf, 'opencode_folds')
326+
if not ok or not prev_folds then
327+
return {}
328+
end
329+
330+
local was_open = {}
331+
vim.api.nvim_win_call(win, function()
332+
for _, range in ipairs(prev_folds) do
333+
if vim.fn.foldclosed(range.from) == -1 then
334+
was_open[range.from] = true
335+
end
336+
end
337+
end)
338+
339+
return was_open
340+
end
341+
323342
---Set the folds for the output buffer
324343
---@param fold_ranges table<{from: number, to: number}>
325344
function M.set_folds(fold_ranges)
326345
local windows = state.windows
327-
if not windows or not windows.output_buf then
346+
if not M.mounted() then
328347
return
329348
end
349+
---@cast windows OpencodeWindowState
330350

331351
local buf = windows.output_buf
332-
if not vim.api.nvim_buf_is_valid(buf) then
333-
return
334-
end
335-
352+
local win = windows.output_win
336353
local folds = fold_ranges or {}
337354

338-
local ok_prev, prev_folds = pcall(vim.api.nvim_buf_get_var, buf, 'opencode_folds')
339-
-- Only consider folds identical if we successfully read the previous state.
340-
if ok_prev and #folds == #prev_folds and vim.deep_equal(prev_folds, folds) then
355+
local ok, prev_folds = pcall(vim.api.nvim_buf_get_var, buf, 'opencode_folds')
356+
prev_folds = ok and prev_folds or {}
357+
358+
if #folds == #prev_folds and vim.deep_equal(prev_folds, folds) then
341359
return
342360
end
343-
prev_folds = ok_prev and prev_folds or {}
344361

345-
local win = windows.output_win
346-
local win_owns_buf = win and vim.api.nvim_win_is_valid(win) and vim.api.nvim_win_get_buf(win) == buf
347-
348-
-- Track which folds were open
349-
local open = {}
350-
if win_owns_buf then
351-
-- Be defensive: the window may become invalid between the earlier check and
352-
-- the call, so use pcall to avoid raising an error.
353-
pcall(vim.api.nvim_win_call, win, function()
354-
for _, range in ipairs(prev_folds) do
355-
if vim.fn.foldclosed(range.from) == -1 then
356-
open[range.from] = true
357-
end
358-
end
359-
end)
360-
end
362+
local was_open = M.get_open_fold_starts(win, buf)
361363

362364
vim.api.nvim_buf_set_var(buf, 'opencode_folds', folds)
363365

364-
if not win_owns_buf then
365-
return
366-
end
367-
368-
pcall(vim.api.nvim_win_call, win, function()
366+
vim.api.nvim_win_call(win, function()
369367
local view = vim.fn.winsaveview()
370-
-- Use zX to reset manual folds consistently (preserve previous behavior).
371368
vim.cmd('silent! normal! zX')
372-
-- 1. Create/update folds (your foldexpr / markers should already handle this)
373-
-- So we only control open/close state
374-
375369
for _, range in ipairs(folds) do
376-
local is_open = open[range.from]
370+
local is_open = was_open[range.from]
371+
local cmd = is_open and 'zo' or 'zc'
377372

378-
if is_open then
379-
-- ensure it's open
380-
if vim.fn.foldclosed(range.from) ~= -1 then
381-
vim.fn.cursor(range.from, 1)
382-
vim.cmd('silent! normal! zo')
383-
end
384-
else
385-
-- ensure it's closed
386-
if vim.fn.foldclosed(range.from) == -1 then
387-
vim.fn.cursor(range.from, 1)
388-
vim.cmd('silent! normal! zc')
389-
end
390-
end
373+
vim.fn.cursor(range.from, 1)
374+
vim.cmd('silent! normal! ' .. cmd)
391375
end
392376

393377
vim.fn.winrestview(view)

lua/opencode/ui/renderer.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ end
420420
---Replace the entire output buffer with formatted output data
421421
---@param output_data Output
422422
function M.render_output(output_data)
423-
if not output_window.buffer_valid() then
423+
if not output_window.mounted() then
424424
return
425425
end
426426
output_window.set_lines(output_data.lines or {})

0 commit comments

Comments
 (0)