Skip to content

Commit 11b23a7

Browse files
committed
perf(ui): improve rendering performance and robustness
1 parent 34cfd5d commit 11b23a7

5 files changed

Lines changed: 158 additions & 150 deletions

File tree

lua/opencode/ui/output_window.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ function M.end_update()
3030
end
3131
_update_depth = _update_depth - 1
3232
if _update_depth == 0 and _update_buf then
33-
vim.api.nvim_set_option_value('modifiable', false, { buf = _update_buf })
33+
pcall(vim.api.nvim_set_option_value, 'modifiable', false, { buf = _update_buf })
3434
_update_buf = nil
3535
end
3636
end

lua/opencode/ui/render_state.lua

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,26 @@ local function shift_action(action, delta)
517517
end
518518
end
519519

520+
--- Binary-search sorted ranges for the first entry whose line_start >= from_line.
521+
--- Returns the index, or #ranges + 1 if none qualify.
522+
---@param ranges {[1]: integer, [2]: integer, [3]: string}[]
523+
---@param from_line integer
524+
---@return integer
525+
local function first_range_at_or_after(ranges, from_line)
526+
local lo, hi = 1, #ranges
527+
local result = #ranges + 1
528+
while lo <= hi do
529+
local mid = math.floor((lo + hi) / 2)
530+
if ranges[mid][1] >= from_line then
531+
result = mid
532+
hi = mid - 1
533+
else
534+
lo = mid + 1
535+
end
536+
end
537+
return result
538+
end
539+
520540
function RenderState:shift_all(from_line, delta)
521541
if delta == 0 then
522542
return
@@ -526,18 +546,28 @@ function RenderState:shift_all(from_line, delta)
526546
return
527547
end
528548

549+
-- Build fresh sorted ranges so we can search for the first entry at
550+
-- or after from_line, then iterate only the suffix that needs shifting.
551+
self:_rebuild_ranges()
552+
529553
local shifted = false
530554

531-
for _, msg_data in pairs(self._messages) do
532-
if msg_data.line_start and msg_data.line_start >= from_line then
555+
local msg_ranges = self._message_ranges
556+
local first_msg = first_range_at_or_after(msg_ranges, from_line)
557+
for i = first_msg, #msg_ranges do
558+
local msg_data = self._messages[msg_ranges[i][3]]
559+
if msg_data then
533560
msg_data.line_start = msg_data.line_start + delta
534561
msg_data.line_end = msg_data.line_end + delta
535562
shifted = true
536563
end
537564
end
538565

539-
for _, part_data in pairs(self._parts) do
540-
if part_data.line_start and part_data.line_start >= from_line then
566+
local part_ranges = self._part_ranges
567+
local first_part = first_range_at_or_after(part_ranges, from_line)
568+
for i = first_part, #part_ranges do
569+
local part_data = self._parts[part_ranges[i][3]]
570+
if part_data then
541571
part_data.line_start = part_data.line_start + delta
542572
part_data.line_end = part_data.line_end + delta
543573
shifted = true

lua/opencode/ui/renderer/buffer.lua

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,11 @@ function M.write_formatted_data(formatted_data, part_id, start_line)
145145
if is_insertion then
146146
output_window.set_lines(new_lines, target_line, target_line)
147147
else
148-
-- Append: temporarily add a separating blank line in-place to avoid an
149-
-- O(n) copy, then restore. set_lines is synchronous so this is safe.
148+
-- Append: overlap the last buffer line with our lines
150149
target_line = target_line - 1
151-
new_lines[#new_lines + 1] = ''
152-
output_window.set_lines(new_lines, target_line)
153-
new_lines[#new_lines] = nil
150+
local append_lines = table.move(new_lines, 1, #new_lines, 1, {})
151+
append_lines[#append_lines + 1] = ''
152+
output_window.set_lines(append_lines, target_line)
154153
end
155154

156155
if part_id and formatted_data.actions then
@@ -300,8 +299,8 @@ function M.remove_part(part_id)
300299
return
301300
end
302301
output_window.begin_update()
303-
output_window.clear_extmarks(cached.line_start - 1, cached.line_end)
304-
output_window.set_lines({}, cached.line_start - 1, cached.line_end)
302+
output_window.clear_extmarks(cached.line_start - 1, cached.line_end + 1)
303+
output_window.set_lines({}, cached.line_start, cached.line_end + 1)
305304
output_window.end_update()
306305
ctx.render_state:remove_part(part_id)
307306
end
@@ -362,8 +361,8 @@ function M.remove_message(message_id)
362361
return
363362
end
364363
output_window.begin_update()
365-
output_window.clear_extmarks(cached.line_start - 1, cached.line_end)
366-
output_window.set_lines({}, cached.line_start - 1, cached.line_end)
364+
output_window.clear_extmarks(cached.line_start - 1, cached.line_end + 1)
365+
output_window.set_lines({}, cached.line_start, cached.line_end + 1)
367366
output_window.end_update()
368367
ctx.render_state:remove_message(message_id)
369368
end

lua/opencode/ui/renderer/events.lua

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,6 @@ function M.on_permission_updated(permission)
410410

411411
permission_window.add_permission(permission)
412412
M.render_permissions_display()
413-
buf.rerender_part('permission-display-part')
414413
scroll(true)
415414
end
416415

@@ -432,9 +431,9 @@ function M.on_permission_replied(properties)
432431
if #state.pending_permissions == 0 then
433432
buf.remove_part('permission-display-part')
434433
buf.remove_message('permission-display-message')
434+
else
435+
M.render_permissions_display()
435436
end
436-
437-
buf.rerender_part('permission-display-part')
438437
end
439438

440439
---Handle question.asked — show the question picker UI

0 commit comments

Comments
 (0)