Skip to content

Commit eabd6a5

Browse files
committed
fix: markdown flashing when updating parts
1 parent 9151cda commit eabd6a5

4 files changed

Lines changed: 42 additions & 54 deletions

File tree

lua/opencode/ui/renderer.lua

Lines changed: 34 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ local scheduler = require('opencode.ui.renderer.scheduler')
1111

1212
local M = {}
1313

14-
local trigger_on_data_rendered
15-
1614
local function with_suppressed_scroll_tracking(fn)
1715
return output_window.with_suppressed_scroll_tracking(fn)
1816
end
@@ -71,32 +69,15 @@ local function apply_render_plan(render_plan, should_scroll)
7169
return true
7270
end
7371

74-
trigger_on_data_rendered()
72+
M.trigger_on_data_rendered()
7573
return true
7674
end
7775

7876
-- Expose event handlers on M so tests can call them directly and subscriptions
7977
-- can be stubbed cleanly (e.g. stub(renderer, '_render_full_session_data'))
8078
M.on_session_updated = events.on_session_updated
8179

82-
trigger_on_data_rendered = require('opencode.util').debounce(function()
83-
local cb_type = type(config.ui.output.rendering.on_data_rendered)
84-
if cb_type == 'boolean' then
85-
return
86-
end
87-
if not state.windows or not state.windows.output_buf or not state.windows.output_win then
88-
return
89-
end
90-
if cb_type == 'function' then
91-
pcall(config.ui.output.rendering.on_data_rendered, state.windows.output_buf, state.windows.output_win)
92-
elseif vim.fn.exists(':RenderMarkdown') > 0 then
93-
vim.cmd(':RenderMarkdown')
94-
elseif vim.fn.exists(':Markview') > 0 then
95-
vim.cmd(':Markview render ' .. state.windows.output_buf)
96-
end
97-
end, config.ui.output.rendering.markdown_debounce_ms or 250)
98-
99-
local function trigger_on_data_rendered_now()
80+
local function invoke_on_data_rendered()
10081
local cb_type = type(config.ui.output.rendering.on_data_rendered)
10182
if cb_type == 'boolean' then
10283
return
@@ -113,8 +94,13 @@ local function trigger_on_data_rendered_now()
11394
end
11495
end
11596

97+
M.trigger_on_data_rendered =
98+
require('opencode.util').debounce(invoke_on_data_rendered, config.ui.output.rendering.markdown_debounce_ms or 250)
99+
116100
---Reset all renderer state and clear the output buffer
117-
function M.reset()
101+
---@param opts? { trigger_on_data_rendered?: boolean }
102+
function M.reset(opts)
103+
opts = opts or {}
118104
ctx:reset()
119105
output_window.clear()
120106

@@ -127,7 +113,9 @@ function M.reset()
127113
permission_window.clear_all()
128114
state.renderer.reset()
129115

130-
trigger_on_data_rendered_now()
116+
if opts.trigger_on_data_rendered ~= false then
117+
invoke_on_data_rendered()
118+
end
131119
end
132120

133121
---Unsubscribe from all events and reset
@@ -154,22 +142,22 @@ function M.setup_subscriptions(subscribe)
154142
end
155143

156144
local subs = {
157-
{ 'session.updated', events.on_session_updated },
158-
{ 'session.compacted', events.on_session_compacted },
159-
{ 'session.error', events.on_session_error },
160-
{ 'message.updated', events.on_message_updated },
161-
{ 'message.removed', events.on_message_removed },
162-
{ 'message.part.updated', events.on_part_updated },
163-
{ 'message.part.removed', events.on_part_removed },
164-
{ 'permission.updated', events.on_permission_updated },
165-
{ 'permission.asked', events.on_permission_updated },
166-
{ 'permission.replied', events.on_permission_replied },
167-
{ 'question.asked', events.on_question_asked },
168-
{ 'question.replied', events.clear_question_display },
169-
{ 'question.rejected', events.clear_question_display },
170-
{ 'file.edited', events.on_file_edited },
171-
{ 'custom.restore_point.created', events.on_restore_points },
172-
{ 'custom.emit_events.finished', M.on_emit_events_finished },
145+
{ 'session.updated', events.on_session_updated },
146+
{ 'session.compacted', events.on_session_compacted },
147+
{ 'session.error', events.on_session_error },
148+
{ 'message.updated', events.on_message_updated },
149+
{ 'message.removed', events.on_message_removed },
150+
{ 'message.part.updated', events.on_part_updated },
151+
{ 'message.part.removed', events.on_part_removed },
152+
{ 'permission.updated', events.on_permission_updated },
153+
{ 'permission.asked', events.on_permission_updated },
154+
{ 'permission.replied', events.on_permission_replied },
155+
{ 'question.asked', events.on_question_asked },
156+
{ 'question.replied', events.clear_question_display },
157+
{ 'question.rejected', events.clear_question_display },
158+
{ 'file.edited', events.on_file_edited },
159+
{ 'custom.restore_point.created', events.on_restore_points },
160+
{ 'custom.emit_events.finished', M.on_emit_events_finished },
173161
}
174162

