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
9 changes: 9 additions & 0 deletions lua/opencode/ui/output_window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,15 @@ function M.setup_autocmds(windows, group)
end,
})

vim.api.nvim_create_autocmd('TabEnter', {
group = group,
callback = function()
if state.ui.is_window_in_current_tab(windows.output_win) then
require('opencode.ui.renderer.flush').resume_deferred_rendering()
end
end,
})

vim.api.nvim_create_autocmd('BufEnter', {
group = group,
buffer = windows.output_buf,
Expand Down
3 changes: 3 additions & 0 deletions lua/opencode/ui/renderer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,9 @@ function M.scroll_to_bottom(force)
if not vim.api.nvim_win_is_valid(output_win) then
return
end
if not state.ui.is_window_in_current_tab(output_win) then
return
end

if force or config.ui.output.always_scroll_to_bottom or output_window.is_at_bottom(output_win) then
scroll.scroll_win_to_bottom(output_win, output_buf)
Expand Down
42 changes: 38 additions & 4 deletions lua/opencode/ui/renderer/flush.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ local append = require('opencode.ui.renderer.append')
local M = {}
local warned_part_render_error = false

local function output_window_is_in_background_tab()
local output_win = state.windows and state.windows.output_win
return output_win
and vim.api.nvim_win_is_valid(output_win)
and not state.ui.is_window_in_current_tab(output_win)
end

---@param part_id string
---@param message_id string|nil
---@param err any
Expand Down Expand Up @@ -462,15 +469,27 @@ local function do_trigger_on_data_rendered()
if not state.windows or not state.windows.output_buf or not state.windows.output_win then
return
end
vim.b[state.windows.output_buf].opencode_markdown_namespace = output_window.markdown_namespace
local output_buf = state.windows.output_buf
local output_win = state.windows.output_win
if
not vim.api.nvim_buf_is_valid(output_buf)
or not vim.api.nvim_win_is_valid(output_win)
or vim.api.nvim_win_get_buf(output_win) ~= output_buf
then
return
end
vim.b[output_buf].opencode_markdown_namespace = output_window.markdown_namespace
if cb_type == 'function' then
pcall(config.ui.output.rendering.on_data_rendered, state.windows.output_buf, state.windows.output_win)
pcall(config.ui.output.rendering.on_data_rendered, output_buf, output_win)
elseif not state.ui.is_window_in_current_tab(output_win) then
ctx.markdown_render_scheduled = true
return
elseif vim.fn.exists(':RenderMarkdown') > 0 then
vim.api.nvim_buf_call(state.windows.output_buf, function()
vim.api.nvim_win_call(output_win, function()
vim.cmd(':RenderMarkdown buf_enable')
end)
elseif vim.fn.exists(':Markview') > 0 then
vim.cmd(':Markview render ' .. state.windows.output_buf)
vim.cmd(':Markview render ' .. output_buf)
end
end

Expand Down Expand Up @@ -509,6 +528,9 @@ function M.end_bulk_mode()
if not ctx.bulk_mode then
return
end
if output_window_is_in_background_tab() then
return
end
ctx.bulk_mode = false
local lines = ctx.bulk_buffer_lines
if #lines == 0 then
Expand Down Expand Up @@ -554,11 +576,23 @@ end

---Flush all pending renderer changes to the output buffer.
function M.flush()
if output_window_is_in_background_tab() then
return
end
local pending = snapshot_pending()
local applied = apply_pending(pending, new_formatter_context())
if applied and not ctx.bulk_mode then
M.request_on_data_rendered()
end
end

---Apply renderer work deferred while the output window was in another tab.
function M.resume_deferred_rendering()
M.flush()
if ctx.bulk_mode then
M.end_bulk_mode()
end
M.flush_pending_on_data_rendered()
end

return M
62 changes: 60 additions & 2 deletions tests/unit/services_session_runtime_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ describe('opencode.services.session_runtime', function()
end)

describe('markdown rendering metadata', function()
it('stores the markdown namespace on the output buffer before rendering', function()
it('defers markdown rendering until the output tab becomes current', function()
local output_window = require('opencode.ui.output_window')
local buf = vim.api.nvim_create_buf(false, true)
local win = vim.api.nvim_open_win(buf, false, {
Expand All @@ -671,9 +671,19 @@ describe('opencode.services.session_runtime', function()
state.ui.set_windows({ output_buf = buf, output_win = win })
vim.api.nvim_buf_set_var(buf, 'opencode_markdown_namespace', 0)

local output_tab = vim.api.nvim_get_current_tabpage()
vim.cmd('tabnew')
local current_tab = vim.api.nvim_get_current_tabpage()
local current_win = vim.api.nvim_get_current_win()

local defer_stub = stub(vim, 'defer_fn').invokes(function(cb)
cb()
return 1
return {
is_closing = function()
return false
end,
close = function() end,
}
end)
local original_exists = vim.fn.exists
vim.fn.exists = function(name)
Expand All @@ -687,12 +697,60 @@ describe('opencode.services.session_runtime', function()
flush.trigger_on_data_rendered()

assert.equals(output_window.markdown_namespace, vim.b[buf].opencode_markdown_namespace)
assert.stub(cmd_stub).was_not_called()
assert.equals(current_tab, vim.api.nvim_get_current_tabpage())
assert.equals(current_win, vim.api.nvim_get_current_win())

vim.api.nvim_set_current_tabpage(output_tab)
flush.flush_pending_on_data_rendered()

assert.stub(cmd_stub).was_called_with(':RenderMarkdown buf_enable')

cmd_stub:revert()
defer_stub:revert()
vim.fn.exists = original_exists
state.ui.set_windows(nil)
vim.api.nvim_set_current_tabpage(current_tab)
vim.cmd('tabclose')
pcall(vim.api.nvim_win_close, win, true)
pcall(vim.api.nvim_buf_delete, buf, { force = true })
end)

it('defers output buffer writes while the output window is in another tab', function()
local ctx = require('opencode.ui.renderer.ctx')
local buf = vim.api.nvim_create_buf(false, true)
local win = vim.api.nvim_open_win(buf, false, {
relative = 'editor',
width = 20,
height = 5,
row = 0,
col = 0,
style = 'minimal',
})
local output_tab = vim.api.nvim_get_current_tabpage()

state.ui.set_windows({ output_buf = buf, output_win = win })
ctx:reset()
flush.begin_bulk_mode()
ctx.bulk_buffer_lines = { 'deferred output' }

vim.cmd('tabnew')
local background_tab = vim.api.nvim_get_current_tabpage()
flush.end_bulk_mode()

assert.same({ '' }, vim.api.nvim_buf_get_lines(buf, 0, -1, false))
assert.is_true(ctx.bulk_mode)

vim.api.nvim_set_current_tabpage(output_tab)
flush.resume_deferred_rendering()

assert.same({ 'deferred output', '' }, vim.api.nvim_buf_get_lines(buf, 0, -1, false))
assert.is_false(ctx.bulk_mode)

ctx:reset()
state.ui.set_windows(nil)
vim.api.nvim_set_current_tabpage(background_tab)
vim.cmd('tabclose')
pcall(vim.api.nvim_win_close, win, true)
pcall(vim.api.nvim_buf_delete, buf, { force = true })
end)
Expand Down
Loading