debt - cleanup from sidebar support in modal editors (#306141)

* debt - cleanup from sidebar support in modal editors

* .

* Update src/vs/platform/editor/common/editor.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* .

* Scope sidebar tree action runner to sidebar selection

Agent-Logs-Url: https://github.com/microsoft/vscode/sessions/7e47c5a7-9a3f-4353-975d-ab48a16bdc86

Co-authored-by: bpasero <900690+bpasero@users.noreply.github.com>

* .

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: bpasero <900690+bpasero@users.noreply.github.com>
This commit is contained in:
Benjamin Pasero
2026-03-29 21:50:40 +02:00
committed by GitHub
parent 647e421782
commit 9a6bf81db5
8 changed files with 389 additions and 182 deletions

View File

@@ -1001,58 +1001,7 @@ export class ChangesViewPane extends ViewPane {
// Create the tree
if (!this.tree && this.listContainer) {
const resourceLabels = this._register(this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility }));
const actionRunner = this.renderDisposables.add(new ChangesViewActionRunner(
() => this.viewModel.activeSessionResourceObs.get(),
() => this.getSessionDiscardRef(),
() => this.getTreeSelection(),
));
this.tree = this.instantiationService.createInstance(
WorkbenchCompressibleObjectTree<ChangesTreeElement>,
'ChangesViewTree',
this.listContainer,
new ChangesTreeDelegate(),
[this.instantiationService.createInstance(ChangesTreeRenderer, resourceLabels, MenuId.ChatEditingSessionChangeToolbar, actionRunner)],
{
alwaysConsumeMouseWheel: false,
accessibilityProvider: {
getAriaLabel: (element: ChangesTreeElement) => isChangesFileItem(element) ? basename(element.uri.path) : element.name,
getWidgetAriaLabel: () => localize('changesViewTree', "Changes Tree")
},
dnd: {
getDragURI: (element: ChangesTreeElement) => element.uri.toString(),
getDragLabel: (elements) => {
const uris = elements.map(e => e.uri);
if (uris.length === 1) {
return this.labelService.getUriLabel(uris[0], { relative: true });
}
return `${uris.length}`;
},
dispose: () => { },
onDragOver: () => false,
drop: () => { },
onDragStart: (data, originalEvent) => {
try {
const elements = data.getData() as ChangesTreeElement[];
const uris = elements.filter(isChangesFileItem).map(e => e.uri);
this.instantiationService.invokeFunction(accessor => fillEditorsDragData(accessor, uris, originalEvent));
} catch {
// noop
}
},
},
identityProvider: {
getId: (element: ChangesTreeElement) => element.uri.toString()
},
indent: this.viewModel.viewModeObs.get() === ChangesViewMode.List ? 0 : 8,
compressionEnabled: true,
twistieAdditionalCssClass: (e: unknown) => {
return this.viewModel.viewModeObs.get() === ChangesViewMode.List
? 'force-no-twistie'
: undefined;
},
}
);
this.tree = this.createChangesTree(this.listContainer, this.onDidChangeBodyVisibility, this._store);
}
// Register tree event handlers
@@ -1062,9 +1011,8 @@ export class ChangesViewPane extends ViewPane {
// Re-layout when collapse state changes so the card height adjusts
this.renderDisposables.add(tree.onDidChangeContentHeight(() => this.layoutSplitView()));
const openFileItem = (item: IChangesFileItem, items: IChangesFileItem[], sideBySide: boolean, preserveFocus?: boolean, pinned?: boolean, includeSidebar = true) => {
const openFileItem = (item: IChangesFileItem, items: IChangesFileItem[], sideBySide: boolean, preserveFocus: boolean, pinned: boolean, includeSidebar: boolean) => {
const { uri: modifiedFileUri, originalUri, isDeletion } = item;
const currentIndex = items.indexOf(item);
const sidebar = includeSidebar ? {
@@ -1073,15 +1021,16 @@ export class ChangesViewPane extends ViewPane {
}
} : undefined;
const navigation = items.length > 1 ? {
const navigation = {
total: items.length,
current: currentIndex,
navigate: (index: number) => {
if (index >= 0 && index < items.length) {
openFileItem(items[index], items, false, undefined, undefined, includeSidebar);
const target = items[index];
if (target) {
openFileItem(target, items, false, false, false, includeSidebar);
}
}
} : undefined;
};
const group = sideBySide ? SIDE_GROUP : ACTIVE_GROUP;
@@ -1114,7 +1063,7 @@ export class ChangesViewPane extends ViewPane {
}
const items = combinedEntriesObs.get();
openFileItem(e.element, items, e.sideBySide);
openFileItem(e.element, items, e.sideBySide, !!e.editorOptions?.preserveFocus, !!e.editorOptions?.pinned, true);
}));
}
@@ -1241,39 +1190,13 @@ export class ChangesViewPane extends ViewPane {
container: HTMLElement,
onDidLayout: Event<{ readonly height: number; readonly width: number }>,
items: IChangesFileItem[],
openFileItem: (item: IChangesFileItem, items: IChangesFileItem[], sideBySide: boolean, preserveFocus?: boolean, pinned?: boolean, includeSidebar?: boolean) => void,
openFileItem: (item: IChangesFileItem, items: IChangesFileItem[], sideBySide: boolean, preserveFocus: boolean, pinned: boolean, includeSidebar: boolean) => void,
): IDisposable {
const disposables = new DisposableStore();
const labels = disposables.add(this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: Event.None }));
container.classList.add('chat-editing-session-list');
const tree = disposables.add(this.instantiationService.createInstance(
WorkbenchCompressibleObjectTree<ChangesTreeElement>,
'ModalEditorSidebar',
container,
new ChangesTreeDelegate(),
[this.instantiationService.createInstance(ChangesTreeRenderer, labels, undefined /* no menu */, undefined /* no action runner */)],
{
alwaysConsumeMouseWheel: false,
multipleSelectionSupport: false,
accessibilityProvider: {
getAriaLabel: (element: ChangesTreeElement) => isChangesFileItem(element) ? basename(element.uri.path) : element.name,
getWidgetAriaLabel: () => localize('modalEditorSidebar', "Files"),
},
keyboardNavigationLabelProvider: {
getKeyboardNavigationLabel: (element: ChangesTreeElement) => isChangesFileItem(element) ? basename(element.uri.path) : element.name,
getCompressedNodeKeyboardNavigationLabel: (elements: ChangesTreeElement[]) => elements.map(e => isChangesFileItem(e) ? basename(e.uri.path) : e.name).join('/'),
},
identityProvider: {
getId: (element: ChangesTreeElement) => element.uri.toString()
},
indent: 0,
compressionEnabled: false,
setRowLineHeight: false,
supportDynamicHeights: false,
twistieAdditionalCssClass: () => 'force-no-twistie',
}
));
const tree = this.createChangesTree(container, Event.None, disposables, () => tree.getSelection().filter(item => !!item && isChangesFileItem(item)));
tree.setChildren(null, items.map(item => ({ element: item as ChangesTreeElement, collapsible: false })));
@@ -1282,7 +1205,7 @@ export class ChangesViewPane extends ViewPane {
let updatingSelection = false;
disposables.add(tree.onDidOpen(e => {
if (e.element && isChangesFileItem(e.element) && !updatingSelection) {
openFileItem(e.element, items, e.sideBySide, e.editorOptions.preserveFocus, e.editorOptions.pinned, false /* sidebar already rendered */);
openFileItem(e.element, items, e.sideBySide, !!e.editorOptions.preserveFocus, !!e.editorOptions.pinned, false /* preserve existing sidebar */);
}
}));
@@ -1318,8 +1241,67 @@ export class ChangesViewPane extends ViewPane {
return disposables;
}
private createChangesTree(
container: HTMLElement,
onDidChangeVisibility: Event<boolean>,
disposables: DisposableStore,
getSelection?: () => IChangesFileItem[],
): WorkbenchCompressibleObjectTree<ChangesTreeElement> {
const resourceLabels = disposables.add(this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility }));
const actionRunner = disposables.add(new ChangesViewActionRunner(
() => this.viewModel.activeSessionResourceObs.get(),
() => this.getSessionDiscardRef(),
getSelection ?? (() => this.getTreeSelection()),
));
return disposables.add(this.instantiationService.createInstance(
WorkbenchCompressibleObjectTree<ChangesTreeElement>,
'ChangesViewTree',
container,
new ChangesTreeDelegate(),
[this.instantiationService.createInstance(ChangesTreeRenderer, resourceLabels, MenuId.ChatEditingSessionChangeToolbar, actionRunner)],
{
alwaysConsumeMouseWheel: false,
accessibilityProvider: {
getAriaLabel: (element: ChangesTreeElement) => isChangesFileItem(element) ? basename(element.uri.path) : element.name,
getWidgetAriaLabel: () => localize('changesViewTree', "Changes Tree")
},
dnd: {
getDragURI: (element: ChangesTreeElement) => element.uri.toString(),
getDragLabel: (elements) => {
const uris = elements.map(e => e.uri);
if (uris.length === 1) {
return this.labelService.getUriLabel(uris[0], { relative: true });
}
return `${uris.length}`;
},
dispose: () => { },
onDragOver: () => false,
drop: () => { },
onDragStart: (data, originalEvent) => {
try {
const elements = data.getData() as ChangesTreeElement[];
const uris = elements.filter(isChangesFileItem).map(e => e.uri);
this.instantiationService.invokeFunction(accessor => fillEditorsDragData(accessor, uris, originalEvent));
} catch {
// noop
}
},
},
identityProvider: {
getId: (element: ChangesTreeElement) => element.uri.toString()
},
indent: this.viewModel.viewModeObs.get() === ChangesViewMode.List ? 0 : 8,
compressionEnabled: true,
twistieAdditionalCssClass: (e: unknown) => {
return this.viewModel.viewModeObs.get() === ChangesViewMode.List
? 'force-no-twistie'
: undefined;
},
}
));
}
override dispose(): void {
this.tree?.dispose();
this.tree = undefined;
super.dispose();
}