175163
for _, sub in ipairs(subs) do
@@ -214,7 +202,7 @@ end
214202
---Called after a full session fetch or when revert state changes
215203
---@param session_data OpencodeMessage[]
216204
function M._render_full_session_data(session_data)
217-
M.reset()
205+
M.reset({ trigger_on_data_rendered = false })
218206

219207
if not state.active_session then
220208
return
@@ -308,15 +296,13 @@ function M.scroll_to_bottom(force)
308296
local prev_line_count = ctx.prev_line_count
309297
ctx.prev_line_count = line_count
310298

311-
trigger_on_data_rendered()
312-
313299
local should_scroll = force
314300
or prev_line_count == 0
315301
or config.ui.output.always_scroll_to_bottom
316302
or (function()
317-
local ok_cursor, cursor = pcall(vim.api.nvim_win_get_cursor, output_win)
318-
return ok_cursor and cursor and (cursor[1] >= prev_line_count or cursor[1] >= line_count)
319-
end)()
303+
local ok_cursor, cursor = pcall(vim.api.nvim_win_get_cursor, output_win)
304+
return ok_cursor and cursor and (cursor[1] >= prev_line_count or cursor[1] >= line_count)
305+
end)()
320306

321307
if should_scroll then
322308
with_suppressed_scroll_tracking(function()
@@ -328,7 +314,7 @@ function M.scroll_to_bottom(force)
328314
end)
329315
end
330316

331-
trigger_on_data_rendered()
317+
M.trigger_on_data_rendered()
332318
end
333319

334320
---Re-render the permission display when focus changes (updates shortcut hints)
@@ -345,11 +331,11 @@ function M.on_session_changed(_, new, old)
345331
return
346332
end
347333

348-
if new and ctx.last_rendered_session_id == new.id and state.messages ~= nil then
334+
if new and ctx.last_rendered_session_id == new.id and state.messages ~= nil then
349335
return
350336
end
351337

352-
M.reset()
338+
M.reset({ trigger_on_data_rendered = not new })
353339
if new then
354340
M.render_full_session()
355341
end

lua/opencode/ui/renderer/buffer_ops.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ local function apply_changed_line_patches(previous_output, next_output)
179179

180180
if output_window.buffer_valid() then
181181
output_window.begin_update()
182-
output_window.clear_extmarks(0, -1, true)
182+
output_window.clear_extmarks(0, -1)
183183
for _, patch in ipairs(patches) do
184184
local start_line = patch.old_start_line + line_delta
185185
local old_end_line_exclusive = patch.old_end_line_exclusive + line_delta
@@ -216,7 +216,7 @@ local function apply_block_patch(visible_render, patch)
216216

217217
if output_window.buffer_valid() then
218218
output_window.begin_update()
219-
output_window.clear_extmarks(patch.start_line, patch.old_end_line_exclusive, true)
219+
output_window.clear_extmarks(patch.start_line, patch.old_end_line_exclusive)
220220
output_window.set_lines(replacement.lines or {}, patch.start_line, patch.old_end_line_exclusive)
221221
output_window.set_extmarks(replacement.extmarks, patch.start_line)
222222
output_window.end_update()

lua/opencode/util.lua

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,9 @@ function M.debounce(func, delay)
348348
end
349349
local args = { ... }
350350
timer = vim.defer_fn(function()
351-
func(unpack(args))
351+
vim.schedule(function()
352+
func(unpack(args))
353+
end)
352354
end, delay or 100)
353355
end
354356
end
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ local backend = require('opencode.ui.renderer.buffer_ops')
33
local ctx = require('opencode.ui.renderer.ctx')
44
local state = require('opencode.state')
55

6-
describe('renderer backend', function()
6+
describe('renderer buffer_ops', function()
77
local original_set_lines
88
local original_clear_extmarks
99
local original_set_extmarks
@@ -224,7 +224,7 @@ describe('renderer backend', function()
224224

225225
assert.same({ 'same', 'changed', 'same', '' }, output.lines)
226226
assert.same('begin', calls[1])
227-
assert.same({ 'clear', 1, 2, true }, calls[2])
227+
assert.same({ 'clear', 1, 2 }, calls[2])
228228
assert.same({ 'lines', { 'changed' }, 1, 2 }, calls[3])
229229
assert.same({ 'extmarks', {}, 1 }, calls[4])
230230
assert.same('end', calls[5])
@@ -282,7 +282,7 @@ describe('renderer backend', function()
282282

283283
assert.same({ 'header', 'new body', 'footer', '' }, output.lines)
284284
assert.same('begin', calls[1])
285-
assert.same({ 'clear', 0, -1, true }, calls[2])
285+
assert.same({ 'clear', 0, -1 }, calls[2])
286286
assert.same({ 'lines', { 'new body' }, 1, 2 }, calls[3])
287287
assert.same({ 'extmarks', {}, nil }, calls[4])
288288
assert.same('end', calls[5])

0 commit comments

Comments
 (0)