Address PR review feedback
This commit is contained in:
parent
1291776597
commit
0285cd1f51
3 changed files with 51 additions and 12 deletions
|
|
@ -2590,6 +2590,11 @@ struct ContentView: View {
|
||||||
case completed(reason: String)
|
case completed(reason: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum BackgroundWorkspacePrimePolicy {
|
||||||
|
static let timeoutSeconds: TimeInterval = 2.0
|
||||||
|
static let pollIntervalNanoseconds: UInt64 = 50_000_000
|
||||||
|
}
|
||||||
|
|
||||||
private func primeBackgroundWorkspaceIfNeeded(workspaceId: UUID) async {
|
private func primeBackgroundWorkspaceIfNeeded(workspaceId: UUID) async {
|
||||||
let shouldPrime = await MainActor.run {
|
let shouldPrime = await MainActor.run {
|
||||||
tabManager.pendingBackgroundWorkspaceLoadIds.contains(workspaceId)
|
tabManager.pendingBackgroundWorkspaceLoadIds.contains(workspaceId)
|
||||||
|
|
@ -2601,7 +2606,7 @@ struct ContentView: View {
|
||||||
dlog("workspace.backgroundPrime.start workspace=\(workspaceId.uuidString.prefix(5))")
|
dlog("workspace.backgroundPrime.start workspace=\(workspaceId.uuidString.prefix(5))")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let timeout = Date().addingTimeInterval(2.0)
|
let timeout = Date().addingTimeInterval(BackgroundWorkspacePrimePolicy.timeoutSeconds)
|
||||||
while !Task.isCancelled {
|
while !Task.isCancelled {
|
||||||
let state = await MainActor.run {
|
let state = await MainActor.run {
|
||||||
stepBackgroundWorkspacePrime(workspaceId: workspaceId)
|
stepBackgroundWorkspacePrime(workspaceId: workspaceId)
|
||||||
|
|
@ -2609,7 +2614,7 @@ struct ContentView: View {
|
||||||
switch state {
|
switch state {
|
||||||
case .pending:
|
case .pending:
|
||||||
if Date() < timeout {
|
if Date() < timeout {
|
||||||
try? await Task.sleep(nanoseconds: 50_000_000)
|
try? await Task.sleep(nanoseconds: BackgroundWorkspacePrimePolicy.pollIntervalNanoseconds)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
|
|
|
||||||
|
|
@ -635,6 +635,7 @@ class TabManager: ObservableObject {
|
||||||
qos: .utility
|
qos: .utility
|
||||||
)
|
)
|
||||||
private var initialWorkspaceGitProbeGenerationByWorkspace: [UUID: UUID] = [:]
|
private var initialWorkspaceGitProbeGenerationByWorkspace: [UUID: UUID] = [:]
|
||||||
|
private var initialWorkspaceGitProbeTimersByWorkspace: [UUID: [DispatchSourceTimer]] = [:]
|
||||||
|
|
||||||
// Recent tab history for back/forward navigation (like browser history)
|
// Recent tab history for back/forward navigation (like browser history)
|
||||||
private var tabHistory: [UUID] = []
|
private var tabHistory: [UUID] = []
|
||||||
|
|
@ -870,6 +871,7 @@ class TabManager: ObservableObject {
|
||||||
) {
|
) {
|
||||||
let normalizedDirectory = normalizeDirectory(directory)
|
let normalizedDirectory = normalizeDirectory(directory)
|
||||||
let generation = UUID()
|
let generation = UUID()
|
||||||
|
cancelInitialWorkspaceGitProbeTimers(workspaceId: workspaceId)
|
||||||
initialWorkspaceGitProbeGenerationByWorkspace[workspaceId] = generation
|
initialWorkspaceGitProbeGenerationByWorkspace[workspaceId] = generation
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
@ -880,9 +882,12 @@ class TabManager: ObservableObject {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let delays = Self.initialWorkspaceGitProbeDelays
|
let delays = Self.initialWorkspaceGitProbeDelays
|
||||||
|
var timers: [DispatchSourceTimer] = []
|
||||||
for (index, delay) in delays.enumerated() {
|
for (index, delay) in delays.enumerated() {
|
||||||
let isLastAttempt = index == delays.count - 1
|
let isLastAttempt = index == delays.count - 1
|
||||||
initialWorkspaceGitProbeQueue.asyncAfter(deadline: .now() + delay) {
|
let timer = DispatchSource.makeTimerSource(queue: initialWorkspaceGitProbeQueue)
|
||||||
|
timer.schedule(deadline: .now() + delay, repeating: .never)
|
||||||
|
timer.setEventHandler { [weak self] in
|
||||||
let snapshot = Self.initialWorkspaceGitMetadataSnapshot(for: normalizedDirectory)
|
let snapshot = Self.initialWorkspaceGitMetadataSnapshot(for: normalizedDirectory)
|
||||||
Task { @MainActor [weak self] in
|
Task { @MainActor [weak self] in
|
||||||
self?.applyInitialWorkspaceGitMetadataSnapshot(
|
self?.applyInitialWorkspaceGitMetadataSnapshot(
|
||||||
|
|
@ -895,7 +900,25 @@ class TabManager: ObservableObject {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
timers.append(timer)
|
||||||
|
timer.resume()
|
||||||
}
|
}
|
||||||
|
initialWorkspaceGitProbeTimersByWorkspace[workspaceId] = timers
|
||||||
|
}
|
||||||
|
|
||||||
|
private func cancelInitialWorkspaceGitProbeTimers(workspaceId: UUID) {
|
||||||
|
guard let timers = initialWorkspaceGitProbeTimersByWorkspace.removeValue(forKey: workspaceId) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for timer in timers {
|
||||||
|
timer.setEventHandler {}
|
||||||
|
timer.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func clearInitialWorkspaceGitProbe(workspaceId: UUID) {
|
||||||
|
initialWorkspaceGitProbeGenerationByWorkspace.removeValue(forKey: workspaceId)
|
||||||
|
cancelInitialWorkspaceGitProbeTimers(workspaceId: workspaceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func applyInitialWorkspaceGitMetadataSnapshot(
|
private func applyInitialWorkspaceGitMetadataSnapshot(
|
||||||
|
|
@ -909,17 +932,17 @@ class TabManager: ObservableObject {
|
||||||
defer {
|
defer {
|
||||||
if isLastAttempt,
|
if isLastAttempt,
|
||||||
initialWorkspaceGitProbeGenerationByWorkspace[workspaceId] == generation {
|
initialWorkspaceGitProbeGenerationByWorkspace[workspaceId] == generation {
|
||||||
initialWorkspaceGitProbeGenerationByWorkspace.removeValue(forKey: workspaceId)
|
clearInitialWorkspaceGitProbe(workspaceId: workspaceId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard initialWorkspaceGitProbeGenerationByWorkspace[workspaceId] == generation else { return }
|
guard initialWorkspaceGitProbeGenerationByWorkspace[workspaceId] == generation else { return }
|
||||||
guard let workspace = tabs.first(where: { $0.id == workspaceId }) else {
|
guard let workspace = tabs.first(where: { $0.id == workspaceId }) else {
|
||||||
initialWorkspaceGitProbeGenerationByWorkspace.removeValue(forKey: workspaceId)
|
clearInitialWorkspaceGitProbe(workspaceId: workspaceId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard workspace.panels[panelId] != nil else {
|
guard workspace.panels[panelId] != nil else {
|
||||||
initialWorkspaceGitProbeGenerationByWorkspace.removeValue(forKey: workspaceId)
|
clearInitialWorkspaceGitProbe(workspaceId: workspaceId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -927,7 +950,7 @@ class TabManager: ObservableObject {
|
||||||
workspace.panelDirectories[panelId] ?? workspace.currentDirectory
|
workspace.panelDirectories[panelId] ?? workspace.currentDirectory
|
||||||
)
|
)
|
||||||
if let currentDirectory, currentDirectory != expectedDirectory {
|
if let currentDirectory, currentDirectory != expectedDirectory {
|
||||||
initialWorkspaceGitProbeGenerationByWorkspace.removeValue(forKey: workspaceId)
|
clearInitialWorkspaceGitProbe(workspaceId: workspaceId)
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
dlog(
|
dlog(
|
||||||
"workspace.gitProbe.skip workspace=\(workspaceId.uuidString.prefix(5)) " +
|
"workspace.gitProbe.skip workspace=\(workspaceId.uuidString.prefix(5)) " +
|
||||||
|
|
@ -980,7 +1003,7 @@ class TabManager: ObservableObject {
|
||||||
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
|
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
|
||||||
process.arguments = ["git", "-C", directory] + arguments
|
process.arguments = ["git", "-C", directory] + arguments
|
||||||
process.standardOutput = stdout
|
process.standardOutput = stdout
|
||||||
process.standardError = Pipe()
|
process.standardError = FileHandle.nullDevice
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try process.run()
|
try process.run()
|
||||||
|
|
@ -988,12 +1011,13 @@ class TabManager: ObservableObject {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drain stdout while the subprocess is active so large repos cannot fill the pipe buffer.
|
||||||
|
let data = stdout.fileHandleForReading.readDataToEndOfFile()
|
||||||
process.waitUntilExit()
|
process.waitUntilExit()
|
||||||
guard process.terminationStatus == 0 else {
|
guard process.terminationStatus == 0 else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = stdout.fileHandleForReading.readDataToEndOfFile()
|
|
||||||
return String(data: data, encoding: .utf8)
|
return String(data: data, encoding: .utf8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1196,7 +1220,7 @@ class TabManager: ObservableObject {
|
||||||
guard tabs.count > 1 else { return }
|
guard tabs.count > 1 else { return }
|
||||||
guard let index = tabs.firstIndex(where: { $0.id == workspace.id }) else { return }
|
guard let index = tabs.firstIndex(where: { $0.id == workspace.id }) else { return }
|
||||||
sentryBreadcrumb("workspace.close", data: ["tabCount": tabs.count - 1])
|
sentryBreadcrumb("workspace.close", data: ["tabCount": tabs.count - 1])
|
||||||
initialWorkspaceGitProbeGenerationByWorkspace.removeValue(forKey: workspace.id)
|
clearInitialWorkspaceGitProbe(workspaceId: workspace.id)
|
||||||
|
|
||||||
AppDelegate.shared?.notificationStore?.clearNotifications(forTabId: workspace.id)
|
AppDelegate.shared?.notificationStore?.clearNotifications(forTabId: workspace.id)
|
||||||
unwireClosedBrowserTracking(for: workspace)
|
unwireClosedBrowserTracking(for: workspace)
|
||||||
|
|
@ -1218,7 +1242,7 @@ class TabManager: ObservableObject {
|
||||||
@discardableResult
|
@discardableResult
|
||||||
func detachWorkspace(tabId: UUID) -> Workspace? {
|
func detachWorkspace(tabId: UUID) -> Workspace? {
|
||||||
guard let index = tabs.firstIndex(where: { $0.id == tabId }) else { return nil }
|
guard let index = tabs.firstIndex(where: { $0.id == tabId }) else { return nil }
|
||||||
initialWorkspaceGitProbeGenerationByWorkspace.removeValue(forKey: tabId)
|
clearInitialWorkspaceGitProbe(workspaceId: tabId)
|
||||||
|
|
||||||
let removed = tabs.remove(at: index)
|
let removed = tabs.remove(at: index)
|
||||||
unwireClosedBrowserTracking(for: removed)
|
unwireClosedBrowserTracking(for: removed)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
@ -16,7 +17,16 @@ sys.path.insert(0, str(Path(__file__).parent))
|
||||||
from cmux import cmux, cmuxError
|
from cmux import cmux, cmuxError
|
||||||
|
|
||||||
|
|
||||||
SOCKET_PATH = os.environ.get("CMUX_SOCKET", "/tmp/cmux-debug.sock")
|
def _resolve_socket_path() -> str:
|
||||||
|
socket_path = os.environ.get("CMUX_SOCKET", "").strip()
|
||||||
|
if not socket_path:
|
||||||
|
raise cmuxError("CMUX_SOCKET is required (expected /tmp/cmux-debug-<tag>.sock)")
|
||||||
|
if not re.fullmatch(r"/tmp/cmux-debug-[^/]+\.sock", socket_path):
|
||||||
|
raise cmuxError(f"CMUX_SOCKET must be a tagged debug socket, got: {socket_path!r}")
|
||||||
|
return socket_path
|
||||||
|
|
||||||
|
|
||||||
|
SOCKET_PATH = _resolve_socket_path()
|
||||||
|
|
||||||
|
|
||||||
def _must(cond: bool, msg: str) -> None:
|
def _must(cond: bool, msg: str) -> None:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue