Change:
Currently the scroll top position update is not context-aware, meaning when we have cell height changes, we don't know if it's because output height change, or users re-run a cell and outputs are updated/replaced, or users shift+enter to run cell and navigate to next cell. Users might have different expectations for each scenario but currently our heuristics for updating the scroll top didn't take the context into account. Let's use this issue to finalize the desired behavior of scroll top update. The polished/new design should cover [ ] Update element height API respects context (execution, execute+create, output update) [ ] Reveal API should handle out of sync view/content height Click the run button or press Ctrl+Enter to run focused cell If there is no enough room/space after the cell (e.g., the focused cell is the last cell) Users have disabled [ ] There is no output before execution, the view should scroll up to reveal the output fully [ ] Some users might want the bottom of the output to be fully revealed, when the output is larger than the viewport [ ] There is output before execution, the cell position should be persistent Users have enabled [ ] Reveal cell output If there is enough room after the cell [ ] Reveal cell output [ ] If the cell input is larger than the viewport, execute cell should reveal the output properly [ ] Collapse focused cell output should always keep the cell in viewport [ ] Type/Pasting content into focused cell should keep the cell in viewport [x] [ ] Notebook cell list reveal api might reveal a position on a cell whose view height is not synced with real editor height yet. [ ] Execute non-focused cell, should it keep the focused cell position persistent or keep the top of the target cell persistent? [ ] Non focused cell output height change should not change scroll top on scroll [ ] Shift+enter executes and moves focus to next cell, then new focused cell should always be in viewport [x] Scroll top position of inactive notebook editor should not change if the first partially visible cell's height change
I noticed an issue recently, is this related? Paste a large amount of text into a code cell The notebook scrolls down so far that the focused code cell is out of the viewport I can open a separate issue if you want
I guess what you were seeing is , I'll update the description to include this.
Couple cases discovered in the latest notebook sync. both of these are likely tied to outputLineLimit being large @ 100 when the shift-enter behavior is set to "firstline" the events are as follows: first line of next cell is focused and shown at the bottom of the viewport output is cleared and the focused cell is pulled upwards against the currently executing cell output streams and pushes the focused cell out of the viewport. my ideal thoughts: anchor the focused cell (with setting), maintaining the first line visible. output will stream in, pushing the executing cell upwards out of the viewport shift-enter set to "fullCell" and next cell has a large output focus shifts to next cell, and the viewport exposes the bottom of the output for that cell. there is no cell editor in the viewport my thoughts: "fullCell" behavior should show the top of the next cell editor at the top of the viewport

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 {