resolve conflicts
Dependencies
- [2]
RO35V4H4Merge text.love - [3]
NUJMQXN6Merge text.love - [4]
CAG7PP5YMerge text.love - [5]
KKQKPGCIresolve conflicts - [6]
3QNOKBFMbeginnings of a test harness - [7]
73OCE2MCafter much struggle, a brute-force undo - [8]
XNFTJHC4split keyboard handling between Text and Drawing - [9]
FS2ITYYHrecord a known issue - [10]
2TQUKHBCMerge lines.love - [11]
3PSFWAILMerge lines.love - [12]
VXORMHMEdelete experimental REPL - [13]
K2X6G75Zstart writing some tests for drawings - [14]
EIPRCZQCMerge text.love - [15]
REAIVN7WMerge lines.love - [16]
SGMA5JLEsave the list of tests in repo - [17]
ZLJYLPOTMerge lines.love - [18]
AYX33NBCMerge lines.love - [19]
34BZ5ZKNMerge lines.love - [20]
4CTZOJPCstop pretending globals are local - [21]
G3DLS5OUaudit all asserts - [22]
M5JXTW56Merge text.love - [23]
RU4HIK43Merge lines.love - [24]
ED4Z6ORCcleaner API for file-system access - [25]
N2NUGNN4include a brief reference enabling many useful apps - [26]
QXVD2RIFadd state arg to Drawing.mouse_released - [27]
JIK7ZRYIbugfix: imprecision in drawing - [28]
C3GUE45IMerge text.love - [29]
TVCPXAAUrename - [30]
KMSL74GAsupport selections in the source editor - [31]
A4STVUZIfix support for absolute paths in Windows - [32]
XX7G2FFJintermingle freehand line drawings with text - [33]
R5QXEHUIsomebody stop me - [34]
C3NYQP57Merge lines.love - [35]
AVTNUQYRbasic test-enabled framework - [36]
RIAFJLMZMerge text.love - [37]
KWIVKQQ7Merge lines.love - [38]
RSZD5A7Gforgot to add json.lua - [39]
KKMFQDR4editing source code from within the app - [40]
VXRYVZ74Merge text.love - [41]
VP5KC4XZMerge lines.love - [42]
66X36NZNa little more prose describing manual_tests - [43]
6VJTQKW7start supporting LÖVE v12 - [44]
4YDBYBA4clean up memory leak experiments - [45]
BULPIBEGbeginnings of a module for the text editor - [46]
6LJZN727handle chords - [47]
CE4LZV4Tdrop last couple of manual tests - [48]
OTIBCAUJlove2d scaffold - [49]
BLWAYPKVextract a module - [50]
ORKN6EOBMerge lines.love - [51]
HYEAFRZ2split mouse_pressed events between Text and Drawing - [52]
3G723RV5Merge text.love - [53]
PJ5PQAQErecord support for multiple versions - [54]
2344TV56Merge lines.love - [55]
MGJZHZC4Merge lines.love - [56]
YF2ATH2QMerge lines.love - [57]
ATQO62TFMerge lines.love - [58]
LWPFEZBIMerge lines.love - [59]
VHUNJHXBMerge lines.love - [60]
LYN3L74Wcorrect commit f3abc2cbf2 - [61]
NFI42KGXmore correct absolute path detection - [62]
LXTTOB33extract a couple of files - [63]
4SR3Z4Y3document the version of LÖVE I've been using - [64]
T4FRZSYLdelete an ancient, unused file - [65]
KYNGDE2Cconsistent names in a few more places - [66]
QD4LOFQRMerge text.love - [67]
JDZVBFEIMerge lines.love - [68]
D2GCFTTTclean up repl functionality - [69]
TLOAPLBJadd a license - [70]
JOPVPUSAediting source code from within the app - [71]
5SM6DRHKport inscript's bugfix to source editor - [72]
D4B52CQ2Merge lines.love - [73]
MD3W5IRAnew fork: rip out drawing support - [74]
ONHKBLLCMerge lines.love - [75]
WPW3AVFSmore precise shape selection - [76]
2CK5QI7Wmake love event names consistent - [77]
IGBTDA6YMerge text.love - [78]
4HR3G5ZDMerge text.love - [79]
X43ZIKR3Merge text.love - [80]
2L5MEZV3experiment: new edit namespace - [81]
2CFLXLIEMerge text.love - [82]
Q6RXCILQMerge text.love - [83]
VBU5YHLRMerge lines.love - [84]
FM5LDKGTMerge text.love - [85]
D2TYFYG2Merge text.love - [86]
BJ5X5O4Alet's prevent the text cursor from ever getting on a drawing - [87]
OGUV4HSAremove some memory leaks from rendered fragments - [88]
MXSAHZN4Merge lines.love - [89]
TBTRYEBPMerge lines.love - [90]
VHQCNMARseveral more modules - [91]
ISOFHXB2App.width can no longer take a Text - [92]
36Z442IVback to commit 8123959e52f without code editing - [93]
2CTN2IEFMerge lines.love - [94]
DRFE3B3Zmouse buttons are integers, not strings - [95]
3OTESDW6move drawing.starty into line cache - [*]
JCXL74WVbring back everything from commit a68647ae22
Change contents
- file deletion: geom.lua 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' thenelseassert(false, ('unknown drawing mode %s'):format(shape.mode))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 endendendfunction 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 then - file deletion: drawing.lua drawing.lua
assert(false, ('unknown drawing mode %s'):format(shape.mode))assert(false, ('unknown drawing mode %s'):format(shape.mode))assert(false, ('unknown drawing mode %s'):format(State.current_drawing_mode))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))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')for _=1,7 dofor i=2,#shape.points-1 dolocal a = shape.points[i-1]local b = shape.points[i]local c = shape.points[i+1]b.x = round((a.x + b.x + c.x)/3)b.y = round((a.y + b.y + c.y)/3)endendendfunction round(num)return math.floor(num+.5)endfunction Drawing.find_or_insert_point(points, x,y, width)-- check if UI would snap the two points togetherfor i,point in ipairs(points) doif Drawing.near(point, x,y, width) thenreturn iendendtable.insert(points, {x=x, y=y})return #pointsendfunction Drawing.near(point, x,y, width)local px,py = Drawing.pixels(x, width),Drawing.pixels(y, width)local cx,cy = Drawing.pixels(point.x, width), Drawing.pixels(point.y, width)return (cx-px)*(cx-px) + (cy-py)*(cy-py) < Same_point_distance*Same_point_distanceendfunction Drawing.pixels(n, width) -- parts to pixelsreturn math.floor(n*width/256)endfunction Drawing.coord(n, width) -- pixels to partsreturn math.floor(n*256/width)endfunction table.find(h, x)for k,v in pairs(h) doif v == x thenreturn kendendendendendfunction Drawing.smoothen(shape)table.remove(shape.vertices, idx)if #shape.vertices < 3 thenshape.mode = 'deleted'endelseshape.mode = 'deleted'endendenddrawing.points[i].deleted = trueendlocal drawing,_,_,shape = Drawing.select_shape_at_mouse(State)if drawing thenshape.mode = 'deleted'endelseif chord == 'C-h' and not App.mouse_down(1) thenlocal drawing = Drawing.select_drawing_at_mouse(State)if drawing thendrawing.show_help = trueendelseif chord == 'escape' and App.mouse_down(1) thenlocal _,drawing = Drawing.current_drawing(State)drawing.pending = {}endendfunction Drawing.complete_rectangle(firstx,firsty, secondx,secondy, x,y)if firstx == secondx thenreturn x,secondy, x,firstyendif firsty == secondy thenreturn secondx,y, firstx,yendlocal first_slope = (secondy-firsty)/(secondx-firstx)-- slope of second edge:-- -1/first_slope-- equation of line containing the second edge:-- y-secondy = -1/first_slope*(x-secondx)-- => 1/first_slope*x + y + (- secondy - secondx/first_slope) = 0-- now we want to find the point on this line that's closest to the mouse pointer.-- https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_an_equationlocal a = 1/first_slopelocal c = -secondy - secondx/first_slopelocal thirdx = round(((x-a*y) - a*c) / (a*a + 1))local thirdy = round((a*(-x + a*y) - c) / (a*a + 1))-- slope of third edge = first_slope-- equation of line containing third edge:-- y - thirdy = first_slope*(x-thirdx)-- => -first_slope*x + y + (-thirdy + thirdx*first_slope) = 0-- now we want to find the point on this line that's closest to the first pointlocal a = -first_slopelocal c = -thirdy + thirdx*first_slopelocal fourthx = round(((firstx-a*firsty) - a*c) / (a*a + 1))local fourthy = round((a*(-firstx + a*firsty) - c) / (a*a + 1))return thirdx,thirdy, fourthx,fourthyendfunction Drawing.complete_square(firstx,firsty, secondx,secondy, x,y)-- use x,y only to decide which side of the first edge to complete the square onlocal deltax = secondx-firstxlocal deltay = secondy-firstylocal thirdx = secondx+deltaylocal thirdy = secondy-deltaxif not geom.same_side(firstx,firsty, secondx,secondy, thirdx,thirdy, x,y) thendeltax = -deltaxdeltay = -deltaythirdx = secondx+deltaythirdy = secondy-deltaxendlocal fourthx = firstx+deltaylocal fourthy = firsty-deltaxreturn thirdx,thirdy, fourthx,fourthyendfunction Drawing.current_drawing(State)local x, y = App.mouse_x(), App.mouse_y()for drawing_index,drawing in ipairs(State.lines) doif drawing.mode == 'drawing' thenlocal line_cache = State.line_cache[drawing_index]if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) thenreturn drawing_index,drawing,line_cacheendendendreturn nilendfunction Drawing.select_shape_at_mouse(State)for drawing_index,drawing in ipairs(State.lines) doif drawing.mode == 'drawing' thenlocal x, y = App.mouse_x(), App.mouse_y()local line_cache = State.line_cache[drawing_index]if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) thenlocal mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)for i,shape in ipairs(drawing.shapes) dofunction Drawing.keychord_press(State, chord)endState.lines.current_drawing.pending = {}State.lines.current_drawing = nilendendendif #drawing.pending.vertices == 2 thenlocal mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h thenlocal first = drawing.points[drawing.pending.vertices[1]]local second = drawing.points[drawing.pending.vertices[2]]local thirdx,thirdy, fourthx,fourthy = Drawing.complete_square(first.x,first.y, second.x,second.y, mx,my)table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, thirdx,thirdy, State.width))table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, fourthx,fourthy, State.width))table.insert(drawing.shapes, drawing.pending)endendelseif drawing.pending.mode == 'circle' thenlocal mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h thenlocal center = drawing.points[drawing.pending.center]drawing.pending.radius = round(geom.dist(center.x,center.y, mx,my))table.insert(drawing.shapes, drawing.pending)endelseif drawing.pending.mode == 'arc' thenlocal mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h thenlocal center = drawing.points[drawing.pending.center]drawing.pending.end_angle = geom.angle_with_hint(center.x,center.y, mx,my, drawing.pending.end_angle)table.insert(drawing.shapes, drawing.pending)endelseif drawing.pending.mode == 'name' then-- drop itelseif #drawing.pending.vertices == 2 thenlocal mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h thenlocal first = drawing.points[drawing.pending.vertices[1]]local second = drawing.points[drawing.pending.vertices[2]]local thirdx,thirdy, fourthx,fourthy = Drawing.complete_rectangle(first.x,first.y, second.x,second.y, mx,my)table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, thirdx,thirdy, State.width))table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, fourthx,fourthy, State.width))table.insert(drawing.shapes, drawing.pending)endelse-- too few points; draw nothingendelseif drawing.pending.mode == 'square' thenfunction Drawing.mouse_release(State, x,y, mouse_button)local pmx, pmy = App.mouse_x(), App.mouse_y()local mx = Drawing.coord(pmx-State.left, State.width)local my = Drawing.coord(pmy-line_cache.starty, State.width)if App.mouse_down(1) thenif Drawing.in_drawing(drawing, line_cache, pmx,pmy, State.left,State.right) thenif drawing.pending.mode == 'freehand' thentable.insert(drawing.pending.points, {x=mx, y=my})elseif drawing.pending.mode == 'move' thendrawing.pending.target_point.x = mxdrawing.pending.target_point.y = myDrawing.relax_constraints(drawing, drawing.pending.target_point_index)endendelseif State.current_drawing_mode == 'move' thenif Drawing.in_drawing(drawing, line_cache, pmx, pmy, State.left,State.right) thendrawing.pending.target_point.x = mxdrawing.pending.target_point.y = myDrawing.relax_constraints(drawing, drawing.pending.target_point_index)endelse-- do nothingendendfunction Drawing.relax_constraints(drawing, p)for _,shape in ipairs(drawing.shapes) doif shape.mode == 'manhattan' thenif shape.p1 == p thenshape.mode = 'line'elseif shape.p2 == p thenshape.mode = 'line'endelseif shape.mode == 'rectangle' or shape.mode == 'square' thenfor _,v in ipairs(shape.vertices) doif v == p thenshape.mode = 'polygon'endendendendendendend-- a couple of operations on drawings need to constantly check the state of the mousefunction Drawing.update(State)if State.lines.current_drawing == nil then return endlocal drawing = State.lines.current_drawinglocal line_cache = State.line_cache[State.lines.current_drawing_index]function Drawing.mouse_press(State, drawing_index, x,y, mouse_button)endendfunction Drawing.in_drawing(drawing, line_cache, x,y, left,right)if line_cache.starty == nil then return false end -- outside current pagelocal width = right-leftreturn y >= line_cache.starty and y < line_cache.starty + Drawing.pixels(drawing.h, width) and x >= left and x < rightend-- after mouse_releaseendendfunction Drawing.draw_pending_shape(drawing, top, left,right)local width = right-leftlocal pmx,pmy = App.mouse_x(), App.mouse_y()local function px(x) return Drawing.pixels(x, width)+left endlocal function py(y) return Drawing.pixels(y, width)+top endlocal mx = Drawing.coord(pmx-left, width)local my = Drawing.coord(pmy-top, width)-- recreate pixels from coords to precisely mimic how the drawing will look - 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_select.lua source_select.lua
assert(maxl == line_index, ('maxl %d not equal to line_index %d'):format(maxl, line_index))assert(minl == line_index, ('minl %d not equal to line_index %d'):format(minl, line_index))assert(minl == maxl and minl == line_index, ('minl %d, maxl %d and line_index %d are not all equal'):format(minl, maxl, line_index))assert(minl < maxl, ('minl %d not < maxl %d'):format(minl, maxl))assert(minl < maxl, ('minl %d not < maxl %d'):format(minl, maxl))local result = {State.lines[minl].data:sub(min_offset)}for i=minl+1,maxl-1 doif State.lines[i].mode == 'text' thentable.insert(result, State.lines[i].data)endendtable.insert(result, State.lines[maxl].data:sub(1, max_offset-1))return table.concat(result, '\n')endlocal rhs = State.lines[maxl].data:sub(max_offset)for i=maxl,minl+1,-1 dotable.remove(State.lines, i)table.remove(State.line_cache, i)endState.lines[minl].data = State.lines[minl].data:sub(1, min_offset-1)..rhsendfunction Text.selection(State)if State.selection1.line == nil then return end-- min,max = sorted(State.selection1,State.cursor1)local minl,minp = State.selection1.line,State.selection1.poslocal maxl,maxp = State.cursor1.line,State.cursor1.posif minl > maxl thenminl,maxl = maxl,minlminp,maxp = maxp,minpelseif minl == maxl thenif minp > maxp thenminp,maxp = maxp,minpendendlocal min_offset = Text.offset(State.lines[minl].data, minp)local max_offset = Text.offset(State.lines[maxl].data, maxp)if minl == maxl thenreturn State.lines[minl].data:sub(min_offset, max_offset-1)endreturn minp,maxpendend-- draw highlight for line corresponding to (lo,hi) given an approximate x,y and pos on the same screen line-- Creates text objects every time, so use this sparingly.-- Returns some intermediate computation useful elsewhere.function Text.draw_highlight(State, line, x,y, pos, lo,hi)if lo thenlocal lo_offset = Text.offset(line.data, lo)local hi_offset = Text.offset(line.data, hi)local pos_offset = Text.offset(line.data, pos)local lo_pxif pos == lo thenlo_px = 0elselocal before = line.data:sub(pos_offset, lo_offset-1)return minp,bposelsereturn apos,maxpelseif b_lt then - 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)endfunction reset_file_navigator()Show_file_navigator = falseFile_navigation.index = 1File_navigation.filter = ''File_navigation.candidates = File_navigation.all_candidatesend - edit in file.lua at line 79
- resolve order conflict in file.lua at line 79[97.3530]