Skip to content

Commit 7b6c54d

Browse files
committed
refactor: clean up renderer/formatter
Now that we have one code path, I could remove some wrapper functions. Also cleaned up some times / variable names to be more consistent.
1 parent 4a4b6d0 commit 7b6c54d

File tree

2 files changed

+51
-99
lines changed

2 files changed

+51
-99
lines changed

lua/opencode/ui/formatter.lua

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -242,25 +242,28 @@ function M._format_error(output, message)
242242
M._format_callout(output, 'ERROR', vim.inspect(message.error))
243243
end
244244

245-
---@param output Output Output object to write to
246-
---@param message MessageInfo
245+
---@param message OpencodeMessage
247246
---@param msg_idx number Message index in the session
248-
function M._format_message_header(output, message, msg_idx)
249-
local role = message.role or 'unknown'
250-
local icon = message.role == 'user' and icons.get('header_user') or icons.get('header_assistant')
247+
---@return Output
248+
function M.format_message_header(message, msg_idx)
249+
local output = Output.new()
250+
251+
output:add_lines(M.separator)
252+
local role = message.info.role or 'unknown'
253+
local icon = message.info.role == 'user' and icons.get('header_user') or icons.get('header_assistant')
251254

252-
local time = message.time and message.time.created or nil
255+
local time = message.info.time and message.info.time.created or nil
253256
local time_text = (time and ' (' .. util.time_ago(time) .. ')' or '')
254257
local role_hl = 'OpencodeMessageRole' .. role:sub(1, 1):upper() .. role:sub(2)
255-
local model_text = message.modelID and ' ' .. message.modelID or ''
256-
local debug_text = config.debug and ' [' .. message.id .. ']' or ''
258+
local model_text = message.info.modelID and ' ' .. message.info.modelID or ''
259+
local debug_text = config.debug and ' [' .. message.info.id .. ']' or ''
257260

258261
output:add_empty_line()
259262
output:add_metadata({ msg_idx = msg_idx, part_idx = 1, role = role, type = 'header' })
260263

261264
local display_name
262265
if role == 'assistant' then
263-
local mode = message.mode
266+
local mode = message.info.mode
264267
if mode and mode ~= '' then
265268
display_name = mode:upper()
266269
else
@@ -291,6 +294,7 @@ function M._format_message_header(output, message, msg_idx)
291294
})
292295

293296
output:add_line('')
297+
return output
294298
end
295299

296300
---@param output Output Output object to write to
@@ -660,23 +664,28 @@ function M._add_vertical_border(output, start_line, end_line, hl_group, win_col)
660664
end
661665
end
662666

663-
---Internal function that formats a message part into an existing output object
664-
---@param output Output Output object to write to
665-
---@param part MessagePart
666-
---@param message_info {msg_idx: number, part_idx: number, role: string, message: table}
667-
function M._format_part(output, part, message_info)
667+
---Formats a single message part and returns the resulting output object
668+
---@param part MessagePart The part to format
669+
---@param role 'user'|'assistant'|'system' The role, user or assistant, that created this part
670+
---@param msg_idx integer The index of the message in state.messages
671+
---@param part_idx integer The index of the part in state.messages[msg_idx].parts
672+
---@return Output
673+
function M.format_part(part, role, msg_idx, part_idx)
674+
local output = Output.new()
675+
676+
-- FIXME: do we need metadata? it looks like maybe only for snapshots?
668677
local metadata = {
669-
msg_idx = message_info.msg_idx,
670-
part_idx = message_info.part_idx,
671-
role = message_info.role,
678+
msg_idx = msg_idx,
679+
part_idx = part_idx,
680+
role = role,
672681
type = part.type,
673682
snapshot = part.snapshot,
674683
}
675684
output:add_metadata(metadata)
676685

677686
local content_added = false
678687

679-
if message_info.role == 'user' then
688+
if role == 'user' then
680689
if part.type == 'text' and part.text then
681690
if part.synthetic == true then
682691
M._format_selection_context(output, part)
@@ -692,7 +701,7 @@ function M._format_part(output, part, message_info)
692701
M._add_vertical_border(output, file_line - 1, file_line, 'OpencodeMessageRoleUser', -3)
693702
content_added = true
694703
end
695-
elseif message_info.role == 'assistant' then
704+
elseif role == 'assistant' then
696705
if part.type == 'text' and part.text then
697706
M._format_assistant_message(output, vim.trim(part.text))
698707
content_added = true
@@ -708,26 +717,6 @@ function M._format_part(output, part, message_info)
708717
if content_added then
709718
output:add_empty_line()
710719
end
711-
end
712-
713-
---Public function that formats a single message part and returns a new output object
714-
---@param part MessagePart
715-
---@param message_info {msg_idx: number, part_idx: number, role: string, message: table}
716-
---@return Output
717-
function M.format_part_single(part, message_info)
718-
local output = Output.new()
719-
M._format_part(output, part, message_info)
720-
return output
721-
end
722-
723-
---@param message OpencodeMessage
724-
---@param msg_idx number
725-
---@return Output
726-
function M.format_message_header_single(message, msg_idx)
727-
local output = Output.new()
728-
729-
output:add_lines(M.separator)
730-
M._format_message_header(output, message.info, msg_idx)
731720

732721
return output
733722
end

lua/opencode/ui/renderer.lua

Lines changed: 23 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -374,15 +374,10 @@ end
374374

