@@ -656,3 +656,69 @@ describe('navigation jumplist preservation', function()
656656 assert .equals (0 , mark [2 ])
657657 end )
658658end )
659+
660+ describe (' navigation hidden-messages-notice handling' , function ()
661+ local output_buf , output_win
662+ local original_windows
663+
664+ before_each (function ()
665+ original_windows = state .store .get (' windows' )
666+ state .ui .clear_hidden_window_state ()
667+
668+ output_buf = vim .api .nvim_create_buf (false , true )
669+ output_win = vim .api .nvim_open_win (output_buf , true , {
670+ relative = ' editor' ,
671+ width = 80 ,
672+ height = 20 ,
673+ row = 0 ,
674+ col = 0 ,
675+ })
676+ state .ui .set_windows ({ output_buf = output_buf , output_win = output_win })
677+ end )
678+
679+ after_each (function ()
680+ state .ui .clear_hidden_window_state ()
681+ pcall (vim .api .nvim_win_close , output_win , true )
682+ pcall (vim .api .nvim_buf_delete , output_buf , { force = true })
683+
684+ if original_windows ~= nil then
685+ state .ui .set_windows (original_windows )
686+ else
687+ state .ui .clear_windows ()
688+ end
689+ end )
690+
691+ it (' does not jump [[ to the hidden-messages notice when max_messages truncates' , function ()
692+ local ctx = require (' opencode.ui.renderer.ctx' )
693+ -- Simulate a session where max_messages truncates older messages: the
694+ -- hidden notice was emitted into state.messages via on_message_updated,
695+ -- so it lives in state.messages with a real line_start. The bug was that
696+ -- get_prev_rendered_message iterated state.messages in reverse and matched
697+ -- the notice first, jumping the user back to the buffer top.
698+ state .renderer .set_messages ({
699+ { info = { id = ' real_old' , role = ' assistant' , sessionID = ' s1' } },
700+ { info = { id = ' real_mid' , role = ' user' , sessionID = ' s1' } },
701+ { info = { id = ' __opencode_hidden_messages_notice__' , role = ' system' , sessionID = ' s1' } },
702+ })
703+ ctx .render_state :set_message (
704+ { info = { id = ' __opencode_hidden_messages_notice__' , role = ' system' , sessionID = ' s1' } },
705+ 1 ,
706+ 2
707+ )
708+ ctx .render_state :set_message ({ info = { id = ' real_old' , role = ' assistant' , sessionID = ' s1' } }, 4 , 8 )
709+ ctx .render_state :set_message ({ info = { id = ' real_mid' , role = ' user' , sessionID = ' s1' } }, 10 , 18 )
710+
711+ vim .api .nvim_buf_set_lines (output_buf , 0 , - 1 , false , vim .fn [' repeat' ]({ ' line' }, 25 ))
712+ -- Cursor at the first line of `real_mid` (line 11). Without the fix,
713+ -- get_prev_rendered_message(11) iterated state.messages in reverse and
714+ -- landed on the hidden notice at line 1, returning it (since its
715+ -- line_start=1 satisfies `1+1 < 11`), so the user was yanked to line 2.
716+ -- With the fix, the synthetic notice is skipped and `real_old` is chosen.
717+ vim .api .nvim_win_set_cursor (output_win , { 11 , 0 })
718+
719+ navigation .goto_prev_message ()
720+
721+ local cursor = vim .api .nvim_win_get_cursor (output_win )
722+ assert .equals (5 , cursor [1 ])
723+ end )
724+ end )
0 commit comments