Skip to content

Commit 7f50103

Browse files
committed
feat(redo): update redo to match opencode behavior
1 parent 50d1ddd commit 7f50103

File tree

7 files changed

+116
-36
lines changed

7 files changed

+116
-36
lines changed

lua/opencode/api.lua

Lines changed: 86 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ function M.unshare()
556556

557557
state.api_client
558558
:unshare_session(state.active_session.id)
559-
:and_then(function()
559+
:and_then(function(response)
560560
vim.schedule(function()
561561
vim.notify('Session unshared successfully', vim.log.levels.INFO)
562562
end)
@@ -568,21 +568,22 @@ function M.unshare()
568568
end)
569569
end
570570

571-
function M.undo()
571+
---@param messageId? string
572+
function M.undo(messageId)
572573
if not state.active_session then
573574
vim.notify('No active session to undo', vim.log.levels.WARN)
574575
return
575576
end
576577

577-
local last_user_message = state.last_user_message
578-
if not last_user_message then
578+
local message_to_revert = messageId or state.last_user_message and state.last_user_message.info.id
579+
if not message_to_revert then
579580
vim.notify('No user message to undo', vim.log.levels.WARN)
580581
return
581582
end
582583

583584
state.api_client
584585
:revert_message(state.active_session.id, {
585-
messageID = last_user_message.info.id,
586+
messageID = message_to_revert,
586587
})
587588
:and_then(function(response)
588589
vim.schedule(function()
@@ -596,25 +597,93 @@ function M.undo()
596597
end)
597598
end
598599

600+
-- Returns the ID of the next user message after the current undo point
601+
-- This is a port of the opencode tui logic
602+
-- https://github.com/sst/opencode/blob/dev/packages/tui/internal/components/chat/messages.go#L1199
603+
function find_next_message_for_redo()
604+
if not state.active_session then
605+
return nil
606+
end
607+
608+
local revert_time = 0
609+
local revert = state.active_session.revert
610+
611+
if not revert then
612+
return nil
613+
end
614+
615+
for _, message in ipairs(state.messages or {}) do
616+
if message.info.id == revert.messageID then
617+
revert_time = math.floor(message.info.time.created)
618+
break
619+
end
620+
if revert.partID and revert.partID ~= '' then
621+
for _, part in ipairs(message.parts) do
622+
if part.id == revert.partID and part.state and part.state.time then
623+
revert_time = math.floor(part.state.time.start)
624+
break
625+
end
626+
end
627+
end
628+
end
629+
630+
-- Find next user message after revert time
631+
local next_message_id = nil
632+
for _, msg in ipairs(state.messages or {}) do
633+
if msg.info.role == 'user' and msg.info.time.created > revert_time then
634+
next_message_id = msg.info.id
635+
break
636+
end
637+
end
638+
return next_message_id
639+
end
640+
599641
function M.redo()
600642
if not state.active_session then
601-
vim.notify('No active session to undo', vim.log.levels.WARN)
643+
vim.notify('No active session to redo', vim.log.levels.WARN)
602644
return
603645
end
604-
ui.render_output(true)
605646

606-
state.api_client
607-
:unrevert_messages(state.active_session.id)
608-
:and_then(function(response)
609-
vim.schedule(function()
610-
vim.cmd('checktime')
647+
if not state.active_session.revert or state.active_session.revert.messageID == '' then
648+
vim.notify('Nothing to redo', vim.log.levels.WARN)
649+
return
650+
end
651+
652+
if not state.messages then
653+
return
654+
end
655+
656+
local next_message_id = find_next_message_for_redo()
657+
if not next_message_id then
658+
state.api_client
659+
:unrevert_messages(state.active_session.id)
660+
:and_then(function(response)
661+
vim.schedule(function()
662+
vim.cmd('checktime')
663+
end)
611664
end)
612-
end)
613-
:catch(function(err)
614-
vim.schedule(function()
615-
vim.notify('Failed to undo last message: ' .. vim.inspect(err), vim.log.levels.ERROR)
665+
:catch(function(err)
666+
vim.schedule(function()
667+
vim.notify('Failed to redo message: ' .. vim.inspect(err), vim.log.levels.ERROR)
668+
end)
616669
end)
617-
end)
670+
else
671+
-- Calling revert on a "later" message is like a redo
672+
state.api_client
673+
:revert_message(state.active_session.id, {
674+
messageID = next_message_id,
675+
})
676+
:and_then(function(response)
677+
vim.schedule(function()
678+
vim.cmd('checktime')
679+
end)
680+
end)
681+
:catch(function(err)
682+
vim.schedule(function()
683+
vim.notify('Failed to redo message: ' .. vim.inspect(err), vim.log.levels.ERROR)
684+
end)
685+
end)
686+
end
618687
end
619688

620689
---@param answer? 'once'|'always'|'reject'

0 commit comments

Comments
 (0)