Some of this was working in assumptions about the menu bar, but it was also just wrong before, not taking the zoom factor into account. As before, I'm not very good at reasoning in pixels.
MAUOADNV62ZEWXWUTNJPVNWE6IFEDN23F6S5YVWW743EQJQ6NNVAC update_editor_box = function(node, preserve_screen_top_of_cursor_node)if node.editor == nil then return end-- Compute screen_top1 in viewport coordinates because the editor's font takes scaling into account.if vy(node.y) > 0 thenif not preserve_screen_top_of_cursor_node or node ~= Cursor_node thennode.editor.screen_top1.line = 1node.editor.screen_top1.pos = 1endnode.editor.top = vy(node.y)elsenode.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, -vy(node.y))endnode.editor.left = math.floor(vx(node.x+Line_number_width))node.editor.right = math.ceil(vx(node.x+node.w))edit.update_font_settings(node.editor, scale(20))Text.redraw_all(node.editor)end
{"scale":7,"on.mouse_press":179,"on.mouse_release":367,"font":353,"initialize_editor":450,"copy_shape":396,"box_height":345,"on.keychord_press":391,"compute_layout":385,"on":1,"on.draw":452,"Page":444,"vx":5,"on.initialize":350,"Viewport":459,"update_editor_box":473,"vy":462,"on.update":368,"y_of_schema1":364,"on.text_input":388,"parent":472,"Surface":422,"on.code_change":306,"sy":469,"B":379,"line_height":365,"to_text":180,"add_thick_line":400,"Cursor_node":172,"schema1_of_y":467,"A":433}
update_editor_box = function(node, preserve_screen_top_of_cursor_node)if node.editor == nil then return end-- Compute screen_top1 in viewport coordinates because the editor's font takes scaling into account.if vy(node.y) > 0 thenif not preserve_screen_top_of_cursor_node or node ~= Cursor_node thennode.editor.screen_top1.line = 1node.editor.screen_top1.pos = 1endnode.editor.top = vy(node.y)elsenode.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, vy(node.y))endnode.editor.left = math.floor(vx(node.x+Line_number_width))node.editor.right = math.ceil(vx(node.x+node.w))edit.update_font_settings(node.editor, scale(20))Text.redraw_all(node.editor)end
{"A":433,"on.draw":452,"B":379,"y_of_schema1":364,"font":353,"copy_shape":396,"Cursor_node":172,"scale":7,"add_thick_line":400,"parent":471,"Viewport":459,"on":1,"on.update":368,"schema1_of_y":467,"on.mouse_press":179,"on.mouse_release":367,"initialize_editor":450,"Page":444,"box_height":345,"on.keychord_press":391,"compute_layout":385,"sy":469,"vx":5,"on.code_change":306,"update_editor_box":472,"on.text_input":388,"line_height":365,"to_text":180,"vy":462,"Surface":422,"on.initialize":350}
update_editor_box = function(node, preserve_screen_top_of_cursor_node)if node.editor == nil then return end-- Compute screen_top1 in viewport coordinates because the editor's font takes scaling into account.if vy(node.y) > Menu_bar_height - 5 thenif not preserve_screen_top_of_cursor_node or node ~= Cursor_node thennode.editor.screen_top1.line = 1node.editor.screen_top1.pos = 1endnode.editor.top = vy(node.y)elsenode.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, vy(node.y))endnode.editor.left = math.floor(vx(node.x+Line_number_width))node.editor.right = math.ceil(vx(node.x+node.w))edit.update_font_settings(node.editor, scale(20))Text.redraw_all(node.editor)end
{"A":433,"on.draw":452,"B":379,"y_of_schema1":364,"font":353,"copy_shape":396,"Cursor_node":172,"scale":7,"add_thick_line":400,"parent":470,"Viewport":459,"on":1,"on.update":368,"schema1_of_y":467,"on.mouse_press":179,"on.mouse_release":367,"initialize_editor":450,"Page":444,"box_height":345,"on.keychord_press":391,"compute_layout":385,"sy":469,"vx":5,"on.code_change":306,"update_editor_box":471,"on.text_input":388,"line_height":365,"to_text":180,"vy":462,"Surface":422,"on.initialize":350}
update_editor_box = function(node, preserve_screen_top_of_cursor_node)if node.editor == nil then return endif node.y > Viewport.y thenif not preserve_screen_top_of_cursor_node or node ~= Cursor_node thennode.editor.screen_top1.line = 1node.editor.screen_top1.pos = 1endnode.editor.top = vy(node.y)elsenode.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, vy(node.y))endnode.editor.left = math.floor(vx(node.x+Line_number_width))node.editor.right = math.ceil(vx(node.x+node.w))edit.update_font_settings(node.editor, scale(20))Text.redraw_all(node.editor)end
{"line_height":365,"on.text_input":388,"box_height":345,"compute_layout":385,"font":353,"on.code_change":306,"sy":469,"Surface":422,"Page":444,"parent":469,"to_text":180,"update_editor_box":470,"on.initialize":350,"y_of_schema1":364,"initialize_editor":450,"on.draw":452,"copy_shape":396,"vx":5,"Viewport":459,"vy":462,"schema1_of_y":467,"on.mouse_press":179,"on.mouse_release":367,"on.update":368,"on":1,"on.keychord_press":391,"add_thick_line":400,"A":433,"scale":7,"Cursor_node":172,"B":379}
sy = function(vy)-- turn viewport coordinates into surface coordinates-- vy = Menu_bar_height + scale(sy-Viewport.y)return (vy-Menu_bar_height)/Viewport.zoom + Viewport.yend
{"line_height":365,"on.text_input":388,"box_height":345,"compute_layout":385,"font":353,"on.code_change":306,"sy":469,"Surface":422,"Page":444,"parent":468,"to_text":180,"update_editor_box":468,"on.initialize":350,"y_of_schema1":364,"initialize_editor":450,"on.draw":452,"copy_shape":396,"vx":5,"Viewport":459,"vy":462,"schema1_of_y":467,"on.mouse_press":179,"on.mouse_release":367,"on.update":368,"on":1,"on.keychord_press":391,"add_thick_line":400,"A":433,"scale":7,"Cursor_node":172,"B":379}
update_editor_box = function(node, preserve_screen_top_of_cursor_node)if node.editor == nil then return endif node.y > Viewport.y thenif not preserve_screen_top_of_cursor_node or node ~= Cursor_node thennode.editor.screen_top1.line = 1node.editor.screen_top1.pos = 1endnode.editor.top = vy(node.y)elsenode.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, Viewport.y - node.y)endnode.editor.left = math.floor(vx(node.x+Line_number_width))node.editor.right = math.ceil(vx(node.x+node.w))edit.update_font_settings(node.editor, scale(20))Text.redraw_all(node.editor)end
{"line_height":365,"on.text_input":388,"box_height":345,"compute_layout":385,"font":353,"on.code_change":306,"Surface":422,"Page":444,"parent":467,"to_text":180,"update_editor_box":468,"on.initialize":350,"y_of_schema1":364,"initialize_editor":450,"on.draw":452,"copy_shape":396,"vx":5,"Viewport":459,"vy":462,"schema1_of_y":467,"on.mouse_press":179,"on.mouse_release":367,"on.update":368,"on":1,"on.keychord_press":391,"add_thick_line":400,"A":433,"scale":7,"Cursor_node":172,"B":379}
schema1_of_y = function(editor, y)-- return line/pos of screen line starting near a given y offset,-- and the (negative) offset remaining after the calculation-- invariants:-- - 0 >= y_offset >= -Line_height-- - let loc, y_offset = schema1_of_y(pane, y)-- y + y_offset == y_of_schema1(pane, loc)print(y)assert(y >= 0)local y_offset = yfor i=1,#editor.lines doText.populate_screen_line_starting_pos(editor, i)local height = line_height(editor, i)if y_offset < height thenlocal line = editor.lines[i]local nlines = math.floor(y_offset/editor.line_height)assert(nlines >= 0 and nlines < #editor.line_cache[i].screen_line_starting_pos)local pos = editor.line_cache[i].screen_line_starting_pos[nlines+1] -- switch to 1-indexingy_offset = y_offset - nlines*editor.line_heightreturn {line=i, pos=pos}, -y_offsetendy_offset = y_offset - heightend-- y is below the panereturn {line=#editor.lines+1, pos=1}, y_offset -- positive valueend
{"line_height":365,"on.text_input":388,"box_height":345,"compute_layout":385,"font":353,"on.code_change":306,"Surface":422,"Page":444,"parent":466,"to_text":180,"update_editor_box":466,"on.initialize":350,"y_of_schema1":364,"initialize_editor":450,"on.draw":452,"copy_shape":396,"vx":5,"Viewport":459,"vy":462,"schema1_of_y":467,"on.mouse_press":179,"on.mouse_release":367,"on.update":368,"on":1,"on.keychord_press":391,"add_thick_line":400,"A":433,"scale":7,"Cursor_node":172,"B":379}
update_editor_box = function(node, preserve_screen_top_of_cursor_node)if node.editor == nil then return endif node.y > Viewport.y thenif not preserve_screen_top_of_cursor_node or node ~= Cursor_node thennode.editor.screen_top1.line = 1node.editor.screen_top1.pos = 1endnode.editor.top = vy(node.y)elsenode.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, Viewport.y - node.y)endnode.editor.top = node.editor.topnode.editor.left = math.floor(vx(node.x+Line_number_width))node.editor.right = math.ceil(vx(node.x+node.w))edit.update_font_settings(node.editor, scale(20))Text.redraw_all(node.editor)end
{"line_height":365,"on.text_input":388,"box_height":345,"compute_layout":385,"font":353,"on.code_change":306,"Surface":422,"Page":444,"parent":465,"to_text":180,"update_editor_box":466,"on.initialize":350,"y_of_schema1":364,"initialize_editor":450,"on.draw":452,"copy_shape":396,"vx":5,"Viewport":459,"vy":462,"schema1_of_y":366,"on.mouse_press":179,"on.mouse_release":367,"on.update":368,"on":1,"on.keychord_press":391,"add_thick_line":400,"A":433,"scale":7,"Cursor_node":172,"B":379}
update_editor_box = function(node, preserve_screen_top_of_cursor_node)if node.editor == nil then return endif node.y > Viewport.y thenif not preserve_screen_top_of_cursor_node or node ~= Cursor_node thennode.editor.screen_top1.line = 1node.editor.screen_top1.pos = 1endnode.editor.top = vy(node.y)elsenode.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, Viewport.y - node.y)endnode.editor.top = node.editor.topprint(node.y, Viewport.y, node.editor.top)node.editor.left = math.floor(vx(node.x+Line_number_width))node.editor.right = math.ceil(vx(node.x+node.w))edit.update_font_settings(node.editor, scale(20))Text.redraw_all(node.editor)end
{"line_height":365,"on.text_input":388,"box_height":345,"compute_layout":385,"font":353,"on.code_change":306,"Surface":422,"Page":444,"parent":464,"to_text":180,"update_editor_box":465,"on.initialize":350,"y_of_schema1":364,"initialize_editor":450,"on.draw":452,"copy_shape":396,"vx":5,"Viewport":459,"vy":462,"schema1_of_y":366,"on.mouse_press":179,"on.mouse_release":367,"on.update":368,"on":1,"on.keychord_press":391,"add_thick_line":400,"A":433,"scale":7,"Cursor_node":172,"B":379}
update_editor_box = function(node, preserve_screen_top_of_cursor_node)if node.editor == nil then return endif node.y > Viewport.y thenif not preserve_screen_top_of_cursor_node or node ~= Cursor_node thennode.editor.screen_top1.line = 1node.editor.screen_top1.pos = 1endnode.editor.top = vy(node.y)elsenode.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, Viewport.y - node.y - Menu_bar_height)endnode.editor.top = node.editor.topprint(node.y, Viewport.y, node.editor.top)node.editor.left = math.floor(vx(node.x+Line_number_width))node.editor.right = math.ceil(vx(node.x+node.w))edit.update_font_settings(node.editor, scale(20))Text.redraw_all(node.editor)end
{"line_height":365,"on.text_input":388,"box_height":345,"compute_layout":385,"font":353,"on.code_change":306,"Surface":422,"Page":444,"parent":463,"to_text":180,"update_editor_box":464,"on.initialize":350,"y_of_schema1":364,"initialize_editor":450,"on.draw":452,"copy_shape":396,"vx":5,"Viewport":459,"vy":462,"schema1_of_y":366,"on.mouse_press":179,"on.mouse_release":367,"on.update":368,"on":1,"on.keychord_press":391,"add_thick_line":400,"A":433,"scale":7,"Cursor_node":172,"B":379}
update_editor_box = function(node, preserve_screen_top_of_cursor_node)if node.editor == nil then return endif node.y > Viewport.y thenif not preserve_screen_top_of_cursor_node or node ~= Cursor_node thennode.editor.screen_top1.line = 1node.editor.screen_top1.pos = 1endnode.editor.top = vy(node.y)elsenode.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, Viewport.y - node.y + Menu_bar_height)endnode.editor.top = node.editor.topprint(node.y, Viewport.y, node.editor.top)node.editor.left = math.floor(vx(node.x+Line_number_width))node.editor.right = math.ceil(vx(node.x+node.w))edit.update_font_settings(node.editor, scale(20))Text.redraw_all(node.editor)end
{"line_height":365,"on.text_input":388,"box_height":345,"compute_layout":385,"font":353,"on.code_change":306,"Surface":422,"Page":444,"parent":462,"to_text":180,"update_editor_box":463,"on.initialize":350,"y_of_schema1":364,"initialize_editor":450,"on.draw":452,"copy_shape":396,"vx":5,"Viewport":459,"vy":462,"schema1_of_y":366,"on.mouse_press":179,"on.mouse_release":367,"on.update":368,"on":1,"on.keychord_press":391,"add_thick_line":400,"A":433,"scale":7,"Cursor_node":172,"B":379}
vy = function(y)return Menu_bar_height + scale(y-Viewport.y)end
{"line_height":365,"on.text_input":388,"box_height":345,"compute_layout":385,"font":353,"on.code_change":306,"Surface":422,"Page":444,"parent":461,"to_text":180,"update_editor_box":458,"on.initialize":350,"y_of_schema1":364,"initialize_editor":450,"on.draw":452,"copy_shape":396,"vx":5,"Viewport":459,"vy":462,"schema1_of_y":366,"on.mouse_press":179,"on.mouse_release":367,"on.update":368,"on":1,"on.keychord_press":391,"add_thick_line":400,"A":433,"scale":7,"Cursor_node":172,"B":379}
vy = function(y)return Menu_bar_height + Margin_top + scale(y-Viewport.y)end
{"line_height":365,"on.text_input":388,"box_height":345,"compute_layout":385,"font":353,"on.code_change":306,"Surface":422,"Page":444,"parent":460,"to_text":180,"update_editor_box":458,"on.initialize":350,"y_of_schema1":364,"initialize_editor":450,"on.draw":452,"copy_shape":396,"vx":5,"Viewport":459,"vy":461,"schema1_of_y":366,"on.mouse_press":179,"on.mouse_release":367,"on.update":368,"on":1,"on.keychord_press":391,"add_thick_line":400,"A":433,"scale":7,"Cursor_node":172,"B":379}
vy = function(y)return Menu_bar_height + scale(y-Viewport.y)end
{"line_height":365,"on.mouse_press":179,"Viewport":459,"on.text_input":388,"on":1,"on.mouse_release":367,"box_height":345,"on.update":368,"scale":7,"Cursor_node":172,"on.code_change":306,"B":379,"update_editor_box":458,"vy":460,"A":433,"schema1_of_y":366,"on.keychord_press":391,"compute_layout":385,"on.draw":452,"add_thick_line":400,"Page":444,"copy_shape":396,"initialize_editor":450,"y_of_schema1":364,"Surface":422,"on.initialize":350,"to_text":180,"parent":459,"font":353,"vx":5}
{"line_height":365,"on.mouse_press":179,"Viewport":459,"on.text_input":388,"on":1,"on.mouse_release":367,"box_height":345,"on.update":368,"scale":7,"Cursor_node":172,"on.code_change":306,"B":379,"update_editor_box":458,"vy":457,"A":433,"schema1_of_y":366,"on.keychord_press":391,"compute_layout":385,"on.draw":452,"add_thick_line":400,"Page":444,"copy_shape":396,"initialize_editor":450,"y_of_schema1":364,"Surface":422,"on.initialize":350,"to_text":180,"parent":458,"font":353,"vx":5}
Viewport = {x=0, y=0, w=800,h=600, zoom=1.0}
update_editor_box = function(node, preserve_screen_top_of_cursor_node)if node.editor == nil then return endif node.y > Viewport.y thenif not preserve_screen_top_of_cursor_node or node ~= Cursor_node thennode.editor.screen_top1.line = 1node.editor.screen_top1.pos = 1endnode.editor.top = vy(node.y)elsenode.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, Viewport.y - node.y)endnode.editor.top = node.editor.topprint(node.y, Viewport.y, node.editor.top)node.editor.left = math.floor(vx(node.x+Line_number_width))node.editor.right = math.ceil(vx(node.x+node.w))edit.update_font_settings(node.editor, scale(20))Text.redraw_all(node.editor)end
{"y_of_schema1":364,"A":433,"line_height":365,"scale":7,"B":379,"on.text_input":388,"on.mouse_release":367,"copy_shape":396,"to_text":180,"box_height":345,"parent":457,"on":1,"on.keychord_press":391,"on.update":368,"Page":444,"on.code_change":306,"update_editor_box":458,"font":353,"on.mouse_press":179,"compute_layout":385,"vy":457,"Surface":422,"add_thick_line":400,"initialize_editor":450,"on.draw":452,"schema1_of_y":366,"vx":5,"Cursor_node":172,"Viewport":439,"on.initialize":350}
vy = function(y)return scale(y-Viewport.y)end
{"y_of_schema1":364,"A":433,"line_height":365,"scale":7,"B":379,"on.text_input":388,"on.mouse_release":367,"copy_shape":396,"to_text":180,"box_height":345,"parent":456,"on":1,"on.keychord_press":391,"on.update":368,"Page":444,"on.code_change":306,"update_editor_box":456,"font":353,"on.mouse_press":179,"compute_layout":385,"vy":457,"Surface":422,"add_thick_line":400,"initialize_editor":450,"on.draw":452,"schema1_of_y":366,"vx":5,"Cursor_node":172,"Viewport":439,"on.initialize":350}
update_editor_box = function(node, preserve_screen_top_of_cursor_node)if node.editor == nil then return endif node.y > Viewport.y thenif not preserve_screen_top_of_cursor_node or node ~= Cursor_node thennode.editor.screen_top1.line = 1node.editor.screen_top1.pos = 1endnode.editor.top = vy(node.y)elsenode.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, Viewport.y - node.y)endnode.editor.top = node.editor.top + Menu_bar_heightprint(node.y, Viewport.y, node.editor.top)node.editor.left = math.floor(vx(node.x+Line_number_width))node.editor.right = math.ceil(vx(node.x+node.w))edit.update_font_settings(node.editor, scale(20))Text.redraw_all(node.editor)end
{"y_of_schema1":364,"A":433,"line_height":365,"scale":7,"B":379,"on.text_input":388,"on.mouse_release":367,"copy_shape":396,"to_text":180,"box_height":345,"parent":455,"on":1,"on.keychord_press":391,"on.update":368,"Page":444,"on.code_change":306,"update_editor_box":456,"font":353,"on.mouse_press":179,"compute_layout":385,"vy":448,"Surface":422,"add_thick_line":400,"initialize_editor":450,"on.draw":452,"schema1_of_y":366,"vx":5,"Cursor_node":172,"Viewport":439,"on.initialize":350}
update_editor_box = function(node, preserve_screen_top_of_cursor_node)if node.editor == nil then return endif node.y > Viewport.y thenif not preserve_screen_top_of_cursor_node or node ~= Cursor_node thennode.editor.screen_top1.line = 1node.editor.screen_top1.pos = 1endnode.editor.top = vy(node.y)elsenode.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, Viewport.y - node.y)endnode.editor.top = node.editor.top + Menu_bar_heightprint(node.y, node.editor.top)node.editor.left = math.floor(vx(node.x+Line_number_width))node.editor.right = math.ceil(vx(node.x+node.w))edit.update_font_settings(node.editor, scale(20))Text.redraw_all(node.editor)end
{"y_of_schema1":364,"A":433,"line_height":365,"scale":7,"B":379,"on.text_input":388,"on.mouse_release":367,"copy_shape":396,"to_text":180,"box_height":345,"parent":454,"on":1,"on.keychord_press":391,"on.update":368,"Page":444,"on.code_change":306,"update_editor_box":455,"font":353,"on.mouse_press":179,"compute_layout":385,"vy":448,"Surface":422,"add_thick_line":400,"initialize_editor":450,"on.draw":452,"schema1_of_y":366,"vx":5,"Cursor_node":172,"Viewport":439,"on.initialize":350}
update_editor_box = function(node, preserve_screen_top_of_cursor_node)if node.editor == nil then return endif node.y > Viewport.y thenif not preserve_screen_top_of_cursor_node or node ~= Cursor_node thennode.editor.screen_top1.line = 1node.editor.screen_top1.pos = 1endnode.editor.top = vy(node.y)elsenode.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, Viewport.y - node.y)endnode.editor.top = node.editor.top + Menu_bar_heightnode.editor.left = math.floor(vx(node.x+Line_number_width))node.editor.right = math.ceil(vx(node.x+node.w))edit.update_font_settings(node.editor, scale(20))Text.redraw_all(node.editor)end
{"y_of_schema1":364,"A":433,"line_height":365,"scale":7,"B":379,"on.text_input":388,"on.mouse_release":367,"copy_shape":396,"to_text":180,"box_height":345,"parent":453,"on":1,"on.keychord_press":391,"on.update":368,"Page":444,"on.code_change":306,"update_editor_box":454,"font":353,"on.mouse_press":179,"compute_layout":385,"vy":448,"Surface":422,"add_thick_line":400,"initialize_editor":450,"on.draw":452,"schema1_of_y":366,"vx":5,"Cursor_node":172,"Viewport":439,"on.initialize":350}
update_editor_box = function(node, preserve_screen_top_of_cursor_node)if node.editor == nil then return endif node.y >= Viewport.y thenif not preserve_screen_top_of_cursor_node or node ~= Cursor_node thennode.editor.screen_top1.line = 1node.editor.screen_top1.pos = 1endnode.editor.top = vy(node.y)elsenode.editor.screen_top1, node.editor.top = schema1_of_y(node.editor, Viewport.y - node.y)endnode.editor.top = node.editor.top + Menu_bar_heightnode.editor.left = math.floor(vx(node.x+Line_number_width))node.editor.right = math.ceil(vx(node.x+node.w))edit.update_font_settings(node.editor, scale(20))Text.redraw_all(node.editor)end
{"y_of_schema1":364,"A":433,"line_height":365,"scale":7,"B":379,"on.text_input":388,"on.mouse_release":367,"copy_shape":396,"to_text":180,"box_height":345,"parent":452,"on":1,"on.keychord_press":391,"on.update":368,"Page":444,"on.code_change":306,"update_editor_box":453,"font":353,"on.mouse_press":179,"compute_layout":385,"vy":448,"Surface":422,"add_thick_line":400,"initialize_editor":450,"on.draw":452,"schema1_of_y":366,"vx":5,"Cursor_node":172,"Viewport":439,"on.initialize":350}