Skip to content

Commit d78904a

Browse files
committed
feat(redo): update redo to match opencode behavior
1 parent dbb7ef9 commit d78904a

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
@@ -558,7 +558,7 @@ function M.unshare()
558558

559559
state.api_client
560560
:unshare_session(state.active_session.id)
561-
:and_then(function()
561+
:and_then(function(response)
562562
vim.schedule(function()
563563
vim.notify('Session unshared successfully', vim.log.levels.INFO)
564564
end)
@@ -570,21 +570,22 @@ function M.unshare()
570570
end)
571571
end
572572

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

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

585586
state.api_client
586587
:revert_message(state.active_session.id, {
587-
messageID = last_user_message.info.id,
588+
messageID = message_to_revert,
588589
})
589590
:and_then(function(response)
590591
vim.schedule(function()
@@ -598,25 +599,93 @@ function M.undo()
598599
end)
599600
end
600601

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

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

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

0 commit comments

Comments
 (0)