|
73 | 73 | ---@param windows OpencodeWindowState? |
74 | 74 | function M.mounted(windows) |
75 | 75 | 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 |
85 | 82 | end |
86 | 83 |
|
87 | 84 | ---Check if the cursor in the output window is at (or was at) the bottom of |
@@ -320,74 +317,61 @@ end |
320 | 317 | _G.opencode_fold_expr = M.fold_expr |
321 | 318 | _G.opencode_fold_text = M.fold_text |
322 | 319 |
|
| 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 | + |
323 | 342 | ---Set the folds for the output buffer |
324 | 343 | ---@param fold_ranges table<{from: number, to: number}> |
325 | 344 | function M.set_folds(fold_ranges) |
326 | 345 | local windows = state.windows |
327 | | - if not windows or not windows.output_buf then |
| 346 | + if not M.mounted() then |
328 | 347 | return |
329 | 348 | end |
| 349 | + ---@cast windows OpencodeWindowState |
330 | 350 |
|
331 | 351 | 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 |
336 | 353 | local folds = fold_ranges or {} |
337 | 354 |
|
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 |
341 | 359 | return |
342 | 360 | end |
343 | | - prev_folds = ok_prev and prev_folds or {} |
344 | 361 |
|
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) |
361 | 363 |
|
362 | 364 | vim.api.nvim_buf_set_var(buf, 'opencode_folds', folds) |
363 | 365 |
|
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() |
369 | 367 | local view = vim.fn.winsaveview() |
370 | | - -- Use zX to reset manual folds consistently (preserve previous behavior). |
371 | 368 | 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 | | - |
375 | 369 | 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' |
377 | 372 |
|
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) |
391 | 375 | end |
392 | 376 |
|
393 | 377 | vim.fn.winrestview(view) |
|
0 commit comments