I think this works!
So the cause of all my trouble was a historical accident: I thought of computing viewport from screen_top before I thought of bring_cursor_of_cursor_pane_in_view. Turning the symbolic dependency DAG of variables into a graph turns out to be no end of trouble, where A sometimes depends on B and B sometimes on A.
Let's see if we uncover any issues.
NP5DODWMHP22MO6Z3B6NYDR7C3DLV2BD4NKT2RFFFOSIDPC3ZPWQC 4WAZ3E5BASY4GJDS5XNDRPHD4S4NVKRWHYOSFT6EXRTMM7Y5EKPAC PKRRCD2P4LVQLDUCEJBQSKL4SJXRJ4P432VX7V27BBMGDMXIZAQAC JMUE7GSN6QDQZ6NDRB55MRJMKJN6LBD6MVQPKROYPDOIXM7I3XNQC UAX3KJOIN3XBSLKP4IAWAWSCI3DLDKF22A6HRMGA4FHY3AS2QRVAC KKMFQDR43ZWVCDRHQLWWX3FCWCFA3ZSXYOBRJNPHUQZR2XPKWULAC 3U6YMMN2GMRL27TLPE7V3KUGM6MI2L3SAQSNU6HMNRINVXVRL3QAC VPCPK52KMU4MZUXP4SUSJJDEHDR4C3KJ45HLMUQUK32FMY6OCQ4QC F3DV7MD3AGOBXDBGFQXB5SK7ASMNGQSTN52P5AJ4MJEETMVTKYWQC B3DXRFR34ASOJPIYXNKV27XXLRW2METE7TDN3IWLPE7AOSWVSXAAC DYAYOMDPEQEUGTUPXWKICPBICZ67D6OW773FVIMS7XPIYPOYY2DQC GR4RROJFJOGM2WLQSVK5R5UYD7IK6ITF6UP5RVHAHD3J4OFJWGCAC 3IBO5P7DVWMJM42VZL5HIFAJH55FK77K2IKPGMNI55GRXDXT4WUQC CSV2GBWMGLN6CR3FIUUQ3FTZAFJJFO7DTPNOKOY5IOXNFI4O67IAC AAYK7CECIP4352RGBPFBV52XUKI6YECVJN24PVCN4FG4NW7NQLKAC AHOO2ILEJWTPCYHJH26WAF7A4YYVMHFX4UWHSAAAMHI73TSQZ6CAC KTABGWEVR6D5KLOCVAOUDNQMRB3BUFU4GTR3OIAWLJSIPIG6G46QC 37AATGG5QIXOBXWA3DMSVMIIDSULA36NPETHOYZIOZ4G4RZLMHCAC if should_update_screen_top(column_index, pane_index, pane, options) thenif body_sy < Display_settings.y thenpane.screen_top1, y_offset = schema1_of_y(pane, Display_settings.y - body_sy)elsepane.screen_top1 = {line=1, pos=1}endend
function should_update_screen_top(column_index, pane_index, pane, options)if options == nil then return true endif column_index ~= Cursor_pane.col then return true endif pane_index ~= Cursor_pane.row then return true endreturn not options.preserve_screen_top_of_cursor_paneend
endend-- return whether update happenedfunction maybe_update_screen_top_of_cursor_pane(pane, old_top)local cursor_sy = up_edge_sy(Cursor_pane.col, Cursor_pane.row) + y_of_schema1(pane, pane.cursor1)if not eq(old_top, pane.screen_top1) and eq(old_top, {line=1, pos=1}) and pane.top > Header_height and cursor_sy - Display_settings.y > App.screen.height - Header_height - Line_height then-- updating screen_top1 can be jarring if the pane had room above it on screen-- the editor updates screen_top1, then we update viewport based on screen_top1, and it goes from somewhere lower down to all the way at top of screen-- prefer to pan the surface when possiblepane.screen_top1 = old_topbring_cursor_of_cursor_pane_in_view('down')Surface.cursor_on_screen_check = true -- cursor was on screen before keystroke, so it should remain on screen afterreturn false -- bring_cursor_of_cursor_pane_in_view updated Display_settings.y but not pane.screen_top1
local screen_top_updated = not eq(old_top, pane.screen_top1)if screen_top_updated thenDisplay_settings.y = up_edge_sy(Cursor_pane.col, Cursor_pane.row) + y_of_schema1(pane, pane.screen_top1)Surface.cursor_on_screen_check = true -- cursor was on screen before keystroke, so it should remain on screen afterendreturn screen_top_updated