Change:
a notebook that has several cells, and put this code in the second to last cell the cell so that it has output, and shrink the window so that the single cell+output takes up the whole scrollable region the cell again with shift+enter :bug: the notebook ends up scrolling up, instead of showing the full output and next cell. !
The current behavior of the list view is to use the second visible element as an anchor which can cause some strange behavior. In this scenario: next cell is focused and revealed, making that cell the anchor as second visible, since the executing cell takes up the rest of the visible region. output is removed, moving more elements into view from above is and the initial anchor is no longer the second element, so it is scrolled out of view I'll try to play around with some options, but my initial thought would be to use the executing cell as the anchor. The output could expand and push the next cell out of view, but typing in that cell will bring it back into view. cc
I wanted to see the behavior locally. I really can't seem to repro this, my notebook stays pretty stable as the cell shrinks and grows. What am I missing?
I think I found it. I believe you have disabled, am I correct? Can you reproduce this when flipping that setting back to the default value of ?
It repro's with or without - you just need to scroll down enough so that the resized cell is only partially visible. The strange behavior is due to the notebook cell list setting different anchors depending on whether the element top is above the render top.
As I looked into the remaining open issue and PR , I was trying to grasp what the issue really is, what has been actually fixed and what's left to fix. Then, I pulled latest and ran out of sources and this is even worse than my last recording. Everything jumps around even when the cell is fully in view: Did just break this even more? I suggest reverting , pausing, simplifying the problem, narrowing in on a solution and consider fixing it in debt week so as to not cause regressions during this endgame week. cc
thanks for the testing, the two recordings you posted above were using different execution commands so I want to ensure we refer to the same behavior: in , it's re-executing the active cell (ctrl+enter), which should always stay visually stable. I think it still works this way out from source in the latest , from what I saw, it's running shift+enter: run cell and move to next This command moves focus to the next cell, so the cell is now the focused cell Then the output of the previous cell (which is no longer focused) starts to grow The change introduced via 's last PR was to ensure that the focused cell stay visually stable, once they are revealed into view can you confirm if this is what you are seeing?
I captured a recording for what probably was comparing, right side is Stable and left side Insiders In both setup, the next cell is fully visible In Stable, shift+enter moves the next cell up, and then push it down, but the new focused cell is still visible in the viewport at the end In Insiders, shift+enter moves the next cell up, and when the previous starts to grow, it does not move the focused cell downwards Even though the new behavior is more self explanatory (from our perspective), but unluckily is not the same as Jupyter Lab. It means we are changing from one custom behavior (our previous heuristics) to another (always keep focused cell stable)
Thanks for clarifying this It was not clear to me that (1) shift+enter and clicking the run cell action did different things, nor that (2) this bug was only about one of these actions.
fixed by

Discussion:
// do not scroll the viewport if resized element is out of viewport heightDiff = size - originalSize; } else { <del> if (anchorIndex !== null && anchorIndex > index && anchorIndex <= lastRenderRange.end) { </del> <ins> if (anchorIndex !== null && anchorIndex > index && anchorIndex < lastRenderRange.end) { </ins> // anchor in viewport // resized element in viewport and above the anchor heightDiff = size - originalSize;

