@@ -12,6 +12,14 @@ use super::{TabAutofixState, TurnState};
1212pub ( crate ) const DEFAULT_TAB_ID : & str = "0" ;
1313
1414static NEXT_COMPLETED_TURN_LAYOUT_NAMESPACE : AtomicU64 = AtomicU64 :: new ( 1 ) ;
15+ const MAX_COMPLETED_TURN_LAYOUT_CHANGES : usize = 2048 ;
16+
17+ pub ( crate ) struct CompletedTurnLayoutChanges {
18+ pub namespace : u64 ,
19+ pub generation : u64 ,
20+ pub len : usize ,
21+ pub dirty_indices : Option < Vec < usize > > ,
22+ }
1523
1624#[ derive( Debug , Clone , Copy , PartialEq , Eq , Serialize , Deserialize ) ]
1725pub enum NoticeKind {
@@ -407,6 +415,8 @@ pub struct TabSession {
407415 pub ( crate ) completed_turn_layout_ids : Vec < u64 > ,
408416 pub ( crate ) completed_turn_layout_revisions : Vec < u64 > ,
409417 pub ( crate ) next_completed_turn_layout_id : u64 ,
418+ pub ( crate ) completed_turn_layout_generation : u64 ,
419+ pub ( crate ) completed_turn_layout_changes : VecDeque < ( u64 , usize ) > ,
410420 /// Tab/Shift+Tab selects a past turn (most recent first). Enter then
411421 /// toggles `CompletedTurn.expanded`. None means no selection — Enter
412422 /// goes to the input/prompt path as before.
@@ -523,22 +533,50 @@ pub struct TabSession {
523533}
524534
525535impl TabSession {
526- pub ( crate ) fn completed_turn_layout_metadata ( & mut self ) -> ( u64 , Vec < ( u64 , u64 ) > ) {
536+ fn record_completed_turn_layout_change ( & mut self , index : usize ) {
537+ if self . completed_turn_layout_generation == u64:: MAX {
538+ self . completed_turn_layout_namespace =
539+ NEXT_COMPLETED_TURN_LAYOUT_NAMESPACE . fetch_add ( 1 , Ordering :: Relaxed ) ;
540+ self . completed_turn_layout_generation = 1 ;
541+ self . completed_turn_layout_changes . clear ( ) ;
542+ } else {
543+ self . completed_turn_layout_generation += 1 ;
544+ }
545+ self . completed_turn_layout_changes
546+ . push_back ( ( self . completed_turn_layout_generation , index) ) ;
547+ while self . completed_turn_layout_changes . len ( ) > MAX_COMPLETED_TURN_LAYOUT_CHANGES {
548+ self . completed_turn_layout_changes . pop_front ( ) ;
549+ }
550+ }
551+
552+ fn sync_completed_turn_layout_metadata ( & mut self ) {
527553 if self . completed_turn_layout_namespace == 0 {
528554 self . completed_turn_layout_namespace =
529555 NEXT_COMPLETED_TURN_LAYOUT_NAMESPACE . fetch_add ( 1 , Ordering :: Relaxed ) ;
530556 }
531557
532558 let len = self . completed_turns . len ( ) ;
559+ if self . completed_turn_layout_ids . len ( ) > len {
560+ self . completed_turn_layout_namespace =
561+ NEXT_COMPLETED_TURN_LAYOUT_NAMESPACE . fetch_add ( 1 , Ordering :: Relaxed ) ;
562+ self . completed_turn_layout_generation = 0 ;
563+ self . completed_turn_layout_changes . clear ( ) ;
564+ }
533565 self . completed_turn_layout_ids . truncate ( len) ;
534566 self . completed_turn_layout_revisions . truncate ( len) ;
535567 while self . completed_turn_layout_ids . len ( ) < len {
568+ let index = self . completed_turn_layout_ids . len ( ) ;
536569 self . next_completed_turn_layout_id =
537570 self . next_completed_turn_layout_id . wrapping_add ( 1 ) . max ( 1 ) ;
538571 self . completed_turn_layout_ids
539572 . push ( self . next_completed_turn_layout_id ) ;
540573 self . completed_turn_layout_revisions . push ( 0 ) ;
574+ self . record_completed_turn_layout_change ( index) ;
541575 }
576+ }
577+
578+ pub ( crate ) fn completed_turn_layout_metadata ( & mut self ) -> ( u64 , Vec < ( u64 , u64 ) > ) {
579+ self . sync_completed_turn_layout_metadata ( ) ;
542580
543581 (
544582 self . completed_turn_layout_namespace ,
@@ -550,17 +588,98 @@ impl TabSession {
550588 )
551589 }
552590
591+ pub ( crate ) fn completed_turn_layout_changes_since (
592+ & mut self ,
593+ previous : Option < ( u64 , u64 ) > ,
594+ ) -> CompletedTurnLayoutChanges {
595+ self . sync_completed_turn_layout_metadata ( ) ;
596+ let namespace = self . completed_turn_layout_namespace ;
597+ let generation = self . completed_turn_layout_generation ;
598+ let len = self . completed_turns . len ( ) ;
599+ let Some ( ( previous_namespace, previous_generation) ) = previous else {
600+ return CompletedTurnLayoutChanges {
601+ namespace,
602+ generation,
603+ len,
604+ dirty_indices : None ,
605+ } ;
606+ } ;
607+ if previous_namespace != namespace {
608+ return CompletedTurnLayoutChanges {
609+ namespace,
610+ generation,
611+ len,
612+ dirty_indices : None ,
613+ } ;
614+ }
615+ if previous_generation == generation {
616+ return CompletedTurnLayoutChanges {
617+ namespace,
618+ generation,
619+ len,
620+ dirty_indices : Some ( Vec :: new ( ) ) ,
621+ } ;
622+ }
623+
624+ let Some ( first_retained_generation) = self
625+ . completed_turn_layout_changes
626+ . front ( )
627+ . map ( |( generation, _) | * generation)
628+ else {
629+ return CompletedTurnLayoutChanges {
630+ namespace,
631+ generation,
632+ len,
633+ dirty_indices : None ,
634+ } ;
635+ } ;
636+ if previous_generation. saturating_add ( 1 ) < first_retained_generation {
637+ return CompletedTurnLayoutChanges {
638+ namespace,
639+ generation,
640+ len,
641+ dirty_indices : None ,
642+ } ;
643+ }
644+
645+ let mut dirty_indices = self
646+ . completed_turn_layout_changes
647+ . iter ( )
648+ . filter ( |( change_generation, _) | * change_generation > previous_generation)
649+ . map ( |( _, index) | * index)
650+ . collect :: < Vec < _ > > ( ) ;
651+ dirty_indices. sort_unstable ( ) ;
652+ dirty_indices. dedup ( ) ;
653+ CompletedTurnLayoutChanges {
654+ namespace,
655+ generation,
656+ len,
657+ dirty_indices : Some ( dirty_indices) ,
658+ }
659+ }
660+
661+ pub ( crate ) fn completed_turn_layout_item ( & self , index : usize ) -> Option < ( u64 , u64 ) > {
662+ Some ( (
663+ * self . completed_turn_layout_ids . get ( index) ?,
664+ * self . completed_turn_layout_revisions . get ( index) ?,
665+ ) )
666+ }
667+
553668 pub ( crate ) fn mark_completed_turn_layout_dirty ( & mut self , index : usize ) {
554- let _ = self . completed_turn_layout_metadata ( ) ;
669+ self . sync_completed_turn_layout_metadata ( ) ;
555670 if let Some ( revision) = self . completed_turn_layout_revisions . get_mut ( index) {
556671 * revision = revision. wrapping_add ( 1 ) ;
672+ self . record_completed_turn_layout_change ( index) ;
557673 }
558674 }
559675
560676 pub fn clear_completed_turns ( & mut self ) {
561677 self . completed_turns . clear ( ) ;
678+ self . completed_turn_layout_namespace = 0 ;
562679 self . completed_turn_layout_ids . clear ( ) ;
563680 self . completed_turn_layout_revisions . clear ( ) ;
681+ self . completed_turn_layout_generation = 0 ;
682+ self . completed_turn_layout_changes . clear ( ) ;
564683 }
565684
566685 pub fn set_last_completed_turn_trailing_marker ( & mut self , marker : String ) -> bool {
0 commit comments