From 623058a4cbcd16d14941cbbc32ca0098d958a7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Thu, 29 Oct 2026 21:26:32 +0700 Subject: [PATCH] perf(sidebar): stop redrawing the editor on every sidebar filter keystroke (#3876) (cherry picked from commit cb449271eebeec02dcbb9ace55bd891481a4e6ae) --- .../Services/Infrastructure/MainSplitViewController.swift | 1 - .../Views/Main/Extensions/MainContentView+Modifiers.swift | 1 - apple/TablePro/Views/Main/MainContentView.swift | 3 --- apple/TablePro/Views/Sidebar/DatabaseTreeNode.swift | 5 ++++- apple/TablePro/Views/Sidebar/FavoritesOutlineNode.swift | 4 +++- apple/changelog.d/3876.changed.md | 1 + 6 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 apple/changelog.d/3876.changed.md diff --git a/apple/TablePro/Core/Services/Infrastructure/MainSplitViewController.swift b/apple/TablePro/Core/Services/Infrastructure/MainSplitViewController.swift index d5e220c969..841d4cff01 100644 --- a/apple/TablePro/Core/Services/Infrastructure/MainSplitViewController.swift +++ b/apple/TablePro/Core/Services/Infrastructure/MainSplitViewController.swift @@ -982,7 +982,6 @@ internal final class MainSplitViewController: NSSplitViewController { windowTitle: windowTitleBinding(for: workspace), windowSubtitle: windowSubtitleBinding(for: workspace), windowRepresentedURL: windowRepresentedURLBinding(for: workspace), - sidebarState: SharedSidebarState.forConnection(session.connection.id), pendingTruncates: sessionBinding(for: workspace, get: { $0.pendingTruncates }, set: { $0.pendingTruncates = $1 }, defaultValue: []), pendingDeletes: sessionBinding(for: workspace, get: { $0.pendingDeletes }, set: { $0.pendingDeletes = $1 }, defaultValue: []), tableOperationOptions: sessionBinding(for: workspace, get: { $0.tableOperationOptions }, set: { $0.tableOperationOptions = $1 }, defaultValue: [:]), diff --git a/apple/TablePro/Views/Main/Extensions/MainContentView+Modifiers.swift b/apple/TablePro/Views/Main/Extensions/MainContentView+Modifiers.swift index 8c80a72b19..148cceb37f 100644 --- a/apple/TablePro/Views/Main/Extensions/MainContentView+Modifiers.swift +++ b/apple/TablePro/Views/Main/Extensions/MainContentView+Modifiers.swift @@ -21,7 +21,6 @@ import SwiftUI windowTitle: .constant("SQL Query"), windowSubtitle: .constant(""), windowRepresentedURL: .constant(nil), - sidebarState: SharedSidebarState(), pendingTruncates: .constant([]), pendingDeletes: .constant([]), tableOperationOptions: .constant([:]), diff --git a/apple/TablePro/Views/Main/MainContentView.swift b/apple/TablePro/Views/Main/MainContentView.swift index fdc7b3f76a..51dd316a4e 100644 --- a/apple/TablePro/Views/Main/MainContentView.swift +++ b/apple/TablePro/Views/Main/MainContentView.swift @@ -22,7 +22,6 @@ struct MainContentView: View { @Binding var windowSubtitle: String @Binding var windowRepresentedURL: URL? @ObservedObject var schemaService = SchemaService.shared - @ObservedObject var sidebarState: SharedSidebarState @Binding var pendingTruncates: Set @Binding var pendingDeletes: Set @Binding var tableOperationOptions: [DatabaseTreeTableRef: TableOperationOptions] @@ -59,7 +58,6 @@ struct MainContentView: View { windowTitle: Binding, windowSubtitle: Binding, windowRepresentedURL: Binding, - sidebarState: SharedSidebarState, pendingTruncates: Binding>, pendingDeletes: Binding>, tableOperationOptions: Binding<[DatabaseTreeTableRef: TableOperationOptions]>, @@ -74,7 +72,6 @@ struct MainContentView: View { self._windowTitle = windowTitle self._windowSubtitle = windowSubtitle self._windowRepresentedURL = windowRepresentedURL - self.sidebarState = sidebarState self._pendingTruncates = pendingTruncates self._pendingDeletes = pendingDeletes self._tableOperationOptions = tableOperationOptions diff --git a/apple/TablePro/Views/Sidebar/DatabaseTreeNode.swift b/apple/TablePro/Views/Sidebar/DatabaseTreeNode.swift index a179675a9d..0a8aaa293c 100644 --- a/apple/TablePro/Views/Sidebar/DatabaseTreeNode.swift +++ b/apple/TablePro/Views/Sidebar/DatabaseTreeNode.swift @@ -64,7 +64,9 @@ internal struct DatabaseTreeSidePhases: Equatable { } } -final class DatabaseTreeNode: SidebarOutlineNode { +// NSObject so the outline's item lookups hash the pointer: a plain Swift class looks up a Hashable conformance on +// every hash, about a quarter of opening a section of 17,000 tables. +final class DatabaseTreeNode: NSObject, SidebarOutlineNode { enum Status: Equatable { case loading case empty @@ -110,6 +112,7 @@ final class DatabaseTreeNode: SidebarOutlineNode { init(id: String, kind: Kind) { self.id = id self.kind = kind + super.init() } var isExpandable: Bool { diff --git a/apple/TablePro/Views/Sidebar/FavoritesOutlineNode.swift b/apple/TablePro/Views/Sidebar/FavoritesOutlineNode.swift index 0bfa034b9f..150ce581b1 100644 --- a/apple/TablePro/Views/Sidebar/FavoritesOutlineNode.swift +++ b/apple/TablePro/Views/Sidebar/FavoritesOutlineNode.swift @@ -10,7 +10,8 @@ import Foundation /// A reference type because `NSOutlineView` tracks rows by object identity, which is what lets a /// reload keep the expansion and selection a user set. The coordinator caches these by `id` and /// mutates the cached instance rather than building a new one, exactly as the object tree does. -internal final class FavoritesOutlineNode: SidebarOutlineNode { +/// An `NSObject` for the same cheap hashing as `DatabaseTreeNode`. +internal final class FavoritesOutlineNode: NSObject, SidebarOutlineNode { internal enum Kind { case header(String) case databaseEnvironment(FavoriteDatabaseGroup) @@ -26,6 +27,7 @@ internal final class FavoritesOutlineNode: SidebarOutlineNode { internal init(id: String, kind: Kind) { self.id = id self.kind = kind + super.init() } internal var isExpandable: Bool { diff --git a/apple/changelog.d/3876.changed.md b/apple/changelog.d/3876.changed.md new file mode 100644 index 0000000000..f46be23006 --- /dev/null +++ b/apple/changelog.d/3876.changed.md @@ -0,0 +1 @@ +Typing in the sidebar filter no longer redraws the editor and results on every keystroke.