const languageService = accessor.get(ILanguageService); const config = accessor.get(IConfigurationService); <del> const scrollBehavior = config.getValue(NotebookSetting.cellExecutionScroll); </del> <ins> const scrollBehavior = config.getValue(NotebookSetting.scrollToRevealCell); </ins> let focusOptions: IFocusNotebookCellOptions; if (scrollBehavior === 'none') { focusOptions = { skipReveal: true };

type: 'boolean', default: typeof product.quality === 'string' && product.quality !== 'stable' // only enable as default in insiders }, <del> [NotebookSetting.cellExecutionScroll]: { markdownDescription: nls.localize('notebook.revealNextOnExecuteBehavior.description', "How far to scroll when revealing the next cell upon exectuting {0}.", 'notebook.cell.executeAndSelectBelow'), </del> <ins> [NotebookSetting.scrollToRevealCell]: { markdownDescription: nls.localize('notebook.scrolling.revealCellBehavior.description', "How far to scroll when revealing the selected, e.g. when exectuting {0}.", 'notebook.cell.executeAndSelectBelow'), </ins> type: 'string', enum: ['fullCell', 'firstLine', 'none'], markdownEnumDescriptions: [ <del> nls.localize('notebook.revealNextOnExecuteBehavior.fullCell.description', 'Scroll to fully reveal the next cell.'), nls.localize('notebook.revealNextOnExecuteBehavior.firstLine.description', 'Scroll to reveal the first line of the next cell.'), nls.localize('notebook.revealNextOnExecuteBehavior.nonedescription', 'Do not scroll to reveal the next cell.'), </del> <ins> nls.localize('notebook.scrolling.revealCellBehavior.fullCell.description', 'Scroll to fully reveal the selected cell.'), nls.localize('notebook.scrolling.revealCellBehavior.firstLine.description', 'Scroll to reveal the first line of the selected cell.'), nls.localize('notebook.scrolling.revealCellBehavior.nonedescription', 'Do not scroll to reveal the selected cell.'), </ins> ], default: 'fullCell' <ins> }, [NotebookSetting.anchorToFocusedCell]: { markdownDescription: nls.localize('notebook.scrolling.anchorToFocusedCell.description', "Keep the focused cell steady while surrounding cells change size"), type: 'string', enum: ['auto', 'true', 'false'], markdownEnumDescriptions: [ nls.localize('notebook.scrolling.anchorToFocusedCell.auto.description', "Anchor to the focused cell unless {0} is set to {1}", 'notebook.scrolling.revealCellBehavior', 'none') ], default: 'auto' </ins> } } });

if (typeof options?.focusEditorLine === 'number') { this._cursorNavMode.set(true); this.revealInView(cell); <del> } else if (options?.revealBehavior === ScrollToRevealBehavior.fullCell) { this.revealInView(cell); </del> } else if (options?.revealBehavior === ScrollToRevealBehavior.firstLine) { this.revealFirstLineIfOutsideViewport(cell); } else {

import { IListService, IWorkbenchListOptions, WorkbenchList } from 'vs/platform/list/browser/listService'; import { CursorAtBoundary, ICellViewModel, CellEditState, CellFocusMode, ICellOutputViewModel, CellRevealType, CellRevealSyncType, CellRevealRangeType, CursorAtLineBoundary } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl'; <del> import { diff, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind, SelectionStateType, NOTEBOOK_EDITOR_CURSOR_LINE_BOUNDARY } from 'vs/workbench/contrib/notebook/common/notebookCommon'; </del> <ins> import { diff, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind, SelectionStateType, NOTEBOOK_EDITOR_CURSOR_LINE_BOUNDARY, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; </ins> import { ICellRange, cellRangesToIndexes, reduceCellRanges, cellRangesEqual } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { NOTEBOOK_CELL_LIST_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { clamp } from 'vs/base/common/numbers';

contextKeyService: IContextKeyService, options: IWorkbenchListOptions<CellViewModel>, @IListService listService: IListService, <del> @IConfigurationService configurationService: IConfigurationService, </del> <ins> @IConfigurationService private readonly configurationService: IConfigurationService, </ins> @IInstantiationService instantiationService: IInstantiationService ) { super(listUser, container, delegate, renderers, options, contextKeyService, listService, configurationService, instantiationService);

} const focused = this.getFocus(); <del> if (!focused.length) { return this.view.updateElementHeight(index, size, null); } const focus = focused[0]; if (focus <= index) { return this.view.updateElementHeight(index, size, focus); } // the `element` is in the viewport, it's very often that the height update is triggerred by user interaction (collapse, run cell) // then we should make sure that the `element`'s visual view position doesn't change. </del> <ins> const focus = focused.length ? focused[0] : null; </ins> <del> if (this.view.elementTop(index) >= this.view.getScrollTop()) { return this.view.updateElementHeight(index, size, index); </del> <ins> const anchorFocusedSetting = this.configurationService.getValue(NotebookSetting.anchorToFocusedCell); const allowScrolling = this.configurationService.getValue(NotebookSetting.scrollToRevealCell) !== 'none'; const anchorToFocusedCell = anchorFocusedSetting === 'true' || (allowScrolling && anchorFocusedSetting !== 'false'); if (focused && anchorToFocusedCell) { this.view.updateElementHeight(index, size, focus); </ins> } <del> this.view.updateElementHeight(index, size, focus); </del> <ins> this.view.updateElementHeight(index, size, null); </ins> } // override

confirmDeleteRunningCell: 'notebook.confirmDeleteRunningCell', remoteSaving: 'notebook.experimental.remoteSave', gotoSymbolsAllSymbols: 'notebook.gotoSymbols.showAllSymbols', <del> cellExecutionScroll: 'notebook.revealNextOnExecuteBehavior' </del> <ins> scrollToRevealCell: 'notebook.scrolling.revealCellBehavior', anchorToFocusedCell: 'notebook.scrolling.anchorToFocusedCell' </ins> } as const; export const enum CellStatusbarAlignment {