resolve conflicts
Dependencies
- [2]
44BTGR7UMerge lines.love - [3]
WB6SIB7HMerge lines.love - [4]
7YGYHOEOMerge lines.love - [5]
TBTRYEBPMerge lines.love - [6]
G3DLS5OUaudit all asserts - [7]
6K5PFF6Xhelper: trimming whitespace from strings - [8]
4EGQRXDAbugfix: naming points - [9]
W6XUYQKPsource: show files in MRU order - [10]
MGJZHZC4Merge lines.love - [11]
2XLZCWZCbugfix: rectangles and squares are now saved - [12]
WJBZZQE4fold together two largely similar cases - [13]
NFI42KGXmore correct absolute path detection - [14]
2344TV56Merge lines.love - [15]
ED4Z6ORCcleaner API for file-system access - [16]
KWIVKQQ7Merge lines.love - [17]
QYIFOHW3first test! - [18]
3PSFWAILMerge lines.love - [19]
AIBA4RWQhide cursor in log browser window - [20]
BYG5CEMVsupport for naming points - [21]
ZTK4QTZTextract a couple of functions - [22]
MDXGMZU2disable all debug prints - [23]
ONHKBLLCMerge lines.love - [24]
IFTYOERMline.y -> line_cache.starty in a few more places - [25]
ZLJYLPOTMerge lines.love - [26]
62PZGSUCoptimization: moving cursor to next word - [27]
KECEMMMRextract couple of functions - [28]
4KC7I3E2make colors easier to edit - [29]
ATQO62TFMerge lines.love - [30]
R3KXFRZNget rid of to_text - [31]
MD3W5IRAnew fork: rip out drawing support - [32]
2WGRQI5Emissing shape modes in a couple more places - [33]
K2X6G75Zstart writing some tests for drawings - [34]
2CK5QI7Wmake love event names consistent - [35]
LSYLEVBDdrop some redundant args when clearing the cache - [36]
65HNIAOSmake freehand drawings smoother - [37]
3HVBAZPAadd state arg to a few functions - [38]
KKMFQDR4editing source code from within the app - [39]
LDFXFRUObring a few things in sync between run and source - [40]
C3NYQP57Merge lines.love - [41]
OGUV4HSAremove some memory leaks from rendered fragments - [42]
LWPFEZBIMerge lines.love - [43]
ORKN6EOBMerge lines.love - [44]
VHUNJHXBMerge lines.love - [45]
MUJTM6REbring back a level of wrapping - [46]
KTZQ57HVreplace globals with args in a few functions - [47]
KKHSOUW4bugfix: drawings in source editor - [48]
TGZAJUEFbring back a set of constants - [49]
OI4FPFINsupport drawings in the source editor - [50]
TLOAPLBJadd a license - [51]
VJ77YABHmore efficient undo/redo - [52]
RT6EV6OPdelegate update events to drawings - [53]
242L3OQXbugfix: ensure Cursor_line is always on a text line - [54]
BJ5X5O4Alet's prevent the text cursor from ever getting on a drawing - [55]
ILOA5BYFseparate data structure for each line's cache data - [56]
CE4LZV4Tdrop last couple of manual tests - [57]
3OTESDW6move drawing.starty into line cache - [58]
F65ADDGLadd state arg to a few functions - [59]
4VQGE7RAnew test - [60]
LK4ZW4BBbugfix - [61]
2TQUKHBCMerge lines.love - [62]
BULPIBEGbeginnings of a module for the text editor - [63]
QCPXQ2E3add state arg to a few functions - [64]
G54H3YG2get rid of all bifold text - [65]
5SM6DRHKport inscript's bugfix to source editor - [66]
LIKTH6HMupdate stale source X-( - [67]
JDZVBFEIMerge lines.love - [68]
KOYAJWE4extract a couple more methods - [69]
YXQOITYSMerge lines.love - [70]
KV7GGVERcouple of accidental globals - [71]
MXSAHZN4Merge lines.love - [72]
4YDBYBA4clean up memory leak experiments - [73]
2LEXWUW3get rid of some ridiculous code - [74]
O7QH4N4Wspeeding up copy, attempt 1 - [75]
KMSL74GAsupport selections in the source editor - [76]
VDJSUX2Qtypos - [77]
S2MISTTMadd state arg to a few functions - [78]
R5QXEHUIsomebody stop me - [79]
CNCYMM6Amake test initializations a little more obvious - [80]
VP5KC4XZMerge lines.love - [81]
BLWAYPKVextract a module - [82]
GGWAHCLEminor cleanup and a todo for later - [83]
JOPVPUSAediting source code from within the app - [84]
TVCPXAAUrename - [85]
YF2ATH2QMerge lines.love - [86]
5BMR5HRTclick to the left of a line - [87]
PLKNHYZ4extract a function - [88]
KYNGDE2Cconsistent names in a few more places - [89]
DLQAEAC7add state arg to Drawing.mouse_pressed - [90]
BJ2C6F2Bignore 'name' mode in a few places - [91]
LXTTOB33extract a couple of files - [92]
3OKKTUT4up and down arrow now moving by screen line where possible - [93]
REAIVN7WMerge lines.love - [94]
ISOFHXB2App.width can no longer take a Text - [95]
EHSUSZMKmore idiomatic variable names - [96]
D4B52CQ2Merge lines.love - [97]
AYX33NBCMerge lines.love - [98]
RU4HIK43Merge lines.love - [99]
B4JEWKWIhide editor cursor while in file navigator - [100]
VDFARWQXremove some support for long lines from source editor - [101]
YCDYGEZUinclude drawing index in a few places - [102]
VXORMHMEdelete experimental REPL - [103]
SGMA5JLEsave the list of tests in repo - [104]
T4FRZSYLdelete an ancient, unused file - [105]
YTSPVDZHfirst successful pagedown test, first bug found by test - [106]
LF7BWEG4group all editor globals - [107]
3XNFQDDNMerge lines.love - [108]
NYQ7HD4Dmove - [109]
4CTZOJPCstop pretending globals are local - [110]
HYEAFRZ2split mouse_pressed events between Text and Drawing - [111]
OTIBCAUJlove2d scaffold - [112]
2RXZ3PGObeginning of a new approach to scroll+wrap - [113]
AVTNUQYRbasic test-enabled framework - [114]
TGHAJBESuse line cache for drawings as well - [115]
N2NUGNN4include a brief reference enabling many useful apps - [116]
MU2HIRR6Merge lines.love - [117]
2ZYV7D3Whandle tab characters - [118]
Z5HLXU4Padd state arg to a few functions - [119]
3QNOKBFMbeginnings of a test harness - [120]
ZPUQSPQPextract a few methods - [121]
PXSQR2ADhide line numbers from log browser - [122]
6DE7RBZ6move mouse_released events to Drawing - [123]
66X36NZNa little more prose describing manual_tests - [124]
4SR3Z4Y3document the version of LÖVE I've been using - [125]
2K2YDMFHignore 'deleted' shapes when saving to disk - [126]
TRNWIQN6more precise height calculation when scrolling up as much as possible while keeping cursor on screen - [127]
RSZD5A7Gforgot to add json.lua - [128]
WTDKUACNrectangle and square shapes - [129]
VBU5YHLRMerge lines.love - [130]
UZVWYRTYmissing temporary modes in a couple more places - [131]
SRVDX4I5local var - [132]
6LJZN727handle chords - [133]
GZ5WULJVswitch source side to new screen-line-based render - [134]
FS2ITYYHrecord a known issue - [135]
MXA3RZYKdeduce left/right from state where possible - [136]
LNUHQOGHstart passing in Editor_state explicitly - [137]
CWQIPU7Ualways show line numbers in source editor - [138]
SR4C3ZYZadd an assert - [139]
2CTN2IEFMerge lines.love - [140]
XX7G2FFJintermingle freehand line drawings with text - [141]
4PHGNJN6assume starty can be nil in update - [142]
F63Q4OV7several bugfixes - [143]
6VJTQKW7start supporting LÖVE v12 - [144]
TACI4LU6Merge lines.love - [145]
XNFTJHC4split keyboard handling between Text and Drawing - [146]
2L5MEZV3experiment: new edit namespace - [147]
ELJNEPW2simplify cursor-on-screen check - [148]
NQM25OZVreduce use of rfind - [149]
VHQCNMARseveral more modules - [150]
34BZ5ZKNMerge lines.love - [151]
PP2IIHL6stop putting button state in a global - [152]
B4FAIVRAMerge lines.love - [153]
D2GCFTTTclean up repl functionality - [154]
V3EABA35skip multiple consecutive whitespace - [155]
R2ASHK5Cfix a bad merge - [156]
73OCE2MCafter much struggle, a brute-force undo
Change contents
- file deletion: geom.lua
geom = {}function geom.on_shape(x,y, drawing, shape)if shape.mode == 'freehand' thenreturn geom.on_freehand(x,y, drawing, shape)elseif shape.mode == 'line' thenreturn geom.on_line(x,y, drawing, shape)elseif shape.mode == 'manhattan' thenlocal p1 = drawing.points[shape.p1]local p2 = drawing.points[shape.p2]if p1.x == p2.x thenif x ~= p1.x then return false endlocal y1,y2 = p1.y, p2.yif y1 > y2 theny1,y2 = y2,y1endreturn y >= y1-2 and y <= y2+2elseif p1.y == p2.y thenif y ~= p1.y then return false endlocal x1,x2 = p1.x, p2.xif x1 > x2 thenx1,x2 = x2,x1endreturn x >= x1-2 and x <= x2+2endelseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' thenreturn geom.on_polygon(x,y, drawing, shape)elseif shape.mode == 'circle' thenlocal center = drawing.points[shape.center]local dist = geom.dist(center.x,center.y, x,y)return dist > shape.radius*0.95 and dist < shape.radius*1.05elseif shape.mode == 'arc' thenlocal center = drawing.points[shape.center]local dist = geom.dist(center.x,center.y, x,y)if dist < shape.radius*0.95 or dist > shape.radius*1.05 thenreturn falseendreturn geom.angle_between(center.x,center.y, x,y, shape.start_angle,shape.end_angle)elseif shape.mode == 'deleted' thenelseprint(shape.mode)assert(false)endendfunction geom.on_freehand(x,y, drawing, shape)local prevfor _,p in ipairs(shape.points) doif prev thenif geom.on_line(x,y, drawing, {p1=prev, p2=p}) thenreturn trueendendprev = pendreturn falseendfunction geom.on_line(x,y, drawing, shape)local p1,p2if type(shape.p1) == 'number' thenp1 = drawing.points[shape.p1]p2 = drawing.points[shape.p2]elsep1 = shape.p1p2 = shape.p2endif p1.x == p2.x thenif math.abs(p1.x-x) > 2 thenreturn falseendlocal y1,y2 = p1.y,p2.yif y1 > y2 theny1,y2 = y2,y1endreturn y >= y1-2 and y <= y2+2end-- has the right slope and interceptlocal m = (p2.y - p1.y) / (p2.x - p1.x)local yp = p1.y + m*(x-p1.x)if yp < y-2 or yp > y+2 thenreturn falseend-- between endpointslocal k = (x-p1.x) / (p2.x-p1.x)return k > -0.005 and k < 1.005endfunction geom.on_polygon(x,y, drawing, shape)local prevfor _,p in ipairs(shape.vertices) doif prev thenif geom.on_line(x,y, drawing, {p1=prev, p2=p}) thenreturn trueendendprev = pendreturn geom.on_line(x,y, drawing, {p1=shape.vertices[1], p2=shape.vertices[#shape.vertices]})end-- are (x3,y3) and (x4,y4) on the same side of the line between (x1,y1) and (x2,y2)function geom.same_side(x1,y1, x2,y2, x3,y3, x4,y4)if x1 == x2 thenreturn math.sign(x3-x1) == math.sign(x4-x1)endif y1 == y2 thenreturn math.sign(y3-y1) == math.sign(y4-y1)endlocal m = (y2-y1)/(x2-x1)return math.sign(m*(x3-x1) + y1-y3) == math.sign(m*(x4-x1) + y1-y4)endfunction math.sign(x)if x > 0 thenreturn 1elseif x == 0 thenreturn 0elseif x < 0 thenreturn -1endendfunction geom.angle_with_hint(x1, y1, x2, y2, hint)local result = geom.angle(x1,y1, x2,y2)if hint then-- Smooth the discontinuity where angle goes from positive to negative.-- The hint is a memory of which way we drew it last time.while result > hint+math.pi/10 doresult = result-math.pi*2endwhile result < hint-math.pi/10 doresult = result+math.pi*2endendreturn resultend-- result is from -π/2 to 3π/2, approximately adding math.atan2 from Lua 5.3-- (LÖVE is Lua 5.1)function geom.angle(x1,y1, x2,y2)local result = math.atan((y2-y1)/(x2-x1))if x2 < x1 thenresult = result+math.piendreturn resultend-- is the line between x,y and cx,cy at an angle between s and e?function geom.angle_between(ox,oy, x,y, s,e)local angle = geom.angle(ox,oy, x,y)if s > e thens,e = e,send-- I'm not sure this is right or ideal..angle = angle-math.pi*2if s <= angle and angle <= e thenreturn trueendangle = angle+math.pi*2if s <= angle and angle <= e thenreturn trueendangle = angle+math.pi*2return s <= angle and angle <= eendfunction geom.dist(x1,y1, x2,y2) return ((x2-x1)^2+(y2-y1)^2)^0.5 end - file deletion: source_undo.lua source_undo.lua
assert(s, 'failed to snapshot operation for undo history')assert(#State.lines > 0, 'failed to snapshot operation for undo history')assert(false, ('unknown line mode %s'):format(line.mode))assert(from.start_line == to.start_line, 'failed to patch undo operation')assert(#to.lines == to.end_line-to.start_line+1, 'failed to patch undo operation')assert(from.start_line == to.start_line, 'failed to patch undo operation')assert(#to.lines == to.end_line-to.start_line+1, 'failed to patch undo operation')for i=1,#to.lines dotable.insert(line_cache, to.start_line+i-1, {})endend-- https://stackoverflow.com/questions/640642/how-do-you-copy-a-lua-table-by-value/26367080#26367080function deepcopy(obj, seen)if type(obj) ~= 'table' then return obj endif seen and seen[obj] then return seen[obj] endlocal s = seen or {}local result = setmetatable({}, getmetatable(obj))s[obj] = resultfor k,v in pairs(obj) doresult[deepcopy(k, s)] = deepcopy(v, s)endreturn resultendfunction minmax(a, b)return math.min(a,b), math.max(a,b)endfor i=from.end_line,from.start_line,-1 dotable.remove(line_cache, i)endfor i=1,#to.lines dotable.insert(lines, to.start_line+i-1, to.lines[i])endendfunction patch_placeholders(line_cache, from, to)for i=from.end_line,from.start_line,-1 dotable.remove(lines, i)endendif s < 1 then s = 1 endif s > #State.lines then s = #State.lines endif e < 1 then e = 1 endif e > #State.lines then e = #State.lines end-- compare with App.initialize_globalslocal event = {screen_top=deepcopy(State.screen_top1),selection=deepcopy(State.selection1),cursor=deepcopy(State.cursor1),if e == nil thene = send - file deletion: source_text.lua source_text.lua
assert(#line_cache.screen_line_starting_pos >= 1, 'line cache missing screen line info')assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')assert(Text.le1(State.screen_top1, State.cursor1), ('screen_top (line=%d,pos=%d) is below cursor (line=%d,pos=%d)'):format(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos))assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')assert(screen_line_index > 1, 'bumped up against top screen line in line')assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')assert(State.cursor1.pos, 'cursor has no pos')assert(State.cursor1.pos > 1, 'bumped up against start of line')assert(end_offset > start_offset, ('end_offset %d not > start_offset %d'):format(end_offset, start_offset))assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')assert(false, ('invalid pos %d'):format(loc1.pos))assert(false, ('invalid pos %d'):format(loc1.pos))assert(State.cursor1.line == #State.lines+1, 'tried to ensure bottom line of file is text, but failed')assert(top2.line > 1, 'tried to snap cursor to buttom of screen but failed')assert(State.lines[top2.line-1].mode == 'drawing', "expected a drawing but it's not")assert(my >= line_cache.starty, 'failed to map y pixel to line')assert(false, 'failed to map y pixel to line')assert(false, 'failed to map x pixel to pos')assert(false, 'failed to map x pixel to pos')assert(result.screen_pos, 'failed to convert schema-1 coordinate to schema-2')assert(result, "Text.offset returned nil; this is likely a failure to handle utf8")return resultendfunction Text.previous_screen_line(State, loc2)return resultendfunction Text.to1(State, loc2)endfunction Text.x_after(s, pos)local offset = Text.offset(s, math.min(pos+1, #s+1))local s_before = s:sub(1, offset-1)--? print('^'..s_before..'$')end-- return the nearest index of line (in utf8 code points) which lies entirely-- within x pixels of the left margin-- result: 0 to len+1function Text.nearest_pos_less_than(line, x)--? print('', '-- nearest_pos_less_than', line, x)local len = utf8.len(line)local max_x = Text.x_after(line, len)if x > max_x thenreturn len+1endlocal left, right = 0, len+1while true dolocal curr = math.floor((left+right)/2)local currxmin = Text.x_after(line, curr+1)local currxmax = Text.x_after(line, curr+2)--? print('', x, left, right, curr, currxmin, currxmax)if currxmin <= x and x < currxmax thenreturn currendif left >= right-1 thenreturn leftendif currxmin > x thenright = currelseleft = currendendendfunction Text.screen_line_width(State, line_index, i)local line = State.lines[line_index]local line_cache = State.line_cache[line_index]local start_pos = line_cache.screen_line_starting_pos[i]local start_offset = Text.offset(line.data, start_pos)local screen_lineif i < #line_cache.screen_line_starting_pos thenlocal past_end_pos = line_cache.screen_line_starting_pos[i+1]local past_end_offset = Text.offset(line.data, past_end_pos)screen_line = string.sub(line.data, start_offset, past_end_offset-1)elsescreen_line = string.sub(line.data, start_pos)end-- duplicate some logic from Text.drawlocal y = line_cache.starty-- We currently can't draw partial drawings, so either skip it entirely-- or not at all.local h = Drawing_padding_height + Drawing.pixels(State.lines[top2.line-1].h, State.width)if y - h < State.top thenbreakend--? print('skipping drawing of height', h)y = y - htable.insert(State.lines, {mode='text', data=''})table.insert(State.line_cache, {})end--? print(y, App.screen.height, App.screen.height-State.line_height)if y > App.screen.height - State.line_height thenendendif State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) thenState.cursor1.pos = State.cursor1.pos+1if State.cursor1.pos > 1 thenState.cursor1.pos = State.cursor1.pos-1local curr = s:sub(start_offset, end_offset-1)return curr:match(pat)endfunction Text.left(State)if Text.match(State.lines[State.cursor1.line].data, State.cursor1.pos-1, '%s') thenif Text.cursor_at_final_screen_line(State) then-- line is done, skip to next text line--? print('cursor at final screen line of its line')--? print('down', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)local new_screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos[screen_line_index-1]local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos)local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset)State.cursor1.pos = new_screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1--? print('cursor pos is now '..tostring(State.cursor1.pos))endif Text.lt1(State.cursor1, State.screen_top1) then--? print('up', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos)local screen_line_starting_pos, screen_line_index = Text.pos_at_start_of_screen_line(State, State.cursor1)if screen_line_starting_pos == 1 then--? print('cursor is at first screen line of its line')-- line is done; skip to previous text lineschedule_save(State)record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)})elseif chord == 'delete' thenlocal byte_offset = Text.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_offset-1)..t..string.sub(State.lines[State.cursor1.line].data, byte_offset)Text.clear_screen_line_cache(State, State.cursor1.line)State.cursor1.pos = State.cursor1.pos+1for i=1,#line_cache.screen_line_starting_pos dolocal pos = line_cache.screen_line_starting_pos[i] - file deletion: source_file.lua source_file.lua
assert(line, 'drawing in file is incomplete')assert(false, ('unknown drawing mode %s'):format(shape.mode))assert(false, ('unknown drawing mode %s'):format(shape.mode))assert(i, 'drawing in array is incomplete')assert(false, ('unknown drawing mode %s'):format(shape.mode))endtable.insert(drawing.shapes, shape)endreturn i, drawingend--? print(i)if line == '```' then break endlocal shape = json.decode(line)if shape.mode == 'freehand' then-- no changes neededelseif shape.mode == 'line' or shape.mode == 'manhattan' thenlocal name = shape.p1.nameshape.p1 = Drawing.find_or_insert_point(drawing.points, shape.p1.x, shape.p1.y, --[[large width to minimize overlap]] 1600)drawing.points[shape.p1].name = namename = shape.p2.nameshape.p2 = Drawing.find_or_insert_point(drawing.points, shape.p2.x, shape.p2.y, --[[large width to minimize overlap]] 1600)drawing.points[shape.p2].name = nameelseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' thenfor i,p in ipairs(shape.vertices) dolocal name = p.nameshape.vertices[i] = Drawing.find_or_insert_point(drawing.points, p.x,p.y, --[[large width to minimize overlap]] 1600)drawing.points[shape.vertices[i]].name = nameendelseif shape.mode == 'circle' or shape.mode == 'arc' thenlocal name = shape.center.nameshape.center = Drawing.find_or_insert_point(drawing.points, shape.center.x,shape.center.y, --[[large width to minimize overlap]] 1600)drawing.points[shape.center].name = nameelseif shape.mode == 'deleted' then-- ignoreelseendendoutfile:write('```\n')endtable.insert(drawing.shapes, shape)endreturn drawingendfunction store_drawing(outfile, drawing)outfile:write('```lines\n')for _,shape in ipairs(drawing.shapes) doif shape.mode == 'freehand' thenif line == '```' then break endlocal shape = json.decode(line)if shape.mode == 'freehand' then-- no changes neededelseif shape.mode == 'line' or shape.mode == 'manhattan' thenlocal name = shape.p1.nameshape.p1 = Drawing.find_or_insert_point(drawing.points, shape.p1.x, shape.p1.y, --[[large width to minimize overlap]] 1600)drawing.points[shape.p1].name = namename = shape.p2.nameshape.p2 = Drawing.find_or_insert_point(drawing.points, shape.p2.x, shape.p2.y, --[[large width to minimize overlap]] 1600)drawing.points[shape.p2].name = nameelseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' thenfor i,p in ipairs(shape.vertices) dolocal name = p.nameshape.vertices[i] = Drawing.find_or_insert_point(drawing.points, p.x,p.y, --[[large width to minimize overlap]] 1600)drawing.points[shape.vertices[i]].name = nameendelseif shape.mode == 'circle' or shape.mode == 'arc' thenlocal name = shape.center.nameshape.center = Drawing.find_or_insert_point(drawing.points, shape.center.x,shape.center.y, --[[large width to minimize overlap]] 1600)drawing.points[shape.center].name = nameelseif shape.mode == 'deleted' then-- ignoreelse - file deletion: source_edit.lua source_edit.lua
assert(#State.lines == #State.line_cache, ('line_cache is out of date; %d elements when it should be %d'):format(#State.line_cache, #State.lines))assert(Text.le1(State.screen_top1, State.cursor1), ('screen_top (line=%d,pos=%d) is below cursor (line=%d,pos=%d)'):format(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos))assert(false, ('unknown line mode %s'):format(line.mode))endState.cursor_x = nilState.cursor_y = nillocal y = State.top - file deletion: log_browser.lua log_browser.lua
assert(#State.lines == #State.line_cache, ('line_cache is out of date; %d elements when it should be %d'):format(#State.line_cache, #State.lines))else assert(line.section_end, "log line has a section name, but it's neither the start nor end of a section")local sectiony = y+State.line_height-Section_border_padding_verticallove.graphics.line(xleft,y, xleft,sectiony)love.graphics.line(xright,y, xright,sectiony)love.graphics.line(xleft,sectiony, xleft+50-2,sectiony)local mouse_line_index = log_browser.line_index(State, App.mouse_x(), App.mouse_y())local y = State.topfor line_index = State.screen_top1.line,#State.lines doApp.color(Text_color)local line = State.lines[line_index]if y + State.line_height > App.screen.height then break endlocal height = State.line_heightif should_show(line) thenlocal xleft = render_stack_left_margin(State, line_index, line, y)local xright = render_stack_right_margin(State, line_index, line, y)if line.section_name thenApp.color(Section_border_color) - file deletion: commands.lua commands.lua
assert(index, 'file missing from manifest')table.remove(File_navigation.all_candidates, index)table.insert(File_navigation.all_candidates, 1, s) - edit in undo.lua at line 62
assert(false, ('unknown line mode %s'):format(line.mode)) - resolve order conflict in undo.lua at line 62
- edit in text.lua at line 380
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text') - resurrect zombie in text.lua at line 401
local new_screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos[screen_line_index-1] - edit in text.lua at line 401
assert(screen_line_index > 1, 'bumped up against top screen line in line') - edit in text.lua at line 405
assert(screen_line_index > 1, 'bumped up against top screen line in line') - resolve order conflict in text.lua at line 405
- edit in text.lua at line 418
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text') - resurrect zombie in text.lua at line 487
if Text.match(State.lines[State.cursor1.line].data, State.cursor1.pos-1, '%s') then - edit in text.lua at line 487
assert(State.cursor1.pos > 1, 'bumped up against start of line') - edit in text.lua at line 489
assert(State.cursor1.pos > 1, 'bumped up against start of line') - resolve order conflict in text.lua at line 489
- edit in text.lua at line 528
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text') - edit in text.lua at line 552
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text') - resolve order conflict in text.lua at line 552
- edit in text.lua at line 595
assert(State.cursor1.line == #State.lines+1, 'tried to ensure bottom line of file is text, but failed') - resolve order conflict in text.lua at line 595
- edit in text.lua at line 617
assert(top2.line > 1, 'tried to snap cursor to buttom of screen but failed')assert(State.lines[top2.line-1].mode == 'drawing', "expected a drawing but it's not") - resolve order conflict in text.lua at line 617
- edit in text.lua at line 942
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text') - resolve order conflict in text.lua at line 942[7.442]
- replacement in source_undo.lua at line 39
assert(s)assert(s, 'failed to snapshot operation for undo history') - replacement in source_undo.lua at line 43
assert(#State.lines > 0)assert(#State.lines > 0, 'failed to snapshot operation for undo history') - replacement in source_undo.lua at line 68
print(line.mode)assert(false)assert(false, ('unknown line mode %s'):format(line.mode)) - replacement in source_undo.lua at line 82
assert(from.start_line == to.start_line)assert(from.start_line == to.start_line, 'failed to patch undo operation') - replacement in source_undo.lua at line 86
assert(#to.lines == to.end_line-to.start_line+1)assert(#to.lines == to.end_line-to.start_line+1, 'failed to patch undo operation') - replacement in source_undo.lua at line 93
assert(from.start_line == to.start_line)assert(from.start_line == to.start_line, 'failed to patch undo operation') - replacement in source_undo.lua at line 97
assert(#to.lines == to.end_line-to.start_line+1)assert(#to.lines == to.end_line-to.start_line+1, 'failed to patch undo operation') - replacement in source_text.lua at line 20
assert(#line_cache.screen_line_starting_pos >= 1)assert(#line_cache.screen_line_starting_pos >= 1, 'line cache missing screen line info') - replacement in source_text.lua at line 212
assert(State.lines[State.cursor1.line].mode == 'text')assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text') - replacement in source_text.lua at line 289
assert(Text.le1(State.screen_top1, State.cursor1))assert(Text.le1(State.screen_top1, State.cursor1), ('screen_top (line=%d,pos=%d) is below cursor (line=%d,pos=%d)'):format(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)) - replacement in source_text.lua at line 455
assert(State.lines[State.cursor1.line].mode == 'text')assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text') - replacement in source_text.lua at line 481
assert(screen_line_index > 1)assert(screen_line_index > 1, 'bumped up against top screen line in line') - replacement in source_text.lua at line 498
assert(State.lines[State.cursor1.line].mode == 'text')assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text') - replacement in source_text.lua at line 500
assert(State.cursor1.pos)assert(State.cursor1.pos, 'cursor has no pos') - replacement in source_text.lua at line 574
assert(State.cursor1.pos > 1)assert(State.cursor1.pos > 1, 'bumped up against start of line') - edit in source_text.lua at line 608
assert(start_offset) - replacement in source_text.lua at line 609
assert(end_offset > start_offset)assert(end_offset > start_offset, ('end_offset %d not > start_offset %d'):format(end_offset, start_offset)) - replacement in source_text.lua at line 615
assert(State.lines[State.cursor1.line].mode == 'text')assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text') - replacement in source_text.lua at line 648
assert(State.lines[State.cursor1.line].mode == 'text')assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text') - replacement in source_text.lua at line 673
assert(false)assert(false, ('invalid pos %d'):format(loc1.pos)) - replacement in source_text.lua at line 687
assert(false)assert(false, ('invalid pos %d'):format(loc1.pos)) - replacement in source_text.lua at line 712
assert(State.cursor1.line == #State.lines+1)assert(State.cursor1.line == #State.lines+1, 'tried to ensure bottom line of file is text, but failed') - replacement in source_text.lua at line 744
assert(top2.line > 1)assert(State.lines[top2.line-1].mode == 'drawing')assert(top2.line > 1, 'tried to snap cursor to buttom of screen but failed')assert(State.lines[top2.line-1].mode == 'drawing', "expected a drawing but it's not") - replacement in source_text.lua at line 777
assert(my >= line_cache.starty)assert(my >= line_cache.starty, 'failed to map y pixel to line') - replacement in source_text.lua at line 800
assert(false)assert(false, 'failed to map y pixel to line') - replacement in source_text.lua at line 866
assert(false)assert(false, 'failed to map x pixel to pos') - replacement in source_text.lua at line 897
assert(false)assert(false, 'failed to map x pixel to pos') - replacement in source_text.lua at line 928
assert(result.screen_pos)assert(result.screen_pos, 'failed to convert schema-1 coordinate to schema-2') - replacement in source_text.lua at line 970
assert(result)assert(result, "Text.offset returned nil; this is likely a failure to handle utf8") - replacement in source_file.lua at line 66
assert(line)assert(line, 'drawing in file is incomplete') - replacement in source_file.lua at line 91
print(shape.mode)assert(false)assert(false, ('unknown drawing mode %s'):format(shape.mode)) - replacement in source_file.lua at line 125
print(shape.mode)assert(false)assert(false, ('unknown drawing mode %s'):format(shape.mode)) - replacement in source_file.lua at line 163
assert(i)assert(i, 'drawing in array is incomplete') - replacement in source_file.lua at line 189
print(shape.mode)assert(false)assert(false, ('unknown drawing mode %s'):format(shape.mode)) - replacement in source_edit.lua at line 161[7.155492]→[7.4077:4251](∅→∅),[7.4251]→[7.155536:155593](∅→∅),[7.155536]→[7.155536:155593](∅→∅),[7.155593]→[7.8399:8495](∅→∅),[7.8495]→[7.155733:155757](∅→∅),[7.155733]→[7.155733:155757](∅→∅)
if #State.lines ~= #State.line_cache thenprint(('line_cache is out of date; %d when it should be %d'):format(#State.line_cache, #State.lines))assert(false)endif not Text.le1(State.screen_top1, State.cursor1) thenprint(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)assert(false)endassert(#State.lines == #State.line_cache, ('line_cache is out of date; %d elements when it should be %d'):format(#State.line_cache, #State.lines))assert(Text.le1(State.screen_top1, State.cursor1), ('screen_top (line=%d,pos=%d) is below cursor (line=%d,pos=%d)'):format(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)) - replacement in source_edit.lua at line 206
print(line.mode)assert(false)assert(false, ('unknown line mode %s'):format(line.mode)) - resurrect zombie in select.lua at line 128
local rhs = State.lines[maxl].data:sub(max_offset) - edit in select.lua at line 128
assert(minl < maxl, ('minl %d not < maxl %d'):format(minl, maxl)) - edit in select.lua at line 130
assert(minl < maxl, ('minl %d not < maxl %d'):format(minl, maxl)) - resolve order conflict in select.lua at line 130
- resurrect zombie in select.lua at line 155
local result = {State.lines[minl].data:sub(min_offset)} - edit in select.lua at line 155
assert(minl < maxl, ('minl %d not < maxl %d'):format(minl, maxl)) - edit in select.lua at line 157
assert(minl < maxl, ('minl %d not < maxl %d'):format(minl, maxl)) - resolve order conflict in select.lua at line 157
- replacement in log_browser.lua at line 78
assert(#State.lines == #State.line_cache)assert(#State.lines == #State.line_cache, ('line_cache is out of date; %d elements when it should be %d'):format(#State.line_cache, #State.lines)) - replacement in log_browser.lua at line 98
else assert(line.section_end)else assert(line.section_end, "log line has a section name, but it's neither the start nor end of a section") - file un-deletion: geom.lua geom.lua
- resurrect zombie in geom.lua at line 1
assert(false, ('unknown drawing mode %s'):format(shape.mode)) - edit in geom.lua at line 1
geom = {}function geom.on_shape(x,y, drawing, shape)if shape.mode == 'freehand' thenreturn geom.on_freehand(x,y, drawing, shape)elseif shape.mode == 'line' thenreturn geom.on_line(x,y, drawing, shape)elseif shape.mode == 'manhattan' thenlocal p1 = drawing.points[shape.p1]local p2 = drawing.points[shape.p2]if p1.x == p2.x thenif x ~= p1.x then return false endlocal y1,y2 = p1.y, p2.yif y1 > y2 theny1,y2 = y2,y1endreturn y >= y1-2 and y <= y2+2elseif p1.y == p2.y thenif y ~= p1.y then return false endlocal x1,x2 = p1.x, p2.xif x1 > x2 thenx1,x2 = x2,x1endreturn x >= x1-2 and x <= x2+2endelseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' thenreturn geom.on_polygon(x,y, drawing, shape)elseif shape.mode == 'circle' thenlocal center = drawing.points[shape.center]local dist = geom.dist(center.x,center.y, x,y)return dist > shape.radius*0.95 and dist < shape.radius*1.05elseif shape.mode == 'arc' thenlocal center = drawing.points[shape.center]local dist = geom.dist(center.x,center.y, x,y)if dist < shape.radius*0.95 or dist > shape.radius*1.05 thenreturn falseendreturn geom.angle_between(center.x,center.y, x,y, shape.start_angle,shape.end_angle)elseif shape.mode == 'deleted' thenelse - edit in geom.lua at line 42[6.6750]
endendfunction geom.on_freehand(x,y, drawing, shape)local prevfor _,p in ipairs(shape.points) doif prev thenif geom.on_line(x,y, drawing, {p1=prev, p2=p}) thenreturn trueendendprev = pendreturn falseendfunction geom.on_line(x,y, drawing, shape)local p1,p2if type(shape.p1) == 'number' thenp1 = drawing.points[shape.p1]p2 = drawing.points[shape.p2]elsep1 = shape.p1p2 = shape.p2endif p1.x == p2.x thenif math.abs(p1.x-x) > 2 thenreturn falseendlocal y1,y2 = p1.y,p2.yif y1 > y2 theny1,y2 = y2,y1endreturn y >= y1-2 and y <= y2+2end-- has the right slope and interceptlocal m = (p2.y - p1.y) / (p2.x - p1.x)local yp = p1.y + m*(x-p1.x)if yp < y-2 or yp > y+2 thenreturn falseend-- between endpointslocal k = (x-p1.x) / (p2.x-p1.x)return k > -0.005 and k < 1.005endfunction geom.on_polygon(x,y, drawing, shape)local prevfor _,p in ipairs(shape.vertices) doif prev thenif geom.on_line(x,y, drawing, {p1=prev, p2=p}) thenreturn trueendendprev = pendreturn geom.on_line(x,y, drawing, {p1=shape.vertices[1], p2=shape.vertices[#shape.vertices]})end-- are (x3,y3) and (x4,y4) on the same side of the line between (x1,y1) and (x2,y2)function geom.same_side(x1,y1, x2,y2, x3,y3, x4,y4)if x1 == x2 thenreturn math.sign(x3-x1) == math.sign(x4-x1)endif y1 == y2 thenreturn math.sign(y3-y1) == math.sign(y4-y1)endlocal m = (y2-y1)/(x2-x1)return math.sign(m*(x3-x1) + y1-y3) == math.sign(m*(x4-x1) + y1-y4)endfunction math.sign(x)if x > 0 thenreturn 1elseif x == 0 thenreturn 0elseif x < 0 thenreturn -1endendfunction geom.angle_with_hint(x1, y1, x2, y2, hint)local result = geom.angle(x1,y1, x2,y2)if hint then-- Smooth the discontinuity where angle goes from positive to negative.-- The hint is a memory of which way we drew it last time.while result > hint+math.pi/10 doresult = result-math.pi*2endwhile result < hint-math.pi/10 doresult = result+math.pi*2endendreturn resultend-- result is from -π/2 to 3π/2, approximately adding math.atan2 from Lua 5.3-- (LÖVE is Lua 5.1)function geom.angle(x1,y1, x2,y2)local result = math.atan((y2-y1)/(x2-x1))if x2 < x1 thenresult = result+math.piendreturn resultend-- is the line between x,y and cx,cy at an angle between s and e?function geom.angle_between(ox,oy, x,y, s,e)local angle = geom.angle(ox,oy, x,y)if s > e thens,e = e,send-- I'm not sure this is right or ideal..angle = angle-math.pi*2if s <= angle and angle <= e thenreturn trueendangle = angle+math.pi*2if s <= angle and angle <= e thenreturn trueendangle = angle+math.pi*2return s <= angle and angle <= eendfunction geom.dist(x1,y1, x2,y2) return ((x2-x1)^2+(y2-y1)^2)^0.5 end - replacement in file.lua at line 46[7.16435]→[6.6751:6801](∅→∅),[7.16435]→[6.6751:6801](∅→∅),[7.244]→[6.6802:6870](∅→∅),[7.244]→[6.6802:6870](∅→∅),[7.394]→[6.6871:6939](∅→∅),[7.394]→[6.6871:6939](∅→∅)
assert(line, 'drawing in file is incomplete')assert(false, ('unknown drawing mode %s'):format(shape.mode))assert(false, ('unknown drawing mode %s'):format(shape.mode))-- for testsfunction load_array(a)local result = {}local next_line = ipairs(a)local i,line,drawing = 0, ''while true doi,line = next_line(a, i)if i == nil then break endtable.insert(result, {data=line})endif #result == 0 thentable.insert(result, {data=''})endreturn result - edit in file.lua at line 79[7.1381]→[7.1381:1467](∅→∅),[7.1467]→[7.2551:2582](∅→∅),[7.2582]→[7.1490:1566](∅→∅),[7.1490]→[7.1490:1566](∅→∅),[7.1566]→[7.7991:8029](∅→∅),[7.8029]→[7.1810:1839](∅→∅),[7.9433]→[7.1810:1839](∅→∅),[7.1810]→[7.1810:1839](∅→∅),[7.1839]→[7.8030:8066](∅→∅),[7.8066]→[7.1888:1910](∅→∅),[7.9463]→[7.1888:1910](∅→∅),[7.1888]→[7.1888:1910](∅→∅),[7.1910]→[7.532:532](∅→∅),[7.2092]→[6.6940:6988](∅→∅),[7.2092]→[6.6940:6988](∅→∅),[7.3157]→[6.6989:7057](∅→∅),[7.3157]→[6.6989:7057](∅→∅)
-- for testsfunction load_array(a)local result = {}local next_line = ipairs(a)local i,line,drawing = 0, ''while true doi,line = next_line(a, i)if i == nil then break endtable.insert(result, {data=line})endif #result == 0 thentable.insert(result, {data=''})endreturn resultassert(i, 'drawing in array is incomplete')assert(false, ('unknown drawing mode %s'):format(shape.mode)) - resolve order conflict in file.lua at line 79[7.1296]
- edit in edit.lua at line 50
current_drawing_mode = 'line', -- one of the available shape modes - replacement in edit.lua at line 103
if #State.lines ~= #State.line_cache thenprint(('line_cache is out of date; %d when it should be %d'):format(#State.line_cache, #State.lines))assert(false)endif not Text.le1(State.screen_top1, State.cursor1) thenprint(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)assert(false)endassert(#State.lines == #State.line_cache, ('line_cache is out of date; %d elements when it should be %d'):format(#State.line_cache, #State.lines))assert(Text.le1(State.screen_top1, State.cursor1), ('screen_top (line=%d,pos=%d) is below cursor (line=%d,pos=%d)'):format(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)) - edit in edit.lua at line 456[7.12430]→[4.213:213](∅→∅),[7.5492]→[6.7493:7557](∅→∅),[7.5492]→[6.7493:7557](∅→∅),[7.5492]→[6.7493:7557](∅→∅),[7.1023]→[6.7131:7492](∅→∅),[7.1023]→[6.7131:7492](∅→∅)
assert(false, ('unknown line mode %s'):format(line.mode))assert(#State.lines == #State.line_cache, ('line_cache is out of date; %d elements when it should be %d'):format(#State.line_cache, #State.lines))assert(Text.le1(State.screen_top1, State.cursor1), ('screen_top (line=%d,pos=%d) is below cursor (line=%d,pos=%d)'):format(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)) - resolve order conflict in edit.lua at line 456[7.12430]
- edit in drawing.lua at line 36
assert(shape) - replacement in drawing.lua at line 115
print(shape.mode)assert(false)assert(false, ('unknown drawing mode %s'):format(shape.mode)) - edit in drawing.lua at line 127
assert(false, ('unknown drawing mode %s'):format(shape.mode)) - resolve order conflict in drawing.lua at line 127
- replacement in drawing.lua at line 208
print(shape.mode)assert(false)assert(false, ('unknown drawing mode %s'):format(shape.mode)) - edit in drawing.lua at line 218
assert(false, ('unknown drawing mode %s'):format(shape.mode)) - resolve order conflict in drawing.lua at line 218
- replacement in drawing.lua at line 239
print(State.current_drawing_mode)assert(false)assert(false, ('unknown drawing mode %s'):format(State.current_drawing_mode)) - replacement in drawing.lua at line 254
assert(drawing.mode == 'drawing')assert(drawing.mode == 'drawing', 'Drawing.update: line is not a drawing') - edit in drawing.lua at line 297
assert(false, ('unknown drawing mode %s'):format(State.current_drawing_mode)) - resolve order conflict in drawing.lua at line 297
- replacement in drawing.lua at line 341
assert(#drawing.pending.vertices <= 2)assert(#drawing.pending.vertices <= 2, 'Drawing.mouse_release: rectangle has too many pending vertices') - replacement in drawing.lua at line 356
assert(#drawing.pending.vertices <= 2)assert(#drawing.pending.vertices <= 2, 'Drawing.mouse_release: square has too many pending vertices') - replacement in drawing.lua at line 385
print(drawing.pending.mode)assert(false)assert(false, ('unknown drawing mode %s'):format(drawing.pending.mode)) - edit in drawing.lua at line 393[7.207]→[6.7775:7852](∅→∅),[7.207]→[6.7775:7852](∅→∅),[7.207]→[6.7775:7852](∅→∅),[7.1574]→[6.7853:7966](∅→∅),[7.1574]→[6.7853:7966](∅→∅),[7.2272]→[6.7967:8077](∅→∅),[7.2272]→[6.7967:8077](∅→∅),[7.6342]→[6.8078:8158](∅→∅),[7.6342]→[6.8078:8158](∅→∅)
assert(drawing.mode == 'drawing', 'Drawing.update: line is not a drawing')assert(#drawing.pending.vertices <= 2, 'Drawing.mouse_release: rectangle has too many pending vertices')assert(#drawing.pending.vertices <= 2, 'Drawing.mouse_release: square has too many pending vertices')assert(false, ('unknown drawing mode %s'):format(drawing.pending.mode)) - resolve order conflict in drawing.lua at line 393
- replacement in drawing.lua at line 543
assert(idx)assert(idx, 'point to delete is not in vertices') - edit in drawing.lua at line 639
assert(shape) - edit in drawing.lua at line 656
assert(point) - replacement in drawing.lua at line 692
print(shape.mode)assert(false)assert(false, ('unknown drawing mode %s'):format(shape.mode)) - replacement in drawing.lua at line 697
assert(shape.mode == 'freehand')assert(shape.mode == 'freehand', 'can only smoothen freehand shapes') - edit in drawing.lua at line 744[7.11016]→[6.8159:8221](∅→∅),[7.11016]→[6.8159:8221](∅→∅),[7.21459]→[6.8222:8288](∅→∅),[7.21459]→[6.8222:8288](∅→∅),[7.230]→[6.8289:8361](∅→∅),[7.230]→[6.8289:8361](∅→∅)
assert(idx, 'point to delete is not in vertices')assert(false, ('unknown drawing mode %s'):format(shape.mode))assert(shape.mode == 'freehand', 'can only smoothen freehand shapes') - resolve order conflict in drawing.lua at line 744[7.42200]
- replacement in commands.lua at line 139
assert(index)assert(index, 'file missing from manifest')