macOS: split tree zoom state should encode as path, not full node (#8505)

Fixes #8356

Zoom state should encode as a path so that it can be mapped to a
reference to the node in `root`. Previously, we were encoding a full
node which was instantiating an extra terminal on restore.

AI disclaimer: Claude code did the Codable implementation.
pull/8506/head
Mitchell Hashimoto 2025-09-03 09:08:45 -07:00 committed by GitHub
commit e6d60dee07
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 142 additions and 88 deletions

View File

@ -1,7 +1,7 @@
import AppKit
/// SplitTree represents a tree of views that can be divided.
struct SplitTree<ViewType: NSView & Codable>: Codable {
struct SplitTree<ViewType: NSView & Codable> {
/// The root of the tree. This can be nil to indicate the tree is empty.
let root: Node?
@ -29,12 +29,12 @@ struct SplitTree<ViewType: NSView & Codable>: Codable {
}
/// The path to a specific node in the tree.
struct Path {
struct Path: Codable {
let path: [Component]
var isEmpty: Bool { path.isEmpty }
enum Component {
enum Component: Codable {
case left
case right
}
@ -334,6 +334,60 @@ extension SplitTree {
}
}
// MARK: SplitTree Codable
fileprivate enum CodingKeys: String, CodingKey {
case version
case root
case zoomed
static let currentVersion: Int = 1
}
extension SplitTree: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Check version
let version = try container.decode(Int.self, forKey: .version)
guard version == CodingKeys.currentVersion else {
throw DecodingError.dataCorrupted(
DecodingError.Context(
codingPath: decoder.codingPath,
debugDescription: "Unsupported SplitTree version: \(version)"
)
)
}
// Decode root
self.root = try container.decodeIfPresent(Node.self, forKey: .root)
// Zoomed is encoded as its path. Get the path and then find it.
if let zoomedPath = try container.decodeIfPresent(Path.self, forKey: .zoomed),
let root = self.root {
self.zoomed = root.node(at: zoomedPath)
} else {
self.zoomed = nil
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
// Encode version
try container.encode(CodingKeys.currentVersion, forKey: .version)
// Encode root
try container.encodeIfPresent(root, forKey: .root)
// Zoomed is encoded as its path since its a reference type. This lets us
// map it on decode back to the correct node in root.
if let zoomed, let path = root?.path(to: zoomed) {
try container.encode(path, forKey: .zoomed)
}
}
}
// MARK: SplitTree.Node
extension SplitTree.Node {

View File

@ -4,7 +4,7 @@ import Cocoa
class TerminalRestorableState: Codable {
static let selfKey = "state"
static let versionKey = "version"
static let version: Int = 4
static let version: Int = 5
let focusedSurface: String?
let surfaceTree: SplitTree<Ghostty.SurfaceView>