375375
---Event handler for message.updated events
376376
---Creates new message or updates existing message info
377-
---@param properties {info: MessageInfo} Event properties
378-
function M.on_message_updated(properties)
379-
if not properties or not properties.info then
380-
return
381-
end
382-
377+
---@param message {info: MessageInfo} Event properties
378+
function M.on_message_updated(message)
383379
---@type OpencodeMessage
384-
local message = properties
385-
if not message.info.id or not message.info.sessionID then
380+
if not message or not message.info or not message.info.id or not message.info.sessionID then
386381
return
387382
end
388383

@@ -400,7 +395,7 @@ function M.on_message_updated(properties)
400395
found_idx = #state.messages
401396
M._message_map:add_message(message.info.id, found_idx)
402397

403-
local header_data = formatter.format_message_header_single(message, found_idx)
398+
local header_data = formatter.format_message_header(message, found_idx)
404399
M._write_formatted_data(header_data)
405400

406401
state.current_message = message
@@ -433,22 +428,21 @@ function M.on_part_updated(properties)
433428
return
434429
end
435430

436-
local msg_wrapper, msg_idx = M._message_map:get_message_by_id(part.messageID, state.messages)
431+
local message, msg_idx = M._message_map:get_message_by_id(part.messageID, state.messages)
437432

438-
if not msg_wrapper or not msg_idx then
433+
if not message or not msg_idx then
439434
vim.notify('Could not find message for part: ' .. vim.inspect(part), vim.log.levels.WARN)
440435
return
441436
end
442437

443-
local message = msg_wrapper.info
444-
msg_wrapper.parts = msg_wrapper.parts or {}
438+
message.parts = message.parts or {}
445439

446440
local is_new_part = not M._message_map:has_part(part.id)
447441
local part_idx
448442

449443
if is_new_part then
450-
table.insert(msg_wrapper.parts, part)
451-
part_idx = #msg_wrapper.parts
444+
table.insert(message.parts, part)
445+
part_idx = #message.parts
452446
M._message_map:add_part(part.id, msg_idx, part_idx, part.callID)
453447
else
454448
part_idx = M._message_map:update_part(part.id, part, state.messages)
@@ -473,17 +467,7 @@ function M.on_part_updated(properties)
473467
}
474468
end
475469

476-
local ok, formatted = pcall(formatter.format_part_single, part, {
477-
msg_idx = msg_idx,
478-
part_idx = part_idx,
479-
role = message.role,
480-
message = msg_wrapper,
481-
})
482-
483-
if not ok then
484-
vim.notify('format_part_single error: ' .. tostring(formatted), vim.log.levels.ERROR)
485-
return
486-
end
470+
local formatted = formatter.format_part(part, message.info.role, msg_idx, part_idx)
487471

488472
if is_new_part then
489473
M._insert_part_to_buffer(part.id, formatted)
@@ -535,8 +519,8 @@ function M.on_message_removed(properties)
535519
return
536520
end
537521

538-
local msg_wrapper = state.messages[message_idx]
539-
for _, part in ipairs(msg_wrapper.parts or {}) do
522+
local message = state.messages[message_idx]
523+
for _, part in ipairs(message.parts or {}) do
540524
if part.id then
541525
M._remove_part_from_buffer(part.id)
542526
end
@@ -572,28 +556,18 @@ end
572556

573557
---Event handler for permission.updated events
574558
---Re-renders part that requires permission
575-
---@param properties OpencodePermission Event properties
576-
function M.on_permission_updated(properties)
577-
if not properties then
559+
---@param permission OpencodePermission Event properties
560+
function M.on_permission_updated(permission)
561+
if not permission or not permission.messageID or not permission.callID then
578562
return
579563
end
580564

581-
local permission = properties
582-
if not permission.messageID or not permission.callID then
583-
return
584-
end
585-
586-
local prev_permission = state.current_permission
587-
state.current_permission = nil
588-
589-
if prev_permission and prev_permission.id ~= permission.id then
565+
if state.current_permission and state.current_permission.id ~= permission.id then
590566
-- we got a permission request while we had an existing one?
591-
vim.notify('Two pending permissions? existing: ' .. prev_permission.id .. ' new: ' .. permission.id)
592-
-- rerender previous part to remove old permission
593-
local prev_perm_part_id = M._find_part_by_call_id(prev_permission.callID)
594-
if prev_perm_part_id then
595-
M._rerender_part(prev_perm_part_id)
596-
end
567+
vim.notify('Two pending permissions? existing: ' .. state.current_permission.id .. ' new: ' .. permission.id)
568+
569+
-- This will rerender the part with the old permission
570+
M.on_permission_replied({})
597571
end
598572

599573
state.current_permission = permission
@@ -647,24 +621,13 @@ function M._rerender_part(part_id)
647621
return
648622
end
649623

650-
local part, msg_wrapper, msg_idx, part_idx = M._message_map:get_part_by_id(part_id, state.messages)
624+
local part, message, msg_idx, part_idx = M._message_map:get_part_by_id(part_id, state.messages)
651625

652-
if not part or not msg_wrapper then
626+
if not part or not message or not msg_idx or not part_idx then
653627
return
654628
end
655629

656-
local message_with_parts = vim.tbl_extend('force', msg_wrapper.info, { parts = msg_wrapper.parts })
657-
local ok, formatted = pcall(formatter.format_part_single, part, {
658-
msg_idx = msg_idx or 1,
659-
part_idx = part_idx or 1,
660-
role = msg_wrapper.info.role,
661-
message = message_with_parts,
662-
})
663-
664-
if not ok then
665-
vim.notify('format_part_single error: ' .. tostring(formatted), vim.log.levels.ERROR)
666-
return
667-
end
630+
local formatted = formatter.format_part(part, message.info.role, msg_idx, part_idx)
668631

669632
M._replace_part_in_buffer(part_id, formatted)
670633
end

0 commit comments

Comments
 (0)