Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions apps/ios/ADE/App/ADEAppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ final class ADEAppDelegate: NSObject, UIApplicationDelegate {
return true
}

/// Transcript render caches (parsed Markdown blocks, inline attributed
/// strings, syntax highlighting) are all derived state — a long chat can
/// hold megabytes of it, and every entry can be rebuilt on demand. When the
/// system says it wants memory back, give it these first.
func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
workPurgeMarkdownRenderCaches()
ADECodeRenderingCache.shared.purgeOnMemoryWarning()
MainActor.assumeIsolated {
WorkPendingUploadPreviewStore.shared.purge()
}
}

/// Register the approval-alert category so approval pushes carry inline
/// Approve / Deny actions on the lock screen and in Notification Center. The
/// brain stamps `aps.category = "ADE_APPROVAL"` on those alerts; the action
Expand Down
26 changes: 26 additions & 0 deletions apps/ios/ADE/Views/Components/ADECodeRenderingCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ final class ADECodeRenderingCache {
private let attributedCache = NSCache<NSString, AttributedStringBox>()
private let regexCache = NSCache<NSString, NSRegularExpression>()
private let regexLock = NSLock()
/// Streaming state, not a cache: one in-progress code block per language.
/// Guarded by its own lock because highlighting is reachable from any actor.
private let prefixLock = NSLock()
private var highlightPrefixes: [FilesLanguage: SyntaxHighlightPrefix] = [:]

private init() {
tokenCache.countLimit = 64
Expand All @@ -31,6 +35,28 @@ final class ADECodeRenderingCache {
attributedCache.setObject(AttributedStringBox(value: attributed), forKey: key as NSString)
}

func highlightPrefix(for language: FilesLanguage) -> SyntaxHighlightPrefix? {
prefixLock.lock()
defer { prefixLock.unlock() }
return highlightPrefixes[language]
}

func storeHighlightPrefix(_ prefix: SyntaxHighlightPrefix, for language: FilesLanguage) {
prefixLock.lock()
defer { prefixLock.unlock() }
highlightPrefixes[language] = prefix
}

/// Drops derived renders. Compiled regexes are cheap to hold and hot on every
/// highlight, so they stay.
func purgeOnMemoryWarning() {
tokenCache.removeAllObjects()
attributedCache.removeAllObjects()
prefixLock.lock()
highlightPrefixes.removeAll()
prefixLock.unlock()
}

func regex(for pattern: String) -> NSRegularExpression? {
let key = pattern as NSString
if let cached = regexCache.object(forKey: key) {
Expand Down
Loading
Loading