Change:
Testing Ideally there would be commands to navigate between changes. And they might deserve an action in the diff title bar. Same as the editor diff experience. !
Why is for these commands? I would expect them to be available from the command palette, since in a regular text editor Show Previous Change and Show Next Change both appear.

Discussion:
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ActiveEditorContext } from 'vs/workbench/common/contextkeys'; import { DiffElementViewModelBase } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel'; <del> import { NOTEBOOK_DIFF_CELL_INPUT, NOTEBOOK_DIFF_CELL_PROPERTY, NOTEBOOK_DIFF_CELL_PROPERTY_EXPANDED } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser'; </del> <ins> import { INotebookTextDiffEditor, NOTEBOOK_DIFF_CELL_INPUT, NOTEBOOK_DIFF_CELL_PROPERTY, NOTEBOOK_DIFF_CELL_PROPERTY_EXPANDED } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser'; </ins> import { NotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor'; import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/common/notebookDiffEditorInput'; <del> import { openAsTextIcon, renderOutputIcon, revertIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; </del> <ins> import { nextChangeIcon, openAsTextIcon, previousChangeIcon, renderOutputIcon, revertIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; </ins> import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { ICommandActionTitle } from 'vs/platform/action/common/action'; import { DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; <ins> import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { NOTEBOOK_DIFF_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon'; </ins> // ActiveEditorContext.isEqualTo(SearchEditorConstants.SearchEditorID)

} }); <ins> registerAction2(class extends Action2 { constructor() { super( { id: 'notebook.diff.action.previous', title: localize('notebook.diff.action.previous.title', "Show Previous Change"), icon: previousChangeIcon, f1: false, keybinding: { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F3, weight: KeybindingWeight.WorkbenchContrib }, menu: { id: MenuId.EditorTitle, group: 'navigation', when: ActiveEditorContext.isEqualTo(NotebookTextDiffEditor.ID) } } ); } run(accessor: ServicesAccessor) { const editorService: IEditorService = accessor.get(IEditorService); if (editorService.activeEditorPane?.getId() !== NOTEBOOK_DIFF_EDITOR_ID) { return; } const editor = editorService.activeEditorPane.getControl() as INotebookTextDiffEditor | undefined; editor?.previousChange(); } }); registerAction2(class extends Action2 { constructor() { super( { id: 'notebook.diff.action.next', title: localize('notebook.diff.action.next.title', "Show Next Change"), icon: nextChangeIcon, f1: false, keybinding: { primary: KeyMod.Alt | KeyCode.F3, weight: KeybindingWeight.WorkbenchContrib }, menu: { id: MenuId.EditorTitle, group: 'navigation', when: ActiveEditorContext.isEqualTo(NotebookTextDiffEditor.ID) } } ); } run(accessor: ServicesAccessor) { const editorService: IEditorService = accessor.get(IEditorService); if (editorService.activeEditorPane?.getId() !== NOTEBOOK_DIFF_EDITOR_ID) { return; } const editor = editorService.activeEditorPane.getControl() as INotebookTextDiffEditor | undefined; editor?.nextChange(); } }); </ins> Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration({ id: 'notebook', order: 100,

import * as nls from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; <ins> import { lastIndex } from 'vs/base/common/arrays'; </ins> import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { EditorPaneSelectionChangeReason, EditorPaneSelectionCompareResult, IEditorOpenContext, IEditorPaneSelection, IEditorPaneSelectionChangeEvent, IEditorPaneWithSelection } from 'vs/workbench/common/editor'; <del> import { getDefaultNotebookCreationOptions, NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; </del> <ins> import { getDefaultNotebookCreationOptions } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; </ins> import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { NotebookDiffEditorInput } from '../../common/notebookDiffEditorInput'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';

this._list.triggerScrollFromMouseWheelEvent(event); } <ins> previousChange(): void { let currFocus = this._list.getFocus()[0]; if (isNaN(currFocus) || currFocus < 0) { currFocus = 0; } // find the index of previous change let prevChangeIndex = currFocus - 1; while (prevChangeIndex >= 0) { const vm = this._diffElementViewModels[prevChangeIndex]; if (vm.type !== 'unchanged') { break; } prevChangeIndex--; } if (prevChangeIndex >= 0) { this._list.setFocus([prevChangeIndex]); this._list.reveal(prevChangeIndex); } else { // go to the last one const index = lastIndex(this._diffElementViewModels, vm => vm.type !== 'unchanged'); if (index >= 0) { this._list.setFocus([index]); this._list.reveal(index); } } } nextChange(): void { let currFocus = this._list.getFocus()[0]; if (isNaN(currFocus) || currFocus < 0) { currFocus = 0; } // find the index of next change let nextChangeIndex = currFocus + 1; while (nextChangeIndex < this._diffElementViewModels.length) { const vm = this._diffElementViewModels[nextChangeIndex]; if (vm.type !== 'unchanged') { break; } nextChangeIndex++; } if (nextChangeIndex < this._diffElementViewModels.length) { this._list.setFocus([nextChangeIndex]); this._list.reveal(nextChangeIndex); } else { // go to the first one const index = this._diffElementViewModels.findIndex(vm => vm.type !== 'unchanged'); if (index >= 0) { this._list.setFocus([index]); this._list.reveal(index); } } } </ins> createOutput(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: DiffNestedCellViewModel, output: IInsetRenderOutput, getOffset: () => number, diffSide: DiffSide): void { this._insetModifyQueueByOutputId.queue(output.source.model.outputId + (diffSide === DiffSide.Modified ? '-right' : 'left'), async () => { const activeWebview = diffSide === DiffSide.Modified ? this._modifiedWebview : this._originalWebview;

return this._overflowContainer; } <del> override getControl(): NotebookEditorWidget | undefined { return undefined; </del> <ins> override getControl(): INotebookTextDiffEditor | undefined { return this; </ins> } protected override setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void {

focusNextNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): Promise<void>; updateOutputHeight(cellInfo: ICommonCellInfo, output: ICellOutputViewModel, height: number, isInit: boolean): void; deltaCellOutputContainerClassNames(diffSide: DiffSide, cellId: string, added: string[], removed: string[]): void; <ins> previousChange(): void; nextChange(): void; </ins> } export interface IDiffNestedCellViewModel {

private _layoutNow() { const layoutInfo = this.notebookEditor.getLayoutInfo(); const height = layoutInfo.height; <del> const scrollHeight = layoutInfo.scrollHeight; </del> <ins> const contentHeight = this._diffElementViewModels.map(view => view.layoutInfo.totalHeight).reduce((a, b) => a + b, 0); </ins> const ratio = browser.PixelRatio.value; this._domNode.setWidth(this.width); this._domNode.setHeight(height);

this._domNode.domNode.height = height * ratio; const ctx = this._domNode.domNode.getContext('2d')!; ctx.clearRect(0, 0, this.width * ratio, height * ratio); <del> this._renderCanvas(ctx, this.width * ratio, height * ratio, scrollHeight * ratio, ratio); </del> <ins> this._renderCanvas(ctx, this.width * ratio, height * ratio, contentHeight * ratio, ratio); </ins> this._renderOverviewViewport(); }

export const revertIcon = registerIcon('notebook-revert', Codicon.discard, localize('revertIcon', 'Icon to revert in notebook editors.')); export const renderOutputIcon = registerIcon('notebook-render-output', Codicon.preview, localize('renderOutputIcon', 'Icon to render output in diff editor.')); export const mimetypeIcon = registerIcon('notebook-mimetype', Codicon.code, localize('mimetypeIcon', 'Icon for a mime type in notebook editors.')); <ins> export const previousChangeIcon = registerIcon('notebook-diff-editor-previous-change', Codicon.arrowUp, localize('previousChangeIcon', 'Icon for the previous change action in the diff editor.')); export const nextChangeIcon = registerIcon('notebook-diff-editor-next-change', Codicon.arrowDown, localize('nextChangeIcon', 'Icon for the next change action in the diff editor.')); </ins>