Cache Ghostty config loads on UI path
Fixes CMUXTERM-MACOS-A7
This commit is contained in:
parent
12e9c1e317
commit
4b98feb263
3 changed files with 109 additions and 4 deletions
|
|
@ -2,11 +2,14 @@ import Foundation
|
|||
import AppKit
|
||||
|
||||
struct GhosttyConfig {
|
||||
enum ColorSchemePreference {
|
||||
enum ColorSchemePreference: Hashable {
|
||||
case light
|
||||
case dark
|
||||
}
|
||||
|
||||
private static let loadCacheLock = NSLock()
|
||||
private static var cachedConfigsByColorScheme: [ColorSchemePreference: GhosttyConfig] = [:]
|
||||
|
||||
var fontFamily: String = "Menlo"
|
||||
var fontSize: CGFloat = 12
|
||||
var theme: String?
|
||||
|
|
@ -45,7 +48,45 @@ struct GhosttyConfig {
|
|||
return backgroundColor.darken(by: isLightBackground ? 0.08 : 0.4)
|
||||
}
|
||||
|
||||
static func load() -> GhosttyConfig {
|
||||
static func load(
|
||||
preferredColorScheme: ColorSchemePreference? = nil,
|
||||
useCache: Bool = true,
|
||||
loadFromDisk: (_ preferredColorScheme: ColorSchemePreference) -> GhosttyConfig = Self.loadFromDisk
|
||||
) -> GhosttyConfig {
|
||||
let resolvedColorScheme = preferredColorScheme ?? currentColorSchemePreference()
|
||||
if useCache, let cached = cachedLoad(for: resolvedColorScheme) {
|
||||
return cached
|
||||
}
|
||||
|
||||
let loaded = loadFromDisk(resolvedColorScheme)
|
||||
if useCache {
|
||||
storeCachedLoad(loaded, for: resolvedColorScheme)
|
||||
}
|
||||
return loaded
|
||||
}
|
||||
|
||||
static func invalidateLoadCache() {
|
||||
loadCacheLock.lock()
|
||||
cachedConfigsByColorScheme.removeAll()
|
||||
loadCacheLock.unlock()
|
||||
}
|
||||
|
||||
private static func cachedLoad(for colorScheme: ColorSchemePreference) -> GhosttyConfig? {
|
||||
loadCacheLock.lock()
|
||||
defer { loadCacheLock.unlock() }
|
||||
return cachedConfigsByColorScheme[colorScheme]
|
||||
}
|
||||
|
||||
private static func storeCachedLoad(
|
||||
_ config: GhosttyConfig,
|
||||
for colorScheme: ColorSchemePreference
|
||||
) {
|
||||
loadCacheLock.lock()
|
||||
cachedConfigsByColorScheme[colorScheme] = config
|
||||
loadCacheLock.unlock()
|
||||
}
|
||||
|
||||
private static func loadFromDisk(preferredColorScheme: ColorSchemePreference) -> GhosttyConfig {
|
||||
var config = GhosttyConfig()
|
||||
|
||||
// Match Ghostty's default load order on macOS.
|
||||
|
|
@ -64,7 +105,12 @@ struct GhosttyConfig {
|
|||
|
||||
// Load theme if specified
|
||||
if let themeName = config.theme {
|
||||
config.loadTheme(themeName)
|
||||
config.loadTheme(
|
||||
themeName,
|
||||
environment: ProcessInfo.processInfo.environment,
|
||||
bundleResourceURL: Bundle.main.resourceURL,
|
||||
preferredColorScheme: preferredColorScheme
|
||||
)
|
||||
}
|
||||
|
||||
return config
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ struct WorkspaceContentView: View {
|
|||
syncBonsplitNotificationBadges()
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .ghosttyConfigDidReload)) { _ in
|
||||
GhosttyConfig.invalidateLoadCache()
|
||||
refreshGhosttyAppearanceConfig(reason: "ghosttyConfigDidReload")
|
||||
}
|
||||
.onChange(of: colorScheme) { oldValue, newValue in
|
||||
|
|
@ -175,7 +176,7 @@ struct WorkspaceContentView: View {
|
|||
static func resolveGhosttyAppearanceConfig(
|
||||
reason: String = "unspecified",
|
||||
backgroundOverride: NSColor? = nil,
|
||||
loadConfig: () -> GhosttyConfig = GhosttyConfig.load,
|
||||
loadConfig: () -> GhosttyConfig = { GhosttyConfig.load() },
|
||||
defaultBackground: () -> NSColor = { GhosttyApp.shared.defaultBackgroundColor }
|
||||
) -> GhosttyConfig {
|
||||
var next = loadConfig()
|
||||
|
|
|
|||
|
|
@ -126,6 +126,64 @@ final class GhosttyConfigTests: XCTestCase {
|
|||
XCTAssertEqual(rgb255(config.backgroundColor), RGB(red: 253, green: 246, blue: 227))
|
||||
}
|
||||
|
||||
func testLoadCachesPerColorScheme() {
|
||||
GhosttyConfig.invalidateLoadCache()
|
||||
defer { GhosttyConfig.invalidateLoadCache() }
|
||||
|
||||
var loadCount = 0
|
||||
let loadFromDisk: (GhosttyConfig.ColorSchemePreference) -> GhosttyConfig = { scheme in
|
||||
loadCount += 1
|
||||
var config = GhosttyConfig()
|
||||
config.fontFamily = "\(scheme)-\(loadCount)"
|
||||
return config
|
||||
}
|
||||
|
||||
let lightFirst = GhosttyConfig.load(
|
||||
preferredColorScheme: .light,
|
||||
loadFromDisk: loadFromDisk
|
||||
)
|
||||
let lightSecond = GhosttyConfig.load(
|
||||
preferredColorScheme: .light,
|
||||
loadFromDisk: loadFromDisk
|
||||
)
|
||||
let darkFirst = GhosttyConfig.load(
|
||||
preferredColorScheme: .dark,
|
||||
loadFromDisk: loadFromDisk
|
||||
)
|
||||
|
||||
XCTAssertEqual(loadCount, 2)
|
||||
XCTAssertEqual(lightFirst.fontFamily, "light-1")
|
||||
XCTAssertEqual(lightSecond.fontFamily, "light-1")
|
||||
XCTAssertEqual(darkFirst.fontFamily, "dark-2")
|
||||
}
|
||||
|
||||
func testLoadCacheInvalidationForcesReload() {
|
||||
GhosttyConfig.invalidateLoadCache()
|
||||
defer { GhosttyConfig.invalidateLoadCache() }
|
||||
|
||||
var loadCount = 0
|
||||
let loadFromDisk: (GhosttyConfig.ColorSchemePreference) -> GhosttyConfig = { _ in
|
||||
loadCount += 1
|
||||
var config = GhosttyConfig()
|
||||
config.fontFamily = "reload-\(loadCount)"
|
||||
return config
|
||||
}
|
||||
|
||||
let first = GhosttyConfig.load(
|
||||
preferredColorScheme: .dark,
|
||||
loadFromDisk: loadFromDisk
|
||||
)
|
||||
GhosttyConfig.invalidateLoadCache()
|
||||
let second = GhosttyConfig.load(
|
||||
preferredColorScheme: .dark,
|
||||
loadFromDisk: loadFromDisk
|
||||
)
|
||||
|
||||
XCTAssertEqual(loadCount, 2)
|
||||
XCTAssertEqual(first.fontFamily, "reload-1")
|
||||
XCTAssertEqual(second.fontFamily, "reload-2")
|
||||
}
|
||||
|
||||
func testLegacyConfigFallbackUsesLegacyFileWhenConfigGhosttyIsEmpty() {
|
||||
XCTAssertTrue(
|
||||
GhosttyApp.shouldLoadLegacyGhosttyConfig(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue