Sessions - changes view improvements (#301485)

* Sessions - changes view improvements

* Fix compilation
This commit is contained in:
Ladislau Szomoru
2026-03-13 15:01:41 +01:00
committed by GitHub
parent ac40642c8e
commit f883b96951
3 changed files with 27 additions and 33 deletions

View File

@@ -52,13 +52,12 @@ import { AgentSessionProviders } from '../../../../workbench/contrib/chat/browse
import { ChatContextKeys } from '../../../../workbench/contrib/chat/common/actions/chatContextKeys.js';
import { IChatSessionFileChange, IChatSessionFileChange2, isIChatSessionFileChange2 } from '../../../../workbench/contrib/chat/common/chatSessionsService.js';
import { chatEditingWidgetFileStateContextKey, hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingService, ModifiedFileEntryState } from '../../../../workbench/contrib/chat/common/editing/chatEditingService.js';
import { getChatSessionType } from '../../../../workbench/contrib/chat/common/model/chatUri.js';
import { createFileIconThemableTreeContainerScope } from '../../../../workbench/contrib/files/browser/views/explorerView.js';
import { IActivityService, NumberBadge } from '../../../../workbench/services/activity/common/activity.js';
import { IEditorService, MODAL_GROUP, SIDE_GROUP } from '../../../../workbench/services/editor/common/editorService.js';
import { IExtensionService } from '../../../../workbench/services/extensions/common/extensions.js';
import { IWorkbenchLayoutService } from '../../../../workbench/services/layout/browser/layoutService.js';
import { ISessionsManagementService } from '../../sessions/browser/sessionsManagementService.js';
import { IActiveSessionItem, ISessionsManagementService } from '../../sessions/browser/sessionsManagementService.js';
import { GITHUB_REMOTE_FILE_SCHEME } from '../../fileTreeView/browser/githubFileSystemProvider.js';
import { CodeReviewStateKind, getCodeReviewFilesFromSessionChanges, getCodeReviewVersion, ICodeReviewService, PRReviewStateKind } from '../../codeReview/browser/codeReviewService.js';
import { IGitRepository, IGitService } from '../../../../workbench/contrib/git/common/gitService.js';
@@ -117,13 +116,6 @@ interface IChangesFolderItem {
readonly name: string;
}
interface IActiveSession {
readonly resource: URI;
readonly sessionType: string;
readonly repository: URI | undefined;
readonly worktree: URI | undefined;
}
type ChangesTreeElement = IChangesFileItem | IChangesFolderItem;
function isChangesFileItem(element: ChangesTreeElement): element is IChangesFileItem {
@@ -261,7 +253,7 @@ export class ChangesViewPane extends ViewPane {
}
// Track the active session used by this view
private readonly activeSession: IObservableWithChange<IActiveSession | undefined>;
private readonly activeSession: IObservableWithChange<IActiveSessionItem | undefined>;
private readonly activeSessionFileCountObs: IObservableWithChange<number>;
private readonly activeSessionHasChangesObs: IObservableWithChange<boolean>;
private readonly activeSessionRepositoryChangesObs: IObservableWithChange<IChangesFileItem[] | undefined>;
@@ -310,7 +302,7 @@ export class ChangesViewPane extends ViewPane {
this.versionModeContextKey.set(ChangesVersionMode.AllChanges);
// Track active session from sessions management service
this.activeSession = derivedOpts<IActiveSession | undefined>({
this.activeSession = derivedOpts<IActiveSessionItem | undefined>({
equalsFn: (a, b) => isEqual(a?.resource, b?.resource),
}, reader => {
const activeSession = this.sessionManagementService.activeSession.read(reader);
@@ -318,12 +310,7 @@ export class ChangesViewPane extends ViewPane {
return undefined;
}
return {
resource: activeSession.resource,
repository: activeSession.repository,
worktree: activeSession.worktree,
sessionType: getChatSessionType(activeSession.resource),
};
return activeSession;
}).recomputeInitiallyAndOnChange(this._store);
// Track active session repository changes
@@ -381,7 +368,7 @@ export class ChangesViewPane extends ViewPane {
const viewSessionTypeKey = this.scopedContextKeyService.createKey<string>(ChatContextKeys.agentSessionType.key, '');
this._register(autorun(reader => {
const activeSession = this.activeSession.read(reader);
viewSessionTypeKey.set(activeSession?.sessionType ?? '');
viewSessionTypeKey.set(activeSession?.providerType ?? '');
}));
}
@@ -411,10 +398,8 @@ export class ChangesViewPane extends ViewPane {
return 0;
}
const isBackgroundSession = activeSession.sessionType === AgentSessionProviders.Background;
let editingSessionCount = 0;
if (!isBackgroundSession) {
if (activeSession.providerType !== AgentSessionProviders.Background) {
const sessions = this.chatEditingService.editingSessionsObs.read(reader);
const session = sessions.find(candidate => isEqual(candidate.chatSessionResource, activeSession.resource));
editingSessionCount = session ? session.entries.read(reader).length : 0;
@@ -509,7 +494,7 @@ export class ChangesViewPane extends ViewPane {
const activeSession = this.activeSession.read(reader);
// Background chat sessions render the working set based on the session files, not the editing session
if (activeSession?.sessionType === AgentSessionProviders.Background) {
if (activeSession?.providerType === AgentSessionProviders.Background) {
return [];
}
@@ -720,7 +705,7 @@ export class ChangesViewPane extends ViewPane {
const chatSessionTypeKey = this.scopedContextKeyService.createKey<string>(ChatContextKeys.agentSessionType.key, '');
this.renderDisposables.add(autorun(reader => {
const activeSession = this.activeSession.read(reader);
chatSessionTypeKey.set(activeSession?.sessionType ?? '');
chatSessionTypeKey.set(activeSession?.providerType ?? '');
}));
// Bind required context keys for the menu buttons
@@ -757,8 +742,8 @@ export class ChangesViewPane extends ViewPane {
this.renderDisposables.add(bindContextKey(hasUncommittedChangesContextKey, this.scopedContextKeyService, r => hasUncommittedChangesObs.read(r)));
const isMergeBaseBranchProtectedObs = derived(reader => {
const state = this.activeSessionRepositoryObs.read(reader)?.state.read(reader);
return state?.HEAD?.base?.isProtected === true;
const activeSession = this.activeSession.read(reader);
return activeSession?.worktreeBaseBranchProtected === true;
});
this.renderDisposables.add(bindContextKey(isMergeBaseBranchProtectedContextKey, this.scopedContextKeyService, r => isMergeBaseBranchProtectedObs.read(r)));

View File

@@ -87,6 +87,7 @@ suite('getGitHubContext', () => {
repository: undefined,
worktree: undefined,
worktreeBranchName: undefined,
worktreeBaseBranchProtected: undefined,
providerType: 'copilot-cloud-agent',
...overrides,
};

View File

@@ -54,6 +54,7 @@ export interface IActiveSessionItem {
readonly repository: URI | undefined;
readonly worktree: URI | undefined;
readonly worktreeBranchName: string | undefined;
readonly worktreeBaseBranchProtected: boolean | undefined;
readonly providerType: string;
}
@@ -205,10 +206,10 @@ export class SessionsManagementService extends Disposable implements ISessionsMa
}
}
private getRepositoryFromMetadata(session: IAgentSession): [URI | undefined, URI | undefined, string | undefined] {
private getRepositoryFromMetadata(session: IAgentSession): [URI | undefined, URI | undefined, string | undefined, boolean | undefined] {
const metadata = session.metadata;
if (!metadata) {
return [undefined, undefined, undefined];
return [undefined, undefined, undefined, undefined];
}
if (session.providerType === AgentSessionProviders.Cloud) {
@@ -219,12 +220,12 @@ export class SessionsManagementService extends Disposable implements ISessionsMa
authority: 'github',
path: `/${metadata.owner}/${metadata.name}/${encodeURIComponent(branch)}`
});
return [repositoryUri, undefined, undefined];
return [repositoryUri, undefined, undefined, undefined];
}
const workingDirectoryPath = metadata?.workingDirectoryPath as string | undefined;
if (workingDirectoryPath) {
return [URI.file(workingDirectoryPath), undefined, undefined];
return [URI.file(workingDirectoryPath), undefined, undefined, undefined];
}
const repositoryPath = metadata?.repositoryPath as string | undefined;
@@ -234,11 +235,13 @@ export class SessionsManagementService extends Disposable implements ISessionsMa
const worktreePathUri = typeof worktreePath === 'string' ? URI.file(worktreePath) : undefined;
const worktreeBranchName = metadata?.branchName as string | undefined;
const worktreeBaseBranchProtected = metadata?.baseBranchProtected as boolean | undefined;
return [
URI.isUri(repositoryPathUri) ? repositoryPathUri : undefined,
URI.isUri(worktreePathUri) ? worktreePathUri : undefined,
worktreeBranchName];
worktreeBranchName,
worktreeBaseBranchProtected];
}
getActiveSession(): IActiveSessionItem | undefined {
@@ -460,7 +463,7 @@ export class SessionsManagementService extends Disposable implements ISessionsMa
if (session) {
if (isAgentSession(session)) {
this.lastSelectedSession = session.resource;
const [repository, worktree, worktreeBranchName] = this.getRepositoryFromMetadata(session);
const [repository, worktree, worktreeBranchName, worktreeBaseBranchProtected] = this.getRepositoryFromMetadata(session);
activeSessionItem = {
isUntitled: false,
label: session.label,
@@ -468,6 +471,7 @@ export class SessionsManagementService extends Disposable implements ISessionsMa
repository: repository,
worktree,
worktreeBranchName: worktreeBranchName,
worktreeBaseBranchProtected: worktreeBaseBranchProtected === true,
providerType: session.providerType,
};
} else {
@@ -478,6 +482,7 @@ export class SessionsManagementService extends Disposable implements ISessionsMa
repository: session.repoUri,
worktree: undefined,
worktreeBranchName: undefined,
worktreeBaseBranchProtected: undefined,
providerType: session.target,
};
this._newActiveSessionDisposables.clear();
@@ -490,6 +495,7 @@ export class SessionsManagementService extends Disposable implements ISessionsMa
repository: session.repoUri,
worktree: undefined,
worktreeBranchName: undefined,
worktreeBaseBranchProtected: undefined,
providerType: session.target,
});
}
@@ -529,7 +535,8 @@ export class SessionsManagementService extends Disposable implements ISessionsMa
a.repository?.toString() === b.repository?.toString() &&
a.worktree?.toString() === b.worktree?.toString() &&
a.worktreeBranchName === b.worktreeBranchName &&
a.providerType === b.providerType
a.providerType === b.providerType &&
a.worktreeBaseBranchProtected === b.worktreeBaseBranchProtected
);
}
@@ -605,8 +612,9 @@ export class SessionsManagementService extends Disposable implements ISessionsMa
repository,
worktree,
worktreeBranchName: undefined,
worktreeBaseBranchProtected: undefined,
providerType: agentSession.providerType,
});
} satisfies IActiveSessionItem);
}
resolveSessionFileUri(sessionResource: URI, relativePath: string): URI | undefined {