So far I've just changed how existing variables are organized, and put some scaffolding in place for dealing with the new types. Next up: rewriting the code for scrolling to something that feels more obviously correct.
2RXZ3PGOTTZ6M4R372JXIKPLBQKPVBMAXNPIEO2HZDN4EMYW4GNAC O6T3TPXDUSZKH2JHNHWIMSEV3UADIHHF26IYA44X3RCRXNUXEKBQC 2ZYV7D3W2HPQW2HYB7XDPM4T7KEWPUFPZ77BDLCCDSCLRPJFK6PQC GE56XURAWLO62DQKV4KJBJR4RGN2MVHQME2B44VVTCBYTBPZRP4AC 3TFEAQSWVFGSH3ISZ4Q3DFR3YPPWHEIBUEVR3XWB7QX6VKHW455QC PYGMASTVHDTGX3LDTL364UWXEHVSWQ7STAJLZZI5YY6EA6EEICOAC H2DPLWMVRFYTO2CQTG54FMT2LF3B6UKLXH32CUA22DNQJVP5XBNQC MGT5FTJ35MGYCQO3TZVK3RYUIN5YX475R4XG7RO42SYLYF4AIKFAC BULPIBEGL7TMK6CVIE7IS7WGAHGOSUJBGJSFQK542MOWGHP2ADQQC WLHI7KD3LJTQH6V7RLVJWGZUR4YQK6LN4OIUMIN45BGMMQGN6RNQC JY4VK7L2JKRWRV45QEMGLWPFAQRUWKFHMAL6DWNYEDCKO5Y4W5FQC 537TQ2QNPKPG322I4OIMN5IY22S45Z42LEBBZ2IN5MVM355BEJTAC XNFTJHC4QSHNSIWNN7K6QZEZ37GTQYKHS4EPNSVPQCUSWREROGIQC 5DOC2CBMBDMAOJ7IKLDGVRCY4SNPCJTTF7DK7WGNLPGNV4AWVJNAC DAENUOGV7KR6MZVXS36HEN3SZC4RFIS6REGAFVBOFEPO76EUDGIAC DLQMM2656JHXX3ONOEM6UIOXKFJFT5QT7RHWK7YS2W77PVZWHRSAC SVJZZDC3K6AKAXHGRNAZKRE2ZXEKJANNLG7LSSUZJARFBL5F7C4AC 3CSIZJ33MAZTTJUJX7H2VDJRGZ3A5AWKVSAQIMV3UQACVWNZA6ZAC C42QQZSFFGU6DZ73MCPGYZJQ675YTMEOJAPQLHKRJLWQH5GMWHMQC HBZ2UCUFM6EYLFZGUQVJDCLANO4UXYMBOU3TFPB2JASJMB53ZGXAC PR4KIAZDOBQMEUOV2G7ZEZUW3E4L5ZCHYSS7PTYWGXPSNVRAGHCAC 2POFQQLW42ZQCF7NBTIFLYKXBYT5PVSC3T5UOURIEPYNFVBN2MKAC KJKKASHZCC5JD6G6PWQ4TA42NVI2CNTAZ667GA76H272DD6KCNRQC 242L3OQXTU2TCAINRJXQEEDSXQXM7Y7USUPBK37ZNM3A7V5TUDSAC TRNWIQN6RPLDLYWULLKG5L255E7E3DPNGLCSLAF6IJWYQRCCLARQC HIH47LNBDXHB2PU2HPPG47IBX5QKJXB74G2SZ5B3ZUYYPTYR3TYAC HYEAFRZ2UEKDYTAE2GDQLHEJBPQASP2NDLMXB7F6MTVK2BKOXKEAC PHQPLJUQZOYZ7B3IDADDANMVXLKIKTU5DRSSEWTSDYCSDKX7M7JAC BOFNXP5GZDCUMQG3LQVTSSFEQP7REQ4RIRJLDLETFSAGFTVDVEKAC 5L7K4GBDEAFH44LMLNKVFMHLWDNXXBKRPEI347VE5ZLXVFSMD2FAC AVQ5MC5DWNLI6LUUIPGBLGP4LKRPGWBY4THNY25OBT2FAVHC6MCAC OTIBCAUJ3KDQJLVDN3A536DLZGNRYMGJLORZVR3WLCGXGO6UGO6AC MGOQ5XAVFTWZPBG2O5ZTGSEKU6BRJKQZLDV6CM4737VD2FAEB5JQC JCSLDGAH2F6AIY4Z6XM6K4LOMW7EFY3E4NF5YXLMHLTYTX3A4Z3QC A2QPFRFJNWDHBYRRLJFBK5BOTOWXDT5DYCKHRRKVBZNDA4NE3CHQC OIB2QPRCB4MAVZV5NCEKSAL45ITT6V4BYSET3Q2VCT3WBOIC4QVQC 2C7CTIQYDDYVQJNKX2OSHZ6VMAMPOGNUVTSFAUV7HQCPMZR2YRUAC BJ5X5O4ACBBJ56LRBBSTCW6IBQP4HAEOOOPNH3SKTA4F66YTOIDAC Y6FTGOHJH2OQTJVB2GQTJNXSZFGI2QWAS3V2NOFM64O5U5RNVKDQC 7IKRRESBHMYHHKW4XHUEEKHKPOBLAGZ7A7FJMRU32MTRKIV6S7GQC DXT4QTAH5G6J7ZB3SMOOXVECKWYUPZVE2ODMUFTPPNHLTOSZLQSAC 252M2QMDBMNWHBZY5MDSC7WVYO5JBLJYPVMW5W4IVJCZVYRQ5IQQC if Debug_new_render then print('checking to draw', pos, Top_screen_line_starting_pos) endif line_index > Screen_top_line or pos >= Top_screen_line_starting_pos then
if Debug_new_render then print('checking to draw', pos, Screen_top1.pos) endif line_index > Screen_top1.line or pos >= Screen_top1.pos then
if line_index == Cursor_line thenif pos <= Cursor_pos and pos + frag_len > Cursor_pos thenText.draw_cursor(x+Text.cursor_x2(frag, Cursor_pos-pos+1), y)
if line_index == Cursor1.line thenif pos <= Cursor1.pos and pos + frag_len > Cursor1.pos thenText.draw_cursor(x+Text.cursor_x2(frag, Cursor1.pos-pos+1), y)
Lines[Cursor_line].data = string.sub(Lines[Cursor_line].data, 1, byte_offset)..t..string.sub(Lines[Cursor_line].data, byte_offset+1)Lines[Cursor_line].fragments = nilCursor_pos = Cursor_pos+1
Lines[Cursor1.line].data = string.sub(Lines[Cursor1.line].data, 1, byte_offset)..t..string.sub(Lines[Cursor1.line].data, byte_offset+1)Lines[Cursor1.line].fragments = nilCursor1.pos = Cursor1.pos+1
local byte_offset = utf8.offset(Lines[Cursor_line].data, Cursor_pos)table.insert(Lines, Cursor_line+1, {mode='text', data=string.sub(Lines[Cursor_line].data, byte_offset)})Lines[Cursor_line].data = string.sub(Lines[Cursor_line].data, 1, byte_offset-1)Lines[Cursor_line].fragments = nilCursor_line = Cursor_line+1Cursor_pos = 1
local byte_offset = utf8.offset(Lines[Cursor1.line].data, Cursor1.pos)table.insert(Lines, Cursor1.line+1, {mode='text', data=string.sub(Lines[Cursor1.line].data, byte_offset)})Lines[Cursor1.line].data = string.sub(Lines[Cursor1.line].data, 1, byte_offset-1)Lines[Cursor1.line].fragments = nilCursor1.line = Cursor1.line+1Cursor1.pos = 1
assert(Lines[Cursor_line].mode == 'text')if Cursor_pos <= utf8.len(Lines[Cursor_line].data) thenCursor_pos = Cursor_pos+1
assert(Lines[Cursor1.line].mode == 'text')if Cursor1.pos <= utf8.len(Lines[Cursor1.line].data) thenCursor1.pos = Cursor1.pos+1
if Cursor_pos > 1 thenlocal byte_start = utf8.offset(Lines[Cursor_line].data, Cursor_pos-1)local byte_end = utf8.offset(Lines[Cursor_line].data, Cursor_pos)
if Cursor1.pos > 1 thenlocal byte_start = utf8.offset(Lines[Cursor1.line].data, Cursor1.pos-1)local byte_end = utf8.offset(Lines[Cursor1.line].data, Cursor1.pos)
Lines[Cursor_line].data = string.sub(Lines[Cursor_line].data, 1, byte_start-1)..string.sub(Lines[Cursor_line].data, byte_end)
Lines[Cursor1.line].data = string.sub(Lines[Cursor1.line].data, 1, byte_start-1)..string.sub(Lines[Cursor1.line].data, byte_end)
Cursor_pos = utf8.len(Lines[Cursor_line-1].data)+1Lines[Cursor_line-1].data = Lines[Cursor_line-1].data..Lines[Cursor_line].dataLines[Cursor_line-1].fragments = niltable.remove(Lines, Cursor_line)
Cursor1.pos = utf8.len(Lines[Cursor1.line-1].data)+1Lines[Cursor1.line-1].data = Lines[Cursor1.line-1].data..Lines[Cursor1.line].dataLines[Cursor1.line-1].fragments = niltable.remove(Lines, Cursor1.line)
if Cursor_pos <= utf8.len(Lines[Cursor_line].data) thenlocal byte_start = utf8.offset(Lines[Cursor_line].data, Cursor_pos)local byte_end = utf8.offset(Lines[Cursor_line].data, Cursor_pos+1)
if Cursor1.pos <= utf8.len(Lines[Cursor1.line].data) thenlocal byte_start = utf8.offset(Lines[Cursor1.line].data, Cursor1.pos)local byte_end = utf8.offset(Lines[Cursor1.line].data, Cursor1.pos+1)
Lines[Cursor_line].data = string.sub(Lines[Cursor_line].data, 1, byte_start-1)..string.sub(Lines[Cursor_line].data, byte_end)
Lines[Cursor1.line].data = string.sub(Lines[Cursor1.line].data, 1, byte_start-1)..string.sub(Lines[Cursor1.line].data, byte_end)
Lines[Cursor_line].data = Lines[Cursor_line].data..Lines[Cursor_line+1].dataLines[Cursor_line].fragments = niltable.remove(Lines, Cursor_line+1)
Lines[Cursor1.line].data = Lines[Cursor1.line].data..Lines[Cursor1.line+1].dataLines[Cursor1.line].fragments = niltable.remove(Lines, Cursor1.line+1)
Cursor_line = new_cursor_lineif Lines[Cursor_line].screen_line_starting_pos == nil thenCursor_pos = Text.nearest_cursor_pos(Lines[Cursor_line].data, Cursor_x)
Cursor1.line = new_cursor_lineif Lines[Cursor1.line].screen_line_starting_pos == nil thenCursor1.pos = Text.nearest_cursor_pos(Lines[Cursor1.line].data, Cursor_x)
if Screen_top_line == Cursor_line and Top_screen_line_starting_pos == screen_line_starting_pos thenTop_screen_line_starting_pos = screen_line_starting_pos--? print('pos of top of screen is also '..tostring(Top_screen_line_starting_pos)..' of the same line')
if Screen_top1.line == Cursor1.line and Screen_top1.pos == screen_line_starting_pos thenScreen_top1.pos = screen_line_starting_pos--? print('pos of top of screen is also '..tostring(Screen_top1.pos)..' of the same line')
local s = string.sub(Lines[Cursor_line].data, screen_line_starting_pos)Cursor_pos = screen_line_starting_pos + Text.nearest_cursor_pos(s, Cursor_x) - 1
local s = string.sub(Lines[Cursor1.line].data, screen_line_starting_pos)Cursor1.pos = screen_line_starting_pos + Text.nearest_cursor_pos(s, Cursor_x) - 1
if Screen_top_line == Cursor_line and Top_screen_line_starting_pos == screen_line_starting_pos thenTop_screen_line_starting_pos = new_screen_line_starting_pos--? print('also setting pos of top of screen to '..tostring(Top_screen_line_starting_pos))
if Screen_top1.line == Cursor1.line and Screen_top1.pos == screen_line_starting_pos thenScreen_top1.pos = new_screen_line_starting_pos--? print('also setting pos of top of screen to '..tostring(Screen_top1.pos))
local s = string.sub(Lines[Cursor_line].data, new_screen_line_starting_pos)Cursor_pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(s, Cursor_x) - 1--? print('cursor pos is now '..tostring(Cursor_pos))
local s = string.sub(Lines[Cursor1.line].data, new_screen_line_starting_pos)Cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(s, Cursor_x) - 1--? print('cursor pos is now '..tostring(Cursor1.pos))
Cursor_line = new_cursor_lineCursor_pos = Text.nearest_cursor_pos(Lines[Cursor_line].data, Cursor_x)--? print(Cursor_pos)
Cursor1.line = new_cursor_lineCursor1.pos = Text.nearest_cursor_pos(Lines[Cursor1.line].data, Cursor_x)--? print(Cursor1.pos)
--? print(Cursor_line, Cursor_pos, Screen_bottom_line)if Cursor_line > Screen_bottom_line then--? print('screen top before:', Screen_top_line, Top_screen_line_starting_pos)Screen_top_line = Cursor_line
--? print(Cursor1.line, Cursor1.pos, Screen_bottom1.line)if Cursor1.line > Screen_bottom1.line then--? print('screen top before:', Screen_top1.line, Screen_top1.pos)Screen_top1.line = Cursor1.line
local s = string.sub(Lines[Cursor_line].data, new_screen_line_starting_pos)Cursor_pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(s, Cursor_x) - 1--? print('cursor pos is now '..tostring(Cursor_pos))Screen_top_line = Cursor_line
local s = string.sub(Lines[Cursor1.line].data, new_screen_line_starting_pos)Cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(s, Cursor_x) - 1--? print('cursor pos is now '..tostring(Cursor1.pos))Screen_top1.line = Cursor1.line
for i=#Lines[Cursor_line].screen_line_starting_pos,1,-1 dolocal spos = Lines[Cursor_line].screen_line_starting_pos[i]if spos <= Cursor_pos then
for i=#Lines[Cursor1.line].screen_line_starting_pos,1,-1 dolocal spos = Lines[Cursor1.line].screen_line_starting_pos[i]if spos <= Cursor1.pos then
i=#Lines[Cursor_line].screen_line_starting_poslocal spos = Lines[Cursor_line].screen_line_starting_pos[i]return spos <= Cursor_pos
i=#Lines[Cursor1.line].screen_line_starting_poslocal spos = Lines[Cursor1.line].screen_line_starting_pos[i]return spos <= Cursor1.pos
if Lines[Screen_top_line-1].mode == 'drawing' thenh = 20 + Drawing.pixels(Lines[Screen_top_line-1].h)elseif Lines[Screen_top_line-1].screen_line_starting_pos == nil then
if Lines[Screen_top1.line-1].mode == 'drawing' thenh = 20 + Drawing.pixels(Lines[Screen_top1.line-1].h)elseif Lines[Screen_top1.line-1].screen_line_starting_pos == nil then
function Text.to2(pos1)local result = {line=pos1.line, screen_line=1}if Line[pos1.line].screen_line_starting_pos == nil thenresult.screen_pos = pos1.poselsefor i=#Lines[pos1.line].screen_line_starting_pos,1,-1 dolocal spos = Lines[pos1.line].screen_line_starting_pos[i]if spos <= Cursor1.pos thenresult.screen_line = iresult.screen_pos = sposbreakendendendassert(result.screen_pos)return resultendfunction Text.to1(pos2)local result = {line=pos2.line, pos=pos2.screen_pos}if pos2.screen_line > 1 thenresult.pos = Lines[pos2.line].screen_line_starting_pos[pos2.screen_line] + pos2.screen_posendreturn resultend
Cursor_line = 1Cursor_pos = 1 -- in Unicode codepoints, from 1 to utf8.len(line) + 1
-- Lines can be too long to fit on screen, in which case they _wrap_ into-- multiple _screen lines_.---- Therefore, any potential location for the cursor can be described in two ways:-- * schema 1: As a combination of line index and position within a line (in utf8 codepoint units)-- * schema 2: As a combination of line index, screen line index within the line, and a position within the screen line.---- Most of the time we'll only persist positions in schema 1, translating to-- schema 2 when that's convenient.Cursor1 = {line=1, pos=1} -- position of cursorScreen_top1 = {line=1, pos=1} -- position of start of screen line at top of screenScreen_bottom1 = {line=1, pos=1} -- position of start of screen line at bottom of screen
-- scrolling supportScreen_top_line = 1Screen_bottom_line = 1Top_screen_line_starting_pos = 1 -- when top of screen starts in between a wrapped line
Screen_top_line = Screen_bottom_lineCursor_line = Screen_top_lineTop_screen_line_starting_pos = 1Cursor_pos = Top_screen_line_starting_pos
Screen_top1.line = Screen_bottom1.lineScreen_top1.pos = 1Cursor1.line = Screen_top1.lineCursor1.pos = Screen_top1.pos
if Lines[Screen_top_line].mode == 'drawing' theny = y - Drawing.pixels(Lines[Screen_top_line].h)
if Lines[Screen_top1.line].mode == 'drawing' theny = y - Drawing.pixels(Lines[Screen_top1.line].h)