I can't believe I didn't catch this until now. All I had to do is open MobyDick.markdown from https://www.hogbaysoftware.com/posts/moby-dick-workout, press page-down and click on the top screen line (or any screen line containing the same line as the top screen line). Easy to catch with any file containing lots of overly long lines, as happens in particular at the start of Moby Dick.
I had seen this problem before, but it seemed to disappear after unrelated changes, and I convinced myself I'd fixed it as a side-effect. The bug just failed to manifest if the top line happened to start at the top of the screen. Scroll down a few pages in Moby Dick and the dialogue starts and line length drops precipitously.
MYC7XR5QOT2AXHF6UNGSNFFD5VL6UHGUZQBP7PWWLZ5NNXE7UMTAC
TNHZZYWPVWKA3OEBRH3QQ64GW3D2BQL5JJAVIUKQM5WBTYWASMIQC
LAW2O3NWVFTPBSKIMIXPAGYBDOCHYJNKCAVWKNKH62G42DIKZCYQC
PLKNHYZ4KXWWKC2DHXCI4WVO23I7VMEVYT5H2J6JDE4S3D3CHDJQC
GJLOKCYKETWXJXBOS5222HVZIKBDOGLLR5QLUZYCTZG7FBYDTQMQC
IWYLK45KJSPRXKW55OD4GEPMLTYMMTXNFJJU26JTZN3RE35DWSCQC
LXTTOB33N2HCUZFIUDRQGGBVHK2HODRG4NBLH6RXRQZDCHF27BSAC
H2DPLWMVRFYTO2CQTG54FMT2LF3B6UKLXH32CUA22DNQJVP5XBNQC
BULPIBEGL7TMK6CVIE7IS7WGAHGOSUJBGJSFQK542MOWGHP2ADQQC
CCYSVZA2ONWXB6XJXWSIEBY4CS2LGBEVV3RB6KZ6I4XYRXQLSTXQC
DLQMM2656JHXX3ONOEM6UIOXKFJFT5QT7RHWK7YS2W77PVZWHRSAC
HYEAFRZ2UEKDYTAE2GDQLHEJBPQASP2NDLMXB7F6MTVK2BKOXKEAC
6E3HVYWFP3JLJ3DJ5BH4WGJUXQV5MDCBCE5GH3SXRHRQZOG4VJLQC
BOFNXP5GZDCUMQG3LQVTSSFEQP7REQ4RIRJLDLETFSAGFTVDVEKAC
HOSPP2ANSW654DYRTC6CQUQA2GUKV6T2FI7QBKXD2DZS3R32IMGAC
CBPV5SSIJFGEZLGF7LMC35KZWUTZUFNAWTTVNYDMEEUK4EBOMOVQC
U52E2XZNDEMIX5QJC6TREX5BSLNYG23Y4XQVFFKS6OFB2KIBW7BAC
5L7K4GBDEAFH44LMLNKVFMHLWDNXXBKRPEI347VE5ZLXVFSMD2FAC
MP2TBKU6CNDMZKENYMBV62F5KQ27ZWEVPVRFS2RESVDQQT2IRR4AC
CPZGQT72EBP3SEDBPDWQRK5IUGA664PHXNP2GOHJLP43PKPWF25AC
65XHTZEKUTGHMOIWAFRH7ZVGUP4DWBCUT2TN4Y3LHYILKWTTBLKAC
OTIBCAUJ3KDQJLVDN3A536DLZGNRYMGJLORZVR3WLCGXGO6UGO6AC
OYXDYPGSJK2QICJ6RBA7357WT4FSNAWRUT77YLQHT3F3VYMWGNFQC
UWNHC4AAO3SPOYLPANTO4WKCTZL7KAYC73Q2YUZFFW7K26FVJ7FQC
ESETRNLB3MIJ2SID6HJMMP52FEVUBLGK2HLWD75KDQZAKQMKSF2QC
YTSPVDZHEN5LLNMGIBUBLPWFWSFM3SOHBRGWYSDEVFKRTH24ARRQC
CVGE3SIGJRGCLY3A2RBPGFXAEKVZXUUIZQLRHJLM4VPUM4SHEZIAC
function test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen()
io.write('\ntest_click_on_wrapping_line_rendered_from_partway_at_top_of_screen')
-- display a wrapping line from its second screen line
App.screen.init{width=80, height=80}
-- 12345678901234
Lines = load_array{"madam I'm adam"}
Line_width = 75
Cursor1 = {line=1, pos=8}
Screen_top1 = {line=1, pos=7}
Screen_bottom1 = {}
App.draw()
local y = Margin_top
App.screen.check(y, "I'm ada", 'F - test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen/baseline/screen:2')
y = y + Line_height
-- click past end of second screen line
App.run_after_mouse_click(App.screen.width-2,y-2, 1)
-- cursor moves to end of screen line
check_eq(Cursor1.line, 1, 'F - test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen/cursor:line')
check_eq(Cursor1.pos, 13, 'F - test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen/cursor:pos')
end
local y = line.y
for screen_line_index,screen_line_starting_pos in ipairs(line.screen_line_starting_pos) do
local screen_line_starting_byte_offset = Text.offset(line.data, screen_line_starting_pos)
local y = line.starty
local start_screen_line_index = Text.screen_line_index(line, line.startpos)
for screen_line_index = start_screen_line_index,#line.screen_line_starting_pos do
local screen_line_starting_pos = line.screen_line_starting_pos[screen_line_index]
local screen_line_starting_byte_offset = Text.offset(line.data, screen_line_starting_pos)