@@ -3,6 +3,7 @@ use std::borrow::Cow;
33use std:: cell:: Cell ;
44use std:: cell:: RefCell ;
55use std:: collections:: VecDeque ;
6+ use std:: rc:: Rc ;
67
78use ratatui:: prelude:: * ;
89use ratatui:: widgets:: { Block , Borders , Paragraph , Wrap } ;
@@ -51,10 +52,7 @@ impl PreparedChatLayout {
5152
5253struct PreparedCompletedTurn {
5354 index : usize ,
54- lines : Vec < Line < ' static > > ,
55- height : usize ,
56- expanded : bool ,
57- prompt_rows : Vec < PromptRowGeometry > ,
55+ layout : Rc < CachedCompletedTurn > ,
5856}
5957
6058#[ derive( Clone , Copy , PartialEq , Eq ) ]
@@ -79,7 +77,7 @@ struct CachedCompletedTurn {
7977
8078struct CompletedTurnCacheEntry {
8179 key : CompletedTurnCacheKey ,
82- value : CachedCompletedTurn ,
80+ value : Rc < CachedCompletedTurn > ,
8381 bytes : usize ,
8482}
8583
@@ -90,17 +88,23 @@ struct CompletedTurnLayoutCache {
9088}
9189
9290impl CompletedTurnLayoutCache {
93- fn get ( & mut self , key : CompletedTurnCacheKey ) -> Option < CachedCompletedTurn > {
91+ fn get ( & mut self , key : CompletedTurnCacheKey ) -> Option < Rc < CachedCompletedTurn > > {
9492 let index = self . entries . iter ( ) . position ( |entry| entry. key == key) ?;
9593 let entry = self . entries . remove ( index) ?;
96- let value = entry. value . clone ( ) ;
94+ let value = Rc :: clone ( & entry. value ) ;
9795 self . entries . push_back ( entry) ;
9896 Some ( value)
9997 }
10098
101- fn insert ( & mut self , key : CompletedTurnCacheKey , value : CachedCompletedTurn , bytes : usize ) {
99+ fn insert (
100+ & mut self ,
101+ key : CompletedTurnCacheKey ,
102+ value : CachedCompletedTurn ,
103+ bytes : usize ,
104+ ) -> Rc < CachedCompletedTurn > {
105+ let value = Rc :: new ( value) ;
102106 if bytes > MAX_COMPLETED_TURN_CACHE_ENTRY_BYTES {
103- return ;
107+ return value ;
104108 }
105109 if let Some ( index) = self . entries . iter ( ) . position ( |entry| entry. key == key) {
106110 if let Some ( previous) = self . entries . remove ( index) {
@@ -116,8 +120,12 @@ impl CompletedTurnLayoutCache {
116120 self . bytes = self . bytes . saturating_sub ( evicted. bytes ) ;
117121 }
118122 self . bytes = self . bytes . saturating_add ( bytes) ;
119- self . entries
120- . push_back ( CompletedTurnCacheEntry { key, value, bytes } ) ;
123+ self . entries . push_back ( CompletedTurnCacheEntry {
124+ key,
125+ value : Rc :: clone ( & value) ,
126+ bytes,
127+ } ) ;
128+ value
121129 }
122130}
123131
@@ -129,6 +137,7 @@ thread_local! {
129137#[ cfg( test) ]
130138thread_local ! {
131139 static COMPLETED_TURN_LINE_BUILD_COUNT : Cell <usize > = const { Cell :: new( 0 ) } ;
140+ static COMPLETED_TURN_LINE_MATERIALIZATION_COUNT : Cell <usize > = const { Cell :: new( 0 ) } ;
132141}
133142
134143#[ cfg( test) ]
@@ -146,6 +155,21 @@ fn record_completed_turn_line_build() {
146155 COMPLETED_TURN_LINE_BUILD_COUNT . with ( |count| count. set ( count. get ( ) + 1 ) ) ;
147156}
148157
158+ #[ cfg( test) ]
159+ pub ( crate ) fn reset_completed_turn_line_materialization_count ( ) {
160+ COMPLETED_TURN_LINE_MATERIALIZATION_COUNT . with ( |count| count. set ( 0 ) ) ;
161+ }
162+
163+ #[ cfg( test) ]
164+ pub ( crate ) fn completed_turn_line_materialization_count ( ) -> usize {
165+ COMPLETED_TURN_LINE_MATERIALIZATION_COUNT . with ( Cell :: get)
166+ }
167+
168+ #[ cfg( test) ]
169+ fn record_completed_turn_line_materialization ( ) {
170+ COMPLETED_TURN_LINE_MATERIALIZATION_COUNT . with ( |count| count. set ( count. get ( ) + 1 ) ) ;
171+ }
172+
149173fn tool_output_lines ( output : & ToolCallOutput ) -> Vec < String > {
150174 let mut lines = output. text . lines ( ) . rev ( ) ;
151175 let mut tail: Vec < String > = lines
@@ -349,17 +373,12 @@ pub fn prepare(app: &mut App, area_width: u16) -> PreparedChatLayout {
349373 lines,
350374 } ;
351375 let bytes = completed_turn_cache_bytes ( & value) ;
352- COMPLETED_TURN_LAYOUT_CACHE . with ( |cache| {
353- cache. borrow_mut ( ) . insert ( key, value. clone ( ) , bytes) ;
354- } ) ;
355- value
376+ COMPLETED_TURN_LAYOUT_CACHE
377+ . with ( |cache| cache. borrow_mut ( ) . insert ( key, value, bytes) )
356378 } ) ;
357379 PreparedCompletedTurn {
358380 index,
359- height : cached. height ,
360- lines : cached. lines ,
361- expanded : cached. expanded ,
362- prompt_rows : cached. prompt_rows ,
381+ layout : cached,
363382 }
364383 } )
365384 . collect ( ) ;
@@ -389,7 +408,7 @@ pub fn prepare(app: &mut App, area_width: u16) -> PreparedChatLayout {
389408 . sum :: < usize > ( )
390409 + completed_turns
391410 . iter ( )
392- . map ( |turn| turn. height )
411+ . map ( |turn| turn. layout . height )
393412 . sum :: < usize > ( )
394413 + rendered_lines_height ( & pending_lines, wrap_width)
395414 + rendered_lines_height ( & welcome_lines, wrap_width) )
@@ -769,13 +788,12 @@ pub fn render(frame: &mut Frame, app: &mut App, area: Rect, prepared: PreparedCh
769788 let mut selection_reached = selection_target_idx. is_none ( ) ;
770789 let mut rendered_rows_below = rendered_lines_height ( & reversed_lines, wrap_width) ;
771790 for prepared_turn in completed_turns. into_iter ( ) . rev ( ) {
772- let PreparedCompletedTurn {
773- index : idx,
774- mut lines,
775- height : turn_height,
776- expanded,
777- prompt_rows,
778- } = prepared_turn;
791+ let PreparedCompletedTurn { index : idx, layout } = prepared_turn;
792+ #[ cfg( test) ]
793+ record_completed_turn_line_materialization ( ) ;
794+ let turn_height = layout. height ;
795+ let expanded = layout. expanded ;
796+ let prompt_rows = layout. prompt_rows . clone ( ) ;
779797 turn_hit_offsets. push ( ( idx, rendered_rows_below, turn_height, expanded, prompt_rows) ) ;
780798 if selection_target_idx == Some ( idx) {
781799 let selected_end = rendered_rows_below. saturating_add ( turn_height) ;
@@ -792,7 +810,7 @@ pub fn render(frame: &mut Frame, app: &mut App, area: Rect, prepared: PreparedCh
792810 . saturating_add ( 32 ) ;
793811 selection_reached = true ;
794812 }
795- reversed_lines. extend ( lines. drain ( .. ) . rev ( ) ) ;
813+ reversed_lines. extend ( layout . lines . iter ( ) . rev ( ) . cloned ( ) ) ;
796814 rendered_rows_below = rendered_rows_below. saturating_add ( turn_height) ;
797815 if reversed_lines. len ( ) >= requested_lines && selection_reached {
798816 truncated = true ;
0 commit comments