дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +02:00
fix(editor): keep the saved layout when a session restore meets open tabs (#2325)
A session restore that lands after tabs were already opened (a file launched into LightEdit while the window was still restoring, or a file opened from the CLI into a project window during startup) appended every restored tab to the current pane: saved splits collapsed into one pane, restored pinned tabs landed after unpinned ones, a pane could hold several preview tabs, and the tab limit was skipped. The restore now starts from the saved state and reopens the live tabs into it through the normal tab rules (pin order, one preview per pane, tab limit without evicting the live tabs), keeping their slots and line/column targets; the launched tab ends up active and focused in the saved active pane. An empty window still takes the saved layout as is. Refs #2229
Цей коміт міститься в:
1 джерело
2680ba8463
коміт
0e673c206a
3 files changed
+266
-47
No files matched your search
@@ -338,8 +338,8 @@ export class EditorController implements SlotHost {
|
||||
.catch((e) => this.documentError(e))
|
||||
.then(() => {
|
||||
if (generation !== this.projectGeneration) return;
|
||||
if (root) this.restoreSession(root);
|
||||
else if (s.lightEdit) this.restoreSession(LIGHT_EDIT_SESSION);
|
||||
if (root) return this.restoreSession(root);
|
||||
else if (s.lightEdit) return this.restoreSession(LIGHT_EDIT_SESSION);
|
||||
});
|
||||
} else if (previous.opening && !s.opening && this.closeCommitted) {
|
||||
// A failed project open returns to the same project.
|
||||
@@ -354,7 +354,7 @@ export class EditorController implements SlotHost {
|
||||
this.upgradeWrappedImageTabs(wrappers);
|
||||
}),
|
||||
);
|
||||
if (prevRoot) this.restoreSession(prevRoot);
|
||||
if (prevRoot) this.projectTransition = this.restoreSession(prevRoot);
|
||||
if (this.manager.backend.onFsChanged)
|
||||
this.disposers.push(this.manager.backend.onFsChanged((e) => this.onFilesRemoved(e.removed)));
|
||||
const onWindowBlur = () => {
|
||||
@@ -520,7 +520,7 @@ export class EditorController implements SlotHost {
|
||||
}
|
||||
|
||||
/** Reopens the tabs, splits and carets saved for `root` (after the project opened). */
|
||||
restoreSession(root: string) {
|
||||
async restoreSession(root: string) {
|
||||
this.sessionRoot = root;
|
||||
const session = loadSession(root);
|
||||
if (!session) return;
|
||||
@@ -528,17 +528,29 @@ export class EditorController implements SlotHost {
|
||||
const kinds = useContributions.getState().editorKinds;
|
||||
for (const pane of Object.values(state.panes))
|
||||
pane.tabs = pane.tabs.map((tab) => (documentLifecycle(tab.input, kinds) ? { ...tab, preview: false } : tab));
|
||||
this.restoredViews = new Map(Object.entries(session.files));
|
||||
const current = useTabs.getState();
|
||||
const liveKeys = openKeys(current);
|
||||
this.restoredViews = new Map(Object.entries(session.files).filter(([path]) => !liveKeys.has(fileKey(path))));
|
||||
this.restoredTabs = new Set(Object.values(state.panes).flatMap((p) => p.tabs.map((t) => t.id)));
|
||||
this.restoring = true;
|
||||
try {
|
||||
// A restore landing after tabs were already opened (e.g. a launched file
|
||||
// admitted while the transition was still in flight) must not wipe them.
|
||||
useTabs.setState((current) => mergeSessionTabs(current, state), true);
|
||||
useTabs.setState(mergeSessionTabs(current, state), true);
|
||||
} finally {
|
||||
this.restoring = false;
|
||||
}
|
||||
for (const pane of Object.values(state.panes)) if (pane.active) void this.ensureSlot(pane.active);
|
||||
if (liveKeys.size)
|
||||
for (const paneId of paneIds(useTabs.getState())) await this.enforcePaneTabLimit(paneId, undefined, liveKeys);
|
||||
const next = useTabs.getState();
|
||||
for (const pane of Object.values(next.panes)) if (pane.active) void this.ensureSlot(pane.active);
|
||||
if (liveKeys.size) {
|
||||
const active = this.activeTabOf(next);
|
||||
if (active) {
|
||||
await this.ensureSlot(active.id);
|
||||
if (this.activeTabOf()?.id === active.id) this.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private activeTabOf(s: TabsState = useTabs.getState()): Tab | null {
|
||||
@@ -1131,24 +1143,7 @@ export class EditorController implements SlotHost {
|
||||
this.admitting = false;
|
||||
}
|
||||
const canFocus = () => this.isCurrentOpening(generation) && this.activeTabOf()?.id === tabId;
|
||||
const tabPrefs = editorPrefs().tabs;
|
||||
await this.removeGuarded(
|
||||
(current) =>
|
||||
current.panes[paneId]
|
||||
? enforceTabLimit(
|
||||
current,
|
||||
paneId,
|
||||
tabPrefs.tabLimit,
|
||||
tabPrefs.closingPolicy,
|
||||
(tab) =>
|
||||
inputDirty(tab.input, useContributions.getState().editorKinds) ??
|
||||
!!(tab.input.path && useDocMeta.getState()[tab.input.path]?.dirty),
|
||||
activate ? tabId : (current.panes[paneId]?.active ?? tabId),
|
||||
(tab) => inputDirty(tab.input, useContributions.getState().editorKinds) === true,
|
||||
)
|
||||
: current,
|
||||
{ protect: (tab) => inputDirty(tab.input, useContributions.getState().editorKinds) === true },
|
||||
);
|
||||
await this.enforcePaneTabLimit(paneId, activate ? tabId : (useTabs.getState().panes[paneId]?.active ?? tabId));
|
||||
if (!isTextInput(input)) {
|
||||
if (o.focus !== false)
|
||||
requestAnimationFrame(() => {
|
||||
@@ -1184,6 +1179,29 @@ export class EditorController implements SlotHost {
|
||||
}
|
||||
}
|
||||
|
||||
private enforcePaneTabLimit(paneId: string, keep?: string, liveKeys?: ReadonlySet<string>) {
|
||||
const prefs = editorPrefs().tabs;
|
||||
const protect = (tab: Tab) =>
|
||||
!!liveKeys?.has(tab.input.key) || inputDirty(tab.input, useContributions.getState().editorKinds) === true;
|
||||
return this.removeGuarded(
|
||||
(current) =>
|
||||
current.panes[paneId]
|
||||
? enforceTabLimit(
|
||||
current,
|
||||
paneId,
|
||||
prefs.tabLimit,
|
||||
prefs.closingPolicy,
|
||||
(tab) =>
|
||||
inputDirty(tab.input, useContributions.getState().editorKinds) ??
|
||||
!!(tab.input.path && useDocMeta.getState()[tab.input.path]?.dirty),
|
||||
keep ?? current.panes[paneId]?.active ?? undefined,
|
||||
protect,
|
||||
)
|
||||
: current,
|
||||
{ protect },
|
||||
);
|
||||
}
|
||||
|
||||
activate(paneId: string, tabId: string, focus = true) {
|
||||
const restored = restoreMaximizedToolWindow();
|
||||
++this.activationGeneration;
|
||||
|
||||
Reference in new issue
Заблокувати користувача