@@ -633,6 +633,238 @@ describe("peekstack.persist.sessions", function()
633633 vim .notify = original_notify
634634 end )
635635
636+ it (" should save pinned and buffer_mode in session items" , function ()
637+ local path = make_file (" pinned_mode" , { " line1" , " line2" })
638+ local loc = make_location (path , 0 )
639+ local model = stack .push (loc , { title = " Pinned" })
640+ assert .is_not_nil (model )
641+ model .pinned = true
642+
643+ local source_model = stack .push (loc , { title = " Source" , buffer_mode = " source" })
644+ assert .is_not_nil (source_model )
645+
646+ persist .save_current (" pinned_session" , { silent = true , sync = true })
647+
648+ local data = migrate .ensure (read_and_wait (test_scope ))
649+ local items = data .sessions .pinned_session .items
650+ assert .equals (2 , # items )
651+ assert .is_true (items [1 ].pinned )
652+ assert .equals (" source" , items [2 ].buffer_mode )
653+ end )
654+
655+ it (" should save parent_popup_id in session items" , function ()
656+ local path = make_file (" parent_child" , { " line1" , " line2" })
657+ local parent = stack .push (make_location (path , 0 ), { title = " Parent" })
658+ assert .is_not_nil (parent )
659+ vim .api .nvim_set_current_win (parent .winid )
660+
661+ local child = stack .push (make_location (path , 1 ), { title = " Child" })
662+ assert .is_not_nil (child )
663+ assert .equals (parent .id , child .parent_popup_id )
664+
665+ persist .save_current (" parent_child_session" , { silent = true , sync = true })
666+
667+ local data = migrate .ensure (read_and_wait (test_scope ))
668+ local items = data .sessions .parent_child_session .items
669+ assert .equals (2 , # items )
670+ assert .is_not_nil (items [1 ].popup_id )
671+ assert .equals (items [1 ].popup_id , items [2 ].parent_popup_id )
672+ end )
673+
674+ it (" should restore pinned and buffer_mode from session" , function ()
675+ local original_push = stack .push
676+ local original_reflow = stack .reflow
677+
678+ write_and_wait (test_scope , {
679+ version = 2 ,
680+ sessions = {
681+ restore_fields = {
682+ items = {
683+ {
684+ uri = " file:///tmp/a.lua" ,
685+ range = { start = { line = 0 , character = 0 }, [" end" ] = { line = 0 , character = 1 } },
686+ provider = " test" ,
687+ ts = os.time (),
688+ pinned = true ,
689+ buffer_mode = " source" ,
690+ popup_id = 10 ,
691+ },
692+ },
693+ meta = { created_at = os.time (), updated_at = os.time () },
694+ },
695+ },
696+ })
697+
698+ local pushed_opts = {}
699+ local pushed_models = {}
700+
701+ local ok , err = pcall (function ()
702+ stack .push = function (_loc , opts )
703+ table.insert (pushed_opts , vim .deepcopy (opts or {}))
704+ local model = { id = # pushed_opts }
705+ table.insert (pushed_models , model )
706+ return model
707+ end
708+ stack .reflow = function () end
709+
710+ local restored = nil
711+ persist .restore (" restore_fields" , {
712+ silent = true ,
713+ on_done = function (result )
714+ restored = result
715+ end ,
716+ })
717+
718+ local waited = vim .wait (wait_timeout_ms , function ()
719+ return restored ~= nil
720+ end , wait_interval_ms )
721+ assert .is_true (waited , " Timed out waiting for restore callback" )
722+
723+ assert .is_true (restored )
724+ assert .equals (1 , # pushed_opts )
725+ assert .equals (" source" , pushed_opts [1 ].buffer_mode )
726+ assert .is_true (pushed_models [1 ].pinned )
727+ end )
728+
729+ stack .push = original_push
730+ stack .reflow = original_reflow
731+
732+ if not ok then
733+ error (err )
734+ end
735+ end )
736+
737+ it (" should remap parent_popup_id when restoring session" , function ()
738+ local original_push = stack .push
739+ local original_reflow = stack .reflow
740+
741+ write_and_wait (test_scope , {
742+ version = 2 ,
743+ sessions = {
744+ remap_parent = {
745+ items = {
746+ {
747+ uri = " file:///tmp/parent.lua" ,
748+ range = { start = { line = 0 , character = 0 }, [" end" ] = { line = 0 , character = 1 } },
749+ provider = " test" ,
750+ ts = os.time (),
751+ popup_id = 100 ,
752+ },
753+ {
754+ uri = " file:///tmp/child.lua" ,
755+ range = { start = { line = 1 , character = 0 }, [" end" ] = { line = 1 , character = 1 } },
756+ provider = " test" ,
757+ ts = os.time (),
758+ popup_id = 101 ,
759+ parent_popup_id = 100 ,
760+ },
761+ },
762+ meta = { created_at = os.time (), updated_at = os.time () },
763+ },
764+ },
765+ })
766+
767+ local pushed_opts = {}
768+ local next_model_id = 200
769+
770+ local ok , err = pcall (function ()
771+ stack .push = function (_loc , opts )
772+ table.insert (pushed_opts , vim .deepcopy (opts or {}))
773+ local model = { id = next_model_id }
774+ next_model_id = next_model_id + 1
775+ return model
776+ end
777+ stack .reflow = function () end
778+
779+ local restored = nil
780+ persist .restore (" remap_parent" , {
781+ silent = true ,
782+ on_done = function (result )
783+ restored = result
784+ end ,
785+ })
786+
787+ local waited = vim .wait (wait_timeout_ms , function ()
788+ return restored ~= nil
789+ end , wait_interval_ms )
790+ assert .is_true (waited , " Timed out waiting for restore callback" )
791+
792+ assert .is_true (restored )
793+ assert .equals (2 , # pushed_opts )
794+ -- First item (parent) should have no parent_popup_id
795+ assert .is_nil (pushed_opts [1 ].parent_popup_id )
796+ -- Second item (child) should have remapped parent_popup_id (200, not 100)
797+ assert .equals (200 , pushed_opts [2 ].parent_popup_id )
798+ end )
799+
800+ stack .push = original_push
801+ stack .reflow = original_reflow
802+
803+ if not ok then
804+ error (err )
805+ end
806+ end )
807+
808+ it (" should drop orphaned parent_popup_id when parent is not in session" , function ()
809+ local original_push = stack .push
810+ local original_reflow = stack .reflow
811+
812+ -- Session where the parent (popup_id=50) is NOT present in items
813+ write_and_wait (test_scope , {
814+ version = 2 ,
815+ sessions = {
816+ orphan_parent = {
817+ items = {
818+ {
819+ uri = " file:///tmp/orphan.lua" ,
820+ range = { start = { line = 0 , character = 0 }, [" end" ] = { line = 0 , character = 1 } },
821+ provider = " test" ,
822+ ts = os.time (),
823+ popup_id = 51 ,
824+ parent_popup_id = 50 ,
825+ },
826+ },
827+ meta = { created_at = os.time (), updated_at = os.time () },
828+ },
829+ },
830+ })
831+
832+ local pushed_opts = {}
833+
834+ local ok , err = pcall (function ()
835+ stack .push = function (_loc , opts )
836+ table.insert (pushed_opts , vim .deepcopy (opts or {}))
837+ return { id = 999 }
838+ end
839+ stack .reflow = function () end
840+
841+ local restored = nil
842+ persist .restore (" orphan_parent" , {
843+ silent = true ,
844+ on_done = function (result )
845+ restored = result
846+ end ,
847+ })
848+
849+ local waited = vim .wait (wait_timeout_ms , function ()
850+ return restored ~= nil
851+ end , wait_interval_ms )
852+ assert .is_true (waited , " Timed out waiting for restore callback" )
853+
854+ assert .is_true (restored )
855+ assert .equals (1 , # pushed_opts )
856+ -- parent_popup_id should be nil (orphaned), not 50
857+ assert .is_nil (pushed_opts [1 ].parent_popup_id )
858+ end )
859+
860+ stack .push = original_push
861+ stack .reflow = original_reflow
862+
863+ if not ok then
864+ error (err )
865+ end
866+ end )
867+
636868 it (" should save the root stack when stack view is active" , function ()
637869 local stack_view = require (" peekstack.ui.stack_view" )
638870
0 commit comments