You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Repo and compare browsers stat every entry on the file system on every frame (plus O(n²) Contains lookups) #498
Since #482/#490 ("Classify browser entries from the repository root"), the browsers decide whether an entry is a folder by asking the file system (RepoBrowsing.IsDirectory → Directory.Exists) instead of looking at how the entry is spelled. That is the correct check, but it runs again on every frame rather than once when the listing is taken:
Compare browser.ShowCompareBrowser (ProjectDirector/ProjectDirector.cs:2074-2080) does the following every frame:
IsDirectory (ProjectDirector/RepoBrowsing.cs:52-53) calls Directory.Exists once per root. A plain file therefore costs a failed stat in both repositories on every frame.
Repo browser.ShowRepoBrowser (ProjectDirector.cs:2242-2247) does the same for the base repository.
Per-row lookups. Each compare-browser row also calls BrowserContentsBase.Contains(path) and BrowserContentsCompare.Contains(path) (around ProjectDirector.cs:2112-2113 and 2182-2183). These are linear scans of a Collection<T>, so each frame does O(n²) work.
The listing only changes in SwitchRepoBrowserPath and SwitchCompareBrowserPath (ProjectDirector.cs:2439-2471). Those are the only places the classification can change.
Why it matters
The browser is drawn whenever a repository is selected. Comparing a folder of 150 entries against a sibling repository costs about 300 stat calls per frame, which is about 18,000 per second at 60 fps.
Each call gets noticeably more expensive in directory-heavy trees, and on Windows with real-time antivirus scanning. The UI can then stutter just from showing a folder.
The listing also isn't refreshed after Give/Take/X (#456), so re-checking every frame doesn't even keep it current.
Suggested fix / acceptance criteria
Classify entries once, when the listing is taken. RepoBrowsing.List or the Switch*BrowserPath methods would produce three things, stored next to BrowserContentsBase and BrowserContentsCompare:
the folders
the files
which side each entry is on, as one HashSet<RelativePath> per side
ShowRepoBrowser and ShowCompareBrowser read only the cached sets. They make no file-system calls and no linear Contains lookups while drawing.
Priority: Medium. Hundreds of stat calls per frame plus O(n²) Contains lookups while a browser is open. Results are correct, but it can make the UI stutter on large folders or under antivirus scanning.
Area / suggested assignee:ShowRepoBrowser / ShowCompareBrowser in ProjectDirector.cs and RepoBrowsing. Owner: @matt-edmondson
Activity
matt-edmondson commentedon Oct 19, 2026
Triage
statcalls per frame plus O(n²)Containslookups while a browser is open. Results are correct, but it can make the UI stutter on large folders or under antivirus scanning.ShowRepoBrowser/ShowCompareBrowserinProjectDirector.csandRepoBrowsing. Owner: @matt-edmondsonSwitch*BrowserPathinto cached folder/file sets and per-sideHashSets, re-list after Give/Take/X, and keep the draw path free of file-system calls. Close or update Compare browser: Give/Take and X only respond on the first row that shows them, and a row that was acted on stays listed until you navigate away #456 with the same change.Generated by Claude Code