Skip to content

Commit d8945ea

Browse files
datlechinclaude
andcommitted
refactor(datagrid): unify cell edit path, class-based RowProvider cache (#918)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2324323 commit d8945ea

7 files changed

Lines changed: 139 additions & 173 deletions

TablePro/Views/Main/Child/MainEditorContentView.swift

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,6 @@ private struct SortedRowsCache {
1818
let resultVersion: Int
1919
}
2020

21-
/// Per-tab row provider cache entry — groups all cache-invalidation keys together
22-
private struct RowProviderCacheEntry {
23-
let provider: InMemoryRowProvider
24-
let resultVersion: Int
25-
let metadataVersion: Int
26-
let sortState: SortState
27-
}
28-
2921
/// Main editor content with tab bar and content switching
3022
struct MainEditorContentView: View {
3123
// MARK: - Dependencies
@@ -69,8 +61,7 @@ struct MainEditorContentView: View {
6961

7062
@State private var sortCache: [UUID: SortedRowsCache] = [:]
7163

72-
// Per-tab row provider cache — avoids recreation on every SwiftUI render.
73-
@State private var tabProviderCache: [UUID: RowProviderCacheEntry] = [:]
64+
@State private var providerCache = RowProviderCache()
7465
@State private var cachedChangeManager: AnyChangeManager?
7566
@State private var erDiagramViewModels: [UUID: ERDiagramViewModel] = [:]
7667
@State private var serverDashboardViewModels: [UUID: ServerDashboardViewModel] = [:]
@@ -127,26 +118,28 @@ struct MainEditorContentView: View {
127118
favoriteDialogQuery = FavoriteDialogQuery(query: query)
128119
}
129120
.onChange(of: tabManager.tabIds) { _, newIds in
130-
guard !sortCache.isEmpty || !tabProviderCache.isEmpty || !erDiagramViewModels.isEmpty
121+
guard !sortCache.isEmpty || !providerCache.isEmpty || !erDiagramViewModels.isEmpty
131122
|| !serverDashboardViewModels.isEmpty else {
132123
coordinator.cleanupSortCache(openTabIds: Set(newIds))
133124
return
134125
}
135126
let openTabIds = Set(newIds)
136127
sortCache = sortCache.filter { openTabIds.contains($0.key) }
137128
coordinator.cleanupSortCache(openTabIds: openTabIds)
138-
tabProviderCache = tabProviderCache.filter { openTabIds.contains($0.key) }
129+
providerCache.retain(tabIds: openTabIds)
139130
erDiagramViewModels = erDiagramViewModels.filter { openTabIds.contains($0.key) }
140131
serverDashboardViewModels = serverDashboardViewModels.filter { openTabIds.contains($0.key) }
141132
}
142-
.onChange(of: tabManager.selectedTabId) { _, newId in
133+
.onChange(of: tabManager.selectedTabId) { _, _ in
143134
updateHasQueryText()
144135

145-
guard let newId, let tab = tabManager.selectedTab else { return }
146-
let cached = tabProviderCache[newId]
147-
if cached?.resultVersion != tab.resultVersion
148-
|| cached?.metadataVersion != tab.metadataVersion
149-
{
136+
guard let tab = tabManager.selectedTab else { return }
137+
if providerCache.provider(
138+
for: tab.id,
139+
resultVersion: tab.resultVersion,
140+
metadataVersion: tab.metadataVersion,
141+
sortState: tab.sortState
142+
) == nil {
150143
cacheRowProvider(for: tab)
151144
}
152145
}
@@ -157,7 +150,7 @@ struct MainEditorContentView: View {
157150
cacheRowProvider(for: tab)
158151
}
159152
coordinator.onTeardown = { [self] in
160-
tabProviderCache.removeAll()
153+
providerCache.removeAll()
161154
sortCache.removeAll()
162155
cachedChangeManager = nil
163156
}
@@ -570,32 +563,33 @@ struct MainEditorContentView: View {
570563

571564
private func rowProvider(for tab: QueryTab) -> InMemoryRowProvider {
572565
if tab.rowBuffer.isEvicted {
573-
Task { @MainActor in tabProviderCache.removeValue(forKey: tab.id) }
566+
providerCache.remove(for: tab.id)
574567
return makeRowProvider(for: tab)
575568
}
576-
if let entry = tabProviderCache[tab.id],
577-
entry.resultVersion == tab.resultVersion,
578-
entry.metadataVersion == tab.metadataVersion,
579-
entry.sortState == tab.sortState
580-
{
581-
return entry.provider
569+
if let cached = providerCache.provider(
570+
for: tab.id,
571+
resultVersion: tab.resultVersion,
572+
metadataVersion: tab.metadataVersion,
573+
sortState: tab.sortState
574+
) {
575+
return cached
582576
}
583577
let provider = makeRowProvider(for: tab)
584-
Task { @MainActor in
585-
tabProviderCache[tab.id] = RowProviderCacheEntry(
586-
provider: provider,
587-
resultVersion: tab.resultVersion,
588-
metadataVersion: tab.metadataVersion,
589-
sortState: tab.sortState
590-
)
591-
}
578+
providerCache.store(
579+
provider,
580+
for: tab.id,
581+
resultVersion: tab.resultVersion,
582+
metadataVersion: tab.metadataVersion,
583+
sortState: tab.sortState
584+
)
592585
return provider
593586
}
594587

595588
private func cacheRowProvider(for tab: QueryTab) {
596589
let provider = makeRowProvider(for: tab)
597-
tabProviderCache[tab.id] = RowProviderCacheEntry(
598-
provider: provider,
590+
providerCache.store(
591+
provider,
592+
for: tab.id,
599593
resultVersion: tab.resultVersion,
600594
metadataVersion: tab.metadataVersion,
601595
sortState: tab.sortState

TablePro/Views/Results/DataGridView+RowActions.swift

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -74,28 +74,7 @@ extension TableViewCoordinator {
7474

7575
@MainActor
7676
func setCellValueAtColumn(_ value: String?, at rowIndex: Int, columnIndex: Int) {
77-
guard let tableView = tableView else { return }
78-
guard columnIndex >= 0 && columnIndex < rowProvider.columns.count else { return }
79-
80-
let columnName = rowProvider.columns[columnIndex]
81-
let oldValue = rowProvider.value(atRow: rowIndex, column: columnIndex)
82-
let originalRow = rowProvider.rowValues(at: rowIndex) ?? []
83-
84-
changeManager.recordCellChange(
85-
rowIndex: rowIndex,
86-
columnIndex: columnIndex,
87-
columnName: columnName,
88-
oldValue: oldValue,
89-
newValue: value,
90-
originalRow: originalRow
91-
)
92-
93-
rowProvider.updateValue(value, at: rowIndex, columnIndex: columnIndex)
94-
95-
let tableColumnIndex = columnIndex + 1
96-
tableView.reloadData(
97-
forRowIndexes: IndexSet(integer: rowIndex),
98-
columnIndexes: IndexSet(integer: tableColumnIndex))
77+
commitCellEdit(row: rowIndex, columnIndex: columnIndex, newValue: value)
9978
}
10079

10180
func copyCellValue(at rowIndex: Int, columnIndex: Int) {

TablePro/Views/Results/DataGridView+TypePicker.swift

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,7 @@ extension TableViewCoordinator {
3030
currentValue: currentValue,
3131
onCommit: { newValue in
3232
guard let self else { return }
33-
self.commitPopoverEdit(
34-
tableView: tableView,
35-
row: row,
36-
column: column,
37-
columnIndex: columnIndex,
38-
newValue: newValue
39-
)
33+
self.commitPopoverEdit(row: row, columnIndex: columnIndex, newValue: newValue)
4034
},
4135
onDismiss: dismiss
4236
)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//
2+
// DataGridView+CellCommit.swift
3+
// TablePro
4+
//
5+
6+
import AppKit
7+
8+
extension TableViewCoordinator {
9+
func commitCellEdit(row: Int, columnIndex: Int, newValue: String?) {
10+
guard let tableView else { return }
11+
guard columnIndex >= 0 && columnIndex < rowProvider.columns.count else { return }
12+
13+
let oldValue = rowProvider.value(atRow: row, column: columnIndex)
14+
guard oldValue != newValue else { return }
15+
16+
let columnName = rowProvider.columns[columnIndex]
17+
changeManager.recordCellChange(
18+
rowIndex: row,
19+
columnIndex: columnIndex,
20+
columnName: columnName,
21+
oldValue: oldValue,
22+
newValue: newValue,
23+
originalRow: rowProvider.rowValues(at: row) ?? []
24+
)
25+
26+
rowProvider.updateValue(newValue, at: row, columnIndex: columnIndex)
27+
delegate?.dataGridDidEditCell(row: row, column: columnIndex, newValue: newValue)
28+
29+
let tableColumnIndex = DataGridView.tableColumnIndex(for: columnIndex)
30+
tableView.reloadData(
31+
forRowIndexes: IndexSet(integer: row),
32+
columnIndexes: IndexSet(integer: tableColumnIndex)
33+
)
34+
}
35+
}

TablePro/Views/Results/Extensions/DataGridView+Editing.swift

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -78,24 +78,7 @@ extension TableViewCoordinator {
7878
}
7979

8080
func commitOverlayEdit(row: Int, columnIndex: Int, newValue: String) {
81-
let oldValue = rowProvider.value(atRow: row, column: columnIndex)
82-
guard oldValue != newValue else { return }
83-
84-
let columnName = rowProvider.columns[columnIndex]
85-
changeManager.recordCellChange(
86-
rowIndex: row,
87-
columnIndex: columnIndex,
88-
columnName: columnName,
89-
oldValue: oldValue,
90-
newValue: newValue,
91-
originalRow: rowProvider.rowValues(at: row) ?? []
92-
)
93-
94-
rowProvider.updateValue(newValue, at: row, columnIndex: columnIndex)
95-
delegate?.dataGridDidEditCell(row: row, column: columnIndex, newValue: newValue)
96-
97-
let tableColumnIndex = DataGridView.tableColumnIndex(for: columnIndex)
98-
tableView?.reloadData(forRowIndexes: IndexSet(integer: row), columnIndexes: IndexSet(integer: tableColumnIndex))
81+
commitCellEdit(row: row, columnIndex: columnIndex, newValue: newValue)
9982
}
10083

10184
func handleOverlayTabNavigation(row: Int, column: Int, forward: Bool) {
@@ -159,24 +142,7 @@ extension TableViewCoordinator {
159142
let oldValue = rowProvider.value(atRow: row, column: columnIndex)
160143
let newValue: String? = rawInput.isEmpty && oldValue == nil ? nil : rawInput
161144

162-
guard oldValue != newValue else { return true }
163-
164-
let columnName = rowProvider.columns[columnIndex]
165-
changeManager.recordCellChange(
166-
rowIndex: row,
167-
columnIndex: columnIndex,
168-
columnName: columnName,
169-
oldValue: oldValue,
170-
newValue: newValue,
171-
originalRow: rowProvider.rowValues(at: row) ?? []
172-
)
173-
174-
rowProvider.updateValue(newValue, at: row, columnIndex: columnIndex)
175-
delegate?.dataGridDidEditCell(row: row, column: columnIndex, newValue: newValue)
176-
177-
Task { @MainActor in
178-
tableView.reloadData(forRowIndexes: IndexSet(integer: row), columnIndexes: IndexSet(integer: column))
179-
}
145+
commitCellEdit(row: row, columnIndex: columnIndex, newValue: newValue)
180146

181147
(control as? CellTextField)?.restoreTruncatedDisplay()
182148

0 commit comments

Comments
 (0)