Scan engine QoL: exclusions, streaming, cancel-keeps-partial, pause/resume, rate display - #114
Merged
Merged
Conversation
…esume, rate display Unit 2 of the scan-engine QoL patch: adds a folder exclusion list (name and absolute-prefix matching, wired into Settings and four context menus), cancel-keeps-partial-results, live streaming of scan snapshots into list views during a scan, an items/sec + elapsed-time display, and in-memory pause/resume backed by an NSCondition-based ScanCancellation. Core changes: - ScanOptions gains excludeEnabled/excludedPathComponents/excludedAbsolutePrefixes; FileSystemScanner.scanItem skips matching children before recursing. - StorageScan gains isPartial; ScanAccumulator gains a lock-guarded snapshot() used both mid-scan (streaming) and by tests. - ScanCancellation is rewritten on NSCondition to add pause()/resume()/waitIfPaused(), re-checking cancellation on every wake so cancel-while-paused can't hang. - ScanStore pairs each throttled progress tick with the scanner's paired snapshot (onSnapshot fires before progress in the same emitProgressLocked call, so the pairing is same-tick rather than one-tick-stale) and publishes it to session.scan as the scan progresses; cancelScan() now preserves rather than resets state. - Added items/sec EMA + elapsed-time display in the sidebar footer, and a toolbar Pause/Resume control alongside Cancel. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…f161bfa8bce # Conflicts: # Sources/StorageScope/Views/CleanupReviewView.swift # Sources/StorageScope/Views/StorageItemTable.swift
This was referenced Jul 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ScanOptions.excludeEnabled/excludedPathComponents/excludedAbsolutePrefixes, wired into Settings (toggle + add/remove list, seeded withnode_modules,.git,~/Library/Caches,~/Library/Application Support) and an "Exclude This Folder" context-menu action in StorageItemTable, TreeExplorerView, CleanupReviewView, and DuplicateCandidatesView (which targets the containing folder, since duplicate rows are always files).StorageScan.isPartialmarks in-progress snapshots.FileSystemScanner.scan(...)gains anonSnapshotcallback fired at the same throttled cadence asprogress, publishing ranked lists intosession.scanwhile a scan is running.ScanProgress.itemsPerSecond(EMA-smoothed inScanStore), plus an elapsed-time readout in the sidebar footer.ScanCancellationrewritten onNSConditionwithpause()/resume()/waitIfPaused(); a toolbar Pause/Resume button next to Cancel.Notable fixes made during self-review
onSnapshotfired afterprogressinsideemitProgressLocked, soScanStore's snapshot/progress pairing was always one throttle-tick stale — reordered soonSnapshotfires first.excludeFolder/excludeFolder(atPath:)to matchFileSystemScanner.isExcluded's comparison (both now usestandardizedFileURL.path), so exclusions set from a symlinked-ancestor path still match on rescan.!store.isScanningin all four views — it routes throughrescan(), which no-ops silently while a scan is already in flight (reachable now that streaming makesstore.scannon-nil mid-scan).waitIfPaused()blocks insideDispatchQueue.concurrentPerformclosures, so pausing a very wide directory scan can approach GCD's shared worker-pool ceiling.Test plan
swift test— 184 tests, 3 consecutive full-suite runs greenswift buildandswift build -c release— both cleansnapshot()mid-walk, pause/resume blocking + cancel-while-paused,ScanSession.canPauseScan/canResumeScanguards,ScanStorecancel-keeps-partial-snapshot🤖 Generated with Claude Code