Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/vs/sessions/services/sessions/browser/sessionsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,9 @@ export class SessionsService extends Disposable implements ISessionsService {
// no slot remains); drive the open flow below so the fallback is fully
// opened.
if (e.removed.length) {
for (const session of e.removed) {
this._sessionStates.delete(session.resource);
}
this._visibility.removeMany(e.removed.map(r => r.sessionId));
}

Expand Down Expand Up @@ -886,6 +889,26 @@ export class SessionsService extends Disposable implements ISessionsService {

private _saveSessionStates(): void {
const entries = this._snapshotVisibleSessionStates();

// Also persist the per-session state (closed chats, last active chat) of
// sessions that are not currently visible, so a session switched out of
// the grid keeps its closed-chat set across a reload. Grid-placement
// fields are stripped so they are not restored into the grid.
const visible = new ResourceMap<true>();
for (const entry of entries) {
visible.set(URI.parse(entry.sessionResource), true);
}
Comment thread
sandy081 marked this conversation as resolved.
for (const [resource, state] of this._sessionStates) {
if (visible.has(resource)) {
continue;
}
entries.push({
sessionResource: state.sessionResource,
activeChatResource: state.activeChatResource,
closedChatResources: state.closedChatResources,
});
}
Comment thread
sandy081 marked this conversation as resolved.

this.storageService.store(ACTIVE_SESSION_STATES_KEY, JSON.stringify(entries), StorageScope.WORKSPACE, StorageTarget.MACHINE);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1404,6 +1404,62 @@ suite('SessionsManagementService', () => {
await second.restoreVisibleSessions();
assert.deepStrictEqual((second.activeSession.get()?.closedChats.get() ?? []).map(c => c.title.get()), ['b']);
});

test('a chat closed in a non-active session stays closed across a restart', async () => {
const mainA = chat('mainA');
const chatA2 = chat('a2');
const sessionA = stubSession({
sessionId: 'A', providerId: 'test',
status: constObservable(SessionStatus.Completed),
chats: constObservable([mainA, chatA2]),
mainChat: constObservable(mainA),
capabilities: constObservable({ supportsMultipleChats: true }),
});
const mainB = chat('mainB');
const chatB2 = chat('b2');
const sessionB = stubSession({
sessionId: 'B', providerId: 'test',
status: constObservable(SessionStatus.Completed),
chats: constObservable([mainB, chatB2]),
mainChat: constObservable(mainB),
capabilities: constObservable({ supportsMultipleChats: true }),
});
const storage = disposables.add(new InMemoryStorageService());
const provider = new class extends TestSessionsProvider {
constructor() { super(sessionA); }
override getSessions(): ISession[] { return [sessionA, sessionB]; }
};
const makeView = () => {
const instantiationService = disposables.add(new TestInstantiationService());
instantiationService.stub(IStorageService, storage);
instantiationService.stub(ILogService, new NullLogService());
instantiationService.stub(IContextKeyService, disposables.add(new MockContextKeyService()));
instantiationService.stub(ISessionsProvidersService, new TestSessionsProvidersService([provider]));
instantiationService.stub(IUriIdentityService, { extUri: extUriBiasedIgnorePathCase });
instantiationService.stub(IChatWidgetService, new TestChatWidgetService());
instantiationService.stub(IProgressService, new TestProgressService());
instantiationService.stub(IChatService, new class extends mock<IChatService>() {
override readonly onDidSubmitRequest = Event.None;
});
const service = disposables.add(instantiationService.createInstance(SessionsManagementService));
return createView(instantiationService, service, disposables);
};

// First window: close a chat in each session, end on session A so B is
// no longer visible, then simulate shutdown (flush storage).
const first = makeView();
await first.openSession(sessionB.resource);
await first.closeChat(first.activeSession.get()!, chatB2);
await first.openSession(sessionA.resource);
await first.closeChat(first.activeSession.get()!, chatA2);
await storage.flush();

// Second window: restore, then switch to B and confirm its chat is still closed.
const second = makeView();
await second.restoreVisibleSessions();
await second.openSession(sessionB.resource);
assert.deepStrictEqual((second.activeSession.get()?.closedChats.get() ?? []).map(c => c.title.get()), ['b2']);
});
});

suite('createQuickChat', () => {
Expand Down
Loading