Stabilize sidebar directory ordering when split focus changes (#1798)
* Add sidebar directory ordering regression test * Stabilize sidebar directory ordering --------- Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
This commit is contained in:
parent
7cbac356fc
commit
0010e10bf5
3 changed files with 119 additions and 12 deletions
|
|
@ -11731,17 +11731,10 @@ private struct TabItemView: View, Equatable {
|
|||
}
|
||||
|
||||
private func directorySummaryText(orderedPanelIds: [UUID]) -> String? {
|
||||
guard !tab.panels.isEmpty else { return nil }
|
||||
let home = SidebarPathFormatter.homeDirectoryPath
|
||||
var seen: Set<String> = []
|
||||
var entries: [String] = []
|
||||
for panelId in orderedPanelIds {
|
||||
let directory = tab.panelDirectories[panelId] ?? tab.currentDirectory
|
||||
let entries = tab.sidebarDirectoriesInDisplayOrder(orderedPanelIds: orderedPanelIds).compactMap { directory in
|
||||
let shortened = SidebarPathFormatter.shortenedPath(directory, homeDirectoryPath: home)
|
||||
guard !shortened.isEmpty else { continue }
|
||||
if seen.insert(shortened).inserted {
|
||||
entries.append(shortened)
|
||||
}
|
||||
return shortened.isEmpty ? nil : shortened
|
||||
}
|
||||
return entries.isEmpty ? nil : entries.joined(separator: " | ")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4739,7 +4739,7 @@ enum SidebarBranchOrdering {
|
|||
for panelId in orderedPanelIds {
|
||||
let panelBranch = normalized(panelBranches[panelId]?.branch)
|
||||
let branch = panelBranch ?? defaultBranchForPanels
|
||||
let directory = normalized(panelDirectories[panelId] ?? defaultDirectory)
|
||||
let directory = normalized(panelDirectories[panelId])
|
||||
guard branch != nil || directory != nil else { continue }
|
||||
|
||||
let panelDirty = panelBranch != nil
|
||||
|
|
@ -5933,6 +5933,67 @@ final class Workspace: Identifiable, ObservableObject {
|
|||
)
|
||||
}
|
||||
|
||||
private func normalizedSidebarDirectory(_ directory: String?) -> String? {
|
||||
guard let directory else { return nil }
|
||||
let trimmed = directory.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
return trimmed.isEmpty ? nil : trimmed
|
||||
}
|
||||
|
||||
private func canonicalSidebarDirectoryKey(_ directory: String?) -> String? {
|
||||
guard let directory = normalizedSidebarDirectory(directory) else { return nil }
|
||||
let expanded = NSString(string: directory).expandingTildeInPath
|
||||
let standardized = NSString(string: expanded).standardizingPath
|
||||
let cleaned = standardized.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
return cleaned.isEmpty ? nil : cleaned
|
||||
}
|
||||
|
||||
private func sidebarResolvedDirectory(for panelId: UUID) -> String? {
|
||||
if let directory = normalizedSidebarDirectory(panelDirectories[panelId]) {
|
||||
return directory
|
||||
}
|
||||
if let requestedDirectory = normalizedSidebarDirectory(
|
||||
terminalPanel(for: panelId)?.requestedWorkingDirectory
|
||||
) {
|
||||
return requestedDirectory
|
||||
}
|
||||
guard panelId == focusedPanelId else { return nil }
|
||||
return normalizedSidebarDirectory(currentDirectory)
|
||||
}
|
||||
|
||||
private func sidebarResolvedPanelDirectories(orderedPanelIds: [UUID]) -> [UUID: String] {
|
||||
var resolved: [UUID: String] = [:]
|
||||
for panelId in orderedPanelIds {
|
||||
if let directory = sidebarResolvedDirectory(for: panelId) {
|
||||
resolved[panelId] = directory
|
||||
}
|
||||
}
|
||||
return resolved
|
||||
}
|
||||
|
||||
func sidebarDirectoriesInDisplayOrder(orderedPanelIds: [UUID]) -> [String] {
|
||||
let resolvedDirectories = sidebarResolvedPanelDirectories(orderedPanelIds: orderedPanelIds)
|
||||
var ordered: [String] = []
|
||||
var seen: Set<String> = []
|
||||
|
||||
for panelId in orderedPanelIds {
|
||||
guard let directory = resolvedDirectories[panelId],
|
||||
let key = canonicalSidebarDirectoryKey(directory) else { continue }
|
||||
if seen.insert(key).inserted {
|
||||
ordered.append(directory)
|
||||
}
|
||||
}
|
||||
|
||||
if ordered.isEmpty, let fallbackDirectory = normalizedSidebarDirectory(currentDirectory) {
|
||||
return [fallbackDirectory]
|
||||
}
|
||||
|
||||
return ordered
|
||||
}
|
||||
|
||||
func sidebarDirectoriesInDisplayOrder() -> [String] {
|
||||
sidebarDirectoriesInDisplayOrder(orderedPanelIds: sidebarOrderedPanelIds())
|
||||
}
|
||||
|
||||
func sidebarGitBranchesInDisplayOrder(orderedPanelIds: [UUID]) -> [SidebarGitBranchState] {
|
||||
SidebarBranchOrdering
|
||||
.orderedUniqueBranches(
|
||||
|
|
@ -5953,8 +6014,8 @@ final class Workspace: Identifiable, ObservableObject {
|
|||
SidebarBranchOrdering.orderedUniqueBranchDirectoryEntries(
|
||||
orderedPanelIds: orderedPanelIds,
|
||||
panelBranches: panelGitBranches,
|
||||
panelDirectories: panelDirectories,
|
||||
defaultDirectory: currentDirectory,
|
||||
panelDirectories: sidebarResolvedPanelDirectories(orderedPanelIds: orderedPanelIds),
|
||||
defaultDirectory: normalizedSidebarDirectory(currentDirectory),
|
||||
fallbackBranch: gitBranch
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1629,6 +1629,59 @@ final class WorkspacePanelGitBranchTests: XCTestCase {
|
|||
XCTAssertEqual(branches.map(\.isDirty), [true, false, false])
|
||||
}
|
||||
|
||||
func testSidebarBranchDirectoryEntriesStayStableAcrossFocusedSplitChanges() {
|
||||
let workspace = Workspace()
|
||||
let leftLiveDirectory = "/repo/left/live"
|
||||
let rightFocusedDirectory = "/repo/right/focused"
|
||||
let leftFocusedDirectory = "/repo/left/focused"
|
||||
let rightRequestedDirectory = "/repo/right/requested"
|
||||
|
||||
guard let leftPanelId = workspace.focusedPanelId else {
|
||||
XCTFail("Expected initial focused panel")
|
||||
return
|
||||
}
|
||||
|
||||
workspace.updatePanelDirectory(panelId: leftPanelId, directory: leftLiveDirectory)
|
||||
|
||||
guard let rightSplitPanel = workspace.newTerminalSplit(
|
||||
from: leftPanelId,
|
||||
orientation: .horizontal,
|
||||
focus: false
|
||||
),
|
||||
let rightPaneId = workspace.paneId(forPanelId: rightSplitPanel.id),
|
||||
let rightRequestedPanel = workspace.newTerminalSurface(
|
||||
inPane: rightPaneId,
|
||||
focus: false,
|
||||
workingDirectory: rightRequestedDirectory
|
||||
) else {
|
||||
XCTFail("Expected right split panes for sidebar directory ordering test")
|
||||
return
|
||||
}
|
||||
|
||||
let orderedPanelIds = workspace.sidebarOrderedPanelIds()
|
||||
XCTAssertEqual(orderedPanelIds, [leftPanelId, rightSplitPanel.id, rightRequestedPanel.id])
|
||||
|
||||
workspace.currentDirectory = rightFocusedDirectory
|
||||
let entriesWhenRightLooksFocused = workspace.sidebarBranchDirectoryEntriesInDisplayOrder(
|
||||
orderedPanelIds: orderedPanelIds
|
||||
)
|
||||
|
||||
workspace.currentDirectory = leftFocusedDirectory
|
||||
let entriesWhenLeftLooksFocused = workspace.sidebarBranchDirectoryEntriesInDisplayOrder(
|
||||
orderedPanelIds: orderedPanelIds
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
entriesWhenRightLooksFocused,
|
||||
entriesWhenLeftLooksFocused,
|
||||
"Expected sidebar directory ordering to ignore focused-workspace cwd churn when panel-specific directories are available"
|
||||
)
|
||||
XCTAssertEqual(
|
||||
entriesWhenRightLooksFocused.map(\.directory),
|
||||
[leftLiveDirectory, rightRequestedDirectory]
|
||||
)
|
||||
}
|
||||
|
||||
func testSidebarDerivedCollectionsMatchWhenUsingPrecomputedPanelOrder() {
|
||||
let workspace = Workspace()
|
||||
guard let leftFirstPanelId = workspace.focusedPanelId,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue