Change:
if (addUndoStops) { editor.pushUndoStop(); } <ins> const scrollState = StableEditorScrollState.capture(editor); </ins> const edits = FormattingEdit._handleEolEdits(editor, _edits); if (edits.length === 1 && FormattingEdit._isFullModelReplaceEdit(editor, edits[0])) { // We use replace semantics and hope that markers stay put...

Discussion:
<!-- Do Not Delete This! bugreporttemplate --<!-- Please read our Rules of Conduct: --<!-- Read our guide about submitting issues: --<!-- Search existing issues to avoid creating duplicates. --<!-- Test using the latest Insiders build to see if your issue has already been fixed: --<!-- Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. --<!-- Launch with to check. --Does this issue occur when all extensions are disabled?: Yes <!-- If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. --<!-- Issues caused by an extension need to be reported directly to the extension publisher. The 'Help Report Issue' dialog can assist with this. --- VS Code Version: current main branch OS Version: MacOS 12.15.1 Steps to Reproduce: some code in a code editor with formatting-on-save set up, e.g. with Prettier changes that will be reformatted into more lines of code, such as the cursor under these changes how the cursor shifts This is what it looks like: ! It's not common but it happens that there are enough changes above the cursor that a save moves the cursor off the screen like this. I would prefer the cursor to stay put like this: ! An explicit format at least moves the cursor to the center of the screen if it disappears, but format-on-save doesn't. I think the current line should stay put instead of moving off the screen or to the middle of the screen. I have the beginning of the fix ready (seen in action in the second gif) and I'd be happy to submit a PR.
Can you please provide the full contents here? I doubt it will reproduce with 3 lines of code and your recordings show that you have much more content Also, please try this with the built-in TS formatter extension. Asking because not all formatter compute minimal formatting edits, they often instruct us to replace the whole file content and in those situations it is harder to preserve state
here's the file I used in the animations; it's just some placeholder code, not natural but it illustrates how the problem happens. If your window is big, you can duplicate the "b" lines.

I've tried it with a file extension, I hope that runs the built-in TS formatter as you suggested, . It behaves the same.
thanks for the extra info. I can reproduce, tho the cursor stays stable but the scroll position changes (and hides the cursor)
yes exactly; the editor does a good job in my experience of keeping the cursor in the right place in the text, but not on the screen. Thanks for clarifying the issue title. If you like, I can propose a PR that would fix it; I'm thinking something like this: if the file fits on the screen after the format, scroll so that the whole file is visible; else if the cursor is off the screen before the format, or if the formatting doesn't add or remove lines above the cursor position, I wouldn't touch the scroll position of the editor, so the same line number is visible at the top; else if the cursor is on the screen before the format and formatting adds lines above the cursor: if before the formatting there is any content above the screen (the first line of text is not visible); or if the cursor would disappear off the screen after the formatting, preserve cursor position on screen() if the cursor is on the screen before the format and formatting removes lines above the cursor: if after the formatting there is any content above the screen (the first line of text is not visible); or if the cursor would disappear off the screen after the formatting, preserve cursor position on screen() (*) Preserving cursor position on screen means the editor would change the window's scroll position so the cursor stays put relative to the visible screen and lines are or removed above the screen. It's a lot of special cases, but I believe it describes natural expected behaviour. I would also propose to implement the same behaviour for explicit formatting, not only for format-on-save. What do you think?
For suggestions we do something similar, accepting one can cause edits on lines above and we don't want the scroll position to change. We solve that by capturing the scroll state and by the edits... The challenge with format-on-save (and other save participants) is that they must not have an editor attached to them (you can save documents with editors) and that the format-on-save doesn't really know about editors and scroll states. Maybe can help to make this simpler Good news (or debt) is that some save participants already check if they run with an editor or not.

Formatting is one of them and the scroll-state persisting could be done for it (at the potential conflict cost of other participants doing something else) That makes sense - all formatting should be using  or (same file)
Thanks , I'll have a look at at how suggestions handle it and see if I can propose a clean-looking PR. Thanks also for pointing out those functions in ; I found them earlier as well when I was doing a quick-n-dirty proof-of-concept fix for myself.
Heads up, I'm still on it, just held up by $DAYJOB; thanks for the pointers, there's definitely space for reuse.