@@ -166,50 +166,83 @@ export function buildTaskForestLayout(
166166 children . sort ( compareTasks ) ;
167167 }
168168
169- const subtreeCounts = new Map < number , Record < DesktopTaskStatus , number > > ( ) ;
170- const subtreeSizes = new Map < number , number > ( ) ;
169+ const subtreeValues = new Map <
170+ number ,
171+ { size : number ; counts : Record < DesktopTaskStatus , number > }
172+ > ( ) ;
173+ const visiting = new Set < number > ( ) ;
171174
172- function collect ( task : DesktopTaskForestNode , seen = new Set < number > ( ) ) : {
175+ function collect ( task : DesktopTaskForestNode ) : {
173176 size : number ;
174177 counts : Record < DesktopTaskStatus , number > ;
175178 } {
176- if ( seen . has ( task . number ) ) {
179+ const memoized = subtreeValues . get ( task . number ) ;
180+ if ( memoized ) {
181+ return memoized ;
182+ }
183+ if ( visiting . has ( task . number ) ) {
177184 return { size : 0 , counts : { ...EMPTY_STATUS_COUNTS } } ;
178185 }
179- seen . add ( task . number ) ;
186+ visiting . add ( task . number ) ;
180187 let size = 1 ;
181188 let counts = { ...EMPTY_STATUS_COUNTS } ;
182189 for ( const child of childrenByParent . get ( task . number ) ?? [ ] ) {
190+ if ( visiting . has ( child . number ) ) {
191+ continue ;
192+ }
183193 counts [ child . status ] += 1 ;
184- const childValue = collect ( child , new Set ( seen ) ) ;
194+ const childValue = collect ( child ) ;
185195 size += childValue . size ;
186196 counts = mergeCounts ( counts , childValue . counts ) ;
187197 }
188- subtreeCounts . set ( task . number , counts ) ;
189- subtreeSizes . set ( task . number , size ) ;
190- return { size, counts } ;
198+ visiting . delete ( task . number ) ;
199+ const value = { size, counts } ;
200+ subtreeValues . set ( task . number , value ) ;
201+ return value ;
191202 }
192203
193204 for ( const root of roots ) {
194205 collect ( root ) ;
195206 }
207+ for ( const task of tasks ) {
208+ if ( ! subtreeValues . has ( task . number ) ) {
209+ roots . push ( task ) ;
210+ collect ( task ) ;
211+ }
212+ }
196213
197214 roots . sort ( ( left , right ) => {
198215 const runningDelta = Number ( isRunning ( right ) ) - Number ( isRunning ( left ) ) ;
199216 return (
200217 runningDelta ||
201- ( subtreeSizes . get ( right . number ) ?? 1 ) - ( subtreeSizes . get ( left . number ) ?? 1 ) ||
218+ ( subtreeValues . get ( right . number ) ?. size ?? 1 ) -
219+ ( subtreeValues . get ( left . number ) ?. size ?? 1 ) ||
202220 updatedTime ( right ) - updatedTime ( left ) ||
203221 left . number - right . number
204222 ) ;
205223 } ) ;
206224
207225 const nodes : TaskForestLayoutNode [ ] = [ ] ;
226+ const placed = new Set < number > ( ) ;
208227 let nextY = 24 ;
209228
210- function place ( task : DesktopTaskForestNode , depth : number , yHint : number ) : number {
229+ function place (
230+ task : DesktopTaskForestNode ,
231+ depth : number ,
232+ yHint : number ,
233+ path = new Set < number > ( ) ,
234+ ) : number {
235+ if ( placed . has ( task . number ) || path . has ( task . number ) ) {
236+ return yHint ;
237+ }
238+ path . add ( task . number ) ;
211239 const visibleChildren =
212- depth + 1 < maxDepth ? childrenByParent . get ( task . number ) ?? [ ] : [ ] ;
240+ depth + 1 < maxDepth
241+ ? ( childrenByParent . get ( task . number ) ?? [ ] ) . filter (
242+ ( child ) => ! path . has ( child . number ) ,
243+ )
244+ : [ ] ;
245+ const subtree = subtreeValues . get ( task . number ) ;
213246 if ( visibleChildren . length === 0 ) {
214247 nodes . push ( {
215248 task,
@@ -219,16 +252,18 @@ export function buildTaskForestLayout(
219252 height : nodeHeight ,
220253 depth,
221254 hiddenDescendantCount :
222- depth + 1 >= maxDepth ? Math . max ( 0 , ( subtreeSizes . get ( task . number ) ?? 1 ) - 1 ) : 0 ,
223- descendantStatusCounts : subtreeCounts . get ( task . number ) ?? { ...EMPTY_STATUS_COUNTS } ,
255+ depth + 1 >= maxDepth ? Math . max ( 0 , ( subtree ?. size ?? 1 ) - 1 ) : 0 ,
256+ descendantStatusCounts : subtree ?. counts ?? { ...EMPTY_STATUS_COUNTS } ,
224257 } ) ;
258+ placed . add ( task . number ) ;
259+ path . delete ( task . number ) ;
225260 return yHint + nodeHeight + rowGap ;
226261 }
227262
228263 let childY = yHint ;
229264 const childStart = childY ;
230265 for ( const child of visibleChildren ) {
231- childY = place ( child , depth + 1 , childY ) ;
266+ childY = place ( child , depth + 1 , childY , path ) ;
232267 }
233268 const childEnd = childY - rowGap ;
234269 const y = Math . max ( yHint , childStart + ( childEnd - childStart - nodeHeight ) / 2 ) ;
@@ -240,8 +275,10 @@ export function buildTaskForestLayout(
240275 height : nodeHeight ,
241276 depth,
242277 hiddenDescendantCount : 0 ,
243- descendantStatusCounts : subtreeCounts . get ( task . number ) ?? { ...EMPTY_STATUS_COUNTS } ,
278+ descendantStatusCounts : subtree ?. counts ?? { ...EMPTY_STATUS_COUNTS } ,
244279 } ) ;
280+ placed . add ( task . number ) ;
281+ path . delete ( task . number ) ;
245282 return Math . max ( childY , y + nodeHeight + rowGap ) ;
246283 }
247284
0 commit comments