QCPXQ2E3USF3Z6R6WJ2JKHTRMPKA6QWXFKKRMLXA3MXABJEL543AC
WLJCIXYMSTCNSYCFOEBQNDLBZ5D2Z3WTF4E4WYL5CFGIJ434FKNQC
3MAZEQK5AR3IJJ2ENHHYDPDICIK645NE5QWR54Z52BHGHE6VR5XQC
BULPIBEGL7TMK6CVIE7IS7WGAHGOSUJBGJSFQK542MOWGHP2ADQQC
SPSW74Y5OJ54Y7VQ3SJFCJR5CYDKTR4A3TOEVZODDZLUSDDU2GZAC
3TDOZESEOYHGF6LYKR6PYSPNFI3QUGED2BKM5LUDEKJKRIX3ACEAC
OGUV4HSA7XGSQLUVWBAE3AE263Z7Z6G3BZOB4CN2AOYD2DEJMOZAC
LF7BWEG4DKQI7NMXMZC4LC2BE5PB42HK5PD6OYBNIDMAZBJASOKQC
HMODUNJEQLZ3W46GKYIDL55F6COVXHTIC6UW4AK3SXOOKOPE6NNAC
XNFTJHC4QSHNSIWNN7K6QZEZ37GTQYKHS4EPNSVPQCUSWREROGIQC
KOYAJWE4NJ2J4X3SHEAVMRXYZPZGOMTI7OX3PTUQIDIZ2GQI6UKAC
Z5HLXU4PJWWJJDBCK52NBD6PIRIA3TAN2BKZB5HBYFGIDBX4F5HAC
2CH77LZCSHAKRKLCCJGDGECVYFNCEV23NF3PFXHAQ2E33AJGSNVAC
ZPUQSPQPQFVRUIHGLAWW3IDBYODIWDHO62HAC3WWF5TM3CIJGHNQC
2LOQ5ALJYHWSMU7ROSKD66BYGMK3O6HYNUQMGCZVKTRDOLEI75NQC
CVSRHMJ2BM4LPVG67ULIVQMP2NW3YY2JC2ZQBEA6EB5KVM4O2L5AC
KZ5GAYRPWF2BA5VEIW3A4G2TULATBL7YEDGFJU42GBP5DET7BI3AC
GGJEDJOOKE5LM5KERQOWLM5FRIJGIF5UZBFPGY4F4MWBHY6Y5YUQC
QCQTMUZ7M3BKJFTKXTTXL4TS4CAQNIUNK3LR3WQIJDU3VVTOPS6AC
YJJ4X4JGABMVA5JBQW5UAWI543P3Y7NDVFTOHA6LIDA5KSFGUFNQC
HTWAM4NZFOY463TNSKYIM2EWB7QNBGDRRTTGHF5N3Z4TGC7Q3SFAC
62PZGSUCEXJOCVWEOOENSDJITJFR27BGW7BPGFYVD3E5M6446RQQC
KECEMMMRW2VVBZ567HJQPGLC57LTSBKWH7UFP32IW43D23X6WTEQC
F65ADDGLR2PNXVSM2XBHM3OSLQC2OTRR3GQBI7DJWIKPJCJ5CSOAC
5OALPNN3FGDKFM4K5EQZV6FU6GCKHEVSJDXM6XFFC7LGXES7GLWQC
HYEAFRZ2UEKDYTAE2GDQLHEJBPQASP2NDLMXB7F6MTVK2BKOXKEAC
PR4KIAZDOBQMEUOV2G7ZEZUW3E4L5ZCHYSS7PTYWGXPSNVRAGHCAC
242L3OQXTU2TCAINRJXQEEDSXQXM7Y7USUPBK37ZNM3A7V5TUDSAC
MDXGMZU2MBEDMTB755D3RRYEFKF54GTTYTI5XJYKKKN5ZFQWZXTAC
KOTI3MFGQ4PDS4I75JIJG734LTET6745VGTSMNFYYASVIO6H2KPAC
TRNWIQN6RPLDLYWULLKG5L255E7E3DPNGLCSLAF6IJWYQRCCLARQC
3OKKTUT4Q7W44JHILOFV5BVUA7ZOBIHBCEXGZ65CPXV4PRLI2W4QC
CTJ3IZGSPY4DBHC6OYNL4DZE24MXYQBM3KVJZTQHM5DI5TED5ZLQC
MYC7XR5QOT2AXHF6UNGSNFFD5VL6UHGUZQBP7PWWLZ5NNXE7UMTAC
UV4EWOLYCQ27TL6IGGLKNQX3UUOF7HJ5EJVCZYW345X6BK4J7YQAC
5FW7YOFTLKHRND6IOR4HG4X3C5BO2WV5KTEUW3PPKCRU5L5GXKXQC
K4OBZSHEBIZBAKPH3F7ASDGCPLB7D5W5QLFJQYSM5XOYDPB4BUHAC
U52E2XZNDEMIX5QJC6TREX5BSLNYG23Y4XQVFFKS6OFB2KIBW7BAC
CPZGQT72EBP3SEDBPDWQRK5IUGA664PHXNP2GOHJLP43PKPWF25AC
BOFNXP5GZDCUMQG3LQVTSSFEQP7REQ4RIRJLDLETFSAGFTVDVEKAC
2RXZ3PGOTTZ6M4R372JXIKPLBQKPVBMAXNPIEO2HZDN4EMYW4GNAC
H2DPLWMVRFYTO2CQTG54FMT2LF3B6UKLXH32CUA22DNQJVP5XBNQC
EGH7XDBKE3R74VXLNTCAP5LJTRBPFUEMPS647MJARDGCMUHJG2QQC
MP2TBKU6CNDMZKENYMBV62F5KQ27ZWEVPVRFS2RESVDQQT2IRR4AC
AYE2VEGJ63AWWX76SFQZLOTBIZOQRWBG4AZMIOSVOI2WZVRQJXYAC
HOSPP2ANSW654DYRTC6CQUQA2GUKV6T2FI7QBKXD2DZS3R32IMGAC
GK47BBCYVEZ3OEQ7ISE2WCJULAFZ35WC6EYJ5CTBYNM26RSAELOQC
WOXIYUTL4NU7ACHQYXEXJDSXCRDLQ2X457KO6C7GEXFQZ43F3L7QC
QLTJG7Q33ABBTDJ55K3OPLNSYBFBIVRS3UABXEY73RHYMOOJ542QC
LXTTOB33N2HCUZFIUDRQGGBVHK2HODRG4NBLH6RXRQZDCHF27BSAC
RF5ALVNYB2FMU7LRRD5LMQC7P6OO4BX3NXIGWNZTQ2CD62RBRRFAC
K464QQR4FTXFUMHFWAGOD5DJ6YHUBUKRHLXF2ORE74DVT7TVQ35QC
OTIBCAUJ3KDQJLVDN3A536DLZGNRYMGJLORZVR3WLCGXGO6UGO6AC
PX7DDEMOBGPVK3FXKK5XEPG24CJXZSVW67DLG2JZZ5E77NVEAA3AC
2L5MEZV344TOZLVY3432RHJFIRVXFD6O3GWLL5O4CV66BGAFTURQC
LNUHQOGHIOFGJXNGA3DZLYEASLYYDGLN2I3EDZY5ANASQAHCG3YQC
PTDO2SOTXEI6FROZ2AVRFXSKKNKCRMPPTQSI5LWD45UVGDJPMSGQC
local top2 = Text.to2(State.screen_top1, State.margin_left, App.screen.width-State.margin_right)
top2 = Text.previous_screen_line(top2, State.margin_left, App.screen.width-State.margin_right)
State.screen_top1 = Text.to1(top2)
Text.redraw_all() -- if we're scrolling, reclaim all fragments to avoid memory leaks
local top2 = Text.to2(State, State.screen_top1, State.margin_left, App.screen.width-State.margin_right)
top2 = Text.previous_screen_line(State, top2, State.margin_left, App.screen.width-State.margin_right)
State.screen_top1 = Text.to1(State, top2)
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
local top2 = Text.to2(State.screen_top1, left, right)
top2 = Text.previous_screen_line(top2, left, right)
State.screen_top1 = Text.to1(top2)
local top2 = Text.to2(State, State.screen_top1, left, right)
top2 = Text.previous_screen_line(State, top2, left, right)
State.screen_top1 = Text.to1(State, top2)
function Text.cursor_at_final_screen_line(left, right)
Text.populate_screen_line_starting_pos(Editor_state.lines[Editor_state.cursor1.line], left, right)
local screen_lines = Editor_state.lines[Editor_state.cursor1.line].screen_line_starting_pos
--? print(screen_lines[#screen_lines], Editor_state.cursor1.pos)
return screen_lines[#screen_lines] <= Editor_state.cursor1.pos
function Text.cursor_at_final_screen_line(State, left, right)
Text.populate_screen_line_starting_pos(State.lines[State.cursor1.line], left, right)
local screen_lines = State.lines[State.cursor1.line].screen_line_starting_pos
--? print(screen_lines[#screen_lines], State.cursor1.pos)
return screen_lines[#screen_lines] <= State.cursor1.pos
function Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(left, right)
local y = Editor_state.margin_top
while Editor_state.cursor1.line <= #Editor_state.lines do
if Editor_state.lines[Editor_state.cursor1.line].mode == 'text' then
function Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State, left, right)
local y = State.margin_top
while State.cursor1.line <= #State.lines do
if State.lines[State.cursor1.line].mode == 'text' then
--? print('cursor skips', Editor_state.cursor1.line)
y = y + Editor_state.drawing_padding_height + Drawing.pixels(Editor_state.lines[Editor_state.cursor1.line].h)
Editor_state.cursor1.line = Editor_state.cursor1.line + 1
--? print('cursor skips', State.cursor1.line)
y = y + State.drawing_padding_height + Drawing.pixels(State.lines[State.cursor1.line].h)
State.cursor1.line = State.cursor1.line + 1
if Editor_state.cursor1.line > #Editor_state.lines then
assert(Editor_state.cursor1.line == #Editor_state.lines+1)
table.insert(Editor_state.lines, {mode='text', data=''})
if State.cursor1.line > #State.lines then
assert(State.cursor1.line == #State.lines+1)
table.insert(State.lines, {mode='text', data=''})
-- should never modify Editor_state.cursor1
function Text.snap_cursor_to_bottom_of_screen(left, right)
local top2 = Text.to2(Editor_state.cursor1, left, right)
-- should never modify State.cursor1
function Text.snap_cursor_to_bottom_of_screen(State, left, right)
local top2 = Text.to2(State, State.cursor1, left, right)
--? print('cursor pos '..tostring(Editor_state.cursor1.pos)..' is on the #'..tostring(top2.screen_line)..' screen line down')
local y = App.screen.height - Editor_state.line_height
--? print('cursor pos '..tostring(State.cursor1.pos)..' is on the #'..tostring(top2.screen_line)..' screen line down')
local y = App.screen.height - State.line_height
if top2.screen_line > 1 or Editor_state.lines[top2.line-1].mode == 'text' then
local h = Editor_state.line_height
if y - h < Editor_state.margin_top then
if top2.screen_line > 1 or State.lines[top2.line-1].mode == 'text' then
local h = State.line_height
if y - h < State.margin_top then
local h = Editor_state.drawing_padding_height + Drawing.pixels(Editor_state.lines[top2.line-1].h)
if y - h < Editor_state.margin_top then
local h = State.drawing_padding_height + Drawing.pixels(State.lines[top2.line-1].h)
if y - h < State.margin_top then
Editor_state.screen_top1 = Text.to1(top2)
--? print('top1 finally:', Editor_state.screen_top1.line, Editor_state.screen_top1.pos)
Text.redraw_all() -- if we're scrolling, reclaim all fragments to avoid memory leaks
State.screen_top1 = Text.to1(State, top2)
--? print('top1 finally:', State.screen_top1.line, State.screen_top1.pos)
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
return y < line.starty + Editor_state.line_height*(#line.screen_line_starting_pos - Text.screen_line_index(line, line.startpos) + 1)
return y < line.starty + State.line_height*(#line.screen_line_starting_pos - Text.screen_line_index(line, line.startpos) + 1)
-- manual test:
-- line: abc
-- def
-- gh
-- fragments: abc, def, gh
-- click inside e
-- line_starting_pos = 1 + 3 = 4
-- nearest_cursor_pos('defgh', mx) = 2
-- Editor_state.cursor1.pos = 4 + 2 - 1 = 5
-- manual test:
-- click inside h
-- line_starting_pos = 1 + 3 + 3 = 7
-- nearest_cursor_pos('gh', mx) = 2
-- Editor_state.cursor1.pos = 7 + 2 - 1 = 8
Text.populate_screen_line_starting_pos(Editor_state.lines[pos1.line], left, right)
for i=#Editor_state.lines[pos1.line].screen_line_starting_pos,1,-1 do
local spos = Editor_state.lines[pos1.line].screen_line_starting_pos[i]
Text.populate_screen_line_starting_pos(State.lines[pos1.line], left, right)
for i=#State.lines[pos1.line].screen_line_starting_pos,1,-1 do
local spos = State.lines[pos1.line].screen_line_starting_pos[i]
local l = Editor_state.lines[pos2.line-1]
Text.populate_screen_line_starting_pos(Editor_state.lines[pos2.line-1], left, right)
return {line=pos2.line-1, screen_line=#Editor_state.lines[pos2.line-1].screen_line_starting_pos, screen_pos=1}
local l = State.lines[pos2.line-1]
Text.populate_screen_line_starting_pos(State.lines[pos2.line-1], left, right)
return {line=pos2.line-1, screen_line=#State.lines[pos2.line-1].screen_line_starting_pos, screen_pos=1}
function Text.tweak_screen_top_and_cursor(left, right)
--? print('a', Editor_state.selection1.line)
if Editor_state.screen_top1.pos == 1 then return end
local line = Editor_state.lines[Editor_state.screen_top1.line]
-- resize helper
function Text.tweak_screen_top_and_cursor(State, left, right)
--? print('a', State.selection1.line)
if State.screen_top1.pos == 1 then return end
local line = State.lines[State.screen_top1.line]
if Text.lt1(Editor_state.cursor1, Editor_state.screen_top1) then
Editor_state.cursor1 = {line=Editor_state.screen_top1.line, pos=Editor_state.screen_top1.pos}
elseif Editor_state.cursor1.line >= Editor_state.screen_bottom1.line then
if Text.lt1(State.cursor1, State.screen_top1) then
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
elseif State.cursor1.line >= State.screen_bottom1.line then
local line = Editor_state.lines[Editor_state.screen_bottom1.line]
Editor_state.cursor1 = {
line=Editor_state.screen_bottom1.line,
pos=Text.to_pos_on_line(line, App.screen.width-5, App.screen.height-5, left, right),
local line = State.lines[State.screen_bottom1.line]
State.cursor1 = {
line=State.screen_bottom1.line,
pos=Text.to_pos_on_line(State, line, App.screen.width-5, App.screen.height-5, left, right),
if Text.in_line(line, x,y, left, right) then
return line_index, Text.to_pos_on_line(line, x,y, left, right)
if Text.in_line(State, line, x,y, left, right) then
return line_index, Text.to_pos_on_line(State, line, x,y, left, right)